CASifying Moodle
Default CAS Plugin
The latest version of Moodle, 1.9, comes packaged with an authentication plugin for CAS. This authentication plugin should work for connecting Moodle to Middlebury's CAS server, but relies on an LDAP server for attribute lookup. While not ideal, this configuration should work with a few caveats that are listed in our CAS documentation.
Advantages of direct LDAP
Using the default CAS plugin rather than LDAP directly has the following advantages:
- Moodle will use the same 'web-id' for uniquely identifying users
- Once a custom CAS plugin is created, the user-id ('user' table, 'username' column in the Moodle database) will stay the same, hopefully preventing any need for user-mapping or making that job much easier if it is needed.
- Users will not have to enter their credentials again if they have already logged into another application via CAS.
Caveats
- Because groups in our LDAP server (Active Directory) may be members of groups themselves, your application will need to manually traverse the group hierarchy in order to get a full list of groups.
Example: A class-group does not directly have any members, but rather is a container for three groups (instructors, students, audits) that each directly contain members. - We may run multiple LDAP servers in the future to hold information on visitors and other constituent groups. At that time you may encounter users who can authenticate via CAS, but are not listed in our primary LDAP server.
Configuration in Moodle
After enabling the CAS module, use the following configuration settings:
CAS server configuration
Setting |
Value |
---|---|
Hostname |
login.middlebury.edu |
Base URI |
cas/ |
Port |
443 |
Version |
2.0 |
Proxy mode |
No |
Logout CAS |
Optional, we use 'Yes' |
Multi-authentication |
Optional, we use 'No' |
Server validation |
Optional |
LDAP server configuration
Setting | Value |
---|---|
Host URL | ldap://ad.middlebury.edu |
Version | 3 |
LDAP Encoding | utf-8 |
Bind settings
Setting | Value |
---|---|
Distinguished Name | CN=********,CN=Users,DC=middlebury,DC=edu |
Password | ******** |
User lookup settings
Setting | Value |
---|---|
User type | MS ActiveDirectory |
Contexts | DC=middlebury,DC=edu |
Search subcontexts | Yes |
Dereference aliases | No (not sure what this does) |
User attribute | middleburyCollegeUID |
Member attribute | member |
Member attribute uses dn | 1 |
Object class |
Course creator
Setting | Value |
---|---|
Attribute creators | We don't use this. |
Group creators | We don't use this. |
Cron synchronization script
Setting | Value |
---|---|
Removed ext user | Optional, we use 'Suspend internal' |
Data mapping
The fields that are mapped should probably be set to Update local "On every login" and Lock value "Locked", but for some you might want to have them pre-populated on creation and then allow user-editing of the value. Update external should always be "Never".
Setting | Value |
---|---|
First name | givenName |
Surname | sn |
Email address | |
City/town | |
Country | |
Language | (msExchUserCulture might work, but returns values with a hyphen rather than an underscore: en-US rather than en_US. Haven't been able to test if this works or not.) |
Description | title |
Web page | |
ID number | |
Institution | company |
Department | department |
Phone 1 | telephoneNumber |
Phone 2 | |
Address | extensionAttribute3 |
Account Migration Strategies.
There are two general migration strategies possible:
1. Keep the old LDAP and any self-registered Moodle accounts in place and add CAS as a login option
The advantage of this is that old accounts will still work and have access to all old data. The disadvantage is that new logins via CAS will not have access to the courses and data that a person created using their old account. Each current user will have two accounts in the system, possibly with the same display name (which could add confusion).
Users may be able to give their new accounts access to their old courses if desired and let attrition take care of the rest of the old content.
2. Swap out the old LDAP login system for CAS logins, migrating account info
Rather than just adding CAS as a login option, add CAS as a login option, remove the old LDAP login option, and update the user table to replace the old username with the new MiddleburyCollegeUID and change the auth-type for the account from ldap to cas.
This strategy allows users to log in via CAS and see all of their old content and courses. They will still only have one account in the system. The log-in field they see is the only thing that would change for them (and their username/password, but those will now be in line with other systems that authenticate via CAS).
The disadvantage to this strategy is the need to map the old user accounts in the Moodle database to their new ones in CAS/ActiveDirectory. If the usernames are not equivalent to those in the Active Directory, you'll need to look up users in the Active Directory based on some other fields (first name, last name, or some other data) in order to find their MiddleburyCollegeUIDs.
Migration Strategy #2 - Updating User Records
If Moodle has been in use previously, users will not be able to access their existing courses when they log-in via CAS as duplicate accounts will be created. To fix this the Moodle user table must be updated to change the auth column values from 'ldap' to 'cas' and the username column from the sAMAccountName (e.g. afranco) to the MiddleburyCollegeUID (e.g. B0F836FCDB74DDFF7A17C02C62CDB227). Once this change has been made for all users, logins that occur via CAS will retain their internal priviliges and associations to courses.
After updating the user records in this way, it is important to disable LDAP authentication so that duplicate accounts to not get created again.
Example Update Script - match sAMAccountName against username
Note, the script will fail to update LDAP user records to CAS user records if the user has already logged in via CAS. A duplicate key exception on the username column will prevent update, so run this before users log in via CAS.
<?php $ldapHost = 'ldap://middlebury.edu'; $ldapUser = '******'; $ldapPass = '******'; $ldapBase = 'dc=middlebury,dc=edu'; $dsn = 'mysql:host=localhost;dbname=afranco_moodle'; $dbUser = '******'; $dbPass = '******'; $dbTablePrefix = 'mdl_'; $db = new PDO($dsn, $dbUser, $dbPass); $ldapLink = ldap_connect($ldapHost); if (!$ldapLink) throw new Exception('Could not connect to the ldap server'); if (!ldap_bind($ldapLink, $ldapUser, $ldapPass)) throw new Exception('Could not bind to the ldap server'); $updateStmt = $db->prepare("UPDATE ".$dbTablePrefix."user SET auth='cas', username=:uid WHERE id=:id LIMIT 1"); foreach ($db->query("SELECT id, username FROM ".$dbTablePrefix."user WHERE auth='ldap';") as $row) { $id = $row['id']; $username = $row['username']; // Look up the user in LDAP $results = ldap_search($ldapLink, $ldapBase, "(sAMAccountName=".$username.")", array("MiddleburyCollegeUID"), 0, 1); // Just skip if the user in no longer in LDAP, i.e. alumni if (!$results) { print "Could not find $username in LDAP\n"; continue; } $info = ldap_get_entries($ldapLink, $results); // Just skip if the user in no longer in LDAP, i.e. alumni if (!$info['count']) { print "Could not find $username in LDAP\n"; continue; } // if they don't have a MiddleburyCollegeUID (i.e. some vendors), there is nothing we can do, skip if (!isset($info[0]['middleburycollegeuid']) || !$info[0]['middleburycollegeuid']['count'] || !strlen($info[0]['middleburycollegeuid'][0])) continue; $uid = $info[0]['middleburycollegeuid'][0]; print "Updating $username\tto\t$uid"; $num = $updateStmt->execute(array(':uid' => $uid, ':id' => $id)); if ($num) print "\t ==> Success"; else print "\t ==> Failed"; print "\n"; }
Example Update Script - match givenName and sn against firstname and lastname
Note, the script will fail to update LDAP user records to CAS user records if the user has already logged in via CAS. A duplicate key exception on the username column will prevent update, so run this before users log in via CAS.
<?php $ldapHost = 'ldap://middlebury.edu'; $ldapUser = '******'; $ldapPass = '******'; $ldapBase = 'dc=middlebury,dc=edu'; $dsn = 'mysql:host=localhost;dbname=afranco_moodle'; $dbUser = '******'; $dbPass = '******'; $dbTablePrefix = 'mdl_'; $db = new PDO($dsn, $dbUser, $dbPass); $ldapLink = ldap_connect($ldapHost); if (!$ldapLink) throw new Exception('Could not connect to the ldap server'); if (!ldap_bind($ldapLink, $ldapUser, $ldapPass)) throw new Exception('Could not bind to the ldap server'); $updateStmt = $db->prepare("UPDATE ".$dbTablePrefix."user SET auth='cas', username=:uid WHERE id=:id LIMIT 1"); foreach ($db->query("SELECT id, username, firstname, lastname FROM ".$dbTablePrefix."user WHERE auth='ldap';") as $row) { $id = $row['id']; $username = $row['username']; $firstname = $row['firstname']; $lastname = $row['lastname']; // Look up the user in LDAP $results = ldap_search($ldapLink, $ldapBase, "&(givenName=".$firstname.")(sn=".$lastname.")", array("MiddleburyCollegeUID"), 0, 1); // Just skip if the user in no longer in LDAP, i.e. alumni if (!$results) { print "Could not find $firstname $lastname in LDAP\n"; continue; } $info = ldap_get_entries($ldapLink, $results); // Just skip if the user in no longer in LDAP, i.e. alumni if (!$info['count']) { print "Could not find $firstname $lastname in LDAP\n"; continue; } // if they don't have a MiddleburyCollegeUID (i.e. some vendors), there is nothing we can do, skip if (!isset($info[0]['middleburycollegeuid']) || !$info[0]['middleburycollegeuid']['count'] || !strlen($info[0]['middleburycollegeuid'][0])) continue; $uid = $info[0]['middleburycollegeuid'][0]; print "Updating $firstname $lastname\tto\t$uid"; $num = $updateStmt->execute(array(':uid' => $uid, ':id' => $id)); if ($num) print "\t ==> Success"; else print "\t ==> Failed"; print "\n"; }
Other Notes
Guest Accounts
Middlebury College is in the process of launching a "self-registered guest account" that would allow guests to be authenticated to web applications via centrally-managed guest accounts that use guests' email addresses as their credential. Guest IDs are deterministic (email hashed with a salt), so we can map email addresses to guest IDs if needed. Provide Adam Franco (afrancoATmiddleburyDOTedu) with a list of guest email addresses and he can return you a mapping between those email addresses and the guest IDs.
Because our guest accounts are stored in a second AD, attribute lookup for guests will always fail. Setting attributes to be 'Unlocked if empty' will allow guests to fill in their name and other information after they log in.
Future: Custom CAS Plugin with Attribute Support
Ideally, at some point in the future we would like to create a customized CAS plugin that makes use of the user attributes returned in the CAS response. This page will be updated once such a custom plugin has been developed.
Using a custom CAS plugin has the following advantages over the default CAS plugin:
- When implemented, visitor accounts would be available in Moodle
- A full list of groups (including parent groups) would be available
- This page was last edited on 14 November 2022, at 15:12.
- Privacy policy
- About Library & ITS Wiki
- Disclaimers