Browse Source

Merge pull request #2504 from nextcloud/downstream-ldap-15

Downstream LDAP Range Support
tags/v12.0.0beta1
Roeland Jago Douma 7 years ago
parent
commit
f86f4eb98d
1 changed files with 122 additions and 22 deletions
  1. 122
    22
      apps/user_ldap/lib/Access.php

+ 122
- 22
apps/user_ldap/lib/Access.php View File

@@ -182,46 +182,146 @@ 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) {
$lowercaseAttribute = strtolower($attribute);
for($i=0;$i<$result[$attribute]['count'];$i++) {
if($this->resemblesDN($attribute)) {
$values[] = $this->helper->sanitizeDN($result[$attribute][$i]);
} elseif($lowercaseAttribute === 'objectguid' || $lowercaseAttribute === '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 [];
}
/**

Loading…
Cancel
Save