/** * Return specified user attributes from LDAP. * * @param string|array $input Optional string or array of attributes to return. * @param boolean $null Include null or non existent values. * @param boolean $changes Use the attribute changes (before change commit). * * @return mixed Ldap attribute results. * * @since 2.0 * @throws SHLdapException */ public function getAttributes($input = null, $null = false, $changes = false) { if (is_null($this->_dn)) { $this->getId(false); } elseif ($this->_dn instanceof Exception) { // Do not retry. Ldap configuration or user has problems. throw $this->_dn; } $needToFind = array(); $inputFilled = array(); if (!is_null($input)) { // Have to make sure that unless its null then its in an array $input = is_string($input) ? array($input) : $input; $inputFilled = array_fill_keys($input, null); // This array is what we must find (i.e. not in the cached variable) $needToFind = (array_keys(array_diff_key($inputFilled, $this->_attributes))); /* * Combines the current cached attributes with the input attributes with null values. * This will stop the input values from being re-queried on another method call even * if they don't exist. */ $this->_attributes = (array_merge($inputFilled, $this->_attributes)); } /* * We use the "plugin get attributes" method for efficiency purposes. On the * first execution of this method, we attempt to gather Ldap user attributes * that are required from this call in addition to what the Ldap plugins require. * * This means we should only have to call for the user attributes once from Ldap. */ if ($this->_usePlugins) { // Only run the sequence once $this->_usePlugins = false; /* * -- Get the Ldap user attributes via plugins -- * This section will get an array of user detail attributes for the user * using Ldap plugins to help with discovery of required Ldap attributes. */ $extras = SHFactory::getDispatcher('ldap')->trigger( 'onLdapBeforeRead', array(&$this, array('dn' => $this->_dn, 'source' => __METHOD__)) ); // For each of the LDAP plug-ins returned, merge their extra attributes. foreach ($extras as $extra) { $needToFind = array_merge($needToFind, $extra); } // Add both of the uid and fullname to the set of attributes to get. $needToFind[] = $this->client->ldap_fullname; $needToFind[] = $this->client->ldap_uid; // Check for a fake email $fakeEmail = (strpos($this->client->ldap_email, (SHLdap::USERNAME_REPLACE)) !== false) ? true : false; // Add the email attribute only if not a fake email is supplied. if (!$fakeEmail) { $needToFind[] = $this->client->ldap_email; } // Re-order array to ensure an LDAP read is successful and no duplicates exist. $needToFind = array_values(array_unique($needToFind)); // Swap the attribute names to array keys ready for the result $filled = array_fill_keys($needToFind, null); /* * Combines the current cached attributes with the input attributes with null values. * This will stop the input values from being re-queried on another method call even * if they don't exist. */ $this->_attributes = (array_merge($filled, $this->_attributes)); // Get Ldap user attributes $result = $this->client->read($this->_dn, null, $needToFind); if ($result->countEntries()) { // Merge the extra attributes to the cache ready for returning $this->_attributes = array_replace($this->_attributes, array_intersect_key($result->getEntry(0), $this->_attributes)); } /* * Save any attributes that weren't found in Ldap and then make it unique * so theres no duplicates in the null attributes list. */ $unreturnedVals = array_diff($needToFind, array_keys($result->getEntry(0, array()))); $this->_nullAttributes = array_merge(array_diff($unreturnedVals, $this->_nullAttributes), $this->_nullAttributes); if ($fakeEmail) { // Inject the fake email by replacing the username placeholder with the username from ldap $email = str_replace(SHLdap::USERNAME_REPLACE, $this->_attributes[$this->client->ldap_uid][0], $this->client->ldap_email); $this->_attributes[$this->client->ldap_email] = array($email); // As the last instruction from the fakeEmail condition added email to null, lets remove it if (($index = array_search($this->client->ldap_email, $this->_nullAttributes)) !== false) { unset ($this->_nullAttributes[$index]); } } if (SHLdapHelper::triggerEvent( 'onLdapAfterRead', array(&$this, &$this->_attributes, array('dn' => $this->_dn, 'source' => __METHOD__)) ) === false) { // Cancelled login due to plug-in throw new RuntimeException(JText::_('LIB_SHUSERADAPTERSLDAP_ERR_10912'), 10912); } // Blank need to find as there isn't anything more need finding $needToFind = array(); } // Check if extra attributes are required if (count($needToFind)) { $result = $this->client->read($this->_dn, null, $needToFind); if ($result->countEntries()) { // Merge the extra attributes to the cache ready for returning $this->_attributes = array_replace($this->_attributes, array_intersect_key($result->getEntry(0), $this->_attributes)); } /* * Save any attributes that weren't found in Ldap and then make it unique * so theres no duplicates in the null attributes list. */ $unreturnedVals = array_diff($needToFind, array_keys($result->getEntry(0, array()))); $this->_nullAttributes = array_merge(array_diff($unreturnedVals, $this->_nullAttributes), $this->_nullAttributes); } else { // If there are no attributes then get them all from LDAP if (!count($this->_attributes)) { $this->_attributes = $this->client->read($this->_dn, null)->getEntry(0, array()); } } $return = $this->_attributes; // Remove null values from the attributes if we dont want them if (!$null) { $return = array_diff_key($this->_attributes, array_flip($this->_nullAttributes)); $inputFilled = array_diff_key($inputFilled, array_flip($this->_nullAttributes)); } // Include staged changes to the attributes $return = $changes ? array_merge($return, $this->_changes) : $return; // Returns only the specified inputs unless all attributes are wanted return is_null($input) ? $return : array_replace($inputFilled, array_intersect_key($return, $inputFilled)); }
public function testSlapdErrorFunctions() { $config = TestsHelper::getLdapConfig(214); $ldap = new SHLdap($config); $ldap->connect(); $ldap->proxyBind(); try { $ldap->read('cn=doesntexist,dc=shmanic,dc=net'); } catch (Exception $e) { if ($code = $ldap->getErrorCode() === 32) { if (!$ldap->getErrorMsg() === SHLdap::errorToString($code)) { $this->fail('Incorrect response message'); } } } }
/** * Get a users Ldap distinguished name with optional bind authentication. * * @param boolean $authenticate Attempt to authenticate the user (i.e. * bind the user with the password supplied) * * @return string User DN. * * @since 2.1 * @throws InvalidArgumentException Invalid argument in config related error * @throws SHLdapException Ldap specific error. * @throws SHExceptionInvaliduser User invalid error. */ private function _getDn($authenticate = false) { $replaced = str_replace(SHLdap::USERNAME_REPLACE, $this->username, $this->_userParams['user_query']); /* * A basic detection check for LDAP filter. * (i.e. distinguished names do not start and end with brackets). */ $useSearch = preg_match('/(?<!\\S)[\\(]([\\S]+)[\\)](?!\\S)/', $this->_userParams['user_query']) ? true : false; SHLog::add("Attempt to retrieve user distinguished name using '{$replaced}' " . ($useSearch ? ' with search.' : ' with direct bind.'), 102, JLog::DEBUG, 'ldap'); // Get a array of distinguished names from either the search or direct bind methods. $DNs = $useSearch ? $this->_getDnBySearch() : $this->_getDnDirect(); if (empty($DNs)) { /* * Cannot find the specified username. We are going to throw * a special user not found error to try to split between * configuration errors and invalid errors. However, this might * still be a configuration error. */ throw new SHExceptionInvaliduser(JText::_('LIB_SHLDAP_ERR_10302'), 10302, $this->username); } // Check if we have to authenticate the distinguished name with a password if ($authenticate) { // Attempt to bind each distinguished name with the specified password then return it foreach ($DNs as $dn) { if ($this->client->bind($dn, $this->password)) { // Successfully binded with this distinguished name SHLog::add("Successfully authenticated {$this->username} with distinguished name {$dn}.", 102, JLog::DEBUG, 'ldap'); return $dn; } } if ($useSearch) { // User found, but was unable to bind with the supplied password throw new SHExceptionInvaliduser(JText::_('LIB_SHLDAP_ERR_10303'), 10303, $this->username); } else { // Unable to bind directly to the given distinguished name parameters throw new SHExceptionInvaliduser(JText::_('LIB_SHLDAP_ERR_10304'), 10304, $this->username); } } else { $result = false; if ($useSearch) { /* We can be sure the distinguished name(s) exists in the Ldap * directory. However, we cannot be sure if the correct * distinguished name is returned for the specified user without * authenticating. Therefore, we have to assume the first (and * hopefully only) distinguished name is correct. * If the correct configuration has been given and the Ldap * directory is well organised, this will always be correct. */ $result = $DNs[0]; } else { /* Unlike searching, binding directly means we cannot be sure * if the distinguished name(s) exists in the Ldap directory. * Therefore, lets attempt to bind with a proxy user, then Ldap * read each distinguished name's entity to check if it exists. * If binding with the proxy user fails, then we have no option * but to assume the first distinguished name exists. */ if ($this->client->proxyBind()) { foreach ($DNs as $dn) { try { $read = $this->client->read($dn, null, array('dn')); } catch (Exception $e) { // We don't need to worry about the exception too much SHLog::add("Failed to read direct bind without auth DN {$dn}.", 102, JLog::DEBUG, 'ldap'); continue; } // Check if the distinguished name entity exists if ($read->countEntries() > 0) { // It exists so we assume this is the correct distinguished name. $result = $dn; break; } } if ($result === false) { // Failed to find any of the distinguished name(s) in the Ldap directory. throw new SHExceptionInvaliduser(JText::_('LIB_SHLDAP_ERR_10305'), 10305, $this->username); } } else { // Unable to check Ldap directory, so have to assume the first is correct $result = $DNs[0]; } } SHLog::add("Using distinguished name {$result} for user {$this->username}.", 102, JLog::DEBUG, 'ldap'); return $result; } }