Tuesday, May 24, 2011

CFSelenium News: No Need To Start Selenium Server Separately Anymore

In my earlier blog post on how to install and run CFSelenium, my steps included directions on copying the .jar file for the Selenium-RC/Selenium Server application to an easily accessible location and then starting your CFSelenium testing session by executing that .jar file from the command line.

I'm happy to report that anyone using CFSelenium in conjunction with ColdFusion 8 or 9 can skip those particular steps.  Marc Esher (of MXUnit fame) recently contributed code to the CFSelenium project that enables CFSelenium to start the Selenium Server (if it's not already running) anytime you run a test, and stops the server once the test is complete.  This code is now part of the latest version of CFSelenium that can be download via the CFSelenium GitHub page.

Unfortunately, when I revised the tag-based, CF 7/8-friendly versions of the CFSelenium files to incorporate Marc's code and ran it against CF 7, I found that CF 7 apparently could not start and stop the Selenium Server via the new code.  So ColdFusion 7 users will still have to start Selenium Server from the command line, but otherwise CFSelenium still works in CF 7.

Monday, May 23, 2011

Integrating Mura with the Jasig Central Authentication Services (CAS)

I've already posted most of this information on the Mura forums, but I wanted to post it here as well for anyone looking for information on this topic.

With the blessings of my boss, I've been playing around with the ColdFusion-powered Mura content management system to see if it might be a good CMS option for us.  From what I've seen so far, Mura is a well-thought out CMS system that is both powerful and easy to use, which is a difficult balancing act.

One of the things my boss wanted me to investigate was whether or not we could tie Mura in with our single sign-on solution, which is CAS (Central Authentication Service), a Jasig project originally created at Yale.  When a user tries to access a page or a site that requires them to authenticate, they are redirected to the CAS server and enters their LDAP-based user id and password on the CAS login page.  If they successfully authenticate, the CAS server redirects them back to the original site and stores a token as a cookie in the user's browser.  If the user visits a different website secured by CAS, the cookie allows them access to that site without the need to log in again.

Mura provides a plugin architecture that allows developers to intercept certain Mura events and run their own code.  A number of Mura shops have created plugins that intercept the Mura login events in order to tie in with their in-house directory and authentication servers, but those login events are triggered by the admin and user login forms built into Mura.  I couldn't go that route:  I needed to circumvent and replace the Mura login form(s) with the CAS login form and have Mura log in the user based on the credential information returned by CAS.

After some digging into the code and some trial-and-error, I came up with a way to authenticate Mura administrators via our CAS system.  First, I created admin user accounts in Mura where the username matched the username/user identifier returned by a successful CAS login.  Then I created a Mura plugin that would fire with the OnGlobalRequestStart event with the following event handler:



...The code in this plugin will only execute if the user navigates to /admin/index.cfm (without any URL query parameters) and the "mura" struct in the session scope contains an empty userId key.  Without these conditionals, the login code will run at times when you don't want it to, like when the user is trying to log out or when saving page edits (apparently saving content changes involves navigating to the admin/index.cfm page without URL query parameters).

The NetId is the user identifier returned by CAS.  If it's not found in the session scope, the action is redirected to adminCASLogin.cfm, a variation on the CAS authentication script posted on the CAS website (https://wiki.jasig.org/display/CASC/ColdFusion+client+script).  The user enters their username and password on our CAS login form page, and if the log in is successful the adminCASLogin.cfm page copies the NetId to the session scope and uses cflocation to go to admin/index.cfm.

Now that the NetID exists in session, the plugin queries the tUsers table (the Mura database table containing, well, user records) for an admin account with a username that matches the NetId and gets back the Mura userId for that person, and provides that userId along with other values to the loginManager's loginByUserID() function.  The admin user ends up in the administrative Dashboard, logged in and ready to go.

The one drawback to this method is that when a user logs out, they are presented with the normal Mura admin login form, which can be a little confusing.  But without knowing the password to their Mura user record, they would still have to log in via CAS.

I wrote a similar plugin for requiring CAS authentication to access a particular Mura site (an "internal" site only viable to authorized personnel).  In this case, the event handler executes in response to the OnSiteRequestStart event:



...in this case, I had to create my own session variable (session.userId) for denoting if the user had already authenticated via CAS.  The siteCASlogin.cfm file is pretty much the same as the adminCASLogin.cfm file in the first example except that when it completes the CAS authentication it uses cflocation to return to the index.cfm page of the site (rather than admin/index.cfm).

If the user successfully authenticates via CAS and has any sort of Mura user account, they will be granted access to the site, and if they are a Mura admin they will be logged in so they can edit the site if they wish.