As the entry point to an organization's services, XML Gateways are responsible for controlling access to these services. This typically involves authentication and authorization against a user directory (LDAP). However, it is often the case that Identity and Access Management (IAM) is handled by some 3rd party access management product. One compelling reason for this is so that access control to both Web and Web services traffic is handled in one centrally administered location.
One popular IAM product is OpenSSO, formerly called Sun Java System Access Manager (JSAM). With JSAM, the only way to integrate was to write custom code using its Java API. Layer 7 has had a working integration of this kind for years. For more information about this method a comprehensive overview by our European Technical Director, Francois Lascelles can be found on the Sun Developer Network here: http://developers.sun.com/identity/reference/techart/xmlgatewayopensso.html
However, as part of the release of version 8.0, OpenSSO has exposed a number of its features as both REST and SOAP services called Identity Services. In this tutorial we'll show how integration between Layer 7 gateways and OpenSSO's IAM layer is even easier and more flexible using these newly available services.
A typical idiom when interacting with any IAM product involves two steps:
The goal for our policy is to follow this idiom using OpenSSO as described by the above flow chart. We will not cover OpenSSO user and resource configuration here as there is plenty of documentation for this already available.
The complete, working policy will look something like this:
Lines 3-7 set up variables that will be used in the event of an error and a SOAP fault needs to be returned. The four soapFault* context variables can be updated throughout the policy (lines 22 & 31) depending on the current step being executed so that the fault information returned is specific to the step that generated the error.
Line 9 simply creates a variable to hold the name of the cookie issued/used by OpenSSO. This variable is used in various places throughout our policy so in the event the name of the cookie changes we just need to update this single variable.
Lines 10-13 show the types of credentials that are allowed. For this service we require that client requests include one of: HTTP Cookie, HTTP Basic or WS-Security Username Token Basic credentials, but we could add any credential type that requires a username & password in clear text.
At this point we branch our policy based on whether or not the request came in with an OpenSSO cookie. (Note that we could have branched our policy earlier by inserting the Require HTTP Cookie assertion between lines 15 & 16 and the Require HTTP Basic and Require WS-Security UsernameToken Profile assertions inside an OR statement between lines 20 & 21. However, I find that separating the credential gathering from subsequent processing easier to understand and follow.)
Lines 15-19 are executed if the incoming request contained a cookie. The only thing of note here is that we use a Regular Expression to extract only the cookie value, omitting any cookie attributes (eg. Path, domain, etc..) that may also be included.
This value is then saved to a variable called ssoToken for later use.
Lines 20 – 28 are executed if no cookie was included in the request. In this case we must call OpenSSO's authenticate method. To build this request message we can use the Set Context Variable assertion and populate it with a template request (generated using SoapUI).
Once we have our template request, we simply populate the required values with context variables. The request.username and request.password variables are built-in variables available anytime an access control assertion that requires clear text credentials is present in policy.
Our authentication request can then be routed to the IdentityServices endpoint (Line 24). Be sure to change the Requested message source in the Request HTTP Rules tab of the routing assertion to route the authnReq message variable as opposed to the default request. Similarly, I've changed the Response message destination from the default response to authnResp.
Once we receive the response message, we'll evaluate it using XPath. We extract the SSO token out of the response message and save it using the same variable name as above, ssoToken.
At this point we've successfully authenticated the inbound credentials, either by using the cookie if one was present, or by authenticating directly against OpenSSO. Our final step is to authorize these credentials against a particular resource. This is what we do in lines 29-33.
We build our authorization request by once again using the Set Context Variable assertion and populating it with a template request.
We use the ssoToken variable we obtained earlier in policy as the subject id and another built in variable called service.defaultRoutingURL as the resource. The value of the resource must match a resource that has been configured in OpenSSO.
Once we have a complete request message we call the authorize method. Once again, but sure to modify the Request message source and Response message destination in the routing assertion to use message variables other than the default request and response. (eg. authzReq & authzResp).
We will evaluate the response using XPath again. The response to this call is quite simple. It contains a return element that's either true or false. If the response returns true, the credentials have successfully been authorized for the desired resource. In this case we simply route the Web service request to the back end for processing. In the event these credentials are not authorized a SOAP fault will be returned with the detail configured on line 31.
In the Response HTTP Rules for the routing assertion that routes the original Web service request, note that we return the OpenSSO cookie in the response back to the caller so that it can be used for subsequent requests.
We now have a simple and flexible way to integrate with OpenSSO's IAM layer from the Layer 7 Gateway directly from within policy.