Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

The CAS Server is a Spring Web Application, but it is designed according to standard layers or functional components that can be plugged into a standard J2EE diagram. There are three main components, each of which can be subdivided into subcomponents.

...

CAS 2 was written at Yale and was a Servlet. It was tiny compared to subsequent versions of CAS, but it just did the basic features and that was enough.

CAS 3 was a replacement that performed the same functions and used the same protocol, but provided a base to add additional optional features. The Spring Framework was selected as the base technology. Spring is good because it creates an application that can be reconfigured by changing some XML files without writing new code. If you are a Java programmer, then Spring provides its own richer API for doing lots of things that can be done with vanilla Java, but you have to read a new manual to understand them. This is not a place where I can reasonably introduce Spring, so either you know it or you can pick up a book.

CAS 5 has been released as a simpler configuration solution. The problem with CAS 3 is that every Bean of Java code was connected to every other Bean with an explicit XML configuration element. This is very powerful, but it produces massive configuration files that take time to learn and use. Subsequent generations of Spring have adopted an "autowire" approach, where default configuration is done with annotations in the Java source. CAS 5 can be configured with one very large properties file. You specify the properties of the options you select, and any option where you specify no properties simply doesn't get used.

Since we are currently using CAS 3, this document will tend to show XML configuration of Beans. The same Beans are still there in CAS 5, but some element will be configured differently.

CAS was designed to be flexible. In the original design, flexible meant that it would be easy to add new function by adding new Java code. Later releases were targeted to campuses with no Java development capability, so more recently flexibility is the opportunity to select from prepackaged options. Since Yale has had Java developers and has worked with CAS from the beginning, we have not been scared to write a little code and make CAS work exactly the way we want it to.

So to get along with CAS at Yale, you need to know a bit of the design that normally only developers care about.

Java creates objects with the "new" statement, and if the object requires parameters in order to fill in required fields, then they are passed to the "constructor". Spring creates Beans, which is an instance of a named class represented by an XML "Bean" element in some Spring configuration file. If the object requires parameters to fill in the values of required fields, then those parameters are in the XML as either Attributes of the Bean element of child elements of the Bean.

Java classes may require that a field be assigned a reference to another Bean. This is called "injection". Typically there will be an interface that defines the function that the Bean provides, and if in the entire application there is only one Bean that implements this interface then there is no problem figuring out what type of class has to be injected. However, there are several different ways that a password can be validated, and several different ways to store Ticket information for future use, and several ways to configure Services. In these cases you have to configure one specific choice from the many types of Beans that could provide the functions needed by the interface. In CAS 3 you would generate one XML element that names the class you have selected to perform the function. In CAS 5, you provide initialization parameters for the one class that could be used to satisfy the interface, and if you did not provide parameters that would allow any of the alternatives you might have selected to actually be loaded and run, then the only Bean that can be initialized must be the one that provides the required interface services.

There are a few objects that are created by the Tomcat container (a Request, a Response, a Session, and the things connected to them). There are also a few data objects that CAS creates during normal request processing: Credentials (a userid and password), Tickets (login or service tickets), Services. Everything else is a Spring Bean.

The good news is that CAS has a well defined set of layers represented by interfaces, and these layers can be described using the standard J2EE terminology. The next step is then to identify the key configuration components for each layer or interface and how they fit into the overall CAS process.

  • The front end is a "Presentation Layer" that handles arriving HTTP requests. It is divided into two parts:
    1. The end user login from the browser is handled by a Spring Webflow configured in XML. If the user is already logged in (the CAS Cookie is present) then the request may be handled automatically as a Single SignOn. If not, the flow can optionally look for non-interactive forms of authentication like a User X.509 certificate and private key installed in the Browser. Otherwise, the flow presents the CAS Login page and gets the userid and password. Finally it generates the Cookie and returns the Service Ticket ID string.
    2. However, CAS also gets administrative requests and Service Ticket validation requests (/serviceValidate). These are handled by the Spring MVC mechanism, which is basically the Spring version of a Servlet.
  • The Business Layer (where J2EE would have EJBs) validates the login (by verifying the userid/password against a backend system such as Kerberos or LDAP), and it creates Tickets including the Login TGT and Service Tickets.
  • Tickets are stored in a Ticket Cache. Normally this is just an in-memory collection, although it can be replicated (for clustered failover) or stored in a shared database. Since Tickets can be persisted (even though they frequently aren't) they become Entities in a logical Persistence Layer.

...

CAS 3 uses two different Spring APIs to configure and code the Presentation Layer.

Protocol (Validate) Requests

The calls to validate Service Tickets and issue Proxy Tickets are processed by Spring MVC configured in the WEB-INF/cas-servlet.xml file.Spring "Action" Beans are configured that expose a method that receives the Request and Response object of an incoming HTTP operation.

Code Block

    <bean id="serviceValidateController" class="org.jasig.cas.web.ServiceValidateController"
        p:validationSpecificationClass="org.jasig.cas.validation.Cas20WithoutProxyingValidationSpecification"
        p:centralAuthenticationService-ref="centralAuthenticationService"
        p:proxyHandler-ref="proxy20Handler"
        p:argumentExtractor-ref="casArgumentExtractor" />

...

Each Action Bean is called when the end of a request URL contains a string mapped to the Bean (again in cas-servlet.xml):

Code Block

   <prop
       key="/serviceValidate">
       serviceValidateController
   </prop>

Login Webflow

The "/login" function is mapped to an entirely separate API called Spring WebFlow. WebFlow is a common computer design pattern called a "State Machine" coded as a sequence of XML elements in the WEB-INF/login-webflow.xml file. In some sense, a WebFlow demonstrates how to replace a dozen lines of Java with a hundred lines of considerably more complex XML (but at least you are not coding).

Each state is represented by an XML element that either performs a simple EL test or else calls a Bean method. The result of the test or return value of the method is "success" or "error" and generates a transition (a GOTO) to another state.

Code Block

    <action-state id="startAuthenticate">
        <action bean="x509Check" />
        <transition on="success" to="sendTicketGrantingTicket" />
        <transition on="error" to="viewLoginForm" />
    </action-state>

...

The most important secondary internal service, and the most important Spring configuration element, is the AuthenticationManager. An AuthenticationManager handles supports login requests, validating the user Credentials and generating (after a sucessful login) the TGT.

...

Credentials are created by the Presentation Layer from something in the HTTP request. In the simple case, the Userid and Password are extracted from the form and combined to create a Credential object. If the container is configured to accept User Certificates, an X.509 Certificate can be extracted from the Request object and presented as a Credential. If SPNEGO is enabled, then the Browser of a client logged into a Kerberos 5 realm (typically an Active Directory Domain) can present a Kerberos 5 Service Ticket as proof of identity.

Each AuthenticationHandler configured to the AuthenticationManager is called in turn. If it supports the type of Credential object, it can claim and validate the Credential. In the case of a Userid/Password from a Form, this means sending these field on to a backend system (a Kerberos 5 KDC, an LDAP directory, a JDBC database) that maintains a list of userids and passwords and can verify that the pair of fields are valid.

However, the container has often already done a lot of the validation. can present a Kerberos 5 Service Ticket as proof of identity. Of course in the real world, most credentials consist of a userid and password string.

In the Spring configuration file, the AuthenticationManager has a property that gets a List of AuthenticationHandlers. Each Handler is called in turn and is given a chance to accept or pass on a particular type of credential. An X.509 Handler will only look at Certificate credentials and will pass on userid/password credentials. There can be more than one userid/password Handler if you want to authenticate users to multiple backend systems. Spring configuration allows you to plug in a Handler that does LDAP if you want to authenticate passwords using LDAP, or JAAS if you want to authenticate passwords using Kerberos.

When X.509 User Certificates are enabled to Tomcat or JBoss, then the container has already verified that the Certificate was digitally signed by a Certificate Authority in its list of configured "trusted" CAs. With SPNEGO, all the Kerberos 5 keys and tickets protocol has already been done down in the presentation layer using the Java interface to the system GSSAPI service. So some AuthenticationHandlers really validate the Credentials, while others simply verify that the type of object corresponds to something the container or Presentation Layer has already validated and then returns "true" because such Credentials are implicitly trusted.

...

  • The user types something into a form. Since he is an end user, it should be as simple as possible. Suppose he types "gilbert".
  • The AuthenticationHandler validates the Credential object containing "gilbert". However, in order to authenticate to a particular back end service, it may be necessary to transform what was typed in to something specific to that particular back end service. At Yale, if you want to use Kerberos 5 protocol to the MIT Kerberos servers you would use Principal name "gilbert@NET.YALE.EDU", while using the same protocol against Active Directory would use the same JaasAuthenticationHandler configuration and simply transfom transform the typed in userid to "gilbert@YU.YALE.EDU".
  • However, at Yale "gilbert" is a Netid that is maintained across a number of back end system.  Any system protected against unauthorized modification might be used to authenticate this person, and while it may be of administrative interest how the authentication was done, when some CAS Service validates a Service Ticket it expects to get the naked Netid "gilbert". So even though a fully qualified Principal name may be used with the back end service, the unqualified name would be stored by CAS in the Ticket space.
  • That works fine until Yale joins one or more federations of universities and allows cross-institutional login. Now to support legacy systems, the unqualified "gilbert" would be returned to all Yale services that expect that CAS only authenticates local Yale users. New services that are ready to accept fully qualified cross-institutional Principal names might get back "gilbert@harvard.edu" and would realize that this is almost certainly some other person named "gilbert" at a different institution and, therefore, someone completely different from the local Netid "gilbert" at Yale.

...

JAAS (Kerberos) Password Handler

Code Block

       <bean  class="org.jasig.cas.authentication.handler.support.JaasAuthenticationHandler"
             p:realm="CASJAASconfig" />

JAAS is a rather complicated standard feature of Java that has its own more comprehensive description page.

The configuration example given above tells CAS to authenticate the userid and password through JAAS using the bundle of parameters in the JAAS "login configuration" file named "CASJAASconfig". To determine exactly what JAAS is going to do, you have to search the environment, find the login configuration file, and lookup the name.

...

LDAP (AD) Password Handler

Code Block

                <bean class="org.jasig.cas.adaptors.ldap.BindLdapAuthenticationHandler">
                       <property name="filter" value="sAMAccountName=%u" />
                       <property name="searchBase" value="dc=yu,dc=yale,dc=edu" />
                       <property name="contextSource" ref="ldapContextSource" />
                       <property name="ignorePartialResultException" value="yes" />
                </bean>

...

 X.509 Certificate Handler

Code Block

                <bean class="org.jasig.cas.adaptors.x509.authentication.handler.support.X509CredentialsAuthenticationHandler"
                    p:trustedIssuerDnPattern="CN=Yale University ITS Issuing Certifying Authority.+"
                    p:maxPathLength="3"
                    p:maxPathLengthAllowUnspecified="true"
                    />

...

X.509 Certificate Resolver

Code Block

<bean class="org.jasig.cas.adaptors.x509.authentication.principal.X509CertificateCredentialsToIdentifierPrincipalResolver"
		p:identifier="$CN" />

...