You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@cocoon.apache.org by Paul Joseph <pj...@gmail.com> on 2011/06/14 13:35:26 UTC

How to implement Single Sign On in a Cocoon application (Note: Cocoon 2.1.x)

Hi there,

I post this to save others needless hours of gazing into the "void."

The customer requested that Single Sign On (SSO) be implemented.

(Note: for newbies like me, I had to do some research and found that 
what this means.  Say like me, you have a cocoon application that is 
password protected and is on some server on the network.  With SSO, once 
a user is logged into their machine, they do not need to retype their 
username/password when they access this application.)

There are many implementations and a large number of acronyms and it was 
all very confusing.  But I found in the end, it was quite simple.

Let us say my Cocoon app is on a server called Cocoon_Server.  Now if 
the enterprise has SSO, then they have a "Domain Server" that we query 
to find out if the user has the proper credentials.  Lets say that the 
user is on a third machine called User_Computer.

There is a extremely useful resource http://spnego.sourceforge.net/ that 
provides everything you need with all the gobbledegook completely 
invisible to us.

Ideally, you would first implement the simple stand alone example that 
they provide to make sure that your SSO infrastructure and your 
development machine are setup correctly.
(http://spnego.sourceforge.net/pre_flight.html).  The one "GOTCHA" was 
that the is case sensitive and the documents at this link don't say 
that.  You must follow the exact case that they show in their example.

Once you have your SSO development infrastructure setup correctly, its 
time to integrate it into Cocoon.  I use flow script and Java, but there 
is a gotcha as at least in 2.1, cocoon.request returns a strange object 
that isn't quite compatible with HttpServletRequest and which is what 
you need for the spnego.jar file. To be able to do this, you need the 
ObjectModel passed to you and the easiest way I found was to do that 
using an "Action".

The first step is to put the spnego jar file from the site above into 
your lib directory.  I put it in cocoon/lib/local though probably 
putting it in the lib directory for my app would have been the more 
correct choice.

Then write a Cocoon "Action" as follows:

1) Put an entry for your action in your "root" sitemap:
<map:actions>
<map:action name="ssologin" 
src="org.apache.cocoon.myapplication.bean.SpnegoCocoonAuthenticator"/>
</map:actions>


2) in your Login page's sitemap, add the following:

<map:match pattern="ssologin">
<map:act type ="ssologin">
<map:call function="{ssologin}"/>
</map:act>
</map:match>

3) Write your action as follows, modifying  it if you need anything 
different:

package org.apache.cocoon.myapplication.bean;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.Principal;
import java.security.PrivilegedActionException;
import java.util.HashMap;
import java.util.Map;

import javax.security.auth.login.LoginException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import net.sourceforge.spnego.SpnegoAuthenticator;
import net.sourceforge.spnego.SpnegoHttpFilter.Constants;
import net.sourceforge.spnego.SpnegoHttpServletResponse;
import net.sourceforge.spnego.SpnegoPrincipal;

import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.ietf.jgss.GSSException;

import org.apache.cocoon.environment.http.HttpEnvironment;

import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.avalon.framework.parameters.Parameters;

import org.apache.cocoon.acting.Action;
import org.apache.cocoon.environment.Redirector;
import org.apache.cocoon.environment.SourceResolver;

public class SpnegoCocoonAuthenticator implements Action, Initializable, 
ThreadSafe {
     private HttpSession session = null;
     protected SpnegoAuthenticator authenticator = null;

     public final void initialize() throws LifecycleException {

         final Map<String, String> map = new HashMap<String, String>();
         map.put(Constants.ALLOW_BASIC, "true");
         map.put("spnego.allow.localhost", "true");
         map.put("spnego.allow.unsecure.basic", "true");
         map.put("spnego.login.client.module", "spnego-client");
         map.put("spnego.krb5.conf", "krb5.conf");
         map.put("spnego.login.conf", "login.conf");
         map.put("spnego.preauth.username", 
"Charlie_a_user_who_has_permissions_on_DOMAIN_SERVER");
         map.put("spnego.preauth.password", "StrongPassword");
         map.put("spnego.login.server.module", "spnego-server");
         map.put("spnego.prompt.ntlm", "true");
         map.put("spnego.allow.delegation", "true");
         map.put("spnego.logger.level", "1");

         try {
             authenticator = new SpnegoAuthenticator(map);
         } catch (LoginException e) {
             throw new LifecycleException(e);
         } catch (FileNotFoundException e) {
             throw new LifecycleException(e);
         } catch (GSSException e) {
             throw new LifecycleException(e);
         } catch (PrivilegedActionException e) {
             throw new LifecycleException(e);
         } catch (URISyntaxException e) {
             throw new LifecycleException(e);
         }
     }

     public Map act(Redirector redirector, SourceResolver resolver, Map 
objectModel, String src, Parameters parameters) throws Exception {

         final HttpServletRequest httpRequest = (HttpServletRequest) 
objectModel.get(HttpEnvironment.HTTP_REQUEST_OBJECT);

         final SpnegoHttpServletResponse spnegoResponse = new 
SpnegoHttpServletResponse(
                 (HttpServletResponse) (HttpServletResponse) 
objectModel.get(HttpEnvironment.HTTP_RESPONSE_OBJECT));

         final SpnegoPrincipal principal;

         try {
             principal = this.authenticator.authenticate(httpRequest, 
spnegoResponse);

         } catch (GSSException e) {
             throw new IOException(e);
         }


         //you now have the basic info.--if there is a valid name, the 
user is legit.
         //you can do more stuff if you need to like get her roles etc. 
but I didn't need any more.
         String userName = principal.getName();

         Map map = new HashMap(1);

         if(userName != null || !userName.equals(""))
             map.put("ssologin", "LoggedInScriptName");
         else
             map.put("ssologin", "InvalidUserScriptName");

         return map;
     }
}

4) Write a flowscript to handle "LoggedInScriptName" that puts the user 
right into the app as though she had typed the username/password.  Also, 
to handle an invalid user, write the InvalidUserScriptName to handle the 
case of an invalid user.

That's it.  Seems to work!  I need to remove the username/pwd and other 
hardcoding and streamline it a bit, but the above is more or less all 
one needs.

Best.
Paul





---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@cocoon.apache.org
For additional commands, e-mail: users-help@cocoon.apache.org