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:
- 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'sparseRealm
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. - The
TokenIssueOperation
instance then calls thecanHandleToken
method on each of the registered token providers. In this example, only theSAMLTokenProvider
token provider is registered. ThecanHandleToken
method parameters include the token type and the realm name. - 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 theSAMLTokenProvider
finds the realm name in its map, it returnstrue
from thecanHandleToken
method. - The
TokenIssueOperation
instance now calls thecreateToken
method on theSAMLTokenProvider
instance, in order to issue the token in the specified realm. - The
SAMLTokenProvider
looks up the specified realm in the realm map and retrieves the correspondingSAMLRealm
instance. TheSAMLRealm
instance encapsulates the data that is specific to this realm.For example, if the specified realm isA
, theSAMLRealm
instance records that the corresponding issuer name isA-Issuer
and the alias of the signing key to use for this realm isStsKeyA
. - 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 isA
, theSAMLTokenProvider
embeds theA-Issuer
string in the SAML token's issuer element and the SAML token is signed using theStsKeyA
private key from thestsstore.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:
- 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.NoteThe 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”.
- The
TokenValidateOperation
instance then calls thecanHandleToken
method on each of the registered token validators. In this example, only theSAMLTokenValidator
token validator is registered. ThecanHandleToken
method parameters include the token type and the realm name.NoteThe defaultSAMLTokenValidator
class ignores the realm parameter in thecanHandleToken
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. - The
TokenValidateOperation
instance then calls thevalidateToken
method on theSAMLTokenValidator
, in order to validate the token in the specified realm. - 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 thesignaturePropertiesFile
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, theSAMLTokenValidator
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
, andC
, you must store the corresponding certificates (public part of the signature keys),StsKeyA
,StsKeyB
, andStsKeyC
, in thestsstore.jks
Java keystore file. - 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, theSAMLTokenValidator
calls out to the customSAMLRealmCodec
instance. TheSAMLRealmCodec
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="ws-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="ws-security.username" value="alice"/> <entry key="ws-security.callback-handler" value="org.apache.cxf.systest.sts.common.CommonCallbackHandler"/> <entry key="ws-security.sts.token.username" value="myclientkey"/> <entry key="ws-security.sts.token.properties" value="clientKeystore.properties"/> <entry key="ws-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. ws-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 theSTSClient
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 theSTSClient
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:
- 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”).
- 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). - 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.
- 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 anIdentityMapper
instance is configured on the STS properties object. - If an
IdentityMapper
is configured, the STS transforms the validated token's principal by calling themapPrincipal
method on theIdentityMapper
. The mapped identity will now be used as the transformed token's principal.NoteIn the context of SAML tokens, the principal corresponds to the value of theSubject/NameID
element in the SAML token. - 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.
- 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; } }