Using the REMOTE_USER

This page describe how to use credentials other than UsernamePasswordCredentials with CAS 3.

Use case

Suppose that your Apache environment or a JAAS realm or a Java Servlet Filter set the REMOTE_USER with the authenticated user before the request even gets to CAS.

How would we configure CAS 3 to consume that remote user field?

Implementation

Java code

Credentials

We need some Credentials that bear an already resolved Principal:

PrincipalBearingCredentials
package org.jasig.cas.adaptors.generic;

import org.jasig.cas.authentication.principal.Credentials;
import org.jasig.cas.authentication.principal.Principal;

/**
 * Credentials that bear the fully resolved and authenticated Principal.
 * 
 * These Credentials are a mechanism to pass into CAS information about an
 * authentication and Principal resolution that has already happened in layers
 * in front of CAS, e.g. by means of a Java Servlet Filter.
 * 
 * DO NOT accept these Credentials from arbitrary web-servicy calls to CAS.
 * Rather, the code constructing these Credentials must be trusted.
 */
public class PrincipalBearingCredentials 
    implements Credentials {

    private Principal principal;
    
    public Principal getPrincipal(){
        return this.principal;
    }
    
    public void setPrincipal(Principal principal) {
        this.principal = principal;
    }
    
}

Extracting the REMOTE_USER into the Credentials

We need a CredentialsBinder to extract the remote user into a Principal that we can store into our Credentials.

RemoteUserCredentialsBinder
/**
 * Extracts the remote user from the request into the PrincipalBearingCredentials.
 * @version $Revision:$ $Date:$
 */
public class RemoteUserCredentialsBinder 
    implements CredentialsBinder {

    public void bind(HttpServletRequest request, Credentials credentials) {
        String remoteUser = request.getRemoteUser();
        
        SimplePrincipal principal = new SimplePrincipal(remoteUser);
        ((PrincipalBearingCredentials) credentials).setPrincipal(principal);
        
    }

    public boolean supports(Class clazz) {
        return PrincipalBearingCredentials.class.equals(clazz);
    }

}

AuthenticationHandler

We need an AuthenticationHandler that will approve authentication of our dummy credentials

PrincipalBearingCredentialsAuthnHandler

/**
 * AuthenticationHandler which authenticates Principal-bearing credentials.
 * Authentication must have occured at the time the Principal-bearing credentials
 * were created, so we perform no further authentication.
 * 
 */
public class PrincipalBearingCredentialsAuthnHandler 
    implements AuthenticationHandler {

    public boolean authenticate(Credentials credentials) 
        throws AuthenticationException {
        
        // we're assuming supports() was called and we supported the
        // credentials, so they must be PrincipalBearingCredentials.
        return true;
    }

    public boolean supports(Credentials credentials) {
        return credentials instanceof PrincipalBearingCredentials;
    }

}

CredentialsToPrincipalResolver

We need a CredentialsToPrincipalResolver to extract the Principal out of the Credentials.

PrincipalBearingCredentialsToPrincipalResolver
package org.jasig.cas.adaptors.generic;

import org.jasig.cas.authentication.principal.Credentials;
import org.jasig.cas.authentication.principal.CredentialsToPrincipalResolver;
import org.jasig.cas.authentication.principal.Principal;

/**
 * Extracts the Principal out of PrincipalBearingCredentials.
 */
public class PrincipalBearingCredentialsToPrincipalResolver 
    implements CredentialsToPrincipalResolver {

    public Principal resolvePrincipal(Credentials credentials) {
        return ((PrincipalBearingCredentials) credentials).getPrincipal();
    }

    public boolean supports(Credentials credentials) {
        return credentials instanceof PrincipalBearingCredentials;
    }

}

Wiring

Now that we have the necessary Java class implementations, we need to wire them in.

deployerConfigContext.xml

Declare the authentication handler:

Injecting our authentication handler into the authentication manager
<property name="authenticationHandlers">
    <list>
        <bean 
         class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler" />
	<bean
	class="org.jasig.cas.adaptors.generic.PrincipalBearingCredentialsAuthnHandler" />
    </list>
</property>

And declare our CredentialsToPrincipalResolver:

Injecting our credentials to principal resolver into the authentication manager
<property name="credentialsToPrincipalResolvers">
    <list>
        <bean
            class="org.jasig.cas.authentication.principal.PrincipalBearingCredentialsToPrincipalResolver" />
	<bean
            class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver" />
    </list>
</property>

cas-servlet.xml

Declare our Credentials class and our CredentialsBinder in configuring the Login Form Action in cas-servlet.xml

declaring our Credentials class and CredentialsToPrincipalResolver in cas-servlet.xml
<bean
    id="loginFormAction"
    class="org.jasig.cas.web.flow.LoginFormAction">
    <property
        name="centralAuthenticationService"
        ref="centralAuthenticationService" />
    <property
        name="credentialsBinder">
        <bean 
            class="org.jasig.cas.adaptors.generic.RemoteUserCredentialsBinder"/>
    </property>
    <property
        name="formObjectClass"
        value="org.jasig.cas.adaptors.generic.PrincipalBearingCredentials" />
</bean>

Demonstrating

One way to set the REMOTE_USER is to use the CASFilter. We can front our CAS 3 instance with our existing CAS 2 instance.

Declaring the CASFilter

<filter>
  <filter-name>CAS Filter</filter-name>
  <filter-class>edu.yale.its.tp.cas.client.filter.CASFilter</filter-class>
  <init-param>
    <param-name>edu.yale.its.tp.cas.client.filter.loginUrl</param-name>
    <param-value>https://secure.its.yale.edu/cas/login</param-value>
  </init-param>
  <init-param>
    <param-name>edu.yale.its.tp.cas.client.filter.validateUrl</param-name>
    <param-value>https://secure.its.yale.edu/cas/serviceValidate</param-value>
  </init-param>
  <init-param>
    <param-name>edu.yale.its.tp.cas.client.filter.serverName</param-name>
    <param-value>hkg2.cis.yale.edu:8080</param-value>
  </init-param>
  <init-param>
	<param-name>edu.yale.its.tp.cas.client.filter.wrapRequest</param-name>
	<param-value>true</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>CAS Validate Filter</filter-name>
  <url-pattern>/login</url-pattern>
</filter-mapping>

Removing the username and password fields from the UI

And we need an alternative login.jsp without the username and password fields:

casLoginView.jsp
<%@ page session="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
	<head>
		<!-- $Id: casLoginView.jsp,v 1.10 2005/05/24 18:57:18 sbattaglia Exp $ -->
	
		<!-- DOCUMENT TITLE: CHANGE TO NEW TITLE -->
		<title>JA-SIG  Central  Authentication  Service (CAS)</title>
		<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
	
		<!-- KEYWORDS AND DESCRIPTIONS GO INTO THIS SECTION -->
	
		<meta name="keywords" content="Central Authentication Service,JA-SIG,CAS" />
		<meta name="description" content="The login page for the JA-SIG Central Authentication Service" />
		<meta name="author" content="Bart Grebowiec, Scott Battaglia" />
		
	
		<!-- THIS CODE PROVIDES THE FORMATTING FOR THE TEXT - PLEASE LEAVE INTACT -->
		<link rel="stylesheet" href="<spring:theme code="css" />" type="text/css" media="all" />
		<script src="js/common.js" type="text/javascript"></script>
	</head>


  <body onload="init();">
	<!-- HEADER -->
	<div id="header">
		<a id="top">Java Architecture Special Interest Group</a>
		<h1>JA-SIG Central  Authentication  Service</h1>
	</div>
	<!-- END HEADER -->

	<!-- CONTENT -->
	<div id="content">

		<div class="dataset clear" style="position: relative;">
			<h2 style="margin-bottom:0;">Please Log In</h2>

			<p style="margin-top:-.5em;border:1px solid #ccc;background-color:#ffc;color:#000;padding:5px;">
			  Congratulations on bringing CAS online!  
We've fronted this CAS 3 instance with authentication to CAS 2. <br />
			</p>

			<form method="post" action="">
							<p><input style="width:1.5em;border:0;padding:0;margin:0;" 
type="checkbox" id="warn" name="warn" value="true" tabindex="3" /> 
							   <label for="warn"  accesskey="w">
<span class="accesskey">W</span>arn me before logging me into other sites.</label></p>

							<input type="hidden" name="lt" value="${flowExecutionId}" />
  							<input type="hidden" name="_currentStateId" value="${currentStateId}" />
							<input type="hidden" name="_eventId" value="submit" />

							<p><input type="submit" 
class="button" accesskey="l" value="LOGIN" tabindex="4" /></p>
						</div>

					</div>
				</fieldset>
			</form>
		</div>
	</div><!-- END CONTENT -->

	<!-- FOOTER -->
   	<div id="footer">
		<hr />
		<p style="margin-top:1em;">
			Copyright &copy; 2005 JA-SIG.  All rights reserved.
		</p>
	</div>
	<!-- END FOOTER -->

</body>
</html>

Trying it out

Visit the CAS server explicitly specifying the /login on the path. You will CAS (2) authenticate and then be able to submit the CAS 3 login form. Specify a service and you'll get a ST which validates to authenticate your CAS 2 NetID.