CASifying Sun Access Manager

Introduction

The following guide explains how we modified Sun Access Manager (AM) to implement CAS single sign on for the AM 7.1 proptected applications. 

CAS Login Module

Create a module properties file named CASAMLoginModule.xml.

Compile and packaging cas.am.CASAMLoginModule and cas.am.CASAMPrincipal into CASAMLoginModule.jar. The codes are attached at the bottom of this page.

Also download the CAS client casclient.jar (we're using CAS Client 2.0.11).

Copy CASAMLoginModule.jar to <AM_BASE_DIR>/web-src/services/WEB-INF/lib.

Copy casclient.jar to <AM_BASE_DIR>/web-src/services/WEB-INF/lib.

Copy CASAMLoginModule.xml to <AM_BASE_DIR>/web-src/services/config/auth/default.

Redeploy (Please refer to <AM_BASE_DIR>/samples/authentication/spi/Readme_Redeploy.html) amserver.war file.

*Tips: for development purpose, you can put the jars and xml into amserver.war directly and redeploy it as a normal web application on your web container. The directory structure is as follows:
      amserver.war\WEB-INF\lib\CASAMLoginModule.jar
      amserver.war\WEB-INF\lib\casclient.jar
      amserver.war\config\auth\default\CASAMLoginModule.xml

Configuring Access Manager

Login to Access Manager Console as amadmin, using the URL: http://<host>.<domain>:<port>/amserver/UI/Login.

Click on "Configuration" tab.

Select "Core" within "Authentication"

Add class file name cas.am.CASAMLoginModule to "Pluggable Auth Modules Classes"

Click on Save button to save the changes in console.

*Alternate way:
Login to Access Manager Console as amadmin, using the URL: http://<host>.<domain>:<port>/amconsole.
Click "Service Configuration" tab.
Select "Core" under "Authentication Modules".
Add class file name cas.am.CASAMLoginModule to "Pluggable Authentication Modules Classes"
Click on Save button to save the changes in console.

Running the Login Module

Enter URL http://<host>.<domain>:<port>/amserver/UI/Login?module=CASAMLoginModule&goto=http://<host>.<domain>:<port>/portal/dt. (If you choose to use an organization other than the default, please specify that in the URL using the 'org' parameter.)

CASAMLoginModule.java

package cas.am;

import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.login.LoginException;
import com.sun.identity.authentication.spi.AMLoginModule;
import com.sun.identity.authentication.spi.AuthLoginException;

import javax.servlet.http.HttpServletRequest;

import edu.yale.its.tp.cas.client.*;

public class CASAMLoginModule extends AMLoginModule {

    private String userTokenId;
    private java.security.Principal userPrincipal = null;

    public CASAMLoginModule() throws LoginException{
	System.out.println("CASAMLoginModule()");
    }

    public void init(Subject subject, Map sharedState, Map options) {
	System.out.println("CASAMLoginModule initialization");
    }

  /**
   * Returns a service ID (URL) as a composite of the preconfigured server
   * name and the runtime request.
   */
  private String getService(HttpServletRequest request) throws AuthLoginException{
  	String casServerName = request.getServerName()+":"+request.getServerPort();
  	try {
           String service =  Util.getService(request, casServerName);
           return service;
        } catch (Exception e){
           throw new AuthLoginException("");
        }
  }

    public int process(Callback[] callbacks, int state) throws AuthLoginException {
	int currentState = state;

        HttpServletRequest request = getHttpServletRequest();

        String urlOfThisService = getService(request);

        //TODO remove hardcode of cas urls.
        String casLoginURL = "https://localhost:8443/cas/login";
        String casValidateURL = "https://localhost:8443/cas/serviceValidate";

        String loginFailureURL = casLoginURL + "?service=" + urlOfThisService;
	setLoginFailureURL(loginFailureURL);

        String ticket = request.getParameter("ticket");

        if(ticket==null) {
        	System.out.println("CASAMLoginModule Authentication No Ticket!");
        	throw new AuthLoginException("no ticket found!");
        }

	  String user = null;
	  String errorCode = null;
	  String errorMessage = null;

	  /* instantiate a new ServiceTicketValidator */
	  ServiceTicketValidator sv = new ServiceTicketValidator();
	  /* set its parameters */
	  sv.setCasValidateUrl(casValidateURL);
	  sv.setService(urlOfThisService);
	  sv.setServiceTicket(ticket);

	  /* contact CAS and validate */
	  try{
                sv.validate();
          } catch (Exception e) {
          	System.out.println("CASAMLoginModule Authentication Validate Fail!");
          	throw new AuthLoginException(e.getMessage());
          }

	  if(sv.isAuthenticationSuccesful()) {
	  	user = sv.getUser();
	  } else {
	  	errorCode = sv.getErrorCode();
	  	errorMessage = sv.getErrorMessage();
	  	/* handle the error */
	  	System.out.println("CASAMLoginModule Authentication Fail!" + errorMessage);
	  	throw new AuthLoginException(errorMessage);
	  }

	  /* The user is now authenticated. */
	  userTokenId = user;
	  System.out.println("CASAMLoginModule Authentication Pass!");

	return -1;
    }

    public java.security.Principal getPrincipal() {
        if (userPrincipal != null) {
            return userPrincipal;
        } else if (userTokenId != null) {
            userPrincipal = new CASAMPrincipal(userTokenId);
            return userPrincipal;
        } else {
            return null;
        }
    }
}

CASAMPrincipal.java

package cas.am;

import javax.security.auth.*;
import javax.security.auth.login.*;
import javax.security.auth.callback.*;

import java.security.Principal;


public class CASAMPrincipal implements Principal, java.io.Serializable {

    /**
     * @serial
     */
    private String name;


    public CASAMPrincipal(String name) {
	if (name == null)
	    throw new NullPointerException("illegal null input");

	this.name = name;
    }

    /**
     * Return the username for this <code>CASAMPrincipal</code>.
     *
     * <p>
     *
     * @return the username for this <code>CASAMPrincipal</code>
     */
    public String getName() {
	return name;
    }

    /**
     * Return a string representation of this <code>CASAMPrincipal</code>.
     *
     * <p>
     *
     * @return a string representation of this <code>CASAMPrincipal</code>.
     */
    public String toString() {
	return("CASAMPrincipal:  " + name);
    }

    /**
     * Compares the specified Object with this <code>CASAMPrincipal</code>
     * for equality.  Returns true if the given object is also a
     * <code>CASAMPrincipal</code> and the two CASAMPrincipals
     * have the same username.
     *
     * <p>
     *
     * @param o Object to be compared for equality with this
     *		<code>CASAMPrincipal</code>.
     *
     * @return true if the specified Object is equal equal to this
     *		<code>CASAMPrincipal</code>.
     */
    public boolean equals(Object o) {
	if (o == null)
	    return false;

        if (this == o)
            return true;

        if (!(o instanceof CASAMPrincipal))
            return false;
        CASAMPrincipal that = (CASAMPrincipal)o;

	if (this.getName().equals(that.getName()))
	    return true;
	return false;
    }

    /**
     * Return a hash code for this <code>CASAMPrincipal</code>.
     *
     * <p>
     *
     * @return a hash code for this <code>CASAMPrincipal</code>.
     */
    public int hashCode() {
	return name.hashCode();
    }
}

CASAMLoginModule.xml

<?xml version='1.0' encoding="UTF-8"?>

<!DOCTYPE ModuleProperties PUBLIC "=//iPlanet//Authentication Module Properties XML Interface 1.0 DTD//EN"
          "jar://com/sun/identity/authentication/Auth_Module_Properties.dtd">

<ModuleProperties moduleName="CASAMLoginModule" version="1.0" >
</ModuleProperties>