Page 1 of 2

LDAP Authentication

Posted: Fri Feb 16, 2007 5:46 pm
by wickhamandrew
I have created a hack that will allow you to use LDAP authentication, like many people have been requesting. I have attached the files. If you have any questions, feel free to e-mail me.

UPDATE!!
I have tested this and confirm it works with Active Directory also! You must use a base of cn=users,dc=example,dc=com in order for it to work.

Important Issues to Consider:
1. Your admin account will no longer work if the user is not in the LDAP directory. This can be fixed by a.) going into the database in the users table and changing the admin user to be the same as an account in the LDAP directory or b.) create a admin user in the LDAP directory.

2. You must change the variables in the class.ldap.inc.php file to reflect your LDAP schema. The things you will have to change are the hostname and the basedir. The hostname can either by an IP address or DNS host of the LDAP server. The basedir is the location where the users are stored in the LDAP schema. For our OSX server it is as follows: ldap.domain.com and cn=users,dc=domain,dc=com.

3. This was written for CMSMS 1.0.4, if you upgrade or do not have this version the hack may not work.

Installation Instructions:
1. Save the below as class.ldap.inc.php and copy it to lib/classes/.

Code: Select all

<?php

// -----=============================================----- \\
//                LDAP Authentication Class                \\
// -----=============================================----- \\
// Class written by Andrew Wickham in January of 2005.     \\
//                                                         \\
// http://lake.stark.k12.oh.us                             \\
// http://www.liquidpro.net                                \\           
// --------------------------------------------------------\\

class LDAP {
	public $connection;
	public $bind;

	public $hostname = "ldap.example.com";
	public $basedir = "cn=users,dc=ldap,dc=example,dc=com";
	
	function connect() {
		$this->connection = ldap_connect($this->hostname) or exit("Error: " . ldap_error($this->connection));
		ldap_set_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, 3) or exit("Error: " . ldap_error($this->connection));
	}
	
	function disconnect() {
		ldap_close($this->connection);   	
	}
	
	function bind($username, $password) {
		$this->connect();
		
		$this->bind = @ldap_bind($this->connection, "uid=" . $username . "," . $this->basedir, $password);
		if(!$this->bind)
			$this->bind = @ldap_bind($this->connection, "cn=" . $username . "," . $this->basedir, $password);
	
		if(!$this->bind)
			$authenticated = false;
		else
			$authenticated = true;
	
		$this->disconnect();
		
		return $authenticated;
	}
	
	function connection() {
		return $this->connection;
	}
	
	function error() {
		echo "Error: " . ldap_error($this->connection);
	}
}

?>
2. Save the code below as class.useroperations.inc.php and copy the file to lib/classes/ and replace the existing one.

Code: Select all

<?php
#CMS - CMS Made Simple
#(c)2004 by Ted Kulp (tedkulp@users.sf.net)
#This project's homepage is: http://cmsmadesimple.org
#
#This program is free software; you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation; either version 2 of the License, or
#(at your option) any later version.
#
#This program is distributed in the hope that it will be useful,
#BUT withOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.
#You should have received a copy of the GNU General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#$Id: class.user.inc.php 2961 2006-06-25 04:49:31Z wishy $

/**
 * Class for doing user related functions.  Maybe of the User object functions
 * are just wrappers around these.
 *
 * @since 0.6.1
 * @package CMS
 */

require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'class.user.inc.php');

class UserOperations
{
	/**
	 * Gets a list of all users
	 *
	 * @returns array An array of User objects
	 * @since 0.6.1
	 */
	function &LoadUsers()
	{
		global $gCms;
		$db = &$gCms->GetDb();

		$result = array();

		$query = "SELECT user_id, username, password, first_name, last_name, email, active, admin_access FROM ".cms_db_prefix()."users ORDER BY username";
		$dbresult = $db->Execute($query);

		while ($dbresult && $row = $dbresult->FetchRow())
		{
			$oneuser =& new User();
			$oneuser->id = $row['user_id'];
			$oneuser->username = $row['username'];
			$oneuser->firstname = $row['first_name'];
			$oneuser->lastname = $row['last_name'];
			$oneuser->email = $row['email'];
			$oneuser->password = $row['password'];
			$oneuser->active = $row['active'];
			$oneuser->adminaccess = $row['admin_access'];
			$result[] =& $oneuser;
		}

		return $result;
	}


	/**
	 * Gets a list of all users in a given group
	 *
	 * @param mixed $groupid Group for the loaded users
	 * @returns array An array of User objects
	 */
	function &LoadUsersInGroup($groupid)
	{
		global $gCms;
		$db = &$gCms->GetDb();
		$result = array();

		$query = "SELECT u.user_id, u.username, u.password, u.first_name, u.last_name, u.email, u.active, u.admin_access FROM ".cms_db_prefix()."users u, ".cms_db_prefix()."groups g, ".cms_db_prefix()."user_groups cg where cg.user_id = u.user_id and cg.group_id = g.group_id and g.group_id =? ORDER BY username";
		$dbresult = $db->Execute($query, array($groupid));

		while ($dbresult && $row = $dbresult->FetchRow())
		{
			$oneuser =& new User();
			$oneuser->id = $row['user_id'];
			$oneuser->username = $row['username'];
			$oneuser->firstname = $row['first_name'];
			$oneuser->lastname = $row['last_name'];
			$oneuser->email = $row['email'];
			$oneuser->password = $row['password'];
			$oneuser->active = $row['active'];
			$oneuser->adminaccess = $row['admin_access'];
			$result[] =& $oneuser;
		}

		return $result;
	}

	/**
	 * Loads a user by username.
	 *
	 * @param mixed $username Username to load
	 * @param mixed $password Password to check against
	 * @param mixed $activeonly Only load the user if they are active
	 * @param mixed $adminaccessonly Only load the user if they have admin access
	 *
	 * @returns mixed If successful, the filled User object.  If it fails, it returns false.
	 * @since 0.6.1
	 */
	function &LoadUserByUsername($username, $password = '', $activeonly = true, $adminaccessonly = false)
	{
		$result = false;

		global $gCms;
		$db = &$gCms->GetDb();

		$params = array();

		$query = "SELECT user_id FROM ".cms_db_prefix()."users WHERE username = ?";
		$params[] = $username;

		if ($activeonly == true)
		{
			$query .= " AND active = 1";
		}

		if ($adminaccessonly == true)
		{
			$query .= " AND admin_access = 1";
		}

		$dbresult = $db->Execute($query, $params);

		if ($dbresult && $dbresult->RecordCount() > 0)
		{
			// Include the LDAP class
			require("class.ldap.inc.php");
			$ldap = new LDAP();
						
			// Attempt to bind to LDAP, if it's successful return the user object
			if($ldap->bind($username, $password)) 
			{
				$row = $dbresult->FetchRow();
				$id = $row['user_id'];
				$result =& UserOperations::LoadUserByID($id);
			}
		}
		
		return $result;
	}

	/**
	 * Loads a user by user id.
	 *
	 * @param mixed $id User id to load
	 *
	 * @returns mixed If successful, the filled User object.  If it fails, it returns false.
	 * @since 0.6.1
	 */
	function &LoadUserByID($id)
	{
		$result = false;

		global $gCms;
		$db = &$gCms->GetDb();

		$query = "SELECT username, password, active, first_name, last_name, admin_access, email FROM ".cms_db_prefix()."users WHERE user_id = ?";
		$dbresult = $db->Execute($query, array($id));

		while ($dbresult && $row = $dbresult->FetchRow())
		{
			$oneuser =& new User();
			$oneuser->id = $id;
			$oneuser->username = $row['username'];
			$oneuser->password = $row['password'];
			$oneuser->firstname = $row['first_name'];
			$oneuser->lastname = $row['last_name'];
			$oneuser->email = $row['email'];
			$oneuser->adminaccess = $row['admin_access'];
			$oneuser->active = $row['active'];
			$result =& $oneuser;
		}

		return $result;
	}

	/**
	 * Saves a new user to the database.
	 *
	 * @param mixed $usre User object to save
	 *
	 * @returns mixed The new user id.  If it fails, it returns -1.
	 * @since 0.6.1
	 */
	function InsertUser($user)
	{
		$result = -1; 

		global $gCms;
		$db = &$gCms->GetDb();

		$time = $db->DBTimeStamp(time());
		$new_user_id = $db->GenID(cms_db_prefix()."users_seq");
		$query = "INSERT INTO ".cms_db_prefix()."users (user_id, username, password, active, first_name, last_name, email, admin_access, create_date, modified_date) VALUES (?,?,?,?,?,?,?,?,".$time.",".$time.")";
		#$dbresult = $db->Execute($query, array($new_user_id, $user->username, $user->password, $user->active, $user->firstname, $user->lastname, $user->email, $user->adminaccess));
		$dbresult = $db->Execute($query, array($new_user_id, $user->username, $user->password, $user->active, $user->firstname, $user->lastname, $user->email, 1)); //Force admin access on
		if ($dbresult !== false)
		{
			$result = $new_user_id;
		}

		return $result;
	}

	/**
	 * Updates an existing user in the database.
	 *
	 * @param mixed $user User object to save
	 *
	 * @returns mixed If successful, true.  If it fails, false.
	 * @since 0.6.1
	 */
	function UpdateUser($user)
	{
		$result = false; 

		global $gCms;
		$db = &$gCms->GetDb();

		$time = $db->DBTimeStamp(time());
		$query = "UPDATE ".cms_db_prefix()."users SET username = ?, password = ?, active = ?, modified_date = ".$time.", first_name = ?, last_name = ?, email = ?, admin_access = ? WHERE user_id = ?";
		#$dbresult = $db->Execute($query, array($user->username, $user->password, $user->active, $user->firstname, $user->lastname, $user->email, $user->adminaccess, $user->id));
		$dbresult = $db->Execute($query, array($user->username, $user->password, $user->active, $user->firstname, $user->lastname, $user->email, 1, $user->id));
		if ($dbresult !== false)
		{
			$result = true;
		}

		return $result;
	}

	/**
	 * Deletes an existing user from the database.
	 *
	 * @param mixed $id Id of the user to delete
	 *
	 * @returns mixed If successful, true.  If it fails, false.
	 * @since 0.6.1
	 */
	function DeleteUserByID($id)
	{
		$result = false;

		global $gCms;
		$db = &$gCms->GetDb();

		$query = "DELETE FROM ".cms_db_prefix()."additional_users where user_id = ?";
		$db->Execute($query, array($id));

		$query = "DELETE FROM ".cms_db_prefix()."users where user_id = ?";
		$dbresult = $db->Execute($query, array($id));

		$query = "DELETE FROM ".cms_db_prefix()."userprefs where user_id = ?";
		$dbresult = $db->Execute($query, array($id));

		if ($dbresult !== false)
		{
			$result = true;
		}

		return $result;
	}

	/**
	 * Show the number of pages the given user's id owns.
	 *
	 * @param mixed $id Id of the user to count
	 *
	 * @returns mixed Number of pages they own.  0 if any problems.
	 * @since 0.6.1
	 */
	function CountPageOwnershipByID($id)
	{
		$result = 0;

		global $gCms;
		$db = &$gCms->GetDb();

		$query = "SELECT count(*) AS count FROM ".cms_db_prefix()."content WHERE owner_id = ?";
		$dbresult = $db->Execute($query, array($id));

		if ($dbresult && $dbresult->RecordCount() > 0)
		{
			$row = $dbresult->FetchRow();
			if (isset($row["count"]))
			{
				$result = $row["count"];
			}
		}

		return $result;
	}

	function GenerateDropdown($currentuserid='', $name='ownerid')
	{
		$result = '';

		$allusers = UserOperations::LoadUsers();

		if (count($allusers) > 0)
		{
			$result .= '<select name="'.$name.'">';
			foreach ($allusers as $oneuser)
			{
				$result .= '<option value="'.$oneuser->id.'"';
				if ($oneuser->id == $currentuserid)
				{
					$result .= ' selected="selected"';
				}
				$result .= '>'.$oneuser->username.'</option>';
			}
			$result .= '</select>';
		}

		return $result;
	}
}

?>

Re: LDAP Authentication

Posted: Sat Feb 17, 2007 5:32 am
by badhoy
very cool.  I'll have to fire up an LDAP server and give it a try.

Re: LDAP Authentication

Posted: Sat Feb 17, 2007 4:19 pm
by Pierre M.
Nice. LDAP, MLE and versioning : 3 steps towards international corporate sites.
Pierre M.

Re: LDAP Authentication

Posted: Sat Feb 17, 2007 10:44 pm
by wickhamandrew
Thanks guys. Please let me know if you have any troubles/questions.

Re: LDAP Authentication

Posted: Sat Mar 03, 2007 12:59 pm
by Pierre M.
Hello,

I've put some words in the wiki :
http://wiki.cmsmadesimple.org/index.php ... And_Tricks
Please review my text as I'm not a native English writer.

Pierre M.

Re: LDAP Authentication

Posted: Sun Mar 04, 2007 4:31 pm
by wickhamandrew
Thanks Pierre.

Maybe this is something that can be integrated into the next version? I could help with the development if needed. The user could select somewhere in the admin options that authentication should be done via LDAP instead of the CMSMS authentication. We could even make it modular so other people could develop and add to it (for example vBulletin, Invision Power Board, etc.) without having to hack the code up.

Re: LDAP Authentication

Posted: Sun Mar 04, 2007 8:10 pm
by petert
wickhamandrew wrote: We could even make it modular so other people could develop and add to it (for example vBulletin, Invision Power Board, etc.) without having to hack the code up.
please add an IMAP authentication too :)

Re: LDAP Authentication

Posted: Sun Mar 04, 2007 9:58 pm
by fredt
PeterT,

What do you mean/want to achieve with this??

If you're interested, I've already been playing with reading & parsing pop3 mails... I think opening a new topic here or in Requests would be fine, too !

FredT
(let's create the T gang  ;) )

Re: LDAP Authentication

Posted: Sun Mar 04, 2007 10:59 pm
by petert
fredt wrote: PeterT,

What do you mean/want to achieve with this??
I used my mailserver to authenticate people for a different project. The people log-in on the site using their e-mail adress and password on my mailserver.
fredt wrote: If you're interested, I've already been playing with reading & parsing pop3 mails... I think opening a new topic here or in Requests would be fine, too !
No, my request has got nothing to do with e-mail itself, just the authentication through a mailserver is what I am looking for.
fredt wrote: FredT
(let's create the T gang  ;) )
Hihi, my real name starts with a T, the circle is round....

Re: LDAP Authentication

Posted: Mon Mar 05, 2007 3:06 am
by wickhamandrew
Just to let everyone know, I set up an Active Directory server this weekend and tested it out on AD, and it works. You must use cn=users,dc=example,dc=com in order for it to work.

P.S. I will be working on IMAP Authentication this week.

Re: LDAP Authentication

Posted: Mon Mar 05, 2007 6:44 am
by petert
wickhamandrew wrote: Just to let everyone know, I set up an Active Directory server this weekend and tested it out on AD, and it works. You must use cn=users,dc=example,dc=com in order for it to work.

P.S. I will be working on IMAP Authentication this week.
great!

Keep up the good work :)

Re: LDAP Authentication

Posted: Tue Mar 06, 2007 2:14 pm
by Pierre M.
wickhamandrew wrote: Maybe this is something that can be integrated into the next version? I could help with the development if needed. The user could select somewhere in the admin options that authentication should be done via LDAP instead of the CMSMS authentication. We could even make it modular so other people could develop and add to it...
Exciting ! I hope somebody of the dev team has some time to hack on this with you to integrate it The Right Way(TM).
Have fun !
Pierre M.

Re: LDAP Authentication

Posted: Wed Sep 26, 2007 6:51 pm
by arhodes
I created a Plugin far having LDAP users in CMSMS. The project can be found at:

http://dev.cmsmadesimple.org/projects/ldapusers/

Sorry for the lack of documentation in it, but here are some quick notes:

-Authenticates against local users first [this is so if ldap goes down, you can still have a backup admin user]
-Requires a small 10 line hack in the core, which it applies for you
-Keeps user's info in the normal 'users' table, and automatically updates that info on each login
-Allows users to be automatically added to CMSMS upon first login if it finds them in the LDAP directory


Tested on CMSMS 1.1.2


P.S. - I am interested in making this a pure plugin that doesn't require a hack. So, for those involved in the core, if you like the plugin and want to talk about developing a more 'stable' solution, please let me know.

MODIFICATION: The hack is to the /lib/classes/class.global.inc.php file and I attached the diff for it

Re: LDAP Authentication

Posted: Thu Sep 27, 2007 10:08 am
by Pierre M.
Hello,

"a pure plugin that doesn't require a hack" would be very cool. A 2.0 compatible plugin/module too.
Have fun coding

Pierre M.

Re: LDAP Authentication

Posted: Tue Oct 09, 2007 7:39 pm
by pgoneill
I'm curious.  Is there a way to hide an entire site behind this LDAP Users module?

That is, replace FrontEndUsers with LDAP users?