[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/lib/plugins/authad/adLDAP/classes/ -> adLDAPUsers.php (source)

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