Red Hat Training

A Red Hat training course is available for Red Hat Fuse

9.5. Enabling Realms in the STS

9.5.1. Issuing Tokens in Multiple Realms

Overview

Apache CXF optionally supports the concept of security realms in the STS. The WS-Trust specification does not explicitly discuss the concept of security realms, but one fairly natural approach you can use is to identify an STS issuer identity with a security realm. Enabling security realms requires you to implement and configure a variety of custom components in the STS.
Figure 9.11, “Realm-Aware SAML Token Issuer” shows an overview of how SAML tokens are issued in a realm-aware STS.

Figure 9.11. Realm-Aware SAML Token Issuer

Realm aware token issuing steps

A realm-aware STS can issue SAML tokens in the following manner:
  1. When a realm-aware STS receives an issue token request, it tries to find out what realm to issue the token in, by calling out to the realm parser instance.
    WS-Trust does not define a standard way to associate a token with a realm. Hence, you must work out your own approach for indicating the realm and codify this approach by providing a custom implementation of the RealmParser interface. The realm parser's parseRealm method returns a string, which is the name of the realm to issue the token in.
    For example, you could identify the realm, by inspecting the URL of the STS Web service endpoint that was invoked. The pathname of the URL could include a segment that identifies the realm.
  2. The TokenIssueOperation instance then calls the canHandleToken method on each of the registered token providers. In this example, only the SAMLTokenProvider token provider is registered. The canHandleToken method parameters include the token type and the realm name.
  3. Assuming that the token type matches (for example, the client is requesting a SAML token), the SAMLTokenProvider looks up the realm name in its realm map to make sure that it can handle this realm. If the SAMLTokenProvider finds the realm name in its map, it returns true from the canHandleToken method.
  4. The TokenIssueOperation instance now calls the createToken method on the SAMLTokenProvider instance, in order to issue the token in the specified realm.
  5. The SAMLTokenProvider looks up the specified realm in the realm map and retrieves the corresponding SAMLRealm instance. The SAMLRealm instance encapsulates the data that is specific to this realm.
    For example, if the specified realm is A, the SAMLRealm instance records that the corresponding issuer name is A-Issuer and the alias of the signing key to use for this realm is StsKeyA.
  6. The SAMLTokenProvider now uses the realm-specific data in combination with the generic data from the STS properties instance to issue the SAML token in the specified realm.
    For example, if the specified realm is A, the SAMLTokenProvider embeds the A-Issuer string in the SAML token's issuer element and the SAML token is signed using the StsKeyA private key from the stsstore.jks Java keystore file.

Configuring the realm parser

Because there is no standard way to associate a realm with an issue token request, you must decide yourself how to identify a realm. Codify the approach by implementing the RealmParser interface and then register your custom realm parser by injecting it into the realmParser property of the STS properties bean.
For example, you could register the custom URLRealmParser instance with the StaticSTSProperties bean as follows:
<beans ... >
    ...
    <bean id="transportSTSProperties" class="org.apache.cxf.sts.StaticSTSProperties">
        ...
        <property name="realmParser" ref="customRealmParser" />
        ...
    </bean>

    <bean id="customRealmParser"
          class="org.apache.cxf.systest.sts.realms.URLRealmParser" />
    ...
</beans>

Sample URL realm parser

To implement a custom realm parser, you must override and implement the following method from the RealmParser interface:
public String parseRealm(WebServiceContext context) throws STSException;
The parseRealm passes an instance of javax.xml.ws.WebServiceContext, which provides access to message context and security information about the current request message (issue token request). You can use this message context information to identify the current realm.
For example, the URLRealmParser used in the previous example works by examining the URL of the invoked STS Web service endpoint and checking whether any known realm names are embedded in the URL. The realm name embedded in the URL is then taken to be the realm to issue the token in and the realm is then returned from the parseRealm method.
// Java
package org.apache.cxf.systest.sts.realms;

import javax.xml.ws.WebServiceContext;

import org.apache.cxf.sts.RealmParser;
import org.apache.cxf.ws.security.sts.provider.STSException;

/**
 * A test implementation of RealmParser which returns a realm depending on a String contained
 * in the URL of the service request.
 */
public class URLRealmParser implements RealmParser {

    public String parseRealm(WebServiceContext context) throws STSException {
        String url = (String)context.getMessageContext().get("org.apache.cxf.request.url");
        if (url.contains("realmA")) {
            return "A";
        } else if (url.contains("realmB")) {
            return "B";
        } else if (url.contains("realmC")) {
            return "C";
        }
        
        return null;
    }
    
}
A null return value indicates that the STS should use the default realm (as defined by the issuer and signatureUsername properties of the STS properties bean).

Configuring the realm map

In a realm-aware STS, the SAMLTokenProvider token provider must be initialized with a realm map, which provides the requisite data about each realm. For example, the scenario shown in Figure 9.11, “Realm-Aware SAML Token Issuer” uses a realm map like the following:
<beans ... >
    ...
    <bean id="transportIssueDelegate" class="org.apache.cxf.sts.operation.TokenIssueOperation">
        <property name="tokenProviders" ref="transportTokenProviders" />
        <property name="services" ref="transportService" />
        <property name="stsProperties" ref="transportSTSProperties" />
    </bean>

    <util:list id="transportTokenProviders">
        <ref bean="transportSAMLProvider" />
    </util:list>

    <bean id="transportSAMLProvider" class="org.apache.cxf.sts.token.provider.SAMLTokenProvider">
        <property name="realmMap" ref="realms" />
    </bean>

    <util:map id="realms">
        <entry key="A" value-ref="realmA" />
        <entry key="B" value-ref="realmB" />
        <entry key="C" value-ref="realmC" />
    </util:map>

    <bean id="realmA" class="org.apache.cxf.sts.token.realm.SAMLRealm">
        <property name="issuer" value="A-Issuer" />
        <property name="signatureAlias" value="StsKeyA" />
    </bean>

    <bean id="realmB" class="org.apache.cxf.sts.token.realm.SAMLRealm">
        <property name="issuer" value="B-Issuer" />
        <property name="signatureAlias" value="StsKeyB" />
    </bean>

    <bean id="realmC" class="org.apache.cxf.sts.token.realm.SAMLRealm">
        <property name="issuer" value="C-Issuer" />
        <property name="signatureAlias" value="StsKeyC" />
    </bean>
    ...
</beans>

9.5.2. Validating Tokens in Multiple Realms

Overview

Figure 9.12, “Realm-Aware SAML Token Validation” shows an overview of how SAML tokens are validated in a realm-aware STS.

Figure 9.12. Realm-Aware SAML Token Validation

Realm aware token validating steps

A realm-aware STS can validate SAML tokens in the following manner:
  1. When a realm-aware STS receives a validate token request, it tries to find out what realm to issue the token in, by calling out to the realm parser instance.
    Note
    The realm identified by the realm parser in this step is not necessarily the same realm that the token was originally issued in. See the section called “Validating tokens across realms”.
  2. The TokenValidateOperation instance then calls the canHandleToken method on each of the registered token validators. In this example, only the SAMLTokenValidator token validator is registered. The canHandleToken method parameters include the token type and the realm name.
    Note
    The default SAMLTokenValidator class ignores the realm parameter in the canHandleToken method, so it will attempt to validate the token in any realm. If you need to implement realm-specific validation steps, however, you have the option of implementing a custom SAML token validator that pays attention to the realm parameter.
  3. The TokenValidateOperation instance then calls the validateToken method on the SAMLTokenValidator, in order to validate the token in the specified realm.
  4. The SAMLTokenValidator attempts to validate the received SAML token by checking whether it has been signed by a trusted key. The public part of the signing key pair must match one of the trusted certificates stored in the signature trust store (as configured by the signaturePropertiesFile property in the STS properties instance).
    Hence, for each of the supported realms, the public part of the realm's signing key must be present in the signature trust store (or at least one of the certificate's in that realm's trust chain). Otherwise, the SAMLTokenValidator will not be able to validate tokens that were issued in that realm.
    For example, if you want to be able to validate tokens in the realms, A, B, and C, you must store the corresponding certificates (public part of the signature keys), StsKeyA, StsKeyB, and StsKeyC, in the stsstore.jks Java keystore file.
  5. In case the client needs the information, the SAMLTokenValidator also embeds the name of the realm where the token was originally issued into the Validate response message. This is not necessarily the same realm as the realm that the token has just been validated in.
    To find the original realm that the token was issued in, the SAMLTokenValidator calls out to the custom SAMLRealmCodec instance. The SAMLRealmCodec instance tries to figure out the issuing realm by examining the token contents. If the issuing realm can be established, this information is included in the Validate response message.

Configuring the realm parser

The realm-aware SAML token validator requires a realm parser, just like the realm-aware SAML token provider. Generally, both validator and provider can share the same realm parser instance—see Section 9.5.1, “Issuing Tokens in Multiple Realms”.

Validating tokens across realms

It can happen that a token needs to be validated in a realm that is not the same realm as the realm where the token was issued. When you consider that the main purpose of the WS-Trust standard is to enable single-sign on, you can understand why it is desirable to support this feature. If a WS client needs to send requests to servers that are in different security realms, it would be a serious drawback, if the client was forced to obtain separate tokens for each of the realms. Hence, the STS Validate operation must be prepared to validate a token issued in a realm that is different from the realm it is being validated in.

Response from Validate operation

For the convenience of the client, which might need to know the realm that a token was originally issued in, the SAMLTokenValidator can be configured to discover the token's issuing realm and embed this information in the Validate operation's response. To give the SAMLTokenValidator the ability to discover the token's issuing realm, you must implement and register a SAMLRealmCodec instance.

Configuring the SAMLRealmCodec

The following Spring XML fragment shows how to instantiate and register the custom IssuerSAMLRealmCodec instance, which implements the SAMLRealmCodec interface:
<beans ... >
    ...
    <bean id="transportValidateDelegate" class="org.apache.cxf.sts.operation.TokenValidateOperation">
        <property name="tokenProviders" ref="transportTokenProviders" />
        <property name="tokenValidators" ref="transportTokenValidators" />
        <property name="stsProperties" ref="transportSTSProperties" />
    </bean>
    ...
    <util:list id="transportTokenValidators">
        <ref bean="transportSAMLValidator" />
    </util:list>

    <bean id="transportSAMLValidator"
          class="org.apache.cxf.sts.token.validator.SAMLTokenValidator">
        ...
        <property name="samlRealmCodec" ref="customSAMLRealmCodec" />
    </bean>

    <bean id="customSAMLRealmCodec"
          class="org.apache.cxf.systest.sts.realms.IssuerSAMLRealmCodec" />
    ...
</beans>

Sample implementation of SAMLRealmCodec

To implement a SAMLRealmCodec, you need to override and implement the following method:
public String getRealmFromToken(AssertionWrapper assertion)
Where the assertion parameter holds the contents of the SAML token. The assumption made here is that the realm name is either embedded in the SAML token somehow or the identity of the realm can somehow be inferred from the SAML token contents. For example, the SAML issuer name can typically be identified with a security realm.
The following examples shows a sample implementation, IssuerSAMLRealmCodec, which infers the realm name from the value of the issuer string:
// Java
package org.apache.cxf.systest.sts.realms;

import org.apache.cxf.sts.token.realm.SAMLRealmCodec;
import org.apache.ws.security.saml.ext.AssertionWrapper;


/**
 * This class returns a realm associated with a SAML Assertion depending on the issuer.
 */
public class IssuerSAMLRealmCodec implements SAMLRealmCodec {
    
    /**
     * Get the realm associated with the AssertionWrapper parameter
     * @param assertion a SAML Assertion wrapper object
     * @return the realm associated with the AssertionWrapper parameter
     */
    public String getRealmFromToken(AssertionWrapper assertion) {
        if ("A-Issuer".equals(assertion.getIssuerString())) {
            return "A";
        } else if ("B-Issuer".equals(assertion.getIssuerString())) {
            return "B";
        }
        return null;
    }
    
}

9.5.3. Token Transformation across Realms

Overview

Token transformation is a special case of token validation across realms. As explained in Section 9.5.2, “Validating Tokens in Multiple Realms”, it is possible to configure the STS to recognize and validate tokens that were issued in a different realm. But this is usually not sufficient for cross-realm interoperability. The foreign token might not have the right format for the target realm and the token's principal might not be recognized.
The solution to this interoperability problem is to re-issue the foreign token in the format required by the target realm and, if necessary, to map the token's principal to its equivalent in the target realm (assuming, of course, that the principal has an account in both realms). This is what is meant by token transformation.
Because the need for token transformation is usually recognized during token validation, the token transformation process is implemented as an extension of the Validate operation.

Triggering token transformation

Token transformation gets triggered when you configure the WS endpoint of the relying party to validate incoming tokens, as follows:
<beans ... >
   ...
   <jaxws:endpoint id="doubleitrealmtransform"
      implementor="org.apache.cxf.systest.sts.common.DoubleItPortTypeImpl"
      endpointName="s:DoubleItRealmTransformPort"
      serviceName="s:DoubleItService"
      depends-on="ClientAuthHttpsSettings"
      address="https://localhost:${testutil.ports.Server}/doubleit/services/doubleitrealmtransform"
      wsdlLocation="org/apache/cxf/systest/sts/realms/DoubleIt.wsdl"
      xmlns:s="http://www.example.org/contract/DoubleIt">
        
      <jaxws:properties>
         <entry key="ws-security.saml2.validator">
            <bean class="org.apache.cxf.ws.security.trust.STSTokenValidator"/>
         </entry>
         <entry key="security.sts.client">
               <bean class="org.apache.cxf.ws.security.trust.STSClient">
                   <constructor-arg ref="cxf"/>
                   <property name="wsdlLocation" 
                       value="https://localhost:${testutil.ports.STSServer}/SecurityTokenService/realmB?wsdl"/>
                   <property name="serviceName" 
                       value="{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}SecurityTokenService"/>
                   <property name="endpointName" 
                       value="{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}Transport_Port"/>
                   <property name="properties">
                       <map>
                           <entry key="security.username" value="alice"/>
                           <entry key="security.callback-handler" 
                                  value="org.apache.cxf.systest.sts.common.CommonCallbackHandler"/>
                           <entry key="security.sts.token.username" value="myclientkey"/>
                           <entry key="security.sts.token.properties" value="clientKeystore.properties"/> 
                           <entry key="security.sts.token.usecert" value="true"/> 
                       </map>
                   </property>
                   <property name="tokenType" 
                        value="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"/>
               </bean>            
         </entry> 
      </jaxws:properties> 
   </jaxws:endpoint>
   ...
</beans>
The following properties set on jaxws:endpoint element are of key importance in configuring token transformation:
ws-security.saml2.validator
By initializing this property with an instance of the STSTokenValidator class, you are instructing the JAX-WS endpoint to validate incoming tokens by contacting the STS and invoking the Validate operation.
security.sts.client
When validation is enabled on the JAX-WS endpoint, you must also configure an STSClient instance, which encapsulates all of the settings required to connect to the STS. The properties you can set on the STSClient instance are discussed in detail in Creating an STSClient Instance.
tokenType
In order to enable a token transformation request (as distinct from a simple validation request), you must also set the tokenType property on the STSClient instance. This is the key setting that triggers token transformation. When this setting is present, the Validate operation will perform token transformation and return a newly issued token of the specified type in the Validate response message.
For the list of possible token type URIs you can specify here, see Table 8.2.

Relying party as a gateway service

The relying party in the token transformation scenario typically acts as a gateway service. That is, having obtained a transformed token from the STS, it can then make invocations in the target realm on behalf of the client, using the newly-issued transformed token.

Transformation algorithm

When the STS receives a token transformation request (through the Validate operation), it processes the request as follows:
  1. When the STS receives the Validate request message, it performs all of the usual tests to validate the received token (see Section 9.1.4, “Customizing the Validate Operation”).
  2. After validating the token successfully, the STS checks whether the TokenType has been explicitly set in the Validate request message (that is, whether the token type has some value other than the default dummy value).
  3. If the token type was explicitly set, the STS proceeds to transform the token, which means that it issues a new token to replace the validated token.
  4. The STS now checks whether the current realm (as determined by the realm parser—see the section called “Configuring the realm parser”) is the same as the realm that issued the received token (as determined by the configured SAMLRealmCodec—see the section called “Configuring the SAMLRealmCodec”). If the realms are different, the STS checks whether an IdentityMapper instance is configured on the STS properties object.
  5. If an IdentityMapper is configured, the STS transforms the validated token's principal by calling the mapPrincipal method on the IdentityMapper. The mapped identity will now be used as the transformed token's principal.
    Note
    In the context of SAML tokens, the principal corresponds to the value of the Subject/NameID element in the SAML token.
  6. The STS now proceeds to issue a new token in the current realm using the (possibly transformed) principal, based on the data in the validated token. The STS iterates over all of the registered token providers, until it finds a token provider that can handle the requested token type in the current realm.
  7. The STS then issues a new token by calling out to the token provider and returns the newly issued token in the Validate response message.

Configuring the TokenValidateOperation

The following Spring XML fragment shows an example of how the TokenValidateOperation instance is configured in an STS that supports token transformation:
<beans ... >
   ...
    <bean id="transportValidateDelegate" class="org.apache.cxf.sts.operation.TokenValidateOperation">
        <property name="tokenProviders" ref="transportTokenProviders" />
        <property name="tokenValidators" ref="transportTokenValidators" />
        <property name="stsProperties" ref="transportSTSProperties" />
    </bean>
   ...
</beans>
As you might expect, you are required to provide a list of token validators to the tokenValidators property (as is usual for the Valdate operation—for example, see Section 9.1.4, “Customizing the Validate Operation”). What you might not expect, however, is that you are also required to provide a list of token providers to the tokenProviders property: this is because the Validate operation is also responsible for issuing new tokens, in the token transformation scenario.

Implementing an IdentityMapper

In the context of token transformation, it is frequently necessary to implement an identity mapper, because the principal in the source realm is typically not the same as the principal in the target realm. To implement an identity mapper class, you inherit from the IdentityMapper interface and implement the mapPrincipal method, as shown in the following example:
// Java
package org.apache.cxf.systest.sts.realms;

import java.security.Principal;

import org.apache.cxf.sts.IdentityMapper;
import org.apache.ws.security.CustomTokenPrincipal;

/**
 * A test implementation of RealmParser.
 */
public class CustomIdentityMapper implements IdentityMapper {

    /**
     * Map a principal in the source realm to the target realm
     * @param sourceRealm the source realm of the Principal
     * @param sourcePrincipal the principal in the source realm
     * @param targetRealm the target realm of the Principal
     * @return the principal in the target realm
     */
    public Principal mapPrincipal(String sourceRealm, Principal sourcePrincipal, String targetRealm) {
        if ("A".equals(sourceRealm) && "B".equals(targetRealm)) {
            return new CustomTokenPrincipal("B-Principal");
        } else if ("B".equals(sourceRealm) && "A".equals(targetRealm)) {
            return new CustomTokenPrincipal("A-Principal");
        }
        return null;
    }

}
The CustomTokenPrincipal class is just a simple implementation of the java.security.Principal interface, which holds the string value of the returned principal.

Configuring the IdentityMapper

The IdentityMapper instance is configured by setting the identityMapper property on the STS properties instance, as follows:
<beans ... >
    ...
    <bean id="transportSTSProperties" class="org.apache.cxf.sts.StaticSTSProperties">
        ...
        <property name="identityMapper" ref="customIdentityMapper" />
        <property name="realmParser" ref="customRealmParser" />
    </bean>

    <bean id="customIdentityMapper"
          class="org.apache.cxf.systest.sts.realms.CustomIdentityMapper" />

    <bean id="customRealmParser"
          class="org.apache.cxf.systest.sts.realms.URLRealmParser" />
    ...
</beans>

9.5.4. Realms Demonstration

Overview

The sample code in this section is taken from the STS system tests in the source distribution of Apache CXF. The test illustrates several different aspects of STS realms, including realm-aware token issuing, validation across realms, and token transformation.

Demonstration location

You can find the Java code under the following directory:
CXFInstallDir/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/realms
And the associated resource files under the following directory:
CXFInstallDir/services/sts/systests/advanced/src/test/resources/org/apache/cxf/systest/sts/realms

First STS server for A and C realms

Figure 9.13, “STS Server for A and C realms” shows how the first STS server is configured for realms A, C and default.

Figure 9.13. STS Server for A and C realms

The first STS server supports the realms A, C, and default and opens distinct Web service ports for each of these three realms.

STS for realms A and C

The STS for realms A and C is configured as follows:

STS endpoint configuration for realms A and C

The WS endpoints of the STS for realms A and C are configured as follows in the STS's Spring XML file:
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:cxf="http://cxf.apache.org/core"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:sec="http://cxf.apache.org/configuration/security"
  xmlns:http="http://cxf.apache.org/transports/http/configuration"
  xmlns:httpj="http://cxf.apache.org/transports/http-jetty/configuration"
  xmlns:jaxws="http://cxf.apache.org/jaxws"
  xmlns:util="http://www.springframework.org/schema/util"
  xsi:schemaLocation="
            http://cxf.apache.org/core
            http://cxf.apache.org/schemas/core.xsd
            http://cxf.apache.org/configuration/security
            http://cxf.apache.org/schemas/configuration/security.xsd
            http://cxf.apache.org/jaxws
            http://cxf.apache.org/schemas/jaxws.xsd
            http://cxf.apache.org/transports/http/configuration
            http://cxf.apache.org/schemas/configuration/http-conf.xsd
            http://cxf.apache.org/transports/http-jetty/configuration
            http://cxf.apache.org/schemas/configuration/http-jetty.xsd
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/util
            http://www.springframework.org/schema/util/spring-util-2.0.xsd">
    
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
    
    <cxf:bus>
        <cxf:features>
            <cxf:logging/>
        </cxf:features>
    </cxf:bus>

    <bean id="transportSTSProviderBean"
        class="org.apache.cxf.ws.security.sts.provider.SecurityTokenServiceProvider">
        <property name="issueOperation" ref="transportIssueDelegate" />
        <property name="validateOperation" ref="transportValidateDelegate" />
    </bean>
    ...
    <jaxws:endpoint id="RealmASTS" implementor="#transportSTSProviderBean"
        address="https://localhost:${testutil.ports.STSServer.2}/SecurityTokenService/realmA"
        ...
    </jaxws:endpoint>

    <jaxws:endpoint id="RealmCSTS" implementor="#transportSTSProviderBean"
        address="https://localhost:${testutil.ports.STSServer.2}/SecurityTokenService/realmC"
        ...
    </jaxws:endpoint>

    <jaxws:endpoint id="DefaultRealmSTS" implementor="#transportSTSProviderBean"
        address="https://localhost:${testutil.ports.STSServer.2}/SecurityTokenService/realmdefault"
        ...
    </jaxws:endpoint>

    <httpj:engine-factory id="ClientAuthHttpsSettings"
        bus="cxf">
        <httpj:engine port="${testutil.ports.STSServer.2}">
            <httpj:tlsServerParameters>
                ...
                <sec:clientAuthentication want="true" required="true" />
            </httpj:tlsServerParameters>
        </httpj:engine>
    </httpj:engine-factory>
   
</beans>
Note, in particular that the STS defines three different endpoints: for realm A, for realm C, and for the default realm. The endpoint URL that the client connects to, determines the realm in which the token is issued (see Example 9.5, “Demonstration RealmParser Implementation”).

Issue configuration for realms A and C

For realms A and C, the TokenIssueOperation instance is configured as follows:
<beans ... >
    ...
    <bean id="transportIssueDelegate" class="org.apache.cxf.sts.operation.TokenIssueOperation">
        <property name="tokenProviders" ref="transportTokenProviders" />
        <property name="services" ref="transportService" />
        <property name="stsProperties" ref="transportSTSProperties" />
    </bean>

    <util:list id="transportTokenProviders">
        <ref bean="transportSAMLProvider" />
    </util:list>

    <bean id="transportSAMLProvider" class="org.apache.cxf.sts.token.provider.SAMLTokenProvider">
        <property name="realmMap" ref="realms" />
    </bean>

    <util:map id="realms">
        <entry key="A" value-ref="realmA" />
        <entry key="C" value-ref="realmC" />
    </util:map>

    <bean id="realmA" class="org.apache.cxf.sts.token.realm.SAMLRealm">
        <property name="issuer" value="A-Issuer" />
        <property name="signatureAlias" value="myclientkey" />
    </bean>

    <bean id="realmC" class="org.apache.cxf.sts.token.realm.SAMLRealm">
        <property name="issuer" value="C-Issuer" />
        <property name="signatureAlias" value="myservicekey" />
    </bean>

    <!-- List of Web service endpoints that can use this STS -->
    <bean id="transportService" class="org.apache.cxf.sts.service.StaticService">
        <property name="endpoints" ref="transportEndpoints" />
    </bean>

    <util:list id="transportEndpoints">
        <value>https://localhost:(\d)*/doubleit/services/doubleitrealm.*
        </value>
    </util:list>
    ...
</beans>
As usual, the TokenIssueOperation is configured with a SAML token provider, but this SAML token provider is also configured with a realm map (through the realmMap property). The SAML token provider uses the realm map to retrieve the extra data that it needs to generate and sign a SAML token in each of the supported realms (see Section 9.5.1, “Issuing Tokens in Multiple Realms”).

Validate configuration for realms A and C

For realms A and C, the TokenValidateOperation instance is configured as follows:
<beans ... >
    ...
    <bean id="transportValidateDelegate" class="org.apache.cxf.sts.operation.TokenValidateOperation">
        <property name="tokenProviders" ref="transportTokenProviders" />
        <property name="tokenValidators" ref="transportTokenValidators" />
        <property name="stsProperties" ref="transportSTSProperties" />
    </bean>

    <util:list id="transportTokenProviders">
        <ref bean="transportSAMLProvider" />
    </util:list>
    ...
    <util:list id="transportTokenValidators">
        <ref bean="transportSAMLValidator" />
    </util:list>

    <bean id="transportSAMLValidator"
          class="org.apache.cxf.sts.token.validator.SAMLTokenValidator">
    </bean>
    ...
</beans>
Notice how both a list of token validators and token providers is set on the TokenValidateOperation instance. The token provider list is needed in case the STS is asked to issue a new token, in the context of token transformation (see Section 9.5.3, “Token Transformation across Realms”).

STS properties for realms A and C

The STS properties instance encapsulates some general-purpose configuration settings that are used by various components of the STS. The STS properties instance for realms A and C is configured as follows:
<beans ... >
    ...
    <bean id="transportSTSProperties" class="org.apache.cxf.sts.StaticSTSProperties">
        <property name="signaturePropertiesFile"
            value="org/apache/cxf/systest/sts/realms/stsKeystoreRealms.properties" />
        <property name="signatureUsername" value="mystskey" />
        <property name="callbackHandlerClass"
            value="org.apache.cxf.systest.sts.common.CommonCallbackHandler" />
        <property name="realmParser" ref="customRealmParser" />
        <property name="issuer" value="saml1-issuer" />
    </bean>

    <bean id="customRealmParser"
          class="org.apache.cxf.systest.sts.realms.URLRealmParser" />
    ...
</beans>
Note in particular that the realmParser property is initialized with an instance of the URLRealmParser class, whose implementation is shown in Example 9.5, “Demonstration RealmParser Implementation”. The realm parser figures out the current realm by examining the message context.

Second STS server for B realm

Figure 9.14, “STS Server for B realm” shows how the second STS server is configured for the B realm.

Figure 9.14. STS Server for B realm

The second STS server supports just realm B, and is configured to support a token transformation scenario.

STS for realm B

The STS for realm B is configured as follows:

STS configuration for realm B

The WS endpoint of the STS for realm B is configured as follows in the STS's Spring XML file:
<beans ... >
    
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
    
    <cxf:bus>
        <cxf:features>
            <cxf:logging/>
        </cxf:features>
    </cxf:bus>

    <bean id="transportSTSProviderBean"
        class="org.apache.cxf.ws.security.sts.provider.SecurityTokenServiceProvider">
        <property name="issueOperation" ref="transportIssueDelegate" />
        <property name="validateOperation" ref="transportValidateDelegate" />
    </bean>
    ...
    <jaxws:endpoint id="RealmBSTS" implementor="#transportSTSProviderBean"
        address="https://localhost:${testutil.ports.STSServer}/SecurityTokenService/realmB"
        ...
    </jaxws:endpoint>

    <httpj:engine-factory id="ClientAuthHttpsSettings"
        bus="cxf">
        <httpj:engine port="${testutil.ports.STSServer}">
            <httpj:tlsServerParameters>
                ...
                <sec:clientAuthentication want="true"
                    required="true" />
            </httpj:tlsServerParameters>
        </httpj:engine>
    </httpj:engine-factory>
   
</beans>
Note, in particular that the STS embeds the name of the realm, realmB, in the WS endpoint address URL. The endpoint URL that the client connects to, determines the realm in which the token is issued (see Example 9.5, “Demonstration RealmParser Implementation”).

Issue configuration for realm B

For realm B, the TokenIssueOperation instance is configured as follows:
<beans ... >
    ...
    <bean id="transportIssueDelegate" class="org.apache.cxf.sts.operation.TokenIssueOperation">
        <property name="tokenProviders" ref="transportTokenProviders" />
        <property name="services" ref="transportService" />
        <property name="stsProperties" ref="transportSTSProperties" />
    </bean>

    <util:list id="transportTokenProviders">
        <ref bean="transportSAMLProvider" />
    </util:list>

    <bean id="transportSAMLProvider"
          class="org.apache.cxf.sts.token.provider.SAMLTokenProvider">
        <property name="realmMap" ref="realms" />
    </bean>

    <util:map id="realms">
        <entry key="B" value-ref="realmB" />
    </util:map>

    <bean id="realmB" class="org.apache.cxf.sts.token.realm.SAMLRealm">
        <property name="issuer" value="B-Issuer" />
    </bean>

    <!-- List of Web service endpoints that can use this STS -->
    <bean id="transportService" class="org.apache.cxf.sts.service.StaticService">
        <property name="endpoints" ref="transportEndpoints" />
    </bean>

    <util:list id="transportEndpoints">
        <value>https://localhost:(\d)*/doubleit/services/doubleitrealm.*
        </value>
    </util:list>
    ...
</beans>
As usual, the TokenIssueOperation is configured with a SAML token provider, but this SAML token provider is also configured with a realm map (through the realmMap property). The SAML token provider uses the realm map to retrieve the extra data that it needs to generate and sign a SAML token in each of the supported realms (see Section 9.5.1, “Issuing Tokens in Multiple Realms”).

Validate configuration for realm B

For realm B, the TokenValidateOperation instance is configured as follows:
<beans ... >
    ...
    <bean id="transportValidateDelegate" class="org.apache.cxf.sts.operation.TokenValidateOperation">
        <property name="tokenProviders" ref="transportTokenProviders" />
        <property name="tokenValidators" ref="transportTokenValidators" />
        <property name="stsProperties" ref="transportSTSProperties" />
    </bean>

    <util:list id="transportTokenProviders">
        <ref bean="transportSAMLProvider" />
    </util:list>
    ...
    <util:list id="transportTokenValidators">
        <ref bean="transportSAMLValidator" />
    </util:list>

    <bean id="transportSAMLValidator" class="org.apache.cxf.sts.token.validator.SAMLTokenValidator">
        <property name="subjectConstraints" ref="subjectConstraintList" />
        <property name="samlRealmCodec" ref="customSAMLRealmCodec" />
    </bean>

    <util:list id="subjectConstraintList">
        <value>.*CN=www.client.com.*</value>
        <value>.*CN=www.sts.com.*</value>
    </util:list>

    <bean id="customSAMLRealmCodec"
          class="org.apache.cxf.systest.sts.realms.IssuerSAMLRealmCodec" />
    ...
</beans>
In one of the test scenarios, the STS for realm B is expected to validate a token that was issued in a different realm. For this reason, the SAML token validator initializes the samlRealmCodec property with a reference to the SAML realm codec implementation, IssuerSAMLRealmCodec. The SAML realm codec parses the received token in order to discover what realm it was originally issued in. See Example 9.6, “Demonstration SAMLRealmCodec Implementation”.

STS properties for realm B

The STS properties instance encapsulates some general-purpose configuration settings that are used by various components of the STS. The STS properties instance for realm B is configured as follows:
<beans ... >
    ...
    <bean id="transportSTSProperties" class="org.apache.cxf.sts.StaticSTSProperties">
        <property name="signaturePropertiesFile" value="stsKeystore.properties" />
        <property name="signatureUsername" value="mystskey" />
        <property name="callbackHandlerClass"
            value="org.apache.cxf.systest.sts.common.CommonCallbackHandler" />
        <property name="issuer" value="saml2-issuer" />
        <property name="identityMapper" ref="customIdentityMapper" />
        <property name="realmParser" ref="customRealmParser" />
    </bean>

    <bean id="customIdentityMapper"
          class="org.apache.cxf.systest.sts.realms.CustomIdentityMapper" />

    <bean id="customRealmParser"
          class="org.apache.cxf.systest.sts.realms.URLRealmParser" />
    ...
</beans>
In particular, the STS properties are configured with a realm parser (whose implementation is shown in Example 9.5, “Demonstration RealmParser Implementation”) and an identity mapper (whose implementation is shown in Example 9.7, “Demonstration IdentityMapper Implementation”).
The identity mapper is needed to support the token transformation scenario—see Section 9.5.3, “Token Transformation across Realms”.

Realm parser implementation

Example 9.5, “Demonstration RealmParser Implementation” shows the sample implementation of the realm parser. This implementation of the realm parser examines the address URL of the STS endpoint that the client sent its request to. The tail of the URL path determines the realm name. If no realm name is recognized, the parseRealm method returns null, to select the default realm (that is, the realm configured by default, by the STS properties instance).

Example 9.5. Demonstration RealmParser Implementation

// Java
package org.apache.cxf.systest.sts.realms;

import javax.xml.ws.WebServiceContext;

import org.apache.cxf.sts.RealmParser;
import org.apache.cxf.ws.security.sts.provider.STSException;

/**
 * A test implementation of RealmParser which returns a realm depending on a String contained
 * in the URL of the service request.
 */
public class URLRealmParser implements RealmParser {

    public String parseRealm(WebServiceContext context) throws STSException {
        String url = (String)context.getMessageContext().get("org.apache.cxf.request.url");
        if (url.contains("realmA")) {
            return "A";
        } else if (url.contains("realmB")) {
            return "B";
        } else if (url.contains("realmC")) {
            return "C";
        }
        
        return null;
    }
    
}

SAMLRealmCodec implementation

Example 9.6, “Demonstration SAMLRealmCodec Implementation” shows the sample implementation of the SAMLRealmCodec. The purpose of the codec is to determine the realm that originally issued the received token, by inspecting the contents of the token. In this implementation, it is assumed the SAML assertion's Issuer string uniquely identifies the issuing realm.

Example 9.6. Demonstration SAMLRealmCodec Implementation

// Java
package org.apache.cxf.systest.sts.realms;

import org.apache.cxf.sts.token.realm.SAMLRealmCodec;
import org.apache.ws.security.saml.ext.AssertionWrapper;


/**
 * This class returns a realm associated with a SAML Assertion depending on the issuer.
 */
public class IssuerSAMLRealmCodec implements SAMLRealmCodec {
    
    /**
     * Get the realm associated with the AssertionWrapper parameter
     * @param assertion a SAML Assertion wrapper object
     * @return the realm associated with the AssertionWrapper parameter
     */
    public String getRealmFromToken(AssertionWrapper assertion) {
        if ("A-Issuer".equals(assertion.getIssuerString())) {
            return "A";
        } else if ("B-Issuer".equals(assertion.getIssuerString())) {
            return "B";
        }
        return null;
    }
    
}

IdentityMapper implementation

Example 9.7, “Demonstration IdentityMapper Implementation” shows the sample implementation of the identity mapper. The purpose of the identity mapper is to map the principal name from the source realm to the corresponding principal name in the target realm, in the context of a token transformation scenario.

Example 9.7. Demonstration IdentityMapper Implementation

// Java
package org.apache.cxf.systest.sts.realms;

import java.security.Principal;

import org.apache.cxf.sts.IdentityMapper;
import org.apache.ws.security.CustomTokenPrincipal;

/**
 * A test implementation of RealmParser.
 */
public class CustomIdentityMapper implements IdentityMapper {

    /**
     * Map a principal in the source realm to the target realm
     * @param sourceRealm the source realm of the Principal
     * @param sourcePrincipal the principal in the source realm
     * @param targetRealm the target realm of the Principal
     * @return the principal in the target realm
     */
    public Principal mapPrincipal(String sourceRealm, Principal sourcePrincipal, String targetRealm) {
        if ("A".equals(sourceRealm) && "B".equals(targetRealm)) {
            return new CustomTokenPrincipal("B-Principal");
        } else if ("B".equals(sourceRealm) && "A".equals(targetRealm)) {
            return new CustomTokenPrincipal("A-Principal");
        }
        return null;
    }

}