1*76ce1169SAndreas Gohr<?php 2*76ce1169SAndreas Gohr/** 3*76ce1169SAndreas Gohr * PHP LDAP CLASS FOR MANIPULATING ACTIVE DIRECTORY 4*76ce1169SAndreas Gohr * Version 4.0.4 5*76ce1169SAndreas Gohr * 6*76ce1169SAndreas Gohr * PHP Version 5 with SSL and LDAP support 7*76ce1169SAndreas Gohr * 8*76ce1169SAndreas Gohr * Written by Scott Barnett, Richard Hyland 9*76ce1169SAndreas Gohr * email: scott@wiggumworld.com, adldap@richardhyland.com 10*76ce1169SAndreas Gohr * http://adldap.sourceforge.net/ 11*76ce1169SAndreas Gohr * 12*76ce1169SAndreas Gohr * Copyright (c) 2006-2012 Scott Barnett, Richard Hyland 13*76ce1169SAndreas Gohr * 14*76ce1169SAndreas Gohr * We'd appreciate any improvements or additions to be submitted back 15*76ce1169SAndreas Gohr * to benefit the entire community :) 16*76ce1169SAndreas Gohr * 17*76ce1169SAndreas Gohr * This library is free software; you can redistribute it and/or 18*76ce1169SAndreas Gohr * modify it under the terms of the GNU Lesser General Public 19*76ce1169SAndreas Gohr * License as published by the Free Software Foundation; either 20*76ce1169SAndreas Gohr * version 2.1 of the License. 21*76ce1169SAndreas Gohr * 22*76ce1169SAndreas Gohr * This library is distributed in the hope that it will be useful, 23*76ce1169SAndreas Gohr * but WITHOUT ANY WARRANTY; without even the implied warranty of 24*76ce1169SAndreas Gohr * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 25*76ce1169SAndreas Gohr * Lesser General Public License for more details. 26*76ce1169SAndreas Gohr * 27*76ce1169SAndreas Gohr * @category ToolsAndUtilities 28*76ce1169SAndreas Gohr * @package adLDAP 29*76ce1169SAndreas Gohr * @subpackage User 30*76ce1169SAndreas Gohr * @author Scott Barnett, Richard Hyland 31*76ce1169SAndreas Gohr * @copyright (c) 2006-2012 Scott Barnett, Richard Hyland 32*76ce1169SAndreas Gohr * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html LGPLv2.1 33*76ce1169SAndreas Gohr * @revision $Revision: 97 $ 34*76ce1169SAndreas Gohr * @version 4.0.4 35*76ce1169SAndreas Gohr * @link http://adldap.sourceforge.net/ 36*76ce1169SAndreas Gohr */ 37*76ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/../adLDAP.php'); 38*76ce1169SAndreas Gohrrequire_once(dirname(__FILE__) . '/../collections/adLDAPUserCollection.php'); 39*76ce1169SAndreas Gohr 40*76ce1169SAndreas Gohr/** 41*76ce1169SAndreas Gohr* USER FUNCTIONS 42*76ce1169SAndreas Gohr*/ 43*76ce1169SAndreas Gohrclass adLDAPUsers { 44*76ce1169SAndreas Gohr /** 45*76ce1169SAndreas Gohr * The current adLDAP connection via dependency injection 46*76ce1169SAndreas Gohr * 47*76ce1169SAndreas Gohr * @var adLDAP 48*76ce1169SAndreas Gohr */ 49*76ce1169SAndreas Gohr protected $adldap; 50*76ce1169SAndreas Gohr 51*76ce1169SAndreas Gohr public function __construct(adLDAP $adldap) { 52*76ce1169SAndreas Gohr $this->adldap = $adldap; 53*76ce1169SAndreas Gohr } 54*76ce1169SAndreas Gohr 55*76ce1169SAndreas Gohr /** 56*76ce1169SAndreas Gohr * Validate a user's login credentials 57*76ce1169SAndreas Gohr * 58*76ce1169SAndreas Gohr * @param string $username A user's AD username 59*76ce1169SAndreas Gohr * @param string $password A user's AD password 60*76ce1169SAndreas Gohr * @param bool optional $prevent_rebind 61*76ce1169SAndreas Gohr * @return bool 62*76ce1169SAndreas Gohr */ 63*76ce1169SAndreas Gohr public function authenticate($username, $password, $preventRebind = false) { 64*76ce1169SAndreas Gohr return $this->adldap->authenticate($username, $password, $preventRebind); 65*76ce1169SAndreas Gohr } 66*76ce1169SAndreas Gohr 67*76ce1169SAndreas Gohr /** 68*76ce1169SAndreas Gohr * Create a user 69*76ce1169SAndreas Gohr * 70*76ce1169SAndreas Gohr * If you specify a password here, this can only be performed over SSL 71*76ce1169SAndreas Gohr * 72*76ce1169SAndreas Gohr * @param array $attributes The attributes to set to the user account 73*76ce1169SAndreas Gohr * @return bool 74*76ce1169SAndreas Gohr */ 75*76ce1169SAndreas Gohr public function create($attributes) 76*76ce1169SAndreas Gohr { 77*76ce1169SAndreas Gohr // Check for compulsory fields 78*76ce1169SAndreas Gohr if (!array_key_exists("username", $attributes)){ return "Missing compulsory field [username]"; } 79*76ce1169SAndreas Gohr if (!array_key_exists("firstname", $attributes)){ return "Missing compulsory field [firstname]"; } 80*76ce1169SAndreas Gohr if (!array_key_exists("surname", $attributes)){ return "Missing compulsory field [surname]"; } 81*76ce1169SAndreas Gohr if (!array_key_exists("email", $attributes)){ return "Missing compulsory field [email]"; } 82*76ce1169SAndreas Gohr if (!array_key_exists("container", $attributes)){ return "Missing compulsory field [container]"; } 83*76ce1169SAndreas Gohr if (!is_array($attributes["container"])){ return "Container attribute must be an array."; } 84*76ce1169SAndreas Gohr 85*76ce1169SAndreas Gohr if (array_key_exists("password",$attributes) && (!$this->adldap->getUseSSL() && !$this->adldap->getUseTLS())){ 86*76ce1169SAndreas Gohr throw new adLDAPException('SSL must be configured on your webserver and enabled in the class to set passwords.'); 87*76ce1169SAndreas Gohr } 88*76ce1169SAndreas Gohr 89*76ce1169SAndreas Gohr if (!array_key_exists("display_name", $attributes)) { 90*76ce1169SAndreas Gohr $attributes["display_name"] = $attributes["firstname"] . " " . $attributes["surname"]; 91*76ce1169SAndreas Gohr } 92*76ce1169SAndreas Gohr 93*76ce1169SAndreas Gohr // Translate the schema 94*76ce1169SAndreas Gohr $add = $this->adldap->adldap_schema($attributes); 95*76ce1169SAndreas Gohr 96*76ce1169SAndreas Gohr // Additional stuff only used for adding accounts 97*76ce1169SAndreas Gohr $add["cn"][0] = $attributes["display_name"]; 98*76ce1169SAndreas Gohr $add["samaccountname"][0] = $attributes["username"]; 99*76ce1169SAndreas Gohr $add["objectclass"][0] = "top"; 100*76ce1169SAndreas Gohr $add["objectclass"][1] = "person"; 101*76ce1169SAndreas Gohr $add["objectclass"][2] = "organizationalPerson"; 102*76ce1169SAndreas Gohr $add["objectclass"][3] = "user"; //person? 103*76ce1169SAndreas Gohr //$add["name"][0]=$attributes["firstname"]." ".$attributes["surname"]; 104*76ce1169SAndreas Gohr 105*76ce1169SAndreas Gohr // Set the account control attribute 106*76ce1169SAndreas Gohr $control_options = array("NORMAL_ACCOUNT"); 107*76ce1169SAndreas Gohr if (!$attributes["enabled"]) { 108*76ce1169SAndreas Gohr $control_options[] = "ACCOUNTDISABLE"; 109*76ce1169SAndreas Gohr } 110*76ce1169SAndreas Gohr $add["userAccountControl"][0] = $this->accountControl($control_options); 111*76ce1169SAndreas Gohr 112*76ce1169SAndreas Gohr // Determine the container 113*76ce1169SAndreas Gohr $attributes["container"] = array_reverse($attributes["container"]); 114*76ce1169SAndreas Gohr $container = "OU=" . implode(", OU=",$attributes["container"]); 115*76ce1169SAndreas Gohr 116*76ce1169SAndreas Gohr // Add the entry 117*76ce1169SAndreas Gohr $result = @ldap_add($this->adldap->getLdapConnection(), "CN=" . $add["cn"][0] . ", " . $container . "," . $this->adldap->getBaseDn(), $add); 118*76ce1169SAndreas Gohr if ($result != true) { 119*76ce1169SAndreas Gohr return false; 120*76ce1169SAndreas Gohr } 121*76ce1169SAndreas Gohr 122*76ce1169SAndreas Gohr return true; 123*76ce1169SAndreas Gohr } 124*76ce1169SAndreas Gohr 125*76ce1169SAndreas Gohr /** 126*76ce1169SAndreas Gohr * Account control options 127*76ce1169SAndreas Gohr * 128*76ce1169SAndreas Gohr * @param array $options The options to convert to int 129*76ce1169SAndreas Gohr * @return int 130*76ce1169SAndreas Gohr */ 131*76ce1169SAndreas Gohr protected function accountControl($options) 132*76ce1169SAndreas Gohr { 133*76ce1169SAndreas Gohr $val=0; 134*76ce1169SAndreas Gohr 135*76ce1169SAndreas Gohr if (is_array($options)) { 136*76ce1169SAndreas Gohr if (in_array("SCRIPT",$options)){ $val=$val+1; } 137*76ce1169SAndreas Gohr if (in_array("ACCOUNTDISABLE",$options)){ $val=$val+2; } 138*76ce1169SAndreas Gohr if (in_array("HOMEDIR_REQUIRED",$options)){ $val=$val+8; } 139*76ce1169SAndreas Gohr if (in_array("LOCKOUT",$options)){ $val=$val+16; } 140*76ce1169SAndreas Gohr if (in_array("PASSWD_NOTREQD",$options)){ $val=$val+32; } 141*76ce1169SAndreas Gohr //PASSWD_CANT_CHANGE Note You cannot assign this permission by directly modifying the UserAccountControl attribute. 142*76ce1169SAndreas Gohr //For information about how to set the permission programmatically, see the "Property flag descriptions" section. 143*76ce1169SAndreas Gohr if (in_array("ENCRYPTED_TEXT_PWD_ALLOWED",$options)){ $val=$val+128; } 144*76ce1169SAndreas Gohr if (in_array("TEMP_DUPLICATE_ACCOUNT",$options)){ $val=$val+256; } 145*76ce1169SAndreas Gohr if (in_array("NORMAL_ACCOUNT",$options)){ $val=$val+512; } 146*76ce1169SAndreas Gohr if (in_array("INTERDOMAIN_TRUST_ACCOUNT",$options)){ $val=$val+2048; } 147*76ce1169SAndreas Gohr if (in_array("WORKSTATION_TRUST_ACCOUNT",$options)){ $val=$val+4096; } 148*76ce1169SAndreas Gohr if (in_array("SERVER_TRUST_ACCOUNT",$options)){ $val=$val+8192; } 149*76ce1169SAndreas Gohr if (in_array("DONT_EXPIRE_PASSWORD",$options)){ $val=$val+65536; } 150*76ce1169SAndreas Gohr if (in_array("MNS_LOGON_ACCOUNT",$options)){ $val=$val+131072; } 151*76ce1169SAndreas Gohr if (in_array("SMARTCARD_REQUIRED",$options)){ $val=$val+262144; } 152*76ce1169SAndreas Gohr if (in_array("TRUSTED_FOR_DELEGATION",$options)){ $val=$val+524288; } 153*76ce1169SAndreas Gohr if (in_array("NOT_DELEGATED",$options)){ $val=$val+1048576; } 154*76ce1169SAndreas Gohr if (in_array("USE_DES_KEY_ONLY",$options)){ $val=$val+2097152; } 155*76ce1169SAndreas Gohr if (in_array("DONT_REQ_PREAUTH",$options)){ $val=$val+4194304; } 156*76ce1169SAndreas Gohr if (in_array("PASSWORD_EXPIRED",$options)){ $val=$val+8388608; } 157*76ce1169SAndreas Gohr if (in_array("TRUSTED_TO_AUTH_FOR_DELEGATION",$options)){ $val=$val+16777216; } 158*76ce1169SAndreas Gohr } 159*76ce1169SAndreas Gohr return $val; 160*76ce1169SAndreas Gohr } 161*76ce1169SAndreas Gohr 162*76ce1169SAndreas Gohr /** 163*76ce1169SAndreas Gohr * Delete a user account 164*76ce1169SAndreas Gohr * 165*76ce1169SAndreas Gohr * @param string $username The username to delete (please be careful here!) 166*76ce1169SAndreas Gohr * @param bool $isGUID Is the username a GUID or a samAccountName 167*76ce1169SAndreas Gohr * @return array 168*76ce1169SAndreas Gohr */ 169*76ce1169SAndreas Gohr public function delete($username, $isGUID = false) 170*76ce1169SAndreas Gohr { 171*76ce1169SAndreas Gohr $userinfo = $this->info($username, array("*"), $isGUID); 172*76ce1169SAndreas Gohr $dn = $userinfo[0]['distinguishedname'][0]; 173*76ce1169SAndreas Gohr $result = $this->adldap->folder()->delete($dn); 174*76ce1169SAndreas Gohr if ($result != true) { 175*76ce1169SAndreas Gohr return false; 176*76ce1169SAndreas Gohr } 177*76ce1169SAndreas Gohr return true; 178*76ce1169SAndreas Gohr } 179*76ce1169SAndreas Gohr 180*76ce1169SAndreas Gohr /** 181*76ce1169SAndreas Gohr * Groups the user is a member of 182*76ce1169SAndreas Gohr * 183*76ce1169SAndreas Gohr * @param string $username The username to query 184*76ce1169SAndreas Gohr * @param bool $recursive Recursive list of groups 185*76ce1169SAndreas Gohr * @param bool $isGUID Is the username passed a GUID or a samAccountName 186*76ce1169SAndreas Gohr * @return array 187*76ce1169SAndreas Gohr */ 188*76ce1169SAndreas Gohr public function groups($username, $recursive = NULL, $isGUID = false) 189*76ce1169SAndreas Gohr { 190*76ce1169SAndreas Gohr if ($username === NULL) { return false; } 191*76ce1169SAndreas Gohr if ($recursive === NULL) { $recursive = $this->adldap->getRecursiveGroups(); } // Use the default option if they haven't set it 192*76ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()) { return false; } 193*76ce1169SAndreas Gohr 194*76ce1169SAndreas Gohr // Search the directory for their information 195*76ce1169SAndreas Gohr $info = @$this->info($username, array("memberof", "primarygroupid"), $isGUID); 196*76ce1169SAndreas Gohr $groups = $this->adldap->utilities()->niceNames($info[0]["memberof"]); // Presuming the entry returned is our guy (unique usernames) 197*76ce1169SAndreas Gohr 198*76ce1169SAndreas Gohr if ($recursive === true){ 199*76ce1169SAndreas Gohr foreach ($groups as $id => $groupName){ 200*76ce1169SAndreas Gohr $extraGroups = $this->adldap->group()->recursiveGroups($groupName); 201*76ce1169SAndreas Gohr $groups = array_merge($groups, $extraGroups); 202*76ce1169SAndreas Gohr } 203*76ce1169SAndreas Gohr } 204*76ce1169SAndreas Gohr 205*76ce1169SAndreas Gohr return $groups; 206*76ce1169SAndreas Gohr } 207*76ce1169SAndreas Gohr 208*76ce1169SAndreas Gohr /** 209*76ce1169SAndreas Gohr * Find information about the users. Returned in a raw array format from AD 210*76ce1169SAndreas Gohr * 211*76ce1169SAndreas Gohr * @param string $username The username to query 212*76ce1169SAndreas Gohr * @param array $fields Array of parameters to query 213*76ce1169SAndreas Gohr * @param bool $isGUID Is the username passed a GUID or a samAccountName 214*76ce1169SAndreas Gohr * @return array 215*76ce1169SAndreas Gohr */ 216*76ce1169SAndreas Gohr public function info($username, $fields = NULL, $isGUID = false) 217*76ce1169SAndreas Gohr { 218*76ce1169SAndreas Gohr if ($username === NULL) { return false; } 219*76ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()) { return false; } 220*76ce1169SAndreas Gohr 221*76ce1169SAndreas Gohr if ($isGUID === true) { 222*76ce1169SAndreas Gohr $username = $this->adldap->utilities()->strGuidToHex($username); 223*76ce1169SAndreas Gohr $filter = "objectguid=" . $username; 224*76ce1169SAndreas Gohr } 225*76ce1169SAndreas Gohr else if (strstr($username, "@")) { 226*76ce1169SAndreas Gohr $filter = "userPrincipalName=" . $username; 227*76ce1169SAndreas Gohr } 228*76ce1169SAndreas Gohr else { 229*76ce1169SAndreas Gohr $filter = "samaccountname=" . $username; 230*76ce1169SAndreas Gohr } 231*76ce1169SAndreas Gohr $filter = "(&(objectCategory=person)({$filter}))"; 232*76ce1169SAndreas Gohr if ($fields === NULL) { 233*76ce1169SAndreas Gohr $fields = array("samaccountname","mail","memberof","department","displayname","telephonenumber","primarygroupid","objectsid"); 234*76ce1169SAndreas Gohr } 235*76ce1169SAndreas Gohr if (!in_array("objectsid", $fields)) { 236*76ce1169SAndreas Gohr $fields[] = "objectsid"; 237*76ce1169SAndreas Gohr } 238*76ce1169SAndreas Gohr $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields); 239*76ce1169SAndreas Gohr $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr); 240*76ce1169SAndreas Gohr 241*76ce1169SAndreas Gohr if (isset($entries[0])) { 242*76ce1169SAndreas Gohr if ($entries[0]['count'] >= 1) { 243*76ce1169SAndreas Gohr if (in_array("memberof", $fields)) { 244*76ce1169SAndreas Gohr // AD does not return the primary group in the ldap query, we may need to fudge it 245*76ce1169SAndreas Gohr if ($this->adldap->getRealPrimaryGroup() && isset($entries[0]["primarygroupid"][0]) && isset($entries[0]["objectsid"][0])){ 246*76ce1169SAndreas Gohr //$entries[0]["memberof"][]=$this->group_cn($entries[0]["primarygroupid"][0]); 247*76ce1169SAndreas Gohr $entries[0]["memberof"][] = $this->adldap->group()->getPrimaryGroup($entries[0]["primarygroupid"][0], $entries[0]["objectsid"][0]); 248*76ce1169SAndreas Gohr } else { 249*76ce1169SAndreas Gohr $entries[0]["memberof"][] = "CN=Domain Users,CN=Users," . $this->adldap->getBaseDn(); 250*76ce1169SAndreas Gohr } 251*76ce1169SAndreas Gohr if (!isset($entries[0]["memberof"]["count"])) { 252*76ce1169SAndreas Gohr $entries[0]["memberof"]["count"] = 0; 253*76ce1169SAndreas Gohr } 254*76ce1169SAndreas Gohr $entries[0]["memberof"]["count"]++; 255*76ce1169SAndreas Gohr } 256*76ce1169SAndreas Gohr } 257*76ce1169SAndreas Gohr 258*76ce1169SAndreas Gohr return $entries; 259*76ce1169SAndreas Gohr } 260*76ce1169SAndreas Gohr return false; 261*76ce1169SAndreas Gohr } 262*76ce1169SAndreas Gohr 263*76ce1169SAndreas Gohr /** 264*76ce1169SAndreas Gohr * Find information about the users. Returned in a raw array format from AD 265*76ce1169SAndreas Gohr * 266*76ce1169SAndreas Gohr * @param string $username The username to query 267*76ce1169SAndreas Gohr * @param array $fields Array of parameters to query 268*76ce1169SAndreas Gohr * @param bool $isGUID Is the username passed a GUID or a samAccountName 269*76ce1169SAndreas Gohr * @return mixed 270*76ce1169SAndreas Gohr */ 271*76ce1169SAndreas Gohr public function infoCollection($username, $fields = NULL, $isGUID = false) 272*76ce1169SAndreas Gohr { 273*76ce1169SAndreas Gohr if ($username === NULL) { return false; } 274*76ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()) { return false; } 275*76ce1169SAndreas Gohr 276*76ce1169SAndreas Gohr $info = $this->info($username, $fields, $isGUID); 277*76ce1169SAndreas Gohr 278*76ce1169SAndreas Gohr if ($info !== false) { 279*76ce1169SAndreas Gohr $collection = new adLDAPUserCollection($info, $this->adldap); 280*76ce1169SAndreas Gohr return $collection; 281*76ce1169SAndreas Gohr } 282*76ce1169SAndreas Gohr return false; 283*76ce1169SAndreas Gohr } 284*76ce1169SAndreas Gohr 285*76ce1169SAndreas Gohr /** 286*76ce1169SAndreas Gohr * Determine if a user is in a specific group 287*76ce1169SAndreas Gohr * 288*76ce1169SAndreas Gohr * @param string $username The username to query 289*76ce1169SAndreas Gohr * @param string $group The name of the group to check against 290*76ce1169SAndreas Gohr * @param bool $recursive Check groups recursively 291*76ce1169SAndreas Gohr * @param bool $isGUID Is the username passed a GUID or a samAccountName 292*76ce1169SAndreas Gohr * @return bool 293*76ce1169SAndreas Gohr */ 294*76ce1169SAndreas Gohr public function inGroup($username, $group, $recursive = NULL, $isGUID = false) 295*76ce1169SAndreas Gohr { 296*76ce1169SAndreas Gohr if ($username === NULL) { return false; } 297*76ce1169SAndreas Gohr if ($group === NULL) { return false; } 298*76ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()) { return false; } 299*76ce1169SAndreas Gohr if ($recursive === NULL) { $recursive = $this->adldap->getRecursiveGroups(); } // Use the default option if they haven't set it 300*76ce1169SAndreas Gohr 301*76ce1169SAndreas Gohr // Get a list of the groups 302*76ce1169SAndreas Gohr $groups = $this->groups($username, $recursive, $isGUID); 303*76ce1169SAndreas Gohr 304*76ce1169SAndreas Gohr // Return true if the specified group is in the group list 305*76ce1169SAndreas Gohr if (in_array($group, $groups)) { 306*76ce1169SAndreas Gohr return true; 307*76ce1169SAndreas Gohr } 308*76ce1169SAndreas Gohr 309*76ce1169SAndreas Gohr return false; 310*76ce1169SAndreas Gohr } 311*76ce1169SAndreas Gohr 312*76ce1169SAndreas Gohr /** 313*76ce1169SAndreas Gohr * Determine a user's password expiry date 314*76ce1169SAndreas Gohr * 315*76ce1169SAndreas Gohr * @param string $username The username to query 316*76ce1169SAndreas Gohr * @param book $isGUID Is the username passed a GUID or a samAccountName 317*76ce1169SAndreas Gohr * @requires bcmath http://www.php.net/manual/en/book.bc.php 318*76ce1169SAndreas Gohr * @return array 319*76ce1169SAndreas Gohr */ 320*76ce1169SAndreas Gohr public function passwordExpiry($username, $isGUID = false) 321*76ce1169SAndreas Gohr { 322*76ce1169SAndreas Gohr if ($username === NULL) { return "Missing compulsory field [username]"; } 323*76ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()) { return false; } 324*76ce1169SAndreas Gohr if (!function_exists('bcmod')) { throw new adLDAPException("Missing function support [bcmod] http://www.php.net/manual/en/book.bc.php"); }; 325*76ce1169SAndreas Gohr 326*76ce1169SAndreas Gohr $userInfo = $this->info($username, array("pwdlastset", "useraccountcontrol"), $isGUID); 327*76ce1169SAndreas Gohr $pwdLastSet = $userInfo[0]['pwdlastset'][0]; 328*76ce1169SAndreas Gohr $status = array(); 329*76ce1169SAndreas Gohr 330*76ce1169SAndreas Gohr if ($userInfo[0]['useraccountcontrol'][0] == '66048') { 331*76ce1169SAndreas Gohr // Password does not expire 332*76ce1169SAndreas Gohr return "Does not expire"; 333*76ce1169SAndreas Gohr } 334*76ce1169SAndreas Gohr if ($pwdLastSet === '0') { 335*76ce1169SAndreas Gohr // Password has already expired 336*76ce1169SAndreas Gohr return "Password has expired"; 337*76ce1169SAndreas Gohr } 338*76ce1169SAndreas Gohr 339*76ce1169SAndreas Gohr // Password expiry in AD can be calculated from TWO values: 340*76ce1169SAndreas Gohr // - User's own pwdLastSet attribute: stores the last time the password was changed 341*76ce1169SAndreas Gohr // - Domain's maxPwdAge attribute: how long passwords last in the domain 342*76ce1169SAndreas Gohr // 343*76ce1169SAndreas Gohr // Although Microsoft chose to use a different base and unit for time measurements. 344*76ce1169SAndreas Gohr // This function will convert them to Unix timestamps 345*76ce1169SAndreas Gohr $sr = ldap_read($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), 'objectclass=*', array('maxPwdAge')); 346*76ce1169SAndreas Gohr if (!$sr) { 347*76ce1169SAndreas Gohr return false; 348*76ce1169SAndreas Gohr } 349*76ce1169SAndreas Gohr $info = ldap_get_entries($this->adldap->getLdapConnection(), $sr); 350*76ce1169SAndreas Gohr $maxPwdAge = $info[0]['maxpwdage'][0]; 351*76ce1169SAndreas Gohr 352*76ce1169SAndreas Gohr 353*76ce1169SAndreas Gohr // See MSDN: http://msdn.microsoft.com/en-us/library/ms974598.aspx 354*76ce1169SAndreas Gohr // 355*76ce1169SAndreas Gohr // pwdLastSet contains the number of 100 nanosecond intervals since January 1, 1601 (UTC), 356*76ce1169SAndreas Gohr // stored in a 64 bit integer. 357*76ce1169SAndreas Gohr // 358*76ce1169SAndreas Gohr // The number of seconds between this date and Unix epoch is 11644473600. 359*76ce1169SAndreas Gohr // 360*76ce1169SAndreas Gohr // maxPwdAge is stored as a large integer that represents the number of 100 nanosecond 361*76ce1169SAndreas Gohr // intervals from the time the password was set before the password expires. 362*76ce1169SAndreas Gohr // 363*76ce1169SAndreas Gohr // We also need to scale this to seconds but also this value is a _negative_ quantity! 364*76ce1169SAndreas Gohr // 365*76ce1169SAndreas Gohr // If the low 32 bits of maxPwdAge are equal to 0 passwords do not expire 366*76ce1169SAndreas Gohr // 367*76ce1169SAndreas Gohr // Unfortunately the maths involved are too big for PHP integers, so I've had to require 368*76ce1169SAndreas Gohr // BCMath functions to work with arbitrary precision numbers. 369*76ce1169SAndreas Gohr if (bcmod($maxPwdAge, 4294967296) === '0') { 370*76ce1169SAndreas Gohr return "Domain does not expire passwords"; 371*76ce1169SAndreas Gohr } 372*76ce1169SAndreas Gohr 373*76ce1169SAndreas Gohr // Add maxpwdage and pwdlastset and we get password expiration time in Microsoft's 374*76ce1169SAndreas Gohr // time units. Because maxpwd age is negative we need to subtract it. 375*76ce1169SAndreas Gohr $pwdExpire = bcsub($pwdLastSet, $maxPwdAge); 376*76ce1169SAndreas Gohr 377*76ce1169SAndreas Gohr // Convert MS's time to Unix time 378*76ce1169SAndreas Gohr $status['expiryts'] = bcsub(bcdiv($pwdExpire, '10000000'), '11644473600'); 379*76ce1169SAndreas Gohr $status['expiryformat'] = date('Y-m-d H:i:s', bcsub(bcdiv($pwdExpire, '10000000'), '11644473600')); 380*76ce1169SAndreas Gohr 381*76ce1169SAndreas Gohr return $status; 382*76ce1169SAndreas Gohr } 383*76ce1169SAndreas Gohr 384*76ce1169SAndreas Gohr /** 385*76ce1169SAndreas Gohr * Modify a user 386*76ce1169SAndreas Gohr * 387*76ce1169SAndreas Gohr * @param string $username The username to query 388*76ce1169SAndreas Gohr * @param array $attributes The attributes to modify. Note if you set the enabled attribute you must not specify any other attributes 389*76ce1169SAndreas Gohr * @param bool $isGUID Is the username passed a GUID or a samAccountName 390*76ce1169SAndreas Gohr * @return bool 391*76ce1169SAndreas Gohr */ 392*76ce1169SAndreas Gohr public function modify($username, $attributes, $isGUID = false) 393*76ce1169SAndreas Gohr { 394*76ce1169SAndreas Gohr if ($username === NULL) { return "Missing compulsory field [username]"; } 395*76ce1169SAndreas Gohr if (array_key_exists("password", $attributes) && !$this->adldap->getUseSSL() && !$this->adldap->getUseTLS()) { 396*76ce1169SAndreas Gohr throw new adLDAPException('SSL/TLS must be configured on your webserver and enabled in the class to set passwords.'); 397*76ce1169SAndreas Gohr } 398*76ce1169SAndreas Gohr 399*76ce1169SAndreas Gohr // Find the dn of the user 400*76ce1169SAndreas Gohr $userDn = $this->dn($username, $isGUID); 401*76ce1169SAndreas Gohr if ($userDn === false) { 402*76ce1169SAndreas Gohr return false; 403*76ce1169SAndreas Gohr } 404*76ce1169SAndreas Gohr 405*76ce1169SAndreas Gohr // Translate the update to the LDAP schema 406*76ce1169SAndreas Gohr $mod = $this->adldap->adldap_schema($attributes); 407*76ce1169SAndreas Gohr 408*76ce1169SAndreas Gohr // Check to see if this is an enabled status update 409*76ce1169SAndreas Gohr if (!$mod && !array_key_exists("enabled", $attributes)){ 410*76ce1169SAndreas Gohr return false; 411*76ce1169SAndreas Gohr } 412*76ce1169SAndreas Gohr 413*76ce1169SAndreas Gohr // Set the account control attribute (only if specified) 414*76ce1169SAndreas Gohr if (array_key_exists("enabled", $attributes)){ 415*76ce1169SAndreas Gohr if ($attributes["enabled"]){ 416*76ce1169SAndreas Gohr $controlOptions = array("NORMAL_ACCOUNT"); 417*76ce1169SAndreas Gohr } 418*76ce1169SAndreas Gohr else { 419*76ce1169SAndreas Gohr $controlOptions = array("NORMAL_ACCOUNT", "ACCOUNTDISABLE"); 420*76ce1169SAndreas Gohr } 421*76ce1169SAndreas Gohr $mod["userAccountControl"][0] = $this->accountControl($controlOptions); 422*76ce1169SAndreas Gohr } 423*76ce1169SAndreas Gohr 424*76ce1169SAndreas Gohr // Do the update 425*76ce1169SAndreas Gohr $result = @ldap_modify($this->adldap->getLdapConnection(), $userDn, $mod); 426*76ce1169SAndreas Gohr if ($result == false) { 427*76ce1169SAndreas Gohr return false; 428*76ce1169SAndreas Gohr } 429*76ce1169SAndreas Gohr 430*76ce1169SAndreas Gohr return true; 431*76ce1169SAndreas Gohr } 432*76ce1169SAndreas Gohr 433*76ce1169SAndreas Gohr /** 434*76ce1169SAndreas Gohr * Disable a user account 435*76ce1169SAndreas Gohr * 436*76ce1169SAndreas Gohr * @param string $username The username to disable 437*76ce1169SAndreas Gohr * @param bool $isGUID Is the username passed a GUID or a samAccountName 438*76ce1169SAndreas Gohr * @return bool 439*76ce1169SAndreas Gohr */ 440*76ce1169SAndreas Gohr public function disable($username, $isGUID = false) 441*76ce1169SAndreas Gohr { 442*76ce1169SAndreas Gohr if ($username === NULL) { return "Missing compulsory field [username]"; } 443*76ce1169SAndreas Gohr $attributes = array("enabled" => 0); 444*76ce1169SAndreas Gohr $result = $this->modify($username, $attributes, $isGUID); 445*76ce1169SAndreas Gohr if ($result == false) { return false; } 446*76ce1169SAndreas Gohr 447*76ce1169SAndreas Gohr return true; 448*76ce1169SAndreas Gohr } 449*76ce1169SAndreas Gohr 450*76ce1169SAndreas Gohr /** 451*76ce1169SAndreas Gohr * Enable a user account 452*76ce1169SAndreas Gohr * 453*76ce1169SAndreas Gohr * @param string $username The username to enable 454*76ce1169SAndreas Gohr * @param bool $isGUID Is the username passed a GUID or a samAccountName 455*76ce1169SAndreas Gohr * @return bool 456*76ce1169SAndreas Gohr */ 457*76ce1169SAndreas Gohr public function enable($username, $isGUID = false) 458*76ce1169SAndreas Gohr { 459*76ce1169SAndreas Gohr if ($username === NULL) { return "Missing compulsory field [username]"; } 460*76ce1169SAndreas Gohr $attributes = array("enabled" => 1); 461*76ce1169SAndreas Gohr $result = $this->modify($username, $attributes, $isGUID); 462*76ce1169SAndreas Gohr if ($result == false) { return false; } 463*76ce1169SAndreas Gohr 464*76ce1169SAndreas Gohr return true; 465*76ce1169SAndreas Gohr } 466*76ce1169SAndreas Gohr 467*76ce1169SAndreas Gohr /** 468*76ce1169SAndreas Gohr * Set the password of a user - This must be performed over SSL 469*76ce1169SAndreas Gohr * 470*76ce1169SAndreas Gohr * @param string $username The username to modify 471*76ce1169SAndreas Gohr * @param string $password The new password 472*76ce1169SAndreas Gohr * @param bool $isGUID Is the username passed a GUID or a samAccountName 473*76ce1169SAndreas Gohr * @return bool 474*76ce1169SAndreas Gohr */ 475*76ce1169SAndreas Gohr public function password($username, $password, $isGUID = false) 476*76ce1169SAndreas Gohr { 477*76ce1169SAndreas Gohr if ($username === NULL) { return false; } 478*76ce1169SAndreas Gohr if ($password === NULL) { return false; } 479*76ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()) { return false; } 480*76ce1169SAndreas Gohr if (!$this->adldap->getUseSSL() && !$this->adldap->getUseTLS()) { 481*76ce1169SAndreas Gohr throw new adLDAPException('SSL must be configured on your webserver and enabled in the class to set passwords.'); 482*76ce1169SAndreas Gohr } 483*76ce1169SAndreas Gohr 484*76ce1169SAndreas Gohr $userDn = $this->dn($username, $isGUID); 485*76ce1169SAndreas Gohr if ($userDn === false) { 486*76ce1169SAndreas Gohr return false; 487*76ce1169SAndreas Gohr } 488*76ce1169SAndreas Gohr 489*76ce1169SAndreas Gohr $add=array(); 490*76ce1169SAndreas Gohr $add["unicodePwd"][0] = $this->encodePassword($password); 491*76ce1169SAndreas Gohr 492*76ce1169SAndreas Gohr $result = @ldap_mod_replace($this->adldap->getLdapConnection(), $userDn, $add); 493*76ce1169SAndreas Gohr if ($result === false){ 494*76ce1169SAndreas Gohr $err = ldap_errno($this->adldap->getLdapConnection()); 495*76ce1169SAndreas Gohr if ($err) { 496*76ce1169SAndreas Gohr $msg = 'Error ' . $err . ': ' . ldap_err2str($err) . '.'; 497*76ce1169SAndreas Gohr if($err == 53) { 498*76ce1169SAndreas Gohr $msg .= ' Your password might not match the password policy.'; 499*76ce1169SAndreas Gohr } 500*76ce1169SAndreas Gohr throw new adLDAPException($msg); 501*76ce1169SAndreas Gohr } 502*76ce1169SAndreas Gohr else { 503*76ce1169SAndreas Gohr return false; 504*76ce1169SAndreas Gohr } 505*76ce1169SAndreas Gohr } 506*76ce1169SAndreas Gohr 507*76ce1169SAndreas Gohr return true; 508*76ce1169SAndreas Gohr } 509*76ce1169SAndreas Gohr 510*76ce1169SAndreas Gohr /** 511*76ce1169SAndreas Gohr * Encode a password for transmission over LDAP 512*76ce1169SAndreas Gohr * 513*76ce1169SAndreas Gohr * @param string $password The password to encode 514*76ce1169SAndreas Gohr * @return string 515*76ce1169SAndreas Gohr */ 516*76ce1169SAndreas Gohr public function encodePassword($password) 517*76ce1169SAndreas Gohr { 518*76ce1169SAndreas Gohr $password="\"".$password."\""; 519*76ce1169SAndreas Gohr $encoded=""; 520*76ce1169SAndreas Gohr for ($i=0; $i <strlen($password); $i++){ $encoded.="{$password{$i}}\000"; } 521*76ce1169SAndreas Gohr return $encoded; 522*76ce1169SAndreas Gohr } 523*76ce1169SAndreas Gohr 524*76ce1169SAndreas Gohr /** 525*76ce1169SAndreas Gohr * Obtain the user's distinguished name based on their userid 526*76ce1169SAndreas Gohr * 527*76ce1169SAndreas Gohr * 528*76ce1169SAndreas Gohr * @param string $username The username 529*76ce1169SAndreas Gohr * @param bool $isGUID Is the username passed a GUID or a samAccountName 530*76ce1169SAndreas Gohr * @return string 531*76ce1169SAndreas Gohr */ 532*76ce1169SAndreas Gohr public function dn($username, $isGUID=false) 533*76ce1169SAndreas Gohr { 534*76ce1169SAndreas Gohr $user = $this->info($username, array("cn"), $isGUID); 535*76ce1169SAndreas Gohr if ($user[0]["dn"] === NULL) { 536*76ce1169SAndreas Gohr return false; 537*76ce1169SAndreas Gohr } 538*76ce1169SAndreas Gohr $userDn = $user[0]["dn"]; 539*76ce1169SAndreas Gohr return $userDn; 540*76ce1169SAndreas Gohr } 541*76ce1169SAndreas Gohr 542*76ce1169SAndreas Gohr /** 543*76ce1169SAndreas Gohr * Return a list of all users in AD 544*76ce1169SAndreas Gohr * 545*76ce1169SAndreas Gohr * @param bool $includeDescription Return a description of the user 546*76ce1169SAndreas Gohr * @param string $search Search parameter 547*76ce1169SAndreas Gohr * @param bool $sorted Sort the user accounts 548*76ce1169SAndreas Gohr * @return array 549*76ce1169SAndreas Gohr */ 550*76ce1169SAndreas Gohr public function all($includeDescription = false, $search = "*", $sorted = true) 551*76ce1169SAndreas Gohr { 552*76ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()) { return false; } 553*76ce1169SAndreas Gohr 554*76ce1169SAndreas Gohr // Perform the search and grab all their details 555*76ce1169SAndreas Gohr $filter = "(&(objectClass=user)(samaccounttype=" . adLDAP::ADLDAP_NORMAL_ACCOUNT .")(objectCategory=person)(cn=" . $search . "))"; 556*76ce1169SAndreas Gohr $fields = array("samaccountname","displayname"); 557*76ce1169SAndreas Gohr $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields); 558*76ce1169SAndreas Gohr $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr); 559*76ce1169SAndreas Gohr 560*76ce1169SAndreas Gohr $usersArray = array(); 561*76ce1169SAndreas Gohr for ($i=0; $i<$entries["count"]; $i++){ 562*76ce1169SAndreas Gohr if ($includeDescription && strlen($entries[$i]["displayname"][0])>0){ 563*76ce1169SAndreas Gohr $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["displayname"][0]; 564*76ce1169SAndreas Gohr } elseif ($includeDescription){ 565*76ce1169SAndreas Gohr $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["samaccountname"][0]; 566*76ce1169SAndreas Gohr } else { 567*76ce1169SAndreas Gohr array_push($usersArray, $entries[$i]["samaccountname"][0]); 568*76ce1169SAndreas Gohr } 569*76ce1169SAndreas Gohr } 570*76ce1169SAndreas Gohr if ($sorted) { 571*76ce1169SAndreas Gohr asort($usersArray); 572*76ce1169SAndreas Gohr } 573*76ce1169SAndreas Gohr return $usersArray; 574*76ce1169SAndreas Gohr } 575*76ce1169SAndreas Gohr 576*76ce1169SAndreas Gohr /** 577*76ce1169SAndreas Gohr * Converts a username (samAccountName) to a GUID 578*76ce1169SAndreas Gohr * 579*76ce1169SAndreas Gohr * @param string $username The username to query 580*76ce1169SAndreas Gohr * @return string 581*76ce1169SAndreas Gohr */ 582*76ce1169SAndreas Gohr public function usernameToGuid($username) 583*76ce1169SAndreas Gohr { 584*76ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()){ return false; } 585*76ce1169SAndreas Gohr if ($username === null){ return "Missing compulsory field [username]"; } 586*76ce1169SAndreas Gohr 587*76ce1169SAndreas Gohr $filter = "samaccountname=" . $username; 588*76ce1169SAndreas Gohr $fields = array("objectGUID"); 589*76ce1169SAndreas Gohr $sr = @ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields); 590*76ce1169SAndreas Gohr if (ldap_count_entries($this->adldap->getLdapConnection(), $sr) > 0) { 591*76ce1169SAndreas Gohr $entry = @ldap_first_entry($this->adldap->getLdapConnection(), $sr); 592*76ce1169SAndreas Gohr $guid = @ldap_get_values_len($this->adldap->getLdapConnection(), $entry, 'objectGUID'); 593*76ce1169SAndreas Gohr $strGUID = $this->adldap->utilities()->binaryToText($guid[0]); 594*76ce1169SAndreas Gohr return $strGUID; 595*76ce1169SAndreas Gohr } 596*76ce1169SAndreas Gohr return false; 597*76ce1169SAndreas Gohr } 598*76ce1169SAndreas Gohr 599*76ce1169SAndreas Gohr /** 600*76ce1169SAndreas Gohr * Return a list of all users in AD that have a specific value in a field 601*76ce1169SAndreas Gohr * 602*76ce1169SAndreas Gohr * @param bool $includeDescription Return a description of the user 603*76ce1169SAndreas Gohr * @param string $searchField Field to search search for 604*76ce1169SAndreas Gohr * @param string $searchFilter Value to search for in the specified field 605*76ce1169SAndreas Gohr * @param bool $sorted Sort the user accounts 606*76ce1169SAndreas Gohr * @return array 607*76ce1169SAndreas Gohr */ 608*76ce1169SAndreas Gohr public function find($includeDescription = false, $searchField = false, $searchFilter = false, $sorted = true){ 609*76ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()){ return false; } 610*76ce1169SAndreas Gohr 611*76ce1169SAndreas Gohr // Perform the search and grab all their details 612*76ce1169SAndreas Gohr $searchParams = ""; 613*76ce1169SAndreas Gohr if ($searchField) { 614*76ce1169SAndreas Gohr $searchParams = "(" . $searchField . "=" . $searchFilter . ")"; 615*76ce1169SAndreas Gohr } 616*76ce1169SAndreas Gohr $filter = "(&(objectClass=user)(samaccounttype=" . adLDAP::ADLDAP_NORMAL_ACCOUNT .")(objectCategory=person)" . $searchParams . ")"; 617*76ce1169SAndreas Gohr $fields = array("samaccountname","displayname"); 618*76ce1169SAndreas Gohr $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields); 619*76ce1169SAndreas Gohr $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr); 620*76ce1169SAndreas Gohr 621*76ce1169SAndreas Gohr $usersArray = array(); 622*76ce1169SAndreas Gohr for ($i=0; $i < $entries["count"]; $i++) { 623*76ce1169SAndreas Gohr if ($includeDescription && strlen($entries[$i]["displayname"][0]) > 0) { 624*76ce1169SAndreas Gohr $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["displayname"][0]; 625*76ce1169SAndreas Gohr } 626*76ce1169SAndreas Gohr else if ($includeDescription) { 627*76ce1169SAndreas Gohr $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["samaccountname"][0]; 628*76ce1169SAndreas Gohr } 629*76ce1169SAndreas Gohr else { 630*76ce1169SAndreas Gohr array_push($usersArray, $entries[$i]["samaccountname"][0]); 631*76ce1169SAndreas Gohr } 632*76ce1169SAndreas Gohr } 633*76ce1169SAndreas Gohr if ($sorted){ 634*76ce1169SAndreas Gohr asort($usersArray); 635*76ce1169SAndreas Gohr } 636*76ce1169SAndreas Gohr return ($usersArray); 637*76ce1169SAndreas Gohr } 638*76ce1169SAndreas Gohr 639*76ce1169SAndreas Gohr /** 640*76ce1169SAndreas Gohr * Move a user account to a different OU 641*76ce1169SAndreas Gohr * 642*76ce1169SAndreas Gohr * @param string $username The username to move (please be careful here!) 643*76ce1169SAndreas Gohr * @param array $container The container or containers to move the user to (please be careful here!). 644*76ce1169SAndreas Gohr * accepts containers in 1. parent 2. child order 645*76ce1169SAndreas Gohr * @return array 646*76ce1169SAndreas Gohr */ 647*76ce1169SAndreas Gohr public function move($username, $container) 648*76ce1169SAndreas Gohr { 649*76ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()) { return false; } 650*76ce1169SAndreas Gohr if ($username === null) { return "Missing compulsory field [username]"; } 651*76ce1169SAndreas Gohr if ($container === null) { return "Missing compulsory field [container]"; } 652*76ce1169SAndreas Gohr if (!is_array($container)) { return "Container must be an array"; } 653*76ce1169SAndreas Gohr 654*76ce1169SAndreas Gohr $userInfo = $this->info($username, array("*")); 655*76ce1169SAndreas Gohr $dn = $userInfo[0]['distinguishedname'][0]; 656*76ce1169SAndreas Gohr $newRDn = "cn=" . $username; 657*76ce1169SAndreas Gohr $container = array_reverse($container); 658*76ce1169SAndreas Gohr $newContainer = "ou=" . implode(",ou=",$container); 659*76ce1169SAndreas Gohr $newBaseDn = strtolower($newContainer) . "," . $this->adldap->getBaseDn(); 660*76ce1169SAndreas Gohr $result = @ldap_rename($this->adldap->getLdapConnection(), $dn, $newRDn, $newBaseDn, true); 661*76ce1169SAndreas Gohr if ($result !== true) { 662*76ce1169SAndreas Gohr return false; 663*76ce1169SAndreas Gohr } 664*76ce1169SAndreas Gohr return true; 665*76ce1169SAndreas Gohr } 666*76ce1169SAndreas Gohr 667*76ce1169SAndreas Gohr /** 668*76ce1169SAndreas Gohr * Get the last logon time of any user as a Unix timestamp 669*76ce1169SAndreas Gohr * 670*76ce1169SAndreas Gohr * @param string $username 671*76ce1169SAndreas Gohr * @return long $unixTimestamp 672*76ce1169SAndreas Gohr */ 673*76ce1169SAndreas Gohr public function getLastLogon($username) { 674*76ce1169SAndreas Gohr if (!$this->adldap->getLdapBind()) { return false; } 675*76ce1169SAndreas Gohr if ($username === null) { return "Missing compulsory field [username]"; } 676*76ce1169SAndreas Gohr $userInfo = $this->info($username, array("lastLogonTimestamp")); 677*76ce1169SAndreas Gohr $lastLogon = adLDAPUtils::convertWindowsTimeToUnixTime($userInfo[0]['lastLogonTimestamp'][0]); 678*76ce1169SAndreas Gohr return $lastLogon; 679*76ce1169SAndreas Gohr } 680*76ce1169SAndreas Gohr 681*76ce1169SAndreas Gohr} 682*76ce1169SAndreas Gohr?> 683