The 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 issue tokens.
1) The TokenIssueOperation
TokenIssueOperation is used to issue tokens in the STS. It implements the IssueOperation and IssueSingleOperation interfaces in the STS provider framework. In addition to the properties that it inherits from AbstractOperation (detailed in the previous post), it has a single property that can be configured:
- ClaimsManager claimsManager - An object that is used to handle claims. This will be covered in a future post.
Recall that AbstractOperation uses the RequestParser to parse a client request into TokenRequirements and KeyRequirements objects. TokenIssueOperation populates a TokenProviderParameters object with values extracted from the TokenRequirements and KeyRequirements objects. A number of different processing steps then occur before a TokenProvider implementation is used to retrieve the desired token, comprising of realm parsing, claims handling, and AppliesTo parsing. Claims handling will be discussed in the next article.
1.1) Realm Parsing
We have discussed how realms are used with TokenProviders to provide tokens, and also how they work with TokenValidators to validate a given token. However, we did not cover how realms are defined in the first place. Recall that the STSPropertiesMBean configuration object defined on AbstractOperation has a RealmParser property. The RealmParser is an interface which defines a pluggable way of defining a realm for the current request. It has a single method:
- String parseRealm(WebServiceContext context) - Return the realm of the current request given a WebServiceContext object.
Therefore if you wish to issue tokens in multiple realms, it is necessary to create an implementation of the RequestParser which will return a realm String given a context object. For example, different realms could be returned based on the endpoint URL or a HTTP parameter. This realm will then get used to select a TokenProvider implementation to use to issue a token of the desired type. It will also be used for token validation in a similar way.
1.2) AppliesTo parsing
An AppliesTo element contains an address that refers to the recipient of the issued token. If an AppliesTo element was sent as part of the request then the CXF STS requires that it must be explicitly handled. This is done by the list of ServiceMBean objects that can be configured on AbstractOperation. The ServiceMBean interface represents a service, and has the following methods (amongst others):
- boolean isAddressInEndpoints(String address) - Return true if the supplied address corresponds to a known address for this service.
- void setEndpoints(List<String> endpoints) - Set the list of endpoint addresses that correspond to this service.
The STS ships with a single implementation of this interface, the StaticService. For the normal use-case of handling an AppliesTo element, the user creates a StaticService object and calls setEndpoints with a set of Strings that correspond to a list of regular expressions that match the allowable set of token recipients (by address). The TokenIssueOperation will extract the URL address from the EndpointReference child of the received AppliesTo element, and then iterate through the list of ServiceMBean objects and ask each one whether the given address is known to that ServiceMBean object. If an AppliesTo address is received, and no ServiceMBean is configured that can deal with that URL, then an exception is thrown.
The ServiceMBean also defines a number of optional configuration options, such as the default KeyType and TokenType Strings to use for that Service, if the client does not supply them. It also allows the user to set a custom EncryptionProperties object, which defines a set of acceptable encryption algorithms to use to encrypt issued tokens for that service.
2) Token creation and response
Once the TokenIssuerOperation has processed the client request, it iterates through the list of defined TokenProvider implementations to see if each "can handle" the desired token type in the configured realm (if any). If no TokenProvider is defined, or if no TokenProvider can handle the desired token type, then an exception is thrown. Otherwise, a token is created, and a response object is constructed containing the following items:
- The context attribute (if any was specified).
- The Token Type.
- The requested token (possibly encrypted, depending on configuration).
- A number of references to that token (can be disabled by configuration).
- The received AppliesTo address (if any).
- The RequestedProofToken (if a Computed Key Algorithm was used).
- The Entropy generated by the STS (if any, can be encrypted).
- The lifetime of the generated token.
- The KeySize that was used (if any).
3) TokenIssueOperation Example
Finally, it's time to look at an example of how to spring-load the STS so that it can issue tokens. This particular example uses a security policy that requires a UsernameToken over the symmetric binding. As the STS is a web service, we first define an endpoint:
<jaxws:endpoint id="UTSTS" implementor="#utSTSProviderBean" address="http://.../SecurityTokenService/UT" wsdlLocation=".../ws-trust-1.4-service.wsdl" xmlns:ns1="http://docs.oasis-open.org/ws-sx/ws-trust/200512/" serviceName="ns1:SecurityTokenService" endpointName="ns1:UT_Port"> <jaxws:properties> <entry key="ws-security.callback-handler" value="..."/> <entry key="ws-security.signature.properties" value="stsKeystore.properties"/> </jaxws:properties> </jaxws:endpoint>
The jaxws:properties are required to parse the incoming message. The CallbackHandler is used to validate the UsernameToken and provide the password required to access the private key defined in the signature properties parameter. The "implementor" of the jaxws:endpoint is the SecurityTokenServiceProvider class defined in the STS provider framework:
<bean id="utSTSProviderBean" class="org.apache.cxf.ws.security.sts.provider.SecurityTokenServiceProvider"> <property name="issueOperation" ref="utIssueDelegate"/> ... </bean>
This bean supports the Issue Operation via a TokenIssueOperation instance:
<bean id="utIssueDelegate" class="org.apache.cxf.sts.operation.TokenIssueOperation"> <property name="tokenProviders" ref="utSamlTokenProvider"/> <property name="services" ref="utService"/> <property name="stsProperties" ref="utSTSProperties"/> </bean>
This TokenIssueOperation instance has a single TokenProvider configured to issue SAML Tokens (with a default Subject and Attribute statement):
<bean id="utSamlTokenProvider" class="org.apache.cxf.sts.token.provider.SAMLTokenProvider"> </bean>
The TokenIssueOperation also refers to a single StaticService implementation, which in turn defines a single URL expression to use to compare any received AppliesTo addresses:
<bean id="utService" class="org.apache.cxf.sts.service.StaticService"> <property name="endpoints" ref="utEndpoints"/> </bean>
<util:list id="utEndpoints"> <value>http://localhost:(\d)*/(doubleit|metrowsp)/services/doubleit(UT|.*symmetric.*|.*)</value> </util:list>
Finally, the TokenIssueOperation is configured with a StaticSTSProperties object. This class contains properties that define what private key to use to sign issued SAML tokens, as well as the Issuer name to use in the generated token.
<bean id="utSTSProperties" class="org.apache.cxf.sts.StaticSTSProperties"> <property name="signaturePropertiesFile" value="stsKeystore.properties"/> <property name="signatureUsername" value="mystskey"/> <property name="callbackHandlerClass" value="..."/> <property name="issuer" value="DoubleItSTSIssuer"/> ... </bean>