views:

102

answers:

2

Hi all, I'm setting up a glassfish server with a single EJB3 as a mocked up backend for a POC. Everything was working fine until I went to add some basic authentication. Just plan text userid and password, nothing sophisticated for this job. I added the following annotations to the EJB:

@WebService(name = "Banking", serviceName = "Banking", targetNamespace = BANKING_NAMESPACE)
@DeclareRoles("user")
@Stateless
public class Banking {
    ...

    @RolesAllowed("user")
    @SOAPBinding(parameterStyle = ParameterStyle.BARE)
    @WebMethod(action = BANKING_NAMESPACE + "/logon", operationName = "logon")
    @WebResult(targetNamespace = XmlStrings.BANKING_MODEL_NAMESPACE)
    public LogonResponse logon(@WebParam(targetNamespace = XmlStrings.BANKING_MODEL_NAMESPACE) Logon request) throws WebServiceException {
     ...
    }
}

According to what I've read of EJB3 spec, this is pretty common for doing a SOAP web service.

However when I send this xml:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:mod="http://www.dhcbank.com/banking/model"&gt;
    <soapenv:Header>
        <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"&gt;
            <wsse:UsernameToken wsu:Id="UsernameToken-79" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"&gt;
                <wsse:Username>fred</wsse:Username>
                <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"&gt;fred&lt;/wsse:Password&gt;
            </wsse:UsernameToken>
        </wsse:Security>
    </soapenv:Header>
    <soapenv:Body>
        <mod:logon/>
    </soapenv:Body>
</soapenv:Envelope>

I get the following error back as a SOAP fault:

java.lang.Exception: Client not authorized for invocation of public com.dhcbank.www.banking.schema.LogonResponse com.dhcbank.www.banking.Banking.logon(com.dhcbank.www.banking.schema.Logon) throws javax.xml.ws.WebServiceException

And in the glassfish log:

[#|2010-10-10T12:49:27.497+1100|INFO|glassfish3.0.1|javax.enterprise.system.core.security|_ThreadID=41;_ThreadName=http-thread-pool-8080-(2);|JACC Policy Provider: Failed Permission Check, context(BankingEAR/Banking_war_internal)- permission((javax.security.jacc.EJBMethodPermission Banking logon,ServiceEndpoint,com.dhcbank.www.banking.schema.Logon))|#]

In the glassfish admin screens I added a user called fred with a fred password and assigned it to a groups called user. But that didn't work.

I did some more reading which suggested that I create a sun-ejb-jar.xml file and add it to the ear file. So I created it with this content:

<sun-ejb-jar>
    <enterprise-beans>
        <ejb>
            <ejb-name>Banking</ejb-name>
            <webservice-endpoint>
                <port-component-name>Banking</port-component-name>
                    <login-config>
                        <auth-method>BASIC</auth-method>
                        <realm>file</realm>
                </login-config>
            </webservice-endpoint>               
        </ejb>
    </enterprise-beans>
</sun-ejb-jar>

This is as near as I can tell, correct. However I could not find anything that told me what the values of the port-component-name element should be. So I don't know if I've got it right.

Security does still not appear to be working and I cannot figure out why. Does anyone have any experience with this and can point me at what I've got wrong or not done?

A: 

I don't think that you're currently creating the appropriate HTTP header for Basic Authentication. I'm not sure how you create the SOAP request but if you're using a JAX-WS client, the JAX-WS FAQ documents the following:

Q. How do I do basic authentication in JAX-WS?

You can do the following:

HelloService service = new HelloService();
Hello proxy = (service.getHelloPort());
((BindingProvider)proxy).getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "userfoo");
((BindingProvider)proxy).getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "passbar");

USERNAME_PROPERTY, PASSWORD_PROPERTY are used primarily for service requests. I think when you instantiate Service, it fetches WSDL and the server is returning 401. You could try any one of the following solutions.

  1. Use java.net.Authenticator class in your client application.
  2. Provide a local access to the WSDL using catalog. There is a catalog sample in the jax-ws distribution.
  3. Configure web.xml to allow GET requests without authentication

And unless I'm wrong, the usernametoken would fit if the webservice expects the authentication in the SOAP header, which is not the case according to your description.

In other words, for me, you're currently not sending the credentials for the BASIC auth.

See also

Pascal Thivent
I'm using SoapUI and the xml is what it generates.
Derek Clarkson
@Derek Would this help: http://thewonggei.wordpress.com/2010/08/05/configure-http-basic-auth-once-for-soapui-test-suties/
Pascal Thivent
Okies, found some errors in the xml because I hand typed the security section. I've fixed those in the above code and retested in SoapUI. The Soap message is correct and this is the correct layout of the ws-security headers. I've used them before for basic security and in many SoapUI tests. Usually I add the user to the SoapUI project and set it on the Soap request from the SoapUI auth settings. SoapUI then generates this message with the embedded ws-security elements.
Derek Clarkson
@Derek My point was that nothing in your question allowed to conclude that the *HTTP header* for basic auth was defined correctly (the ws-security header is irrelevant for the question), hence my answer :)
Pascal Thivent
Okies. Your answer was confusing me because I've not used http headers for authentication of soap requests. Always used ws-security lements in the SOAP header as per above. :-)
Derek Clarkson
@Derek. No problem. [Basic authentication](http://en.wikipedia.org/wiki/Basic_access_authentication) is a "pure HTTP" thing and you need to set a suitable `Authorization` HTTP header field. Did you configure SoapUI for that as suggested in [the other link posted above](http://thewonggei.wordpress.com/2010/08/05/configure-http-basic-auth-once-for-soapui-test-suties/)? What do you get after doing so?
Pascal Thivent
Yes that was confusing me. People I've been working with were referring to the ws-security plain text authorization in the SOAP headers as BASIC authentication. :-( I've now worked out what was wrong with the code and what it was doing. I'll post an answer once I've cleaned it up and filtered out the cruft.
Derek Clarkson
A: 

I'm assuming your declared role "user" is the same role name in your file realm? if not provide this mapping in your descriptor:

<sun-ejb-jar>
   <security-role-mapping>
     <role-name>user</role-name>
     <group-name>filerealm-group-name</group-name>
   </security-role-mapping>
   ...
chrome
Yep, I came across that too. At first I had a group within the file realm in glassfish called "user" as well as a role called "user" in the EJB. I then tried added a security-role-mapping and changing the glasshfish group to "customers", mapping "user" to "customers as per you example. It didn't work. Every time I reload the ear file containing the ejb I see messages from Glassfish saying "No Principals mapped to role "user". I suspect this is the core of the issue.
Derek Clarkson
I also see a message about two ejbs (although I only have one) being mapped with the same name. If I comment out the @Stateless annotation, the message goes away. However all authentication is also turned off. So I seem to need that annotation.
Derek Clarkson