A previous blog post detailed the AbstractOperation class that provides some core functionality for the STS operations, such as parsing the client request into a format that the TokenProviders/TokenValidators/TokenCancellers can consume. In this post we will examine an extension of AbstractOperation that is used to validate tokens.1) The TokenValidateOperation
TokenValidateOperation is used to validate tokens in the STS. It implements the ValidateOperation interface in the STS provider framework. In addition to the properties that it inherits from AbstractOperation (detailed previously), it has a single property that can be configured:
- List<TokenValidator> tokenValidators - A list of TokenValidator implementations to use to validate tokens.
Recall that AbstractOperation uses the RequestParser to parse a client request into TokenRequirements and KeyRequirements objects. TokenValidateOperation first checks that a "ValidateTarget" token was received and successfully parsed (if so it will be stored in the TokenRequirements object). If no token was received then an exception is thrown.2) Token validation and response
TokenValidateOperation then populates a TokenValidatorParameters object with values extracted from the TokenRequirements and KeyRequirements objects. It iterates through the list of defined TokenValidator implementations to see if any "can handle" the received token. If no TokenValidator is defined, or if no TokenValidator can handle the received token, then an exception is thrown. Otherwise, the received token is validated. The TokenValidateOperation then checks to see whether token transformation is required.2.1) Token Transformation
If the received token is successfully validated, and if the client supplies a TokenType in the request that does not correspond to the WS-Trust "status" namespace, then the TokenValidateOperation attempts to transform the validated token into a token of the requested type. Token transformation works in a similar way to token issuing, as detailed previously. A TokenProviderParameters object is constructed and the same processing steps (Realm parsing, AppliesTo parsing) are followed as for token issuing.
One additional processing step occurs before the token is transformed. If the TokenValidatorResponse object has a principal that was set by the TokenValidator implementation, then it is set as the principal of the TokenProviderParameters object. However, it is possible that the token is being issued in a different realm to that of the validated token, and the principal might also need to be transformed. Recall that the STSPropertiesMBean configuration object defined on AbstractOperation has an IdentityMapper property. This interface is used to map identities across realms. It has a single method:
- Principal mapPrincipal(String sourceRealm, Principal sourcePrincipal, String targetRealm) - Map a principal from a source realm to a target realm
If the source realm is not null (the realm of the validated token as returned in TokenValidatorResponse), and if it does not equal the target realm (as set by the RealmParser), then the identity mapper is used to map the principal to the target realm and this is stored in TokenProviderParameters for use in token generation. After the (optional) identity mapping step, TokenValidateOperation iterates through the TokenProvider list to find an implementation that can "handle" the desired token type in the given (target) realm (if applicable). If no TokenProvider is defined, or if no TokenProvider can handle the desired token type, then an exception is thrown.2.2) Token response
After token validation has been performed, and after any optional token transformation steps, a response object is constructed containing the following items:
- The context attribute (if any was specified).
- The received Token Type (if any was specified, or the "status" token type if validation was successful).
- Whether the received token was valid or not (status code & reason).
- If the received token was valid, and if token transformation successfully occurred:
- The transformed token.
- The lifetime of the transformed token.
- A number of references to that token (can be disabled by configuration).
Finally, it's time to look at an example of how to spring-load the STS so that it can validate tokens. This particular example uses a security policy that requires a UsernameToken over the transport binding (client auth is disabled). As the STS is a web service, we first define an endpoint:
<jaxws:endpoint id="transportSTS" implementor="#transportSTSProviderBean" address="http://.../SecurityTokenService/Transport" wsdlLocation=".../ws-trust-1.4-service.wsdl" xmlns:ns1="http://docs.oasis-open.org/ws-sx/ws-trust/200512/" serviceName="ns1:SecurityTokenService" endpointName="ns1:Transport_Port"> <jaxws:properties> <entry key="ws-security.callback-handler" value="..."/> </jaxws:properties> </jaxws:endpoint>
The CallbackHandler JAX-WS property is used to validate the UsernameToken. The "implementor" of the jaxws:endpoint is the SecurityTokenServiceProvider class defined in the STS provider framework:
<bean id="transportSTSProviderBean" class="org.apache.cxf.ws.security.sts.provider.SecurityTokenServiceProvider"> ... <property name="validateOperation" ref="transportValidateDelegate"/> </bean>
This bean supports the Validate Operation via a TokenValidateOperation instance:
<bean id="transportValidateDelegate" class="org.apache.cxf.sts.operation.TokenValidateOperation"> <property name="tokenValidators" ref="transportTokenValidators"/> <property name="stsProperties" ref="transportSTSProperties"/> </bean>
This TokenValidateOperation instance has a number of different TokenValidator instances configured:
<util:list id="transportTokenValidators"> <ref bean="transportSamlTokenValidator"/> <ref bean="transportX509TokenValidator"/> <ref bean="transportUsernameTokenValidator"/> </util:list> <bean id="transportX509TokenValidator" class="org.apache.cxf.sts.token.validator.X509TokenValidator"/> <bean id="transportUsernameTokenValidator" class="org.apache.cxf.sts.token.validator.UsernameTokenValidator"/> <bean id="transportSamlTokenValidator" class="org.apache.cxf.sts.token.validator.SAMLTokenValidator"/> </bean>
Finally the STSPropertiesMBean object that is used is given as follows:
<bean id="transportSTSProperties" class="org.apache.cxf.sts.StaticSTSProperties"> <property name="signaturePropertiesFile" value="..."/> <property name="signatureUsername" value="mystskey"/> <property name="callbackHandlerClass" value="..."/> <property name="encryptionPropertiesFile" value="..."/> <property name="issuer" value="DoubleItSTSIssuer"/> <property name="encryptionUsername" value="myservicekey"/> </bean>