Wednesday, September 12, 2012

Extracting and Passing WSS Name Token Security Header in BPEL

How to extract and insert SOAP headers in BPEL 2.0 is documented in the Oracle doc here http://docs.oracle.com/cd/E12839_01/integration.1111/e10224/bp_manipdoc.htm#SOASE415 However, like all Oracle docs, it takes some hands on finessing to figure out the specifics.

Additionally, for WSS security tokens, I have to dance around the WSS security XSD files in order to get it to work. It may not be the best way, but it seems to work.

Let's look at how to extract SOAP headers in BPEL. As Oracle document indicates, it takes two steps. You need to be very precise with each step, otherwise, you won't see the headers inside BPEL.

Step I -  update the WSDL - the Oracle document provides an excellent sample with multiple headers, it just needs some more detailed descriptions. Let me copy the sample here:
<!-- custom header -->
   <message name="CustomHeaderMessage">
    <part name="header1" element="tns:header1"/>
    <part name="header2" element="tns:header2"/>
  </message>
  <binding name="HeaderServiceBinding" type="tns:HeaderService">
    <soap:binding style="document"
      transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="initiate">
      <soap:operation style="document" soapAction="initiate"/>
        <input>
          <soap:header message="tns:CustomHeaderMessage"
            part="header1" use="literal"/>
          <soap:header message="tns:CustomHeaderMessage"
            part="header2" use="literal"/>
          <soap:body use="literal"/>
        </input>
    </operation>
  </binding>

Step 1.1 - define the messages to enclose the header elements to be passed in SOAP header. 
Notice that in the <!-- custom header --> section, The CustomHeaderMessage has two parts.  In order to pass in multiple SOAP headers, each SOAP header needs to be included as a part in this message.

The part names are "part1" and "part2". Although not critical, in the real world, you should give them some more meaningful part names.

Although obvious, it is still worth noting that these elements need be defined somewhere or imported from somewhere. For example, in your schema "header1" may hypothetically defined as:

<element name="header1">
<complexType>
<sequence>
<element name="fristName" type="string"/>
<element name="lastName" type="string"/>
</sequence>
</complexType>
</element>
In the case of WSS security header, I have to import a few related XSD's - see details below.

Step 1.2 - update the operation in the binding section

The Oracle sample may not look like the typical BPEL WSDL, so here is the changes I made to my WSDL:

<wsdl:operation name="process">
<wsdl:input  message="client:fooBPELRequestMessage">
<soap:header message="client:securityHeaderMsg"  part="payload" use="literal"/>
<soap:header message="client:fooBPELResponseMessage"  part="payload" use="literal"/>
</wsdl:input>
<wsdl:output message="client:fooBPELResponseMessage"/>
</wsdl:operation>

Here I am passing two SOAP headers. Keep in mind, very importantly, in Step 1.1, these two headers need be declared in a single message as two parts.

Step II - Receive (extract) headers in BPEL

Step 2.1 - define a message-type variable using the CustomHeaderMessage

    <variable name="customHeaderVar" messageType="client:customHeaders"/>

Step 2.2 - update receive activity
  <receive name="receiveInput" 
partnerLink="foobpel_client" 
portType="client:fooBPEL" 
operation="process" variable="inputVariable" 
createInstance="yes" 
bpelx:headerVariable="customHeaderVar"/>
There you go, customHeaderVar will contain the SOAP header values if you pass them in. That's a big "if", because you do not pass the headers in your SOAP call, or do not pass them in correctly, you still won't receive the headers, although it causes no harm to the receive activity even if you don't pass the headers in yours SOAP call.

In my case, I am only interested in the WSS security header, so I first define a "security" variable:
   <variable name="SecurityVar" element="ns2:Security"/> 
where ns2 is xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
then I pick up the WSS header:
    <assign name="AssignCustomHeader">
      <copy>
        <from>$customHeaderVar.payload1</from>
        <to>$SecurityVar</to>
      </copy>
    </assign>

Step III passing SOAP headers in BPEL invoke activity

Finally, I can pass in the WSS security header in the invoke:

     <invoke name="InvokeBar"
                partnerLink="fooCallsBar" portType="ns1:barBPEL"
                operation="process" inputVariable="Invoke_process_InputVariable"
                outputVariable="Invoke_process_OutputVariable"
                bpelx:invokeAsDetail="no" bpelx:inputHeaderVariable="SecurityVar"/>

The WSS Security XSD Conundrum 

I couldn't effectively importing WSS security XSD remotely, it takes forever for JDEV to drill down the WSS security element structure, so I ended up copying these 3 files locally:
"oasis-200401-wss-wssecurity-secext-1.0.xsd", 
"oasis-200401-wss-wssecurity-utility-1.0.xsd" 
"xmldsig-core-schema.xsd"
I had to comment out this line in the first file, so it won't bring my house down 
<!--xsd:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd"/-->

Here is my entire WSDL:

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="fooBPEL"
             targetNamespace="http://xmlns.oracle.com/cis/foo/fooBPEL"
             xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
             xmlns:client="http://xmlns.oracle.com/cis/foo/fooBPEL"
             xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
             xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" 
             xmlns:plnk="http://docs.oasis-open.org/wsbpel/2.0/plnktype">
<wsdl:types>
<schema xmlns="http://www.w3.org/2001/XMLSchema">
<import namespace="http://xmlns.oracle.com/cis/foo/fooBPEL" schemaLocation="xsd/fooBPEL.xsd" />
<import namespace="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" 
                        schemaLocation="xsd/oasis-200401-wss-wssecurity-secext-1.0.xsd" />
</schema>
</wsdl:types>
<wsdl:message name="fooBPELRequestMessage">
<wsdl:part name="payload" element="client:process"/>
</wsdl:message>
<wsdl:message name="fooBPELResponseMessage">
<wsdl:part name="payload" element="client:processResponse"/>
</wsdl:message>
<wsdl:message name="securityHeaderMsg">
<wsdl:part name="payload" element="wsse:Security"/>
</wsdl:message>
<wsdl:message name="customHeaders">
<wsdl:part name="payload1" element="wsse:Security"/>
<wsdl:part name="payload2" element="client:processResponse"/>
</wsdl:message>
<wsdl:portType name="fooBPEL">
<wsdl:operation name="process">
<wsdl:input  message="client:fooBPELRequestMessage">
                                <soap:header message="client:securityHeaderMsg"  part="payload" use="literal"/>
                                <soap:header message="client:fooBPELResponseMessage"  part="payload" use="literal"/>
                        </wsdl:input>
<wsdl:output message="client:fooBPELResponseMessage"/>
</wsdl:operation>
</wsdl:portType>
<plnk:partnerLinkType name="fooBPEL">
<plnk:role name="fooBPELProvider" portType="client:fooBPEL"/>
</plnk:partnerLinkType>
</wsdl:definitions>

Here is the SOAP UI test payload:

soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:foob="http://xmlns.oracle.com/cis/foo/fooBPEL">
   <soapenv:Header>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
      <wsse:UsernameToken>
         <wsse:Username>weblogic</wsse:Username>
         <wsse:Password>welcome1</wsse:Password>
      </wsse:UsernameToken>
   </wsse:Security>
   <processResponse xmlns="http://xmlns.oracle.com/cis/foo/fooBPEL">
      <result>this is my header</result>
   </processResponse>
   </soapenv:Header>
   <soapenv:Body>
      <foob:process>
         <foob:input>SOAPUI invokes foo</foob:input>
      </foob:process>
   </soapenv:Body>
</soapenv:Envelope>

Snippet of BPEL source:


         xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
  ...
  <variables>
    <variable name="inputVariable" messageType="client:fooBPELRequestMessage"/>
    <variable name="outputVariable" messageType="client:fooBPELResponseMessage"/>
    <variable name="Invoke_process_InputVariable"
              messageType="ns1:barBPELRequestMessage"/>
    <variable name="Invoke_process_OutputVariable"
              messageType="ns1:barBPELResponseMessage"/>
    <variable name="SecurityVar" element="ns2:Security"/>
  </variables>

  <sequence name="main">

    <!-- Receive input from requestor. (Note: This maps to operation defined in fooBPEL.wsdl) -->
    <receive name="receiveInput" partnerLink="foobpel_client" portType="client:fooBPEL" operation="process" variable="inputVariable"
    createInstance="yes" bpelx:headerVariable="customHeaderVar"/>
    <assign name="AssignCustomHeader">
      <copy>
        <from>$customHeaderVar.payload1</from>
        <to>$SecurityVar</to>
      </copy>
    </assign>
    <assign name="AssignBarInput">
      <copy>
        <from>"knock, knock, foo's calling"</from>
        <to>$Invoke_process_InputVariable.payload/ns1:input</to>
      </copy>
    </assign>

    <invoke name="InvokeBar"
            partnerLink="fooCallsBar" portType="ns1:barBPEL"
            operation="process" inputVariable="Invoke_process_InputVariable"
            outputVariable="Invoke_process_OutputVariable"
            bpelx:invokeAsDetail="no" bpelx:inputHeaderVariable="SecurityVar"/>






3 comments:

  1. I think you've left some steps out here because this does not seem to work (or even compile)

    What is the significance of "Bar"?

    ReplyDelete
  2. Hi Keith,

    I haven't touched this forever. But here is the original source when I worked on it. https://drive.google.com/file/d/0B-S8707t0bpibi1BUkRzOTktaGc/edit?usp=sharing

    Let me know if it works for you. Good luck.

    ReplyDelete
  3. i have replicated the same steps.Still not able to get any data populated in header.Any assign activity using the variable throws up an error "no data found in node".
    Also I could not find the file at the afore mentioned location.
    Please help.
    Regards,
    Will

    ReplyDelete