Monday, September 27, 2010

Cross Posted to DZone

The SOA Platform - JBossESB - SAML post was just cross posted on DZone:

http://java.dzone.com/articles/asserting-security-jbossesb

Monday, September 20, 2010

Asserting Security - The JBossESB Integration with SAML in the JBoss SOA Platform

Introduction - When Two User Accounts is One Too Many

Have you ever noticed how whenever you visit a new doctor, you have to fill out a new medical history, longhand and on paper? Sometimes you make a mistake or sometimes someone else makes a mistake transcribing the information. Why can't the information be shared in a secure way? The same type of problem exists with computer systems, web applications, and passwords. If you frequently access multiple remote systems or applications, you invariably have to manage multiple user accounts. (At one of my past jobs, I actually had 17 user accounts across several systems.  The killer was that the accounts all had different password complexity rules and expiration schedules.)

What's happening here is that you have authentication and authorization information in different security "domains". What's needed in our internet and web-based world, is a standard way to securely (and easily!) share user accounts between systems. In other words, we need a way to perform "single sign-on" (SSO) between multiple security domains. What you want to be able to do is to have the different applications and sites that you rely on, regardless of each application's implementation or operating environment, to be able to safely share your login information, without you having to login repeatedly, or maintain multiple different accounts.

One solution to this problem is the "Security Assertion Markup Language" (SAML).

The Standard: SAML

SAML was created by the Security Services Technical Committee of the Organization for the Advancement of Structured Information Standards (OASIS - http://www.oasis-open.org).

Wikipedia (http://en.wikipedia.org/wiki/SAML) defines SAML as: '...an XML-based standard for exchanging authentication and authorization data between security domains, that is, between an identity provider (a producer of assertions) and a service provider (a consumer of assertions)...'
Ref: Organization for the Advancement of Structured Information Standards (OASIS - http://www.oasis-open.org)

Let's dissect that deceptively short definition into smaller pieces. Each of these pieces covers one of SAML's important characteristics and the motivations for its creation.
  • XML based - One of the goals of the SAML is for it to be a standard that can be used by different applications and systems to replace proprietary security methods. If you're going to create a standard, then basing it on XML is a good start!
  • Exchanging data - The ultimate goal is of course to support the secure exchange of data between applications and systems and users. In the context of security, the applications, systems, and users are referred to as "principals.")
  • Authentication and Authorization - These two aspects of security are related, but refer to different tasks. Authentication refers to verifying the identity of the principle. Authorization refers to verifying that the principal is permitted to perform a specific action.
  • Security domains - "Federation" is an important concept to keep in mind when you talk about security domains. In the context of a political system, federation refers to the separating of overlapping of areas of control between the states that make up a greater federation. It may be that each state is able to make laws specific to the state, but they must also abide by the laws of the greater federation. (Ever hear of Fort Sumter? http://www.nps.gov/fosu/) It's like that when security domains are federated too. You might want to establish a large-scale domain for all your employees, so that they can all use the same login information to access multiple shared systems such as vacation trackers or a corporate intranet, while at the same time, you can establish smaller domains so that restricted groups of those employees can use that same login information to access purchasing or payroll information.
  • Identity Provider - In the context of using SAML, an identity provider supplies authentication information when and where requested by a principal. Note that this in contrast to security systems that rely on cookies stored and supplied by the client web browsers, or ticketing systems such as Kerberos where the client supplies a ticket to a service. In SAML, a Identity Providers are the producers of security "assertions."
  • Service Provider - In the context of using SAML, service providers rely on Identity Providers. When the principal requests its identity to be confirmed, an Identity Provider sends this information, in the form of a SAML sercurity assertion. So, in SAML, Service Providers are the consumers of the security assertions.
But, while it is a standard, it is not an implementation. For a SAML implemention, we need to look at JBoss PicketLink.

The Implementation: JBoss PicketLink

PicketLink (http://jboss.org/picketlink) is the JBoss Identity Management (IdM) project. PicketLink is actually built on the JBoss "PicketBox" (formerly known as "JBoss Security") framework. PicketBox (http://www.jboss.org/picketbox) provides developers with a framework where these security functions can be easily configured: authentication, authorization, auditing, and mapping (Principal to Roles and Attributes).

PicketLink is an "umbrella" project that pulls together multiple components to support various security and Identity Management requirements. As listed on the PicketLink project site, these components are:
  • AuthZ (a authorization framework)
  • Federated Identity (remember our discussion of federated security domains a minute ago?)
  • Identity Management (this provides a common framework for handing users and groups and supports using identity repositories such as LDAP)
  • Negotiation (this provides SPNego*/Kerberos based Desktop single sign-on)
  • XACML  (an implementation of the Oasis eXtensible Access Control Markup Language - this provides fine-grained authorization capabilities)
* Simple and Protected GSSAPI (Generic Security Services Application Programming Interface) Negotiation Mechanism - yes, that is a mouthful.



Security Token Service (STS)

The SAML integration with the JBossESB in the SOA Platform is supported by the PicketLink Security Token Service (PicketLinkSTS). PicketLink provides login modules (https://community.jboss.org/wiki/PicketLinkSTSLoginModules) two of which JBossESB uses.

The primary focus of the ESB integration is for PicketLink to provide security modules and SAML Token support for ESB services. (http://community.jboss.org/wiki/JBossESBSAMLSupport) The ESB service configures the org.picketlink.identity.federation.core.wstrust.auth.STSIssuingLoginModule class to issue (produce) a SAML Security Token. 

The best way to look at this integration is to see it in action. So, we'll "learn by doing" by examining it in a SOA Platform "quickstart."

The Quickstart

I never get tired of saying that one of the great features of the SOA Platform is its extensive, and always growing, set of "quickstart" programs. These programs go far beyond being simple examples as they clearly illustrate various features supported by the Platform. They also serve as a great resource for writing your own applications. For our example, we'll look at the "security_saml" quickstart.

This quickstart illustrates the SOA Platform JBossESB's support for SAML support as provided by PicketLink's Security Token Service (PicketLinkSTS). As the quickstart's readme file states:

  The following (4) procedures will be demonstrated by this quickstart:
  1. Using the PicketLink Project's STSIssuingLoginModule to issue a SAML Assertion from PicketLinkSTS, then
  2. Using the PicketLink Project's STSValidatingLoginModule to validate a SAML Assertion from PicketLinkSTS, then
  3. Injecting the SAML Assertion into a SOAP Message, then
  4. Using SOAPProcessor to invoke an external Web Service that is secured by PicketLinkSTS.
Before we walk through the actions that quickstart performs, let's examine the configuration and binary files that control these procedures. Because the quickstart has to handle the issuing and validation of the
SAML Assertion in addition to actually configuring the quickstart's web service and sending a message to that service, there are a larger number of configuration, execution, and test data related XML files than are used in some other quickstarts.

But, hey, don't panic! We'll examine the files in detail and describe how they are used and also, how they all tie together. Let's divide and conquer and examine these files in the following logical groups:

Group #1 - PicketLinkSTS related files
  • picketlink-sts-client.properties
  • login-config-unfiltered.xml
  • jboss-service.xml
  • jboss-wsse-client.xml
 Group #2 - Test data related files
  • soap-request.xml
  • smooks-saml-injector-template.xml
  • |smooks-saml-injector.xml
Group #3 - ESB archive related files 
  • deployment.xm
  • jbm-queue-service.xml
  • jboss-esb.xml
  • jbossesb-properties.xml
NOTE: We'll hop around a bit between these groups of files, depending on the relationships between the files.

Group #1 - PicketLinkSTS related files

Let's start with the PicketLink's Security Token Service related files.

The picketlink-sts-client.properties file defines the STS configuration for the STSIssuingLoginModule and STSValidatingLoginModule.

1:  serviceName=PicketLinkSTS  
2:  portName=PicketLinkSTSPort  
3:  endpointAddress=http://localhost:8080/picketlink-sts/PicketLinkSTS  
4:  username=admin  
5:  password=admin  

  • Line 1 - The service name. Make a mental note of this as we'll come back to this in a minute.
  • Line 3 - The endpoint of service. This will be active once we deploy the quickstart.
  • Line 4-5 - This username and password are actually only used by the STSValidatingLoginModule.  In contrast, the STSIssuingLoginModule uses callbacks to retrieve the username and password from the authentication request, which is extracted from the SOAP Security header. Where is the SOAP Security header defined? Read on.
Like most of the SOA Platform quickstarts, the security_saml quickstart initiates its actions by generating a message that then is sent over the Platform's JBossESB to selected services. The soap-request.xml file defines the SOAP request that is sent.

1:  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:good="http://security_saml/goodbyeworld" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">  
2:    <soapenv:Header>  
3:      <wsse:Security>  
4:     <wsse:UsernameToken>  
5:      <wsse:Username>admin</wsse:Username>  
6:      <wsse:Password>admin</wsse:Password>  
7:     </wsse:UsernameToken>  
8:    </wsse:Security>  
9:    </soapenv:Header>  
10:    <soapenv:Body>  
11:      <good:sayGoodbye>  
12:       <message>Goodbye!!</message>  
13:      </good:sayGoodbye>  
14:    </soapenv:Body>  
15:   </soapenv:Envelope>  

  • Lines 4-7 - These are the lines that we're interested in for this file. The "UsernameToken" security header information is used by the STSIssuingLoginModule as the username/credential for the user for whom a security token should be issued.
Speaking of the STSIssuingLoginModule, let's shift gears and look at the "quickstart-specific-assemblies" ant target in the build.xml file. This target is called when the quickstart is deployed.

1:   <target name="quickstart-specific-assemblies">  
2:     <!-- Overriden from the target of the same name in base-build.xml. -->  
3:     <!-- Called by the "deploy" target. Don't call directly!! -->  
4:    
5:     <!-- Build the webservice... -->  
6:       <war warfile="build/${ant.project.name}.war"  
7:         webxml="${basedir}/war/resources/WEB-INF/web.xml">  
8:         <fileset dir="${basedir}/war/view">  
9:           <include name="**/*"/>  
10:         </fileset>  
11:       </war>  
12:    
13:       <copy file="${basedir}/login-config-unfiltered.xml" tofile="${basedir}/login-config.xml" filtering="true" overwrite="true">  
14:               <filterset>  
15:                   <filter token="STS_CONFIG_FILE_PATH" value="${basedir}/picketlink-sts-client.properties"/>  
16:               </filterset>  
17:           </copy>  
18:     </target>  

  • Lines 6-11 create the .war file that is used to deploy the quickstart's target web service. We'll come back to this service in a bit.
  • For now, let's look at the element on lines 13-17. What's happening here is that the login-config-unfiltered.xml file is used as a template to create a login-config.xml file that will be used by the quickstart's services. The processing of the element replaces the "STS_CONFIG_FILE_PATH" string in login-config-unfiltered.xml with the value defined in line 44.
What's the point of doing this templating? After all, why not just edit the login-config.xml file directly? It's a safety measure and is typically used with ant to build a project so that you can easily and safely make changes to a project and perhaps several configuration files with one command. This is much safer than trying to remember all the places that you have to edit the same information.

In our case, this results in a login-config.xml file that looks like this:


1:  <policy>  
2:      <application-policy name="saml-issue-token">  
3:      <authentication>  
4:        <login-module code="org.picketlink.identity.federation.core.wstrust.auth.STSIssuingLoginModule" flag="required">  
5:          <module-option name="configFile">/jboss/local/502_CR3_signed/jboss-soa-p.5.0.0/jboss-as/samples/quickstarts/security_saml/picketlink-sts-client.properties</module-option>  
6:          <module-option name="endpointURI">http://security_saml/goodbyeworld</module-option>  
7:          <module-option name="password-stacking">true</module-option>  
8:        </login-module>  
9:        <login-module code="org.picketlink.identity.federation.core.wstrust.auth.STSValidatingLoginModule" flag="required">  
10:           <module-option name="configFile">/jboss/local/502_CR3_signed/jboss-soa-p.5.0.0/jboss-as/samples/quickstarts/security_saml/picketlink-sts-client.properties</module-option>  
11:           <module-option name="password-stacking">useFirstPass</module-option>  
12:         </login-module>  
13:       </authentication>  
14:     </application-policy>  
15:    
16:     <application-policy name="saml-validate-token">  
17:       <authentication>  
18:         <login-module code="org.picketlink.identity.federation.core.wstrust.auth.STSValidatingLoginModule" flag="required">  
19:           <module-option name="configFile">/jboss/local/502_CR3_signed/jboss-soa-p.5.0.0/jboss-as/samples/quickstarts/security_saml/picketlink-sts-client.properties</module-option>  
20:           <module-option name="useOptionsCredentials">true</module-option>  
21:         </login-module>  
22:       </authentication>  
23:     </application-policy>  
24:   </policy>  

What's happening in this file? We're defining security domains. Remember how we talked about the quickstart demonstrating how to either issue (produce) or validate (consume) a SAML Security Token.  Each has an applicaiton policy defined. For JBoss servers, security domains are configured with elements files named "login-config.xml."
  • Line 2 - Here's the start of the domain that is used to issue the SAML Security Token.
  • Line 4 - A reference to the STSIssuingLoginModule that we discussed earlier.
  • Line 5 - And here's a pointer to the picketlink-sts-client.properties file that we discussed a minute ago. (Yes, all these configuration files do tie together.)
  • Line 6 - The endpoint of the target web service is: http://security_saml/goodbyeworld - note that the endpointURI in STS properties is actually just the "domain" to which the security token will be applicable. Imagine you have two systems where access to one does not necessarily imply access to the other, yet they both want to use the same STS. You would look at the endpointURI to verify the token is for the relevant system. The system itself may include many web services that are secured as a collective.
  • Line 7 - Each login module gets a shared map. When the "password-stacking" option is set to "useFirstPass," with this option, the login module looks for username/pass in that shared map instead of using the callback handler.
  • Line 9 - And, here's the start of the domain that is used to validate the SAML Security Token.
  • Line 16 - And here's the start of the domain that is used to validate the SAML Security Token.
  • Line 20 - What's interesting to note here is the use of the "useOptionsCredentials" login module. Unlike "password-stacking," this option causes the login module to use credentials from its properties file. In the case of our quickstart, this is the picketlink-sts-client.properties file. In this context, "true" allows the login module to store the password in the shared map, in anticipation of its later use by another login module through "useFirstPass".
OK, our login credentials are happily defined. What's next? The security domains defined in login-config are static. They are read by the server when the server is started or an application is deployed. Let's look now at the jboss-service.xml file:

1:  <server>  
2:    <mbean code="org.jboss.security.auth.login.DynamicLoginConfig" name="org.jboss.esb.samples.quickstarts.security_saml:service=LoginModule">  
3:      <attribute name="AuthConfig">login-config.xml</attribute>  
4:      <depends optional-attribute-name="LoginConfigService">jboss.security:service=XMLLoginConfig</depends>  
5:      <depends optional-attribute-name="SecurityManagerService">jboss.security:service=JaasSecurityManager</depends>  
6:    </mbean>  
7:  </server>  
8:    

  • Line 2 - As its name indicates, org.jboss.security.auth.login.DynamicLoginConfig enables us to dynamically deploy a security domain. In the case of our quickstart, the MBean makes use of the XMLLoginConfig service (line 4) and the SecurityManagerService (line 5).
And the last of the security-related is the jboss-wsse-client.xml file. This file defines the JBoss WebService configuration that the quickstart client will use to access the web service. The configuration defined in this file will cover all requests sent by the client, and all responses received by the client. Note that this has really nothing to do with SAML/STS. The quickstart has configured the ESB service to support WS-Security UsernameToken, so that we can then use this user/password to authenticate against STS and request a token.

1:  <?xml version="1.0" encoding="UTF-8"?>  
2:  <jboss-ws-security xmlns="http://www.jboss.com/ws-security/config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
3:   xsi:schemaLocation="http://www.jboss.com/ws-security/config http://www.jboss.com/ws-security/schema/jboss-ws-security_1_0.xsd">  
4:   <config>  
5:     <username/>  
6:   </config>  
7:  </jboss-ws-security>  

  • Lines 4-6 - This config element indicates that all requests and responses must contain a username.
For more information on Web Service Security, see: http://docs.sun.com/source/819-0788/xwss2.html, http://community.jboss.org/wiki/WSSecurityConfig, http://community.jboss.org/wiki/jbossws-ws-securityoptions

Group #2 - Test data related files

We discussed the soap-request.xml a few minutes ago. Let's look at the smooks-saml-injector-template.xml and smooks-saml-injector.xml files now. These files are used to transform a message, to inject the SAML assertion into a SOAP message that is sent using WS Security.

As its name indicates, the smooks-saml-injector-template.xml file is a temlplate and is used by the smooks-saml-injector.xml file. Let's look at smooks-saml-injector.xml

1:   <?xml version="1.0"?>  
2:   <smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd">  
3:    
4:     <import file="smooks-saml-injector-template.xml">  
5:     <param name="selector">/Envelope/Header/Security</param>  
6:     <param name="selectorNamespace">http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd</param>  
7:     <param name="securityNamespace">http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd</param>  
8:     <param name="headerNamespace">http://schemas.xmlsoap.org/soap/envelope/</param>  
9:   </import>  
10:      
11:    <import file="smooks-saml-injector-template.xml">  
12:      <param name="selector">/Envelope/Header</param>  
13:      <param name="selectorNamespace">http://schemas.xmlsoap.org/soap/envelope/</param>  
14:      <param name="securityNamespace">http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd</param>  
15:      <param name="headerNamespace">http://schemas.xmlsoap.org/soap/envelope/</param>  
16:      <param name="addXmlNs">false</param>  
17:    </import>  
18:     
19:   </smooks-resource-list>  

What we see in this file are two separate elements. Each defines the selector, security and header namespaces that are used to define the security domain. But, what's the reason behind the second ? And why does it refer to http://schemas.xmlsoap.org/soap/envelope instead of http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd? The reason is that sometimes there is no need to inject the namespaces as the wsse:security may be qualified already. That's the reason for the second block.

Before we look at group #3 (the ESB related files), let's take a look at the web service and the web service client used by the quickstart .

The web service itself is pretty basic - it's defined in GoodbyeWorldWS.java


1:   package org.jboss.soa.esb.samples.quickstart.securitysaml.webservice;  
2:    
3:   import javax.jws.WebService;  
4:   import javax.jws.WebMethod;  
5:   import javax.jws.Oneway;  
6:   import javax.jws.WebParam;  
7:   import javax.jws.HandlerChain;  
8:   import javax.jws.soap.SOAPBinding;  
9:    
10:   import org.jboss.soa.esb.message.Message;  
11:   import org.jboss.soa.esb.message.Body;  
12:   import org.jboss.soa.esb.actions.ActionUtils;  
13:   import org.jboss.soa.esb.actions.soap.SOAPProcessor;  
14:    
15:   @HandlerChain(file="handlerchain.xml")  
16:   @WebService(name = "GoodbyeWorldWS", targetNamespace="http://security_saml/goodbyeworld")  
17:   public class GoodbyeWorldWS {  
18:    
19:     @WebMethod  
20:     public String sayGoodbye(@WebParam(name="message") String message) {  
21:       Message esbMessage = SOAPProcessor.getMessage();  
22:       System.out.println("Succesfully invoked WS!");  
23:       System.out.println("SOAPRequest perhaps mediated by ESB:\n" + esbMessage.getBody().get());  
24:       return "... Ah Goodbye then!!!! - " + message;  
25:     }  
26:    
27:     @WebMethod  
28:     public String sayAdios(String message) {  
29:       Message esbMessage = SOAPProcessor.getMessage();  
30:       if(esbMessage != null) {  
31:         System.out.println("**** SOAPRequest perhaps mediated by ESB:\n" + esbMessage.getBody().get());  
32:       }  
33:       System.out.println("Web Service Parameter - message=" + message);  
34:       return "... Adios Amigo!!!! - " + message;  
35:     }  
36:       
37:     @WebMethod  
38:     @Oneway  
39:     public void sayGoodbyeWithoutResponse(@WebParam(name="message") String message) {  
40:    
41:       Message esbMessage = SOAPProcessor.getMessage();  
42:       if(esbMessage != null) {  
43:         System.out.println("**** SOAPRequest perhaps mediated by ESB:\n" + esbMessage.getBody().get());  
44:       }  
45:       System.out.println("Web Service Parameter - message=" + message);  
46:     }  
47:       
48:   }  

If this program looks more like a POJO (a "plain old java object") with annotations than a web service, well, you're correct!

One of the features of the JBossESB in the SOA Platform is how it enables you to easily expose a web service's endpoints on the ESB. To do this, you write a "service wrapper webservice" that conforms to the JSR 181 ("Web Services Metadata for the Java Platform") standard. JSR 181 enables you to create webservices from a POJO by adding annotations such as @WebService and @WebMethod to it. This service wrapper webservice wraps calls to your service and exposes that service through JBossESB listeners. Your service can also be accessed through other transports (FTP, JMS, etc.) supported by the JBossESB in the SOA Platform. These 3 types of JBossWS webservice endpoints can be exposed through JBoss ESB listeners using this action:
  • Webservice bundled into a JBossESB .esb archive - When the webservice .war is bundled into a deployed .esb archive
  • Webservice bundled into an .ear - When the .war is bundled into a deployed .ear
  • Standalone webservice - When the webservice .war is deployed
When we examine the jboss-esb.xml file, we'll see that the JBossESB's SOAPProcessor (org.jboss.soa.esb.actions.soap.SOAPProcessor) is used to expose the web service's -endpoints.

But wait, there's more!

On line 15, we see this annotation:  @HandlerChain(file="handlerchain.xml")

A handler is a message interceptor to do processing of incoming and outgoing messages. SOAP handlers provide methods to access SOAP messages.
A HandlerChain is a list of handlers. The handlerchain referenced on line 15 is defined as follows in the handlerchain.xml file.

1:   <?xml version="1.0" encoding="UTF-8"?>  
2:   <jws:handler-config xmlns:jws="http://java.sun.com/xml/ns/javaee">  
3:       <jws:handler-chains>  
4:           <jws:handler-chain>  
5:               <jws:handler>  
6:                   <jws:handler-class>org.picketlink.identity.federation.core.wstrust.handlers.STSSaml20Handler</jws:handler-class>  
7:               </jws:handler>  
8:           </jws:handler-chain>  
9:       </jws:handler-chains>  
10:   </jws:handler-config>  

  • Line 6 - What's interesting in this file is line 6. The org.picketlink.identity.federation.core.wstrust.handlers.STSSaml20Handler handler class enables the quickstart to insert the SAML security assertion into the message as it is processed by the web service.
OK. That's the web service. Now let's look at the quickstart's client code in HttpClient.java

1:  package org.jboss.soa.esb.samples.quickstart.securitysaml.test;  
2:    
3:  import org.jboss.remoting.Client;  
4:  import org.jboss.remoting.InvokerLocator;  
5:  import org.jboss.internal.soa.esb.util.StreamUtils;  
6:  import org.jboss.remoting.transport.http.HTTPMetadataConstants;  
7:    
8:  import java.util.HashMap;  
9:  import java.util.Map;  
10:    
11:  public class HttpClient  
12:  {  
13:    // Default locator values  
14:    private static String transport = "http";  
15:    private static String host = "localhost";  
16:    private static int port = 5400;  
17:    private static String payload;  
18:    
19:    public void makeInvocation(String locatorURI) throws Throwable  
20:    {  
21:     InvokerLocator locator = new InvokerLocator(locatorURI);  
22:     System.out.println("Calling remoting server with locator uri of: " + locatorURI);  
23:    
24:     Client remotingClient = new Client(locator);  
25:     remotingClient.connect();  
26:    
27:     Map metadata = new HashMap();  
28:     metadata.put("TYPE", "POST");  
29:     remotingClient.invokeOneway(payload, metadata );  
30:    
31:     System.out.println("Sent http post to server.");  
32:     Integer responseCode = (Integer) metadata.get(HTTPMetadataConstants.RESPONSE_CODE);  
33:     String responseMessage = (String) metadata.get(HTTPMetadataConstants.RESPONSE_CODE_MESSAGE);  
34:     System.out.println("Response code from server: " + responseCode);  
35:     System.out.println("Response message from server: " + responseMessage);  
36:     remotingClient.disconnect();  
37:    
38:    }  
39:    
40:    public static void main(String[] args) throws Exception  
41:    {  
42:     if(args != null && args.length == 4)  
43:     {  
44:       transport = args[0];  
45:       host = args[1];  
46:       port = Integer.parseInt(args[2]);  
47:           payload = StreamUtils.getResourceAsString(args[3], "UTF-8");  
48:     }  
49:     String locatorURI = transport + "://" + host + ":" + port;  
50:     HttpClient client = new HttpClient();  
51:     try  
52:     {  
53:       client.makeInvocation(locatorURI);  
54:     }  
55:     catch(Throwable e)  
56:     {  
57:       e.printStackTrace();  
58:     }  
59:    }  
60:  }  

There's nothing unusual here as the client simply sends an HTTP POST request to a target URL. How does this client get executed? Through this target defined in the build.xml file.

1:  <target name="runtest" depends="compile"  
2:   description="sends a HTTP request to the JBossRemoting gateway">  
3:    <echo>Http Client</echo>  
4:   <java fork="yes" classname="org.jboss.soa.esb.samples.quickstart.securitysaml.test.HttpClient" failonerror="true">  
5:     <arg value="http"/>  
6:     <arg value="localhost"/>  
7:     <arg value="9888"/>  
8:     <arg value="soap-request.xml"/>  
9:     <classpath refid="exec-classpath"/>  
10:   </java>  
11:  </target>  
12:    

  • Line 7 - OK. There's nothing unusual here either. Or, is there? Look at line #7. Where did port 9888 get defined? After all, the the server is running on port 8080. We'll find the answer when we examine the quickstart's jboss-esb.xml file next.
(And, last of all, the DisplaySubjectAction.java class is a customer ESB action called by services to display the javax.security.auth.Subject extracted from the message.)

Group #3 - ESB archive related files

The deployment.xml, jbm-queue-service.xml, and jbmq-queue-service.xml files all reference the JMS queues and topics that will be needed by the quickstart. The jbossesb-properties.xml file is used to define properties used by the quickstart and to over-ride server-wide settings.

The file that we want to look at in detail is jboss-esb.xml. This file defines the quickstart's services configuration, including listeners and actions, and the providers.

(Note that while we'll be examining most of the contents of the jboss-esb.xml file in detail, we'll be doing it in pieces or fragments so that it's easier to follow. The line numbers in each of these fragments will therefore be different from the actual (whole) file.)

Let's start with the providers. As the name indicates, these elements define resources provided by by the ESB to the quickstart.

1:  <providers>  
2:     <jms-provider name="JBossMessaging" connection-factory="ConnectionFactory">  
3:       <jms-bus busid="quickstartGatewayChannel">  
4:         <jms-message-filter dest-type="QUEUE" dest-name="queue/quickstart_security_saml_gw"/>  
5:      </jms-bus>  
6:      <jms-bus busid="quickstartEsbChannel">  
7:        <jms-message-filter dest-type="QUEUE" dest-name="queue/quickstart_security_saml_esb"/>  
8:      </jms-bus>  
9:    </jms-provider>  
10:      
11:     <jbr-provider name="JBR-Http" protocol="http" host="localhost">  
12:          <jbr-bus busid="Http-1" port="9888" />  
13:     </jbr-provider>  
14:      
15:  </providers>  

  • Line 2 - Here's the start of the JMS provider. This is where we will see the definitions of JMS queues and topics.
  • Line 3 - Note that each queue has a "busid" value defined for it. We'll see the busid's used in the service definitions in a moment.
  • Line 7 - Remember how we discussed how each ESB-unaware queue required a corresponding ESB-aware queue to be defined?
  • Line 11 - This is the definition for a JBoss remoting provider. This makes use of JBoss Remoting (JBR) into JBoss ESB as a transport option and supports HTTP (and SSL, which we're not using in this quickstart). What's interesting here is that the jbr-provider enables us to define the port (remember how we saw the reference to port 9888 in the "runtest" ant target in the build.xml file?) that the provider provides (pardon the pun) to a service.
The remainder of the jboss-esb.xml file defines the quickstart's (3) services. We'll look at the configuration of the services now, and then we'll look at the actual operation of the services when we walk through what happens when we run the quickstart.


1:  <service category="SamlSecurityQuickstart" name="issueTokenService"  
2:      invmScope="GLOBAL"  
3:      description="This service demonstrates how a service can be configured to issue and validate a security token">  
4:      
5:      <security moduleName="saml-issue-token" callbackHandler="org.jboss.soa.esb.services.security.auth.login.JBossSTSIssueCallbackHandler">  
6:        <!-- disable the security context timeout so that our security context is re-evaluated -->  
7:        <property name="org.jboss.soa.esb.services.security.contextTimeout" value="0"/>  
8:      </security>  
9:      
10:     <listeners>  
11:       <jms-listener name="JMSGatewayListener" busidref="quickstartGatewayChannel" is-gateway="true"/>  
12:         <jbr-listener name="Http-Gateway" busidref="Http-1" is-gateway="true">  
13:           <property name="synchronous" value="false"/>  
14:         </jbr-listener>  
15:    </listeners>  
16:     
17:   <actions mep="OneWay">  
18:      
19:      <!-- Uncomment if you'd like to print the current Subject  
20:        <action name="printSubject" class="org.jboss.soa.esb.samples.quickstart.securitysaml.DisplaySubjectAction"/>  
21:        -->  
22:       
23:      <action name="routeAction" class="org.jboss.soa.esb.actions.StaticRouter">  
24:        <property name="destinations">  
25:          <route-to service-category="SamlSecurityQuickstart" service-name="securedSamlService"/>  
26:        </property>  
27:      </action>  
28:      
29:    </actions>  
30:  </service>  

  • Line 1: Service name and category are used to differentiate the services. Every service has a category and name attribute. JBossESB uses these attributes to register a service's listeners (gateway listeners or ESB-aware listeners) endpoints in a service registry.
  • Line 2 - The InVM transport enables services to communicate using the same JVM and without having to go through a second non-gateway provider. This makes things more efficient as the services don't have use any networking protocols between JVM instances. The "GLOBAL" value indicates that the this service can be invoked using the InVM transport using the same JVM.
  • Line 5 - Remember how we defined the appplication-policy name in login-config.xml?  (Hint: The policy name was "saml-issue-token.") That's right. This is the moduleName. The callbackHandler defined here overrides the one defined in jbossesb-properties.xml. Remember that the username and password can be provided by a callback. In the case of this service the callback is JBossSTSIssueCallbackHandler.  The callback handler retreives the username and password of the user for whom a security token should be issued.
  • Line 7 - Setting the org.jboss.soa.esb.services.security.contextTimeout property to zero ensures that every time the service is invoked the security context is re-evaluated.
  • Line 10 - Here's where the definition of the services listeners begins. These are well-named as they "listen" for incoming messages, based on the designated queue and protocol.
  • Line 12 - Remember how we reviewed the "Http-1" busidref in the "providers" section of the jboss-esb file? Well, here's where it's used. The JMS queue associated with this busidref is "listened to" for incoming messages. When a message is received, the service's action pipeline (in this case a pipeline of only one action) is executed. The reference to "gateway" indicates that the message originates outside of the ESB and is passed into the ESD through this "gateway."
  • Line 17 - The service's actions' definitions start here. The message exchange pattern (or "mep" for short) is asynchronous as the service will not wait for a response before moving on.
  • Line 20 - We'll uncomment this line later on so we can see the security subject displayed in the server log.
  • Line 25 - What happens to the message after the service issues the security assert? It's routed to the next service defined in the jboss-esb.xml file. In this case it's a static route, but the ESB actually supports other routing options too, including content based routing. (http://jboss-soa-p.blogspot.com/2009/07/when-content-knows-way-content-based.html) 
Next, let's look at the service to which the first service routes messages.

1:  <service category="SamlSecurityQuickstart" name="securedSamlService"  
2:    invmScope="GLOBAL"  
3:    description="This service demonstrates that an ESB service can be configured to only validate a security token.">  
4:      
5:    <security moduleName="saml-validate-token" callbackHandler="org.jboss.soa.esb.services.security.auth.login.JBossSTSTokenCallbackHandler">  
6:        <!-- disable the security context timeout so that our security context is re-evaluated -->  
7:        <property name="org.jboss.soa.esb.services.security.contextTimeout" value="0"/>  
8:    </security>  
9:      
10:    <actions mep="OneWay">  
11:      
12:      <!-- Uncomment if you'd like to print the current Subject  
13:         <action name="printSubject" class="org.jboss.soa.esb.samples.quickstart.securitysaml.DisplaySubjectAction"/>  
14:      -->  
15:      
16:      <action name="routeAction" class="org.jboss.soa.esb.actions.StaticRouter">  
17:        <property name="destinations">  
18:          <route-to service-category="SamlSecurityQuickstart" service-name="sendExternal"/>  
19:        </property>  
20:     </action>  
21:      
22:    </actions>  
23:  </service>  

There are two interesting lines in this service definition.
  • Line 5 - In contrast to the first service, this service uses the org.jboss.soa.esb.services.security.auth.login.JBossSTSTokenCallbackHandler class as a call back handler. 
  • Line 18 - Routes the message to the next service.
1:  <service category="SamlSecurityQuickstart" name="sendExternal"  
2:    invmScope="GLOBAL"  
3:    description="This service demonstrates that an ESB service can be configured to only validate a security token and call an external WS with the security token.">  
4:      
5:    <security moduleName="saml-validate-token" callbackHandler="org.jboss.soa.esb.services.security.auth.login.JBossSTSTokenCallbackHandler"/>  
6:      
7:    <actions mep="OneWay">  
8:      
9:      <!-- Uncomment if you'd like to print the current Subject  
10:       <action name="printSubject" class="org.jboss.soa.esb.samples.quickstart.securitysaml.DisplaySubjectAction"/>  
11:     -->  
12:      
13:     <action name="addSamlSecurityHeader" class="org.jboss.soa.esb.smooks.SmooksAction">  
14:       <property name="smooksConfig" value="/smooks/smooks-saml-injector.xml" />  
15:     </action>  
16:      
17:     <action name="JBossWSAdapter" class="org.jboss.soa.esb.actions.soap.SOAPProcessor">  
18:       <property name="jbossws-endpoint" value="GoodbyeWorldWS"/>  
19:     </action>  
20:      
21:   </actions>  
22:  </service>  

What's different from the second service in this service is:
  • Lines 13-15 - This action invokes the Smooks script to inject the SAML assert into the message.
  • Lines 17-18 - And finally, the target web service is invoked via the org.jboss.soa.esb.actions.soap.SOAPProcessor out of the box JBossESB action.
Running the Quickstart

OK - let's run the quickstart and watch it in action. Running the quickstarts is a deceptively simple task in that they generally requires only a small number of steps to execute. In the case of the security_saml quickstart, we only have to execute these (2) ant commands:

1:  ant deploy  
2:  ant runtest  

But - hang on  - let's examine in detail just what each command actually does.

ant deploy

Many of the quickstarts require some "set up" work in order to run. In the case of the security_saml quickstart this set up involves deploying not only the quickstart, but also the PicketLink Security Token Server (STS) .war file that is included in the quickstart. Here's the deploy target in the quickstart's build.xml file.

1:  <target name="quickstart-specific-predeploys">  
2:    <copy file="${basedir}/picketlink-sts.war" todir="${org.jboss.esb.server.deploy.dir}"/>  
3:  </target>  

Here's the server log when the quickstart is deployed.

1:  23:04:13,512 INFO [TomcatDeployment] undeploy, ctxPath=/Quickstart_Security_Saml  
2:  23:04:13,547 INFO [EsbDeployment] Stopping 'Quickstart_Security_Saml.esb'  
3:  23:04:13,837 INFO [Http11Protocol] Stopping Coyote HTTP/1.1 on http-localhost%2F127.0.0.1-9888  
4:  23:04:13,844 INFO [JBossRemotingGatewayListener] JBoss Remoting Gateway listener 'listener' stopped.  
5:  23:04:14,075 INFO [EsbDeployment] Destroying 'Quickstart_Security_Saml.esb'  
6:  23:04:14,075 WARN [ServiceMessageCounterLifecycleResource] Calling cleanup on existing service message counters for identity ID-6  
7:  23:04:14,075 WARN [SmooksResource] Closing smooks resources for identity ID-6  
8:  23:04:14,213 INFO [QueueService] Queue[/queue/quickstart_security_saml_esb_reply] stopped  
9:  23:04:14,215 INFO [QueueService] Queue[/queue/quickstart_security_saml_esb] stopped  
10:  23:04:14,216 INFO [QueueService] Queue[/queue/quickstart_security_saml_gw] stopped  
11:  23:04:14,487 INFO [QueueService] Queue[/queue/quickstart_security_saml_gw] started, fullSize=200000, pageSize=2000, downCacheSize=2000  
12:  23:04:14,518 INFO [QueueService] Queue[/queue/quickstart_security_saml_esb] started, fullSize=200000, pageSize=2000, downCacheSize=2000  
13:  23:04:14,550 INFO [QueueService] Queue[/queue/quickstart_security_saml_esb_reply] started, fullSize=200000, pageSize=2000, downCacheSize=2000  
14:  23:04:14,571 INFO [EsbDeployment] Starting ESB Deployment 'Quickstart_Security_Saml.esb'  
15:  23:04:15,870 INFO [CoyoteInvoker] Using org.apache.coyote.http11.Http11Protocol for http (coyote) invoker protocol handler.  
16:  23:04:15,871 INFO [Http11Protocol] Initializing Coyote HTTP/1.1 on http-localhost%2F127.0.0.1-9888  
17:  23:04:15,871 INFO [Http11Protocol] Starting Coyote HTTP/1.1 on http-localhost%2F127.0.0.1-9888  
18:  23:04:15,871 INFO [JBossRemotingGatewayListener] JBoss Remoting Gateway listener 'listener' started.  
19:  23:04:16,090 INFO [TomcatDeployment] deploy, ctxPath=/Quickstart_Security_Saml  

Remember the JBoss Remoting Gateway that we configured to run on port 9888? Here it is:

1:  23:04:15,871 INFO [Http11Protocol] Initializing Coyote HTTP/1.1 on http-localhost%2F127.0.0.1-9888  
2:  23:04:15,871 INFO [Http11Protocol] Starting Coyote HTTP/1.1 on http-localhost%2F127.0.0.1-9888  
3:  23:04:15,871 INFO [JBossRemotingGatewayListener] JBoss Remoting Gateway listener 'listener' started.  

OK, let's run the quickstart and watch it in action!

1:  runtest:  
2:     [echo] Http Client  
3:     [java] Calling remoting server with locator uri of: http://localhost:9888  
4:     [java] Sent http post to server.  
5:     [java] Response code from server: 200  
6:     [java] Response message from server: OK  

Well - that's a bit anti-climatic after all this setup work. Let's and take a more in-depth look at what just happened.

The quickstart begins by sending a message to the JBR-remoting gateway that we defined on port 9888. We can see this in the build.xml file:

1:  <target name="runtest" depends="compile"  
2:    description="sends a HTTP request to the JBossRemoting gateway">  
3:    <echo>Http Client</echo>  
4:    <java fork="yes" classname="org.jboss.soa.esb.samples.quickstart.securitysaml.test.HttpClient" failonerror="true">  
5:       <arg value="http"/>  
6:       <arg value="localhost"/>  
7:       <arg value="9888"/>  
8:       <arg value="soap-request.xml"/>  
9:       <classpath refid="exec-classpath"/>  
10:    </java>  
11:  </target>  

At this point, we'll un-comment the lines in the jboss-esb.xml file that invoke the org.jboss.soa.esb.samples.quickstart.securitysaml.DisplaySubjectAction - and we'll restart the server with logging set to the DEBUG level. If you spend your time developing or testing software, then the ability to "up" the logging level provides you with a goal mine of potential debugging information. Note that for the purposes of this essay, we'll look at selected log messages as logging at the DEBUG level can be very(!) verbose.

The first service defined in the jboss-esb.xml ("issueTokenService") file is located:

1:  2010-09-07 20:46:43,801 DEBUG [org.jboss.internal.soa.esb.services.registry.JAXRRegistryImpl] (JBossRemoting Server Oneway(3)-1) Going to query the registry for name pattern [issueTokenService]  
2:  2010-09-07 20:46:43,898 DEBUG [org.jboss.internal.soa.esb.services.registry.JAXRRegistryImpl] (JBossRemoting Server Oneway(3)-1) Service name: issueTokenService  
3:  2010-09-07 20:46:43,898 DEBUG [org.jboss.internal.soa.esb.services.registry.JAXRRegistryImpl] (JBossRemoting Server Oneway(3)-1) Description: This service demonstrates how a service can be configured  
4:   to issue and validate a security token  

The incoming message is received by the JBR gateway:

1:  2010-09-07 20:46:43,898 DEBUG [org.jboss.internal.soa.esb.services.registry.JAXRRegistryImpl] (JBossRemoting Server Oneway(3)-1) Key id: uddi:juddi.apache.org:1e71f7ae-e72d-4a38-a39c-cad7c1b1d250  
2:  2010-09-07 20:46:43,900 DEBUG [org.jboss.soa.esb.client.ServiceInvoker] (JBossRemoting Server Oneway(3)-1) EPR=InVMEpr [ PortReference < <wsa:Address  
3:  invm://53616d6c5365637572697479517569636b73746172742424242424242424242424246973737565546f6b656e53657276696365/false?false#10000/>, <wsa:ReferenceProperties jbossesb:passByValue :  
4:  false/>, <wsa:ReferenceProperties jbossesb:type : urn:jboss/esb/epr/type/invm/> > ] XML=<wsa:From xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"><wsa:Address>invm:  
5:  //53616d6c5365637572697479517569636b73746172742424242424242424242424246973737565546f6b656e53657276696365/false?false#10000</wsa:Address><wsa:ReferenceProperties>  
6:  <jbossesb:passByValue xmlns:jbossesb="http://schemas.jboss.com/ws/2007/01/jbossesb">false</jbossesb:passByValue><jbossesb:type xmlns:jbossesb="http://schemas.jboss.com/ws/2007  
7:  /01/jbossesb">urn:jboss/esb/epr/type/invm</jbossesb:type></wsa:ReferenceProperties></wsa:From>  
8:  2010-09-07 20:46:43,907 DEBUG [org.jboss.soa.esb.client.ServiceInvoker] (JBossRemoting Server Oneway(3)-1) EPR=EPR: PortReference < <wsa:Address http://localhost:9888/> > XML=<wsa:From  
9:  xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"><wsa:Address>http://localhost:9888</wsa:Address></wsa:From>  
10:  2010-09-07 20:46:43,924 DEBUG [org.jboss.soa.esb.filter.FilterManager] (JBossRemoting Server Oneway(3)-1) FilterManager calling org.jboss.internal.soa.esb.message.filter.MetaDataFilter@8e85e1.onOutput  
11:  2010-09-07 20:46:43,924 DEBUG [org.jboss.soa.esb.filter.FilterManager] (JBossRemoting Server Oneway(3)-1) FilterManager calling org.jboss.internal.soa.esb.message.filter.GatewayFilter@f77a23.onOutput  

And the message is passed to the ESB-aware queue:

1:  2010-09-07 20:46:43,924 DEBUG [org.jboss.internal.soa.esb.couriers.transport.InVMTransport] (JBossRemoting Server Oneway(3)-1) Delivering message to 53616d6c5365637572697479517569636b73746172742424242424242424242424246973737565546f6b656e53657276696365  
2:  2010-09-07 20:46:43,924 DEBUG [org.jboss.internal.soa.esb.couriers.transport.InVMTransport] (JBossRemoting Server Oneway(3)-1) Delivering message direct to 53616d6c5365637572697479517569636b73746172742424242424242424242424246973737565546f6b656e53657276696365 queue  
3:  2010-09-07 20:46:43,926 DEBUG [org.jboss.internal.soa.esb.couriers.transport.InVMTransport] (Thread-29) Pickup of message from 53616d6c5365637572697479517569636b73746172742424242424242424242424246973737565546f6b656e53657276696365  

The assert is created:

1:  2010-09-07 20:46:46,078 DEBUG [org.picketlink.identity.federation.core.wstrust.auth.AbstractSTSLoginModule] (pool-24-thread-1) Added Credential :SamlCredential[<Assertion  
2:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ns2="http://www.w3.org/2001/04/xmlenc#" xmlns:ns3="http://www.w3.org/2000/09/xmldsig#" ID="ID_bc96b4b9-0743-49ca-8e32-b0bd3cd80185"  
3:  IssueInstant="2010-09-08T00:46:45.294Z" Version="2.0"><Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">PicketLinkSTS</Issuer><Subject xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><NameID  
4:   NameQualifier="urn:picketlink:identity-federation" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">admin</NameID><SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"  
5:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion"/></Subject><Conditions NotBefore="2010-09-08T00:46:45.294Z" NotOnOrAfter="2010-09-08T02:46:45.294Z"  
6:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><AudienceRestriction xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Audience xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http://security_saml  
7:  /goodbyeworld</Audience></AudienceRestriction></Conditions><dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:SignedInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">  
8:  <dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#WithComments" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:SignatureMethod  
9:  Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:Reference URI="#ID_bc96b4b9-0743-49ca-8e32-b0bd3cd80185"  
10:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-  
11:  signature" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" xmlns:dsig="http://www.w3.org/2000/09  
12:  /xmldsig#"/><dsig:DigestValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">mcisCWAF28n4AQVYf1OEzeotBFA=</dsig:DigestValue></dsig:Reference></dsig:SignedInfo><dsig:SignatureValue  
13:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">GyX1b4gN/lEiu6wbdddL4uSgPu2uyPALiYgKlCvOScnEBimO62ctj0lRWMVOlNywqso9TcqFS7fb  
14:  4fjbHif/m3S7mFfq+HhRlr3+StTtz9XWFoO/vn2twSKuwToX156RwzGlT4NvMAapmJI474uvValN  
15:  /HcLLHpqB2GI1XwD0kg=</dsig:SignatureValue><dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:KeyValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:RSAKeyValue  
16:   xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Modulus xmlns:dsig="http://www.w3.org/2000/09  
17:  /xmldsig#">suGIyhVTbFvDwZdx8Av62zmP+aGOlsBN8WUE3eEEcDtOIZgO78SImMQGwB2C0eIVMhiLRzVPqoW1  
18:  dCPAveTm653zHOmubaps1fY0lLJDSZbTbhjeYhoQmmaBro/tDpVw5lKJns2qVnMuRK19ju2dxpKw  
19:  lYGGtrP5VQv00dfNPbs=</dsig:Modulus><dsig:Exponent xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">AQAB</dsig:Exponent></dsig:RSAKeyValue></dsig:KeyValue></dsig:KeyInfo>  
20:  </dsig:Signature></Assertion>]  
21:  2010-09-07 20:46:46,081 DEBUG [org.jboss.internal.soa.esb.services.security.PrivateCryptoUtil] (pool-24-thread-1) SealAlgorithm 'TripleDES' keySize '168'  
22:  2010-09-07 20:46:46,173 DEBUG [org.jboss.soa.esb.listeners.message.ActionProcessingPipeline] (pool-24-thread-1) executing processor 0  
23:  org.jboss.soa.esb.listeners.message.OverriddenActionLifecycleProcessor@1ae20eb  

The issueTokenService displays the subject assert:

1:  2010-09-07 20:46:46,175 INFO [STDOUT] (pool-24-thread-1) Subject in MyListenerAction : Subject:  
2:    Public Credential: SamlCredential[<Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ns2="http://www.w3.org/2001/04/xmlenc#" xmlns:ns3="http://www.w3.org/2000/09/xmldsig#"  
3:  ID="ID_bc96b4b9-0743-49ca-8e32-b0bd3cd80185" IssueInstant="2010-09-08T00:46:45.294Z" Version="2.0"><Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">PicketLinkSTS</Issuer><Subject  
4:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><NameID NameQualifier="urn:picketlink:identity-federation" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">admin</NameID><SubjectConfirmation  
5:  Method="urn:oasis:names:tc:SAML:2.0:cm:bearer" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"/></Subject><Conditions NotBefore="2010-09-08T00:46:45.294Z"  
6:  NotOnOrAfter="2010-09-08T02:46:45.294Z" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><AudienceRestriction xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Audience  
7:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http://security_saml/goodbyeworld</Audience></AudienceRestriction></Conditions><dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">  
8:  <dsig:SignedInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#WithComments" xmlns:dsig="http://www.w3.org  
9:  /2000/09/xmldsig#"/><dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:Reference URI="#ID_bc96b4b9-0743-49ca-  
10:  8e32-b0bd3cd80185" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transform Algorithm="http://www.w3.org/2000/09  
11:  /xmldsig#enveloped-signature" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"  
12:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:DigestValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">mcisCWAF28n4AQVYf1OEzeotBFA=</dsig:DigestValue></dsig:Reference>  
13:  </dsig:SignedInfo><dsig:SignatureValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">GyX1b4gN/lEiu6wbdddL4uSgPu2uyPALiYgKlCvOScnEBimO62ctj0lRWMVOlNywqso9TcqFS7fb  
14:  4fjbHif/m3S7mFfq+HhRlr3+StTtz9XWFoO/vn2twSKuwToX156RwzGlT4NvMAapmJI474uvValN  
15:  /HcLLHpqB2GI1XwD0kg=</dsig:SignatureValue><dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:KeyValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:RSAKeyValue  
16:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Modulus xmlns:dsig="http://www.w3.org/2000/09  
17:  /xmldsig#">suGIyhVTbFvDwZdx8Av62zmP+aGOlsBN8WUE3eEEcDtOIZgO78SImMQGwB2C0eIVMhiLRzVPqoW1  
18:  dCPAveTm653zHOmubaps1fY0lLJDSZbTbhjeYhoQmmaBro/tDpVw5lKJns2qVnMuRK19ju2dxpKw  
19:  lYGGtrP5VQv00dfNPbs=</dsig:Modulus><dsig:Exponent xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">AQAB</dsig:Exponent></dsig:RSAKeyValue></dsig:KeyValue></dsig:KeyInfo>  
20:  </dsig:Signature></Assertion>]  
21:  2010-09-07 20:46:46,178 DEBUG [org.jboss.soa.esb.listeners.message.ActionProcessingPipeline] (pool-24-thread-1) executing processor 1 org.jboss.soa.esb.actions.StaticRouter@826935  

The message is routed to the 2nd service "securedSamlService"):

1:  2010-09-07 20:46:46,179 DEBUG [org.jboss.internal.soa.esb.services.registry.JAXRRegistryImpl] (pool-24-thread-1) Going to query the registry for name pattern [securedSamlService]  

Where the assert is validated:

1:  2010-09-07 20:46:46,791 DEBUG [org.picketlink.identity.federation.core.wstrust.auth.STSValidatingLoginModule] (pool-25-thread-1) Validation result: true  

And the subject is displayed again:

1:  2010-09-07 20:46:46,833 INFO [STDOUT] (pool-25-thread-1) Subject in MyListenerAction : Subject:  
2:    Public Credential: SamlCredential[<Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ns2="http://www.w3.org/2001/04/xmlenc#" xmlns:ns3="http://www.w3.org/2000/09/xmldsig#"  
3:   ID="ID_bc96b4b9-0743-49ca-8e32-b0bd3cd80185" IssueInstant="2010-09-08T00:46:45.294Z" Version="2.0"><Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">PicketLinkSTS</Issuer><Subject  
4:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><NameID NameQualifier="urn:picketlink:identity-federation" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">admin</NameID><SubjectConfirmation  
5:  Method="urn:oasis:names:tc:SAML:2.0:cm:bearer" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"/></Subject><Conditions NotBefore="2010-09-08T00:46:45.294Z"  
6:  NotOnOrAfter="2010-09-08T02:46:45.294Z" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><AudienceRestriction xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Audience  
7:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http://security_saml/goodbyeworld</Audience></AudienceRestriction></Conditions><dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">  
8:  <dsig:SignedInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#WithComments" xmlns:dsig="http://www.w3.org  
9:  /2000/09/xmldsig#"/><dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:Reference URI="#ID_bc96b4b9-0743-49ca-  
10:  8e32-b0bd3cd80185" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transform Algorithm="http://www.w3.org/2000/09  
11:  /xmldsig#enveloped-signature" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"  
12:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:DigestValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">mcisCWAF28n4AQVYf1OEzeotBFA=</dsig:DigestValue></dsig:Reference>  
13:  </dsig:SignedInfo><dsig:SignatureValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">GyX1b4gN/lEiu6wbdddL4uSgPu2uyPALiYgKlCvOScnEBimO62ctj0lRWMVOlNywqso9TcqFS7fb  
14:  4fjbHif/m3S7mFfq+HhRlr3+StTtz9XWFoO/vn2twSKuwToX156RwzGlT4NvMAapmJI474uvValN  
15:  /HcLLHpqB2GI1XwD0kg=</dsig:SignatureValue><dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:KeyValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:RSAKeyValue  
16:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Modulus xmlns:dsig="http://www.w3.org/2000/09  
17:  /xmldsig#">suGIyhVTbFvDwZdx8Av62zmP+aGOlsBN8WUE3eEEcDtOIZgO78SImMQGwB2C0eIVMhiLRzVPqoW1  
18:  dCPAveTm653zHOmubaps1fY0lLJDSZbTbhjeYhoQmmaBro/tDpVw5lKJns2qVnMuRK19ju2dxpKw  
19:  lYGGtrP5VQv00dfNPbs=</dsig:Modulus><dsig:Exponent xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">AQAB</dsig:Exponent></dsig:RSAKeyValue></dsig:KeyValue></dsig:KeyInfo>  
20:  </dsig:Signature></Assertion>]  

And the message is routed to the sendExternal service:

1:  2010-09-07 20:46:46,833 DEBUG [org.jboss.soa.esb.listeners.message.ActionProcessingPipeline] (pool-25-thread-1) executing processor 1 org.jboss.soa.esb.actions.StaticRouter@1596c3a  
2:  2010-09-07 20:46:46,834 DEBUG [org.jboss.internal.soa.esb.services.registry.JAXRRegistryImpl] (pool-25-thread-1) Going to query the registry for name pattern [sendExternal]  
3:  2010-09-07 20:46:47,391 DEBUG [org.picketlink.identity.federation.core.wstrust.auth.AbstractSTSLoginModule] (pool-26-thread-1) Added Credential :SamlCredential[<Assertion  
4:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ns2="http://www.w3.org/2001/04/xmlenc#" xmlns:ns3="http://www.w3.org/2000/09/xmldsig#" ID="ID_bc96b4b9-0743-49ca-8e32-b0bd3cd80185"  
5:  IssueInstant="2010-09-08T00:46:45.294Z" Version="2.0"><Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">PicketLinkSTS</Issuer><Subject xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><NameID  
6:   NameQualifier="urn:picketlink:identity-federation" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">admin</NameID><SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"  
7:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion"/></Subject><Conditions NotBefore="2010-09-08T00:46:45.294Z" NotOnOrAfter="2010-09-08T02:46:45.294Z"  
8:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><AudienceRestriction xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Audience xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http://security_saml  
9:  /goodbyeworld</Audience></AudienceRestriction></Conditions><dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:SignedInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">  
10:  <dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#WithComments" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:SignatureMethod  
11:  Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:Reference URI="#ID_bc96b4b9-0743-49ca-8e32-b0bd3cd80185"  
12:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-  
13:  signature" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" xmlns:dsig="http://www.w3.org/2000/09  
14:  /xmldsig#"/><dsig:DigestValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">mcisCWAF28n4AQVYf1OEzeotBFA=</dsig:DigestValue></dsig:Reference></dsig:SignedInfo><dsig:SignatureValue  
15:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">GyX1b4gN/lEiu6wbdddL4uSgPu2uyPALiYgKlCvOScnEBimO62ctj0lRWMVOlNywqso9TcqFS7fb  
16:  4fjbHif/m3S7mFfq+HhRlr3+StTtz9XWFoO/vn2twSKuwToX156RwzGlT4NvMAapmJI474uvValN  
17:  /HcLLHpqB2GI1XwD0kg=</dsig:SignatureValue><dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:KeyValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:RSAKeyValue  
18:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Modulus xmlns:dsig="http://www.w3.org/2000/09  
19:  /xmldsig#">suGIyhVTbFvDwZdx8Av62zmP+aGOlsBN8WUE3eEEcDtOIZgO78SImMQGwB2C0eIVMhiLRzVPqoW1  
20:  dCPAveTm653zHOmubaps1fY0lLJDSZbTbhjeYhoQmmaBro/tDpVw5lKJns2qVnMuRK19ju2dxpKw  
21:  lYGGtrP5VQv00dfNPbs=</dsig:Modulus><dsig:Exponent xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">AQAB</dsig:Exponent></dsig:RSAKeyValue></dsig:KeyValue></dsig:KeyInfo>  
22:  </dsig:Signature></Assertion>]  
23:  2010-09-07 20:46:47,392 DEBUG [org.jboss.soa.esb.listeners.message.ActionProcessingPipeline] (pool-26-thread-1) executing processor 0  
24:  org.jboss.soa.esb.listeners.message.OverriddenActionLifecycleProcessor@1ac4773  

The subject is displayed a final time:

1:  2010-09-07 20:46:47,392 INFO [STDOUT] (pool-26-thread-1) Subject in MyListenerAction : Subject:  
2:    Public Credential: SamlCredential[<Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ns2="http://www.w3.org/2001/04/xmlenc#" xmlns:ns3="http://www.w3.org/2000/09/xmldsig#"  
3:  ID="ID_bc96b4b9-0743-49ca-8e32-b0bd3cd80185" IssueInstant="2010-09-08T00:46:45.294Z" Version="2.0"><Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">PicketLinkSTS</Issuer><Subject  
4:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><NameID NameQualifier="urn:picketlink:identity-federation" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">admin</NameID><SubjectConfirmation  
5:  Method="urn:oasis:names:tc:SAML:2.0:cm:bearer" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"/></Subject><Conditions NotBefore="2010-09-08T00:46:45.294Z"  
6:  NotOnOrAfter="2010-09-08T02:46:45.294Z" xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><AudienceRestriction xmlns="urn:oasis:names:tc:SAML:2.0:assertion"><Audience  
7:  xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http://security_saml/goodbyeworld</Audience></AudienceRestriction></Conditions><dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">  
8:  <dsig:SignedInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#WithComments" xmlns:dsig="http://www.w3.org  
9:  /2000/09/xmldsig#"/><dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:Reference URI="#ID_bc96b4b9-0743-49ca-  
10:  8e32-b0bd3cd80185" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transforms xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Transform Algorithm="http://www.w3.org/2000/09  
11:  /xmldsig#enveloped-signature" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"  
12:  xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"/><dsig:DigestValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">mcisCWAF28n4AQVYf1OEzeotBFA=</dsig:DigestValue></dsig:Reference>  
13:  </dsig:SignedInfo><dsig:SignatureValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">GyX1b4gN/lEiu6wbdddL4uSgPu2uyPALiYgKlCvOScnEBimO62ctj0lRWMVOlNywqso9TcqFS7fb  
14:  4fjbHif/m3S7mFfq+HhRlr3+StTtz9XWFoO/vn2twSKuwToX156RwzGlT4NvMAapmJI474uvValN  
15:  /HcLLHpqB2GI1XwD0kg=</dsig:SignatureValue><dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:KeyValue xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:RSAKeyValue  
16:   xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:Modulus xmlns:dsig="http://www.w3.org/2000/09  
17:  /xmldsig#">suGIyhVTbFvDwZdx8Av62zmP+aGOlsBN8WUE3eEEcDtOIZgO78SImMQGwB2C0eIVMhiLRzVPqoW1  
18:  dCPAveTm653zHOmubaps1fY0lLJDSZbTbhjeYhoQmmaBro/tDpVw5lKJns2qVnMuRK19ju2dxpKw  
19:  lYGGtrP5VQv00dfNPbs=</dsig:Modulus><dsig:Exponent xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">AQAB</dsig:Exponent></dsig:RSAKeyValue></dsig:KeyValue></dsig:KeyInfo></dsig:Signature></Assertion>]  

Smooks inserts the assert into the message:

1:  2010-09-07 20:46:47,916 DEBUG [org.milyn.util.ClassUtil] (pool-26-thread-1) Adding org.milyn.javabean.decoders.BigDecimalDecoder to list of classes  
2:  2010-09-07 20:46:47,917 DEBUG [org.milyn.util.ClassUtil] (pool-26-thread-1) Adding org.milyn.javabean.decoders.BigIntegerDecoder to list of classes  
3:  2010-09-07 20:46:47,918 DEBUG [org.milyn.util.ClassUtil] (pool-26-thread-1) Adding org.milyn.javabean.decoders.BooleanDecoder to list of classes  
4:  (lots more lines like this)  
5:    

And the web service is accessed:

1:  2010-09-07 20:46:48,415 DEBUG [org.jboss.ws.core.EndpointInvocation] (pool-26-thread-1) transformPayloadValue: org.jboss.ws.core.soap.SOAPBodyElementDoc -> org.jboss.soa.esb.samples.quickstart.securitysaml.webservice.jaxws.SayGoodbye  
2:    </soapenv:Header>  
3:    <soapenv:Body>  
4:     <good:sayGoodbye>  
5:       <message>Goodbye!!</message>  
6:     </good:sayGoodbye>  
7:    </soapenv:Body>  
8:  </soapenv:Envelope>  

Closing Thoughts

OK, let's review what happened.

The quickstart demonstrate these (4) procedures:
  1. Using the PicketLink Project's STSIssuingLoginModule to issue a SAML Assertion from PicketLinkSTS, then
  2. Using the PicketLink Project's STSValidatingLoginModule to validate a SAML Assertion from PicketLinkSTS, then
  3. Injecting the SAML Assertion into a SOAP Message, then
  4. Using SOAPProcessor to invoke an external Web Service that is secured by PicketLinkSTS.
And, notice what the quickstart did not have to do - it did not have to include a large amount of custom-written code to issue or validate the SAML Assertion. PicketLinkSTS and the JBossESB in the SOA Platform did the heavy lifting.

Acknowledgements

As always, I want to thank the JBoss SOA Platform team and community (especially Anil Saldhana, Babak Mozaffari, and Kevin Conner for their timely review input for this blog post).

References