<?php /** * * IML LDAP CONNECTOR * * @author axel.hahn@unibe.ch * @license GNU GPL v3 * * SOURCE: <https://git-repo.iml.unibe.ch/iml-open-source/ldap-php-class/> * DOCS: <https://os-docs.iml.unibe.ch/ldap-php-class/index.html> * * 2022-02-22 ah added objGet(), sanitizeFilter() * 2022-08-18 ah mask password (showing 4 chars only) * 2022-08-22 ah mhash is deprecated * 2022-08-26 ah fix verifyPassword * 2024-07-11 ah php8 only: use variable types; update phpdocs * 2024-07-12 ah remove connection port (use server value "ldaps://<host>:<port>" if needed) */ class imlldap { // ---------------------------------------------------------------------- // vars // ---------------------------------------------------------------------- /** * @var array options array for an ldap connection including some base settings and DNs */ private array $_aLdap = [ 'server' => false, 'DnLdapUser' => false, // ldap rdn oder dn 'PwLdapUser' => false, 'DnUserNode' => false, // ou=People... 'DnAppNode' => false, // cn=AppGroup... 'protoVersion' => 3, 'debugLevel' => 0, ]; /** * @var object current ldap connection */ private object|bool $_ldapConn = false; /** * ldap bind object - bind was done? * @var object|bool */ private object|bool $_ldapBind = false; /** * Flag if debug mode is on * @var bool */ var bool $bDebug = false; // ---------------------------------------------------------------------- // functions // ---------------------------------------------------------------------- /** * constructor * @param array $aConfig optional set ldap connection */ public function __construct(array $aConfig = []) { if (!function_exists("ldap_connect")) { die(__CLASS__ . " ERROR: php-ldap module is not installed on this server."); } if (count($aConfig)) { $this->setConfig($aConfig); } } public function __destruct() { $this->close(); } // ---------------------------------------------------------------------- // write debug text // ---------------------------------------------------------------------- /** * turn debug messages on; * if this detail level is not enough, set a value with key debugLevel in * ldap config array * @see setConfig() */ public function debugOn(): void { $this->bDebug = true; if ($this->_aLdap['debugLevel']) { $this->_w(__FUNCTION__ . ' setting debug level ' . $this->_aLdap['debugLevel']); ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, $this->_aLdap['debugLevel']); } } /** * turn debug messages off */ public function debugOff(): void { $this->bDebug = false; ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 0); } /** * write debug message if denugOn() was fired. * * @param string $sText message text * @return boolean */ private function _w(string $sText): bool { if (!$this->bDebug) { return false; } echo __CLASS__ . ' DEBUG: ' . $sText . "<br>\n"; return true; } /** * write last ldap error as debug * * @param string $sText message text * @return boolean */ private function _wLdaperror(string $sText = ''): bool { $this->_w(($sText ? $sText . ' - ' : '') . 'last LDAP-ERROR: ' . ldap_error($this->_ldapConn)); return true; } // ---------------------------------------------------------------------- // setup // ---------------------------------------------------------------------- /** * set a ldap config or modify existing value * * @param array $aConfig new config items with these keys * 'server' => 'ldaps://ldap.example.com', * 'DnLdapUser' => 'cn=Lookup,ou=ServiceAccounts,dc=org,dc=example.com', // ldap rdn oder dn * 'PwLdapUser' => 'PasswordOfLookupUser', // password * 'DnUserNode' => 'ou=People,ou=ORG,dc=org,dc=example.com', * 'protoVersion' => 3 * 'debugLevel' => 0 // value for LDAP_OPT_DEBUG_LEVEL in debugOn() */ public function setConfig(array $aConfig = []): void { if (is_array($aConfig)) { foreach (array_keys($this->_aLdap) as $sKey) { if (array_key_exists($sKey, $aConfig)) { $this->_w(__FUNCTION__ . ' setting ldap ' . $sKey . ' = ' . $aConfig[$sKey]); $this->_aLdap[$sKey] = $aConfig[$sKey]; } } } } // ---------------------------------------------------------------------- // ldap lowlevel functions // ---------------------------------------------------------------------- /** * close an existing ldap connection */ public function close(): void { if ($this->_ldapConn) { $this->_w(__FUNCTION__ . ' closing connection.'); ldap_close($this->_ldapConn); } else { $this->_w(__FUNCTION__ . ' SKIP close.'); } $this->_ldapConn = false; } /** * connect to ldap */ public function connect(): void { if (!array_key_exists('server', $this->_aLdap) || !$this->_aLdap['server']) { die(__CLASS__ . " ERROR: no ldap server was setup set. Use setConfig() first."); } if ($this->_ldapConn) { $this->close(); } $this->_w(__FUNCTION__ . ' connect to ' . $this->_aLdap['server']); $this->_ldapConn = ldap_connect($this->_aLdap['server']); if (!$this->_ldapConn) { $this->_wLdaperror(__FUNCTION__); die(__CLASS__ . " ERROR: ldap connect failed."); } $this->_w(__FUNCTION__ . ' OK, connected.'); ldap_set_option($this->_ldapConn, LDAP_OPT_NETWORK_TIMEOUT, 3); ldap_set_option($this->_ldapConn, LDAP_OPT_TIMELIMIT, 3); if ($this->_aLdap['protoVersion']) { $this->_w(__FUNCTION__ . ' setting protocol version .' . $this->_aLdap['protoVersion']); ldap_set_option($this->_ldapConn, LDAP_OPT_PROTOCOL_VERSION, $this->_aLdap['protoVersion']); } ldap_set_option($this->_ldapConn, LDAP_OPT_REFERRALS, 0); // for AD MS Windows } /** * ldap bind connects with a ldap user. * If the ldap connection was not opened yet the connection will be established. * If a binding exists it will be unbind * * @see connect() * @see unbind() * * @param string $sUser optional: username (overrides _aLdap['DnLdapUser']) * @param string $sPw optional: password (overrides _aLdap['PwLdapUser']) */ public function bind(string $sUser = '', string $sPw = ''): bool { if (!$sUser) { $sUser = $this->_aLdap['DnLdapUser']; $sPw = $this->_aLdap['PwLdapUser']; } if (!$this->_ldapConn) { $this->connect(); } if ($this->_ldapBind) { $this->unbind(); } if (!$sUser) { $this->_w(__FUNCTION__ . ' ERROR: no user was set as first param.'); die("ERROR: no user was given to connect to ldap."); } $this->_w(__FUNCTION__ . ' with user ' . $sUser . ' PW ' . substr($sPw, 0, 4) . '**********'); $this->_ldapBind = @ldap_bind($this->_ldapConn, $sUser, $sPw); if (!$this->_ldapBind) { $this->_wLdaperror(__FUNCTION__); return false; } $this->_w(__FUNCTION__ . ' OK, successful.'); return true; } /** * ldap unbind ... if a bind exists */ public function unbind(): void { if ($this->_ldapBind && !is_bool($this->_ldapBind)) { $this->_w(__FUNCTION__ . ' ...'); ldap_unbind($this->_ldapBind); } else { $this->_w(__FUNCTION__ . ' SKIP.'); } $this->_ldapBind = false; } // ---------------------------------------------------------------------- // ldap highlevel functions // ---------------------------------------------------------------------- /** * check if a DN already exists; return is true/ false * @param string $sDn DN to check * @return boolean */ public function DnExists(string $sDn): bool { $aData = $this->searchDn($sDn, '(&(objectclass=top))', ["*"]); return is_array($aData); } /** * get simpler array from ldap_get_entries after ldap_search * If the given array doesn't contain the key "dn" it returns "false" * * @param array $aRecord single result item * @return array */ public function normalizeSearchentry(array $aRecord): bool|array { if (!is_array($aRecord) || !isset($aRecord['dn'])) { return false; } $aItem = []; unset($aRecord['count']); foreach ($aRecord as $sAttr => $aData) { if (!is_integer($sAttr)) { $value = $aData; if (is_array($aData)) { unset($aData['count']); $bUseArray = count($aData) > 1 || array_search($sAttr, ['hieradata', 'member', 'memberof', 'objectclass']) !== false; if ($bUseArray) { sort($aData); } $value = $bUseArray ? $aData : $aData[0]; } $aItem[$sAttr] = $value; } } return $aItem; } /** * sanitize value to put into a search filter * WARNING: the implementation is incomplete! I replaces the first N ascii chars only * * source: https://www.rfc-editor.org/rfc/rfc4515.txt * * @example: * $sCn = 'John Smith (john)'; * $sSearchFilter = '(cn='.$oLdap->sanitizeFilter($sCn).')'; * * @param string $s value to sanitize * @return string */ static public function sanitizeFilter(string $s): string { // helper array to replace special chars $aReplace = []; for ($i = 0; $i < 65; $i++) { $val = dechex($i); if ($val < 10) { $val = "0$val"; } $aReplace[chr($i)] = '\\' . $val; } $sReturn = $s; $sReturn = str_replace(array_keys($aReplace), array_values($aReplace), $sReturn); return $sReturn; } /** * search in ldap directory and get result as array. * It returns "false" on error: * - no ldap connection * - search failed * * @param string $sDn DN to search for * @param string $sSearchFilter filter in ldap filter syntax * @param array $aAttributesToGet flat array of attributes to fetch * @param boolean $bRecursive recusrive (uses ldap_search) or not (ldap_list) * @return boolean|array */ public function searchDn(string $sDn, string $sSearchFilter = '(objectclass=*)', array $aAttributesToGet = ["*"], bool $bRecursive = true): bool|array { if (!$this->_ldapBind) { if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } $this->_w(__FUNCTION__ . ' DN = ' . $sDn . ' filter = ' . $sSearchFilter . ' attributes = ' . print_r($aAttributesToGet, 1) . ' recursive = ' . ($bRecursive ? 'yes' : 'no')); $oLdapSearch = $bRecursive ? ldap_search($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet) : ldap_list($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet) ; if (!$oLdapSearch) { $this->_w(__FUNCTION__ . " !!!ERROR!!! filter $sSearchFilter failed "); return false; } $aItems = ldap_get_entries($this->_ldapConn, $oLdapSearch); $this->_w(__FUNCTION__ . " count of returned items: " . count($aItems)); // $this->_w(__FUNCTION__ . " <pre>".print_r($aItems,1).'</pre>'); return $aItems; } /** * search for entries in in ldap user node and get result as array * * @param string $sSearchFilter filter in ldap filter syntax * @param array $aAttributesToGet flat array of attributes to fetch * @param bool $bRecursive flag: recursive search? default: true (=yes, recursive) * * @return boolean|array */ public function searchUser(string $sSearchFilter = '', array $aAttributesToGet = ["*"], bool $bRecursive = true): bool|array { return $this->searchDn($this->_aLdap['DnUserNode'], $sSearchFilter, $aAttributesToGet, $bRecursive); /* if (!$this->_ldapBind) { $this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser']); } $this->_w(__FUNCTION__ . ' DN = ' . $this->_aLdap['DnUserNode'] . ' filter = ' . $sSearchFilter); $oLdapSearch = ldap_search( $this->_ldapConn, $this->_aLdap['DnUserNode'], $sSearchFilter, $aAttributesToGet ); $aItems = $oLdapSearch ? ldap_get_entries($this->_ldapConn, $oLdapSearch) : false; return $aItems; */ } /** * search user by a given username or email address. * It returns false if the user does not exist or is * not member of the group 'DnAppNode' (if it was set). * * @param string $sUser user id (uid) or email (mail) to search * @param array $aAttributesToGet i.e. ["ou", "sn", "vorname", "mail", "uid", "memberOf"] * @return boolean|array */ public function getUserInfo(string $sUser, array $aAttributesToGet = ["*"]): bool|array { if (!$this->_ldapBind) { if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } // generate search filter $sSearchFilter = (strpos($sUser, '@')) ? "(mail=$sUser)" : "(uid=$sUser)"; if ($this->_aLdap['DnAppNode']) { $sSearchFilter .= '(memberof=' . $this->_aLdap['DnAppNode'] . ')'; } // $sSearchFilter .= '(memberof=*)'; $sSearchFilter = '(&' . $sSearchFilter . ')'; $aItems = $this->searchUser($sSearchFilter, $aAttributesToGet); if (is_array($aItems) && count($aItems) == 2) { $this->_w(__FUNCTION__ . ' OK: I got a single result: ' . print_r($aItems[0], 1)); return $aItems[0]; } else { $this->_w(__FUNCTION__ . ' ERROR: result is: <pre>' . print_r($aItems, 1) . '</pre>'); } return false; } /** * search for a DN entry with the lookup user by a given username or * email address. It returns false if the user does not exist or is * not member of the group 'DnAppNode' (if it was set). * * @param string $sUser * @return string */ public function getUserDn(string $sUser): bool|string { $this->_w(__FUNCTION__ . '(' . $sUser . ')'); $aItem = $this->getUserInfo($sUser, ["dn"]); if (is_array($aItem) && array_key_exists('dn', $aItem)) { $this->_w(__FUNCTION__ . ' OK: dn was found ' . $aItem['dn']); return $aItem['dn']; } $this->_w(__FUNCTION__ . ' ERROR: dn was NOT found ' . print_r($aItem)); return false; } /** * set a password for a given user; * this requires a ldap bind with master/ admin account * * @param string $sUser username or email * @param string $sPW password * @return boolean */ public function setPassword(string $sUser, string $sPW): bool { if (!$this->_ldapBind) { if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } $sDn = $this->getUserDn($sUser); if ($sDn) { if (!ldap_mod_replace($this->_ldapConn, $sDn, ['userpassword' => "{MD5}" . base64_encode(pack("H*", md5($sPW)))])) { $this->_wLdaperror(__FUNCTION__); return false; } else { return true; } } $this->_w(__FUNCTION__ . ' dn not found (user does not exist in ldap) ' . $sUser); return false; } /** * get NTLM hash from a string * taken from https://secure.php.net/manual/en/ref.hash.php * * @param string $Input * @return string */ private function _getNTLMHash(string $Input): string { // Convert the password from UTF8 to UTF16 (little endian) $Input = iconv('UTF-8', 'UTF-16LE', $Input); // Encrypt it with the MD4 hash $MD4Hash = hash('md4', $Input); // Make it uppercase, not necessary, but it's common to do so with NTLM hashes $NTLMHash = strtoupper($MD4Hash); // Return the result return ($NTLMHash); } /** * set a password for a given user for Samba * this requires a ldap bind with master/ admin account * see https://msdn.microsoft.com/en-us/library/cc223248.aspx * see http://php.net/ldap-modify-batch - last examle * see https://secure.php.net/manual/en/ref.hash.php * * @param string $sUser username or email * @param string $sPW password * @return boolean */ public function setPasswordSamba(string $sUser, string $sPW): bool { $sDn = $this->getUserDn($sUser); if ($sDn) { $sPwField = 'sambaNTPassword'; $sPwValue = $this->_getNTLMHash($sPW); return $this->objUpdate( $sDn, [ $sPwField => $sPwValue, 'SambaPwdLastSet' => date('U'), ] ); } $this->_w(__FUNCTION__ . ' dn not found (user does not exist in ldap) ' . $sUser); return false; } /** * update an ldap object * this requires a ldap bind with master/ admin account * It returns true if the action was successful * * @param string $sDn dn to update * @param array $aItem array of new ldap properties * @return boolean */ public function objAdd(string $sDn, array $aItem): bool { $this->_w(__FUNCTION__ . '("' . $sDn . '", <pre>[' . print_r($aItem, 1) . ']</pre>)'); if (!$this->_ldapBind) { if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } if (!ldap_add($this->_ldapConn, $sDn, $aItem)) { $this->_wLdaperror(__FUNCTION__); return false; } return true; } /** * update an ldap attribute * this requires a ldap bind with master/ admin account * * @param string $sDn dn to update * @param array $aItem array of new ldap properties * @return boolean */ public function objAddAttr(string $sDn, array $aItem): bool { $this->_w(__FUNCTION__ . '("' . $sDn . '", [array])'); if (!$this->_ldapBind) { if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } if ($sDn && is_array($aItem)) { $this->_w(__FUNCTION__ . ' ' . ($this->_ldapConn ? 'Verbindung da' : 'kein LDAP Connect')); $this->_w(__FUNCTION__ . ' ldap_mod_add($this->_ldapConn, "' . $sDn . '", ' . print_r($aItem, 1) . ')'); if (!ldap_mod_add($this->_ldapConn, $sDn, $aItem)) { $this->_w(__FUNCTION__ . ' ldap_mod_add FAILED'); $this->_wLdaperror(__FUNCTION__); return false; } return true; } $this->_w(__FUNCTION__ . ' dn not found (item does not exist in ldap) or item was not ann array ' . print_r($aItem, 1)); return false; } /** * read attributes from ldap node with given DN (using ldap_read) * It returns "false" if the action was not successful * - no ldap connection * - DN or filter didn't match * * @param string $sDn DN to search for * @param string $sSearchFilter filter in ldap filter syntax * @param array $aAttributesToGet flat array of attributes to fetch * @return boolean|array */ public function objGet(string $sDn, string $sSearchFilter = '(objectclass=*)', array $aAttributesToGet = ["*"]): bool|array { $this->_w(__FUNCTION__ . '("' . $sDn . '", filter = ' . $sSearchFilter . ', atttr= ' . print_r($aAttributesToGet, 1) . ' )'); if (!$this->_ldapBind) { if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } $oLdapResult = ldap_read($this->_ldapConn, $sDn, $sSearchFilter, $aAttributesToGet); if (!$oLdapResult) { $this->_w(__FUNCTION__ . " !!!ERROR!!! DN or filter did not match."); return false; } return ldap_get_entries($this->_ldapConn, $oLdapResult); } /** * update an ldap object with given key-value array * if the attribute (key) does not exist it will be created. * this requires a ldap bind with master/ admin account * It returns "false" if the action failed * * @param string $sDn full DN where to update the item * @param array $aItem updated entry * @return boolean */ public function objUpdate(string $sDn, array $aItem): bool { $this->_w(__FUNCTION__ . '("' . $sDn . '", ' . print_r($aItem, 1) . ')'); if (!$this->_ldapBind) { if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } if ($sDn && is_array($aItem)) { if (!ldap_mod_replace($this->_ldapConn, $sDn, $aItem)) { $this->_w(__FUNCTION__ . ' ldap_mod_replace FAILED'); $this->_wLdaperror(__FUNCTION__); return false; } return true; } $this->_w(__FUNCTION__ . ' dn not found (item does not exist in ldap) ' . print_r($aItem, 1)); return false; } /** * delete an ldap object * this requires a ldap bind with master/ admin account * It returns "false" if the action failed * * @param string $sDn full DN to remove * @return boolean */ public function objDelete(string $sDn): bool { $this->_w(__FUNCTION__ . '("' . $sDn . '")'); if (!$this->_ldapBind) { if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } if ($sDn) { if (!ldap_delete($this->_ldapConn, $sDn)) { $this->_wLdaperror(__FUNCTION__); return false; } return true; } $this->_w(__FUNCTION__ . ' missing parameter for DN'); return false; } /** * delete attributes of an ldap object * this requires a ldap bind with master/ admin account * It returns "false" if the action failed * * @example: * remove attribute "userPassword" of user $sUserDn: * <code>$oLdap->objDeleteAttr($sUserDn, ['userPassword'=>[]]</code> * * @param string $sDn DN * @param array $aItem item to remove * @return boolean */ public function objDeleteAttr(string $sDn, array $aItem): bool { $this->_w(__FUNCTION__ . '("' . $sDn . '", [array])'); if (!$this->_ldapBind) { if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } if ($sDn && is_array($aItem)) { $this->_w(__FUNCTION__ . ' ' . ($this->_ldapConn ? 'Verbindung da' : 'kein LDAP Connect')); $this->_w(__FUNCTION__ . ' ldap_mod_del($this->_ldapConn, "' . $sDn . '", ' . print_r($aItem, 1) . ')'); if (!ldap_mod_del($this->_ldapConn, $sDn, $aItem)) { $this->_wLdaperror(__FUNCTION__); return false; } return true; } $this->_w(__FUNCTION__ . ' dn not found (item does not exist in ldap) or item was not an array ' . print_r($aItem, 1)); return false; } /** * check if an attribute exists in a DN * * @param string $sDn DN * @param string $sAttribute attribute name to check * @param string $sAttrValue value to check * @return boolean */ public function objectAttributeExists(string $sDn, string $sAttribute): bool { $this->_w(__FUNCTION__ . '("' . $sDn . '", "' . $sAttribute . '")'); if (!$this->_ldapBind) { if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } $aData = $this->searchDn($sDn, '(&(objectclass=top))', [$sAttribute]); $return = (is_array($aData) && isset($aData[0][strtolower($sAttribute)])); $this->_w(__FUNCTION__ . '(...) returns ' . ($return ? 'true' : 'false')); return $return; } /** * check if an attribute and value exist in a DN * * @param string $sDn DN * @param string $sAttribute attribute name to check * @param string $sAttrValue value to check * @return boolean */ public function objectAttributeAndValueExist(string $sDn, string $sAttribute, string $sAttrValue): bool { $this->_w(__FUNCTION__ . '("' . $sDn . '", "' . $sAttribute . '", "' . $sAttrValue . '")'); if (!$this->_ldapBind) { if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])) { return false; } } $aData = $this->searchDn($sDn, '(&(objectclass=top))', [$sAttribute]); $return = (is_array($aData) && isset($aData[0][strtolower($sAttribute)]) && array_search($sAttrValue, $aData[0][strtolower($sAttribute)]) !== false); $this->_w(__FUNCTION__ . '(...) returns ' . ($return ? 'true' : 'false')); return $return; } /** * check an attribute and value; it will be created if it does not exist * this requires a ldap bind with master/ admin account * * @param string $sDn dn to update * @param string $sAttribute attribute name to check * @param string $sAttrValue value to check * @return boolean */ public function objectAttributeAndValueMustExist(string $sDn, string $sAttribute, string $sAttrValue): bool { $this->_w(__FUNCTION__ . '("' . $sDn . '", "' . $sAttribute . '", "' . $sAttrValue . '")'); // return if it already exists if ($this->objectAttributeAndValueExist($sDn, $sAttribute, $sAttrValue)) { return true; } // create it $this->_w(__FUNCTION__ . " create $sAttribute = $sAttrValue"); $return = $this->objAddAttr($sDn, [$sAttribute => $sAttrValue]); return $return; } /** * create a new user item * this requires a ldap bind with master/ admin account * * @param array $aItem ldap properties * @param string $sDn optional DN where to create the user * @return boolean */ public function userAdd(array $aItem, string $sDn = ""): bool { if (!$sDn) { $sDn = 'cn=' . $aItem['cn'] . ',' . $this->_aLdap['DnUserNode']; } $this->_w(__FUNCTION__ . '([array], "' . $sDn . '")'); if ($sDn) { return $this->objAdd($sDn, $aItem); } $this->_w(__FUNCTION__ . ' node dn where to put the user was not found; set a value DnUserNode in ldap config or set it as 2nd parameter ' . print_r($aItem, 1)); return false; } /** * delete a user * this requires a ldap bind with master/ admin account * * @param string $sUser user to update * @param string $sPW new password to set * @return boolean */ public function userDelete(string $sUserDn): bool { $this->_w(__FUNCTION__ . '(' . $sUserDn . ')'); return $this->objDelete($sUserDn); } /** * update an ldap object * this requires a ldap bind with master/ admin account * * @param array $aItem new user data to update * @return boolean */ public function userUpdate(array $aItem): bool { $this->_w(__FUNCTION__ . '([array])'); $sDn = $this->getUserDn($aItem['uid']); if ($sDn) { if (array_key_exists('cn', $aItem)) { $this->_w(__FUNCTION__ . ' deleting cn entry.'); unset($aItem['cn']); } return $this->objUpdate($sDn, $aItem); } $this->_w(__FUNCTION__ . ' dn not found (user does not exist in ldap) ' . $sDn); return false; } /** * verify user and password * @param string $sUser username or email * @param string $sPW password * @return boolean */ public function verifyPassword(string $sUser, string $sPW): bool { $sDn = $this->getUserDn($sUser); if ($sDn) { return $this->bind($sDn, $sPW); /* if (!$this->bind($this->_aLdap['DnLdapUser'], $this->_aLdap['PwLdapUser'])){ return false; } */ } $this->_w(__FUNCTION__ . ' dn not found (user does not exist in ldap) ' . $sUser); return false; } }