5

I have followed a mash up of a couple tutorials and neither too succesfully!

I am trying to get Apache CXF and WS-Security to call back to my Spring Security authenticator. All is close to working but at tne moment I have a problem getting the password to give Spring security out of the WS-call back.

The Handler below gets galled but pc.getPassword() is null. I want this to be the password sent in Soap so I can pass it to spring

public class ServerPasswordCallback implements CallbackHandler {

public void handle(Callback[] callbacks) throws IOException, 
    UnsupportedCallbackException {

    WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];

    pc.setPassword( pc.getPassword() );
}

My interceptor is set up as so

 <bean id="wsAuthenticationInterceptor" class="com.olympus.viewtheworld.server.security.auth.WSAuthenticationInInterceptor">
    <constructor-arg index="0">
        <map key-type="java.lang.String" value-type="java.lang.Object">
            <entry key="action" value="UsernameToken" />
            <entry key="passwordType" value="PasswordText" />
            <entry key="passwordCallbackClass" value="com.olympus.viewtheworld.server.security.auth.ServerPasswordCallback" />
        </map>
    </constructor-arg>
    <property name="authenticationManager" ref="authenticationManager"/>
</bean>

 <jaxws:endpoint id="secureHelloService"
                 implementor="#secureHelloServiceImpl"
                 implementorClass="com.olympus.viewtheworld.server.service.Impl.SecureHelloServiceImpl"
                 address="/SoapService/secure/hello">
    <jaxws:serviceFactory>
        <ref bean="jaxws-and-aegis-service-factory" />
    </jaxws:serviceFactory>
    <jaxws:inInterceptors>
        <bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor"/>
        <ref bean="wsAuthenticationInterceptor" />
    </jaxws:inInterceptors>
</jaxws:endpoint>

And the soap request I am sending out of SoapUI is

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:test="http://test/">
   <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>rob2</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">passwordxx</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
   <soapenv:Body>
      <test:hello>
         <!--Optional:-->
         <hello>asdf</hello>
      </test:hello>
   </soapenv:Body>
</soapenv:Envelope>

Version wise it is Spring 3.1 and CXF 2.7.0

What do I need to do to see "passwordxx" in the ServerPasswordCallback class? Is it the Soap request, the config or just wrong?!

Cheers, Rob

Tim
  • 19,793
  • 8
  • 70
  • 95
Rob
  • 703
  • 1
  • 7
  • 18

2 Answers2

1

It appears from the documentation on org.apache.ws.security.handler.WSHandlerConstants.PW_CALLBACK_CLASS that the method should instead call pc.setPassword with the stored password to compare the user provided password against as argument, instead of the user provided password itself:

This tag refers to the CallbackHandler implementation class used to obtain passwords. The value of this tag must be the class name of a javax.security.auth.callback.CallbackHandler instance. The callback function

javax.security.auth.callback.CallbackHandler.handle(javax.security.auth.callback.Callback[])

gets an array of org.apache.ws.security.WSPasswordCallback objects. Only the first entry of the array is used. This object contains the username/keyname as identifier. The callback handler must set the password or key associated with this identifier before it returns. The application may set this parameter using the following method:

call.setProperty(WSHandlerConstants.PW_CALLBACK_CLASS, "PWCallbackClass");


I'm using a org.apache.ws.security.validate.Validator to check the validity of the supplied password, and setting the Spring security context there:

@Bean(name = "wssforjInInterceptor")
public WSS4JInInterceptor wssforjInInterceptor() {
    // Configure how we ask for username and password
    Map<String, Object> props = new HashMap<>();
    props.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
    props.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);

    // Password callback
    props.put(WSHandlerConstants.PW_CALLBACK_REF, passwordCallbackHandler());

    // Validator registration
    Map<QName, Object> validators = new HashMap<>();
    String WSS_WSSECURITY_SECEXT_1_0_XSD = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
    QName qName = new QName(WSS_WSSECURITY_SECEXT_1_0_XSD, WSHandlerConstants.USERNAME_TOKEN, "");
    validators.put(qName, usernameTokenValidator());
    props.put(WSS4JInInterceptor.VALIDATOR_MAP, validators);

    WSS4JInInterceptor wss4jInInterceptor = new WSS4JInInterceptor(props);
    return wss4jInInterceptor;
}

I'm not sure whether that approach is any better or worse (I'd appreciate some feedback on this), but perhaps it's useful for the next person to come across this issue. There appears to be a lack of decent up to date documentation on how to integrate Spring security and CXF.

Tim
  • 19,793
  • 8
  • 70
  • 95
0

Ok so this is not the ideal solution and hopefully a better answer will come along a bit later down the line, most likely when I have some more time to look at this.

I use a regex to inspect the full packet and pull out the password field. Code is below. Will update later when I work out the right way to do this as this is defintley not it!

 WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
        String mydata= pc.getRequestData().getMsgContext().toString();        
        Pattern pattern = Pattern.compile("<wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">(.*?)<");
        Matcher matcher = pattern.matcher(mydata);
        if (matcher.find())
        {
             pc.setPassword( matcher.group(1) );
        }
Rob
  • 703
  • 1
  • 7
  • 18