diff --git a/lib/SimpleSAML/Auth/LDAP.php b/lib/SimpleSAML/Auth/LDAP.php index 2fb262f3b3270dcb54cc050889852c5a002ac98e..2c5f699ebc99cd22b2052c3552e455b72b42e537 100644 --- a/lib/SimpleSAML/Auth/LDAP.php +++ b/lib/SimpleSAML/Auth/LDAP.php @@ -187,6 +187,10 @@ class SimpleSAML_Auth_LDAP * The attribute name(s) to search for. * @param string $value * The attribute value to search for. + * Additional search filter + * @param string|null $searchFilter + * The scope of the search + * @param string $scope * @return string * The DN of the resulting found element. * @throws SimpleSAML_Error_Exception if: @@ -201,7 +205,7 @@ class SimpleSAML_Auth_LDAP * @throws SimpleSAML_Error_UserNotFound if: * - Zero entries were found */ - private function search($base, $attribute, $value, $searchFilter = null) + private function search($base, $attribute, $value, $searchFilter = null, $scope = "subtree") { // Create the search filter $attribute = self::escape_filter_value($attribute, false); @@ -218,8 +222,15 @@ class SimpleSAML_Auth_LDAP } // Search using generated filter - SimpleSAML\Logger::debug('Library - LDAP search(): Searching base \'' . $base . '\' for \'' . $filter . '\''); - $result = @ldap_search($this->ldap, $base, $filter, array(), 0, 0, $this->timeout, LDAP_DEREF_NEVER); + SimpleSAML\Logger::debug('Library - LDAP search(): Searching base ('. $scope .') \'' . $base . '\' for \'' . $filter . '\''); + if ($scope === 'base') { + $result = @ldap_read($this->ldap, $base, $filter, array(), 0, 0, $this->timeout, LDAP_DEREF_NEVER); + } else if ($scope === 'onelevel') { + $result = @ldap_list($this->ldap, $base, $filter, array(), 0, 0, $this->timeout, LDAP_DEREF_NEVER); + } else { + $result = @ldap_search($this->ldap, $base, $filter, array(), 0, 0, $this->timeout, LDAP_DEREF_NEVER); + } + if ($result === false) { throw $this->makeException('Library - LDAP search(): Failed search on base \'' . $base . '\' for \'' . $filter . '\''); } @@ -264,6 +275,8 @@ class SimpleSAML_Auth_LDAP * Defaults to FALSE. * @param string|null $searchFilter * Additional searchFilter to be added to the (attribute=value) filter + * @param string $scope + * The scope of the search * @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 @@ -275,14 +288,14 @@ class SimpleSAML_Auth_LDAP * - $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, $scope = 'subtree') { // Traverse all search bases, returning DN if found $bases = SimpleSAML\Utils\Arrays::arrayize($base); foreach ($bases as $current) { try { // Single base search - $result = $this->search($current, $attribute, $value, $searchFilter); + $result = $this->search($current, $attribute, $value, $searchFilter, $scope); // We don't hawe to look any futher if user is found if (!empty($result)) { @@ -316,9 +329,10 @@ class SimpleSAML_Auth_LDAP * @param string|array $attributes Array of attributes requested from LDAP * @param bool $and If multiple filters defined, then either bind them with & or | * @param bool $escape Weather to escape the filter values or not + * @param string $scope The scope of the search * @return array */ - public function searchformultiple($bases, $filters, $attributes = array(), $and = true, $escape = true) + public function searchformultiple($bases, $filters, $attributes = array(), $and = true, $escape = true, $scope = 'subtree') { // Escape the filter values, if requested if ($escape) { @@ -352,7 +366,14 @@ class SimpleSAML_Auth_LDAP // Search each base until result is found $result = false; foreach ($bases as $base) { - $result = @ldap_search($this->ldap, $base, $filter, $attributes, 0, 0, $this->timeout); + if ($scope === 'base') { + $result = @ldap_read($this->ldap, $base, $filter, $attributes, 0, 0, $this->timeout); + } else if ($scope === 'onelevel') { + $result = @ldap_list($this->ldap, $base, $filter, $attributes, 0, 0, $this->timeout); + } else { + $result = @ldap_search($this->ldap, $base, $filter, $attributes, 0, 0, $this->timeout); + } + if ($result !== false && @ldap_count_entries($this->ldap, $result) > 0) { break; } diff --git a/modules/ldap/docs/ldap.md b/modules/ldap/docs/ldap.md index fae1ca1c6ae30d51f946d48a3a8ba4088b38c510..1bad69582b69d30162c4b1b1a7aff9bba7a74919 100644 --- a/modules/ldap/docs/ldap.md +++ b/modules/ldap/docs/ldap.md @@ -63,6 +63,12 @@ authentication source: */ 'search.base' => 'ou=people,dc=example,dc=org', + /* + * The scope of the search. Valid values are 'subtree' and 'onelevel' and 'base', + * first one being the default if no value is set. + */ + 'search.scope' => 'subtree', + /* * The attribute(s) the username should match against. * @@ -94,7 +100,7 @@ You also need to update the `hostname` and `dnpattern` options. The `hostname` should be the hostname of your LDAP server, and the `dnpattern` should be a pattern which can be used to generate the `dn` of a user with a given username. - +- All other options have default values, and are not required. ### Searching for a user ### diff --git a/modules/ldap/lib/Auth/Process/BaseFilter.php b/modules/ldap/lib/Auth/Process/BaseFilter.php index c1da79255aebfd83d0e58a0b047b9013839902e1..b8339017aa3b1f28159e4f7af10098791e06d725 100644 --- a/modules/ldap/lib/Auth/Process/BaseFilter.php +++ b/modules/ldap/lib/Auth/Process/BaseFilter.php @@ -164,6 +164,9 @@ abstract class sspmod_ldap_Auth_Process_BaseFilter extends SimpleSAML_Auth_Proce if (isset($authsource['search.base'])) { $authconfig['ldap.basedn'] = $authsource['search.base']; } + if (isset($authsource['search.scope'])) { + $authconfig['ldap.scope'] = $authsource['search.scope']; + } if (isset($authsource['search.username'])) { $authconfig['ldap.username'] = $authsource['search.username']; } diff --git a/modules/ldap/lib/ConfigHelper.php b/modules/ldap/lib/ConfigHelper.php index d76684bb8026d7721891f8409d527dbbff53595e..0abfc1f42b163a5948d449ed94c14859b057f180 100644 --- a/modules/ldap/lib/ConfigHelper.php +++ b/modules/ldap/lib/ConfigHelper.php @@ -56,30 +56,31 @@ class sspmod_ldap_ConfigHelper */ private $referrals; - /** * 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 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; + /** + * The scope of the search. + */ + private $searchScope; + /** * Additional LDAP filter fields for the search */ @@ -90,31 +91,26 @@ class sspmod_ldap_ConfigHelper */ 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. */ @@ -153,6 +149,7 @@ class sspmod_ldap_ConfigHelper } $this->searchBase = $config->getArrayizeString('search.base'); + $this->searchScope = $config->getString('search.scope', 'subtree'); $this->searchFilter = $config->getString('search.filter', null); $this->searchAttributes = $config->getArray('search.attributes'); @@ -203,7 +200,7 @@ class sspmod_ldap_ConfigHelper } } - $dn = $ldap->searchfordn($this->searchBase, $this->searchAttributes, $username, true, $this->searchFilter); + $dn = $ldap->searchfordn($this->searchBase, $this->searchAttributes, $username, true, $this->searchFilter, $this->searchScope); if ($dn === null) { /* User not found with search. */ SimpleSAML\Logger::info($this->location . ': Unable to find users DN. username=\'' . $username . '\''); @@ -275,7 +272,7 @@ class sspmod_ldap_ConfigHelper } return $ldap->searchfordn($this->searchBase, $attribute, - $value, $allowZeroHits, $this->searchFilter); + $value, $allowZeroHits, $this->searchFilter, $this->searchScope); } public function getAttributes($dn, $attributes = null)