9.5. Enabling Realms in the STS
9.5.1. Issuing Tokens in Multiple Realms
Overview
Figure 9.11. Realm-Aware SAML Token Issuer
Realm aware token issuing steps
- 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
RealmParserinterface. The realm parser'sparseRealmmethod 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
TokenIssueOperationinstance then calls thecanHandleTokenmethod on each of the registered token providers. In this example, only theSAMLTokenProvidertoken provider is registered. ThecanHandleTokenmethod 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
SAMLTokenProviderlooks up the realm name in its realm map to make sure that it can handle this realm. If theSAMLTokenProviderfinds the realm name in its map, it returnstruefrom thecanHandleTokenmethod. - The
TokenIssueOperationinstance now calls thecreateTokenmethod on theSAMLTokenProviderinstance, in order to issue the token in the specified realm. - The
SAMLTokenProviderlooks up the specified realm in the realm map and retrieves the correspondingSAMLRealminstance. TheSAMLRealminstance encapsulates the data that is specific to this realm.For example, if the specified realm isA, theSAMLRealminstance records that the corresponding issuer name isA-Issuerand the alias of the signing key to use for this realm isStsKeyA. - The
SAMLTokenProvidernow 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, theSAMLTokenProviderembeds theA-Issuerstring in the SAML token's issuer element and the SAML token is signed using theStsKeyAprivate key from thestsstore.jksJava keystore file.
Configuring the realm parser
RealmParser interface and then register your custom realm parser by injecting it into the realmParser property of the STS properties bean.
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
RealmParser interface:
public String parseRealm(WebServiceContext context) throws STSException;
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.
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;
}
}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
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
Realm aware token validating steps
- 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
TokenValidateOperationinstance then calls thecanHandleTokenmethod on each of the registered token validators. In this example, only theSAMLTokenValidatortoken validator is registered. ThecanHandleTokenmethod parameters include the token type and the realm name.NoteThe defaultSAMLTokenValidatorclass ignores the realm parameter in thecanHandleTokenmethod, 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
TokenValidateOperationinstance then calls thevalidateTokenmethod on theSAMLTokenValidator, in order to validate the token in the specified realm. - The
SAMLTokenValidatorattempts 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 thesignaturePropertiesFileproperty 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, theSAMLTokenValidatorwill 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.jksJava keystore file. - In case the client needs the information, the
SAMLTokenValidatoralso 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, theSAMLTokenValidatorcalls out to the customSAMLRealmCodecinstance. TheSAMLRealmCodecinstance 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
Validating tokens across realms
Response from Validate operation
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
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
public String getRealmFromToken(AssertionWrapper assertion)
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.
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
Triggering token transformation
<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>jaxws:endpoint element are of key importance in configuring token transformation:
ws-security.saml2.validator- By initializing this property with an instance of the
STSTokenValidatorclass, 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
STSClientinstance, which encapsulates all of the settings required to connect to the STS. The properties you can set on theSTSClientinstance 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
tokenTypeproperty on theSTSClientinstance. 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
Transformation algorithm
- 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
TokenTypehas 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 anIdentityMapperinstance is configured on the STS properties object. - If an
IdentityMapperis configured, the STS transforms the validated token's principal by calling themapPrincipalmethod 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/NameIDelement 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
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>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
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;
}
}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
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
Demonstration location
CXFInstallDir/services/sts/systests/advanced/src/test/java/org/apache/cxf/systest/sts/realms
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
STS for realms A and C
STS endpoint configuration for realms A and C
<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>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
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>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
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>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
<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>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
STS for realm B
STS configuration for realm B
<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>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
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>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
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>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
<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>Realm parser implementation
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
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
// 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;
}
}
Where did the comment section go?
Red Hat's documentation publication system recently went through an upgrade to enable speedier, more mobile-friendly content. We decided to re-evaluate our commenting platform to ensure that it meets your expectations and serves as an optimal feedback mechanism. During this redesign, we invite your input on providing feedback on Red Hat documentation via the discussion platform.