diff --git a/composer.json b/composer.json index 5ee4bab..98d7c14 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "adldap/adldap", + "name": "cupdx/adldap", "type": "library", "description": "adLDAP, PHP LDAP library for manipulating Active Directory", "keywords": [ diff --git a/examples/menu.php b/examples/menu.php index f0fac7c..c8b41e5 100644 --- a/examples/menu.php +++ b/examples/menu.php @@ -1,4 +1,10 @@ main) page. + */ + session_start(); ?> diff --git a/lib/adLDAP/adLDAP.php b/lib/adLDAP/adLDAP.php index f36c3aa..772aeac 100644 --- a/lib/adLDAP/adLDAP.php +++ b/lib/adLDAP/adLDAP.php @@ -1,30 +1,30 @@ ldapConnection) { - return $this->ldapConnection; - } - return false; - } - - /** - * Get the bind status - * - * @return bool - */ - public function getLdapBind() { - return $this->ldapBind; - } - - /** - * Get the current base DN - * - * @return string - */ - public function getBaseDn() { - return $this->baseDn; - } - - /** - * Set the current base DN - * - * @param string $baseDn - */ - public function setBaseDn($baseDn) { - $this->baseDn = $baseDn; - } - - /** - * The group class - * - * @var \adLDAP\classes\adLDAPGroups - */ - protected $groupClass; - - /** - * Get the group class interface - * - * @return \adLDAP\classes\adLDAPGroups - */ - public function group() { - if (!$this->groupClass) { - $this->groupClass = new \adLDAP\classes\adLDAPGroups($this); - } - return $this->groupClass; - } - - /** - * The user class - * - * @var \adLDAP\classes\adLDAPUsers - */ - protected $userClass; - - /** - * Get the userclass interface - * - * @return \adLDAP\classes\adLDAPUsers - */ - public function user() { - if (!$this->userClass) { - $this->userClass = new \adLDAP\classes\adLDAPUsers($this); - } - return $this->userClass; - } - - /** - * The folders class - * - * @var \adLDAP\classes\adLDAPFolders - */ - protected $folderClass; - - /** - * Get the folder class interface - * - * @return \adLDAP\classes\adLDAPFolders - */ - public function folder() { - if (!$this->folderClass) { - $this->folderClass = new \adLDAP\classes\adLDAPFolders($this); - } - return $this->folderClass; - } - - /** - * The utils class - * - * @var \adLDAP\classes\adLDAPUtils - */ - protected $utilClass; - - /** - * Get the utils class interface - * - * @return \adLDAP\classes\adLDAPUtils - */ - public function utilities() { - if (!$this->utilClass) { - $this->utilClass = new \adLDAP\classes\adLDAPUtils($this); - } - return $this->utilClass; - } - - /** - * The contacts class - * - * @var \adLDAP\classes\adLDAPContacts - */ - protected $contactClass; - - /** - * Get the contacts class interface - * - * @return \adLDAP\classes\adLDAPContacts - */ - public function contact() { - if (!$this->contactClass) { - $this->contactClass = new \adLDAP\classes\adLDAPContacts($this); - } - return $this->contactClass; - } - - /** - * The exchange class - * - * @var \adLDAP\classes\adLDAPExchange - */ - protected $exchangeClass; - - /** - * Get the exchange class interface - * - * @return \adLDAP\classes\adLDAPExchange - */ - public function exchange() { - if (!$this->exchangeClass) { - $this->exchangeClass = new \adLDAP\classes\adLDAPExchange($this); - } - return $this->exchangeClass; - } - - /** - * The computers class - * - * @var \adLDAP\classes\adLDAPComputers - */ - protected $computerClass; - - /** - * Get the computers class interface - * - * @return \adLDAP\classes\adLDAPComputers - */ - public function computer() { - if (!$this->computerClass) { - $this->computerClass = new \adLDAP\classes\adLDAPComputers($this); - } - return $this->computerClass; - } - - /** - * Getters and Setters - */ - - /** - * Set the account suffix - * - * @param string $accountSuffix - * @return void - */ - public function setAccountSuffix($accountSuffix) { - $this->accountSuffix = $accountSuffix; - } - - /** - * Get the account suffix - * - * @return string - */ - public function getAccountSuffix() { - return $this->accountSuffix; - } - - /** - * Set the domain controllers array - * - * @param array $domainControllers - * @return void - */ - public function setDomainControllers(array $domainControllers) { - $this->domainControllers = $domainControllers; - } - - /** - * Get the list of domain controllers - * - * @return void - */ - public function getDomainControllers() { - return $this->domainControllers; - } - - /** - * Sets the port number your domain controller communicates over - * - * @param int $adPort - */ - public function setPort($adPort) { - $this->adPort = $adPort; - } - - /** - * Gets the port number your domain controller communicates over - * - * @return int - */ - public function getPort() { - return $this->adPort; - } - - /** - * Set the username of an account with higher priviledges - * - * @param string $adminUsername - * @return void - */ - public function setAdminUsername($adminUsername) { - $this->adminUsername = $adminUsername; - } - - /** - * Get the username of the account with higher priviledges - * - * This will throw an exception for security reasons - */ - public function getAdminUsername() { - throw new adLDAPException('For security reasons you cannot access the domain administrator account details'); - } - - /** - * Set the password of an account with higher priviledges - * - * @param string $adminPassword - * @return void - */ - public function setAdminPassword($adminPassword) { - $this->adminPassword = $adminPassword; - } - - /** - * Get the password of the account with higher priviledges - * - * This will throw an exception for security reasons - */ - public function getAdminPassword() { - throw new adLDAPException('For security reasons you cannot access the domain administrator account details'); - } - - /** - * Set whether to detect the true primary group - * - * @param bool $realPrimaryGroup - * @return void - */ - public function setRealPrimaryGroup($realPrimaryGroup) { - $this->realPrimaryGroup = $realPrimaryGroup; - } - - /** - * Get the real primary group setting - * - * @return bool - */ - public function getRealPrimaryGroup() { - return $this->realPrimaryGroup; - } - - /** - * Set whether to use SSL - * - * @param bool $useSSL - * @return void - */ - public function setUseSSL($useSSL) { - $this->useSSL = $useSSL; - // Set the default port correctly - if($this->useSSL) { - $this->setPort(self::ADLDAP_LDAPS_PORT); - } - else { - $this->setPort(self::ADLDAP_LDAP_PORT); - } - } - - /** - * Get the SSL setting - * - * @return bool - */ - public function getUseSSL() { - return $this->useSSL; - } - - /** - * Set whether to use TLS - * - * @param bool $useTLS - * @return void - */ - public function setUseTLS($useTLS) { - $this->useTLS = $useTLS; - } - - /** - * Get the TLS setting - * - * @return bool - */ - public function getUseTLS() { - return $this->useTLS; - } - - /** - * Set whether to use SSO - * Requires ldap_sasl_bind support. Be sure --with-ldap-sasl is used when configuring PHP otherwise this function will be undefined. - * - * @param bool $useSSO - * @return void - */ - public function setUseSSO($useSSO) { - if ($useSSO === true && !$this->ldapSaslSupported()) { - throw new adLDAPException('No LDAP SASL support for PHP. See: http://www.php.net/ldap_sasl_bind'); - } - $this->useSSO = $useSSO; - } - - /** - * Get the SSO setting - * - * @return bool - */ - public function getUseSSO() { - return $this->useSSO; - } - - /** - * Set whether to lookup recursive groups - * - * @param bool $recursiveGroups - * @return void - */ - public function setRecursiveGroups($recursiveGroups) { - $this->recursiveGroups = $recursiveGroups; - } - - /** - * Get the recursive groups setting - * - * @return bool - */ - public function getRecursiveGroups() { - return $this->recursiveGroups; - } - - /** - * Default Constructor - * - * Tries to bind to the AD domain over LDAP or LDAPs - * - * @param array $options Array of options to pass to the constructor - * @throws \Exception - if unable to bind to Domain Controller - * @return bool - */ - function __construct($options = array()) { - // You can specifically overide any of the default configuration options setup above - if (count($options)>0) { - if (array_key_exists("account_suffix",$options)) { $this->accountSuffix = $options["account_suffix"]; } - if (array_key_exists("base_dn",$options)) { $this->baseDn = $options["base_dn"]; } - if (array_key_exists("domain_controllers",$options)) { - if (!is_array($options["domain_controllers"])) { - throw new adLDAPException('[domain_controllers] option must be an array'); - } - $this->domainControllers = $options["domain_controllers"]; - } - if (array_key_exists("admin_username",$options)) { $this->adminUsername = $options["admin_username"]; } - if (array_key_exists("admin_password",$options)) { $this->adminPassword = $options["admin_password"]; } - if (array_key_exists("real_primarygroup",$options)) { $this->realPrimaryGroup = $options["real_primarygroup"]; } - if (array_key_exists("use_ssl",$options)) { $this->setUseSSL($options["use_ssl"]); } - if (array_key_exists("use_tls",$options)) { $this->useTLS = $options["use_tls"]; } - if (array_key_exists("recursive_groups",$options)) { $this->recursiveGroups = $options["recursive_groups"]; } - if (array_key_exists("follow_referrals", $options)) { $this->followReferrals = $options["follow_referrals"]; } - if (array_key_exists("ad_port",$options)) { $this->setPort($options["ad_port"]); } - if (array_key_exists("sso",$options)) { - $this->setUseSSO($options["sso"]); - if (!$this->ldapSaslSupported()) { - $this->setUseSSO(false); - } - } - } - - if ($this->ldapSupported() === false) { - throw new adLDAPException('No LDAP support for PHP. See: http://www.php.net/ldap'); - } - - return $this->connect(); - } - - /** - * Default Destructor - * - * Closes the LDAP connection - * - * @return void - */ - function __destruct() { - $this->close(); - } - - /** - * Connects and Binds to the Domain Controller - * - * @return bool - */ - public function connect() { - // Connect to the AD/LDAP server as the username/password - $domainController = $this->randomController(); - if ($this->useSSL) { - $this->ldapConnection = ldap_connect("ldaps://" . $domainController, $this->adPort); - } else { - $this->ldapConnection = ldap_connect("ldap://" . $domainController, $this->adPort); - } - - // Set some ldap options for talking to AD - ldap_set_option($this->ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3); - ldap_set_option($this->ldapConnection, LDAP_OPT_REFERRALS, $this->followReferrals); - - if ($this->useTLS) { - ldap_start_tls($this->ldapConnection); - } - - // Bind as a domain admin if they've set it up - if ($this->adminUsername !== NULL && $this->adminPassword !== NULL) { - $this->ldapBind = @ldap_bind($this->ldapConnection, $this->adminUsername . $this->accountSuffix, $this->adminPassword); - if (!$this->ldapBind) { - if ($this->useSSL && !$this->useTLS) { - // If you have problems troubleshooting, remove the @ character from the ldapldapBind command above to get the actual error message - throw new adLDAPException('Bind to Active Directory failed. Either the LDAPs connection failed or the login credentials are incorrect. AD said: ' . $this->getLastError()); - } - else { - throw new adLDAPException('Bind to Active Directory failed. Check the login credentials and/or server details. AD said: ' . $this->getLastError()); - } - } - } - if ($this->useSSO && $_SERVER['REMOTE_USER'] && $this->adminUsername === null && $_SERVER['KRB5CCNAME']) { - putenv("KRB5CCNAME=" . $_SERVER['KRB5CCNAME']); - $this->ldapBind = @ldap_sasl_bind($this->ldapConnection, NULL, NULL, "GSSAPI"); - if (!$this->ldapBind) { - throw new adLDAPException('Rebind to Active Directory failed. AD said: ' . $this->getLastError()); - } - else { - return true; - } - } - - if ($this->baseDn == NULL) { - $this->baseDn = $this->findBaseDn(); - } - return true; - } - - /** - * Closes the LDAP connection - * - * @return void - */ - public function close() { - if ($this->ldapConnection) { - @ldap_close($this->ldapConnection); - } - } - - /** - * Validate a user's login credentials - * - * @param string $username A user's AD username - * @param string $password A user's AD password - * @param bool optional $preventRebind - * @return bool - */ - public function authenticate($username, $password, $preventRebind = false) { - // Prevent null binding - if ($username === NULL || $password === NULL) { return false; } - if (empty($username) || empty($password)) { return false; } - - // Allow binding over SSO for Kerberos - if ($this->useSSO && $_SERVER['REMOTE_USER'] && $_SERVER['REMOTE_USER'] == $username && $this->adminUsername === NULL && $_SERVER['KRB5CCNAME']) { - putenv("KRB5CCNAME=" . $_SERVER['KRB5CCNAME']); - $this->ldapBind = @ldap_sasl_bind($this->ldapConnection, NULL, NULL, "GSSAPI"); - if (!$this->ldapBind) { - throw new adLDAPException('Rebind to Active Directory failed. AD said: ' . $this->getLastError()); - } - else { - return true; - } - } - - // Bind as the user - $ret = true; - $this->ldapBind = @ldap_bind($this->ldapConnection, $username . $this->accountSuffix, $password); - if (!$this->ldapBind) { - $ret = false; - } - - // Cnce we've checked their details, kick back into admin mode if we have it - if ($this->adminUsername !== NULL && !$preventRebind) { - $this->ldapBind = @ldap_bind($this->ldapConnection, $this->adminUsername . $this->accountSuffix , $this->adminPassword); - if (!$this->ldapBind) { - // This should never happen in theory - throw new adLDAPException('Rebind to Active Directory failed. AD said: ' . $this->getLastError()); - } - } - return $ret; - } - - /** - * Return a list of all found objects (except computer) in AD - * $search has to match either cn, displayname, samaccountname or sn - * - * @param bool $includeDescription Return a description,cn, displayname and distinguishedname of the user - * @param string $search Search parameter - * @param bool $sorted Sort the user accounts - * @return array, if $includeDescription=true then a multi-dimensional array - */ - public function search($includeDescription = false, $search = "*", $sorted = true) { - if (!$this->getLdapBind()) { return false; } - - // Perform the search and grab all their details - //$filter = "(|(cn=" . $search . ")(displayname=" . $search . ")(samaccountname=" . $search . "))"; - $filter = "(&(!(objectClass=computer))(|(anr=" . $search . ")))"; - $fields = array("cn","description","displayname","distinguishedname","samaccountname"); - $sr = ldap_search($this->getLdapConnection(), $this->getBaseDn(), $filter, $fields); - $entries = ldap_get_entries($this->getLdapConnection(), $sr); - - $objectArray = array(); - for ($i=0; $i<$entries["count"]; $i++) { - if ($includeDescription && strlen($entries[$i]["description"][0])>0) { - $objectArray[$entries[$i]["samaccountname"][0]] = array($entries[$i]["cn"][0],$entries[$i]["description"][0],$entries[$i]["displayname"][0],$entries[$i]["distinguishedname"][0]); - } elseif ($includeDescription) { - // description is set to displayname if no description is present - $objectArray[$entries[$i]["samaccountname"][0]] = array($entries[$i]["cn"][0],$entries[$i]["displayname"][0],$entries[$i]["displayname"][0],$entries[$i]["distinguishedname"][0]); - } else { - array_push($objectArray, $entries[$i]["samaccountname"][0]); - } - } - if ($sorted) { - asort($objectArray); - } - return $objectArray; - } - - /** - * Returns objectClass in an array - * - * @param string $distinguisedName The full DN of a contact - * @return array - */ - public function getObjectClass($distinguishedName) { - if ($distinguishedName === NULL) { return false; } - if (!$this->getLdapBind()) { return false; } - - $filter = "distinguishedName=" . $this->utilities()->ldapSlashes($distinguishedName); - - $fields = array("objectclass"); - $sr = ldap_search($this->getLdapConnection(), $this->getBaseDn(), $filter, $fields); - $entries = ldap_get_entries($this->getLdapConnection(), $sr); - - $objects = array(); - for ($i=0; $i<$entries[0]["objectclass"]["count"]; $i++) { - array_push($objects, $entries[0]["objectclass"][$i]); - } - return $objects; - } - - /** - * Find the Base DN of your domain controller - * - * @return string - */ - public function findBaseDn() { - $namingContext = $this->getRootDse(array('defaultnamingcontext')); - return $namingContext[0]['defaultnamingcontext'][0]; - } - - /** - * Get the RootDSE properties from a domain controller - * - * @param array $attributes The attributes you wish to query e.g. defaultnamingcontext - * @return array - */ - public function getRootDse($attributes = array("*", "+")) { - if (!$this->ldapBind) { return (false); } - - $sr = @ldap_read($this->ldapConnection, NULL, 'objectClass=*', $attributes); - $entries = @ldap_get_entries($this->ldapConnection, $sr); - return $entries; - } - - /** - * Get last error from Active Directory - * - * This function gets the last message from Active Directory - * This may indeed be a 'Success' message but if you get an unknown error - * it might be worth calling this function to see what errors were raised - * - * return string - */ - public function getLastError() { - return @ldap_error($this->ldapConnection); - } - - /** - * Detect LDAP support in php - * - * @return bool - */ - protected function ldapSupported() { - if (!function_exists('ldap_connect')) { - return false; - } - return true; - } - - /** - * Detect ldap_sasl_bind support in PHP - * - * @return bool - */ - protected function ldapSaslSupported() { - if (!function_exists('ldap_sasl_bind')) { - return false; - } - return true; - } - - /** - * Schema - * - * @param array $attributes Attributes to be queried - * @return array - */ - public function adldap_schema($attributes) { - - // LDAP doesn't like NULL attributes, only set them if they have values - // If you wish to remove an attribute you should set it to a space - // TO DO: Adapt user_modify to use ldap_mod_delete to remove a NULL attribute - $mod=array(); - - // Check every attribute to see if it contains 8bit characters and then UTF8 encode them - array_walk($attributes, array($this, 'encode8bit')); - - if (isset($attributes["address_city"])){ $mod["l"][0]=$attributes["address_city"]; } - if (isset($attributes["address_code"])){ $mod["postalCode"][0]=$attributes["address_code"]; } - //if ($attributes["address_country"]){ $mod["countryCode"][0]=$attributes["address_country"]; } // use country codes? - if (isset($attributes["address_country"])){ $mod["c"][0]=$attributes["address_country"]; } - if (isset($attributes["address_pobox"])){ $mod["postOfficeBox"][0]=$attributes["address_pobox"]; } - if (isset($attributes["address_state"])){ $mod["st"][0]=$attributes["address_state"]; } - if (isset($attributes["address_street"])){ $mod["streetAddress"][0]=$attributes["address_street"]; } - if (isset($attributes["company"])){ $mod["company"][0]=$attributes["company"]; } - if (isset($attributes["change_password"])){ $mod["pwdLastSet"][0]=0; } - if (isset($attributes["department"])){ $mod["department"][0]=$attributes["department"]; } - if (isset($attributes["description"])){ $mod["description"][0]=$attributes["description"]; } - if (isset($attributes["display_name"])){ $mod["displayName"][0]=$attributes["display_name"]; } - if (isset($attributes["email"])){ $mod["mail"][0]=$attributes["email"]; } - if (isset($attributes["expires"])){ $mod["accountExpires"][0]=$attributes["expires"]; } //unix epoch format? - if (isset($attributes["firstname"])){ $mod["givenName"][0]=$attributes["firstname"]; } - if (isset($attributes["home_directory"])){ $mod["homeDirectory"][0]=$attributes["home_directory"]; } - if (isset($attributes["home_drive"])){ $mod["homeDrive"][0]=$attributes["home_drive"]; } - if (isset($attributes["initials"])){ $mod["initials"][0]=$attributes["initials"]; } - if (isset($attributes["logon_name"])){ $mod["userPrincipalName"][0]=$attributes["logon_name"]; } - if (isset($attributes["manager"])){ $mod["manager"][0]=$attributes["manager"]; } //UNTESTED ***Use DistinguishedName*** - if (isset($attributes["office"])){ $mod["physicalDeliveryOfficeName"][0]=$attributes["office"]; } - if (isset($attributes["password"])){ $mod["unicodePwd"][0]=$this->user()->encodePassword($attributes["password"]); } - if (isset($attributes["profile_path"])){ $mod["profilepath"][0]=$attributes["profile_path"]; } - if (isset($attributes["script_path"])){ $mod["scriptPath"][0]=$attributes["script_path"]; } - if (isset($attributes["surname"])){ $mod["sn"][0]=$attributes["surname"]; } - if (isset($attributes["title"])){ $mod["title"][0]=$attributes["title"]; } - if (isset($attributes["telephone"])){ $mod["telephoneNumber"][0]=$attributes["telephone"]; } - if (isset($attributes["mobile"])){ $mod["mobile"][0]=$attributes["mobile"]; } - if (isset($attributes["pager"])){ $mod["pager"][0]=$attributes["pager"]; } - if (isset($attributes["ipphone"])){ $mod["ipphone"][0]=$attributes["ipphone"]; } - if (isset($attributes["web_page"])){ $mod["wWWHomePage"][0]=$attributes["web_page"]; } - if (isset($attributes["fax"])){ $mod["facsimileTelephoneNumber"][0]=$attributes["fax"]; } - if (isset($attributes["enabled"])){ $mod["userAccountControl"][0]=$attributes["enabled"]; } - if (isset($attributes["homephone"])){ $mod["homephone"][0]=$attributes["homephone"]; } - - // Distribution List specific schema - if (isset($attributes["group_sendpermission"])){ $mod["dlMemSubmitPerms"][0]=$attributes["group_sendpermission"]; } - if (isset($attributes["group_rejectpermission"])){ $mod["dlMemRejectPerms"][0]=$attributes["group_rejectpermission"]; } - - // Exchange Schema - if (isset($attributes["exchange_homemdb"])){ $mod["homeMDB"][0]=$attributes["exchange_homemdb"]; } - if (isset($attributes["exchange_mailnickname"])){ $mod["mailNickname"][0]=$attributes["exchange_mailnickname"]; } - if (isset($attributes["exchange_proxyaddress"])){ $mod["proxyAddresses"][0]=$attributes["exchange_proxyaddress"]; } - if (isset($attributes["exchange_usedefaults"])){ $mod["mDBUseDefaults"][0]=$attributes["exchange_usedefaults"]; } - if (isset($attributes["exchange_policyexclude"])){ $mod["msExchPoliciesExcluded"][0]=$attributes["exchange_policyexclude"]; } - if (isset($attributes["exchange_policyinclude"])){ $mod["msExchPoliciesIncluded"][0]=$attributes["exchange_policyinclude"]; } - if (isset($attributes["exchange_addressbook"])){ $mod["showInAddressBook"][0]=$attributes["exchange_addressbook"]; } - if (isset($attributes["exchange_altrecipient"])){ $mod["altRecipient"][0]=$attributes["exchange_altrecipient"]; } - if (isset($attributes["exchange_deliverandredirect"])){ $mod["deliverAndRedirect"][0]=$attributes["exchange_deliverandredirect"]; } - - // This schema is designed for contacts - if (isset($attributes["exchange_hidefromlists"])){ $mod["msExchHideFromAddressLists"][0]=$attributes["exchange_hidefromlists"]; } - if (isset($attributes["contact_email"])){ $mod["targetAddress"][0]=$attributes["contact_email"]; } - - //echo ("
"); print_r($mod);
- /*
- // modifying a name is a bit fiddly
- if ($attributes["firstname"] && $attributes["surname"]){
- $mod["cn"][0]=$attributes["firstname"]." ".$attributes["surname"];
- $mod["displayname"][0]=$attributes["firstname"]." ".$attributes["surname"];
- $mod["name"][0]=$attributes["firstname"]." ".$attributes["surname"];
- }
- */
-
- if (count($mod)==0) { return (false); }
- return ($mod);
- }
-
- /**
- * Convert 8bit characters e.g. accented characters to UTF8 encoded characters
- */
- protected function encode8Bit(&$item, $key) {
- $encode = false;
- if (is_string($item)) {
- for ($i=0; $i> 7) {
- $encode = true;
- }
- }
- }
- if ($encode === true && $key != 'password') {
- $item = utf8_encode($item);
- }
- }
-
- /**
- * Select a random domain controller from your domain controller array
- *
- * @return string
- */
- protected function randomController() {
- mt_srand(doubleval(microtime()) * 100000000); // For older PHP versions
- /*if (sizeof($this->domainControllers) > 1) {
- $adController = $this->domainControllers[array_rand($this->domainControllers)];
- // Test if the controller is responding to pings
- $ping = $this->pingController($adController);
- if ($ping === false) {
- // Find the current key in the domain controllers array
- $key = array_search($adController, $this->domainControllers);
- // Remove it so that we don't end up in a recursive loop
- unset($this->domainControllers[$key]);
- // Select a new controller
- return $this->randomController();
- }
- else {
- return ($adController);
- }
- } */
- return $this->domainControllers[array_rand($this->domainControllers)];
- }
-
- /**
- * Test basic connectivity to controller
- *
- * @return bool
- */
- protected function pingController($host) {
- $port = $this->adPort;
- fsockopen($host, $port, $errno, $errstr, 10);
- if ($errno > 0) {
- return false;
- }
- return true;
- }
+
+ /**
+ * Connection and bind default variables
+ *
+ * @var mixed
+ * @var mixed
+ */
+ protected $ldapConnection;
+ protected $ldapBind;
+
+ /**
+ * Get the active LDAP Connection
+ *
+ * @return resource|bool
+ */
+ public function getLdapConnection()
+ {
+ if ($this->ldapConnection)
+ {
+ return $this->ldapConnection;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the bind status
+ *
+ * @return bool
+ */
+ public function getLdapBind()
+ {
+ return $this->ldapBind;
+ }
+
+ /**
+ * Get the current base DN
+ *
+ * @return string
+ */
+ public function getBaseDn()
+ {
+ return $this->baseDn;
+ }
+
+ /**
+ * Set the current base DN
+ *
+ * @param string $baseDn
+ */
+ public function setBaseDn($baseDn)
+ {
+ $this->baseDn = $baseDn;
+ }
+
+ /**
+ * The group class
+ *
+ * @var \adLDAP\classes\adLDAPGroups
+ */
+ protected $groupClass;
+
+ /**
+ * Get the group class interface
+ *
+ * @return \adLDAP\classes\adLDAPGroups
+ */
+ public function group()
+ {
+ if (!$this->groupClass)
+ {
+ $this->groupClass = new \adLDAP\classes\adLDAPGroups($this);
+ }
+ return $this->groupClass;
+ }
+
+ /**
+ * The user class
+ *
+ * @var \adLDAP\classes\adLDAPUsers
+ */
+ protected $userClass;
+
+ /**
+ * Get the userclass interface
+ *
+ * @return \adLDAP\classes\adLDAPUsers
+ */
+ public function user()
+ {
+ if (!$this->userClass)
+ {
+ $this->userClass = new \adLDAP\classes\adLDAPUsers($this);
+ }
+ return $this->userClass;
+ }
+
+ /**
+ * The folders class
+ *
+ * @var \adLDAP\classes\adLDAPFolders
+ */
+ protected $folderClass;
+
+ /**
+ * Get the folder class interface
+ *
+ * @return \adLDAP\classes\adLDAPFolders
+ */
+ public function folder()
+ {
+ if (!$this->folderClass)
+ {
+ $this->folderClass = new \adLDAP\classes\adLDAPFolders($this);
+ }
+ return $this->folderClass;
+ }
+
+ /**
+ * The utils class
+ *
+ * @var \adLDAP\classes\adLDAPUtils
+ */
+ protected $utilClass;
+
+ /**
+ * Get the utils class interface
+ *
+ * @return \adLDAP\classes\adLDAPUtils
+ */
+ public function utilities()
+ {
+ if (!$this->utilClass)
+ {
+ $this->utilClass = new \adLDAP\classes\adLDAPUtils($this);
+ }
+ return $this->utilClass;
+ }
+
+ /**
+ * The contacts class
+ *
+ * @var \adLDAP\classes\adLDAPContacts
+ */
+ protected $contactClass;
+
+ /**
+ * Get the contacts class interface
+ *
+ * @return \adLDAP\classes\adLDAPContacts
+ */
+ public function contact()
+ {
+ if (!$this->contactClass)
+ {
+ $this->contactClass = new \adLDAP\classes\adLDAPContacts($this);
+ }
+ return $this->contactClass;
+ }
+
+ /**
+ * The exchange class
+ *
+ * @var \adLDAP\classes\adLDAPExchange
+ */
+ protected $exchangeClass;
+
+ /**
+ * Get the exchange class interface
+ *
+ * @return \adLDAP\classes\adLDAPExchange
+ */
+ public function exchange()
+ {
+ if (!$this->exchangeClass)
+ {
+ $this->exchangeClass = new \adLDAP\classes\adLDAPExchange($this);
+ }
+ return $this->exchangeClass;
+ }
+
+ /**
+ * The computers class
+ *
+ * @var \adLDAP\classes\adLDAPComputers
+ */
+ protected $computerClass;
+
+ /**
+ * Get the computers class interface
+ *
+ * @return \adLDAP\classes\adLDAPComputers
+ */
+ public function computer()
+ {
+ if (!$this->computerClass)
+ {
+ $this->computerClass = new \adLDAP\classes\adLDAPComputers($this);
+ }
+ return $this->computerClass;
+ }
+
+ /**
+ * Getters and Setters
+ */
+
+ /**
+ * Set the account suffix
+ *
+ * @param string $accountSuffix
+ * @return void
+ */
+ public function setAccountSuffix($accountSuffix)
+ {
+ $this->accountSuffix = $accountSuffix;
+ }
+
+ /**
+ * Get the account suffix
+ *
+ * @return string
+ */
+ public function getAccountSuffix()
+ {
+ return $this->accountSuffix;
+ }
+
+ /**
+ * Set the domain controllers array
+ *
+ * @param array $domainControllers
+ * @return void
+ */
+ public function setDomainControllers(array $domainControllers)
+ {
+ $this->domainControllers = $domainControllers;
+ }
+
+ /**
+ * Get the list of domain controllers
+ *
+ * @return array
+ */
+ public function getDomainControllers()
+ {
+ return $this->domainControllers;
+ }
+
+ /**
+ * Sets the port number your domain controller communicates over
+ *
+ * @param int $adPort
+ */
+ public function setPort($adPort)
+ {
+ $this->adPort = $adPort;
+ }
+
+ /**
+ * Gets the port number your domain controller communicates over
+ *
+ * @return int
+ */
+ public function getPort()
+ {
+ return $this->adPort;
+ }
+
+ /**
+ * Set the username of an account with higher priviledges
+ *
+ * @param string $adminUsername
+ * @return void
+ */
+ public function setAdminUsername($adminUsername)
+ {
+ $this->adminUsername = $adminUsername;
+ }
+
+ /**
+ * Get the username of the account with higher priviledges
+ *
+ * This will throw an exception for security reasons
+ */
+ public function getAdminUsername()
+ {
+ throw new adLDAPException('For security reasons you cannot access the domain administrator account details');
+ }
+
+ /**
+ * Set the password of an account with higher priviledges
+ *
+ * @param string $adminPassword
+ * @return void
+ */
+ public function setAdminPassword($adminPassword)
+ {
+ $this->adminPassword = $adminPassword;
+ }
+
+ /**
+ * Get the password of the account with higher priviledges
+ *
+ * This will throw an exception for security reasons
+ */
+ public function getAdminPassword()
+ {
+ throw new adLDAPException('For security reasons you cannot access the domain administrator account details');
+ }
+
+ /**
+ * Set whether to detect the true primary group
+ *
+ * @param bool $realPrimaryGroup
+ * @return void
+ */
+ public function setRealPrimaryGroup($realPrimaryGroup)
+ {
+ $this->realPrimaryGroup = $realPrimaryGroup;
+ }
+
+ /**
+ * Get the real primary group setting
+ *
+ * @return bool
+ */
+ public function getRealPrimaryGroup()
+ {
+ return $this->realPrimaryGroup;
+ }
+
+ /**
+ * Set whether to use SSL
+ *
+ * @param bool $useSSL
+ * @return void
+ */
+ public function setUseSSL($useSSL)
+ {
+ $this->useSSL = $useSSL;
+ // Set the default port correctly
+ if ($this->useSSL)
+ {
+ $this->setPort(self::ADLDAP_LDAPS_PORT);
+ }
+ else
+ {
+ $this->setPort(self::ADLDAP_LDAP_PORT);
+ }
+ }
+
+ /**
+ * Get the SSL setting
+ *
+ * @return bool
+ */
+ public function getUseSSL()
+ {
+ return $this->useSSL;
+ }
+
+ /**
+ * Set whether to use TLS
+ *
+ * @param bool $useTLS
+ * @return void
+ */
+ public function setUseTLS($useTLS)
+ {
+ $this->useTLS = $useTLS;
+ }
+
+ /**
+ * Get the TLS setting
+ *
+ * @return bool
+ */
+ public function getUseTLS()
+ {
+ return $this->useTLS;
+ }
+
+ /**
+ * Set whether to use SSO
+ * Requires ldap_sasl_bind support. Be sure --with-ldap-sasl is used when configuring PHP otherwise this function will be undefined.
+ *
+ * @param bool $useSSO
+ * @throws adLDAPException
+ */
+ public function setUseSSO($useSSO)
+ {
+ if ($useSSO === true && !$this->ldapSaslSupported())
+ {
+ throw new adLDAPException('No LDAP SASL support for PHP. See: http://www.php.net/ldap_sasl_bind');
+ }
+ $this->useSSO = $useSSO;
+ }
+
+ /**
+ * Get the SSO setting
+ *
+ * @return bool
+ */
+ public function getUseSSO()
+ {
+ return $this->useSSO;
+ }
+
+ /**
+ * Set whether to lookup recursive groups
+ *
+ * @param bool $recursiveGroups
+ * @return void
+ */
+ public function setRecursiveGroups($recursiveGroups)
+ {
+ $this->recursiveGroups = $recursiveGroups;
+ }
+
+ /**
+ * Get the recursive groups setting
+ *
+ * @return bool
+ */
+ public function getRecursiveGroups()
+ {
+ return $this->recursiveGroups;
+ }
+
+ /**
+ * Default Constructor
+ *
+ * Tries to bind to the AD domain over LDAP or LDAPs
+ *
+ * @param array $options Array of options to pass to the constructor
+ * @throws adLDAPException - if unable to bind to Domain Controller
+ * @return bool
+ */
+ function __construct($options = array())
+ {
+ // You can specifically overide any of the default configuration options setup above
+ if (count($options) > 0)
+ {
+ if (array_key_exists("account_suffix", $options))
+ {
+ $this->accountSuffix = $options["account_suffix"];
+ }
+ if (array_key_exists("base_dn", $options))
+ {
+ $this->baseDn = $options["base_dn"];
+ }
+ if (array_key_exists("domain_controllers", $options))
+ {
+ if (!is_array($options["domain_controllers"]))
+ {
+ throw new adLDAPException('[domain_controllers] option must be an array');
+ }
+ $this->domainControllers = $options["domain_controllers"];
+ }
+ if (array_key_exists("admin_username", $options))
+ {
+ $this->adminUsername = $options["admin_username"];
+ }
+ if (array_key_exists("admin_password", $options))
+ {
+ $this->adminPassword = $options["admin_password"];
+ }
+ if (array_key_exists("real_primarygroup", $options))
+ {
+ $this->realPrimaryGroup = $options["real_primarygroup"];
+ }
+ if (array_key_exists("use_ssl", $options))
+ {
+ $this->setUseSSL($options["use_ssl"]);
+ }
+ if (array_key_exists("use_tls", $options))
+ {
+ $this->useTLS = $options["use_tls"];
+ }
+ if (array_key_exists("recursive_groups", $options))
+ {
+ $this->recursiveGroups = $options["recursive_groups"];
+ }
+ if (array_key_exists("follow_referrals", $options))
+ {
+ $this->followReferrals = $options["follow_referrals"];
+ }
+ if (array_key_exists("ad_port", $options))
+ {
+ $this->setPort($options["ad_port"]);
+ }
+ if (array_key_exists("sso", $options))
+ {
+ $this->setUseSSO($options["sso"]);
+ if (!$this->ldapSaslSupported())
+ {
+ $this->setUseSSO(false);
+ }
+ }
+ }
+
+ if ($this->ldapSupported() === false)
+ {
+ throw new adLDAPException('No LDAP support for PHP. See: http://www.php.net/ldap');
+ }
+
+ return $this->connect();
+ }
+
+ /**
+ * Default Destructor
+ *
+ * Closes the LDAP connection
+ *
+ * @return void
+ */
+ function __destruct()
+ {
+ $this->close();
+ }
+
+ /**
+ * Connects and Binds to the Domain Controller
+ *
+ * @return bool
+ * @throws adLDAPException
+ */
+ public function connect()
+ {
+ // Connect to the AD/LDAP server as the username/password
+ $domainController = $this->randomController();
+ if ($this->useSSL)
+ {
+ $this->ldapConnection = ldap_connect("ldaps://" . $domainController, $this->adPort);
+ }
+ else
+ {
+ $this->ldapConnection = ldap_connect("ldap://" . $domainController, $this->adPort);
+ }
+
+ // Set some ldap options for talking to AD
+ ldap_set_option($this->ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3);
+ ldap_set_option($this->ldapConnection, LDAP_OPT_REFERRALS, $this->followReferrals);
+
+ if ($this->useTLS)
+ {
+ ldap_start_tls($this->ldapConnection);
+ }
+
+ // Bind as a domain admin if they've set it up
+ if ($this->adminUsername !== NULL && $this->adminPassword !== NULL)
+ {
+ $this->ldapBind = @ldap_bind($this->ldapConnection, $this->adminUsername . $this->accountSuffix, $this->adminPassword);
+ if (!$this->ldapBind)
+ {
+ if ($this->useSSL && !$this->useTLS)
+ {
+ // If you have problems troubleshooting, remove the @ character from the ldapldapBind command above to get the actual error message
+ throw new adLDAPException('Bind to Active Directory failed. Either the LDAPs connection failed or the login credentials are incorrect. AD said: ' . $this->getLastError());
+ }
+ else
+ {
+ throw new adLDAPException('Bind to Active Directory failed. Check the login credentials and/or server details. AD said: ' . $this->getLastError());
+ }
+ }
+ }
+ if ($this->useSSO && $_SERVER['REMOTE_USER'] && $this->adminUsername === null && $_SERVER['KRB5CCNAME'])
+ {
+ putenv("KRB5CCNAME=" . $_SERVER['KRB5CCNAME']);
+ $this->ldapBind = @ldap_sasl_bind($this->ldapConnection, NULL, NULL, "GSSAPI");
+ if (!$this->ldapBind)
+ {
+ throw new adLDAPException('Rebind to Active Directory failed. AD said: ' . $this->getLastError());
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ if ($this->baseDn == NULL)
+ {
+ $this->baseDn = $this->findBaseDn();
+ }
+ return true;
+ }
+
+ /**
+ * Closes the LDAP connection
+ *
+ * @return void
+ */
+ public function close()
+ {
+ if ($this->ldapConnection)
+ {
+ @ldap_close($this->ldapConnection);
+ }
+ }
+
+ /**
+ * Validate a user's login credentials
+ *
+ * @param string $username A user's AD username
+ * @param string $password A user's AD password
+ * @param bool $preventRebind optional
+ * @return bool
+ * @throws adLDAPException
+ */
+ public function authenticate($username, $password, $preventRebind = false)
+ {
+ /** @noinspection PhpUndefinedClassInspection */
+ Logging::var_trace('username', $username);
+ /** @noinspection PhpUndefinedClassInspection */
+ Logging::var_trace('preventRebind', $preventRebind);
+
+ // Prevent null binding
+ if ($username === NULL || $password === NULL)
+ {
+ return false;
+ }
+ if (empty($username) || empty($password))
+ {
+ return false;
+ }
+
+ // Allow binding over SSO for Kerberos
+ if ($this->useSSO && $_SERVER['REMOTE_USER'] && $_SERVER['REMOTE_USER'] == $username && $this->adminUsername === NULL && $_SERVER['KRB5CCNAME'])
+ {
+ /** @noinspection PhpUndefinedClassInspection */
+ Logging::trace('putenv("KRB5CCNAME="' . $_SERVER['KRB5CCNAME'] . '" and perform ldap_sasl_bind');
+ putenv("KRB5CCNAME=" . $_SERVER['KRB5CCNAME']);
+ $this->ldapBind = @ldap_sasl_bind($this->ldapConnection, NULL, NULL, "GSSAPI");
+ if (!$this->ldapBind)
+ {
+ throw new adLDAPException('Rebind to Active Directory failed. AD said: ' . $this->getLastError());
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ /** @noinspection PhpUndefinedClassInspection */
+ Logging::trace('ldap_bind(' . $this->ldapConnection . ', ' . $username . $this->accountSuffix . ', $password');
+
+ // Bind as the user
+ $ret = true;
+ $this->ldapBind = @ldap_bind($this->ldapConnection, $username . $this->accountSuffix, $password);
+ if (!$this->ldapBind)
+ {
+ $ret = false;
+ }
+
+ // Once we've checked their details, kick back into admin mode if we have it
+ if ($this->adminUsername !== NULL && !$preventRebind)
+ {
+ /** @noinspection PhpUndefinedClassInspection */
+ Logging::trace('ldap_bind(' . $this->ldapConnection . ', ' . $this->adminUsername . $this->accountSuffix . ', $this->adminPassword');
+
+ $this->ldapBind = @ldap_bind($this->ldapConnection, $this->adminUsername . $this->accountSuffix, $this->adminPassword);
+ if (!$this->ldapBind)
+ {
+ // This should never happen in theory
+ throw new adLDAPException('Rebind to Active Directory failed. AD said: ' . $this->getLastError());
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Return a list of all found objects (except computer) in AD
+ * $search has to match either cn, displayname, samaccountname or sn
+ *
+ * @param bool $includeDescription Return a description,cn, displayname and distinguishedname of the user
+ * @param string $search Search parameter
+ * @param bool $sorted Sort the user accounts
+ * @return array|bool , if $includeDescription=true then a multi-dimensional array
+ * @throws Exception
+ */
+ public function search($includeDescription = false, $search = "*", $sorted = true)
+ {
+ if (!$this->getLdapBind())
+ {
+ return false;
+ }
+
+ // Perform the search and grab all their details
+ //$filter = "(|(cn=" . $search . ")(displayname=" . $search . ")(samaccountname=" . $search . "))";
+ $filter = "(&(!(objectClass=computer))(|(anr=" . $search . ")))";
+ $fields = array("cn", "description", "displayname", "distinguishedname", "samaccountname");
+ try
+ {
+ $sr = ldap_search($this->getLdapConnection(), $this->getBaseDn(), $filter, $fields);
+ } catch (Exception $e)
+ {
+ Logging::var_trace('filter', $filter);
+ Logging::var_trace('fields', $fields);
+ throw new Exception($e->getMessage() . "\nfilter = '" . $filter . "'\nfields = " . print_r($fields, true), 0, $e);
+ }
+ try
+ {
+ $entries = ldap_get_entries($this->getLdapConnection(), $sr);
+ } catch (Exception $e)
+ {
+ /** @noinspection PhpUndefinedVariableInspection */
+ Logging::var_trace('sr', $sr);
+ throw new Exception($e->getMessage() . "\nsr = " . print_r($sr, true), 0, $e);
+ }
+
+ $objectArray = array();
+ for ($i = 0; $i < $entries["count"]; $i++)
+ {
+ if ($includeDescription && strlen($entries[$i]["description"][0]) > 0)
+ {
+ $objectArray[$entries[$i]["samaccountname"][0]] = array($entries[$i]["cn"][0], $entries[$i]["description"][0], $entries[$i]["displayname"][0], $entries[$i]["distinguishedname"][0]);
+ }
+ elseif ($includeDescription)
+ {
+ // description is set to displayname if no description is present
+ $objectArray[$entries[$i]["samaccountname"][0]] = array($entries[$i]["cn"][0], $entries[$i]["displayname"][0], $entries[$i]["displayname"][0], $entries[$i]["distinguishedname"][0]);
+ }
+ else
+ {
+ array_push($objectArray, $entries[$i]["samaccountname"][0]);
+ }
+ }
+ if ($sorted)
+ {
+ asort($objectArray);
+ }
+ return $objectArray;
+ }
+
+ /**
+ * Returns objectClass in an array
+ *
+ * @param $distinguishedName
+ * @return array|bool
+ * @throws Exception
+ */
+ public function getObjectClass($distinguishedName)
+ {
+ if ($distinguishedName === NULL)
+ {
+ return false;
+ }
+ if (!$this->getLdapBind())
+ {
+ return false;
+ }
+
+ $filter = "distinguishedName=" . $this->utilities()->ldapSlashes($distinguishedName);
+
+ $fields = array("objectclass");
+ try
+ {
+ $sr = ldap_search($this->getLdapConnection(), $this->getBaseDn(), $filter, $fields);
+ } catch (Exception $e)
+ {
+ Logging::var_trace('filter', $filter);
+ Logging::var_trace('fields', $fields);
+ throw new Exception($e->getMessage() . "\nfilter = '" . $filter . "'\nfields = " . print_r($fields, true), 0, $e);
+ }
+
+ try
+ {
+ /** @noinspection PhpUndefinedVariableInspection */
+ $entries = ldap_get_entries($this->getLdapConnection(), $sr);
+ } catch (Exception $e)
+ {
+ /** @noinspection PhpUndefinedVariableInspection */
+ Logging::var_trace('sr', $sr);
+ throw new Exception($e->getMessage() . "\nsr = " . print_r($sr, true), 0, $e);
+ }
+
+ $objects = array();
+ for ($i = 0; $i < $entries[0]["objectclass"]["count"]; $i++)
+ {
+ array_push($objects, $entries[0]["objectclass"][$i]);
+ }
+ return $objects;
+ }
+
+ /**
+ * Find the Base DN of your domain controller
+ *
+ * @return string
+ */
+ public function findBaseDn()
+ {
+ $namingContext = $this->getRootDse(array('defaultnamingcontext'));
+ return $namingContext[0]['defaultnamingcontext'][0];
+ }
+
+ /**
+ * Get the RootDSE properties from a domain controller
+ *
+ * @param array $attributes The attributes you wish to query e.g. defaultnamingcontext
+ * @return array|bool
+ */
+ public function getRootDse($attributes = array("*", "+"))
+ {
+ if (!$this->ldapBind)
+ {
+ return (false);
+ }
+
+ $sr = @ldap_read($this->ldapConnection, NULL, 'objectClass=*', $attributes);
+ $entries = @ldap_get_entries($this->ldapConnection, $sr);
+ return $entries;
+ }
+
+ /**
+ * Get last error from Active Directory
+ *
+ * This function gets the last message from Active Directory
+ * This may indeed be a 'Success' message but if you get an unknown error
+ * it might be worth calling this function to see what errors were raised
+ *
+ * return string
+ */
+ public function getLastError()
+ {
+ return @ldap_error($this->ldapConnection);
+ }
+
+ /**
+ * Detect LDAP support in php
+ *
+ * @return bool
+ */
+ protected function ldapSupported()
+ {
+ if (!function_exists('ldap_connect'))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Detect ldap_sasl_bind support in PHP
+ *
+ * @return bool
+ */
+ protected function ldapSaslSupported()
+ {
+ if (!function_exists('ldap_sasl_bind'))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Schema
+ *
+ * @param array $attributes Attributes to be queried
+ * @return array|bool
+ */
+ public function adldap_schema($attributes)
+ {
+
+ // LDAP doesn't like NULL attributes, only set them if they have values
+ // If you wish to remove an attribute you should set it to a space
+ // TO DO: Adapt user_modify to use ldap_mod_delete to remove a NULL attribute
+ $mod = array();
+
+ // Check every attribute to see if it contains 8bit characters and then UTF8 encode them
+ array_walk($attributes, array($this, 'encode8bit'));
+
+ if (isset($attributes["address_city"]))
+ {
+ $mod["l"][0] = $attributes["address_city"];
+ }
+ if (isset($attributes["address_code"]))
+ {
+ $mod["postalCode"][0] = $attributes["address_code"];
+ }
+ //if ($attributes["address_country"]){ $mod["countryCode"][0]=$attributes["address_country"]; } // use country codes?
+ if (isset($attributes["address_country"]))
+ {
+ $mod["c"][0] = $attributes["address_country"];
+ }
+ if (isset($attributes["address_pobox"]))
+ {
+ $mod["postOfficeBox"][0] = $attributes["address_pobox"];
+ }
+ if (isset($attributes["address_state"]))
+ {
+ $mod["st"][0] = $attributes["address_state"];
+ }
+ if (isset($attributes["address_street"]))
+ {
+ $mod["streetAddress"][0] = $attributes["address_street"];
+ }
+ if (isset($attributes["company"]))
+ {
+ $mod["company"][0] = $attributes["company"];
+ }
+ if (isset($attributes["change_password"]))
+ {
+ $mod["pwdLastSet"][0] = 0;
+ }
+ if (isset($attributes["department"]))
+ {
+ $mod["department"][0] = $attributes["department"];
+ }
+ if (isset($attributes["description"]))
+ {
+ $mod["description"][0] = $attributes["description"];
+ }
+ if (isset($attributes["display_name"]))
+ {
+ $mod["displayName"][0] = $attributes["display_name"];
+ }
+ if (isset($attributes["email"]))
+ {
+ $mod["mail"][0] = $attributes["email"];
+ }
+ if (isset($attributes["expires"]))
+ {
+ $mod["accountExpires"][0] = $attributes["expires"];
+ } //unix epoch format?
+ if (isset($attributes["firstname"]))
+ {
+ $mod["givenName"][0] = $attributes["firstname"];
+ }
+ if (isset($attributes["home_directory"]))
+ {
+ $mod["homeDirectory"][0] = $attributes["home_directory"];
+ }
+ if (isset($attributes["home_drive"]))
+ {
+ $mod["homeDrive"][0] = $attributes["home_drive"];
+ }
+ if (isset($attributes["initials"]))
+ {
+ $mod["initials"][0] = $attributes["initials"];
+ }
+ if (isset($attributes["logon_name"]))
+ {
+ $mod["userPrincipalName"][0] = $attributes["logon_name"];
+ }
+ if (isset($attributes["manager"]))
+ {
+ $mod["manager"][0] = $attributes["manager"];
+ } //UNTESTED ***Use DistinguishedName***
+ if (isset($attributes["office"]))
+ {
+ $mod["physicalDeliveryOfficeName"][0] = $attributes["office"];
+ }
+ if (isset($attributes["password"]))
+ {
+ $mod["unicodePwd"][0] = $this->user()->encodePassword($attributes["password"]);
+ }
+ if (isset($attributes["profile_path"]))
+ {
+ $mod["profilepath"][0] = $attributes["profile_path"];
+ }
+ if (isset($attributes["script_path"]))
+ {
+ $mod["scriptPath"][0] = $attributes["script_path"];
+ }
+ if (isset($attributes["surname"]))
+ {
+ $mod["sn"][0] = $attributes["surname"];
+ }
+ if (isset($attributes["title"]))
+ {
+ $mod["title"][0] = $attributes["title"];
+ }
+ if (isset($attributes["telephone"]))
+ {
+ $mod["telephoneNumber"][0] = $attributes["telephone"];
+ }
+ if (isset($attributes["mobile"]))
+ {
+ $mod["mobile"][0] = $attributes["mobile"];
+ }
+ if (isset($attributes["pager"]))
+ {
+ $mod["pager"][0] = $attributes["pager"];
+ }
+ if (isset($attributes["ipphone"]))
+ {
+ $mod["ipphone"][0] = $attributes["ipphone"];
+ }
+ if (isset($attributes["web_page"]))
+ {
+ $mod["wWWHomePage"][0] = $attributes["web_page"];
+ }
+ if (isset($attributes["fax"]))
+ {
+ $mod["facsimileTelephoneNumber"][0] = $attributes["fax"];
+ }
+ if (isset($attributes["enabled"]))
+ {
+ $mod["userAccountControl"][0] = $attributes["enabled"];
+ }
+ if (isset($attributes["homephone"]))
+ {
+ $mod["homephone"][0] = $attributes["homephone"];
+ }
+
+ // Distribution List specific schema
+ if (isset($attributes["group_sendpermission"]))
+ {
+ $mod["dlMemSubmitPerms"][0] = $attributes["group_sendpermission"];
+ }
+ if (isset($attributes["group_rejectpermission"]))
+ {
+ $mod["dlMemRejectPerms"][0] = $attributes["group_rejectpermission"];
+ }
+
+ // Exchange Schema
+ if (isset($attributes["exchange_homemdb"]))
+ {
+ $mod["homeMDB"][0] = $attributes["exchange_homemdb"];
+ }
+ if (isset($attributes["exchange_mailnickname"]))
+ {
+ $mod["mailNickname"][0] = $attributes["exchange_mailnickname"];
+ }
+ if (isset($attributes["exchange_proxyaddress"]))
+ {
+ $mod["proxyAddresses"][0] = $attributes["exchange_proxyaddress"];
+ }
+ if (isset($attributes["exchange_usedefaults"]))
+ {
+ $mod["mDBUseDefaults"][0] = $attributes["exchange_usedefaults"];
+ }
+ if (isset($attributes["exchange_policyexclude"]))
+ {
+ $mod["msExchPoliciesExcluded"][0] = $attributes["exchange_policyexclude"];
+ }
+ if (isset($attributes["exchange_policyinclude"]))
+ {
+ $mod["msExchPoliciesIncluded"][0] = $attributes["exchange_policyinclude"];
+ }
+ if (isset($attributes["exchange_addressbook"]))
+ {
+ $mod["showInAddressBook"][0] = $attributes["exchange_addressbook"];
+ }
+ if (isset($attributes["exchange_altrecipient"]))
+ {
+ $mod["altRecipient"][0] = $attributes["exchange_altrecipient"];
+ }
+ if (isset($attributes["exchange_deliverandredirect"]))
+ {
+ $mod["deliverAndRedirect"][0] = $attributes["exchange_deliverandredirect"];
+ }
+
+ // This schema is designed for contacts
+ if (isset($attributes["exchange_hidefromlists"]))
+ {
+ $mod["msExchHideFromAddressLists"][0] = $attributes["exchange_hidefromlists"];
+ }
+ if (isset($attributes["contact_email"]))
+ {
+ $mod["targetAddress"][0] = $attributes["contact_email"];
+ }
+
+ //echo (""); print_r($mod);
+ /*
+ // modifying a name is a bit fiddly
+ if ($attributes["firstname"] && $attributes["surname"]){
+ $mod["cn"][0]=$attributes["firstname"]." ".$attributes["surname"];
+ $mod["displayname"][0]=$attributes["firstname"]." ".$attributes["surname"];
+ $mod["name"][0]=$attributes["firstname"]." ".$attributes["surname"];
+ }
+ */
+
+ if (count($mod) == 0)
+ {
+ return false;
+ }
+ return $mod;
+ }
+
+ /**
+ * Convert 8bit characters e.g. accented characters to UTF8 encoded characters
+ */
+ protected function encode8Bit(&$item, $key)
+ {
+ $encode = false;
+ if (is_string($item))
+ {
+ for ($i = 0; $i < strlen($item); $i++)
+ {
+ if (ord($item[$i]) >> 7)
+ {
+ $encode = true;
+ }
+ }
+ }
+ if ($encode === true && $key != 'password')
+ {
+ $item = utf8_encode($item);
+ }
+ }
+
+ /**
+ * Select a random domain controller from your domain controller array
+ *
+ * @return string
+ */
+ protected function randomController()
+ {
+ mt_srand(doubleval(microtime()) * 100000000); // For older PHP versions
+ /*if (sizeof($this->domainControllers) > 1) {
+ $adController = $this->domainControllers[array_rand($this->domainControllers)];
+ // Test if the controller is responding to pings
+ $ping = $this->pingController($adController);
+ if ($ping === false) {
+ // Find the current key in the domain controllers array
+ $key = array_search($adController, $this->domainControllers);
+ // Remove it so that we don't end up in a recursive loop
+ unset($this->domainControllers[$key]);
+ // Select a new controller
+ return $this->randomController();
+ }
+ else {
+ return ($adController);
+ }
+ } */
+ return $this->domainControllers[array_rand($this->domainControllers)];
+ }
+
+ /**
+ * Test basic connectivity to controller
+ *
+ * @param $host
+ * @return bool
+ */
+ protected function pingController($host)
+ {
+ $port = $this->adPort;
+ fsockopen($host, $port, $errno, $errstr, 10);
+ if ($errno > 0)
+ {
+ return false;
+ }
+ return true;
+ }
}
/**
-* adLDAP Exception Handler
-*
-* Exceptions of this type are thrown on bind failure or when SSL is required but not configured
-* Example:
-* try {
-* $adldap = new adLDAP();
-* }
-* catch (adLDAPException $e) {
-* echo $e;
-* exit();
-* }
-*/
-class adLDAPException extends \Exception {}
+ * adLDAP Exception Handler
+ *
+ * Exceptions of this type are thrown on bind failure or when SSL is required but not configured
+ * Example:
+ * try {
+ * $adldap = new adLDAP();
+ * }
+ * catch (adLDAPException $e) {
+ * echo $e;
+ * exit();
+ * }
+ */
+class adLDAPException extends \Exception
+{
+}
?>
diff --git a/lib/adLDAP/classes/adLDAPUsers.php b/lib/adLDAP/classes/adLDAPUsers.php
index c3ea8c6..a800f55 100644
--- a/lib/adLDAP/classes/adLDAPUsers.php
+++ b/lib/adLDAP/classes/adLDAPUsers.php
@@ -1,31 +1,36 @@
adldap = $adldap;
- }
-
- /**
- * Validate a user's login credentials
- *
- * @param string $username A user's AD username
- * @param string $password A user's AD password
- * @param bool optional $prevent_rebind
- * @return bool
- */
- public function authenticate($username, $password, $preventRebind = false) {
- return $this->adldap->authenticate($username, $password, $preventRebind);
- }
-
- /**
- * Create a user
- *
- * If you specify a password here, this can only be performed over SSL
- *
- * @param array $attributes The attributes to set to the user account
- * @return bool
- */
- public function create($attributes) {
- // Check for compulsory fields
- if (!array_key_exists("username", $attributes)) { return "Missing compulsory field [username]"; }
- if (!array_key_exists("firstname", $attributes)) { return "Missing compulsory field [firstname]"; }
- if (!array_key_exists("surname", $attributes)) { return "Missing compulsory field [surname]"; }
- if (!array_key_exists("email", $attributes)) { return "Missing compulsory field [email]"; }
- if (!array_key_exists("container", $attributes)) { return "Missing compulsory field [container]"; }
- if (!is_array($attributes["container"])) { return "Container attribute must be an array."; }
-
- if (array_key_exists("password",$attributes) && (!$this->adldap->getUseSSL() && !$this->adldap->getUseTLS())) {
- throw new \adLDAP\adLDAPException('SSL must be configured on your webserver and enabled in the class to set passwords.');
- }
-
- if (!array_key_exists("display_name", $attributes)) {
- $attributes["display_name"] = $attributes["firstname"] . " " . $attributes["surname"];
- }
-
- // Translate the schema
- $add = $this->adldap->adldap_schema($attributes);
-
- // Additional stuff only used for adding accounts
- $add["cn"][0] = $attributes["display_name"];
- $add["samaccountname"][0] = $attributes["username"];
- $add["objectclass"][0] = "top";
- $add["objectclass"][1] = "person";
- $add["objectclass"][2] = "organizationalPerson";
- $add["objectclass"][3] = "user"; //person?
- //$add["name"][0]=$attributes["firstname"]." ".$attributes["surname"];
-
- // Set the account control attribute
- $control_options = array("NORMAL_ACCOUNT");
- if (!$attributes["enabled"]) {
- $control_options[] = "ACCOUNTDISABLE";
- }
- $add["userAccountControl"][0] = $this->accountControl($control_options);
-
- // Determine the container
- $attributes["container"] = array_reverse($attributes["container"]);
- $container = "OU=" . implode(", OU=",$attributes["container"]);
-
- // Add the entry
- $result = @ldap_add($this->adldap->getLdapConnection(), "CN=" . $add["cn"][0] . ", " . $container . "," . $this->adldap->getBaseDn(), $add);
- if ($result != true) {
- return false;
- }
- return true;
- }
-
- /**
- * Account control options
- *
- * @param array $options The options to convert to int
- * @return int
- */
- protected function accountControl($options) {
- $val=0;
-
- if (is_array($options)) {
- if (in_array("SCRIPT",$options)) { $val=$val+1; }
- if (in_array("ACCOUNTDISABLE",$options)) { $val=$val+2; }
- if (in_array("HOMEDIR_REQUIRED",$options)) { $val=$val+8; }
- if (in_array("LOCKOUT",$options)) { $val=$val+16; }
- if (in_array("PASSWD_NOTREQD",$options)) { $val=$val+32; }
- //PASSWD_CANT_CHANGE Note You cannot assign this permission by directly modifying the UserAccountControl attribute.
- //For information about how to set the permission programmatically, see the "Property flag descriptions" section.
- if (in_array("ENCRYPTED_TEXT_PWD_ALLOWED",$options)) { $val=$val+128; }
- if (in_array("TEMP_DUPLICATE_ACCOUNT",$options)) { $val=$val+256; }
- if (in_array("NORMAL_ACCOUNT",$options)) { $val=$val+512; }
- if (in_array("INTERDOMAIN_TRUST_ACCOUNT",$options)) { $val=$val+2048; }
- if (in_array("WORKSTATION_TRUST_ACCOUNT",$options)) { $val=$val+4096; }
- if (in_array("SERVER_TRUST_ACCOUNT",$options)) { $val=$val+8192; }
- if (in_array("DONT_EXPIRE_PASSWORD",$options)){ $val=$val+65536; }
- if (in_array("MNS_LOGON_ACCOUNT",$options)) { $val=$val+131072; }
- if (in_array("SMARTCARD_REQUIRED",$options)) { $val=$val+262144; }
- if (in_array("TRUSTED_FOR_DELEGATION",$options)) { $val=$val+524288; }
- if (in_array("NOT_DELEGATED",$options)) { $val=$val+1048576; }
- if (in_array("USE_DES_KEY_ONLY",$options)) { $val=$val+2097152; }
- if (in_array("DONT_REQ_PREAUTH",$options)) { $val=$val+4194304; }
- if (in_array("PASSWORD_EXPIRED",$options)) { $val=$val+8388608; }
- if (in_array("TRUSTED_TO_AUTH_FOR_DELEGATION",$options)) { $val=$val+16777216; }
- }
- return $val;
- }
-
- /**
- * Delete a user account
- *
- * @param string $username The username to delete (please be careful here!)
- * @param bool $isGUID Is the username a GUID or a samAccountName
- * @return array
- */
- public function delete($username, $isGUID = false) {
- $userinfo = $this->info($username, array("*"), $isGUID);
- $dn = $userinfo[0]['distinguishedname'][0];
- $result = $this->adldap->folder()->delete($dn);
- if ($result != true) {
- return false;
- }
- return true;
- }
-
- /**
- * Groups the user is a member of
- *
- * @param string $username The username to query
- * @param bool $recursive Recursive list of groups
- * @param bool $isGUID Is the username passed a GUID or a samAccountName
- * @return array
- */
- public function groups($username, $recursive = NULL, $isGUID = false) {
- if ($username === NULL) { return false; }
- if ($recursive === NULL) { $recursive = $this->adldap->getRecursiveGroups(); } // Use the default option if they haven't set it
- if (!$this->adldap->getLdapBind()) { return false; }
-
- // Search the directory for their information
- $info = @$this->info($username, array("memberof", "primarygroupid"), $isGUID);
- $groups = $this->adldap->utilities()->niceNames($info[0]["memberof"]); // Presuming the entry returned is our guy (unique usernames)
-
- if ($recursive === true) {
- foreach ($groups as $id => $groupName) {
- $extraGroups = $this->adldap->group()->recursiveGroups($groupName);
- $groups = array_merge($groups, $extraGroups);
- }
- }
- return $groups;
- }
-
- /**
- * Find information about the users. Returned in a raw array format from AD
- *
- * @param string $username The username to query
- * @param array $fields Array of parameters to query
- * @param bool $isGUID Is the username passed a GUID or a samAccountName
- * @return array
- */
- public function info($username, $fields = NULL, $isGUID = false) {
- if ($username === NULL) { return false; }
- if (!$this->adldap->getLdapBind()) { return false; }
-
- if ($isGUID === true) {
- $username = $this->adldap->utilities()->strGuidToHex($username);
- $filter = "objectguid=" . $username;
- }
- else if (strpos($username, "@")) {
- $filter = "userPrincipalName=" . $username;
- }
- else {
- $filter = "samaccountname=" . $username;
- }
- $filter = "(&(objectCategory=person)({$filter}))";
- if ($fields === NULL) {
- $fields = array("samaccountname","mail","memberof","department","displayname","telephonenumber","primarygroupid","objectsid");
- }
- if (!in_array("objectsid", $fields)) {
- $fields[] = "objectsid";
- }
- $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
- $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
-
- if (isset($entries[0])) {
- if ($entries[0]['count'] >= 1) {
- if (in_array("memberof", $fields)) {
- // AD does not return the primary group in the ldap query, we may need to fudge it
- if ($this->adldap->getRealPrimaryGroup() && isset($entries[0]["primarygroupid"][0]) && isset($entries[0]["objectsid"][0])) {
- //$entries[0]["memberof"][]=$this->group_cn($entries[0]["primarygroupid"][0]);
- $entries[0]["memberof"][] = $this->adldap->group()->getPrimaryGroup($entries[0]["primarygroupid"][0], $entries[0]["objectsid"][0]);
- } else {
- $entries[0]["memberof"][] = "CN=Domain Users,CN=Users," . $this->adldap->getBaseDn();
- }
- if (!isset($entries[0]["memberof"]["count"])) {
- $entries[0]["memberof"]["count"] = 0;
- }
- $entries[0]["memberof"]["count"]++;
- }
- }
- return $entries;
- }
- return false;
- }
-
- /**
- * Find information about the users. Returned in a raw array format from AD
- *
- * @param string $username The username to query
- * @param array $fields Array of parameters to query
- * @param bool $isGUID Is the username passed a GUID or a samAccountName
- * @return mixed
- */
- public function infoCollection($username, $fields = NULL, $isGUID = false) {
- if ($username === NULL) { return false; }
- if (!$this->adldap->getLdapBind()) { return false; }
-
- $info = $this->info($username, $fields, $isGUID);
-
- if ($info !== false) {
- $collection = new \adLDAP\collections\adLDAPUserCollection($info, $this->adldap);
- return $collection;
- }
- return false;
- }
-
- /**
- * Determine if a user is in a specific group
- *
- * @param string $username The username to query
- * @param string $group The name of the group to check against
- * @param bool $recursive Check groups recursively
- * @param bool $isGUID Is the username passed a GUID or a samAccountName
- * @return bool
- */
- public function inGroup($username, $group, $recursive = NULL, $isGUID = false) {
- if ($username === NULL) { return false; }
- if ($group === NULL) { return false; }
- if (!$this->adldap->getLdapBind()) { return false; }
- if ($recursive === NULL) { $recursive = $this->adldap->getRecursiveGroups(); } // Use the default option if they haven't set it
-
- // Get a list of the groups
- $groups = $this->groups($username, $recursive, $isGUID);
-
- // Return true if the specified group is in the group list
- if (in_array($group, $groups)) {
- return true;
- }
- return false;
- }
-
- /**
- * Determine a user's password expiry date
- *
- * @param string $username The username to query
- * @param book $isGUID Is the username passed a GUID or a samAccountName
- * @requires bcmath http://www.php.net/manual/en/book.bc.php
- * @return array
- */
- public function passwordExpiry($username, $isGUID = false) {
- if ($username === NULL) { return "Missing compulsory field [username]"; }
- if (!$this->adldap->getLdapBind()) { return false; }
- if (!function_exists('bcmod')) { throw new \adLDAP\adLDAPException("Missing function support [bcmod] http://www.php.net/manual/en/book.bc.php"); };
-
- $userInfo = $this->info($username, array("pwdlastset", "useraccountcontrol"), $isGUID);
- $pwdLastSet = $userInfo[0]['pwdlastset'][0];
- $status = array();
-
- if ($userInfo[0]['useraccountcontrol'][0] == '66048') {
- // Password does not expire
- return "Does not expire";
- }
- if ($pwdLastSet === '0') {
- // Password has already expired
- return "Password has expired";
- }
-
- // Password expiry in AD can be calculated from TWO values:
- // - User's own pwdLastSet attribute: stores the last time the password was changed
- // - Domain's maxPwdAge attribute: how long passwords last in the domain
- //
- // Although Microsoft chose to use a different base and unit for time measurements.
- // This function will convert them to Unix timestamps
- $sr = ldap_read($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), 'objectclass=*', array('maxPwdAge'));
- if (!$sr) {
- return false;
- }
- $info = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
- $maxPwdAge = $info[0]['maxpwdage'][0];
-
- // See MSDN: http://msdn.microsoft.com/en-us/library/ms974598.aspx
- //
- // pwdLastSet contains the number of 100 nanosecond intervals since January 1, 1601 (UTC),
- // stored in a 64 bit integer.
- //
- // The number of seconds between this date and Unix epoch is 11644473600.
- //
- // maxPwdAge is stored as a large integer that represents the number of 100 nanosecond
- // intervals from the time the password was set before the password expires.
- //
- // We also need to scale this to seconds but also this value is a _negative_ quantity!
- //
- // If the low 32 bits of maxPwdAge are equal to 0 passwords do not expire
- //
- // Unfortunately the maths involved are too big for PHP integers, so I've had to require
- // BCMath functions to work with arbitrary precision numbers.
- if (bcmod($maxPwdAge, 4294967296) === '0') {
- return "Domain does not expire passwords";
- }
-
- // Add maxpwdage and pwdlastset and we get password expiration time in Microsoft's
- // time units. Because maxpwd age is negative we need to subtract it.
- $pwdExpire = bcsub($pwdLastSet, $maxPwdAge);
-
- // Convert MS's time to Unix time
- $status['expiryts'] = bcsub(bcdiv($pwdExpire, '10000000'), '11644473600');
- $status['expiryformat'] = date('Y-m-d H:i:s', bcsub(bcdiv($pwdExpire, '10000000'), '11644473600'));
-
- return $status;
- }
-
- /**
- * Modify a user
- *
- * @param string $username The username to query
- * @param array $attributes The attributes to modify. Note if you set the enabled attribute you must not specify any other attributes
- * @param bool $isGUID Is the username passed a GUID or a samAccountName
- * @return bool
- */
- public function modify($username, $attributes, $isGUID = false) {
- if ($username === NULL) { return "Missing compulsory field [username]"; }
- if (array_key_exists("password", $attributes) && !$this->adldap->getUseSSL() && !$this->adldap->getUseTLS()) {
- throw new \adLDAP\adLDAPException('SSL/TLS must be configured on your webserver and enabled in the class to set passwords.');
- }
-
- // Find the dn of the user
- $userDn = $this->dn($username, $isGUID);
- if ($userDn === false) {
- return false;
- }
-
- // Translate the update to the LDAP schema
- $mod = $this->adldap->adldap_schema($attributes);
-
- // Check to see if this is an enabled status update
- if (!$mod && !array_key_exists("enabled", $attributes)) {
- return false;
- }
-
- // Set the account control attribute (only if specified)
- if (array_key_exists("enabled", $attributes)) {
- if ($attributes["enabled"]) {
- $controlOptions = array("NORMAL_ACCOUNT");
- }
- else {
- $controlOptions = array("NORMAL_ACCOUNT", "ACCOUNTDISABLE");
- }
- $mod["userAccountControl"][0] = $this->accountControl($controlOptions);
- }
-
- // Do the update
- $result = @ldap_modify($this->adldap->getLdapConnection(), $userDn, $mod);
- if ($result == false) {
- return false;
- }
- return true;
- }
-
- /**
- * Disable a user account
- *
- * @param string $username The username to disable
- * @param bool $isGUID Is the username passed a GUID or a samAccountName
- * @return bool
- */
- public function disable($username, $isGUID = false) {
- if ($username === NULL) { return "Missing compulsory field [username]"; }
- $attributes = array("enabled" => 0);
- $result = $this->modify($username, $attributes, $isGUID);
- if ($result == false) { return false; }
-
- return true;
- }
-
- /**
- * Enable a user account
- *
- * @param string $username The username to enable
- * @param bool $isGUID Is the username passed a GUID or a samAccountName
- * @return bool
- */
- public function enable($username, $isGUID = false) {
- if ($username === NULL) { return "Missing compulsory field [username]"; }
- $attributes = array("enabled" => 1);
- $result = $this->modify($username, $attributes, $isGUID);
- if ($result == false) { return false; }
-
- return true;
- }
-
- /**
- * Set the password of a user - This must be performed over SSL
- *
- * @param string $username The username to modify
- * @param string $password The new password
- * @param bool $isGUID Is the username passed a GUID or a samAccountName
- * @return bool
- */
- public function password($username, $password, $isGUID = false) {
- if ($username === NULL) { return false; }
- if ($password === NULL) { return false; }
- if (!$this->adldap->getLdapBind()) { return false; }
- if (!$this->adldap->getUseSSL() && !$this->adldap->getUseTLS()) {
- throw new \adLDAP\adLDAPException('SSL must be configured on your webserver and enabled in the class to set passwords.');
- }
-
- $userDn = $this->dn($username, $isGUID);
- if ($userDn === false) {
- return false;
- }
-
- $add=array();
- $add["unicodePwd"][0] = $this->encodePassword($password);
-
- $result = @ldap_mod_replace($this->adldap->getLdapConnection(), $userDn, $add);
- if ($result === false) {
- $err = ldap_errno($this->adldap->getLdapConnection());
- if ($err) {
- $msg = 'Error ' . $err . ': ' . ldap_err2str($err) . '.';
- if($err == 53) {
- $msg .= ' Your password might not match the password policy.';
- }
- throw new \adLDAP\adLDAPException($msg);
- }
- else {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Encode a password for transmission over LDAP
- *
- * @param string $password The password to encode
- * @return string
- */
- public function encodePassword($password) {
- $password="\"".$password."\"";
- $encoded="";
- for ($i=0; $i info($username, array("cn"), $isGUID);
- if ($user[0]["dn"] === NULL) {
- return false;
- }
- $userDn = $user[0]["dn"];
- return $userDn;
- }
-
- /**
- * Return a list of all users in AD
- *
- * @param bool $includeDescription Return a description of the user
- * @param string $search Search parameter
- * @param bool $sorted Sort the user accounts
- * @return array
- */
- public function all($includeDescription = false, $search = "*", $sorted = true) {
- if (!$this->adldap->getLdapBind()) { return false; }
-
- // Perform the search and grab all their details
- $filter = "(&(objectClass=user)(samaccounttype=" . adLDAP::ADLDAP_NORMAL_ACCOUNT .")(objectCategory=person)(cn=" . $search . "))";
- $fields = array("samaccountname","displayname");
- $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
- $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
-
- $usersArray = array();
- for ($i=0; $i<$entries["count"]; $i++) {
- if ($includeDescription && strlen($entries[$i]["displayname"][0])>0) {
- $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["displayname"][0];
- } elseif ($includeDescription) {
- $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["samaccountname"][0];
- } else {
- array_push($usersArray, $entries[$i]["samaccountname"][0]);
- }
- }
- if ($sorted) {
- asort($usersArray);
- }
- return $usersArray;
- }
-
- /**
- * Converts a username (samAccountName) to a GUID
- *
- * @param string $username The username to query
- * @return string
- */
- public function usernameToGuid($username) {
- if (!$this->adldap->getLdapBind()){ return false; }
- if ($username === null){ return "Missing compulsory field [username]"; }
-
- $filter = "samaccountname=" . $username;
- $fields = array("objectGUID");
- $sr = @ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
- if (ldap_count_entries($this->adldap->getLdapConnection(), $sr) > 0) {
- $entry = @ldap_first_entry($this->adldap->getLdapConnection(), $sr);
- $guid = @ldap_get_values_len($this->adldap->getLdapConnection(), $entry, 'objectGUID');
- $strGUID = $this->adldap->utilities()->binaryToText($guid[0]);
- return $strGUID;
- }
- return false;
- }
-
- /**
- * Return a list of all users in AD that have a specific value in a field
- *
- * @param bool $includeDescription Return a description of the user
- * @param string $searchField Field to search search for
- * @param string $searchFilter Value to search for in the specified field
- * @param bool $sorted Sort the user accounts
- * @return array
- */
- public function find($includeDescription = false, $searchField = false, $searchFilter = false, $sorted = true) {
- if (!$this->adldap->getLdapBind()) { return false; }
-
- // Perform the search and grab all their details
- $searchParams = "";
- if ($searchField) {
- $searchParams = "(" . $searchField . "=" . $searchFilter . ")";
- }
- $filter = "(&(objectClass=user)(samaccounttype=" . adLDAP::ADLDAP_NORMAL_ACCOUNT .")(objectCategory=person)" . $searchParams . ")";
- $fields = array("samaccountname","displayname");
- $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
- $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
-
- $usersArray = array();
- for ($i=0; $i < $entries["count"]; $i++) {
- if ($includeDescription && strlen($entries[$i]["displayname"][0]) > 0) {
- $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["displayname"][0];
- }
- else if ($includeDescription) {
- $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["samaccountname"][0];
- }
- else {
- array_push($usersArray, $entries[$i]["samaccountname"][0]);
- }
- }
- if ($sorted) {
- asort($usersArray);
- }
- return ($usersArray);
- }
-
- /**
- * Move a user account to a different OU
- *
- * @param string $username The username to move (please be careful here!)
- * @param array $container The container or containers to move the user to (please be careful here!).
- * accepts containers in 1. parent 2. child order
- * @return array
- */
- public function move($username, $container) {
- if (!$this->adldap->getLdapBind()) { return false; }
- if ($username === null) { return "Missing compulsory field [username]"; }
- if ($container === null) { return "Missing compulsory field [container]"; }
- if (!is_array($container)) { return "Container must be an array"; }
-
- $userInfo = $this->info($username, array("*"));
- $dn = $userInfo[0]['distinguishedname'][0];
- $newRDn = "cn=" . $username;
- $container = array_reverse($container);
- $newContainer = "ou=" . implode(",ou=",$container);
- $newBaseDn = strtolower($newContainer) . "," . $this->adldap->getBaseDn();
- $result = @ldap_rename($this->adldap->getLdapConnection(), $dn, $newRDn, $newBaseDn, true);
- if ($result !== true) {
- return false;
- }
- return true;
- }
-
- /**
- * Get the last logon time of any user as a Unix timestamp
- *
- * @param string $username
- * @return long $unixTimestamp
- */
- public function getLastLogon($username) {
- if (!$this->adldap->getLdapBind()) { return false; }
- if ($username === null) { return "Missing compulsory field [username]"; }
- $userInfo = $this->info($username, array("lastLogonTimestamp"));
- $lastLogon = adLDAPUtils::convertWindowsTimeToUnixTime($userInfo[0]['lastLogonTimestamp'][0]);
- return $lastLogon;
- }
+ * USER FUNCTIONS
+ */
+class adLDAPUsers
+{
+ /**
+ * The current adLDAP connection via dependency injection
+ *
+ * @var adLDAP
+ */
+ protected $adldap;
+
+ public function __construct(adLDAP $adldap)
+ {
+ $this->adldap = $adldap;
+ }
+
+ /**
+ * Validate a user's login credentials
+ *
+ * @param string $username A user's AD username
+ * @param string $password A user's AD password
+ * @param bool $prevent_rebind optional
+ * @return bool
+ */
+ public function authenticate($username, $password, $preventRebind = false)
+ {
+ return $this->adldap->authenticate($username, $password, $preventRebind);
+ }
+
+ /**
+ * Create a user
+ *
+ * If you specify a password here, this can only be performed over SSL
+ *
+ * @param array $attributes The attributes to set to the user account
+ * @return bool
+ */
+ public function create($attributes)
+ {
+ // Check for compulsory fields
+ if (!array_key_exists("username", $attributes))
+ {
+ return "Missing compulsory field [username]";
+ }
+ if (!array_key_exists("firstname", $attributes))
+ {
+ return "Missing compulsory field [firstname]";
+ }
+ if (!array_key_exists("surname", $attributes))
+ {
+ return "Missing compulsory field [surname]";
+ }
+ if (!array_key_exists("email", $attributes))
+ {
+ return "Missing compulsory field [email]";
+ }
+ if (!array_key_exists("container", $attributes))
+ {
+ return "Missing compulsory field [container]";
+ }
+ if (!is_array($attributes["container"]))
+ {
+ return "Container attribute must be an array.";
+ }
+
+ if (array_key_exists("password", $attributes) && (!$this->adldap->getUseSSL() && !$this->adldap->getUseTLS()))
+ {
+ throw new \adLDAP\adLDAPException('SSL must be configured on your webserver and enabled in the class to set passwords.');
+ }
+
+ if (!array_key_exists("display_name", $attributes))
+ {
+ $attributes["display_name"] = $attributes["firstname"] . " " . $attributes["surname"];
+ }
+
+ // Translate the schema
+ $add = $this->adldap->adldap_schema($attributes);
+
+ // Additional stuff only used for adding accounts
+ $add["cn"][0] = $attributes["display_name"];
+ $add["samaccountname"][0] = $attributes["username"];
+ $add["objectclass"][0] = "top";
+ $add["objectclass"][1] = "person";
+ $add["objectclass"][2] = "organizationalPerson";
+ $add["objectclass"][3] = "user"; //person?
+ //$add["name"][0]=$attributes["firstname"]." ".$attributes["surname"];
+
+ // Set the account control attribute
+ $control_options = array("NORMAL_ACCOUNT");
+ if (!$attributes["enabled"])
+ {
+ $control_options[] = "ACCOUNTDISABLE";
+ }
+ $add["userAccountControl"][0] = $this->accountControl($control_options);
+
+ // Determine the container
+ $attributes["container"] = array_reverse($attributes["container"]);
+ $container = "OU=" . implode(", OU=", $attributes["container"]);
+
+ // Add the entry
+ $result = @ldap_add($this->adldap->getLdapConnection(), "CN=" . $add["cn"][0] . ", " . $container . "," . $this->adldap->getBaseDn(), $add);
+ if ($result != true)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Account control options
+ *
+ * @param array $options The options to convert to int
+ * @return int
+ */
+ protected function accountControl($options)
+ {
+ $val = 0;
+
+ if (is_array($options))
+ {
+ if (in_array("SCRIPT", $options))
+ {
+ $val = $val + 1;
+ }
+ if (in_array("ACCOUNTDISABLE", $options))
+ {
+ $val = $val + 2;
+ }
+ if (in_array("HOMEDIR_REQUIRED", $options))
+ {
+ $val = $val + 8;
+ }
+ if (in_array("LOCKOUT", $options))
+ {
+ $val = $val + 16;
+ }
+ if (in_array("PASSWD_NOTREQD", $options))
+ {
+ $val = $val + 32;
+ }
+ //PASSWD_CANT_CHANGE Note You cannot assign this permission by directly modifying the UserAccountControl attribute.
+ //For information about how to set the permission programmatically, see the "Property flag descriptions" section.
+ if (in_array("ENCRYPTED_TEXT_PWD_ALLOWED", $options))
+ {
+ $val = $val + 128;
+ }
+ if (in_array("TEMP_DUPLICATE_ACCOUNT", $options))
+ {
+ $val = $val + 256;
+ }
+ if (in_array("NORMAL_ACCOUNT", $options))
+ {
+ $val = $val + 512;
+ }
+ if (in_array("INTERDOMAIN_TRUST_ACCOUNT", $options))
+ {
+ $val = $val + 2048;
+ }
+ if (in_array("WORKSTATION_TRUST_ACCOUNT", $options))
+ {
+ $val = $val + 4096;
+ }
+ if (in_array("SERVER_TRUST_ACCOUNT", $options))
+ {
+ $val = $val + 8192;
+ }
+ if (in_array("DONT_EXPIRE_PASSWORD", $options))
+ {
+ $val = $val + 65536;
+ }
+ if (in_array("MNS_LOGON_ACCOUNT", $options))
+ {
+ $val = $val + 131072;
+ }
+ if (in_array("SMARTCARD_REQUIRED", $options))
+ {
+ $val = $val + 262144;
+ }
+ if (in_array("TRUSTED_FOR_DELEGATION", $options))
+ {
+ $val = $val + 524288;
+ }
+ if (in_array("NOT_DELEGATED", $options))
+ {
+ $val = $val + 1048576;
+ }
+ if (in_array("USE_DES_KEY_ONLY", $options))
+ {
+ $val = $val + 2097152;
+ }
+ if (in_array("DONT_REQ_PREAUTH", $options))
+ {
+ $val = $val + 4194304;
+ }
+ if (in_array("PASSWORD_EXPIRED", $options))
+ {
+ $val = $val + 8388608;
+ }
+ if (in_array("TRUSTED_TO_AUTH_FOR_DELEGATION", $options))
+ {
+ $val = $val + 16777216;
+ }
+ }
+ return $val;
+ }
+
+ /**
+ * Delete a user account
+ *
+ * @param string $username The username to delete (please be careful here!)
+ * @param bool $isGUID Is the username a GUID or a samAccountName
+ * @return bool
+ */
+ public function delete($username, $isGUID = false)
+ {
+ $userinfo = $this->info($username, array("*"), $isGUID);
+ $dn = $userinfo[0]['distinguishedname'][0];
+ $result = $this->adldap->folder()->delete($dn);
+ if ($result != true)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Groups the user is a member of
+ *
+ * @param string $username The username to query
+ * @param bool $recursive Recursive list of groups
+ * @param bool $isGUID Is the username passed a GUID or a samAccountName
+ * @return array|bool
+ */
+ public function groups($username, $recursive = NULL, $isGUID = false)
+ {
+ if ($username === NULL)
+ {
+ return false;
+ }
+ if ($recursive === NULL)
+ {
+ $recursive = $this->adldap->getRecursiveGroups();
+ } // Use the default option if they haven't set it
+ if (!$this->adldap->getLdapBind())
+ {
+ return false;
+ }
+
+ // Search the directory for their information
+ $info = @$this->info($username, array("memberof", "primarygroupid"), $isGUID);
+ $groups = $this->adldap->utilities()->niceNames($info[0]["memberof"]); // Presuming the entry returned is our guy (unique usernames)
+
+ if ($recursive === true)
+ {
+ foreach ($groups as $id => $groupName)
+ {
+ $extraGroups = $this->adldap->group()->recursiveGroups($groupName);
+ $groups = array_merge($groups, $extraGroups);
+ }
+ }
+ return $groups;
+ }
+
+ /**
+ * Find information about the users. Returned in a raw array format from AD
+ *
+ * @param string $username The username to query
+ * @param array $fields Array of parameters to query
+ * @param bool $isGUID Is the username passed a GUID or a samAccountName
+ * @return array|bool
+ * @throws Exception
+ */
+ public function info($username, $fields = NULL, $isGUID = false)
+ {
+ if ($username === NULL)
+ {
+ return false;
+ }
+ if (!$this->adldap->getLdapBind())
+ {
+ return false;
+ }
+
+ if ($isGUID === true)
+ {
+ $username = $this->adldap->utilities()->strGuidToHex($username);
+ $filter = "objectguid=" . $username;
+ }
+ else if (strpos($username, "@"))
+ {
+ $filter = "userPrincipalName=" . $username;
+ }
+ else
+ {
+ $filter = "|(samaccountname=" . $username . ')';
+ $filter .= "(employeeID=" . $username . ')';
+ }
+ $filter = "(&(objectCategory=person)({$filter}))";
+ if ($fields === NULL)
+ {
+ $fields = array("samaccountname", "mail", "memberof", "department", "displayname", "telephonenumber", "primarygroupid", "objectsid");
+ }
+ if (!in_array("objectsid", $fields))
+ {
+ $fields[] = "objectsid";
+ }
+
+ Logging::var_trace(compact('filter', 'fields'));
+
+ try
+ {
+ $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
+ } catch (Exception $e)
+ {
+ Logging::var_trace('filter', $filter);
+ Logging::var_trace('fields', $fields);
+ throw new Exception($e->getMessage() . "\nfilter = '" . $filter . "'\nfields = " . print_r($fields, true), 0, $e);
+ }
+
+ try
+ {
+ /** @noinspection PhpUndefinedVariableInspection */
+ $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
+ } catch (Exception $e)
+ {
+ /** @noinspection PhpUndefinedVariableInspection */
+ Logging::var_trace('sr', $sr);
+ throw new Exception($e->getMessage() . "\nsr = " . print_r($sr, true), 0, $e);
+ }
+
+ Logging::var_trace(compact('entries'));
+
+ if (isset($entries[0]))
+ {
+ if ($entries[0]['count'] >= 1)
+ {
+ if (in_array("memberof", $fields))
+ {
+ // AD does not return the primary group in the ldap query, we may need to fudge it
+ if ($this->adldap->getRealPrimaryGroup() && isset($entries[0]["primarygroupid"][0]) && isset($entries[0]["objectsid"][0]))
+ {
+ //$entries[0]["memberof"][]=$this->group_cn($entries[0]["primarygroupid"][0]);
+ $entries[0]["memberof"][] = $this->adldap->group()->getPrimaryGroup($entries[0]["primarygroupid"][0], $entries[0]["objectsid"][0]);
+ }
+ else
+ {
+ $entries[0]["memberof"][] = "CN=Domain Users,CN=Users," . $this->adldap->getBaseDn();
+ }
+ if (!isset($entries[0]["memberof"]["count"]))
+ {
+ $entries[0]["memberof"]["count"] = 0;
+ }
+ $entries[0]["memberof"]["count"]++;
+ }
+ }
+ return $entries;
+ }
+ return false;
+ }
+
+ /**
+ * Find information about the users. Returned in a raw array format from AD
+ *
+ * @param string $username The username to query
+ * @param array $fields Array of parameters to query
+ * @param bool $isGUID Is the username passed a GUID or a samAccountName
+ * @return mixed
+ */
+ public function infoCollection($username, $fields = NULL, $isGUID = false)
+ {
+ if ($username === NULL)
+ {
+ return false;
+ }
+ if (!$this->adldap->getLdapBind())
+ {
+ return false;
+ }
+
+ $info = $this->info($username, $fields, $isGUID);
+
+ if ($info !== false)
+ {
+ $collection = new \adLDAP\collections\adLDAPUserCollection($info, $this->adldap);
+ return $collection;
+ }
+ return false;
+ }
+
+ /**
+ * Determine if a user is in a specific group
+ *
+ * @param string $username The username to query
+ * @param string $group The name of the group to check against
+ * @param bool $recursive Check groups recursively
+ * @param bool $isGUID Is the username passed a GUID or a samAccountName
+ * @return bool
+ */
+ public function inGroup($username, $group, $recursive = NULL, $isGUID = false)
+ {
+ if ($username === NULL)
+ {
+ return false;
+ }
+ if ($group === NULL)
+ {
+ return false;
+ }
+ if (!$this->adldap->getLdapBind())
+ {
+ return false;
+ }
+ if ($recursive === NULL)
+ {
+ $recursive = $this->adldap->getRecursiveGroups();
+ } // Use the default option if they haven't set it
+
+ // Get a list of the groups
+ $groups = $this->groups($username, $recursive, $isGUID);
+
+ // Return true if the specified group is in the group list
+ if (in_array($group, $groups))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Determine a user's password expiry date
+ *
+ * @param string $username The username to query
+ * @param bool $isGUID Is the username passed a GUID or a samAccountName
+ * @return array|bool
+ * @throws \adLDAP\adLDAPException
+ * @requires bcmath http://www.php.net/manual/en/book.bc.php
+ */
+ public function passwordExpiry($username, $isGUID = false)
+ {
+ if ($username === NULL)
+ {
+ return "Missing compulsory field [username]";
+ }
+ if (!$this->adldap->getLdapBind())
+ {
+ return false;
+ }
+ if (!function_exists('bcmod'))
+ {
+ throw new \adLDAP\adLDAPException("Missing function support [bcmod] http://www.php.net/manual/en/book.bc.php");
+ };
+
+ $userInfo = $this->info($username, array("pwdlastset", "useraccountcontrol"), $isGUID);
+ $pwdLastSet = $userInfo[0]['pwdlastset'][0];
+ $status = array();
+
+ if ($userInfo[0]['useraccountcontrol'][0] == '66048')
+ {
+ // Password does not expire
+ return "Does not expire";
+ }
+ if ($pwdLastSet === '0')
+ {
+ // Password has already expired
+ return "Password has expired";
+ }
+
+ // Password expiry in AD can be calculated from TWO values:
+ // - User's own pwdLastSet attribute: stores the last time the password was changed
+ // - Domain's maxPwdAge attribute: how long passwords last in the domain
+ //
+ // Although Microsoft chose to use a different base and unit for time measurements.
+ // This function will convert them to Unix timestamps
+ $sr = ldap_read($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), 'objectclass=*', array('maxPwdAge'));
+ if (!$sr)
+ {
+ return false;
+ }
+ $info = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
+ $maxPwdAge = $info[0]['maxpwdage'][0];
+
+ // See MSDN: http://msdn.microsoft.com/en-us/library/ms974598.aspx
+ //
+ // pwdLastSet contains the number of 100 nanosecond intervals since January 1, 1601 (UTC),
+ // stored in a 64 bit integer.
+ //
+ // The number of seconds between this date and Unix epoch is 11644473600.
+ //
+ // maxPwdAge is stored as a large integer that represents the number of 100 nanosecond
+ // intervals from the time the password was set before the password expires.
+ //
+ // We also need to scale this to seconds but also this value is a _negative_ quantity!
+ //
+ // If the low 32 bits of maxPwdAge are equal to 0 passwords do not expire
+ //
+ // Unfortunately the maths involved are too big for PHP integers, so I've had to require
+ // BCMath functions to work with arbitrary precision numbers.
+ if (bcmod($maxPwdAge, 4294967296) === '0')
+ {
+ return "Domain does not expire passwords";
+ }
+
+ // Add maxpwdage and pwdlastset and we get password expiration time in Microsoft's
+ // time units. Because maxpwd age is negative we need to subtract it.
+ $pwdExpire = bcsub($pwdLastSet, $maxPwdAge);
+
+ // Convert MS's time to Unix time
+ $status['expiryts'] = bcsub(bcdiv($pwdExpire, '10000000'), '11644473600');
+ $status['expiryformat'] = date('Y-m-d H:i:s', bcsub(bcdiv($pwdExpire, '10000000'), '11644473600'));
+
+ return $status;
+ }
+
+ /**
+ * Modify a user
+ *
+ * @param string $username The username to query
+ * @param array $attributes The attributes to modify. Note if you set the enabled attribute you must not specify any other attributes
+ * @param bool $isGUID Is the username passed a GUID or a samAccountName
+ * @return bool
+ * @throws \adLDAP\adLDAPException
+ */
+ public function modify($username, $attributes, $isGUID = false)
+ {
+ if ($username === NULL)
+ {
+ return "Missing compulsory field [username]";
+ }
+ if (array_key_exists("password", $attributes) && !$this->adldap->getUseSSL() && !$this->adldap->getUseTLS())
+ {
+ throw new \adLDAP\adLDAPException('SSL/TLS must be configured on your webserver and enabled in the class to set passwords.');
+ }
+
+ // Find the dn of the user
+ $userDn = $this->dn($username, $isGUID);
+ if ($userDn === false)
+ {
+ return false;
+ }
+
+ // Translate the update to the LDAP schema
+ $mod = $this->adldap->adldap_schema($attributes);
+
+ // Check to see if this is an enabled status update
+ if (!$mod && !array_key_exists("enabled", $attributes))
+ {
+ return false;
+ }
+
+ // Set the account control attribute (only if specified)
+ if (array_key_exists("enabled", $attributes))
+ {
+ if ($attributes["enabled"])
+ {
+ $controlOptions = array("NORMAL_ACCOUNT");
+ }
+ else
+ {
+ $controlOptions = array("NORMAL_ACCOUNT", "ACCOUNTDISABLE");
+ }
+ $mod["userAccountControl"][0] = $this->accountControl($controlOptions);
+ }
+
+ // Do the update
+ $result = @ldap_modify($this->adldap->getLdapConnection(), $userDn, $mod);
+ if ($result == false)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Disable a user account
+ *
+ * @param string $username The username to disable
+ * @param bool $isGUID Is the username passed a GUID or a samAccountName
+ * @return bool
+ */
+ public function disable($username, $isGUID = false)
+ {
+ if ($username === NULL)
+ {
+ return "Missing compulsory field [username]";
+ }
+ $attributes = array("enabled" => 0);
+ $result = $this->modify($username, $attributes, $isGUID);
+ if ($result == false)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Enable a user account
+ *
+ * @param string $username The username to enable
+ * @param bool $isGUID Is the username passed a GUID or a samAccountName
+ * @return bool
+ */
+ public function enable($username, $isGUID = false)
+ {
+ if ($username === NULL)
+ {
+ return "Missing compulsory field [username]";
+ }
+ $attributes = array("enabled" => 1);
+ $result = $this->modify($username, $attributes, $isGUID);
+ if ($result == false)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Set the password of a user - This must be performed over SSL
+ *
+ * @param string $username The username to modify
+ * @param string $password The new password
+ * @param bool $isGUID Is the username passed a GUID or a samAccountName
+ * @return bool
+ * @throws \adLDAP\adLDAPException
+ */
+ public function password($username, $password, $isGUID = false)
+ {
+ if ($username === NULL)
+ {
+ return false;
+ }
+ if ($password === NULL)
+ {
+ return false;
+ }
+ if (!$this->adldap->getLdapBind())
+ {
+ return false;
+ }
+ if (!$this->adldap->getUseSSL() && !$this->adldap->getUseTLS())
+ {
+ throw new \adLDAP\adLDAPException('SSL must be configured on your webserver and enabled in the class to set passwords.');
+ }
+
+ $userDn = $this->dn($username, $isGUID);
+ if ($userDn === false)
+ {
+ return false;
+ }
+
+ $add = array();
+ $add["unicodePwd"][0] = $this->encodePassword($password);
+
+ $result = @ldap_mod_replace($this->adldap->getLdapConnection(), $userDn, $add);
+ if ($result === false)
+ {
+ $err = ldap_errno($this->adldap->getLdapConnection());
+ if ($err)
+ {
+ $msg = 'Error ' . $err . ': ' . ldap_err2str($err) . '.';
+ if ($err == 53)
+ {
+ $msg .= ' Your password might not match the password policy.';
+ }
+ throw new \adLDAP\adLDAPException($msg);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Encode a password for transmission over LDAP
+ *
+ * @param string $password The password to encode
+ * @return string
+ */
+ public function encodePassword($password)
+ {
+ $password = "\"" . $password . "\"";
+ $encoded = "";
+ for ($i = 0; $i < strlen($password); $i++)
+ {
+ $encoded .= "{$password{$i}}\000";
+ }
+ return $encoded;
+ }
+
+ /**
+ * Obtain the user's distinguished name based on their userid
+ *
+ *
+ * @param string $username The username
+ * @param bool $isGUID Is the username passed a GUID or a samAccountName
+ * @return string
+ */
+ public function dn($username, $isGUID = false)
+ {
+ $user = $this->info($username, array("cn"), $isGUID);
+ if ($user[0]["dn"] === NULL)
+ {
+ return false;
+ }
+ $userDn = $user[0]["dn"];
+ return $userDn;
+ }
+
+ /**
+ * Return a list of all users in AD
+ *
+ * @param bool $includeDescription Return a description of the user
+ * @param string $search Search parameter
+ * @param bool $sorted Sort the user accounts
+ * @return array|bool
+ */
+ public function all($includeDescription = false, $search = "*", $sorted = true)
+ {
+ if (!$this->adldap->getLdapBind())
+ {
+ return false;
+ }
+
+ // Perform the search and grab all their details
+ $filter = "(&(objectClass=user)(samaccounttype=" . adLDAP::ADLDAP_NORMAL_ACCOUNT . ")(objectCategory=person)(cn=" . $search . "))";
+ $fields = array("samaccountname", "displayname");
+ $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
+ $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
+
+ $usersArray = array();
+ for ($i = 0; $i < $entries["count"]; $i++)
+ {
+ if ($includeDescription && strlen($entries[$i]["displayname"][0]) > 0)
+ {
+ $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["displayname"][0];
+ }
+ elseif ($includeDescription)
+ {
+ $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["samaccountname"][0];
+ }
+ else
+ {
+ array_push($usersArray, $entries[$i]["samaccountname"][0]);
+ }
+ }
+ if ($sorted)
+ {
+ asort($usersArray);
+ }
+ return $usersArray;
+ }
+
+ /**
+ * Converts a username (samAccountName) to a GUID
+ *
+ * @param string $username The username to query
+ * @return string
+ */
+ public function usernameToGuid($username)
+ {
+ if (!$this->adldap->getLdapBind())
+ {
+ return false;
+ }
+ if ($username === null)
+ {
+ return "Missing compulsory field [username]";
+ }
+
+ $filter = "samaccountname=" . $username;
+ $fields = array("objectGUID");
+ $sr = @ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
+ if (ldap_count_entries($this->adldap->getLdapConnection(), $sr) > 0)
+ {
+ $entry = @ldap_first_entry($this->adldap->getLdapConnection(), $sr);
+ $guid = @ldap_get_values_len($this->adldap->getLdapConnection(), $entry, 'objectGUID');
+ $strGUID = $this->adldap->utilities()->binaryToText($guid[0]);
+ return $strGUID;
+ }
+ return false;
+ }
+
+ /**
+ * Return a list of all users in AD that have a specific value in a field
+ *
+ * @param bool $includeDescription Return a description of the user
+ * @param string $searchField Field to search search for
+ * @param string $searchFilter Value to search for in the specified field
+ * @param bool $sorted Sort the user accounts
+ * @return array|bool
+ */
+ public function find($includeDescription = false, $searchField = false, $searchFilter = false, $sorted = true)
+ {
+ if (!$this->adldap->getLdapBind())
+ {
+ return false;
+ }
+
+ // Perform the search and grab all their details
+ $searchParams = "";
+ if ($searchField)
+ {
+ $searchParams = "(" . $searchField . "=" . $searchFilter . ")";
+ }
+ $filter = "(&(objectClass=user)(samaccounttype=" . adLDAP::ADLDAP_NORMAL_ACCOUNT . ")(objectCategory=person)" . $searchParams . ")";
+ $fields = array("samaccountname", "displayname");
+ $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
+ $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
+
+ $usersArray = array();
+ for ($i = 0; $i < $entries["count"]; $i++)
+ {
+ if ($includeDescription && strlen($entries[$i]["displayname"][0]) > 0)
+ {
+ $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["displayname"][0];
+ }
+ else if ($includeDescription)
+ {
+ $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["samaccountname"][0];
+ }
+ else
+ {
+ array_push($usersArray, $entries[$i]["samaccountname"][0]);
+ }
+ }
+ if ($sorted)
+ {
+ asort($usersArray);
+ }
+ return ($usersArray);
+ }
+
+ /**
+ * Move a user account to a different OU
+ *
+ * @param string $username The username to move (please be careful here!)
+ * @param array $container The container or containers to move the user to (please be careful here!).
+ * accepts containers in 1. parent 2. child order
+ * @return array|bool
+ */
+ public function move($username, $container)
+ {
+ if (!$this->adldap->getLdapBind())
+ {
+ return false;
+ }
+ if ($username === null)
+ {
+ return "Missing compulsory field [username]";
+ }
+ if ($container === null)
+ {
+ return "Missing compulsory field [container]";
+ }
+ if (!is_array($container))
+ {
+ return "Container must be an array";
+ }
+
+ $userInfo = $this->info($username, array("*"));
+ $dn = $userInfo[0]['distinguishedname'][0];
+ $newRDn = "cn=" . $username;
+ $container = array_reverse($container);
+ $newContainer = "ou=" . implode(",ou=", $container);
+ $newBaseDn = strtolower($newContainer) . "," . $this->adldap->getBaseDn();
+ $result = @ldap_rename($this->adldap->getLdapConnection(), $dn, $newRDn, $newBaseDn, true);
+ if ($result !== true)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Get the last logon time of any user as a Unix timestamp
+ *
+ * @param string $username
+ * @return float|bool $unixTimestamp
+ */
+ public function getLastLogon($username)
+ {
+ if (!$this->adldap->getLdapBind())
+ {
+ return false;
+ }
+ if ($username === null)
+ {
+ return "Missing compulsory field [username]";
+ }
+ $userInfo = $this->info($username, array("lastLogonTimestamp"));
+ $lastLogon = adLDAPUtils::convertWindowsTimeToUnixTime($userInfo[0]['lastLogonTimestamp'][0]);
+ return $lastLogon;
+ }
}
+
?>
diff --git a/lib/adLDAP/classes/adLDAPUtils.php b/lib/adLDAP/classes/adLDAPUtils.php
index a039e7d..77d178f 100644
--- a/lib/adLDAP/classes/adLDAPUtils.php
+++ b/lib/adLDAP/classes/adLDAPUtils.php
@@ -252,8 +252,8 @@ public function getVersion() {
/**
* Round a Windows timestamp down to seconds and remove the seconds between 1601-01-01 and 1970-01-01
*
- * @param long $windowsTime
- * @return long $unixTime
+ * @param float $windowsTime
+ * @return float $unixTime
*/
public static function convertWindowsTimeToUnixTime($windowsTime) {
$unixTime = round($windowsTime / 10000000) - 11644473600;