diff --git a/lib/SimpleSAML/Auth/LDAP.php b/lib/SimpleSAML/Auth/LDAP.php index 3a3679d7e493641b1af24c221eb43362f3d6300b..f8a56ebf5f32eb21ac46070fae87fd617f1ded46 100644 --- a/lib/SimpleSAML/Auth/LDAP.php +++ b/lib/SimpleSAML/Auth/LDAP.php @@ -22,9 +22,8 @@ if (!defined('LDAP_OPT_DIAGNOSTIC_MESSAGE')) { * @author Anders Lund, UNINETT AS. <anders.lund@uninett.no> * @package SimpleSAMLphp */ -class SimpleSAML_Auth_LDAP { - - +class SimpleSAML_Auth_LDAP +{ /** * LDAP link identifier. * @@ -55,7 +54,8 @@ class SimpleSAML_Auth_LDAP { * @param bool $referrals */ // TODO: Flesh out documentation - public function __construct($hostname, $enable_tls = TRUE, $debug = FALSE, $timeout = 0, $port = 389, $referrals = TRUE) { + public function __construct($hostname, $enable_tls = true, $debug = false, $timeout = 0, $port = 389, $referrals = true) + { // Debug SimpleSAML\Logger::debug('Library - LDAP __construct(): Setup LDAP with ' . @@ -71,7 +71,7 @@ class SimpleSAML_Auth_LDAP { * * OpenLDAP 2.x.x or Netscape Directory SDK x.x needed for this option. */ - if ($debug && !ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7)) { + if ($debug && !ldap_set_option(null, LDAP_OPT_DEBUG_LEVEL, 7)) { SimpleSAML\Logger::warning('Library - LDAP __construct(): Unable to set debug level (LDAP_OPT_DEBUG_LEVEL) to 7'); } @@ -80,7 +80,7 @@ class SimpleSAML_Auth_LDAP { * doesn't actually connect to the server. */ $this->ldap = @ldap_connect($hostname, $port); - if ($this->ldap === FALSE) { + if ($this->ldap === false) { throw $this->makeException('Library - LDAP __construct(): Unable to connect to \'' . $hostname . '\'', ERR_INTERNAL); } @@ -107,7 +107,7 @@ class SimpleSAML_Auth_LDAP { } // Enable TLS, if needed - if (stripos($hostname, "ldaps:") === FALSE and $enable_tls) { + if (stripos($hostname, "ldaps:") === false and $enable_tls) { if (!@ldap_start_tls($this->ldap)) { throw $this->makeException('Library - LDAP __construct(): Unable to force TLS', ERR_INTERNAL); } @@ -123,7 +123,8 @@ class SimpleSAML_Auth_LDAP { * The exception's description * @return Exception */ - private function makeException($description, $type = NULL) { + private function makeException($description, $type = null) + { $errNo = 0x00; // Log LDAP code and description, if possible @@ -200,21 +201,21 @@ class SimpleSAML_Auth_LDAP { * - Failed to get first entry from result * - Failed to get DN for entry * @throws SimpleSAML_Error_UserNotFound if: - * - Zero entries was found + * - Zero entries were found */ - private function search($base, $attribute, $value, $searchFilter=NULL) { - + private function search($base, $attribute, $value, $searchFilter = null) + { // Create the search filter - $attribute = self::escape_filter_value($attribute, FALSE); + $attribute = self::escape_filter_value($attribute, false); $value = self::escape_filter_value($value); $filter = ''; - foreach ($attribute AS $attr) { + foreach ($attribute as $attr) { $filter .= '(' . $attr . '=' . $value. ')'; } $filter = '(|' . $filter . ')'; // Append LDAP filters if defined - if ($searchFilter!=NULL) { + if ($searchFilter != null) { $filter = "(&".$filter."".$searchFilter.")"; } @@ -222,13 +223,13 @@ class SimpleSAML_Auth_LDAP { SimpleSAML\Logger::debug('Library - LDAP search(): Searching base \'' . $base . '\' for \'' . $filter . '\''); // TODO: Should aliases be dereferenced? $result = @ldap_search($this->ldap, $base, $filter, array(), 0, 0, $this->timeout); - if ($result === FALSE) { + if ($result === false) { throw $this->makeException('Library - LDAP search(): Failed search on base \'' . $base . '\' for \'' . $filter . '\''); } // Sanity checks on search results $count = @ldap_count_entries($this->ldap, $result); - if ($count === FALSE) { + if ($count === false) { throw $this->makeException('Library - LDAP search(): Failed to get number of entries returned'); } elseif ($count > 1) { // More than one entry is found. External error @@ -241,11 +242,11 @@ class SimpleSAML_Auth_LDAP { // Resolve the DN from the search result $entry = @ldap_first_entry($this->ldap, $result); - if ($entry === FALSE) { + if ($entry === false) { throw $this->makeException('Library - LDAP search(): Unable to retrieve result after searching base \'' . $base . '\' for \'' . $filter . '\''); } $dn = @ldap_get_dn($this->ldap, $entry); - if ($dn === FALSE) { + if ($dn === false) { throw $this->makeException('Library - LDAP search(): Unable to get DN after searching base \'' . $base . '\' for \'' . $filter . '\''); } // FIXME: Are we now sure, if no excepton has been thrown, that we are returning a DN? @@ -273,15 +274,15 @@ class SimpleSAML_Auth_LDAP { * - LDAP search encounter some problems when searching cataloge * - Not able to connect to LDAP server * @throws SimpleSAML_Error_UserNotFound if: - * - $allowZeroHits er TRUE and no result is found + * - $allowZeroHits is FALSE and no result is found * */ - public function searchfordn($base, $attribute, $value, $allowZeroHits = FALSE, $searchFilter = NULL) { - + public function searchfordn($base, $attribute, $value, $allowZeroHits = false, $searchFilter = null) + { // Traverse all search bases, returning DN if found $bases = SimpleSAML\Utils\Arrays::arrayize($base); - $result = NULL; - foreach ($bases AS $current) { + $result = null; + foreach ($bases as $current) { try { // Single base search $result = $this->search($current, $attribute, $value, $searchFilter); @@ -299,7 +300,7 @@ class SimpleSAML_Auth_LDAP { SimpleSAML\Logger::debug('Library - LDAP searchfordn(): No entries found'); if ($allowZeroHits) { // Zero hits allowed - return NULL; + return null; } else { // Zero hits not allowed throw $this->makeException('Library - LDAP searchfordn(): LDAP search returned zero entries for filter \'(' . @@ -320,11 +321,11 @@ class SimpleSAML_Auth_LDAP { * @param bool $escape Weather to escape the filter values or not * @return array */ - public function searchformultiple($bases, $filters, $attributes = array(), $and = TRUE, $escape = TRUE) { - + public function searchformultiple($bases, $filters, $attributes = array(), $and = true, $escape = true) + { // Escape the filter values, if requested if ($escape) { - $filters = $this->escape_filter_value($filters, FALSE); + $filters = $this->escape_filter_value($filters, false); } // Build search filter @@ -352,16 +353,16 @@ class SimpleSAML_Auth_LDAP { } // Search each base until result is found - $result = FALSE; + $result = false; foreach ($bases as $base) { $result = @ldap_search($this->ldap, $base, $filter, $attributes, 0, 0, $this->timeout); - if ($result !== FALSE) { - break; + if ($result !== false) { + break; } } // Verify that a result was found in one of the bases - if ($result === FALSE) { + if ($result === false) { throw $this->makeException( 'ldap:LdapConnection->search_manual : Failed to search LDAP using base(s) [' . implode('; ', $bases) . '] with filter [' . $filter . ']. LDAP error [' . @@ -377,7 +378,7 @@ class SimpleSAML_Auth_LDAP { // Get all results $results = ldap_get_entries($this->ldap, $result); - if ($results === FALSE) { + if ($results === false) { throw $this->makeException( 'ldap:LdapConnection->search_manual : Unable to retrieve entries from search results' ); @@ -424,10 +425,11 @@ class SimpleSAML_Auth_LDAP { * LDAP_INAPPROPRIATE_AUTH, LDAP_INSUFFICIENT_ACCESS * @throws SimpleSAML_Error_Exception on other errors */ - public function bind($dn, $password, array $sasl_args = NULL) { + public function bind($dn, $password, array $sasl_args = null) + { $authz_id = null; - if ($sasl_args != NULL) { + if ($sasl_args != null) { if (!function_exists('ldap_sasl_bind')) { $ex_msg = 'Library - missing SASL support'; throw $this->makeException($ex_msg); @@ -447,26 +449,26 @@ class SimpleSAML_Auth_LDAP { $error = @ldap_bind($this->ldap, $dn, $password); } - if ($error === TRUE) { + if ($error === true) { // Good $this->authz_id = $authz_id; SimpleSAML\Logger::debug('Library - LDAP bind(): Bind successful with DN \'' . $dn . '\''); - return TRUE; + return true; } /* Handle errors * LDAP_INVALID_CREDENTIALS * LDAP_INSUFFICIENT_ACCESS */ - switch(ldap_errno($this->ldap)) { - case 32: // LDAP_NO_SUCH_OBJECT + switch (ldap_errno($this->ldap)) { + case 32: // LDAP_NO_SUCH_OBJECT // no break - case 47: // LDAP_X_PROXY_AUTHZ_FAILURE + case 47: // LDAP_X_PROXY_AUTHZ_FAILURE // no break - case 48: // LDAP_INAPPROPRIATE_AUTH + case 48: // LDAP_INAPPROPRIATE_AUTH // no break - case 49: // LDAP_INVALID_CREDENTIALS + case 49: // LDAP_INVALID_CREDENTIALS // no break - case 50: // LDAP_INSUFFICIENT_ACCESS + case 50: // LDAP_INSUFFICIENT_ACCESS return false; default: break; @@ -485,12 +487,12 @@ class SimpleSAML_Auth_LDAP { * @param $value * @return void */ - public function setOption($option, $value) { - + public function setOption($option, $value) + { // Attempt to set the LDAP option if (!@ldap_set_option($this->ldap, $option, $value)) { throw $this->makeException( - 'ldap:LdapConnection->setOption : Failed to set LDAP option [' . + 'ldap:LdapConnection->setOption : Failed to set LDAP option [' . $option . '] with the value [' . $value . '] error: ' . ldap_error($this->ldap), ERR_INTERNAL ); @@ -520,8 +522,8 @@ class SimpleSAML_Auth_LDAP { * The array of attributes and their values. * @see http://no.php.net/manual/en/function.ldap-read.php */ - public function getAttributes($dn, $attributes = NULL, $maxsize = NULL) { - + public function getAttributes($dn, $attributes = null, $maxsize = null) + { // Preparations, including a pretty debug message... $description = 'all attributes'; if (is_array($attributes)) { @@ -597,8 +599,8 @@ class SimpleSAML_Auth_LDAP { * @return array|bool */ // TODO: Documentation; only cleared up exception/log messages - public function validate($config, $username, $password = null) { - + public function validate($config, $username, $password = null) + { /* Escape any characters with a special meaning in LDAP. The following * characters have a special meaning (according to RFC 2253): * ',', '+', '"', '\', '<', '>', ';', '*' @@ -620,7 +622,7 @@ class SimpleSAML_Auth_LDAP { $password = addcslashes($password, ',+"\\<>;*'); if (!$this->bind($dn, $password)) { SimpleSAML\Logger::info('Library - LDAP validate(): Failed to authenticate \''. $username . '\' using DN \'' . $dn . '\''); - return FALSE; + return false; } } @@ -646,7 +648,8 @@ class SimpleSAML_Auth_LDAP { * @param array $values Array of values to escape * @return array Array $values, but escaped */ - public static function escape_filter_value($values = array(), $singleValue = TRUE) { + public static function escape_filter_value($values = array(), $singleValue = true) + { // Parameter validation if (!is_array($values)) { $values = array($values); @@ -685,7 +688,8 @@ class SimpleSAML_Auth_LDAP { * @static * @return string */ - public static function asc2hex32($string) { + public static function asc2hex32($string) + { for ($i = 0; $i < strlen($string); $i++) { $char = substr($string, $i, 1); if (ord($char) < 32) { @@ -702,7 +706,8 @@ class SimpleSAML_Auth_LDAP { /** * Convert SASL authz_id into a DN */ - private function authzid_to_dn($searchBase, $searchAttributes, $authz_id) { + private function authzid_to_dn($searchBase, $searchAttributes, $authz_id) + { if (preg_match("/^dn:/", $authz_id)) { return preg_replace("/^dn:/", "", $authz_id); } @@ -723,7 +728,8 @@ class SimpleSAML_Auth_LDAP { * And the patch against lastest PHP release: * http://cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/databases/php-ldap/files/ldap-ctrl-exop.patch */ - public function whoami($searchBase, $searchAttributes) { + public function whoami($searchBase, $searchAttributes) + { $authz_id = ''; if (function_exists('ldap_exop_whoami')) { @@ -742,5 +748,4 @@ class SimpleSAML_Auth_LDAP { return $dn; } - } diff --git a/modules/ldap/docs/ldap.md b/modules/ldap/docs/ldap.md index cbc9e6f777e70662cfb8e6a5a703666a3778aa27..098e4042f5cb925551d4edec4b58312de2459b92 100644 --- a/modules/ldap/docs/ldap.md +++ b/modules/ldap/docs/ldap.md @@ -462,13 +462,15 @@ a listing of all configuration options and their details. * that most products have a special query to recursively search * group membership. * - * Note: Only ActiveDirectory is currently supported. + * Note: Only ActiveDirectory is currently supported + * (OpenLDAP is implemented but not supported, see example below). * * Default: '' * Required: No */ 'ldap.product' => '', 'ldap.product' => 'ActiveDirectory', + 'ldap.product' => 'OpenLDAP', /** @@ -559,3 +561,14 @@ required, see the config info above for details. 'ldap.basedn' => 'DC=example,DC=org' ) +Example for unsupported OpenLDAP usage. +Intention is to filter in 'ou=groups,dc=example,dc=com' for +'(memberUid = <UID>)' and take only the attributes 'cn' (=name of the group). + + 50 => array( + 'class' => 'ldap:AttributeAddUsersGroups', + 'ldap.product' => 'OpenLDAP', + 'ldap.basedn' => 'ou=groups,dc=example,dc=org', + 'attribute.member' => 'cn', + 'attribute.memberof' => 'memberUid', + ), diff --git a/modules/ldap/lib/Auth/Process/AttributeAddFromLDAP.php b/modules/ldap/lib/Auth/Process/AttributeAddFromLDAP.php index 38c85c0b819f3dae2e11850ddf76a6861028cc3a..b1c5910cb3e77de32295e8cf03b09c628479d7e0 100644 --- a/modules/ldap/lib/Auth/Process/AttributeAddFromLDAP.php +++ b/modules/ldap/lib/Auth/Process/AttributeAddFromLDAP.php @@ -29,7 +29,8 @@ * @author Ryan Panning * @package SimpleSAMLphp */ -class sspmod_ldap_Auth_Process_AttributeAddFromLDAP extends sspmod_ldap_Auth_Process_BaseFilter { +class sspmod_ldap_Auth_Process_AttributeAddFromLDAP extends sspmod_ldap_Auth_Process_BaseFilter +{ /** * LDAP attribute to add to the request attributes @@ -60,8 +61,8 @@ class sspmod_ldap_Auth_Process_AttributeAddFromLDAP extends sspmod_ldap_Auth_Pro * @param array $config Configuration information about this filter. * @param mixed $reserved For future use. */ - public function __construct($config, $reserved) { - + public function __construct($config, $reserved) + { /* * For backwards compatibility, check for old config names * @TODO Remove after 2.0 @@ -119,7 +120,7 @@ class sspmod_ldap_Auth_Process_AttributeAddFromLDAP extends sspmod_ldap_Auth_Pro $new_attribute = $this->config->getString('attribute.new', ''); $this->search_attributes[$new_attribute] = $this->config->getString('search.attribute'); } - $this->search_filter = $this->config->getString('search.filter'); + $this->search_filter = $this->config->getString('search.filter'); // get the attribute policy $this->attr_policy = $this->config->getString('attribute.policy', 'merge'); @@ -131,7 +132,8 @@ class sspmod_ldap_Auth_Process_AttributeAddFromLDAP extends sspmod_ldap_Auth_Pro * * @param array &$request The current request */ - public function process(&$request) { + public function process(&$request) + { assert('is_array($request)'); assert('array_key_exists("Attributes", $request)'); @@ -153,7 +155,7 @@ class sspmod_ldap_Auth_Process_AttributeAddFromLDAP extends sspmod_ldap_Auth_Pro // merge the attributes into the ldap_search_filter $filter = str_replace($arrSearch, $arrReplace, $this->search_filter); - if (strpos($filter, '%') !== FALSE) { + if (strpos($filter, '%') !== false) { SimpleSAML\Logger::info('AttributeAddFromLDAP: There are non-existing attributes in the search filter. ('. $this->search_filter.')'); return; @@ -168,7 +170,7 @@ class sspmod_ldap_Auth_Process_AttributeAddFromLDAP extends sspmod_ldap_Auth_Pro // search for matching entries try { $entries = $this->getLdap()->searchformultiple($this->base_dn, $filter, - array_values($this->search_attributes), TRUE, FALSE); + array_values($this->search_attributes), true, false); } catch (Exception $e) { return; // silent fail, error is still logged by LDAP search } diff --git a/modules/ldap/lib/Auth/Process/AttributeAddUsersGroups.php b/modules/ldap/lib/Auth/Process/AttributeAddUsersGroups.php index 6364efe9eddc05952260691b201711964ceb3d9b..ada49327a2cbb170738c5f50882a8bfeb522c081 100644 --- a/modules/ldap/lib/Auth/Process/AttributeAddUsersGroups.php +++ b/modules/ldap/lib/Auth/Process/AttributeAddUsersGroups.php @@ -8,287 +8,312 @@ * @author Ryan Panning <panman@traileyes.com> * @package SimpleSAMLphp */ -class sspmod_ldap_Auth_Process_AttributeAddUsersGroups extends sspmod_ldap_Auth_Process_BaseFilter { - - - /** - * This is run when the filter is processed by SimpleSAML. - * It will attempt to find the current users groups using - * the best method possible for the LDAP product. The groups - * are then added to the request attributes. - * - * @throws SimpleSAML_Error_Exception - * @param $request - */ - public function process(&$request) { - assert('is_array($request)'); - assert('array_key_exists("Attributes", $request)'); - - // Log the process - SimpleSAML\Logger::debug( - $this->title . 'Attempting to get the users groups...' - ); - - // Reference the attributes, just to make the names shorter - $attributes =& $request['Attributes']; - $map =& $this->attribute_map; - - // Get the users groups from LDAP - $groups = $this->getGroups($attributes); - - // Make the array if it is not set already - if (!isset($attributes[$map['groups']])) { - $attributes[$map['groups']] = array(); - } - - // Must be an array, else cannot merge groups - if (!is_array($attributes[$map['groups']])) { - throw new SimpleSAML_Error_Exception( - $this->title . 'The group attribute [' . $map['groups'] . - '] is not an array of group DNs. ' . $this->var_export($attributes[$map['groups']]) - ); - } - - // Add the users group(s) - $group_attribute =& $attributes[$map['groups']]; - $group_attribute = array_merge($group_attribute, $groups); - $group_attribute = array_unique($group_attribute); - - // All done - SimpleSAML\Logger::debug( - $this->title . 'Added users groups to the group attribute [' . - $map['groups'] . ']: ' . implode('; ', $groups) - ); - } - - - /** - * This section of code was broken out because the child - * filter AuthorizeByGroup can use this method as well. - * Based on the LDAP product, it will do an optimized search - * using the required attribute values from the user to - * get their group membership, recursively. - * - * @throws SimpleSAML_Error_Exception - * @param array $attributes - * @return array - */ - protected function getGroups(array $attributes) { - - // Reference the map, just to make the name shorter - $map =& $this->attribute_map; - - // Log the request - SimpleSAML\Logger::debug( - $this->title . 'Checking for groups based on the best method for the LDAP product.' - ); - - // Based on the directory service, search LDAP for groups - // If any attributes are needed, prepare them before calling search method - switch ($this->product) { - - case 'ACTIVEDIRECTORY': - - // Log the AD specific search - SimpleSAML\Logger::debug( - $this->title . 'Searching LDAP using ActiveDirectory specific method.' - ); - - // Make sure the defined dn attribute exists - if (!isset($attributes[$map['dn']])) { - throw new SimpleSAML_Error_Exception( - $this->title . 'The DN attribute [' . $map['dn'] . - '] is not defined in the users Attributes: ' . implode(', ', array_keys($attributes)) - ); - } - - // DN attribute must have a value - if (!isset($attributes[$map['dn']][0]) || !$attributes[$map['dn']][0]) { - throw new SimpleSAML_Error_Exception( - $this->title . 'The DN attribute [' . $map['dn'] . - '] does not have a [0] value defined. ' . $this->var_export($attributes[$map['dn']]) - ); - } - - // Pass to the AD specific search - $groups = $this->searchActiveDirectory($attributes[$map['dn']][0]); - break; - - default: - - // Log the general search - SimpleSAML\Logger::debug( - $this->title . 'Searching LDAP using the default search method.' - ); - - // Make sure the defined memberOf attribute exists - if (!isset($attributes[$map['memberof']])) { - throw new SimpleSAML_Error_Exception( - $this->title . 'The memberof attribute [' . $map['memberof'] . - '] is not defined in the users Attributes: ' . implode(', ', array_keys($attributes)) - ); - } - - // MemberOf must be an array of group DN's - if (!is_array($attributes[$map['memberof']])) { - throw new SimpleSAML_Error_Exception( - $this->title . 'The memberof attribute [' . $map['memberof'] . - '] is not an array of group DNs. ' . $this->var_export($attributes[$map['memberof']]) - ); - } - - // Search for the users group membership, recursively - $groups = $this->search($attributes[$map['memberof']]); - } - - // All done - SimpleSAML\Logger::debug( - $this->title . 'User found to be a member of the groups:' . implode('; ', $groups) - ); - return $groups; - } - - - /** - * Looks for groups from the list of DN's passed. Also - * recursively searches groups for further membership. - * Avoids loops by only searching a DN once. Returns - * the list of groups found. - * - * @param array $memberof - * @return array - */ - protected function search($memberof) { - assert('is_array($memberof)'); - - // Used to determine what DN's have already been searched - static $searched = array(); - - // Init the groups variable - $groups = array(); - - // Shorten the variable name - $map =& $this->attribute_map; - - // Log the search - SimpleSAML\Logger::debug( - $this->title . 'Checking DNs for groups.' . - ' DNs: '. implode('; ', $memberof) . - ' Attributes: ' . $map['memberof'] . ', ' . $map['type'] . - ' Group Type: ' . $this->type_map['group'] - ); - - // Check each DN of the passed memberOf - foreach ($memberof as $dn) { - - // Avoid infinite loops, only need to check a DN once - if (isset($searched[$dn])) { - continue; - } - - // Track all DN's that are searched - // Use DN for key as well, isset() is faster than in_array() - $searched[$dn] = $dn; - - // Query LDAP for the attribute values for the DN - try { - $attributes = $this->getLdap()->getAttributes($dn, array($map['memberof'], $map['type'])); - } catch (SimpleSAML_Error_AuthSource $e) { - continue; // DN must not exist, just continue. Logged by the LDAP object - } - - // Only look for groups - if (!in_array($this->type_map['group'], $attributes[$map['type']])) { - continue; - } - - // Add to found groups array - $groups[] = $dn; - - // Recursively search "sub" groups - $groups = array_merge($groups, $this->search($attributes[$map['memberof']])); - } - - // Return only the unique group names - return array_unique($groups); - } - - - /** - * Searches LDAP using a ActiveDirectory specific filter, - * looking for group membership for the users DN. Returns - * the list of group DNs retrieved. - * - * @param string $dn - * @return array - */ - protected function searchActiveDirectory($dn) { - assert('is_string($dn) && $dn != ""'); - - // Shorten the variable name - $map =& $this->attribute_map; - - // Log the search - SimpleSAML\Logger::debug( - $this->title . 'Searching ActiveDirectory group membership.' . - ' DN: ' . $dn . - ' DN Attribute: ' . $map['dn'] . - ' Member Attribute: ' . $map['member'] . - ' Type Attribute: ' . $map['type'] . - ' Type Value: ' . $this->type_map['group'] . - ' Base: ' . implode('; ', $this->base_dn) - ); - - // AD connections should have this set - $this->getLdap()->setOption(LDAP_OPT_REFERRALS, 0); - - // Search AD with the specific recursive flag - try { - $entries = $this->getLdap()->searchformultiple( - $this->base_dn, - array($map['type'] => $this->type_map['group'], $map['member'] . ':1.2.840.113556.1.4.1941:' => $dn), - array($map['dn']) - ); - - // The search may throw an exception if no entries - // are found, unlikely but possible. - } catch (SimpleSAML_Error_UserNotFound $e) { - return array(); - } - - //Init the groups - $groups = array(); - - // Check each entry.. - foreach ($entries as $entry) { - - // Check for the DN using the original attribute name - if (isset($entry[$map['dn']][0])) { - $groups[] = $entry[$map['dn']][0]; - continue; - } - - // Sometimes the returned attribute names are lowercase - if (isset($entry[strtolower($map['dn'])][0])) { - $groups[] = $entry[strtolower($map['dn'])][0]; - continue; - } - - // AD queries also seem to return the objects dn by default - if (isset($entry['dn'])) { - $groups[] = $entry['dn']; - continue; - } - - // Could not find DN, log and continue - SimpleSAML\Logger::notice( - $this->title . 'The DN attribute [' . - implode(', ', array($map['dn'], strtolower($map['dn']), 'dn')) . - '] could not be found in the entry. ' . $this->var_export($entry) - ); - } - - // All done - return $groups; - } +class sspmod_ldap_Auth_Process_AttributeAddUsersGroups extends sspmod_ldap_Auth_Process_BaseFilter +{ + /** + * This is run when the filter is processed by SimpleSAML. + * It will attempt to find the current users groups using + * the best method possible for the LDAP product. The groups + * are then added to the request attributes. + * + * @throws SimpleSAML_Error_Exception + * @param $request + */ + public function process(&$request) + { + assert('is_array($request)'); + assert('array_key_exists("Attributes", $request)'); + + // Log the process + SimpleSAML\Logger::debug( + $this->title . 'Attempting to get the users groups...' + ); + + // Reference the attributes, just to make the names shorter + $attributes =& $request['Attributes']; + $map =& $this->attribute_map; + + // Get the users groups from LDAP + $groups = $this->getGroups($attributes); + + // Make the array if it is not set already + if (!isset($attributes[$map['groups']])) { + $attributes[$map['groups']] = array(); + } + + // Must be an array, else cannot merge groups + if (!is_array($attributes[$map['groups']])) { + throw new SimpleSAML_Error_Exception( + $this->title . 'The group attribute [' . $map['groups'] . + '] is not an array of group DNs. ' . $this->var_export($attributes[$map['groups']]) + ); + } + + // Add the users group(s) + $group_attribute =& $attributes[$map['groups']]; + $group_attribute = array_merge($group_attribute, $groups); + $group_attribute = array_unique($group_attribute); + + // All done + SimpleSAML\Logger::debug( + $this->title . 'Added users groups to the group attribute [' . + $map['groups'] . ']: ' . implode('; ', $groups) + ); + } + + + /** + * This section of code was broken out because the child + * filter AuthorizeByGroup can use this method as well. + * Based on the LDAP product, it will do an optimized search + * using the required attribute values from the user to + * get their group membership, recursively. + * + * @throws SimpleSAML_Error_Exception + * @param array $attributes + * @return array + */ + protected function getGroups(array $attributes) + { + // Reference the map, just to make the name shorter + $map =& $this->attribute_map; + + // Log the request + SimpleSAML\Logger::debug( + $this->title . 'Checking for groups based on the best method for the LDAP product.' + ); + + // Based on the directory service, search LDAP for groups + // If any attributes are needed, prepare them before calling search method + switch ($this->product) { + + case 'ACTIVEDIRECTORY': + + // Log the AD specific search + SimpleSAML\Logger::debug( + $this->title . 'Searching LDAP using ActiveDirectory specific method.' + ); + + // Make sure the defined dn attribute exists + if (!isset($attributes[$map['dn']])) { + throw new SimpleSAML_Error_Exception( + $this->title . 'The DN attribute [' . $map['dn'] . + '] is not defined in the users Attributes: ' . implode(', ', array_keys($attributes)) + ); + } + + // DN attribute must have a value + if (!isset($attributes[$map['dn']][0]) || !$attributes[$map['dn']][0]) { + throw new SimpleSAML_Error_Exception( + $this->title . 'The DN attribute [' . $map['dn'] . + '] does not have a [0] value defined. ' . $this->var_export($attributes[$map['dn']]) + ); + } + + // Pass to the AD specific search + $groups = $this->searchActiveDirectory($attributes[$map['dn']][0]); + break; + + case 'OPENLDAP': + // Log the OpenLDAP specific search + SimpleSAML\Logger::debug( + $this->title . 'Searching LDAP using OpenLDAP specific method.' + ); + // Print group search string and search for all group names + $openldap_base = $this->config->getString('ldap.basedn','ou=groups,dc=example,dc=com'); + SimpleSAML\Logger::debug( + $this->title . "Searching for groups in ldap.basedn ".$openldap_base." with filter (".$map['memberof']."=".$attributes['uid'][0].") and attributes ".$map['member'] + ); + $groups = array(); + try { + // Intention is to filter in 'ou=groups,dc=example,dc=com' for '(memberUid = <UID>)' and take only the attributes 'cn' (=name of the group) + $all_groups = $this->getLdap()->searchformultiple( $openldap_base, array($map['memberof'] => $attributes['uid'][0]) , array($map['member'])); + } catch (SimpleSAML_Error_UserNotFound $e) { + break; // if no groups found return with empty (still just initialized) groups array + } + // run through all groups and add each to our groups array + foreach ($all_groups as $group_entry) { + $groups[] .= $group_entry[$map['member']][0]; + } + break; + + default: + + // Log the general search + SimpleSAML\Logger::debug( + $this->title . 'Searching LDAP using the default search method.' + ); + + // Make sure the defined memberOf attribute exists + if (!isset($attributes[$map['memberof']])) { + throw new SimpleSAML_Error_Exception( + $this->title . 'The memberof attribute [' . $map['memberof'] . + '] is not defined in the users Attributes: ' . implode(', ', array_keys($attributes)) + ); + } + + // MemberOf must be an array of group DN's + if (!is_array($attributes[$map['memberof']])) { + throw new SimpleSAML_Error_Exception( + $this->title . 'The memberof attribute [' . $map['memberof'] . + '] is not an array of group DNs. ' . $this->var_export($attributes[$map['memberof']]) + ); + } + + // Search for the users group membership, recursively + $groups = $this->search($attributes[$map['memberof']]); + } + + // All done + SimpleSAML\Logger::debug( + $this->title . 'User found to be a member of the groups:' . implode('; ', $groups) + ); + return $groups; + } + + + /** + * Looks for groups from the list of DN's passed. Also + * recursively searches groups for further membership. + * Avoids loops by only searching a DN once. Returns + * the list of groups found. + * + * @param array $memberof + * @return array + */ + protected function search($memberof) + { + assert('is_array($memberof)'); + + // Used to determine what DN's have already been searched + static $searched = array(); + + // Init the groups variable + $groups = array(); + + // Shorten the variable name + $map =& $this->attribute_map; + + // Log the search + SimpleSAML\Logger::debug( + $this->title . 'Checking DNs for groups.' . + ' DNs: '. implode('; ', $memberof) . + ' Attributes: ' . $map['memberof'] . ', ' . $map['type'] . + ' Group Type: ' . $this->type_map['group'] + ); + + // Check each DN of the passed memberOf + foreach ($memberof as $dn) { + + // Avoid infinite loops, only need to check a DN once + if (isset($searched[$dn])) { + continue; + } + + // Track all DN's that are searched + // Use DN for key as well, isset() is faster than in_array() + $searched[$dn] = $dn; + + // Query LDAP for the attribute values for the DN + try { + $attributes = $this->getLdap()->getAttributes($dn, array($map['memberof'], $map['type'])); + } catch (SimpleSAML_Error_AuthSource $e) { + continue; // DN must not exist, just continue. Logged by the LDAP object + } + + // Only look for groups + if (!in_array($this->type_map['group'], $attributes[$map['type']])) { + continue; + } + + // Add to found groups array + $groups[] = $dn; + + // Recursively search "sub" groups + $groups = array_merge($groups, $this->search($attributes[$map['memberof']])); + } + + // Return only the unique group names + return array_unique($groups); + } + + + /** + * Searches LDAP using a ActiveDirectory specific filter, + * looking for group membership for the users DN. Returns + * the list of group DNs retrieved. + * + * @param string $dn + * @return array + */ + protected function searchActiveDirectory($dn) + { + assert('is_string($dn) && $dn != ""'); + + // Shorten the variable name + $map =& $this->attribute_map; + + // Log the search + SimpleSAML\Logger::debug( + $this->title . 'Searching ActiveDirectory group membership.' . + ' DN: ' . $dn . + ' DN Attribute: ' . $map['dn'] . + ' Member Attribute: ' . $map['member'] . + ' Type Attribute: ' . $map['type'] . + ' Type Value: ' . $this->type_map['group'] . + ' Base: ' . implode('; ', $this->base_dn) + ); + + // AD connections should have this set + $this->getLdap()->setOption(LDAP_OPT_REFERRALS, 0); + + // Search AD with the specific recursive flag + try { + $entries = $this->getLdap()->searchformultiple( + $this->base_dn, + array($map['type'] => $this->type_map['group'], $map['member'] . ':1.2.840.113556.1.4.1941:' => $dn), + array($map['dn']) + ); + + // The search may throw an exception if no entries + // are found, unlikely but possible. + } catch (SimpleSAML_Error_UserNotFound $e) { + return array(); + } + + //Init the groups + $groups = array(); + + // Check each entry.. + foreach ($entries as $entry) { + + // Check for the DN using the original attribute name + if (isset($entry[$map['dn']][0])) { + $groups[] = $entry[$map['dn']][0]; + continue; + } + + // Sometimes the returned attribute names are lowercase + if (isset($entry[strtolower($map['dn'])][0])) { + $groups[] = $entry[strtolower($map['dn'])][0]; + continue; + } + + // AD queries also seem to return the objects dn by default + if (isset($entry['dn'])) { + $groups[] = $entry['dn']; + continue; + } + + // Could not find DN, log and continue + SimpleSAML\Logger::notice( + $this->title . 'The DN attribute [' . + implode(', ', array($map['dn'], strtolower($map['dn']), 'dn')) . + '] could not be found in the entry. ' . $this->var_export($entry) + ); + } + + // All done + return $groups; + } } diff --git a/modules/ldap/lib/Auth/Process/BaseFilter.php b/modules/ldap/lib/Auth/Process/BaseFilter.php index 3d328e4481cafe68df03f43a5ba19d121aad8bf6..22aa197bf1bbda1f9348f0d8081543d8520730d8 100644 --- a/modules/ldap/lib/Auth/Process/BaseFilter.php +++ b/modules/ldap/lib/Auth/Process/BaseFilter.php @@ -8,270 +8,271 @@ * @author Ryan Panning <panman@traileyes.com> * @package SimpleSAMLphp */ -abstract class sspmod_ldap_Auth_Process_BaseFilter extends SimpleSAML_Auth_ProcessingFilter { - - /** - * List of attribute "alias's" linked to the real attribute - * name. Used for abstraction / configuration of the LDAP - * attribute names, which may change between dir service. - * - * @var array - */ - protected $attribute_map; - - - /** - * The base DN of the LDAP connection. Used when searching - * the LDAP server. - * - * @var string|array - */ - protected $base_dn; - - - /** - * The construct method will change the filter config into - * a SimpleSAML_Configuration object and store it here for - * later use, if needed. - * - * @var SimpleSAML_Configuration - */ - protected $config; - - - /** - * Instance, object of the ldap connection. Stored here to - * be access later during processing. - * - * @var sspmod_ldap_LdapConnection - */ - private $ldap; - - - /** - * Many times a LDAP product specific query can be used to - * speed up or reduce the filter process. This helps the - * child classes determine the product used to optimize - * those queries. - * - * @var string - */ - protected $product; - - - /** - * The class "title" used in logging and exception messages. - * This should be prepended to the beginning of the message. - * - * @var string - */ - protected $title = 'ldap:BaseFilter : '; - - - /** - * List of LDAP object types, used to determine the type of - * object that a DN references. - * - * @var array - */ - protected $type_map; - - - /** - * Checks the authsource, if defined, for configuration values - * to the LDAP server. Then sets up the LDAP connection for the - * instance/object and stores everything in class members. - * - * @throws SimpleSAML_Error_Exception - * @param array $config - * @param $reserved - */ - public function __construct(&$config, $reserved) { - parent::__construct($config, $reserved); - - // Change the class $title to match it's true name - // This way if the class is extended the proper name is used - $classname = get_class($this); - $classname = explode('_', $classname); - $this->title = 'ldap:' . end($classname) . ' : '; - - // Log the construction - SimpleSAML\Logger::debug( - $this->title . 'Creating and configuring the filter.' - ); - - // If an authsource was defined (an not empty string)... - if (isset($config['authsource']) && $config['authsource']) { - - // Log the authsource request - SimpleSAML\Logger::debug( - $this->title . 'Attempting to get configuration values from authsource [' . - $config['authsource'] . ']' - ); - - // Get the authsources file, which should contain the config - $authsource = SimpleSAML_Configuration::getConfig('authsources.php'); - - // Verify that the authsource config exists - if (!$authsource->hasValue($config['authsource'])) { - throw new SimpleSAML_Error_Exception( - $this->title . 'Authsource [' . $config['authsource'] . - '] defined in filter parameters not found in authsources.php' - ); - } - - // Get just the specified authsource config values - $authsource = $authsource->getConfigItem($config['authsource']); - $authsource = $authsource->toArray(); - - // Make sure it is an ldap source - // TODO: Support ldap:LDAPMulti, if possible - if (@$authsource[0] != 'ldap:LDAP') { - throw new SimpleSAML_Error_Exception( - $this->title . 'Authsource [' . $config['authsource'] . - '] specified in filter parameters is not an ldap:LDAP type' - ); - } - - // Build the authsource config - $authconfig = array(); - $authconfig['ldap.hostname'] = @$authsource['hostname']; - $authconfig['ldap.enable_tls'] = @$authsource['enable_tls']; - $authconfig['ldap.port'] = @$authsource['port']; - $authconfig['ldap.timeout'] = @$authsource['timeout']; - $authconfig['ldap.debug'] = @$authsource['debug']; - $authconfig['ldap.basedn'] = (@$authsource['search.enable'] ? @$authsource['search.base'] : NULL); - $authconfig['ldap.username'] = (@$authsource['search.enable'] ? @$authsource['search.username'] : NULL); - $authconfig['ldap.password'] = (@$authsource['search.enable'] ? @$authsource['search.password'] : NULL); - $authconfig['ldap.username'] = (@$authsource['priv.read'] ? @$authsource['priv.username'] : $authconfig['ldap.username']); - $authconfig['ldap.password'] = (@$authsource['priv.read'] ? @$authsource['priv.password'] : $authconfig['ldap.password']); - - // Only set the username attribute if the authsource specifies one attribute - if (@$authsource['search.enable'] && is_array(@$authsource['search.attributes']) - && count($authsource['search.attributes']) == 1) { - $authconfig['attribute.username'] = reset($authsource['search.attributes']); - } - - // Merge the authsource config with the filter config, - // but have the filter config override the authsource config - $config = array_merge($authconfig, $config); - - // Authsource complete - SimpleSAML\Logger::debug( - $this->title . 'Retrieved authsource [' . $config['authsource'] . - '] configuration values: ' . $this->var_export($authconfig) - ); - } - - // Convert the config array to a config class, - // that way we can verify type and define defaults. - // Store in the instance in-case needed later, by a child class. - $this->config = SimpleSAML_Configuration::loadFromArray($config, 'ldap:AuthProcess'); - - // Set all the filter values, setting defaults if needed - $this->base_dn = $this->config->getArrayizeString('ldap.basedn', ''); - $this->product = $this->config->getString('ldap.product', ''); - - // Cleanup the directory service, so that it is easier for - // child classes to determine service name consistently - $this->product = trim($this->product); - $this->product = strtoupper($this->product); - - // Log the member values retrieved above - SimpleSAML\Logger::debug( - $this->title . 'Configuration values retrieved;' . - ' BaseDN: ' . $this->var_export($this->base_dn) . - ' Product: ' . $this->var_export($this->product) - ); - - // Setup the attribute map which will be used to search LDAP - $this->attribute_map = array( - 'dn' => $this->config->getString('attribute.dn', 'distinguishedName'), - 'groups' => $this->config->getString('attribute.groups', 'groups'), - 'member' => $this->config->getString('attribute.member', 'member'), - 'memberof' => $this->config->getString('attribute.memberof', 'memberOf'), - 'name' => $this->config->getString('attribute.groupname', 'name'), - 'type' => $this->config->getString('attribute.type', 'objectClass'), - 'username' => $this->config->getString('attribute.username', 'sAMAccountName') - ); - - // Log the attribute map - SimpleSAML\Logger::debug( - $this->title . 'Attribute map created: ' . $this->var_export($this->attribute_map) - ); - - // Setup the object type map which is used to determine a DNs' type - $this->type_map = array( - 'group' => $this->config->getString('type.group', 'group'), - 'user' => $this->config->getString('type.user', 'user') - ); - - // Log the type map - SimpleSAML\Logger::debug( - $this->title . 'Type map created: ' . $this->var_export($this->type_map) - ); - } - - - /** - * Getter for the LDAP connection object. Created this getter - * rather than setting in the constructor to avoid unnecessarily - * connecting to LDAP when it might not be needed. - * - * @return sspmod_ldap_LdapConnection - */ - protected function getLdap() { - - // Check if already connected - if ($this->ldap) { - return $this->ldap; - } - - // Get the connection specific options - $hostname = $this->config->getString('ldap.hostname'); - $port = $this->config->getInteger('ldap.port', 389); - $enable_tls = $this->config->getBoolean('ldap.enable_tls', FALSE); - $debug = $this->config->getBoolean('ldap.debug', FALSE); - $timeout = $this->config->getInteger('ldap.timeout', 0); - $username = $this->config->getString('ldap.username', NULL); - $password = $this->config->getString('ldap.password', NULL); - - // Log the LDAP connection - SimpleSAML\Logger::debug( - $this->title . 'Connecting to LDAP server;' . - ' Hostname: ' . $hostname . - ' Port: ' . $port . - ' Enable TLS: ' . ($enable_tls ? 'Yes' : 'No') . - ' Debug: ' . ($debug ? 'Yes' : 'No') . - ' Timeout: ' . $timeout . - ' Username: ' . $username . - ' Password: ' . str_repeat('*', strlen($password)) - ); - - // Connect to the LDAP server to be queried during processing - $this->ldap = new SimpleSAML_Auth_LDAP($hostname, $enable_tls, $debug, $timeout, $port); - $this->ldap->bind($username, $password); - - // All done - return $this->ldap; - } - - - /** - * Local utility function to get details about a variable, - * basically converting it to a string to be used in a log - * message. The var_export() function returns several lines - * so this will remove the new lines and trim each line. - * - * @param mixed $value - * @return string - */ - protected function var_export($value) { - $export = var_export($value, TRUE); - $lines = explode("\n", $export); - foreach ($lines as &$line) { - $line = trim($line); - } - return implode(' ', $lines); - } +abstract class sspmod_ldap_Auth_Process_BaseFilter extends SimpleSAML_Auth_ProcessingFilter +{ + + /** + * List of attribute "alias's" linked to the real attribute + * name. Used for abstraction / configuration of the LDAP + * attribute names, which may change between dir service. + * + * @var array + */ + protected $attribute_map; + + + /** + * The base DN of the LDAP connection. Used when searching + * the LDAP server. + * + * @var string|array + */ + protected $base_dn; + + + /** + * The construct method will change the filter config into + * a SimpleSAML_Configuration object and store it here for + * later use, if needed. + * + * @var SimpleSAML_Configuration + */ + protected $config; + + + /** + * Instance, object of the ldap connection. Stored here to + * be access later during processing. + * + * @var sspmod_ldap_LdapConnection + */ + private $ldap; + + + /** + * Many times a LDAP product specific query can be used to + * speed up or reduce the filter process. This helps the + * child classes determine the product used to optimize + * those queries. + * + * @var string + */ + protected $product; + + + /** + * The class "title" used in logging and exception messages. + * This should be prepended to the beginning of the message. + * + * @var string + */ + protected $title = 'ldap:BaseFilter : '; + + + /** + * List of LDAP object types, used to determine the type of + * object that a DN references. + * + * @var array + */ + protected $type_map; + + + /** + * Checks the authsource, if defined, for configuration values + * to the LDAP server. Then sets up the LDAP connection for the + * instance/object and stores everything in class members. + * + * @throws SimpleSAML_Error_Exception + * @param array $config + * @param $reserved + */ + public function __construct(&$config, $reserved) + { + parent::__construct($config, $reserved); + + // Change the class $title to match it's true name + // This way if the class is extended the proper name is used + $classname = get_class($this); + $classname = explode('_', $classname); + $this->title = 'ldap:' . end($classname) . ' : '; + + // Log the construction + SimpleSAML\Logger::debug( + $this->title . 'Creating and configuring the filter.' + ); + + // If an authsource was defined (an not empty string)... + if (isset($config['authsource']) && $config['authsource']) { + + // Log the authsource request + SimpleSAML\Logger::debug( + $this->title . 'Attempting to get configuration values from authsource [' . + $config['authsource'] . ']' + ); + + // Get the authsources file, which should contain the config + $authsource = SimpleSAML_Configuration::getConfig('authsources.php'); + + // Verify that the authsource config exists + if (!$authsource->hasValue($config['authsource'])) { + throw new SimpleSAML_Error_Exception( + $this->title . 'Authsource [' . $config['authsource'] . + '] defined in filter parameters not found in authsources.php' + ); + } + + // Get just the specified authsource config values + $authsource = $authsource->getConfigItem($config['authsource']); + $authsource = $authsource->toArray(); + + // Make sure it is an ldap source + // TODO: Support ldap:LDAPMulti, if possible + if (@$authsource[0] != 'ldap:LDAP') { + throw new SimpleSAML_Error_Exception( + $this->title . 'Authsource [' . $config['authsource'] . + '] specified in filter parameters is not an ldap:LDAP type' + ); + } + + // Build the authsource config + $authconfig = array(); + $authconfig['ldap.hostname'] = @$authsource['hostname']; + $authconfig['ldap.enable_tls'] = @$authsource['enable_tls']; + $authconfig['ldap.port'] = @$authsource['port']; + $authconfig['ldap.timeout'] = @$authsource['timeout']; + $authconfig['ldap.debug'] = @$authsource['debug']; + $authconfig['ldap.basedn'] = (@$authsource['search.enable'] ? @$authsource['search.base'] : null); + $authconfig['ldap.username'] = (@$authsource['search.enable'] ? @$authsource['search.username'] : null); + $authconfig['ldap.password'] = (@$authsource['search.enable'] ? @$authsource['search.password'] : null); + $authconfig['ldap.username'] = (@$authsource['priv.read'] ? @$authsource['priv.username'] : $authconfig['ldap.username']); + $authconfig['ldap.password'] = (@$authsource['priv.read'] ? @$authsource['priv.password'] : $authconfig['ldap.password']); + + // Only set the username attribute if the authsource specifies one attribute + if (@$authsource['search.enable'] && is_array(@$authsource['search.attributes']) + && count($authsource['search.attributes']) == 1) { + $authconfig['attribute.username'] = reset($authsource['search.attributes']); + } + + // Merge the authsource config with the filter config, + // but have the filter config override the authsource config + $config = array_merge($authconfig, $config); + + // Authsource complete + SimpleSAML\Logger::debug( + $this->title . 'Retrieved authsource [' . $config['authsource'] . + '] configuration values: ' . $this->var_export($authconfig) + ); + } + + // Convert the config array to a config class, + // that way we can verify type and define defaults. + // Store in the instance in-case needed later, by a child class. + $this->config = SimpleSAML_Configuration::loadFromArray($config, 'ldap:AuthProcess'); + + // Set all the filter values, setting defaults if needed + $this->base_dn = $this->config->getArrayizeString('ldap.basedn', ''); + $this->product = $this->config->getString('ldap.product', ''); + + // Cleanup the directory service, so that it is easier for + // child classes to determine service name consistently + $this->product = trim($this->product); + $this->product = strtoupper($this->product); + + // Log the member values retrieved above + SimpleSAML\Logger::debug( + $this->title . 'Configuration values retrieved;' . + ' BaseDN: ' . $this->var_export($this->base_dn) . + ' Product: ' . $this->var_export($this->product) + ); + + // Setup the attribute map which will be used to search LDAP + $this->attribute_map = array( + 'dn' => $this->config->getString('attribute.dn', 'distinguishedName'), + 'groups' => $this->config->getString('attribute.groups', 'groups'), + 'member' => $this->config->getString('attribute.member', 'member'), + 'memberof' => $this->config->getString('attribute.memberof', 'memberOf'), + 'name' => $this->config->getString('attribute.groupname', 'name'), + 'type' => $this->config->getString('attribute.type', 'objectClass'), + 'username' => $this->config->getString('attribute.username', 'sAMAccountName') + ); + + // Log the attribute map + SimpleSAML\Logger::debug( + $this->title . 'Attribute map created: ' . $this->var_export($this->attribute_map) + ); + + // Setup the object type map which is used to determine a DNs' type + $this->type_map = array( + 'group' => $this->config->getString('type.group', 'group'), + 'user' => $this->config->getString('type.user', 'user') + ); + + // Log the type map + SimpleSAML\Logger::debug( + $this->title . 'Type map created: ' . $this->var_export($this->type_map) + ); + } + + /** + * Getter for the LDAP connection object. Created this getter + * rather than setting in the constructor to avoid unnecessarily + * connecting to LDAP when it might not be needed. + * + * @return sspmod_ldap_LdapConnection + */ + protected function getLdap() + { + // Check if already connected + if ($this->ldap) { + return $this->ldap; + } + + // Get the connection specific options + $hostname = $this->config->getString('ldap.hostname'); + $port = $this->config->getInteger('ldap.port', 389); + $enable_tls = $this->config->getBoolean('ldap.enable_tls', false); + $debug = $this->config->getBoolean('ldap.debug', false); + $timeout = $this->config->getInteger('ldap.timeout', 0); + $username = $this->config->getString('ldap.username', null); + $password = $this->config->getString('ldap.password', null); + + // Log the LDAP connection + SimpleSAML\Logger::debug( + $this->title . 'Connecting to LDAP server;' . + ' Hostname: ' . $hostname . + ' Port: ' . $port . + ' Enable TLS: ' . ($enable_tls ? 'Yes' : 'No') . + ' Debug: ' . ($debug ? 'Yes' : 'No') . + ' Timeout: ' . $timeout . + ' Username: ' . $username . + ' Password: ' . str_repeat('*', strlen($password)) + ); + + // Connect to the LDAP server to be queried during processing + $this->ldap = new SimpleSAML_Auth_LDAP($hostname, $enable_tls, $debug, $timeout, $port); + $this->ldap->bind($username, $password); + + // All done + return $this->ldap; + } + + /** + * Local utility function to get details about a variable, + * basically converting it to a string to be used in a log + * message. The var_export() function returns several lines + * so this will remove the new lines and trim each line. + * + * @param mixed $value + * @return string + */ + protected function var_export($value) + { + $export = var_export($value, true); + $lines = explode("\n", $export); + foreach ($lines as &$line) { + $line = trim($line); + } + return implode(' ', $lines); + } } diff --git a/modules/ldap/lib/Auth/Source/LDAP.php b/modules/ldap/lib/Auth/Source/LDAP.php index 83b35faf930bd6f9648cbe1e4b16cfdc6caedf40..7bf979a123fb85c6d80342fca32560c9dafdce39 100644 --- a/modules/ldap/lib/Auth/Source/LDAP.php +++ b/modules/ldap/lib/Auth/Source/LDAP.php @@ -10,45 +10,48 @@ * * @package SimpleSAMLphp */ -class sspmod_ldap_Auth_Source_LDAP extends sspmod_core_Auth_UserPassBase { - - /** - * A LDAP configuration object. - */ - private $ldapConfig; - - - /** - * Constructor for this authentication source. - * - * @param array $info Information about this authentication source. - * @param array $config Configuration. - */ - public function __construct($info, $config) { - assert('is_array($info)'); - assert('is_array($config)'); - - // Call the parent constructor first, as required by the interface - parent::__construct($info, $config); - - $this->ldapConfig = new sspmod_ldap_ConfigHelper($config, - 'Authentication source ' . var_export($this->authId, TRUE)); - } - - - /** - * Attempt to log in using the given username and password. - * - * @param string $username The username the user wrote. - * @param string $password The password the user wrote. - * param array $sasl_arg Associative array of SASL options - * @return array Associative array with the users attributes. - */ - protected function login($username, $password, array $sasl_args = NULL) { - assert('is_string($username)'); - assert('is_string($password)'); - - return $this->ldapConfig->login($username, $password, $sasl_args); - } +class sspmod_ldap_Auth_Source_LDAP extends sspmod_core_Auth_UserPassBase +{ + + /** + * A LDAP configuration object. + */ + private $ldapConfig; + + + /** + * Constructor for this authentication source. + * + * @param array $info Information about this authentication source. + * @param array $config Configuration. + */ + public function __construct($info, $config) + { + assert('is_array($info)'); + assert('is_array($config)'); + + // Call the parent constructor first, as required by the interface + parent::__construct($info, $config); + + $this->ldapConfig = new sspmod_ldap_ConfigHelper($config, + 'Authentication source ' . var_export($this->authId, true)); + } + + + /** + * Attempt to log in using the given username and password. + * + * @param string $username The username the user wrote. + * @param string $password The password the user wrote. + * param array $sasl_arg Associative array of SASL options + * @return array Associative array with the users attributes. + */ + protected function login($username, $password, array $sasl_args = null) + { + assert('is_string($username)'); + assert('is_string($password)'); + + return $this->ldapConfig->login($username, $password, $sasl_args); + } } diff --git a/modules/ldap/lib/Auth/Source/LDAPMulti.php b/modules/ldap/lib/Auth/Source/LDAPMulti.php index 4f5adaf5a280695cd07040c4da5eecf45f0df4ae..e38118efacd8a2a2e1c968c9932e02e8e8a343be 100644 --- a/modules/ldap/lib/Auth/Source/LDAPMulti.php +++ b/modules/ldap/lib/Auth/Source/LDAPMulti.php @@ -10,112 +10,115 @@ * * @package SimpleSAMLphp */ -class sspmod_ldap_Auth_Source_LDAPMulti extends sspmod_core_Auth_UserPassOrgBase { - - /** - * An array with descriptions for organizations. - */ - private $orgs; - - /** - * An array of organization IDs to LDAP configuration objects. - */ - private $ldapOrgs; - - /** - * Whether we should include the organization as part of the username. - */ - private $includeOrgInUsername; - - - /** - * Constructor for this authentication source. - * - * @param array $info Information about this authentication source. - * @param array $config Configuration. - */ - public function __construct($info, $config) { - assert('is_array($info)'); - assert('is_array($config)'); - - // Call the parent constructor first, as required by the interface - parent::__construct($info, $config); - - $cfgHelper = SimpleSAML_Configuration::loadFromArray($config, - 'Authentication source ' . var_export($this->authId, TRUE)); - - - $this->orgs = array(); - $this->ldapOrgs = array(); - foreach ($config as $name => $value) { - - if ($name === 'username_organization_method') { - $usernameOrgMethod = $cfgHelper->getValueValidate( - 'username_organization_method', - array('none', 'allow', 'force')); - $this->setUsernameOrgMethod($usernameOrgMethod); - continue; - } - - if ($name === 'include_organization_in_username') { - $this->includeOrgInUsername = $cfgHelper->getBoolean( - 'include_organization_in_username', FALSE); - continue; - } - - $orgCfg = $cfgHelper->getArray($name); - $orgId = $name; - - if (array_key_exists('description', $orgCfg)) { - $this->orgs[$orgId] = $orgCfg['description']; - } else { - $this->orgs[$orgId] = $orgId; - } - - $orgCfg = new sspmod_ldap_ConfigHelper($orgCfg, - 'Authentication source ' . var_export($this->authId, TRUE) . - ', organization ' . var_export($orgId, TRUE)); - $this->ldapOrgs[$orgId] = $orgCfg; - } - } - - - /** - * Attempt to log in using the given username and password. - * - * @param string $username The username the user wrote. - * @param string $password The password the user wrote. - * @param string $org The organization the user chose. - * @return array Associative array with the users attributes. - */ - protected function login($username, $password, $org, array $sasl_args = NULL) { - assert('is_string($username)'); - assert('is_string($password)'); - assert('is_string($org)'); - - if (!array_key_exists($org, $this->ldapOrgs)) { - // The user has selected an organization which doesn't exist anymore. - SimpleSAML\Logger::warning('Authentication source ' . var_export($this->authId, TRUE) . - ': Organization seems to have disappeared while the user logged in.' . - ' Organization was ' . var_export($org, TRUE)); - throw new SimpleSAML_Error_Error('WRONGUSERPASS'); - } - - if ($this->includeOrgInUsername) { - $username = $username . '@' . $org; - } - - return $this->ldapOrgs[$org]->login($username, $password, $sasl_args); - } - - - /** - * Retrieve list of organizations. - * - * @return array Associative array with the organizations. - */ - protected function getOrganizations() { - return $this->orgs; - } - +class sspmod_ldap_Auth_Source_LDAPMulti extends sspmod_core_Auth_UserPassOrgBase +{ + + /** + * An array with descriptions for organizations. + */ + private $orgs; + + /** + * An array of organization IDs to LDAP configuration objects. + */ + private $ldapOrgs; + + /** + * Whether we should include the organization as part of the username. + */ + private $includeOrgInUsername; + + + /** + * Constructor for this authentication source. + * + * @param array $info Information about this authentication source. + * @param array $config Configuration. + */ + public function __construct($info, $config) + { + assert('is_array($info)'); + assert('is_array($config)'); + + // Call the parent constructor first, as required by the interface + parent::__construct($info, $config); + + $cfgHelper = SimpleSAML_Configuration::loadFromArray($config, + 'Authentication source ' . var_export($this->authId, true)); + + + $this->orgs = array(); + $this->ldapOrgs = array(); + foreach ($config as $name => $value) { + + if ($name === 'username_organization_method') { + $usernameOrgMethod = $cfgHelper->getValueValidate( + 'username_organization_method', + array('none', 'allow', 'force')); + $this->setUsernameOrgMethod($usernameOrgMethod); + continue; + } + + if ($name === 'include_organization_in_username') { + $this->includeOrgInUsername = $cfgHelper->getBoolean( + 'include_organization_in_username', false); + continue; + } + + $orgCfg = $cfgHelper->getArray($name); + $orgId = $name; + + if (array_key_exists('description', $orgCfg)) { + $this->orgs[$orgId] = $orgCfg['description']; + } else { + $this->orgs[$orgId] = $orgId; + } + + $orgCfg = new sspmod_ldap_ConfigHelper($orgCfg, + 'Authentication source ' . var_export($this->authId, true) . + ', organization ' . var_export($orgId, true)); + $this->ldapOrgs[$orgId] = $orgCfg; + } + } + + + /** + * Attempt to log in using the given username and password. + * + * @param string $username The username the user wrote. + * @param string $password The password the user wrote. + * @param string $org The organization the user chose. + * @return array Associative array with the users attributes. + */ + protected function login($username, $password, $org, array $sasl_args = null) + { + assert('is_string($username)'); + assert('is_string($password)'); + assert('is_string($org)'); + + if (!array_key_exists($org, $this->ldapOrgs)) { + // The user has selected an organization which doesn't exist anymore. + SimpleSAML\Logger::warning('Authentication source ' . var_export($this->authId, true) . + ': Organization seems to have disappeared while the user logged in.' . + ' Organization was ' . var_export($org, true)); + throw new SimpleSAML_Error_Error('WRONGUSERPASS'); + } + + if ($this->includeOrgInUsername) { + $username = $username . '@' . $org; + } + + return $this->ldapOrgs[$org]->login($username, $password, $sasl_args); + } + + + /** + * Retrieve list of organizations. + * + * @return array Associative array with the organizations. + */ + protected function getOrganizations() + { + return $this->orgs; + } } diff --git a/modules/ldap/lib/ConfigHelper.php b/modules/ldap/lib/ConfigHelper.php index 7c8802db8acb09f0247a1adba97e67a69bd65d1b..28e4151eefa55e9cd44998c1069b5908c8bdccd6 100644 --- a/modules/ldap/lib/ConfigHelper.php +++ b/modules/ldap/lib/ConfigHelper.php @@ -8,292 +8,297 @@ * * @package SimpleSAMLphp */ -class sspmod_ldap_ConfigHelper { +class sspmod_ldap_ConfigHelper +{ + /** + * String with the location of this configuration. + * Used for error reporting. + */ + private $location; - /** - * String with the location of this configuration. - * Used for error reporting. - */ - private $location; + /** + * The hostname of the LDAP server. + */ + private $hostname; - /** - * The hostname of the LDAP server. - */ - private $hostname; + /** + * Whether we should use TLS/SSL when contacting the LDAP server. + */ + private $enableTLS; - /** - * Whether we should use TLS/SSL when contacting the LDAP server. - */ - private $enableTLS; + /** + * Whether debug output is enabled. + * + * @var bool + */ + private $debug; - /** - * Whether debug output is enabled. - * - * @var bool - */ - private $debug; + /** + * The timeout for accessing the LDAP server. + * + * @var int + */ + private $timeout; + /** + * The port used when accessing the LDAP server. + * + * @var int + */ + private $port; - /** - * The timeout for accessing the LDAP server. - * - * @var int - */ - private $timeout; + /** + * Whether to follow referrals + */ + private $referrals; - /** - * The port used when accessing the LDAP server. - * - * @var int - */ - private $port; - /** - * Whether to follow referrals - */ - private $referrals; + /** + * Whether we need to search for the users DN. + */ + private $searchEnable; - /** - * Whether we need to search for the users DN. - */ - private $searchEnable; + /** + * The username we should bind with before we can search for the user. + */ + private $searchUsername; - /** - * The username we should bind with before we can search for the user. - */ - private $searchUsername; + /** + * The password we should bind with before we can search for the user. + */ + private $searchPassword; - /** - * The password we should bind with before we can search for the user. - */ - private $searchPassword; - - - /** - * Array with the base DN(s) for the search. - */ - private $searchBase; - - /** - * Additional LDAP filter fields for the search - */ - private $searchFilter; - - /** - * The attributes which should match the username. - */ - private $searchAttributes; - - - /** - * The DN pattern we should use to create the DN from the username. - */ - private $dnPattern; - - - /** - * The attributes we should fetch. Can be NULL in which case we will fetch all attributes. - */ - private $attributes; - - - /** - * The user cannot get all attributes, privileged reader required - */ - private $privRead; - - - /** - * The DN we should bind with before we can get the attributes. - */ - private $privUsername; - - - /** - * The password we should bind with before we can get the attributes. - */ - private $privPassword; - - - /** - * Constructor for this configuration parser. - * - * @param array $config Configuration. - * @param string $location The location of this configuration. Used for error reporting. - */ - public function __construct($config, $location) { - assert('is_array($config)'); - assert('is_string($location)'); - - $this->location = $location; - - // Parse configuration - $config = SimpleSAML_Configuration::loadFromArray($config, $location); - - $this->hostname = $config->getString('hostname'); - $this->enableTLS = $config->getBoolean('enable_tls', FALSE); - $this->debug = $config->getBoolean('debug', FALSE); - $this->timeout = $config->getInteger('timeout', 0); - $this->port = $config->getInteger('port', 389); - $this->referrals = $config->getBoolean('referrals', TRUE); - $this->searchEnable = $config->getBoolean('search.enable', FALSE); - $this->privRead = $config->getBoolean('priv.read', FALSE); - - if ($this->searchEnable) { - $this->searchUsername = $config->getString('search.username', NULL); - if ($this->searchUsername !== NULL) { - $this->searchPassword = $config->getString('search.password'); - } - - $this->searchBase = $config->getArrayizeString('search.base'); - $this->searchFilter = $config->getString('search.filter',NULL); - $this->searchAttributes = $config->getArray('search.attributes'); - - } else { - $this->dnPattern = $config->getString('dnpattern'); - } - - // Are privs needed to get to the attributes? - if ($this->privRead) { - $this->privUsername = $config->getString('priv.username'); - $this->privPassword = $config->getString('priv.password'); - } - - $this->attributes = $config->getArray('attributes', NULL); - } - - - /** - * Attempt to log in using the given username and password. - * - * Will throw a SimpleSAML_Error_Error('WRONGUSERPASS') if the username or password is wrong. - * If there is a configuration problem, an Exception will be thrown. - * - * @param string $username The username the user wrote. - * @param string $password The password the user wrote. - * @param arrray $sasl_args Array of SASL options for LDAP bind. - * @return array Associative array with the users attributes. - */ - public function login($username, $password, array $sasl_args = NULL) { - assert('is_string($username)'); - assert('is_string($password)'); - - if (empty($password)) { - SimpleSAML\Logger::info($this->location . ': Login with empty password disallowed.'); - throw new SimpleSAML_Error_Error('WRONGUSERPASS'); - } - - $ldap = new SimpleSAML_Auth_LDAP($this->hostname, $this->enableTLS, $this->debug, $this->timeout, $this->port, $this->referrals); - - if (!$this->searchEnable) { - $ldapusername = addcslashes($username, ',+"\\<>;*'); - $dn = str_replace('%username%', $ldapusername, $this->dnPattern); - } else { - if ($this->searchUsername !== NULL) { - if(!$ldap->bind($this->searchUsername, $this->searchPassword)) { - throw new Exception('Error authenticating using search username & password.'); - } - } - - $dn = $ldap->searchfordn($this->searchBase, $this->searchAttributes, $username, TRUE, $this->searchFilter); - if ($dn === NULL) { - /* User not found with search. */ - SimpleSAML\Logger::info($this->location . ': Unable to find users DN. username=\'' . $username . '\''); - throw new SimpleSAML_Error_Error('WRONGUSERPASS'); - } - } - - if (!$ldap->bind($dn, $password, $sasl_args)) { - SimpleSAML\Logger::info($this->location . ': '. $username . ' failed to authenticate. DN=' . $dn); - throw new SimpleSAML_Error_Error('WRONGUSERPASS'); - } - - /* In case of SASL bind, authenticated and authorized DN may differ */ - if (isset($sasl_args)) - $dn = $ldap->whoami($this->searchBase, $this->searchAttributes); - - /* Are privs needed to get the attributes? */ - if ($this->privRead) { - /* Yes, rebind with privs */ - if(!$ldap->bind($this->privUsername, $this->privPassword)) { - throw new Exception('Error authenticating using privileged DN & password.'); - } - } - - return $ldap->getAttributes($dn, $this->attributes); - } - - - /** - * Search for a DN. - * - * @param string|array $attribute - * The attribute name(s) searched for. If set to NULL, values from - * configuration is used. - * @param string $value - * The attribute value searched for. - * @param bool $allowZeroHits - * Determines if the method will throw an exception if no - * hits are found. Defaults to FALSE. - * @return string - * The DN of the matching element, if found. If no element was - * found and $allowZeroHits is set to FALSE, an exception will - * be thrown; otherwise NULL will be returned. - * @throws SimpleSAML_Error_AuthSource if: - * - LDAP search encounter some problems when searching cataloge - * - Not able to connect to LDAP server - * @throws SimpleSAML_Error_UserNotFound if: - * - $allowZeroHits er TRUE and no result is found - * - */ - public function searchfordn($attribute, $value, $allowZeroHits) { - $ldap = new SimpleSAML_Auth_LDAP($this->hostname, - $this->enableTLS, - $this->debug, - $this->timeout, - $this->port, - $this->referrals); - - if ($attribute == NULL) - $attribute = $this->searchAttributes; - - if ($this->searchUsername !== NULL) { - if(!$ldap->bind($this->searchUsername, $this->searchPassword)) { - throw new Exception('Error authenticating using search username & password.'); - } - } - - return $ldap->searchfordn($this->searchBase, $attribute, - $value, $allowZeroHits); - } - - public function getAttributes($dn, $attributes = NULL) { - if ($attributes == NULL) - $attributes = $this->attributes; - - $ldap = new SimpleSAML_Auth_LDAP($this->hostname, - $this->enableTLS, - $this->debug, - $this->timeout, - $this->port, - $this->referrals); - - /* Are privs needed to get the attributes? */ - if ($this->privRead) { - /* Yes, rebind with privs */ - if(!$ldap->bind($this->privUsername, $this->privPassword)) { - throw new Exception('Error authenticating using privileged DN & password.'); - } - } - - return $ldap->getAttributes($dn, $attributes); - } + /** + * Array with the base DN(s) for the search. + */ + private $searchBase; + + /** + * Additional LDAP filter fields for the search + */ + private $searchFilter; + + /** + * The attributes which should match the username. + */ + private $searchAttributes; + + + /** + * The DN pattern we should use to create the DN from the username. + */ + private $dnPattern; + + + /** + * The attributes we should fetch. Can be NULL in which case we will fetch all attributes. + */ + private $attributes; + + + /** + * The user cannot get all attributes, privileged reader required + */ + private $privRead; + + + /** + * The DN we should bind with before we can get the attributes. + */ + private $privUsername; + + + /** + * The password we should bind with before we can get the attributes. + */ + private $privPassword; + + + /** + * Constructor for this configuration parser. + * + * @param array $config Configuration. + * @param string $location The location of this configuration. Used for error reporting. + */ + public function __construct($config, $location) + { + assert('is_array($config)'); + assert('is_string($location)'); + + $this->location = $location; + + // Parse configuration + $config = SimpleSAML_Configuration::loadFromArray($config, $location); + + $this->hostname = $config->getString('hostname'); + $this->enableTLS = $config->getBoolean('enable_tls', false); + $this->debug = $config->getBoolean('debug', false); + $this->timeout = $config->getInteger('timeout', 0); + $this->port = $config->getInteger('port', 389); + $this->referrals = $config->getBoolean('referrals', true); + $this->searchEnable = $config->getBoolean('search.enable', false); + $this->privRead = $config->getBoolean('priv.read', false); + + if ($this->searchEnable) { + $this->searchUsername = $config->getString('search.username', null); + if ($this->searchUsername !== null) { + $this->searchPassword = $config->getString('search.password'); + } + + $this->searchBase = $config->getArrayizeString('search.base'); + $this->searchFilter = $config->getString('search.filter', null); + $this->searchAttributes = $config->getArray('search.attributes'); + + } else { + $this->dnPattern = $config->getString('dnpattern'); + } + + // Are privs needed to get to the attributes? + if ($this->privRead) { + $this->privUsername = $config->getString('priv.username'); + $this->privPassword = $config->getString('priv.password'); + } + + $this->attributes = $config->getArray('attributes', null); + } + + + /** + * Attempt to log in using the given username and password. + * + * Will throw a SimpleSAML_Error_Error('WRONGUSERPASS') if the username or password is wrong. + * If there is a configuration problem, an Exception will be thrown. + * + * @param string $username The username the user wrote. + * @param string $password The password the user wrote. + * @param arrray $sasl_args Array of SASL options for LDAP bind. + * @return array Associative array with the users attributes. + */ + public function login($username, $password, array $sasl_args = null) + { + assert('is_string($username)'); + assert('is_string($password)'); + + if (empty($password)) { + SimpleSAML\Logger::info($this->location . ': Login with empty password disallowed.'); + throw new SimpleSAML_Error_Error('WRONGUSERPASS'); + } + + $ldap = new SimpleSAML_Auth_LDAP($this->hostname, $this->enableTLS, $this->debug, $this->timeout, $this->port, $this->referrals); + + if (!$this->searchEnable) { + $ldapusername = addcslashes($username, ',+"\\<>;*'); + $dn = str_replace('%username%', $ldapusername, $this->dnPattern); + } else { + if ($this->searchUsername !== null) { + if (!$ldap->bind($this->searchUsername, $this->searchPassword)) { + throw new Exception('Error authenticating using search username & password.'); + } + } + + $dn = $ldap->searchfordn($this->searchBase, $this->searchAttributes, $username, true, $this->searchFilter); + if ($dn === null) { + /* User not found with search. */ + SimpleSAML\Logger::info($this->location . ': Unable to find users DN. username=\'' . $username . '\''); + throw new SimpleSAML_Error_Error('WRONGUSERPASS'); + } + } + + if (!$ldap->bind($dn, $password, $sasl_args)) { + SimpleSAML\Logger::info($this->location . ': '. $username . ' failed to authenticate. DN=' . $dn); + throw new SimpleSAML_Error_Error('WRONGUSERPASS'); + } + + /* In case of SASL bind, authenticated and authorized DN may differ */ + if (isset($sasl_args)) { + $dn = $ldap->whoami($this->searchBase, $this->searchAttributes); + } + + /* Are privs needed to get the attributes? */ + if ($this->privRead) { + /* Yes, rebind with privs */ + if (!$ldap->bind($this->privUsername, $this->privPassword)) { + throw new Exception('Error authenticating using privileged DN & password.'); + } + } + + return $ldap->getAttributes($dn, $this->attributes); + } + + + /** + * Search for a DN. + * + * @param string|array $attribute + * The attribute name(s) searched for. If set to NULL, values from + * configuration is used. + * @param string $value + * The attribute value searched for. + * @param bool $allowZeroHits + * Determines if the method will throw an exception if no + * hits are found. Defaults to FALSE. + * @return string + * The DN of the matching element, if found. If no element was + * found and $allowZeroHits is set to FALSE, an exception will + * be thrown; otherwise NULL will be returned. + * @throws SimpleSAML_Error_AuthSource if: + * - LDAP search encounter some problems when searching cataloge + * - Not able to connect to LDAP server + * @throws SimpleSAML_Error_UserNotFound if: + * - $allowZeroHits is FALSE and no result is found + * + */ + public function searchfordn($attribute, $value, $allowZeroHits) + { + $ldap = new SimpleSAML_Auth_LDAP($this->hostname, + $this->enableTLS, + $this->debug, + $this->timeout, + $this->port, + $this->referrals); + + if ($attribute == null) { + $attribute = $this->searchAttributes; + } + + if ($this->searchUsername !== null) { + if (!$ldap->bind($this->searchUsername, $this->searchPassword)) { + throw new Exception('Error authenticating using search username & password.'); + } + } + + return $ldap->searchfordn($this->searchBase, $attribute, + $value, $allowZeroHits); + } + + public function getAttributes($dn, $attributes = null) + { + if ($attributes == null) { + $attributes = $this->attributes; + } + + $ldap = new SimpleSAML_Auth_LDAP($this->hostname, + $this->enableTLS, + $this->debug, + $this->timeout, + $this->port, + $this->referrals); + + /* Are privs needed to get the attributes? */ + if ($this->privRead) { + /* Yes, rebind with privs */ + if (!$ldap->bind($this->privUsername, $this->privPassword)) { + throw new Exception('Error authenticating using privileged DN & password.'); + } + } + return $ldap->getAttributes($dn, $attributes); + } }