Greets to the MODx community.
Some time ago I wrote
this plugin for MODx 1.0 to handle LDAP Authentication. Unfortunately though I was having trouble getting 2.0.0-beta-4 to work in my test environment.
Recently I updated my test environment and decided to give 2.0.0-beta-5 a spin and lo-and-behold it worked this time. So once I got the CMS up and running, I set to work adapting the LDAP Authentication plugin for 1.0 to function in 2.0. Here are the results:
<?php
/*
LDAP Authentication Plugin
MODx 2.0.0-beta-5
January 2010 - Matt Pierce (http://mpierce.pie2k.com)
This code is provided as-is and without warranty. It is free to use, copy, modify, and redistribute.
NOTE:
MODx only allows 2 possible return values for the OnManagerAuthentication event:
true the username/password combination is valid
false the username/password combination IS NOT valid
The problem with this setup is that there's no room for reporting some other type of authentication problem.
For example, the LDAP server could be temporarily unavailable, or the script may be misconfigured.
It seems that this hasn't escaped the authors. Since a false return value COULD indicate some other failure
MODx checks the input values against the local users table.
tl;dr: If your LDAP server fails some day, you should still be able to login with whatever password you set
in the MODx manager interface.
*/
global $_lang;
$event_name = $modx->event->name;
if(!class_exists('LDAPAuthorization')) {
class LDAPAuthorization {
/*
You need to provide some information about your LDAP server. This isn't
completely 100% fire-and-forget. If you don't know these variables then
you need to contact your LDAP administrator to find them out.
Possible future improvement: Put these values into the plugin properties?
Is that easier for site administrators or would they rather edit this code?
*/
// your ldap server
var $LDAP_Server = "ldap.yourdomain.com";
var $LDAP_Port = 636; // ldaps=636; ldap=389
// the tree on which you should be searching for users
var $LDAP_BaseDN = "o=yourdomain.com,dc=yourdomain,dc=com";
// the field to match the username with (usually "uid")
var $LDAP_SearchField = "uid";
// If your LDAP server requires you to use a DN/Password combination to initiate a search, set these variables.
// If not, set them to null;
var $LDAP_BindDN = "uid=search-accoiunt,ou=generic-accounts,o=yourdomain.com,ou=yourdomain,ou=com";
var $LDAP_BindPW = "******";
// LDAPAuth() : use LDAP to verify a username and password
// parameters:
// $username the user's unique id
// $password the password the user entered on the login form
// returns:
// true the username/password combination is valid
// false the username/password combination is not valid OR
// the script was unable to search the directory
function LDAPAuth($username, $Password)
{
// HOW THIS SCRIPT WORKS:
// The general idea is to take the username and password given by the user, attempt to use it
// to log in to the LDAP directory, then tell the MODx system whether or not the LDAP directory
// accepted the credentials.
// The catch is that an LDAP directory requires you to provide a DN (distinct name) and password,
// not merely a username and password. So, in order to mesh up the username with the DN, we first
// login with some generic account, search for a user with the same username, get the DN, then
// try to login with the DN from the search and the password from the login form.
// connect to the LDAP Server
if ($connect_id = @ldap_connect($this->LDAP_Server, $this->LDAP_Port)) {
// After connect, bind with some account that will allow us to search for a user's DN.
if ($this->LDAP_BindDN && $this->LDAP_BindPW) {
// If the preferences are set, bind with the user's serach DN/PW.
$search_bind_result = @ldap_bind($connect_id, $this->LDAP_BindDN, $this->LDAP_BindPW);
} else {
// If not, try an annonymous bind.
$search_bind_result = @ldap_bind($connect_id);
}
// if the bind performed above was successful ...
if ($search_bind_result) {
// use the username porvided to search for a user with the same username in the directory
$attrs = array("dn", "mail", "uid"); // these are the LDAP fields we want to retrieve.
$search_id = @ldap_search($connect_id, $this->LDAP_BaseDN, "(".$this->LDAP_SearchField."=".$username.")", $attrs);
// get the results of that search
$result_array = ldap_get_entries($connect_id, $search_id);
if (is_array($result_array) && isset($result_array[0])) {
// attempt to bind to the LDAP server with the user's dn and the provided password
if (@ldap_bind($connect_id, $result_array[0]['dn'], $Password)) {
// if successful, return true, all other cases return false
@ldap_close($connect_id);
return true;
} else {
@ldap_close($connect_id);
return false;
}
} else {
@ldap_close($connect_id);
return false;
}
} else {
@ldap_close($connect_id);
return false;
}
} else {
// connection failed, no need to close the connection
return false;
}
}
}
}
// If this is invoked by the "OnManagerAuthentication" event ...
if($event_name == 'OnManagerAuthentication') {
// Create an instance of the authorization object defined above
$auth = new LDAPAuthorization;
// Check the username & password
$result = $auth->LDAPAuth($modx->event->params["user"]->username, $modx->event->params["password"]);
// return the result to MODx
$modx->event->_output = $result;
}
?>
(This code is written for
2.0.0-beta-5 and will hopefully work in later versions of 2.0. If you’re looking for one that works in 1.0 see the link above the code.)
Hopefully this will prove useful to somebody. I know that demand for this sort of thing was relatively high when I wrote the first one - I’m guessing it might still be high now.