Showing posts with label CAS. Show all posts
Showing posts with label CAS. Show all posts

2010-06-29

Grails + Acegi + CAS + Proxy Ticket

Should you have Acegi + CAS setup in Grails and you want to proxy a URL secured behind the same CAS instance you should do something like this:

URL url = new URL(urlString)
SecurityContext ctx = SecurityContextHolder.getContext();
CasAuthenticationToken auth = ctx?.getAuthentication();
Assertion assertion = auth?.getAssertion();
Principal principal = assertion?.getPrincipal();
assert principal.proxyRetriever != null
// the above line asserts that proxy system is properly setup and working
String proxyTicket = principal?.getProxyTicketFor(url?.toString());
assert proxyTicket != null
URL readUrl = new URL(url.toString() + "?ticket=${proxyTicket}")


This only works if your CAS supports proxying and you have a working proxy call back. For details on troubleshooting this with Grails-Acegi 0.5.3 see GRAILSPLUGINS-2231 which includes a working proxy callback controller. Hopefully this will be fixed in an upcoming version so you won't need this reference for long.

If your setup is not working properly the proxyRetriever embedded in the Principal object will be null. If it is null then getProxyTicketFor returns null no matter what. Once you get your CAS and SecurityConfig.groovy working properly the proxyRetriever starts getting magically injected into your application. This doesn't mean you actually have proxying working yet but you can almost rule out client-side configuration errors.

Your Grails application should also have something at /secure/receptor that can catch responses issued by the CAS server. You should see this URI respond to something just after you successfully log in. That URI also needs to be open so the CAS server can post there without needing to login to itself.

NOTE: your CAS server must be able to resolve the URL of the proxy call back. That means if you are running your proxy callback on localhost the responding CAS server must be able to post to that URL. Why? When you request a proxy granting ticket CAS will send the ticket back to your Grails application at the URI /secure/receptor by default. That means your application's URL must be resolvable by the CAS server for proxy tickets to function.

In practice, I run a small CAS server on localhost at an obscure port with the URL to proxy also on localhost when testing proxy ticket granting and other CAS secured proxy services. I work around the SSL issues by using a custom implementation of javax.net.ssl.HostnameVerifier that is installed during bootstrap in development and test-app modes only. The custom verifier (written in Java) looks like:


import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import javax.net.ssl.HttpsURLConnection;

public class CustomHostnameVerifier implements HostnameVerifier {
public String[] exemptNames = {"localhost","testing.com","example.com"};
// whatever hosts need exempting from having a "real" SSL certificate
public boolean verify(String hostname, SSLSession session) {
boolean exempt = false;
if(exemptNames != null) {
for(String name:exemptNames) {
if(hostname.toLowerCase().endsWith(name)) {
exempt = true;
}
}
}
return exempt;
}

// call from BootStrap.groovy or other opportune initializer
public static void init() {
HttpsURLConnection.setDefaultHostnameVerifier(new CustomHostnameVerifier());
}

}


Other work arounds would involve a staging CAS server that was able to resolve the development or testing host during work on proxying. These are up to you.

2008-04-24

Audit Logging Plugin version 0.3: who did what when

When I posted my Audit Logging plug-in I had no idea how to get at the user name of the person using the session. I posted a question on the Grails Dev forum. And the answer supplied by pftravis lead me this...

static String actorKey = 'userPrincipal.name'
static String sessionAttribute = null

def getActor() {
def attr = org.springframework.web.context.request.RequestContextHolder.getRequestAttributes()
def actor = null
if(sessionAttribute) {
actor = attr?.session?.getAttribute(sessionAttribute)
}
else {
actor = resolve(attr,actorKey)
}
return actor
}

... if there is no configuration the default will read the 'userPrincipal.name' from the context. In my set up of the listener I read in the Config.groovy from the grails project and if there is a configuration like this...

auditLog {
actorKey = "session.user.name"
}

...then I'll use a clever little groovy trick to resolve the session, user, and name out of the attributes. If instead the configuration looks like...

auditLog {
actorKey = "userPrincipal.id"
}

... the same trick will read the id of the authenticated user. But, if you are using CAS then you can do this...

auditLog {
username = "${edu.yale.its.tp.cas.client.filter.CASFilter.CAS_FILTER_USER}"
}

... and you get the CAS authenticated user name.

So long story short all you have to know is if your security system uses userPrincipal and if it does whether "userPrincipal.name" or "userPrincipal.id" is right. If you have a home brew security system all you have to know is what part of the authenticated user you want to identify the user by... and specify "session.user.name" or "session.login" depending on how your security system works. And for systems like CAS you can use an attribute key of any kind.

Nice and generic. Check out the latest Grails Audit Logging Plugin. As always any feedback or criticism will be welcome... and my goal in all of this is to contribute and learn.

2007-12-18

Single Sign On with JBoss Portal and Active Directory

No one has asked for it but I've begun work on a SSO project when I'm between official projects. I discovered the power of Acegi and CAS. I have an install of the JBoss portal as well and that requires its own set up too... however it is looking like we are going to use Joomla instead.

I began with understanding how to talk to an ldap server (in my case Active Directory). Once I understood how to query LDAP I began searching around using my groovy scripts to find the object classes, and specific names I would need inside my CAS configuration.

I then followed this tutorial for CAS to authenticate the users:

Next I built in the CAS client into JBoss portal using this wiki page's instructions:

Finding the specific settings were hard. In particular I had to muck about with the ldap_identity-config.xml deep inside our jboss-portal.sar file... it turns out our AD setup is somewhat "special" and needed some extra care beyond what is in this page:

I have no idea how I would have figured out how to do that kind of configuration without that wiki page.

Once I managed to stitch together both sets of instructions I could have my users authenticate via CAS and then the portal would query roles on its own against Active Directory.

Next I'll investigate how to bring Groovy and Grails into this mix. I'll have a project for delivery in March that will need to hook into this SSO system... and likely subsequent projects to link into the CAS system using PHP and Perl.