This topic is locked
[SOLVED]

 LDAP authentication II - The questions

10/6/2006 6:35:38 PM
PHPRunner General questions
T
thesofa author

Hi

Here we go again.

I have 4 applications on the intranet here, all are set up to authenticate with LDAP.

All 4 use totally different ways of doing what seems to be a simple thing.

So I have to have a go at writing it for myself for my PHPR applications <img src='https://asprunner.com/forums/file.php?topicimage=1&fieldname=question&id=3551&image=1&table=forumtopics' class='bbc_emoticon' alt=';)' />

I was planning to base it on the code generated from PHPR with autherentication using a table in the database.

Sergey or others, can you please tell me what I have to return from LDAP query and feed into PHPR?

Alexey admin 10/9/2006

Hi,
all you need is to save a username in $_SESSION["UserID"] variable.

T
thesofa author 10/10/2006

well , its working, still in the testing stage tho, I may have a solution if you want it.

<img src='https://asprunner.com/forums/file.php?topicimage=1&fieldname=reply&id=11751&image=1&table=forumreplies' class='bbc_emoticon' alt=':rolleyes:' />

Sergey Kornilov admin 10/11/2006

Sure, when you have it ready feel free to share this code.
We can add this as a feature to PHPRunner or just write an article on this subject so everyone can benefit from this.

T
thesofa author 10/11/2006

OK, here we go.

LDAP authentication uses a class file called adLDAP.php from sourceforge written by Scott Barnett.

All Hail Scott Barnett.

This file is stored in the LDAP directory in the root of the project, but you can put it in the includes folder if you wish, as long as you change the path in the code section below.

this has loads of documentation in it and there is a good FAQ mentioned in the file.

Then you need to build the project in PHPRunner, selecting authenticate from a table in a database and set up all the tables so it will authenticate the users in the table.

Once you have built the project and tested it and you are happy, you need to add a section into the login.php file as follows.
before the section

if(function_exists("BeforeLogin"))

if(!BeforeLogin(@$_POST["username"],@$_POST["password"]))

$strSQL="select * from ".AddTableWrappers($cLoginTable)." where 1<0";



insert this lot

################################################################################


LDAP AUTHENTICATION MODIFICATION #

################################################################################




//include the class

include ("ldap/adLDAP.php");
//create the LDAP connection

$adldap = new adLDAP();

$ldap_auth = 0;

$ldap_group ="usergroup"; # Specified group for group authentication
// Authenticate

if (($adldap -> authenticate($strlUsername,$strlPassword))){

if ($adldap -> user_ingroup($strlUsername,$ldap_group)){ # Group Authentication Only

$ldap_auth = 1;
// Check if user exists

$sql = "Select from ".AddTableWrappers($cLoginTable)." Where ".AddFieldWrappers($cUserNameField)." = \"$strlUsername\"";

$rs = mysql_query($sql,$conn) or die("USER QUERY FAILED.");
// Update DB for new users

if (mysql_num_rows($rs) < 1) {

$info=$adldap->user_info($strlUsername,array("givenname","sn"));

$strldapfirstname = $info[0][givenname][0]; #sets firstname value from AD

$strldaplastname = $info[0][sn][0]; #sets lastname value from AD

$sql = "INSERT INTO ".AddTableWrappers($cLoginTable)." ( ".AddFieldWrappers($cUserNameField).", firstname, lastname, level)";

$sql .= " SELECT ";

$sql .= "\"$strlUsername\" AS Expr1, "; #adds username to database

$sql .= "\"$strldapfirstname\" AS Expr2, "; #adds firstname to database

$sql .= "\"$strldaplastname\" AS Expr3, "; #adds lastname to database

$sql .= "1 AS Expr4;"; #adds level to database

$result = mysql_query($sql,$conn);

}
// Generate Query

$strSQL = "select
from ".AddTableWrappers($cLoginTable)." where ".AddFieldWrappers($cUserNameField).

"=\"".$strlUsername."\"";

} #Group Authentication Only

}
// Catch failed logins

if ($ldap_auth == 0) $strSQL = "select * from ".AddTableWrappers($cLoginTable)." where ".AddFieldWrappers($cUserNameField)."=\"xxxxx\"";
################################################################################


END OF MODIFICATION #

################################################################################


The line

$ldap_group ="usergroup"; # Specified group for group authentication

holds the group for group membership checking.

the code pulls the first name and last name of the user from ad and inserts it into the table you selected for authentication, you do not need a pasword field in the user table
If you go for group authentication as well, you have 2 lines to uncomment.
All of the coding for the addition to the login.php file was written and kindly given to me by a colleague, Mark Hall, here. He has written some rather nice applications here, the code for this page came from his helpdesk application and he hacked it to work with this project of mine.
I reckon I owe him loads of beer.
and many thanks too.
All Hail Mark Hall

All Hail Mark HallAll Hail Mark Hall

All Hail Mark Hall

All Hail Mark Hall

All Hail Mark Hall

OK, its late and I am silly.

But this works.
In addition you may need to install and configure the MOd_auth_ldap module for PHP, available from Muhammad A Muquit here.

All Hail Muhammad A Muquit

All Hail Muhammad A Muquit

All Hail Muhammad A Muquit

All Hail Muhammad A Muquit

OK, I am old and tired, I shall go to bed.

Plese tell me if you have got it to work, , a line to Mark via his web site might be cool as well as any thanks to Muhammad A Muquit.

Honestly, all I did was a load of reading and then I pestered people to do it for me.

G
gdude66 10/18/2006

Great.

So where do you add the bind and the dn? Does this work with active dirctory? Willing to test.

T
thesofa author 10/18/2006

Great.

So where do you add the bind and the dn? Does this work with active dirctory? Willing to test.



Follow the link near the top of the lon post to sourceforge and download the LDAP class. There are instructions in the file and the bind is done from there when you call authenticate.
I have it working with AD on win2000 server as the root forest server!

We have around 60 staff members using the web based detention system all day, with a school of around 900 pupils.
here is the whole class from sourceforge

<?php

/*

PHP LDAP CLASS FOR MANIPULATING ACTIVE DIRECTORY

Version 1.5
Written by Scott Barnett

email: scott@wiggumworld.com

http://adldap.sourceforge.net/
Copyright © 2006 Scott Barnett
I'd appreciate any improvements or additions to be submitted back

to benefit the entire community <img src='https://asprunner.com/forums/file.php?topicimage=1&fieldname=reply&id=11936&image=1&table=forumreplies' class='bbc_emoticon' alt=':)' />
Works with both PHP 4 and PHP 5
The examples should be pretty self explanatory. If you require more

information, please visit http://adldap.sourceforge.net/
This library is free software; you can redistribute it and/or

modify it under the terms of the GNU Lesser General Public

License as published by the Free Software Foundation; either

version 2.1 of the License, or (at your option) any later version.
This library 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

Lesser General Public License for more details.
****

Something to keep in mind is that Active Directory is a permissions

based directory. If you bind as a domain user, you can't fetch as

much information on other users as you could as a domain admin.

****
FUNCTIONS:
authenticate($username,$password)

Authenticate to the directory with a specific username and password
user_info($user,$fields=NULL)

Returns an array of information for a specific user
user_groups($user,$recursive=NULL)

Returns an array of groups that a user is a member off
user_ingroup($user,$group,$recursive=NULL)

Returns true if the user is a member of the group
group_info($group)

Returns an array of information for a specific group
all_users($include_desc = false, $search = "", $sorted = true)

Returns all AD users (expensive on resources)
all_groups($include_desc = false, $search = "
", $sorted = true)

Returns all AD groups (expensive on resources)
*/
// Different type of accounts in AD

define ('ADLDAP_NORMAL_ACCOUNT', 805306368);

define ('ADLDAP_WORKSTATION_TRUST', 805306369);

define ('ADLDAP_INTERDOMAIN_TRUST', 805306370);

define ('ADLDAP_SECURITY_GLOBAL_GROUP', 268435456);

define ('ADLDAP_DISTRIBUTION_GROUP', 268435457);

define ('ADLDAP_SECURITY_LOCAL_GROUP', 536870912);

define ('ADLDAP_DISTRIBUTION_LOCAL_GROUP', 536870913);
class adLDAP {

// BEFORE YOU ASK A QUESTION, PLEASE READ THE FAQ

// http://adldap.sourceforge.net/faq.php
// You will need to edit these variables to suit your installation

var $_account_suffix="@yourdomain.com";

var $_base_dn ="DC=domain,DC=coml";
// An array of domain controllers. Specify multiple controllers if you

// would like the class to balance the LDAP queries amongst multiple servers

var $_domain_controllers = array ("server1.domain.com");
// optional account with higher privileges for searching

var $_ad_username="topnotchbloke";

var $_ad_password="putarealpasswordinhere";
// AD does not return the primary group. http://support.microsoft.com/?kbid=321360

// This tweak will resolve the real primary group, but may be resource intensive.

// Setting to false will fudge "Domain Users" and is much faster. Keep in mind though that if

// someone's primary group is NOT domain users, this is obviously going to bollocks the results

var $_real_primarygroup=true;
// When querying group memberships, do it recursively

// eg. User Fred is a member of Group A, which is a member of Group B, which is a member of Group C

// user_ingroup("Fred","C") will returns true with this option turned on, false if turned off

var $_recursive_groups=true;
// You should not need to edit anything below this line

//****

**
//other variables

var $_user_dn;

var $_user_pass;

var $_conn;

var $_bind;
// default constructor

function adLDAP(){

//connect to the LDAP server as the username/password

$this->_conn = ldap_connect($this->random_controller());

ldap_set_option($this->_conn, LDAP_OPT_PROTOCOL_VERSION, 3);

ldap_set_option($this->_conn, LDAP_OPT_REFERRALS, 0); //disable plain text passwords

return true;

}
// default destructor

function __destruct(){ ldap_close ($this->_conn); }
function random_controller(){

//select a random domain controller

mt_srand(doubleval(microtime()) 100000000);

return ($this->_domain_controllers[array_rand($this->_domain_controllers)]);

}
// authenticate($username,$password)

// Authenticate to the directory with a specific username and password

// Extremely useful for validating login credentials

function authenticate($username,$password){

//validate a users login credentials

$returnval=false;
if ($username!=NULL && $password!=NULL){ //prevent null bind

$this->_user_dn=$username.$this->_account_suffix;

$this->_user_pass=$password;
$this->_bind = @ldap_bind($this->_conn,$this->_user_dn,$this->_user_pass);

if ($this->_bind){ $returnval=true; }

}

return ($returnval);

}
// rebind()

// Binds to the directory with the default search username and password

// specified above.

function rebind(){

//connect with another account to search with if necessary

$ad_dn=$this->_ad_username.$this->_account_suffix;

$this->_bind = @ldap_bind($this->_conn,$ad_dn,$this->_ad_password);

if ($this->_bind){ return (true); }

return (false);

}
// user_info($user,$fields)

// Returns an array of information for a specific user

function user_info($user,$fields=NULL){

if ($user!=NULL){

if ($this->_ad_username!=NULL){ $this->rebind(); } //bind as a another account if necessary
if ($this->_bind){ //perform the search and grab all their details

$filter="samaccountname=".$user;

if ($fields==NULL){

$fields=array("samaccountname","mail","memberof","department","displayname","telephonenumber","primarygroupid");

//$fields=array("
");

}

$sr=ldap_search($this->_conn,$this->_base_dn,$filter,$fields);

$entries = ldap_get_entries($this->_conn, $sr);
// AD does not return the primary group in the ldap query, we may need to fudge it

if ($this->_real_primarygroup){

$entries[0]["memberof"][]=$this->group_cn($entries[0]["primarygroupid"][0]);

} else {

$entries[0]["memberof"][]="CN=Domain Users,CN=Users,".$this->_base_dn;

}
//echo ("<pre>"); print_r($entries);
$entries[0]["memberof"]["count"]++;

return ($entries);

}

}
return (false);

}
// user_groups($user)

// Returns an array of groups that a user is a member off

function user_groups($user,$recursive=NULL){

if ($this->_ad_username!=NULL){ $this->rebind(); } //bind as a another account if necessary

if ($recursive==NULL){ $recursive=$this->_recursive_groups; }
if ($this->_bind){

//search the directory for their information

$info=@$this->user_info($user,array("memberof"));

//echo ("<pre>"); print_r($info);

$groups=$info[0]["memberof"]; //presuming the entry returned is our guy (unique usernames)
$group_array=$this->nice_names($groups);
if ($recursive){

foreach ($group_array as $id => $group_name){

$extra_groups=$this->recursive_groups($group_name);

$group_array=array_merge($group_array,$extra_groups);

}

}
return ($group_array);

}

return (false);

}
// user_ingroup($user,$group)

// Returns true if the user is a member of the group

function user_ingroup($user,$group,$recursive=NULL){

if ($recursive==NULL){ $recursive=$this->_recursive_groups; }
if (($user!=NULL) && ($group!=NULL)){

if ($this->_ad_username!=NULL){ $this->rebind(); } //bind as a another account if necessary
if ($this->_bind){

$groups=$this->user_groups($user,array("memberof"),$recursive);

if (in_array($group,$groups)){ return (true); }

}

}

return (false);

}
function recursive_groups($group){

$ret_groups=array();
$groups=$this->group_info($group,array("memberof"));

$groups=$groups[0]["memberof"];
if ($groups){

$group_names=$this->nice_names($groups);

$ret_groups=array_merge($ret_groups,$group_names); //final groups to return
foreach ($group_names as $id => $group_name){

$child_groups=$this->recursive_groups($group_name);

$ret_groups=array_merge($ret_groups,$child_groups);

}
}
return ($ret_groups);

}
// take an ldap query and return the nice names, without all the LDAP prefixes (eg. CN, DN)

function nice_names($groups){
$group_array=array();

for ($i=0; $i<$groups["count"]; $i++){ //for each group

$line=$groups[$i];
if (strlen($line)>0){

//more presumptions, they're all prefixed with CN=

//so we ditch the first three characters and the group

//name goes up to the first comma

$bits=explode(",",$line);

$group_array[]=substr($bits[0],3,(strlen($bits[0])-3));

}

}

return ($group_array);

}
function group_cn($gid){

if ($this->_ad_username!=NULL){ $this->rebind(); } //bind as a another account if necessary
// coping with AD not returning the primary group

// http://support.microsoft.com/?kbid=321360

// for some reason it's not possible to search on primarygrouptoken=XXX

// if someone can show otherwise, I'd like to know about it <img src='https://asprunner.com/forums/file.php?topicimage=1&fieldname=reply&id=11936&image=2&table=forumreplies' class='bbc_emoticon' alt=':)' />

// this way is resource intensive and generally a pain in the @#%^
$r=false;
if ($this->_bind){

$filter="(&(objectCategory=group)(samaccounttype=". ADLDAP_SECURITY_GLOBAL_GROUP ."))";

$fields=array("primarygrouptoken","samaccountname","distinguishedname");

$sr=ldap_search($this->_conn,$this->_base_dn,$filter,$fields);

$entries = ldap_get_entries($this->_conn, $sr);
for ($i=0; $i<$entries["count"]; $i++){

if ($entries[$i]["primarygrouptoken"][0]==$gid){

$r=$entries[$i]["distinguishedname"][0];

$i=$entries["count"];

}

}

}

return ($r);

}
// group_info($group_name,$fields=NULL)

// Returns an array of information for a specified group

function group_info($group_name,$fields=NULL){

if ($this->_ad_username!=NULL){ $this->rebind(); } //bind as a another account if necessary
if ($this->_bind){

//escape brackets

$group_name=str_replace("(","\(",$group_name);

$group_name=str_replace(")","\)",$group_name);
$filter="(&(objectCategory=group)(name=".$group_name."))";

//echo ($filter."
");

if ($fields==NULL){ $fields=array("member","memberof","cn","description","distinguishedname","objectcategory","samaccountname"); }

$sr=ldap_search($this->_conn,$this->_base_dn,$filter,$fields);

$entries = ldap_get_entries($this->_conn, $sr);

//print_r($entries);

return ($entries);

}

return (false);

}
function all_users($include_desc = false, $search = "", $sorted = true){

// Returns all AD users

if ($this->_ad_username!=NULL){ $this->rebind(); } //bind as a another account if necessary
if ($this->_bind){

$users_array = array();
//perform the search and grab all their details

$filter = "(&(objectClass=user)(samaccounttype=". ADLDAP_NORMAL_ACCOUNT .")(objectCategory=person)(cn=$search))";

$fields=array("samaccountname","displayname");

$sr=ldap_search($this->_conn,$this->_base_dn,$filter,$fields);

$entries = ldap_get_entries($this->_conn, $sr);
for ($i=0; $i<$entries["count"]; $i++){

if( $include_desc && strlen($entries[$i]["displayname"][0]) > 0 )

$users_array[ $entries[$i]["samaccountname"][0] ] = $entries[$i]["displayname"][0];

else if( $include_desc )

$users_array[ $entries[$i]["samaccountname"][0] ] = $entries[$i]["samaccountname"][0];

else

array_push($users_array, $entries[$i]["samaccountname"][0]);

}

if( $sorted ){ asort($users_array); }

return ($users_array);

}

return (false);

}
function all_groups($include_desc = false, $search = "
", $sorted = true){

// Returns all AD groups

if ($this->_ad_username!=NULL){ $this->rebind(); } //bind as a another account if necessary
if ($this->_bind){

$groups_array = array();
//perform the search and grab all their details

$filter = "(&(objectCategory=group)(samaccounttype=". ADLDAP_SECURITY_GLOBAL_GROUP .")(cn=$search))";

$fields=array("samaccountname","description");

$sr=ldap_search($this->_conn,$this->_base_dn,$filter,$fields);

$entries = ldap_get_entries($this->_conn, $sr);
for ($i=0; $i<$entries["count"]; $i++){

if( $include_desc && strlen($entries[$i]["description"][0]) > 0 )

$groups_array[ $entries[$i]["samaccountname"][0] ] = $entries[$i]["description"][0];

else if( $include_desc )

$groups_array[ $entries[$i]["samaccountname"][0] ] = $entries[$i]["samaccountname"][0];

else
array_push($groups_array, $entries[$i]["samaccountname"][0]);

}

if( $sorted ){ asort($groups_array); }

return ($groups_array);

}

return (false);

}

} // End class
?>


I must point out that this is not my code, it is a direct blag from sourceforge and I must say

All Hail Scott Barnett.

All Hail Scott Barnett.

All Hail Scott Barnett.

All Hail Scott Barnett..

HTH and have fun

G
gdude66 10/20/2006

Many, many thanks. Great job.
BTW I work in a school as well.

Have you built a project for reporting behaviour / conduct problems? This is what I am working on now.

G
gdude66 11/3/2006

Update to LDAP
Thanks Sofa for the support and work on this. There needs to be a change to the login.php file as follows
Add two lines of code to the login.php file from

// username and password are stored in the database

$conn=db_connect();

$strUsername = (string)@$_POST["username"];

$strPassword = (string)@$_POST["password"];



to this

// username and password are stored in the database

$conn=db_connect();

$strUsername = (string)@$_POST["username"];

$strPassword = (string)@$_POST["password"];
[color=#CC0000]$strlPassword = $_POST["password"];



as well as making the other changes in Sofas post.

Works well and is very quick.

Great work again Sofa

T
thesofa author 11/3/2006

Thanks for pointing out my omission, I hope no-one has given up with LDAP due to my laxness?

<img src='https://asprunner.com/forums/file.php?topicimage=1&fieldname=reply&id=12492&image=1&table=forumreplies' class='bbc_emoticon' alt=':D' />