Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apache CXF and WS-Security - Password Callback

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

like image 885
Rob Avatar asked Nov 13 '22 17:11

Rob


1 Answers

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.

like image 66
Tim Avatar answered Nov 30 '22 04:11

Tim