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.

2010-06-24

When NOT to use a Domain Specific Language

There's really one rule I can use to sum up why you would not use a domain specific language.

Do not use a Domain Specific Language when implementation specifics out weigh domain specifics.


This one rule covers numerous cases:


  1. when intimate knowledge of API are as important as function

  2. when performance is critical and requires expert knowledge

  3. when implementation details are nearly as important as semantic details



All these cases and numerous variations on this theme are summed up by acknowledging that the choice of using a Domain Specific Language (DSL) is a design trade off. You are deliberately adding a layer to your application stack. This layer intrinsically incurs a maintenance and/or performance cost. (Not all DSL cost additional clock cycles at run time it depends on your implementation.)

With these costs and benefits in mind you can make an intelligent choice about when and where to use this tool. It is far from a panacea but Domain Specific Languages do have the allure of potentially netting you accidental programmers and preserving folk knowledge about your system and its real world uses that few other techniques offer.