Yale CAS Customizations
Generally speaking, one should try not to make modifications to off the shelf software. However, the advantage of Open Source software is that you have access to the source code if you need it. Sometime a single line change to the source accomplishes quickly something that could otherwise only be done with weeks or months of trial and error through the poorly documented Spring Configuration. Sometimes Yale has special needs for which the JASIG code has no solution.
The good news is that CAS has a logical modular design based on well defined, if sometimes poorly documented, interfaces. If you understand the architecture of Authentication Managers, Authentication Handlers, Credential to Principal Resolvers, and Ticket Caches, then you can create your own implementation of any specific interface and generate custom code that will easily migrate to new releases.
There are several problems that trigger a need for customization:Yale has a small number of customizations to CAS. They are installed using standard CAS conventions for adding code and changing configuration.
To understand the way the customizations integrate to CAS you need to understand the CAS architecture, interfaces, and Spring configuration. This is not, however, a place to review the vanilla CAS design.
JBoss (not Tomcat)
Yale Production Services runs Java applications under JBoss, while JASIG releases CAS for the simpler Tomcat environment.
A Web Application like CAS is packaged in a WAR file. The WAR file contains a bunch of JAR files in the WEB-INF/lib directory and the Java standard says that these JAR files are used first, and only then does the application server search its own libraries. That used to work fine and in Tomcat where the application server provides almost nothing in the way of library services it still works.
However, the modern J2EE standard requires that a full function server provides a wide range of built in services. Sometimes the server (JBoss) has to create a certain type of object and pass it to the application. When the server creates something, it uses its own JAR library files. However, because Tomcat doesn't provide a full set of services, programs like CAS are often packaged up with their own copies of some version of the same JAR files that CAS uses in Tomcat because the server doesn't provide that function itself.
With each new version of JBoss and each new release of CAS, one has to use trial and error to discover conflicts between the JBoss version of a particular service and the version of the same function that comes built into the CAS WAR. Frequently it is just a matter of deleting a JAR file from WEB-INF/lib in CAS. Alternately, you can add an XML element in the JBoss configuration file in CAS to override the normal JBoss processing and absolutely require that JBoss use the CAS JAR and not its own JAR.
The most perplexing problem, but one that everyone can understand, comes from the generic problem of logging errors.
First, there are lots of different ways to do logging and long ago Java gave up trying to develop any standard. Instead there is log4J, slf4j, Commons Logging, and about a dozen other competing solutions to the same problem. Even within a single application, CAS has to consider that different libraries it uses will each have selected a different logging interface. So all the different logging systems get along with each other quite nicely within any application.
Tomcat only supports the basic Servlet functions while JBoss supports the entire suite of J2EE standard services. When an application decides to use a standard API that is part of J2EE but is not installed in Tomcat, they have to include their own copy of the support library. For example, CAS has JPA support for the Ticket Cache. JPA is built into JBoss and every other J2EE server, but to get that service in Tomcat you have to include a copy of Hibernate (or another JPA implementation) in your WAR.
This creates a potential conflict when CAS comes with a Maven project that includes into the WAR duplicate copies (sometimes at slightly different version levels) of services that JBoss supplies out of the box. Do you continue to use the CAS copy of the service, do you shift over to use the JBoss built in version, and if you do nothing then does the code work at all.
The most complex problem deals with the generic problem of "logging", because there are so many different interfaces, implementations, and libraries all concurrently in use in most applications.
Logging is pretty much the same no matter which library or implementation you choose. There are some slight differences that give way to what seems like a dozen different competing solutions to the same problem (log4j, slf4j, Commons logging, JBoss logging, ...). Among the various optional modules of CAS, and then among all the JAR file libraries that CAS includes, there is some code that uses each available interface. Since this is typical, the libraries are designed to all coexist with each other and they can nicely interoperate. It a standalone application, or in Tomcat, they automatically discover each other and work out their differences. However, when you run a bunch of applications inside a server like JBoss, there are two different ways you might like to operate. If the administrator of the JBoss server is also the person expected to read the logs and debug problems, then he wants to control all the logging at the JBoss server level and control what he sees. On the other hand, if five different applications written by five different developers share the same server, and each developer is responsible for his application, then each developer wants to control his own log files and logging options.
JBoss EAP 6.1 tries to leave both options available, but in our experience it is almost impossible to really get the CAS logging details you want if you try to configure the logging at the JBoss server level. However, to transfer 100% of CAS logging control to the configuration files in the CAS application, you have to include a JBoss configuration file in the CAS application that lists absolutely every possible log related JAR file that JBoss and CAS could both know about. That turns out to be 4 jboss logging jars, 4 slf4j jars, the log4j jar, and the commons logging jar. If you forget one of them, then JBoss ends up trying to hook some of the logging and CAS tries to hook some of the logging and you end up with no log messages anywhere.
Yale has a Netid System (not just AD)
CAS added a password expiration mechanism that works well with Active Directory. While Yale uses AD to authenticate passwords, the Netid system that creates, activates, and changes passwords for Netids is driven by databases external to AD. In the past Yale has had its own custom version of Password Expiration logic, although in the future we may attempt to change AD enough to allow us to use the standard JASIG CAS module.
In some cases you want JBoss to control all the logs for all the applications so you have a single unified logging system.
Alternately, the developer of each application may want a separate log for that application that the developer controls independently of JBoss and the other applications.
On paper it should be possible to configure either solution, but in practice we have not figured out how to get JBoss logging to do everything we want for CAS. So at Yale CAS runs under JBoss completely separated from the JBoss logging services and the JBoss provided logging libraries. This is accomplished by adding a JBoss specific configuration file, WEB-INF/jobss-deployment-structure.xml. This file, among other things, lists libraries that JBoss would normally make available to an application that runs under it but where CAS wants to use its own version of the library (or to have no version of library at all). Generally speaking, the Yale CAS version of this file excludes all the expected and obscure logging related libraries for all the logging APIs.
To get the exact opposite result, that is to replace some library that CAS normally includes in WEB-INF/lib with a version of the same code that JBoss supplies as a built in service, the Maven configuration is changed to indicate that the library is "provided" by the server environment.
Maven Dependency Management is supposed to provide the ability to force a specific release of a dependent library across the entire build process. However, building CAS is complicated because it involves compiling the Core library, and then the optional libraries, and then separately assembling them in the Webapp project.Then on top of that, the Yale customizations are added to the vanilla WAR using the CAS recommended technique of Maven "WAR Overlay".
When the dust settles, you occasionally have two different versions of the same library included in WEB-INF/lib. Probably one version was added by the vanilla CAS build process, and then a second version number was unintentionally added during the Yale customizations. You might spend a week of trial and error trying to figure out what you don't understand about Maven Dependency Management syntax, but there is a brute force solution to delete the libraries you don't want (and while you are at it the libraries that conflict with the JBoss enviroment) by specifying the undesirable JAR files in the "packagingExcludes" tag of the maven-war-plugin configuration in the Yale Webapp POM. Basically, this is a place to put in a last minute override and delete of files that you do not want copied to the final WAR artifact.
Project Structure
CAS is distributed as a "parent" Maven project contains a set of "subprojects" in subdirectories. The POM at the top "parent" level lists the subprojects that are to be compiled.
Every CAS Server requires the cas-server-core subproject which contains about 95% of the CAS code. Yale also uses the cas-server-support-ldap optional subproject to access Active Directory using LDAP protocol to authenticate users and passwords.
Yale includes the cas-server-integration-ehcache project to use Ehcache as the ticket replication mechanism for CAS clustering.
CAS includes the cas-server-support-trusted project, although at any given time it may not be used. "Trusted" means that CAS allows some external agency to authenticate users. If we ever use the ability of the F5 to validate X.509 User Certificates and to turn the validated netid into an extended HTTP Header, then the code in this project will provide the support or at least provide a model for the use of this support.
The only Yale optional subproject that is fully used is cas-server-expired-password-change. This supports the Yale custom mechanism to check if passwords have been changed in the last year and redirect the user to the Netid (Change your Password) application if necessary.
The cas-server-preferences module is a Yale customization that was never used but which poses no particular problem if the user navigates to /cas/preferences he is supposed to be presented with a menu of options to customize CAS behavior on this browser. Selections are saved as a Cookie, which is then available to customize the CAS behavior in all subsequent logins. It could be used to enable CAS options that we don't want to present to a user unless he specifically requested them.
Yale developed a package of optional CAS code under the brand name CUSHY. All the CUSHY code is in the cas-server-support-CusyTicketRegistry project, but while the entire project is built, currently CAS at Yale only uses one bean based on one class (CushyClusterConfiguration) and ignores the rest of the code. The CushyClusterConfiguration bean is added to the TicketRegistry XML Spring configuration file to configure Ehcache after discovering the current environment (sandbox, dev, test, prod). Echache needs a configuration file that lists all the other nodes in the cluster. Without this bean, we would need to keep a separate configuration file for each server and the Jenkins install job would have to install the correct file on each machine. Automatically configuring the file based on discovering the hostname of the current machine is much simpler.
Occasional Loss of "Flow" Variables during Login
...
Unfortunately, this fails sometimes at Yale. It is not clear if anyone else experiences this problem with CAS, and it may be due to some local network problem or the way we configure the network. However, frequently enough the Flow seems to get broken when the user hits Enter on the page where he submits the userid and password. The JASIG CAS code stores one important piece of data in a Webflow variable (the service= string) that has to be available after this form is submitted, and it has no logic that can recover if the Flow is broken by some network problem. It is relatively trivial to program around this, because the service string could just as easily be stored in a hidden field of the form as in a Flow variable. However, this is a modification of the JASIG code that makes CAS more robust against failure of the Webflow mechanism for whatever reason.
...
Statistics
There is a standard CAS has a large number of optional features, but they are all configured by the central administrator. There is no provision for the User to select optional CAS behavior. It seemed that this feature would be required in the future, so a "proof of concept" module has been created called CAS preferences. When the user goes to the /cas/preferences URL he is presented with a Form where he can select various check boxes and radio buttons. These choices are then written to a Cookie scoped to the CAS server and are available at the beginning of the login Webflow to influence the path through subsequent login sequences. This could be used to enable additional forms of authentication, special processing, or even additional logging to debug a problem someone else is having.
Statistics
There is a standard CAS page with basic server information, but Yale Production Services has a standard format expected for Opsview. So we used the requirement to add Yale specific monitoring datapage with basic server information, but Yale Production Services has a standard format expected for Opsview. So we used the requirement to add Yale specific monitoring data.
WAR Overlay
The standard CAS Build sequence first complies Core and all the optional JAR files and stores them in the Maven Repository. Then a vanilla cas-server-webapp project build an uncustomized CAS WAR file that contains all the standard HTML and XML files and includes all the previous modules and dependencies in the lib directory.
The last step uses a Maven technique called a WAR Overlay. This is a Maven project that contains only the Yale customization files. Replacement HTML and CSS files, replacement Spring XML configuration files, and the source of a small number of Java classes. This project depends on the vanilla WAR file built in the previous step. That WAR file provides a template for the final CAS WAR, but any files in the WAR Overlay project replace files with the same name in the vanilla template.
While large blocks of Yale Java code are in the the previously mentioned Yale JAR projects, the WAR Overlay contains all the Yale customizations. Thanks to the overlay mechanism, it does not have to duplicate any of the standard CAS files that require no changes. Every file in this project is Yale specific. Any CAS file that is not in the project is supplied by the vanilla CAS release and has not been modified by Yale.