Definition of concerns

In this little example application, we want to explore how to bring an SSO solution to a Tapestry 5 powered application. The different technologies used by the the application are :

This example suppose that you have sufficient understanding of the security problematic (for example, difference between authentication and authorization), a basic understanding of Acegu Security, CAS and Tapestry. You should be familiar with Acegi security handbook and Tapestry 5 documentation.

In this little example, we only address the client side of the problem, so we suppose that a CAS server is already configured and running. For the leftovers of the document, we suppose that this server is accessible trough the URL : http://cas-server:8443/cas.

The application is trivial from a functional point of view. It only has two pages :

  • start page, accessible for everybody, composed by :
    • a link to the second page ;
    • a link to the CAS authentication service ;
  • a "secured" page, only accessible for authenticated user with a role matching the required role.

Download, compile and test

Complete code of this example application is accessible thanks to svn at URL svn://svn.forge.objectweb.org/svnroot/interldap/acegi-cas-client . You can browse the code with the view CVS.

Download it with the command :

$ svn co svn://svn.forge.objectweb.org/svnroot/interldap/acegi-cas-client

The project use maven 2, so you should only have to excute the following command to compile and launch a jetty server running the application :

$ cd acegi-cas-client
$ mvn jetty:run

Go to http://localhost:8080/acegi-cas-client/ to test the application !

Now that the application works, you have to configure the CAS server URL. To do that, edit the "src/main/resources/cas.properties" file.

Note on keystore configuration

You need to add the CAS server certificat to the local keystore of the web application if you don't want to rise "sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderE xception: unable to find valid certification path to requested target" or similar exceptions.

To do that, you just need the certificat of the CAS server and use the ''keytool import'' commande :

$keytool -import -keystore /path/to/jre/lib/security/cacerts -trustcacerts -file /path/to/cas/certificat.pem

Tapestry pages & components

I do a little leap in the future to show you the finale structure of the Tapestry project :

acegi-cas-client
|-- pom.xml
|-- src
|   `-- main
|       |-- java
|       |   `-- org
|       |       `-- interldap
|       |           `-- castest
|       |               |-- acegi
|       |               |   `-- ConstantRoleUserService.java
|       |               `-- tapestry
|       |                   |-- components
|       |                   |   `-- CasLogin.java
|       |                   `-- pages
|       |                       |-- Start.java
|       |                       `-- secure
|       |                           `-- Content.java
|       |-- resources
|       |   |-- applicationContext-acegisecurity.xml
|       |   |-- cas.properties
|       |   |-- log4j.dtd
|       |   |-- log4j.xml
|       |   `-- org
|       |       `-- interldap
|       |           `-- castest
|       |               `-- tapestry
|       |                   |-- components
|       |                   |   `-- CasLogin.html
|       |                   `-- pages
|       |                       |-- Start.html
|       |                       `-- secure
|       |                           `-- Content.html
|       `-- webapp
|           `-- WEB-INF
|               `-- web.xml
`-- target

This project matches a standard maven directory structure, with root package for tapestry 5 : "org.interldap.castest.tapestry". So, we configure the web.xml to handle that :

<context-param>
	<!-- The only significant configuration for Tapestry 5, this informs Tapestry
		of where to look for pages, components and mixins. -->
	<param-name>tapestry.app-package</param-name>
	<param-value>org.linagora.castest.tapestry</param-value>  
</context-param>

The ''Start'' page is the non-secure page accessible at http://localhost:8080/acegi-cas-client/, ''Content'' page is the secure page accessible at http://localhost:8080/acegi-cas-client/secure/content. The ''CasLogin'' component is in charge to create retrieve the CAS server URL to contact.

The code of each page is really simple and self-explanatory, I let you see it at the svn of the project.

Acegi configuration with CAS

That's the most interesting part, and all is done by configuration in the ''applicationContext-acegisecurity.xml'' file. I will not detail here all bean and there parameters, but the global logic of the configuration (you should read Acegi-CAS documentation for a sharper description).

So, in our configuration, the authentication provider is the ''casAuthenticationProvider''. It reads a ticket emitted by the CAS server and decide of the authentication. It is called when the ''casProcessingFilter'' catch a CAS authentication request URL on the application (configured by default to ''/j_acegi_cas_security_check'').

To acquire a ticket, the user need to be redirect to the CAS server login page. There to way to do that :

  • direct access thanks to a link ;
  • redirect after that the ''exceptionTranslationFilter'' had caught an authorization exception (for instance, if you try to go to the secure page without being authenticated) and pass the relay to ''casProcessingFilterEntryPoint''.
Other beans managed the validity cache for ticket, and are not further explored here.

When the authentication is validated by the ''casAuthenticationProvider'', Acegi Security try to retrieve authorizations attach to the user. This is done by ''casAuthoritiesPopulator'' which in turn use ''constantRoleUserService'' as back-end.

The ''constantRoleUserService'' is a really simple authorization back-end that _always_ give ''ROLE_USER'' role. It is given as example to see how to implement a simple authorization back-end.