LDAP Authentication

Have a question or a suggestion about a 3rd party addon module or plugin?
Let us know here.
wickhamandrew
New Member
New Member
Posts: 6
Joined: Fri Feb 16, 2007 5:26 pm

LDAP Authentication

Post 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;
	}
}

?>
Last edited by wickhamandrew on Mon Mar 05, 2007 3:04 am, edited 1 time in total.
badhoy

Re: LDAP Authentication

Post by badhoy »

very cool.  I'll have to fire up an LDAP server and give it a try.
Pierre M.

Re: LDAP Authentication

Post by Pierre M. »

Nice. LDAP, MLE and versioning : 3 steps towards international corporate sites.
Pierre M.
wickhamandrew
New Member
New Member
Posts: 6
Joined: Fri Feb 16, 2007 5:26 pm

Re: LDAP Authentication

Post by wickhamandrew »

Thanks guys. Please let me know if you have any troubles/questions.
Pierre M.

Re: LDAP Authentication

Post 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.
wickhamandrew
New Member
New Member
Posts: 6
Joined: Fri Feb 16, 2007 5:26 pm

Re: LDAP Authentication

Post 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.
User avatar
petert
Power Poster
Power Poster
Posts: 282
Joined: Wed Feb 09, 2005 9:30 pm
Location: behind my desk

Re: LDAP Authentication

Post 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 :)
Mambo sucks, that's why I am here.
Now they call it Joomla, but it still sucks!

CMSMS rules!
User avatar
fredt
Forum Members
Forum Members
Posts: 144
Joined: Mon Jun 27, 2005 10:36 am
Location: Southern France

Re: LDAP Authentication

Post 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  ;) )
User avatar
petert
Power Poster
Power Poster
Posts: 282
Joined: Wed Feb 09, 2005 9:30 pm
Location: behind my desk

Re: LDAP Authentication

Post 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....
Mambo sucks, that's why I am here.
Now they call it Joomla, but it still sucks!

CMSMS rules!
wickhamandrew
New Member
New Member
Posts: 6
Joined: Fri Feb 16, 2007 5:26 pm

Re: LDAP Authentication

Post 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.
User avatar
petert
Power Poster
Power Poster
Posts: 282
Joined: Wed Feb 09, 2005 9:30 pm
Location: behind my desk

Re: LDAP Authentication

Post 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 :)
Mambo sucks, that's why I am here.
Now they call it Joomla, but it still sucks!

CMSMS rules!
Pierre M.

Re: LDAP Authentication

Post 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.
arhodes

Re: LDAP Authentication

Post 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
Attachments

[The extension txt has been deactivated and can no longer be displayed.]

Last edited by arhodes on Wed Sep 26, 2007 6:56 pm, edited 1 time in total.
Pierre M.

Re: LDAP Authentication

Post 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.
pgoneill

Re: LDAP Authentication

Post 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?
Post Reply

Return to “Modules/Add-Ons”