176ce1169SAndreas Gohr<?php 276ce1169SAndreas Gohr/** 376ce1169SAndreas Gohr * PHP LDAP CLASS FOR MANIPULATING ACTIVE DIRECTORY 476ce1169SAndreas Gohr * Version 4.0.4 576ce1169SAndreas Gohr * 676ce1169SAndreas Gohr * PHP Version 5 with SSL and LDAP support 776ce1169SAndreas Gohr * 876ce1169SAndreas Gohr * Written by Scott Barnett, Richard Hyland 976ce1169SAndreas Gohr * email: scott@wiggumworld.com, adldap@richardhyland.com 1076ce1169SAndreas Gohr * http://adldap.sourceforge.net/ 1176ce1169SAndreas Gohr * 1276ce1169SAndreas Gohr * Copyright (c) 2006-2012 Scott Barnett, Richard Hyland 1376ce1169SAndreas Gohr * 1476ce1169SAndreas Gohr * We'd appreciate any improvements or additions to be submitted back 1576ce1169SAndreas Gohr * to benefit the entire community :) 1676ce1169SAndreas Gohr * 1776ce1169SAndreas Gohr * This library is free software; you can redistribute it and/or 1876ce1169SAndreas Gohr * modify it under the terms of the GNU Lesser General Public 1976ce1169SAndreas Gohr * License as published by the Free Software Foundation; either 2076ce1169SAndreas Gohr * version 2.1 of the License. 2176ce1169SAndreas Gohr * 2276ce1169SAndreas Gohr * This library is distributed in the hope that it will be useful, 2376ce1169SAndreas Gohr * but WITHOUT ANY WARRANTY; without even the implied warranty of 2476ce1169SAndreas Gohr * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 2576ce1169SAndreas Gohr * Lesser General Public License for more details. 2676ce1169SAndreas Gohr * 2776ce1169SAndreas Gohr * @category ToolsAndUtilities 2876ce1169SAndreas Gohr * @package adLDAP 2976ce1169SAndreas Gohr * @subpackage User 3076ce1169SAndreas Gohr * @author Scott Barnett, Richard Hyland 3176ce1169SAndreas Gohr * @copyright (c) 2006-2012 Scott Barnett, Richard Hyland 3276ce1169SAndreas Gohr * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html LGPLv2.1 3376ce1169SAndreas Gohr * @revision $Revision: 97 $ 3476ce1169SAndreas Gohr * @version 4.0.4 3576ce1169SAndreas Gohr * @link http://adldap.sourceforge.net/ 3676ce1169SAndreas Gohr */ 3776ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/../adLDAP.php'); 3876ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/../collections/adLDAPUserCollection.php'); 3976ce1169SAndreas Gohr 400489c64bSMoisés Braga Ribeirouse dokuwiki\Utf8\Sort; 410489c64bSMoisés Braga Ribeiro 4276ce1169SAndreas Gohr/** 4376ce1169SAndreas Gohr* USER FUNCTIONS 4476ce1169SAndreas Gohr*/ 4576ce1169SAndreas Gohrclass adLDAPUsers { 4676ce1169SAndreas Gohr /** 4776ce1169SAndreas Gohr * The current adLDAP connection via dependency injection 4876ce1169SAndreas Gohr * 4976ce1169SAndreas Gohr * @var adLDAP 5076ce1169SAndreas Gohr */ 5176ce1169SAndreas Gohr protected $adldap; 5276ce1169SAndreas Gohr 5376ce1169SAndreas Gohr public function __construct(adLDAP $adldap) { 5476ce1169SAndreas Gohr $this->adldap = $adldap; 5576ce1169SAndreas Gohr } 5676ce1169SAndreas Gohr 5776ce1169SAndreas Gohr /** 5876ce1169SAndreas Gohr * Validate a user's login credentials 5976ce1169SAndreas Gohr * 6076ce1169SAndreas Gohr * @param string $username A user's AD username 6176ce1169SAndreas Gohr * @param string $password A user's AD password 6276ce1169SAndreas Gohr * @param bool optional $prevent_rebind 6376ce1169SAndreas Gohr * @return bool 6476ce1169SAndreas Gohr */ 6576ce1169SAndreas Gohr public function authenticate($username, $password, $preventRebind = false) { 6676ce1169SAndreas Gohr return $this->adldap->authenticate($username, $password, $preventRebind); 6776ce1169SAndreas Gohr } 6876ce1169SAndreas Gohr 6976ce1169SAndreas Gohr /** 7076ce1169SAndreas Gohr * Create a user 7176ce1169SAndreas Gohr * 7276ce1169SAndreas Gohr * If you specify a password here, this can only be performed over SSL 7376ce1169SAndreas Gohr * 7476ce1169SAndreas Gohr * @param array $attributes The attributes to set to the user account 7576ce1169SAndreas Gohr * @return bool 7676ce1169SAndreas Gohr */ 7776ce1169SAndreas Gohr public function create($attributes) 7876ce1169SAndreas Gohr { 7976ce1169SAndreas Gohr // Check for compulsory fields 8076ce1169SAndreas Gohr if (!array_key_exists("username", $attributes)){ return "Missing compulsory field [username]"; } 8176ce1169SAndreas Gohr if (!array_key_exists("firstname", $attributes)){ return "Missing compulsory field [firstname]"; } 8276ce1169SAndreas Gohr if (!array_key_exists("surname", $attributes)){ return "Missing compulsory field [surname]"; } 8376ce1169SAndreas Gohr if (!array_key_exists("email", $attributes)){ return "Missing compulsory field [email]"; } 8476ce1169SAndreas Gohr if (!array_key_exists("container", $attributes)){ return "Missing compulsory field [container]"; } 8576ce1169SAndreas Gohr if (!is_array($attributes["container"])){ return "Container attribute must be an array."; } 8676ce1169SAndreas Gohr 8776ce1169SAndreas Gohr if (array_key_exists("password",$attributes) && (!$this->adldap->getUseSSL() && !$this->adldap->getUseTLS())){ 8876ce1169SAndreas Gohr throw new adLDAPException('SSL must be configured on your webserver and enabled in the class to set passwords.'); 8976ce1169SAndreas Gohr } 9076ce1169SAndreas Gohr 9176ce1169SAndreas Gohr if (!array_key_exists("display_name", $attributes)) { 9276ce1169SAndreas Gohr $attributes["display_name"] = $attributes["firstname"] . " " . $attributes["surname"]; 9376ce1169SAndreas Gohr } 9476ce1169SAndreas Gohr 9576ce1169SAndreas Gohr // Translate the schema 9676ce1169SAndreas Gohr $add = $this->adldap->adldap_schema($attributes); 9776ce1169SAndreas Gohr 9876ce1169SAndreas Gohr // Additional stuff only used for adding accounts 9976ce1169SAndreas Gohr $add["cn"][0] = $attributes["display_name"]; 10076ce1169SAndreas Gohr $add["samaccountname"][0] = $attributes["username"]; 10176ce1169SAndreas Gohr $add["objectclass"][0] = "top"; 10276ce1169SAndreas Gohr $add["objectclass"][1] = "person"; 10376ce1169SAndreas Gohr $add["objectclass"][2] = "organizationalPerson"; 10476ce1169SAndreas Gohr $add["objectclass"][3] = "user"; //person? 10576ce1169SAndreas Gohr //$add["name"][0]=$attributes["firstname"]." ".$attributes["surname"]; 10676ce1169SAndreas Gohr 10776ce1169SAndreas Gohr // Set the account control attribute 10876ce1169SAndreas Gohr $control_options = array("NORMAL_ACCOUNT"); 10976ce1169SAndreas Gohr if (!$attributes["enabled"]) { 11076ce1169SAndreas Gohr $control_options[] = "ACCOUNTDISABLE"; 11176ce1169SAndreas Gohr } 11276ce1169SAndreas Gohr $add["userAccountControl"][0] = $this->accountControl($control_options); 11376ce1169SAndreas Gohr 11476ce1169SAndreas Gohr // Determine the container 11576ce1169SAndreas Gohr $attributes["container"] = array_reverse($attributes["container"]); 11676ce1169SAndreas Gohr $container = "OU=" . implode(", OU=",$attributes["container"]); 11776ce1169SAndreas Gohr 11876ce1169SAndreas Gohr // Add the entry 11976ce1169SAndreas Gohr $result = @ldap_add($this->adldap->getLdapConnection(), "CN=" . $add["cn"][0] . ", " . $container . "," . $this->adldap->getBaseDn(), $add); 12076ce1169SAndreas Gohr if ($result != true) { 12176ce1169SAndreas Gohr return false; 12276ce1169SAndreas Gohr } 12376ce1169SAndreas Gohr 12476ce1169SAndreas Gohr return true; 12576ce1169SAndreas Gohr } 12676ce1169SAndreas Gohr 12776ce1169SAndreas Gohr /** 12876ce1169SAndreas Gohr * Account control options 12976ce1169SAndreas Gohr * 13076ce1169SAndreas Gohr * @param array $options The options to convert to int 13176ce1169SAndreas Gohr * @return int 13276ce1169SAndreas Gohr */ 13376ce1169SAndreas Gohr protected function accountControl($options) 13476ce1169SAndreas Gohr { 13576ce1169SAndreas Gohr $val=0; 13676ce1169SAndreas Gohr 13776ce1169SAndreas Gohr if (is_array($options)) { 13876ce1169SAndreas Gohr if (in_array("SCRIPT",$options)){ $val=$val+1; } 13976ce1169SAndreas Gohr if (in_array("ACCOUNTDISABLE",$options)){ $val=$val+2; } 14076ce1169SAndreas Gohr if (in_array("HOMEDIR_REQUIRED",$options)){ $val=$val+8; } 14176ce1169SAndreas Gohr if (in_array("LOCKOUT",$options)){ $val=$val+16; } 14276ce1169SAndreas Gohr if (in_array("PASSWD_NOTREQD",$options)){ $val=$val+32; } 14376ce1169SAndreas Gohr //PASSWD_CANT_CHANGE Note You cannot assign this permission by directly modifying the UserAccountControl attribute. 14476ce1169SAndreas Gohr //For information about how to set the permission programmatically, see the "Property flag descriptions" section. 14576ce1169SAndreas Gohr if (in_array("ENCRYPTED_TEXT_PWD_ALLOWED",$options)){ $val=$val+128; } 14676ce1169SAndreas Gohr if (in_array("TEMP_DUPLICATE_ACCOUNT",$options)){ $val=$val+256; } 14776ce1169SAndreas Gohr if (in_array("NORMAL_ACCOUNT",$options)){ $val=$val+512; } 14876ce1169SAndreas Gohr if (in_array("INTERDOMAIN_TRUST_ACCOUNT",$options)){ $val=$val+2048; } 14976ce1169SAndreas Gohr if (in_array("WORKSTATION_TRUST_ACCOUNT",$options)){ $val=$val+4096; } 15076ce1169SAndreas Gohr if (in_array("SERVER_TRUST_ACCOUNT",$options)){ $val=$val+8192; } 15176ce1169SAndreas Gohr if (in_array("DONT_EXPIRE_PASSWORD",$options)){ $val=$val+65536; } 15276ce1169SAndreas Gohr if (in_array("MNS_LOGON_ACCOUNT",$options)){ $val=$val+131072; } 15376ce1169SAndreas Gohr if (in_array("SMARTCARD_REQUIRED",$options)){ $val=$val+262144; } 15476ce1169SAndreas Gohr if (in_array("TRUSTED_FOR_DELEGATION",$options)){ $val=$val+524288; } 15576ce1169SAndreas Gohr if (in_array("NOT_DELEGATED",$options)){ $val=$val+1048576; } 15676ce1169SAndreas Gohr if (in_array("USE_DES_KEY_ONLY",$options)){ $val=$val+2097152; } 15776ce1169SAndreas Gohr if (in_array("DONT_REQ_PREAUTH",$options)){ $val=$val+4194304; } 15876ce1169SAndreas Gohr if (in_array("PASSWORD_EXPIRED",$options)){ $val=$val+8388608; } 15976ce1169SAndreas Gohr if (in_array("TRUSTED_TO_AUTH_FOR_DELEGATION",$options)){ $val=$val+16777216; } 16076ce1169SAndreas Gohr } 16176ce1169SAndreas Gohr return $val; 16276ce1169SAndreas Gohr } 16376ce1169SAndreas Gohr 16476ce1169SAndreas Gohr /** 16576ce1169SAndreas Gohr * Delete a user account 16676ce1169SAndreas Gohr * 16776ce1169SAndreas Gohr * @param string $username The username to delete (please be careful here!) 16876ce1169SAndreas Gohr * @param bool $isGUID Is the username a GUID or a samAccountName 16976ce1169SAndreas Gohr * @return array 17076ce1169SAndreas Gohr */ 17176ce1169SAndreas Gohr public function delete($username, $isGUID = false) 17276ce1169SAndreas Gohr { 17376ce1169SAndreas Gohr $userinfo = $this->info($username, array("*"), $isGUID); 17476ce1169SAndreas Gohr $dn = $userinfo[0]['distinguishedname'][0]; 17576ce1169SAndreas Gohr $result = $this->adldap->folder()->delete($dn); 17676ce1169SAndreas Gohr if ($result != true) { 17776ce1169SAndreas Gohr return false; 17876ce1169SAndreas Gohr } 17976ce1169SAndreas Gohr return true; 18076ce1169SAndreas Gohr } 18176ce1169SAndreas Gohr 18276ce1169SAndreas Gohr /** 18376ce1169SAndreas Gohr * Groups the user is a member of 18476ce1169SAndreas Gohr * 18576ce1169SAndreas Gohr * @param string $username The username to query 18676ce1169SAndreas Gohr * @param bool $recursive Recursive list of groups 18776ce1169SAndreas Gohr * @param bool $isGUID Is the username passed a GUID or a samAccountName 18876ce1169SAndreas Gohr * @return array 18976ce1169SAndreas Gohr */ 19076ce1169SAndreas Gohr public function groups($username, $recursive = NULL, $isGUID = false) 19176ce1169SAndreas Gohr { 19276ce1169SAndreas Gohr if ($username === NULL) { return false; } 19376ce1169SAndreas Gohr if ($recursive === NULL) { $recursive = $this->adldap->getRecursiveGroups(); } // Use the default option if they haven't set it 19476ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()) { return false; } 19576ce1169SAndreas Gohr 19676ce1169SAndreas Gohr // Search the directory for their information 19776ce1169SAndreas Gohr $info = @$this->info($username, array("memberof", "primarygroupid"), $isGUID); 19876ce1169SAndreas Gohr $groups = $this->adldap->utilities()->niceNames($info[0]["memberof"]); // Presuming the entry returned is our guy (unique usernames) 19976ce1169SAndreas Gohr 20076ce1169SAndreas Gohr if ($recursive === true){ 20176ce1169SAndreas Gohr foreach ($groups as $id => $groupName){ 20276ce1169SAndreas Gohr $extraGroups = $this->adldap->group()->recursiveGroups($groupName); 20376ce1169SAndreas Gohr $groups = array_merge($groups, $extraGroups); 20476ce1169SAndreas Gohr } 20576ce1169SAndreas Gohr } 20676ce1169SAndreas Gohr 20776ce1169SAndreas Gohr return $groups; 20876ce1169SAndreas Gohr } 20976ce1169SAndreas Gohr 21076ce1169SAndreas Gohr /** 21176ce1169SAndreas Gohr * Find information about the users. Returned in a raw array format from AD 21276ce1169SAndreas Gohr * 21376ce1169SAndreas Gohr * @param string $username The username to query 21476ce1169SAndreas Gohr * @param array $fields Array of parameters to query 21576ce1169SAndreas Gohr * @param bool $isGUID Is the username passed a GUID or a samAccountName 21676ce1169SAndreas Gohr * @return array 21776ce1169SAndreas Gohr */ 21876ce1169SAndreas Gohr public function info($username, $fields = NULL, $isGUID = false) 21976ce1169SAndreas Gohr { 22076ce1169SAndreas Gohr if ($username === NULL) { return false; } 22176ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()) { return false; } 22276ce1169SAndreas Gohr 22376ce1169SAndreas Gohr if ($isGUID === true) { 22476ce1169SAndreas Gohr $username = $this->adldap->utilities()->strGuidToHex($username); 22576ce1169SAndreas Gohr $filter = "objectguid=" . $username; 22676ce1169SAndreas Gohr } 22776ce1169SAndreas Gohr else if (strstr($username, "@")) { 22876ce1169SAndreas Gohr $filter = "userPrincipalName=" . $username; 22976ce1169SAndreas Gohr } 23076ce1169SAndreas Gohr else { 23176ce1169SAndreas Gohr $filter = "samaccountname=" . $username; 23276ce1169SAndreas Gohr } 23376ce1169SAndreas Gohr $filter = "(&(objectCategory=person)({$filter}))"; 23476ce1169SAndreas Gohr if ($fields === NULL) { 23576ce1169SAndreas Gohr $fields = array("samaccountname","mail","memberof","department","displayname","telephonenumber","primarygroupid","objectsid"); 23676ce1169SAndreas Gohr } 23776ce1169SAndreas Gohr if (!in_array("objectsid", $fields)) { 23876ce1169SAndreas Gohr $fields[] = "objectsid"; 23976ce1169SAndreas Gohr } 24076ce1169SAndreas Gohr $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields); 24176ce1169SAndreas Gohr $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr); 24276ce1169SAndreas Gohr 24376ce1169SAndreas Gohr if (isset($entries[0])) { 24476ce1169SAndreas Gohr if ($entries[0]['count'] >= 1) { 24576ce1169SAndreas Gohr if (in_array("memberof", $fields)) { 24676ce1169SAndreas Gohr // AD does not return the primary group in the ldap query, we may need to fudge it 24776ce1169SAndreas Gohr if ($this->adldap->getRealPrimaryGroup() && isset($entries[0]["primarygroupid"][0]) && isset($entries[0]["objectsid"][0])){ 24876ce1169SAndreas Gohr //$entries[0]["memberof"][]=$this->group_cn($entries[0]["primarygroupid"][0]); 24976ce1169SAndreas Gohr $entries[0]["memberof"][] = $this->adldap->group()->getPrimaryGroup($entries[0]["primarygroupid"][0], $entries[0]["objectsid"][0]); 25076ce1169SAndreas Gohr } else { 25176ce1169SAndreas Gohr $entries[0]["memberof"][] = "CN=Domain Users,CN=Users," . $this->adldap->getBaseDn(); 25276ce1169SAndreas Gohr } 25376ce1169SAndreas Gohr if (!isset($entries[0]["memberof"]["count"])) { 25476ce1169SAndreas Gohr $entries[0]["memberof"]["count"] = 0; 25576ce1169SAndreas Gohr } 25676ce1169SAndreas Gohr $entries[0]["memberof"]["count"]++; 25776ce1169SAndreas Gohr } 25876ce1169SAndreas Gohr } 25976ce1169SAndreas Gohr 26076ce1169SAndreas Gohr return $entries; 26176ce1169SAndreas Gohr } 26276ce1169SAndreas Gohr return false; 26376ce1169SAndreas Gohr } 26476ce1169SAndreas Gohr 26576ce1169SAndreas Gohr /** 26676ce1169SAndreas Gohr * Find information about the users. Returned in a raw array format from AD 26776ce1169SAndreas Gohr * 26876ce1169SAndreas Gohr * @param string $username The username to query 26976ce1169SAndreas Gohr * @param array $fields Array of parameters to query 27076ce1169SAndreas Gohr * @param bool $isGUID Is the username passed a GUID or a samAccountName 27176ce1169SAndreas Gohr * @return mixed 27276ce1169SAndreas Gohr */ 27376ce1169SAndreas Gohr public function infoCollection($username, $fields = NULL, $isGUID = false) 27476ce1169SAndreas Gohr { 27576ce1169SAndreas Gohr if ($username === NULL) { return false; } 27676ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()) { return false; } 27776ce1169SAndreas Gohr 27876ce1169SAndreas Gohr $info = $this->info($username, $fields, $isGUID); 27976ce1169SAndreas Gohr 28076ce1169SAndreas Gohr if ($info !== false) { 28176ce1169SAndreas Gohr $collection = new adLDAPUserCollection($info, $this->adldap); 28276ce1169SAndreas Gohr return $collection; 28376ce1169SAndreas Gohr } 28476ce1169SAndreas Gohr return false; 28576ce1169SAndreas Gohr } 28676ce1169SAndreas Gohr 28776ce1169SAndreas Gohr /** 28876ce1169SAndreas Gohr * Determine if a user is in a specific group 28976ce1169SAndreas Gohr * 29076ce1169SAndreas Gohr * @param string $username The username to query 29176ce1169SAndreas Gohr * @param string $group The name of the group to check against 29276ce1169SAndreas Gohr * @param bool $recursive Check groups recursively 29376ce1169SAndreas Gohr * @param bool $isGUID Is the username passed a GUID or a samAccountName 29476ce1169SAndreas Gohr * @return bool 29576ce1169SAndreas Gohr */ 29676ce1169SAndreas Gohr public function inGroup($username, $group, $recursive = NULL, $isGUID = false) 29776ce1169SAndreas Gohr { 29876ce1169SAndreas Gohr if ($username === NULL) { return false; } 29976ce1169SAndreas Gohr if ($group === NULL) { return false; } 30076ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()) { return false; } 30176ce1169SAndreas Gohr if ($recursive === NULL) { $recursive = $this->adldap->getRecursiveGroups(); } // Use the default option if they haven't set it 30276ce1169SAndreas Gohr 30376ce1169SAndreas Gohr // Get a list of the groups 30476ce1169SAndreas Gohr $groups = $this->groups($username, $recursive, $isGUID); 30576ce1169SAndreas Gohr 30676ce1169SAndreas Gohr // Return true if the specified group is in the group list 30776ce1169SAndreas Gohr if (in_array($group, $groups)) { 30876ce1169SAndreas Gohr return true; 30976ce1169SAndreas Gohr } 31076ce1169SAndreas Gohr 31176ce1169SAndreas Gohr return false; 31276ce1169SAndreas Gohr } 31376ce1169SAndreas Gohr 31476ce1169SAndreas Gohr /** 31576ce1169SAndreas Gohr * Determine a user's password expiry date 31676ce1169SAndreas Gohr * 31776ce1169SAndreas Gohr * @param string $username The username to query 31876ce1169SAndreas Gohr * @param book $isGUID Is the username passed a GUID or a samAccountName 31959752844SAnders Sandblad * @requires bcmath http://php.net/manual/en/book.bc.php 32076ce1169SAndreas Gohr * @return array 32176ce1169SAndreas Gohr */ 32276ce1169SAndreas Gohr public function passwordExpiry($username, $isGUID = false) 32376ce1169SAndreas Gohr { 32476ce1169SAndreas Gohr if ($username === NULL) { return "Missing compulsory field [username]"; } 32576ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()) { return false; } 32659752844SAnders Sandblad if (!function_exists('bcmod')) { throw new adLDAPException("Missing function support [bcmod] http://php.net/manual/en/book.bc.php"); }; 32776ce1169SAndreas Gohr 32876ce1169SAndreas Gohr $userInfo = $this->info($username, array("pwdlastset", "useraccountcontrol"), $isGUID); 32976ce1169SAndreas Gohr $pwdLastSet = $userInfo[0]['pwdlastset'][0]; 33076ce1169SAndreas Gohr $status = array(); 33176ce1169SAndreas Gohr 33276ce1169SAndreas Gohr if ($userInfo[0]['useraccountcontrol'][0] == '66048') { 33376ce1169SAndreas Gohr // Password does not expire 33476ce1169SAndreas Gohr return "Does not expire"; 33576ce1169SAndreas Gohr } 33676ce1169SAndreas Gohr if ($pwdLastSet === '0') { 33776ce1169SAndreas Gohr // Password has already expired 33876ce1169SAndreas Gohr return "Password has expired"; 33976ce1169SAndreas Gohr } 34076ce1169SAndreas Gohr 34176ce1169SAndreas Gohr // Password expiry in AD can be calculated from TWO values: 34276ce1169SAndreas Gohr // - User's own pwdLastSet attribute: stores the last time the password was changed 34376ce1169SAndreas Gohr // - Domain's maxPwdAge attribute: how long passwords last in the domain 34476ce1169SAndreas Gohr // 34576ce1169SAndreas Gohr // Although Microsoft chose to use a different base and unit for time measurements. 34676ce1169SAndreas Gohr // This function will convert them to Unix timestamps 34776ce1169SAndreas Gohr $sr = ldap_read($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), 'objectclass=*', array('maxPwdAge')); 34876ce1169SAndreas Gohr if (!$sr) { 34976ce1169SAndreas Gohr return false; 35076ce1169SAndreas Gohr } 35176ce1169SAndreas Gohr $info = ldap_get_entries($this->adldap->getLdapConnection(), $sr); 35276ce1169SAndreas Gohr $maxPwdAge = $info[0]['maxpwdage'][0]; 35376ce1169SAndreas Gohr 35476ce1169SAndreas Gohr 35576ce1169SAndreas Gohr // See MSDN: http://msdn.microsoft.com/en-us/library/ms974598.aspx 35676ce1169SAndreas Gohr // 35776ce1169SAndreas Gohr // pwdLastSet contains the number of 100 nanosecond intervals since January 1, 1601 (UTC), 35876ce1169SAndreas Gohr // stored in a 64 bit integer. 35976ce1169SAndreas Gohr // 36076ce1169SAndreas Gohr // The number of seconds between this date and Unix epoch is 11644473600. 36176ce1169SAndreas Gohr // 36276ce1169SAndreas Gohr // maxPwdAge is stored as a large integer that represents the number of 100 nanosecond 36376ce1169SAndreas Gohr // intervals from the time the password was set before the password expires. 36476ce1169SAndreas Gohr // 36576ce1169SAndreas Gohr // We also need to scale this to seconds but also this value is a _negative_ quantity! 36676ce1169SAndreas Gohr // 36776ce1169SAndreas Gohr // If the low 32 bits of maxPwdAge are equal to 0 passwords do not expire 36876ce1169SAndreas Gohr // 36976ce1169SAndreas Gohr // Unfortunately the maths involved are too big for PHP integers, so I've had to require 37076ce1169SAndreas Gohr // BCMath functions to work with arbitrary precision numbers. 37176ce1169SAndreas Gohr if (bcmod($maxPwdAge, 4294967296) === '0') { 37276ce1169SAndreas Gohr return "Domain does not expire passwords"; 37376ce1169SAndreas Gohr } 37476ce1169SAndreas Gohr 37576ce1169SAndreas Gohr // Add maxpwdage and pwdlastset and we get password expiration time in Microsoft's 37676ce1169SAndreas Gohr // time units. Because maxpwd age is negative we need to subtract it. 37776ce1169SAndreas Gohr $pwdExpire = bcsub($pwdLastSet, $maxPwdAge); 37876ce1169SAndreas Gohr 37976ce1169SAndreas Gohr // Convert MS's time to Unix time 38076ce1169SAndreas Gohr $status['expiryts'] = bcsub(bcdiv($pwdExpire, '10000000'), '11644473600'); 38176ce1169SAndreas Gohr $status['expiryformat'] = date('Y-m-d H:i:s', bcsub(bcdiv($pwdExpire, '10000000'), '11644473600')); 38276ce1169SAndreas Gohr 38376ce1169SAndreas Gohr return $status; 38476ce1169SAndreas Gohr } 38576ce1169SAndreas Gohr 38676ce1169SAndreas Gohr /** 38776ce1169SAndreas Gohr * Modify a user 38876ce1169SAndreas Gohr * 38976ce1169SAndreas Gohr * @param string $username The username to query 39076ce1169SAndreas Gohr * @param array $attributes The attributes to modify. Note if you set the enabled attribute you must not specify any other attributes 39176ce1169SAndreas Gohr * @param bool $isGUID Is the username passed a GUID or a samAccountName 39276ce1169SAndreas Gohr * @return bool 39376ce1169SAndreas Gohr */ 39476ce1169SAndreas Gohr public function modify($username, $attributes, $isGUID = false) 39576ce1169SAndreas Gohr { 39676ce1169SAndreas Gohr if ($username === NULL) { return "Missing compulsory field [username]"; } 39776ce1169SAndreas Gohr if (array_key_exists("password", $attributes) && !$this->adldap->getUseSSL() && !$this->adldap->getUseTLS()) { 39876ce1169SAndreas Gohr throw new adLDAPException('SSL/TLS must be configured on your webserver and enabled in the class to set passwords.'); 39976ce1169SAndreas Gohr } 40076ce1169SAndreas Gohr 40176ce1169SAndreas Gohr // Find the dn of the user 40276ce1169SAndreas Gohr $userDn = $this->dn($username, $isGUID); 40376ce1169SAndreas Gohr if ($userDn === false) { 40476ce1169SAndreas Gohr return false; 40576ce1169SAndreas Gohr } 40676ce1169SAndreas Gohr 40776ce1169SAndreas Gohr // Translate the update to the LDAP schema 40876ce1169SAndreas Gohr $mod = $this->adldap->adldap_schema($attributes); 40976ce1169SAndreas Gohr 41076ce1169SAndreas Gohr // Check to see if this is an enabled status update 41176ce1169SAndreas Gohr if (!$mod && !array_key_exists("enabled", $attributes)){ 41276ce1169SAndreas Gohr return false; 41376ce1169SAndreas Gohr } 41476ce1169SAndreas Gohr 41576ce1169SAndreas Gohr // Set the account control attribute (only if specified) 41676ce1169SAndreas Gohr if (array_key_exists("enabled", $attributes)){ 41776ce1169SAndreas Gohr if ($attributes["enabled"]){ 41876ce1169SAndreas Gohr $controlOptions = array("NORMAL_ACCOUNT"); 41976ce1169SAndreas Gohr } 42076ce1169SAndreas Gohr else { 42176ce1169SAndreas Gohr $controlOptions = array("NORMAL_ACCOUNT", "ACCOUNTDISABLE"); 42276ce1169SAndreas Gohr } 42376ce1169SAndreas Gohr $mod["userAccountControl"][0] = $this->accountControl($controlOptions); 42476ce1169SAndreas Gohr } 42576ce1169SAndreas Gohr 42676ce1169SAndreas Gohr // Do the update 42776ce1169SAndreas Gohr $result = @ldap_modify($this->adldap->getLdapConnection(), $userDn, $mod); 42876ce1169SAndreas Gohr if ($result == false) { 42976ce1169SAndreas Gohr return false; 43076ce1169SAndreas Gohr } 43176ce1169SAndreas Gohr 43276ce1169SAndreas Gohr return true; 43376ce1169SAndreas Gohr } 43476ce1169SAndreas Gohr 43576ce1169SAndreas Gohr /** 43676ce1169SAndreas Gohr * Disable a user account 43776ce1169SAndreas Gohr * 43876ce1169SAndreas Gohr * @param string $username The username to disable 43976ce1169SAndreas Gohr * @param bool $isGUID Is the username passed a GUID or a samAccountName 44076ce1169SAndreas Gohr * @return bool 44176ce1169SAndreas Gohr */ 44276ce1169SAndreas Gohr public function disable($username, $isGUID = false) 44376ce1169SAndreas Gohr { 44476ce1169SAndreas Gohr if ($username === NULL) { return "Missing compulsory field [username]"; } 44576ce1169SAndreas Gohr $attributes = array("enabled" => 0); 44676ce1169SAndreas Gohr $result = $this->modify($username, $attributes, $isGUID); 44776ce1169SAndreas Gohr if ($result == false) { return false; } 44876ce1169SAndreas Gohr 44976ce1169SAndreas Gohr return true; 45076ce1169SAndreas Gohr } 45176ce1169SAndreas Gohr 45276ce1169SAndreas Gohr /** 45376ce1169SAndreas Gohr * Enable a user account 45476ce1169SAndreas Gohr * 45576ce1169SAndreas Gohr * @param string $username The username to enable 45676ce1169SAndreas Gohr * @param bool $isGUID Is the username passed a GUID or a samAccountName 45776ce1169SAndreas Gohr * @return bool 45876ce1169SAndreas Gohr */ 45976ce1169SAndreas Gohr public function enable($username, $isGUID = false) 46076ce1169SAndreas Gohr { 46176ce1169SAndreas Gohr if ($username === NULL) { return "Missing compulsory field [username]"; } 46276ce1169SAndreas Gohr $attributes = array("enabled" => 1); 46376ce1169SAndreas Gohr $result = $this->modify($username, $attributes, $isGUID); 46476ce1169SAndreas Gohr if ($result == false) { return false; } 46576ce1169SAndreas Gohr 46676ce1169SAndreas Gohr return true; 46776ce1169SAndreas Gohr } 46876ce1169SAndreas Gohr 46976ce1169SAndreas Gohr /** 47076ce1169SAndreas Gohr * Set the password of a user - This must be performed over SSL 47176ce1169SAndreas Gohr * 47276ce1169SAndreas Gohr * @param string $username The username to modify 47376ce1169SAndreas Gohr * @param string $password The new password 47476ce1169SAndreas Gohr * @param bool $isGUID Is the username passed a GUID or a samAccountName 47576ce1169SAndreas Gohr * @return bool 47676ce1169SAndreas Gohr */ 47776ce1169SAndreas Gohr public function password($username, $password, $isGUID = false) 47876ce1169SAndreas Gohr { 47976ce1169SAndreas Gohr if ($username === NULL) { return false; } 48076ce1169SAndreas Gohr if ($password === NULL) { return false; } 48176ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()) { return false; } 48276ce1169SAndreas Gohr if (!$this->adldap->getUseSSL() && !$this->adldap->getUseTLS()) { 48376ce1169SAndreas Gohr throw new adLDAPException('SSL must be configured on your webserver and enabled in the class to set passwords.'); 48476ce1169SAndreas Gohr } 48576ce1169SAndreas Gohr 48676ce1169SAndreas Gohr $userDn = $this->dn($username, $isGUID); 48776ce1169SAndreas Gohr if ($userDn === false) { 48876ce1169SAndreas Gohr return false; 48976ce1169SAndreas Gohr } 49076ce1169SAndreas Gohr 49176ce1169SAndreas Gohr $add=array(); 49276ce1169SAndreas Gohr $add["unicodePwd"][0] = $this->encodePassword($password); 49376ce1169SAndreas Gohr 49476ce1169SAndreas Gohr $result = @ldap_mod_replace($this->adldap->getLdapConnection(), $userDn, $add); 49576ce1169SAndreas Gohr if ($result === false){ 49676ce1169SAndreas Gohr $err = ldap_errno($this->adldap->getLdapConnection()); 49776ce1169SAndreas Gohr if ($err) { 49876ce1169SAndreas Gohr $msg = 'Error ' . $err . ': ' . ldap_err2str($err) . '.'; 49976ce1169SAndreas Gohr if($err == 53) { 50076ce1169SAndreas Gohr $msg .= ' Your password might not match the password policy.'; 50176ce1169SAndreas Gohr } 50276ce1169SAndreas Gohr throw new adLDAPException($msg); 50376ce1169SAndreas Gohr } 50476ce1169SAndreas Gohr else { 50576ce1169SAndreas Gohr return false; 50676ce1169SAndreas Gohr } 50776ce1169SAndreas Gohr } 50876ce1169SAndreas Gohr 50976ce1169SAndreas Gohr return true; 51076ce1169SAndreas Gohr } 51176ce1169SAndreas Gohr 51276ce1169SAndreas Gohr /** 51376ce1169SAndreas Gohr * Encode a password for transmission over LDAP 51476ce1169SAndreas Gohr * 51576ce1169SAndreas Gohr * @param string $password The password to encode 51676ce1169SAndreas Gohr * @return string 51776ce1169SAndreas Gohr */ 51876ce1169SAndreas Gohr public function encodePassword($password) 51976ce1169SAndreas Gohr { 52076ce1169SAndreas Gohr $password="\"".$password."\""; 52176ce1169SAndreas Gohr $encoded=""; 522*e374f7efSÅsmund Stavdahl for ($i=0; $i <strlen($password); $i++){ $encoded.="{$password[$i]}\000"; } 52376ce1169SAndreas Gohr return $encoded; 52476ce1169SAndreas Gohr } 52576ce1169SAndreas Gohr 52676ce1169SAndreas Gohr /** 52776ce1169SAndreas Gohr * Obtain the user's distinguished name based on their userid 52876ce1169SAndreas Gohr * 52976ce1169SAndreas Gohr * 53076ce1169SAndreas Gohr * @param string $username The username 53176ce1169SAndreas Gohr * @param bool $isGUID Is the username passed a GUID or a samAccountName 53276ce1169SAndreas Gohr * @return string 53376ce1169SAndreas Gohr */ 53476ce1169SAndreas Gohr public function dn($username, $isGUID=false) 53576ce1169SAndreas Gohr { 53676ce1169SAndreas Gohr $user = $this->info($username, array("cn"), $isGUID); 53776ce1169SAndreas Gohr if ($user[0]["dn"] === NULL) { 53876ce1169SAndreas Gohr return false; 53976ce1169SAndreas Gohr } 54076ce1169SAndreas Gohr $userDn = $user[0]["dn"]; 54176ce1169SAndreas Gohr return $userDn; 54276ce1169SAndreas Gohr } 54376ce1169SAndreas Gohr 54476ce1169SAndreas Gohr /** 54576ce1169SAndreas Gohr * Return a list of all users in AD 54676ce1169SAndreas Gohr * 54776ce1169SAndreas Gohr * @param bool $includeDescription Return a description of the user 54876ce1169SAndreas Gohr * @param string $search Search parameter 54976ce1169SAndreas Gohr * @param bool $sorted Sort the user accounts 55076ce1169SAndreas Gohr * @return array 55176ce1169SAndreas Gohr */ 55276ce1169SAndreas Gohr public function all($includeDescription = false, $search = "*", $sorted = true) 55376ce1169SAndreas Gohr { 55476ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()) { return false; } 55576ce1169SAndreas Gohr 55676ce1169SAndreas Gohr // Perform the search and grab all their details 55776ce1169SAndreas Gohr $filter = "(&(objectClass=user)(samaccounttype=" . adLDAP::ADLDAP_NORMAL_ACCOUNT .")(objectCategory=person)(cn=" . $search . "))"; 55876ce1169SAndreas Gohr $fields = array("samaccountname","displayname"); 55976ce1169SAndreas Gohr $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields); 56076ce1169SAndreas Gohr $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr); 56176ce1169SAndreas Gohr 56276ce1169SAndreas Gohr $usersArray = array(); 56376ce1169SAndreas Gohr for ($i=0; $i<$entries["count"]; $i++){ 56476ce1169SAndreas Gohr if ($includeDescription && strlen($entries[$i]["displayname"][0])>0){ 56576ce1169SAndreas Gohr $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["displayname"][0]; 56676ce1169SAndreas Gohr } elseif ($includeDescription){ 56776ce1169SAndreas Gohr $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["samaccountname"][0]; 56876ce1169SAndreas Gohr } else { 56976ce1169SAndreas Gohr array_push($usersArray, $entries[$i]["samaccountname"][0]); 57076ce1169SAndreas Gohr } 57176ce1169SAndreas Gohr } 57276ce1169SAndreas Gohr if ($sorted) { 5730489c64bSMoisés Braga Ribeiro Sort::asort($usersArray); 57476ce1169SAndreas Gohr } 57576ce1169SAndreas Gohr return $usersArray; 57676ce1169SAndreas Gohr } 57776ce1169SAndreas Gohr 57876ce1169SAndreas Gohr /** 57976ce1169SAndreas Gohr * Converts a username (samAccountName) to a GUID 58076ce1169SAndreas Gohr * 58176ce1169SAndreas Gohr * @param string $username The username to query 58276ce1169SAndreas Gohr * @return string 58376ce1169SAndreas Gohr */ 58476ce1169SAndreas Gohr public function usernameToGuid($username) 58576ce1169SAndreas Gohr { 58676ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()){ return false; } 58776ce1169SAndreas Gohr if ($username === null){ return "Missing compulsory field [username]"; } 58876ce1169SAndreas Gohr 58976ce1169SAndreas Gohr $filter = "samaccountname=" . $username; 59076ce1169SAndreas Gohr $fields = array("objectGUID"); 59176ce1169SAndreas Gohr $sr = @ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields); 59276ce1169SAndreas Gohr if (ldap_count_entries($this->adldap->getLdapConnection(), $sr) > 0) { 59376ce1169SAndreas Gohr $entry = @ldap_first_entry($this->adldap->getLdapConnection(), $sr); 59476ce1169SAndreas Gohr $guid = @ldap_get_values_len($this->adldap->getLdapConnection(), $entry, 'objectGUID'); 59576ce1169SAndreas Gohr $strGUID = $this->adldap->utilities()->binaryToText($guid[0]); 59676ce1169SAndreas Gohr return $strGUID; 59776ce1169SAndreas Gohr } 59876ce1169SAndreas Gohr return false; 59976ce1169SAndreas Gohr } 60076ce1169SAndreas Gohr 60176ce1169SAndreas Gohr /** 60276ce1169SAndreas Gohr * Return a list of all users in AD that have a specific value in a field 60376ce1169SAndreas Gohr * 60476ce1169SAndreas Gohr * @param bool $includeDescription Return a description of the user 60576ce1169SAndreas Gohr * @param string $searchField Field to search search for 60676ce1169SAndreas Gohr * @param string $searchFilter Value to search for in the specified field 60776ce1169SAndreas Gohr * @param bool $sorted Sort the user accounts 60876ce1169SAndreas Gohr * @return array 60976ce1169SAndreas Gohr */ 61076ce1169SAndreas Gohr public function find($includeDescription = false, $searchField = false, $searchFilter = false, $sorted = true){ 61176ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()){ return false; } 61276ce1169SAndreas Gohr 61376ce1169SAndreas Gohr // Perform the search and grab all their details 61476ce1169SAndreas Gohr $searchParams = ""; 61576ce1169SAndreas Gohr if ($searchField) { 61676ce1169SAndreas Gohr $searchParams = "(" . $searchField . "=" . $searchFilter . ")"; 61776ce1169SAndreas Gohr } 61876ce1169SAndreas Gohr $filter = "(&(objectClass=user)(samaccounttype=" . adLDAP::ADLDAP_NORMAL_ACCOUNT .")(objectCategory=person)" . $searchParams . ")"; 61976ce1169SAndreas Gohr $fields = array("samaccountname","displayname"); 62076ce1169SAndreas Gohr $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields); 62176ce1169SAndreas Gohr $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr); 62276ce1169SAndreas Gohr 62376ce1169SAndreas Gohr $usersArray = array(); 62476ce1169SAndreas Gohr for ($i=0; $i < $entries["count"]; $i++) { 62576ce1169SAndreas Gohr if ($includeDescription && strlen($entries[$i]["displayname"][0]) > 0) { 62676ce1169SAndreas Gohr $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["displayname"][0]; 62776ce1169SAndreas Gohr } 62876ce1169SAndreas Gohr else if ($includeDescription) { 62976ce1169SAndreas Gohr $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["samaccountname"][0]; 63076ce1169SAndreas Gohr } 63176ce1169SAndreas Gohr else { 63276ce1169SAndreas Gohr array_push($usersArray, $entries[$i]["samaccountname"][0]); 63376ce1169SAndreas Gohr } 63476ce1169SAndreas Gohr } 63576ce1169SAndreas Gohr if ($sorted){ 6360489c64bSMoisés Braga Ribeiro Sort::asort($usersArray); 63776ce1169SAndreas Gohr } 63876ce1169SAndreas Gohr return ($usersArray); 63976ce1169SAndreas Gohr } 64076ce1169SAndreas Gohr 64176ce1169SAndreas Gohr /** 64276ce1169SAndreas Gohr * Move a user account to a different OU 64376ce1169SAndreas Gohr * 64476ce1169SAndreas Gohr * @param string $username The username to move (please be careful here!) 64576ce1169SAndreas Gohr * @param array $container The container or containers to move the user to (please be careful here!). 64676ce1169SAndreas Gohr * accepts containers in 1. parent 2. child order 64776ce1169SAndreas Gohr * @return array 64876ce1169SAndreas Gohr */ 64976ce1169SAndreas Gohr public function move($username, $container) 65076ce1169SAndreas Gohr { 65176ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()) { return false; } 65276ce1169SAndreas Gohr if ($username === null) { return "Missing compulsory field [username]"; } 65376ce1169SAndreas Gohr if ($container === null) { return "Missing compulsory field [container]"; } 65476ce1169SAndreas Gohr if (!is_array($container)) { return "Container must be an array"; } 65576ce1169SAndreas Gohr 65676ce1169SAndreas Gohr $userInfo = $this->info($username, array("*")); 65776ce1169SAndreas Gohr $dn = $userInfo[0]['distinguishedname'][0]; 65876ce1169SAndreas Gohr $newRDn = "cn=" . $username; 65976ce1169SAndreas Gohr $container = array_reverse($container); 66076ce1169SAndreas Gohr $newContainer = "ou=" . implode(",ou=",$container); 66176ce1169SAndreas Gohr $newBaseDn = strtolower($newContainer) . "," . $this->adldap->getBaseDn(); 66276ce1169SAndreas Gohr $result = @ldap_rename($this->adldap->getLdapConnection(), $dn, $newRDn, $newBaseDn, true); 66376ce1169SAndreas Gohr if ($result !== true) { 66476ce1169SAndreas Gohr return false; 66576ce1169SAndreas Gohr } 66676ce1169SAndreas Gohr return true; 66776ce1169SAndreas Gohr } 66876ce1169SAndreas Gohr 66976ce1169SAndreas Gohr /** 67076ce1169SAndreas Gohr * Get the last logon time of any user as a Unix timestamp 67176ce1169SAndreas Gohr * 67276ce1169SAndreas Gohr * @param string $username 67376ce1169SAndreas Gohr * @return long $unixTimestamp 67476ce1169SAndreas Gohr */ 67576ce1169SAndreas Gohr public function getLastLogon($username) { 67676ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()) { return false; } 67776ce1169SAndreas Gohr if ($username === null) { return "Missing compulsory field [username]"; } 67876ce1169SAndreas Gohr $userInfo = $this->info($username, array("lastLogonTimestamp")); 67976ce1169SAndreas Gohr $lastLogon = adLDAPUtils::convertWindowsTimeToUnixTime($userInfo[0]['lastLogonTimestamp'][0]); 68076ce1169SAndreas Gohr return $lastLogon; 68176ce1169SAndreas Gohr } 68276ce1169SAndreas Gohr 68376ce1169SAndreas Gohr} 68476ce1169SAndreas Gohr?> 685