Thursday, October 20, 2011

Using Environment Entries with WebSphere

I just spent the last few days trying to find an easy way to configure my web services deployed as annotated POJOs and I think I've found it. The idea is to provide an easy mechanism to change values through the administration console and have the java code pick up those changes at run time without redeploying. I thought about using a property file but this is inconvenient because we have a clustered environment and editing files in a deployment directory isn't the best idea.

@Resource Saves the Day
Anybody who used JNDI before the days of annotations knows that old style lookups were a little bit of a pain, but maybe I’m just being difficult. Here’s how I got it working - first thing is to add the proper entries in my web.xml:
<web-app...> ... <env-entry> <env-entry-name>DsixE/SomeParameter</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>This really rocks</env-entry-value> </env-entry> ... </web-app>
Next I add an annotation to my code:
    @Resource(name="DsixE/SomeParameter")
    private String someParamter;
I build a WAR, deploy it to WebSphere and notice that I have a new entry in the administration console:


…. which allows me to change the value at any time:


Now the container injects the value defined into member variable someParameter at run time. This is the first time I’ve really used an <env-entry> so I guess this old dog can still learn new tricks.


Tuesday, October 18, 2011

Using Browser Web Services with SSO


A while ago I discussed how web services can be invoked using javascript in a browser, but there was a nagging problem that prevented me from deploying it into a production environment - how do I prevent a hacker from manipulating the user identifier in a request? Suppose we have an application that displays an end user's payroll information. In this case we need web service operation will retrieve information specific to the user's ID. We can't just send the user's identifier in the request because that would allow spoofing the ID of another person resulting in a breach of privacy.

Retrieving the User's Identity from the Container
In a servlet, it is relatively easy to retrieve the user's identity from the request, but this presents a problem if we're using JAX-WS annotated POJOs which have no concept of HTTP or a request object. How the heck do I figure out what the user ID is? I spent many hours looking at WS-Security but I got lost in all the details about policy sets and bindings and I just couldn't get anything to work. Then I found this writeup on developerworks:
http://www.ibm.com/developerworks/websphere/library/techarticles/1001_chung/1001_chung.html?ca=drs-

It is very close to what I wanted to do, but it uses WS-Security and I was dead in the water. Instead, I fell back on servlet security configuration to get the container to use the SSO LTPA token originating from WebSeal. This is the same way I would handle the situation if I was coding a servlet, but again - I'm working with POJOs, so no request object available. The developerworks article revealed the missing piece:
@Resource WebServiceContext wsCtx;

Putting it all Together
I coded a POJO and added JAX-WS annotations to expose it as a web service, then I added an @Resource that tells the container to pass the context:
package com.dsixe;

import javax.annotation.Resource;
import javax.jws.WebService;
import javax.xml.ws.WebServiceContext;

@WebService
public class ContactsService {
    @Resource WebServiceContext wsCtx;

    public String getName() {
        String userID = wsCtx.getUserPrincipal().toString();
        
        if (userID.equals("cdomingue"))
            return "Carl";
        if (userID.equals("csmith"))
            return "Christine";
        
        return "unknown: " + userID;
    }
    
}
Until this point I had never even looked at the web.xml bundled with the annotated POJOs into the WAR that I deployed to Axis2 since there are no servlets in it. I added the following security constraints to the web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>Jax-wsPolicyTest</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
  
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>ContactsService</web-resource-name>
      <url-pattern>
        /*
      </url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <description>
        Roles allowed to execute ContactsService
      </description>
      <role-name>AllAuthenticatedUsers</role-name>
    </auth-constraint>
  </security-constraint>
  
  <security-role>
    <description>All Authenticated Users Role</description>
    <role-name>AllAuthenticatedUsers</role-name>
  </security-role>  
</web-app>
I then deployed to the container and mapped the AllAuthenticatedUsers security role just like I would for any servlet. Protecting the resource caused the container to populate the WebServiceContext object and then I could retrieve the user ID.

Done!

EDIT: I was researching attachments with JAX-WS and found this very comprehensive writeup which I though would be good to read in this context.

EDIT: Here's another interesting read on this topic.