Search code examples
soapwebspherecxfmule

Add User and Pass to SOAP Header in Mule


It seems this should be simple but the solution has been eluding me. My flow is XML -> XSLT translation -> consume web service (IBM Web Sphere Web Service to be specific). I have the pieces working individually but I am having trouble figuring out how to add user/pass to the SOAP header. I would think that I should be able to add them to the keys in the security tab on the Mule SOAP Component (I have the operation set to Proxy Client). Unfortunately, I cannot figure out what the valid keys are. Maybe I am way off base even attempting to use the security tab. So ultimately I need my outgoing XML to contain:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
    <soapenv:Header>
        <wsse:Security soapenv:mustUnderstand="1">
            <wsse:UsernameToken>
                <wsse:Username>
                    myUserName
                </wsse:Username>
                <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">
                    myPa33W0rd
                </wsse:Password>
            </wsse:UsernameToken>
        </wsse:Security>
    </soapenv:Header>
    <soapenv:Body>

Currently my Mule flow is putting out:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>

Do I need to add the security information manually (maybe in the XSLT translation)? That doesn't feel right but I can't figure out how to add it.

Here are the relevant lines from my flow:

<mulexml:xslt-transformer maxIdleTransformers="2" maxActiveTransformers="5" xsl-file="src\main\resources\MappingMapToChangeCatalogEntry.xslt" outputEncoding="US-ASCII" doc:name="XSLT"/>
<cxf:proxy-client payload="body" enableMuleSoapHeaders="true" doc:name="SOAP"/>
<byte-array-to-string-transformer doc:name="Byte Array to String"/>

Solution

  • In order to add WS-Sec you need to configure the CXF WSS4J interceptors and inject them into Mule's CXF message processors.

    Pre 3.3 =

    <spring:bean name="wss4jOutConfiguration"
        class="org.springframework.beans.factory.config.MapFactoryBean">
        <spring:property name="sourceMap">
          <spring:map>
              <spring:entry key="action" value="Signature" />
              <spring:entry key="user" value="joe" />
              <spring:entry key="signaturePropFile" value="org/mule/module/cxf/wssec/wssecurity.properties" />
              <spring:entry key="passwordCallbackClass" value="org.mule.module.cxf.wssec.ClientPasswordCallback" />
          </spring:map>
        </spring:property>
    </spring:bean>
    
    ...
    
        <cxf:proxy-client payload="body" enableMuleSoapHeaders="true" doc:name="SOAP">
            <cxf:outInterceptors>
                <spring:bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
                    <spring:property name="properties" ref="wss4jOutConfiguration"/>
                </spring:bean>
            </cxf:outInterceptors>
        </cxf:proxy-client>
    

    Rough Sample Password Callback class:

    public class ClientPasswordCallback implements CallbackHandler{
    
    @Override
    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        WSPasswordCallback callback = (WSPasswordCallback) callbacks[0];
        if(callback.getIdentifier().equals("joe")){
            callback.setPassword("pass");
        }
    }
    

    See more here: http://www.mulesoft.org/documentation/display/current/WS-Security+Usability+Improvement

    3.3.+ : There is a new cxf:ws-security element availble in 3.3+ Here is an example flow here: https://svn.codehaus.org/mule/tags/mule-3.4-M2/modules/cxf/src/test/resources/org/mule/module/cxf/wssec/cxf-secure-proxy-flow.xml

    <cxf:proxy-client payload="body"
        enableMuleSoapHeaders="true" doc:name="SOAP">
        <cxf:ws-security>
            <cxf:ws-config>
                <cxf:property key="action"
                    value="UsernameToken 
                      Timestamp" />
                <cxf:property key="user" value="joe" />
                <cxf:property key="passwordCallbackClass"
                    value="com.mulesoft.mule.example.security.PasswordCallback" />
                <cxf:property key="mustUnderstand" value="false" />
            </cxf:ws-config>
        </cxf:ws-security>
    </cxf:proxy-client>
    

    Previously I have also just handled he entire envelope myself when using XSLT. I have then passed the user and pass into the XSLT via context params

    <xm:xslt-transformer xsl-file="xslt/ToSomethingSOAPY.xsl">
        <xm:context-property key="user" value="${my.user}" />
        <xm:context-property key="password" value="${my.pass}" />
    </xm:xslt-transformer>
    

    And then reieived them via xsl params like so:

    <xsl:param name="user" />
    
    ....
    
    <wsse:UsernameToken
                            xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
                            wsu:Id="UsernameToken-1018444980">
                            <wsse:Username><xsl:value-of select="$user" /></wsse:Username>