diff options
author | Arthur Schiwon <blizzz@owncloud.com> | 2016-01-07 20:01:24 +0100 |
---|---|---|
committer | Arthur Schiwon <blizzz@arthur-schiwon.de> | 2016-12-05 11:29:16 +0100 |
commit | 6496b95564828726eaed87ab15141846d3da0287 (patch) | |
tree | a4c2ba69a5ee0f6da18969253ec5954ffc23c8d9 /apps/user_ldap/lib | |
parent | eeb81ec783e6e555aa8f1ac3eaa806b3c9dacaf7 (diff) | |
download | nextcloud-server-6496b95564828726eaed87ab15141846d3da0287.tar.gz nextcloud-server-6496b95564828726eaed87ab15141846d3da0287.zip |
range support for LDAP read operations
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
Diffstat (limited to 'apps/user_ldap/lib')
-rw-r--r-- | apps/user_ldap/lib/Access.php | 143 |
1 files changed, 121 insertions, 22 deletions
diff --git a/apps/user_ldap/lib/Access.php b/apps/user_ldap/lib/Access.php index d88378c1888..7f5bc028b6b 100644 --- a/apps/user_ldap/lib/Access.php +++ b/apps/user_ldap/lib/Access.php @@ -182,46 +182,145 @@ class Access extends LDAPUtility implements IUserTools { // 0 won't result in replies, small numbers may leave out groups // (cf. #12306), 500 is default for paging and should work everywhere. $maxResults = $pagingSize > 20 ? $pagingSize : 500; - $this->initPagedSearch($filter, array($dn), array($attr), $maxResults, 0); + $attr = mb_strtolower($attr, 'UTF-8'); + // the actual read attribute later may contain parameters on a ranged + // request, e.g. member;range=99-199. Depends on server reply. + $attrToRead = $attr; + + $values = []; + $isRangeRequest = false; + do { + $result = $this->executeRead($cr, $dn, $attrToRead, $filter, $maxResults); + if(is_bool($result)) { + // when an exists request was run and it was successful, an empty + // array must be returned + return $result ? [] : false; + } + + if (!$isRangeRequest) { + $values = $this->extractAttributeValuesFromResult($result, $attr); + if (!empty($values)) { + return $values; + } + } + + $isRangeRequest = false; + $result = $this->extractRangeData($result, $attr); + if (!empty($result)) { + $normalizedResult = $this->extractAttributeValuesFromResult( + [ $attr => $result['values'] ], + $attr + ); + $values = array_merge($values, $normalizedResult); + + if($result['rangeHigh'] === '*') { + // when server replies with * as high range value, there are + // no more results left + return $values; + } else { + $low = $result['rangeHigh'] + 1; + $attrToRead = $result['attributeName'] . ';range=' . $low . '-*'; + $isRangeRequest = true; + } + } + } while($isRangeRequest); + + \OCP\Util::writeLog('user_ldap', 'Requested attribute '.$attr.' not found for '.$dn, \OCP\Util::DEBUG); + return false; + } + + /** + * Runs an read operation against LDAP + * + * @param resource $cr the LDAP connection + * @param string $dn + * @param string $attribute + * @param string $filter + * @param int $maxResults + * @return array|bool false if there was any error, true if an exists check + * was performed and the requested DN found, array with the + * returned data on a successful usual operation + */ + public function executeRead($cr, $dn, $attribute, $filter, $maxResults) { + $this->initPagedSearch($filter, array($dn), array($attribute), $maxResults, 0); $dn = $this->helper->DNasBaseParameter($dn); - $rr = @$this->ldap->read($cr, $dn, $filter, array($attr)); - if(!$this->ldap->isResource($rr)) { - if ($attr !== '') { + $rr = @$this->ldap->read($cr, $dn, $filter, array($attribute)); + if (!$this->ldap->isResource($rr)) { + if ($attribute !== '') { //do not throw this message on userExists check, irritates - \OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN '.$dn, \OCP\Util::DEBUG); + \OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN ' . $dn, \OCP\Util::DEBUG); } //in case an error occurs , e.g. object does not exist return false; } - if ($attr === '' && ($filter === 'objectclass=*' || $this->ldap->countEntries($cr, $rr) === 1)) { - \OCP\Util::writeLog('user_ldap', 'readAttribute: '.$dn.' found', \OCP\Util::DEBUG); - return array(); + if ($attribute === '' && ($filter === 'objectclass=*' || $this->ldap->countEntries($cr, $rr) === 1)) { + \OCP\Util::writeLog('user_ldap', 'readAttribute: ' . $dn . ' found', \OCP\Util::DEBUG); + return true; } $er = $this->ldap->firstEntry($cr, $rr); - if(!$this->ldap->isResource($er)) { + if (!$this->ldap->isResource($er)) { //did not match the filter, return false return false; } //LDAP attributes are not case sensitive $result = \OCP\Util::mb_array_change_key_case( - $this->ldap->getAttributes($cr, $er), MB_CASE_LOWER, 'UTF-8'); - $attr = mb_strtolower($attr, 'UTF-8'); + $this->ldap->getAttributes($cr, $er), MB_CASE_LOWER, 'UTF-8'); - if(isset($result[$attr]) && $result[$attr]['count'] > 0) { - $values = array(); - for($i=0;$i<$result[$attr]['count'];$i++) { - if($this->resemblesDN($attr)) { - $values[] = $this->helper->sanitizeDN($result[$attr][$i]); - } elseif(strtolower($attr) === 'objectguid' || strtolower($attr) === 'guid') { - $values[] = $this->convertObjectGUID2Str($result[$attr][$i]); + return $result; + } + + /** + * Normalizes a result grom getAttributes(), i.e. handles DNs and binary + * data if present. + * + * @param array $result from ILDAPWrapper::getAttributes() + * @param string $attribute the attribute name that was read + * @return string[] + */ + public function extractAttributeValuesFromResult($result, $attribute) { + $values = []; + if(isset($result[$attribute]) && $result[$attribute]['count'] > 0) { + for($i=0;$i<$result[$attribute]['count'];$i++) { + if($this->resemblesDN($attribute)) { + $values[] = $this->sanitizeDN($result[$attribute][$i]); + } elseif(strtolower($attribute) === 'objectguid' || strtolower($attribute) === 'guid') { + $values[] = $this->convertObjectGUID2Str($result[$attribute][$i]); } else { - $values[] = $result[$attr][$i]; + $values[] = $result[$attribute][$i]; } } - return $values; } - \OCP\Util::writeLog('user_ldap', 'Requested attribute '.$attr.' not found for '.$dn, \OCP\Util::DEBUG); - return false; + return $values; + } + + /** + * Attempts to find ranged data in a getAttribute results and extracts the + * returned values as well as information on the range and full attribute + * name for further processing. + * + * @param array $result from ILDAPWrapper::getAttributes() + * @param string $attribute the attribute name that was read. Without ";range=…" + * @return array If a range was detected with keys 'values', 'attributeName', + * 'attributeFull' and 'rangeHigh', otherwise empty. + */ + public function extractRangeData($result, $attribute) { + $keys = array_keys($result); + foreach($keys as $key) { + if($key !== $attribute && strpos($key, $attribute) === 0) { + $queryData = explode(';', $key); + if(strpos($queryData[1], 'range=') === 0) { + $high = substr($queryData[1], 1 + strpos($queryData[1], '-')); + $data = [ + 'values' => $result[$key], + 'attributeName' => $queryData[0], + 'attributeFull' => $key, + 'rangeHigh' => $high, + ]; + return $data; + } + } + } + return []; } /** |