Thursday, December 13, 2012

Configuring CAS Externally using Spring Import

Over the past few months, my team has been working on integrating the Jasig CAS (Central Authentication Service) framework with our application. CAS is a feature rich enterprise Single Sign On (SSO) service. Most importantly, it's very flexible. We need this flexibility because we have several customers, each with different authentication requirements:. Some with and without Active Directory, and others needing PKI (X.509) authentication. Beyond that, we also need to support light weight options for local development. So the problem we had to solve was how to configure CAS externally so we could dynamically change authentication mechanisms without having to rebuild CAS.

Let me first explain that the recommended way to extend CAS is using maven overlays. It's real simple to modify the default behavior in CAS by copying the files from the CAS WAR into your own WAR directory. For example, most of the authentication configuration exists in /WEB-INF/deployerConfigContext.xml. To change the default authentication behavior, you copy this file to your WARs /WEB-INF directory and modify it to fit your requirements. This is the file that defines all the authentication handlers for things like LDAP (BindLdapAuthenticationHandler), X.509 (X509CredentialsAuthenticationHandler), and my personal favorite the simple username equals password (SimpleTestUsernamePasswordAuthenticationHandler) authentication handler.

So the real problem became how can we enable the SimpleTestUsernamePassword authentication handler for local development and test servers, while disabling it for production systems? We wanted to avoid creating multiple CAS WARs containing configuration for their specific purpose: cas-ldap, cas-x509, cas-simple. Also, we wanted to avoid having a single CAS WAR that contained all methods of authentication. Ironically the solution was really simple and is actually used in other places in CAS.

Spring Import
The solution was spring imports (section 3.2.2.1). This was a feature I was not previously familiar with, but now think is one of the coolest features in spring because it lets you change the behavior of the system without having to rebuild. Spring lets you load in other bean xml files via the spring import tag. And since CAS uses spring, it was easy to modify CAS to be configured externally.

Configuring CAS Externally
First, I assume you have already setup your maven project that is performing the maven overlay. Once you have built your project do the following:

  • copy
    /cas/target/war/work/org.jasig.cas/cas-server-webapp/WEB-INF/deployerConfigContext.xml
    to
    /cas/src/main/webapp/WEB-INF
  • You'll also need to copy this same file to your application servers classpath for your application. For us this would be under the jboss conf directory: /jboss/server/default/conf. I also renamed this file to custom-deployerConfigContext.xml to avoid confusion.
  • Open the file /cas/src/main/webapp/WEB-INF/deployerConfigContext.xml
  • Remove all the bean tags in between the beans tag. This leaves you with nothing but the beans tag with nothing in between.
  • Paste in the following spring import tag:
    <import resource="classpath:custom-deployerConfigContext.xml"/>
  • Save your changes, rebuild and redeploy your CAS WAR
That's it. Now you can edit the custom-deployerConfigContext.xml and add/remove the SimpleTestUsernamePasswordAuthenticationHandler, or any other authentication handlers, without having to rebuild your CAS WAR.

Final Thoughts
Hopefully, you learned a little about how to configure CAS externally and a way to use springs import ability. For the most part this works great, but it isn't 100% fool proof. There are several CAS configuration files, so for the more complex authentication scenarios, you might have to modify the cas-servlet.xml or login-webflow.xml files. For example, if you want to do X.509 authentication, you have to include a bean in the cas-servlet.xml file and modify the login-webflow.xml file. But you could apply the same concepts to these files as well as cas-servlet.xml is just another spring file and login-webflow.xml is a spring webflow file that supports the bean-import tag.

Tuesday, April 5, 2011

What's missing from our REST Services?

While reading the excellent book RESTful Web Services I discovered something. A technique used on the web since its beginning. Twitter and Facebook seem to use it. Yet it was absent in the REST Services I had been developing and if I had to guess most REST developers aren't including it either. What is so valuable the Internet would be useless without it? The answer is hypermedia.

Hypermedia is the technical term used to describe how web pages (resources) are linked or connected. The author of RESTful Web Services calls it connectedness: how the server guides the client from one application state to another by providing links. A good example of a well-connected application is Wikipedia. It's very powerful when you could pick a random page and click through every entry on Wikipedia without ever editing your browsers URL. Performing a search on Google is another great example, as it wouldn't be very useful if the search result didn't include any links. Without these links, the client must know and create predefined rules to build every URL it wants to visit.

So what do links on a website have to do with REST Services? REST was built on the foundation of the web and just because your not returning HTML doesn't mean you shouldn't create relationships between your resources. In fact, it's amazing how powerful embedding links in your responses can be. For instance, it prevents tight coupling between your clients and services as the clients don't have to construct or even know the exact URLs because the services are providing them.

Let's use Twitter as an example. Assume the following is the URL to get the 20 most recent tweets for my account:

http://api.twitter.com/1/statuses/user_timeline.json?screen_name=jlorenzen

And here is a shortened fictitious response:

[
    {
        "text": "Finished watching Battlestar Galatica"
        "id": "55947977415667712"
        "url": "http://api.twitter.com/1/statuses/show/55947977415667712.json"
    },
    {
        "text": "Started watching Battlestar Galatica"
        "id": "45947977415667712"
        "url": "http://api.twitter.com/1/statuses/show/45947977415667712.json"
    }
]

Notice each tweet includes a direct URL. Now clients can use that URL value verses constructing it, and if it changes in the future, clients don't have to make any changes.

Example using Jersey
So what is the best way to include links in your REST Services? Since I use Jersey on a daily basis, I'll go ahead and show an example of how to embed links in your responses using Jersey. Most likely, any REST framework is going to provide the same kind of features.

Using the twitter example above, the User Status Service with links may look something like this (using groovy):

import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.Produces
import javax.ws.rs.QueryParam
import javax.ws.rs.core.Context
import javax.ws.rs.core.UriInfo

import com.test.UserStatuses

@Path("/statuses")
class StatusesResource {
 
    @Context 
    UriInfo uriInfo

    @GET
    @Path("/user_timeline")
    @Produces(["application/json", "application/xml"])
    def UserStatuses getUserTimeline(@QueryParam("screen_name")String screen_name) {
        def statuses = getStatuses(screen_name)
        
        statuses.each {
            it.url = "${uriInfo.baseUri}statuses/show/$it.id"
        }
        
        return new UserStatuses(statuses: statuses)
    }
}

In this simple example, Jersey injects the UriInfo object which we use to get the baseUri of the request. It's really that simple.


Potential Issues
Well for some it may not be that simple. For example, we discovered an issue when using a reverse proxy (apache httpd). In our production environments, we typically setup apache on port 80 to proxy a localhost JBoss on port 8080. Unfortunately, in this setup the UriInfo.getBaseUri() returns localhost:8080 and not the actual original URL the client used; which is obviously not good. Now if you don't use a reverse proxy then no worries. However, if you do or might potentially in the future, a easy solution seems to be to set the Apache Proxy module option ProxyPreserveHost to On. Setting this to On and restarting Apache seems to fix the issue.

JSONView Firefox Plugin
Once you've started embedding URLs in your REST responses, you might find it useful to install the JSONView firefox plugin. It's got some really slick features like formatting the JSON and creating clickable links for URLs.

Friday, October 1, 2010

How to Change Extjs PieChart Colors

When using Sencha's, or extjs, charting capability, most likely your going to want to change the default color scheme. I was faced with this issue today and it's not documented as well as you'd expect. I had to piece together a few articles and I'm still not 100% it's the right way as it's using an undocumented config option. But I wanted to document how I did get it to work to same others some time.

Sencha is very well documented and their examples are great. Here is the Pie Chart example we are going to be updating (using version 3.2.1). For those that don't know, extjs charts are based off of Yahoo's YUI charts which is also requires Flash. Not only is it pretty simple to create charts with extjs, but we already are using extjs so we decided to prototype some charts using it.

Here is the code that produces a simple basic PieChart:

new Ext.Panel({
    width: 400,
    height: 400,
    title: 'Pie Chart with Legend - Favorite Season',
    renderTo: 'container',
    items: {
        store: store,
        xtype: 'piechart',
        dataField: 'total',
        categoryField: 'season',
        extraStyle:
        {
            legend:
            {
                display: 'bottom',
                padding: 5,
                font:
                {
                    family: 'Tahoma',
                    size: 13
                }
            }
        }
    }
}); 

This produces the following PieChart.

To change the default colors provide a series config option:
new Ext.Panel({
    width: 400,
    height: 400,
    title: 'Pie Chart with Legend - Favorite Season',
    renderTo: 'container',
    items: {
        store: store,
        xtype: 'piechart',
        dataField: 'total',
        categoryField: 'season',
        series: [{
            style: {
                colors: ["#ff2400", "#94660e", "#00b8bf", "#edff9f"]
            }
        }],
        extraStyle:
        {
            legend:
            {
                display: 'bottom',
                padding: 5,
                font:
                {
                    family: 'Tahoma',
                    size: 13
                }
            }
        }
    }
});

The chart it produces may not look the most appealing but at least we figured out how to change the colors.
Pie Chart with New Colors


Again, I'm not certain this is the best way to accomplish this as the series config option isn't documented in 3.2.1. But by piecing together this PieChart question and this article, I was able to figure it out.