Friday, August 13, 2010

Using SmartGWT with Jersey RESTful backend on Spring Roo

I decided to give SmartGWT a run, and specifically the RestDataSource functionality using Jersey. To make things easier, I'm using Spring Roo to set up my project and scaffold much of the boilerplate code, configurations etc.

My approach is to have my domain objects exposed as RESTful web services and create a matching SmartGWT DataSource in order to show them in a GUI.

Let's get started, shall we?

First of all, you'll need Spring Roo (unless you just download the project source code and want to run it directly). I'm using the following Roo script to start things off:

project --topLevelPackage com.technowobble
persistence setup --provider HIBERNATE --database HYPERSONIC_IN_MEMORY

entity --class ~.domain.Message
field string --fieldName value

controller class --class ~.ws.MessageResource --preferredMapping rest

dependency add --groupId com.sun.jersey --artifactId jersey-server --version 1.3
dependency add --groupId com.sun.jersey.contribs --artifactId jersey-spring --version 1.3
dependency add --groupId com.smartgwt --artifactId smartgwt --version 2.2

gwt setup

You'll need to add the repositories for Jersey/SmartGWT manually in the genereated pom.xml:

<repository>
 <id>smartgwt</id>
 <url>http://www.smartclient.com/maven2</url>
</repository>
<repository>
    <id>maven2-repository.dev.java.net</id>
    <name>Java.net Repository for Maven</name>
    <url>http://download.java.net/maven/2/</url>
</repository>

Unfortunately the jersey-spring dependency depends on Spring 2.5.6, so that needs to be exluded (manually):

<dependency>
    <groupId>com.sun.jersey.contribs</groupId>
    <artifactId>jersey-spring</artifactId>
    <version>1.3</version>
    <!-- jersey-spring depends on Spring 2.5.6, so exluding as we're on 3.0.0X -->
    <exclusions>
 <exclusion>
     <groupId>org.springframework</groupId>
     <artifactId>spring</artifactId>
 </exclusion>
 <exclusion>
     <groupId>org.springframework</groupId>
     <artifactId>spring-core</artifactId>
 </exclusion>
 <exclusion>
     <groupId>org.springframework</groupId>
     <artifactId>spring-beans</artifactId>
 </exclusion>
 <exclusion>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context</artifactId>
 </exclusion>
 <exclusion>
     <groupId>org.springframework</groupId>
     <artifactId>spring-web</artifactId>
 </exclusion>
 <exclusion>
     <groupId>org.springframework</groupId>
     <artifactId>spring-webmvc</artifactId>
 </exclusion>
    </exclusions>
</dependency>

Add the jersey-servlet to web.xml and the corresponding jersey-servlet.xml into webapp/WEB-INF/spring (in accordance to how Spring Roo does it).

<!-- Initialize the Jersey servlet -->
<servlet>
 <servlet-name>jersey</servlet-name>
        <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/jersey-servlet.xml</param-value>
        </init-param>
 <load-on-startup>2</load-on-startup>        
</servlet>

<servlet-mapping>
 <servlet-name>jersey</servlet-name>
 <url-pattern>/rest/*</url-pattern>
</servlet-mapping>  

(The jersey-servlet.xml doesn't contain anything else than a component scan for Jersey web services).

We also have to update the urlrewrite.xml being used by Roo, to handle our servlet mapping of "/rest/*":

<rule enabled="true">
 <from casesensitive="false">/rest/**</from>
 <to last="true" type="forward">/rest/1</to>
</rule>    

In the generated Message domain object, add instructions to represent it in XML using an @XmlRootElement annotation:

package com.technowobble.domain;

import javax.persistence.Entity;
import javax.xml.bind.annotation.XmlRootElement;

import org.springframework.roo.addon.entity.RooEntity;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.tostring.RooToString;

@XmlRootElement
@Entity
@RooJavaBean
@RooToString
@RooEntity
public class Message {

    private String value;
}


The only thing we're missing on the backend now is the actual implementation of our service... A RestDataSource e.g. expects a response like the following in response to a "fetch" request (taken from the JavaDoc):

 <response>
    <status>0</status>
    <startRow>0</startRow>
   
 <endRow>76</endRow>
    <totalRows>546</totalRows>
    <data>
     
 <record>
          <field1>value</field1>
          <field2>value</field2>
   
 </record>
      <record>
          <field1>value</field1>
         
 <field2>value</field2>
      </record>
      ... 75 total records ... 
   
 </data>
 </response>

I've created some POJOs for Jersey to marshall/unmarshall that would mimic this format in the com.technowobble.ws.util-package. Essentially, there's two abstract classes (DSRequest and DSResponse) containing all common attributes, and two specialized classes (MessageDSResponse, MessageDSRequest) handling the specific marshalling of each domain object. I'm not posting the entire source here, but ask you to look at it from the source code instead...

Before you comment on this - yes, there are probably a smarter way to do this using generics etc, but that's an improvement to be done in a real-world project!

With the xml-mapping in place, now it's time to look at the Jersey resource implementation:

package com.technowobble.ws;

import java.util.Collection;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.apache.commons.beanutils.BeanUtils;
import org.springframework.stereotype.Component;

import com.technowobble.domain.Message;
import com.technowobble.ws.util.DSResponse;
import com.technowobble.ws.util.MessageDSRequest;
import com.technowobble.ws.util.MessageDSResponse;
import com.technowobble.ws.util.OperationType;

/**
 * Jersey resource for a SmartGWT {@link MessageDS}.
 * <p>
 * 
 * @see http://docs.sun.com/app/docs/doc/820-4867/ggnxo?l=en&a=view
 * @see http://blogs.sun.com/enterprisetechtips/entry/jersey_and_spring
 */
@Component
@Path("/message")
public class MessageResource {
 @Produces( { MediaType.APPLICATION_XML })
 @Consumes( { MediaType.TEXT_XML })
 @POST
 @Path("/add")
 public MessageDSResponse create(MessageDSRequest request) {
  MessageDSResponse response = new MessageDSResponse();
  
  if (request.getOperationType() != OperationType.ADD || request.getMessages().size() != 1) {
   response.setStatus(DSResponse.STATUS_FAILURE);
  } else {
   Message message = request.getMessages().iterator().next();   
 
   try {
    // create the message
    message.persist(); 
    response.addMessage(message);
    
    response.setStatus(DSResponse.STATUS_SUCCESS);
   } catch (Exception e) {
    response.setStatus(DSResponse.STATUS_FAILURE);
    e.printStackTrace();
   }   
  }
  
  return response;
 }

 @Produces( { MediaType.APPLICATION_XML })
 @Consumes( { MediaType.TEXT_XML })
 @POST
 @Path("/update")
 public MessageDSResponse update(MessageDSRequest request) {
  MessageDSResponse response = new MessageDSResponse();
  
  if (request.getOperationType() != OperationType.UPDATE || request.getMessages().size() != 1) {
   response.setStatus(DSResponse.STATUS_FAILURE);
  } else {
   try {
    Message data = (Message) request.getMessages().iterator().next();
    Message message = Message.findMessage(data.getId());
    BeanUtils.copyProperties(message, data);
    message.merge();
    response.addMessage(message);
    
    response.setStatus(DSResponse.STATUS_SUCCESS);
   } catch (Exception e) {
    response.setStatus(DSResponse.STATUS_FAILURE);
    e.printStackTrace();
   }
  }
      
  return response;
 }

 @Produces( { MediaType.APPLICATION_XML })
 @Consumes( { MediaType.TEXT_XML })
 @POST
 @Path("/remove")
 public MessageDSResponse delete(MessageDSRequest request) {
  MessageDSResponse response = new MessageDSResponse();
  
  if (request.getOperationType() != OperationType.REMOVE || request.getMessages().size() != 1) {
   response.setStatus(DSResponse.STATUS_FAILURE);
  } else {
   try {
    Message data = request.getMessages().iterator().next();
    Message message = Message.findMessage(data.getId());
    message.remove();
    response.addMessage(message);
    
    response.setStatus(DSResponse.STATUS_SUCCESS);
   } catch (Exception e) {
    response.setStatus(DSResponse.STATUS_FAILURE);
    e.printStackTrace();
   }
  }
      
  return response;
 }

 @POST
 @Produces( { MediaType.APPLICATION_XML})
 @Consumes( { MediaType.TEXT_XML })
 @Path("/read")
 public MessageDSResponse read(MessageDSRequest request) {
  MessageDSResponse response = new MessageDSResponse();
  
  response.setStartRow(request.getStartRow());
  
  if (request.getOperationType() != OperationType.FETCH) {
   response.setStatus(DSResponse.STATUS_FAILURE);
  } else {
   try {
    Collection<Message> messages = Message.findMessageEntries(request.getStartRow(), 1 + (request.getEndRow() - 

request.getStartRow()));
    long count = Message.countMessages();
    response.setEndRow(response.getStartRow()+messages.size()-1);
    response.setTotalRows((int)count);
    for (Message message : messages) {
     response.addMessage(message);
    }
   } catch (Exception e) {
    response.setStatus(DSResponse.STATUS_FAILURE);
   }
   
   response.setStatus(DSResponse.STATUS_SUCCESS);
  }
  
  return response;
 }
}

As you can see, there's one method per CRUD-reqeust from the SmartGWT RestDataSource (add, update, remove, read). They all basically parse the
incoming DSRequest and answers with a DSResponse, according to the specs of SmartGWT.

Now that the backend is in place, let's create a small GWT application to take it for a test drive!

By using Roo, there are some things to consider when adding a new module to the project, which I have blogged about previously. As with any other module, create a module file (in our case Application.gwt.xml), a host page (Application.html), configure the gwt-maven-plugin to use it (in pom.xml) and add two rules to urlrewrite.xml...

With that in place, let's have a look at the entry-point class:

package com.technowobble.gwt.client;

import com.google.gwt.core.client.EntryPoint;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.data.RestDataSource;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.types.RowEndEditAction;
import com.smartgwt.client.widgets.IButton;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.VLayout;
import com.technowobble.gwt.client.datasources.MessageDS;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class Application implements EntryPoint {
 /**
  * This is the entry point method.
  */
 public void onModuleLoad() {

  VLayout layout = new VLayout(15);
  layout.setAutoHeight();

  RestDataSource dataSource = new MessageDS();

  final ListGrid messageGrid = new ListGrid();
  messageGrid.setHeight(300);
  messageGrid.setWidth(500);
  messageGrid.setTitle("Messages");
  messageGrid.setDataSource(dataSource);
  messageGrid.setAutoFetchData(true);
  messageGrid.setCanEdit(true);
  messageGrid.setCanRemoveRecords(true);
  messageGrid.setListEndEditAction(RowEndEditAction.NEXT);

  ListGridField idField = new ListGridField("id", "Id", 40);
  idField.setAlign(Alignment.LEFT);
  ListGridField messageField = new ListGridField("value", "Message");
  messageGrid.setFields(idField, messageField);

  layout.addMember(messageGrid);

  HLayout hLayout = new HLayout(15);

  IButton updateButton = new IButton("Add message");
  updateButton.addClickHandler(new com.smartgwt.client.widgets.events.ClickHandler() {
   public void onClick(com.smartgwt.client.widgets.events.ClickEvent event) {
    Record message = new ListGridRecord();
    message.setAttribute("value", "...");
    messageGrid.addData(message);
   }
  });
  
  hLayout.addMember(updateButton);

  layout.addMember(hLayout);
  layout.draw();
 }
}

All in all, a ListGrid making use of a customized RestDataSource (MessageDS) - which might be more interesting than the actual code above...

Let's have a look at it:

package com.technowobble.gwt.client.datasources;

import com.smartgwt.client.data.OperationBinding;
import com.smartgwt.client.data.RestDataSource;
import com.smartgwt.client.data.fields.DataSourceTextField;
import com.smartgwt.client.types.DSOperationType;
import com.smartgwt.client.types.DSProtocol;
import com.technowobble.domain.Message;

/**
 * SmartGWT datasource for accessing {@link Message} entities over http in a RESTful manner.
 */
public class MessageDS extends RestDataSource {
 
 public MessageDS() {
  setID("MessageDS");
  DataSourceTextField messageId = new DataSourceTextField("id");
  messageId.setPrimaryKey(true);
  messageId.setCanEdit(false);
  
  DataSourceTextField messageValue = new DataSourceTextField("value");
  setFields(messageId, messageValue);
  
  OperationBinding fetch = new OperationBinding();
  fetch.setOperationType(DSOperationType.FETCH);
  fetch.setDataProtocol(DSProtocol.POSTMESSAGE);
  OperationBinding add = new OperationBinding();
  add.setOperationType(DSOperationType.ADD);
  add.setDataProtocol(DSProtocol.POSTMESSAGE);
  OperationBinding update = new OperationBinding();
  update.setOperationType(DSOperationType.UPDATE);
  update.setDataProtocol(DSProtocol.POSTMESSAGE);
  OperationBinding remove = new OperationBinding();
  remove.setOperationType(DSOperationType.REMOVE);
  remove.setDataProtocol(DSProtocol.POSTMESSAGE);
  setOperationBindings(fetch, add, update, remove);
    
  setFetchDataURL("rest/message/read");
  setAddDataURL("rest/message/add");
  setUpdateDataURL("rest/message/update");
  setRemoveDataURL("rest/message/remove");
 }
}

No surprises here... The RestDataSource is configured to match the operations of the RESTful backend, including the properties of the domain object we're exposing.

Done already? Then download the full source code here and give it a try!

"mvn jetty:run-war" should do the trick, although on my Windows machine I have to force a gwt-compilation using "mvn package gwt:compile jetty:run-war"

Enjoy!

Tuesday, July 13, 2010

GWT and Open-ID using Spring Security

In this post I'll combine the GWT and Spring Security integration from http://technowobble.blogspot.com/2010/05/gwt-and-spring-security.html and the Open-ID using Spring Security from http://technowobble.blogspot.com/2010/06/using-spring-securitys-openid.html. I'm assuming you've read them before reading further... :)

I was also inspired by http://www.sociallipstick.com/?p=86 and http://code.google.com/p/dyuproject/wiki/OpenidLoginWithoutLeavingPage to get this working with a pop-up as my sample application is based on GWT - hence, I don't want to direct the user to another page and loose the application state etc.

I'm also showing how to exchange Open-ID attributes with e.g. Google. As with the previous blogposts, the sample application is runnable on Google App Engine.

With no further ado, this is basically what is needed to add Open-ID support to my previous sample application:

From my second post, add Openid4javaFetcher, MyHttpCacheProvider and OpenIdUserDetailsServiceImpl-classes, update the pom.xml with the necessary Open-ID dependencies and add the customized configuration of the OpenIDAuthenticationFilter into applicationContext-security.xml.

Now that all necessary Open-ID stuff is in place, let's start adding the functionality to the application from my first post:

First of all, let's add a "Sign in with Google" button to the LoginDialogBox:

 @UiHandler("googleLoginButton")
 void googleLogin(ClickEvent e) {
  Window.open("/j_spring_openid_security_check?openid_identifier=https://www.google.com/accounts/o8/id", "openid_popup", 

"width=450,height=500,location=1,status=1,resizable=yes");
 }

It will open up a pop-up window posting to the OpenIDAuthenticationFilter, which will take care of the rest of the Open-ID functionality. As discussed in the links mentioned above, I need a callback function that can be called once the Open-ID-request is return, i.e. from the pop-up window to the GWT application window. I therefor made the following changes:

 // general callback for any type of authentication scheme
 RequestCallback callback = new RequestCallback() {
     public void onError(Request request, Throwable exception) {
      showError();
         Log.error(exception.getMessage());
     }
     
     public void onResponseReceived(Request request, Response response) {
         if (response.getStatusCode() == Response.SC_OK) {
          // notify all interested components
          fireEvent(new LoginEvent(true));
          
          // issue the command that triggered the dialog
          if (cmd != null) {
           cmd.execute();
          }
          
          hide();
          
          Log.debug("[success (" + response.getStatusCode() + "," + response.getStatusText() + ")]");
         } else {
          showError();
          Log.error(response.getStatusCode() + "," + response.getStatusText());
         }
     }
 };

 // make the callback function available from JavaScript
 private native void exportMethods(LoginDialog instance) /*-{
  $wnd.handleOpenIDResponse = function(statusCode, statusText) {
   return instance.@com.myappenginecookbook.gwt.client.LoginDialog::onAuthentication(ILjava/lang/String;)(statusCode, 

statusText);
  }
 }-*/;
 
 public void onAuthentication(final int statusCode, final String statusText) {
  // call callback with the needed parameters
  callback.onResponseReceived(null, new Response() {
         @Override
         public String getHeader(String header) {
          return null;
         }

         @Override
         public Header[] getHeaders() {
           return null;
         }

         @Override
         public String getHeadersAsString() {
           return null;
         }

         @Override
         public int getStatusCode() {
           return statusCode;
         }

         @Override
         public String getStatusText() {
           return statusText;
         }

         @Override
         public String getText() {
           return "";
         }
      });
 }

Note that I've broken out the anonymous RequestCallback so that it can be used with both ways of authenticating.

What's left now is to make sure the callback function will be called, which is the responsiblity of the OpenIdAuthenticationFailureHandler and OpenIdAuthenticationSuccessHandler-classes, which will be called by Spring Security at the end of the Open-ID request:

package com.myappenginecookbook.security;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

public class OpenIdAuthenticationSuccessHandler implements
  AuthenticationSuccessHandler {

 @Override
 public void onAuthenticationSuccess(HttpServletRequest request,
   HttpServletResponse response, Authentication authentication)
   throws IOException, ServletException {
  
  response.setContentType("text/html");
  PrintWriter out = response.getWriter();
  
  String html = 
         "<html>" +
      "<head>" +
      "</head>" +
      "<body onload=\"window.opener.handleOpenIDResponse("+HttpServletResponse.SC_OK+",'Authentication accepted');window.close

();\">" +
      "</body>" +
      "</html>";
        
        out.print(html);          
 }
}

The OpenIdAuthenticationSuccessHandler will render a simple html-fragment which utilizes JavaScript to call the exported GWT-method to signal the sucess/failure of the request. The OpenIdAuthenticationFailureHandler is basically identical, but will call the callback with the SC_UNAUTHORIZED http-code instead.

To finalize the upgraded sample app I searched the forums on how to get hold of the exchanged attributes and came up with this thread, and created a CustomOpenIDAuthenticationProvider-class to deal with it:

package com.myappenginecookbook.security;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.openid.OpenIDAttribute;
import org.springframework.security.openid.OpenIDAuthenticationProvider;
import org.springframework.security.openid.OpenIDAuthenticationToken;

public class CustomOpenIDAuthenticationProvider extends
  OpenIDAuthenticationProvider {

 @Override
 protected Authentication createSuccessfulAuthentication(
   UserDetails userDetails, OpenIDAuthenticationToken auth) {
  
  if (userDetails instanceof CustomUser) {
   CustomUser user = (CustomUser) userDetails;
   for (final OpenIDAttribute attribute : auth.getAttributes()) {
        if ("email".equals(attribute.getName())) {
          String email = attribute.getValues().get(0);
          user.setEmail(email);
        }
      }
  }

  return super.createSuccessfulAuthentication(userDetails, auth);
 }
}

Hope this helps in getting your own applications Open-ID aware! Sourcecode can be found here.

Tuesday, June 22, 2010

Using Spring Security's OpenID implementation (openid4java) on Google App Engine

The goal with this exercise is to have a running example of an OpenID login on a simple Spring application, using Google as the OpenID Provider. Note that the application will be running on Google App Engine and that Spring Roo is only used for simplicity of creating the project files. Any Spring-based application could use the same implementation.

First of all, create a simple project using Spring Roo (or any equivalent framework), including the default security setup:

project --topLevelPackage com.technowobble
persistence setup --provider DATANUCLEUS --database GOOGLE_APP_ENGINE
entity --class ~.domain.MyEntity
field string --fieldName name
controller all --package com.technowobble.controller
security setup

This setup only provides us with a form-login, which is not what we wanted. So what about OpenID?

Well, if it wasn't for Google App Engine, I would happily have added an <openid-login>-tag to applicationContext-security.xml, but things are never that easy, are they? What happens is that the OpenIDAuthenticationFilter that would have been injected relys on openid4java, which in turn uses HttpClient4 - known not to work on GAE. There is, however, a workaround that can be made which involves using the latest snapshot of openid4java (0.9.6-SNAPSHOT) instead of the stable release (0.9.5) and using the HttpFetcherFactory-functionality to inject a HttpFetcher that
actually works with GAE.

Let's add the dependencies to the pom.xml:

dependency add --groupId org.springframework.security --artifactId spring-security-openid --version  ${spring.version}
dependency add --groupId org.openid4java --artifactId openid4java-consumer --version 0.9.6-SNAPSHOT

Make sure to exlude the dependency for the stable release by adding the following to the spring-security-openid artifact in your pom.xml:

<exclusions>
 <exclusion>
 <groupId>org.openid4java</groupId>
 <artifactId>openid4java</artifactId>
 </exclusion>
</exclusions>

Note - you need to build openid4java 0.9.6-SNAPSHOT yourself and install it to your repository, unless you can't find a public repository that has it.

Next thing is to configure the OpenIDAuthenticationFilter manually in applicationContext-security.xml, in order to get to the point where the HttpFetcherFactory can be injected:

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-

3.0.xsd">

 <!-- HTTP security configurations -->
    <http auto-config="true" use-expressions="true">
     <form-login login-processing-url="/static/j_spring_security_check" login-page="/login" authentication-failure-url="/login?

login_error=t"/>
        <logout logout-url="/static/j_spring_security_logout"/>
        
        <!-- Configure these elements to secure URIs in your application -->
        <intercept-url pattern="/myentitys/**" access="isAuthenticated()" />
 <intercept-url pattern="/choices/**" access="hasRole('ROLE_ADMIN')"/>        
        <intercept-url pattern="/member/**" access="isAuthenticated()" />
        <intercept-url pattern="/resources/**" access="permitAll" />
        <intercept-url pattern="/static/**" access="permitAll" />
        <intercept-url pattern="/**" access="permitAll" />
 
 <!-- Can't add the <openid-login>-tag, as it's default filter violates app engine's whitelist -->
 <custom-filter position="OPENID_FILTER" ref="myOpenIDAuthenticationFilter" /> 
    </http>
    
    <!-- Start configuration of OpenId-filter -->    
     <beans:bean id="myOpenIDAuthenticationFilter" class="org.springframework.security.openid.OpenIDAuthenticationFilter">
  <beans:property name="authenticationManager" ref="authenticationManager"/>
    <beans:property name="consumer" ref="myOpenID4JavaConsumer"></beans:property>
 </beans:bean>
 
 <beans:bean id="myOpenID4JavaConsumer" class="org.springframework.security.openid.OpenID4JavaConsumer">
  <beans:constructor-arg index="0" ref="myConsumerManager"></beans:constructor-arg>
  <beans:constructor-arg index="1">
   <beans:list value-type="org.springframework.security.openid.OpenIDAttribute">
    <beans:bean class="org.springframework.security.openid.OpenIDAttribute">
     <beans:constructor-arg index="0" value="email"/>
     <beans:constructor-arg index="1" value="http://axschema.org/contact/email"/>
     <beans:property name="required" value="true"/>
    </beans:bean>
   </beans:list>
  </beans:constructor-arg>
 </beans:bean>
 
 <beans:bean id="myConsumerManager" class="org.openid4java.consumer.ConsumerManager">
  <beans:constructor-arg index="0" ref="myRealmVerifierFactory"></beans:constructor-arg>
  <beans:constructor-arg index="1" ref="myDiscovery"></beans:constructor-arg>
  <beans:constructor-arg index="2" ref="myHttpFetcherFactory"></beans:constructor-arg>
 </beans:bean>
 
 <beans:bean id="myRealmVerifierFactory" class="org.openid4java.server.RealmVerifierFactory">
  <beans:constructor-arg index="0" ref="myYadisResolver"></beans:constructor-arg>
 </beans:bean>
 
 <beans:bean id="myYadisResolver" class="org.openid4java.discovery.yadis.YadisResolver">
  <beans:constructor-arg index="0" ref="myHttpFetcherFactory"></beans:constructor-arg>
 </beans:bean>
 
 <beans:bean id="myHttpFetcherFactory" class="org.openid4java.util.HttpFetcherFactory">
  <beans:constructor-arg index="0" ref="myProvider"></beans:constructor-arg>
 </beans:bean>
 
 <beans:bean id="myProvider" class="com.technowobble.security.MyHttpCacheProvider"></beans:bean>
 
 <beans:bean id="myDiscovery" class="org.openid4java.discovery.Discovery">
  <beans:constructor-arg index="0" ref="myHtmlResolver"></beans:constructor-arg>
  <beans:constructor-arg index="1" ref="myYadisResolver"></beans:constructor-arg>
  <beans:constructor-arg index="2" ref="myXriResolver"></beans:constructor-arg>
 </beans:bean>
 
 <beans:bean id="myHtmlResolver" class="org.openid4java.discovery.html.HtmlResolver">
  <beans:constructor-arg index="0" ref="myHttpFetcherFactory"></beans:constructor-arg>
 </beans:bean> 
 
 <beans:bean id="myXriResolver" class="org.openid4java.discovery.xri.XriDotNetProxyResolver">
  <beans:constructor-arg index="0" ref="myHttpFetcherFactory"></beans:constructor-arg>
 </beans:bean>
 <!-- End configuration of OpenId-filter --> 

 <!-- Configure Authentication mechanism -->
    <authentication-manager alias="authenticationManager">
     <!-- SHA-256 values can be produced using 'echo -n your_desired_password | sha256sum' (using normal *nix environments) -->
     <authentication-provider>
      <password-encoder hash="sha-256"/>
         <user-service>
             <user name="admin" password="8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918" authorities="ROLE_ADMIN"/>
          <user name="user" password="04f8996da763b7a969b1028ee3007569eaf3a635486ddab211d512c85b9df8fb" 

authorities="ROLE_USER"/>
      </user-service>
     </authentication-provider>
 <authentication-provider ref="myOpenIDAuthenticationProvider" />
 </authentication-manager>
 
 <beans:bean id="myOpenIDAuthenticationProvider" class="org.springframework.security.openid.OpenIDAuthenticationProvider">
  <beans:property name="userDetailsService" ref="myOpenIdUserDetailsService"></beans:property>
 </beans:bean>
 
 <beans:bean id="myOpenIdUserDetailsService" class="com.technowobble.security.OpenIdUserDetailsServiceImpl"></beans:bean> 

</beans:beans>

There is only one class-reference that is interesting from a GAE perspective in the configuration above - MyHttpCacheProvider. This will inject an Openid4javaFetcher instance that is using a GAE-friendly URLFetchService (taken from the step2 project:

package com.technowobble.security;

import com.google.appengine.api.urlfetch.URLFetchServiceFactory;
import org.openid4java.util.HttpFetcher;
import com.google.inject.Provider;
import com.google.step2.example.consumer.appengine.Openid4javaFetcher;

public class MyHttpCacheProvider implements Provider<HttpFetcher> {

 @Override
 public HttpFetcher get() {
  return new Openid4javaFetcher(URLFetchServiceFactory.getURLFetchService());
 } 
}

Another thing to note is the added intercept-url pattern for "myentitys/**" which secures access to the domain objects created earlier. (This could of course be changed to whatever that needs to be secured).

There's also a custom UserDetailsService that handles the creation of UserDetails-objects using the supplied OpenID information:

package com.technowobble.security;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

/**
 * Implementation of {@link UserDetailsService} for OpenId
 */
public class OpenIdUserDetailsServiceImpl implements UserDetailsService {

 @Override
 public UserDetails loadUserByUsername(String username)
   throws UsernameNotFoundException, DataAccessException {
  return new User(username, "", true, true, true, true, getAuthorities());
 }
 
 /**
  * Utility method for creating a list of {@link GrantedAuthority} objects
  * @return
  */
 private Collection<GrantedAuthority> getAuthorities() {
  List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(2);
  authList.add(new GrantedAuthorityImpl("ROLE_USER"));
  
  return authList;
 }
}

The last thing to be done is to add an option to log in using OpenID, by adding the following form to webapp/WEB-INF/views/login.jspx:

<spring:url value='/static/j_spring_openid_security_check' var="openid_url"/>
<form name="o" action="${openid_url}" method="POST">    
 <div>
     <input type="hidden" id="openid_identifier" name='openid_identifier' value="https://www.google.com/accounts/o8/id"/>
 </div>
 <div class="submit">
     <script type="text/javascript">Spring.addDecoration(new Spring.ValidateAllDecoration({elementId:'proceed', 

event:'onclick'}));</script>
     <input id="proceed" type="submit" value="Sign in with Google"/>
 </div>         
</form>


Everything regarding security is now set up, but there are a few more things to do before we can deploy it using "mvn gae:deploy" (at least if using Spring Roo 1.1.0.M1).

First of all we need to downgrade the datanucleus-appengine artifact to version 1.0.4, as GAE throws an exception when using the default 1.0.7.final from Spring Roo. Secondly, to make the jspx-files compile, we need to add an empty dummy.jsp to webapp/WEB-INF/views (triggers the pre-compilation) and a <jsp:directive.page isELIgnored="false"/> directive to webapp/WEB-INF/layouts/default.jspx (takes care of jstl-versioning problems).

Make sure to update appengine-web.xml with your own application id/version and type "mvn gae:deploy". This command will take an unacceptable amount of time due to precompilation of all jspx-files (supposedly), but will eventually succeed, and you'll be asked for your app engine credentials. Feet up and relax!

A full source of the example project can be found here.

Please note that I haven't successfully managed to run this locally using "mvn gae:run", so that's one question I won't be able to answer. One option might be to switch to HIBERNATE/HYPERSONIC and use "mvn tomcat:run" instead, but I haven't tried it. Note that you have to comment out the constructor argument for the myHttpFetcherFactory-bean in your applicationContext-security.xml, because you're no longer in a GAE environment!

Monday, June 14, 2010

Adding a custom GWT module in a Roo-project

So I started looking into Roo the other day. Seems like a nice tool (once it matures a little more). Anyway - I wanted to create a custom module in addition to the two being generated when adding GWT-support to the project. Not as easy as I thought, mainly because I hadn't seen the Url Rewrite Filter before. I ended up with a 404, no matter what I did, and couldn't figure out why. Frustration!

After reading up on the url rewrite stuff, I was ready to give it another try. Let's create a module called Application, based on the standard sample application you'll get using the GWT wizard in Eclipse. First of all, create the Roo project using the following instructions:

project --topLevelPackage com.technowobble
persistence setup --provider HIBERNATE --database HYPERSONIC_IN_MEMORY
entity --class com.technowobble.domain.MyEntity
gwt setup

(The "gwt setup" setup command generates some code that will not compile unless there's a persistence setup and at least one entity.)

Next, create the Application.gwt.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.0.1//EN" "http://google-web-toolkit.googlecode.com/svn/tags/2.0.1/distro-source/core/src/gwt-module.dtd">
<module rename-to="application">
  <inherits name="com.google.gwt.user.User" />
  <inherits name="com.google.gwt.user.theme.standard.Standard" />

  <inherits name="com.technowobble.gwt.ApplicationCommon" />

  <source path="client" />
  <source path='shared'/>
  
  <entry-point
    class="com.technowobble.gwt.client.Application" />
</module>

We also need to add the actual Application-class (Application.java) and the service classes (GreetingService.java, GreetingServiceAsync.java, GreetingServiceImpl.java and FieldVerifier) from the Eclipse GWT sample application. While we're at it, put the Application.html and Application.css into the src/main/webapp folder.

Of course, the service servlet needs to be added to web.xml:

<servlet>
    <servlet-name>greetServlet</servlet-name>
    <servlet-class>com.technowobble.gwt.server.GreetingServiceImpl</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>greetServlet</servlet-name>
    <url-pattern>/application/greet</url-pattern>
  </servlet-mapping>

Before we can access the new module we need to add the following rules to urlrewrite.xml:

<rule enabled="true">
        <from casesensitive="false">/application/**</from>
        <to last="true" type="forward">/application/$1</to>
    </rule>
    <rule enabled="true">
        <from casesensitive="false">/Application.html</from>
        <to last="true" type="forward">/Application.html</to>
    </rule>    

The html file contains a reference to the stylesheet, which needs to be updated due to the Url Rewrite Filter - just change the href to href="static/Application.css".

That's it. Of course, an interesting exercise would be to combine this with the use of the DispatcherServlet and a Spring/GWT-integration controller like the GwtRpcController being used in my blog entry about Spring Security and GWT

You can find the full source here.

Sunday, June 6, 2010

How to integrate Facebook's JavaScript SDK with GWT

First of all - you need to have a Facebook account. Then you need to create an application on Facebook at http://www.facebook.com/developers/createapp.php, that will give you an App Id. This id is tightly connected to the url/site from which you will be using the JavaScript SDK and you'll be using it later.

For development, localhost addresses works fine - i.e. http://127.0.0.1:8888.

Next thing is to add the actual JavaScript library to you hostpage, which according to Facebook means adding this piece of JavaScript just before the body-endtag:

<!-- Facebook integration -->
<div id="fb-root"></div>
<script>
(function() {
var e = document.createElement('script'); e.async = true;
e.src = document.location.protocol +
'//connect.facebook.net/en_US/all.js';
document.getElementById('fb-root').appendChild(e);
}());
</script>

It will dynamically add a script-tag into your html-file, which bypasses the "Same Origin Policy" all together. (No need for a xd_receiver.htm, as with previous versions of the API, that is).

Now it's time to initialize the Facebook API using native methods in GWT:

private native String initFacebookAPI()
/*-{
$wnd.FB.init({appId: '<YOUR_APP_ID_HERE>', status: true, cookie: true, xfbml: true});
$wnd.FB.Event.subscribe('auth.sessionChange', function(response) {
if (response.session) {
// A user has logged in, and a new cookie has been saved
$wnd.onLogin();
} else {
// The user has logged out, and the cookie has been cleared
$wnd.onLogout();
}          
});
}-*/;

$wnd.FB works because we dynamically included the Facebook .js-file in the previous step. So, anything you'd want to do when initializing the API can be done here, e.g. adding subscriptions to events (as above).

As can be seen, two different methods are being called when a sessionChange-event occurs (depending on what happened). These methods will be regular GWT-methods, but we need to export them into the DOM in order to call them from within native code:

private native void exportMethods(Application instance) /*-{
$wnd.onLogin = function() {
return instance.@com.technowobble.gwt.client.Application::onLogin()();
}
$wnd.onLogout = function() {
return instance.@com.technowobble.gwt.client.Application::onLogout()();
}
$wnd.onAPICall = function(callback, response, exception) {
return instance.@com.technowobble.gwt.client.Application::onAPICall

(Lcom/google/gwt/user/client/rpc/AsyncCallback;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)

(callback, response, exception);
}
}-*/;

There's also on more function being exported (which needs some explanation...) It serves as a callback function for a native method exposing the Facebook Graph API:

private native void callAPI(String path, AsyncCallback<JavaScriptObject> callback) /*-{
$wnd.FB.api(path, function(response) {
if (!response) {
alert('Error occured');
} else if (response.error) {
alert($wnd.dump(response));
// call callback with the actual error
$wnd.onAPICall(callback, null, response.error);
} else if (response.data) {
alert($wnd.dump(response));
// call callback with the actual json-array
$wnd.onAPICall(callback, response.data, null);
} else {
alert($wnd.dump(response));
// call callback with the actual json-object
$wnd.onAPICall(callback, response, null);
} 
});
}-*/;

What happens here is that any GWT-method now can make calls like this:

callAPI("/me", new AsyncCallback<JavaScriptObject>() {

@Override
public void onFailure(Throwable caught) {
Window.alert(caught.getMessage());
}

@Override
public void onSuccess(JavaScriptObject result) {
UserJso fbUser = (UserJso) result;
Window.alert(fbUser.getFullName());
}
});   

The onAPICall exported previously is the method binding the first call to the AsyncCallback:

public void onAPICall(AsyncCallback<JavaScriptObject> callback,
JavaScriptObject response, JavaScriptObject exception) {
if (response != null) {
callback.onSuccess(response);
} else {
ExceptionJso e = (ExceptionJso) exception;
callback.onFailure(new Exception(e.getType() + ": " + e.getMessage()));
}
}

Note that the AsyncCallback used for a specific Graph API call, e.g. "/me" is free to cast the result to an overlay type (the result should be a json-representation). The UserJso looks like this:

package com.technowobble.gwt.client.json;

import com.google.gwt.core.client.JavaScriptObject;

public class UserJso extends JavaScriptObject {
// Overlay types always have protected, zero-arg constructors
protected UserJso() { }

public final native String getFirstName() /*-{ return this.first_name; }-*/;
public final native String getLastName()  /*-{ return this.last_name;  }-*/;

public final String getFullName() {
return getFirstName() + " " + getLastName();
}
}

A full example project can be found here and should get you started on more elaborative usage.

Wednesday, May 19, 2010

GWT and Spring Security

Update! - Based on the post below, and my other post regarding Spring Security and OpenID, I have added Open-ID support to the sample application below. For those interested, here's the write-up of changes.



I've spent quite some time digging into ways of integrating GWT and Spring Security. It all started by reading the following post in the GWT Forum - Best practices/ideas for GWT with Spring Security (or equivalent), and then checking out this blog - GWT and Spring Security. To make matters worse, I started reading Security for GWT Applications and specifically about the "Cross-Site Request Forging"-attacks.

Now, what could I do about it?

Well, starting by setting up my own project (Maven-based) with all updated dependencies (GWT 2.0.3 etc) and started reading the Spring Security Reference Documentation (puh!).

Instead of See Wah Cheng's approach of implementing a custom authentication service, I decided to rely on standard namespace configuration (with some extra configuration, of course). So, after adding the normal filter declaration into web.xml I came up with this configuration for my "applicationContext-security.xml":

<http auto-config="true" entry-point-ref="http401UnauthorizedEntryPoint"
  create-session="always">
  <form-login authentication-success-handler-ref="authenticationSuccessHandler"
   authentication-failure-handler-ref="authenticationFailureHandler" />
  <logout success-handler-ref="logoutSuccessHandler" />

  <custom-filter before="CONCURRENT_SESSION_FILTER" ref="XSRFAttackFilter" />
 </http>

 <beans:bean id="XSRFAttackFilter"
  class="com.myappenginecookbook.security.XSRFAttackFilter" />

 <!--
  Use this entry point to signal to the GWT-caller that the user needs
  to log in to access the resource
 -->
 <beans:bean id="http401UnauthorizedEntryPoint"
  class="com.myappenginecookbook.security.Http401UnauthorizedEntryPoint" />

 <beans:bean id="authenticationSuccessHandler"
  class="com.myappenginecookbook.security.GWTAuthenticationSuccessHandler" />
 <beans:bean id="authenticationFailureHandler"
  class="com.myappenginecookbook.security.GWTAuthenticationFailureHandler" />
 <beans:bean id="logoutSuccessHandler"
  class="com.myappenginecookbook.security.GWTLogoutSuccessHandler" />

Don't worry about the XSRFAttackFilter class for now (we'll get to it).

I'll start by explaining how the entry-point works, and work my way deeper into the code from there...

Basically, I'm relying on standard HTTP Status Codes to signal to the caller (GWT) what Spring Security is requesting, hence the entry-point will always return HttpServletResponse.SC_UNAUTHORIZED:

/**
     * Always returns a 401 error code to the client.
     */
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException arg2) throws IOException,
            ServletException {
        if (logger.isDebugEnabled()) {
            logger.debug("Pre-authenticated entry point called. Rejecting access");
        }
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication required");
    }

The GWTAuthenticationSuccessHandler, GWTAuthenticationFailureHandler and GWTLogoutSuccessHandler works the same way, i.e. always returning HttpServletResponse.SC_OK, HttpServletResponse.SC_UNAUTHORIZED and HttpServletResponse.SC_OK respectively.

The response needs to be picked up in the onFailure()-method of the calling method and handled. I created an AutoErrorHandlingAsyncCallback that will show a login dialog whenever a user needs to login. It's capable of re-submitting the unauthorized command once the user is successfully logged in, but the interesting part is the login functionality itself:

@UiHandler("loginButton")
 void login(ClickEvent e) {
  RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, "/j_spring_security_check");
  rb.setHeader("Content-Type", "application/x-www-form-urlencoded");
  rb.setRequestData("j_username=" + URL.encode(email.getText() + "&j_password=" + URL.encode(password.getText())));
  
  rb.setCallback(new RequestCallback() {
      public void onError(Request request, Throwable exception) {
       showError();
          Log.error(exception.getMessage());
      }
      public void onResponseReceived(Request request, Response response) {
          if (response.getStatusCode() == 200) {
           // notify all interested components
           fireEvent(new LoginEvent(true));
           
           // issue the command that triggered the dialog
           if (cmd != null) {
            cmd.execute();
           }
           
           hide();
           
           Log.debug("[success (" + response.getStatusCode() + "," + response.getStatusText() + ")]");
          } else {
           showError();
           Log.error(response.getStatusCode() + "," + response.getStatusText());
          }
      }
  });
  
        try {
   rb.send();
  } catch (RequestException re) {
   re.printStackTrace();
  }
 }

As you can see, I'm posting to the standard form login page using the RequestBuilder, and the returning response will hold the status codes from the handlers described above.

Given that we came here due to trying to access a restricted resource, the login dialog will re-issue the same command. Also note that it will fire a custom event whenever a user is successfully authenticated, so that other widgets can take appropriate actions.

Now - back to the XSRFAttackFilter! Reading about how to protect your application from XSRF-attacks I stumbled over the following text: Perhaps the simplest solution is to simply add the cookie value to your URL as a GET parameter. The important thing is to get the cookie value up to the server, somehow..

I decided to add a way for the caller to submit the cookie value with any call to the server by modifying the ServiceEntryPoint of the Async-interface:

/**
     * Utility class to get the RPC Async interface from client-side code, with the improved
     * duplication of session cookie information, see {@link http://groups.google.com/group/Google-Web-Toolkit/web/security-for-gwt-applications}.
     * 
     * To be used in conjunction with the {@link XSRFAttackFilter}. 
     */
    public static final class SecureUtil  { 
     private static String entryPoint = null;
     
        public static final SecureServiceAsync getInstance() {
         SecureServiceAsync instance = SecureServiceAsync.Util.getInstance();
         ServiceDefTarget target = (ServiceDefTarget) instance;
         
         if (entryPoint == null) {
          entryPoint = target.getServiceEntryPoint();
         }
                
            target.setServiceEntryPoint(entryPoint + "?JSESSIONID="+Cookies.getCookie("JSESSIONID"));
                
                return instance;
            }
            

        private SecureUtil()
        {
            // Utility class should not be instantiated
        }
    } 

So, instead of calling SecureServiceAsync.Util.getInstance() you'd call SecureService.SecureUtil.getInstance(). This extra piece of information is picked up by the custom XSRFAttackFilter and will be matched against the session id on the server:

@Override
 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
     throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        
        String cookieId = request.getParameter("JSESSIONID");
        String sessionId = request.getSession().getId();
        
        if (log.isLoggable(Level.FINE)) {
         log.fine("cookieId=" + cookieId + " / sessionId=" + sessionId);
        }
        
        // if the user is authenticated, the cookie session id and the id sent as a request param must match
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        
        if (authentication != null && !sessionId.equals(cookieId)) {
         SecurityContextHolder.clearContext();         
         throw new SessionAuthenticationException("Invalid session - you have been logged out!");
        }
        
        chain.doFilter(request, response);
 }

That's it! Hopefully this will give you some more ideas of how GWT and Spring Security can be used together!

For those of you that want a running example, here's the full project source.

Thursday, May 13, 2010

Spring's mailSender with Google App Engine

Ever wanted to use Spring's mailSender in a GAE hosted application? This is how it's done!

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
  <!-- Specific Google protocol used with Google App Engine -->
  <property name="protocol" value="gm" />
 </bean>

Wednesday, May 12, 2010

Introducing Technowobble!

Technowobble is my place to post interesting stuff related to technology in general.

Right now I'm spending time looking into Spring integration with Google Web Toolkit (GWT) in combination with Google App Engine (GAE) and fibbling with my brand new Mac Mini - which is hooked up to my Samsung flatscreen and only accessed through my Iphone (no keyboard, no mouse!).

Another thing catching my tech-attention right now is Spring Roo - I wonder what will be?

Happy reading!