aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCôme Chilliet <91878298+come-nc@users.noreply.github.com>2022-10-20 14:48:50 +0200
committerGitHub <noreply@github.com>2022-10-20 14:48:50 +0200
commit5193b94c217e6afcfaf6fea3b931295a45cb2a09 (patch)
tree9cda8338f355d81d5972da785c70f9b66c5e1aa8
parent53b6d67bc19dfb75b6be64cae9ffeaf6fbfbdd6d (diff)
parent8b5586b7931508ff2ff850cf07c6054ab4f6b41c (diff)
downloadnextcloud-server-5193b94c217e6afcfaf6fea3b931295a45cb2a09.tar.gz
nextcloud-server-5193b94c217e6afcfaf6fea3b931295a45cb2a09.zip
Merge pull request #33703 from nextcloud/fix/ldap-simplify-paged-results
Clean up paged result handling.
-rw-r--r--apps/user_ldap/composer/composer/autoload_classmap.php2
-rw-r--r--apps/user_ldap/composer/composer/autoload_static.php2
-rw-r--r--apps/user_ldap/lib/Access.php82
-rw-r--r--apps/user_ldap/lib/ILDAPWrapper.php19
-rw-r--r--apps/user_ldap/lib/LDAP.php158
-rw-r--r--apps/user_ldap/lib/LDAPUtility.php2
-rw-r--r--apps/user_ldap/lib/PagedResults/IAdapter.php130
-rw-r--r--apps/user_ldap/lib/PagedResults/Php73.php173
8 files changed, 116 insertions, 452 deletions
diff --git a/apps/user_ldap/composer/composer/autoload_classmap.php b/apps/user_ldap/composer/composer/autoload_classmap.php
index ae112b2b604..c9001d3d59a 100644
--- a/apps/user_ldap/composer/composer/autoload_classmap.php
+++ b/apps/user_ldap/composer/composer/autoload_classmap.php
@@ -71,8 +71,6 @@ return array(
'OCA\\User_LDAP\\Migration\\Version1130Date20220110154719' => $baseDir . '/../lib/Migration/Version1130Date20220110154719.php',
'OCA\\User_LDAP\\Migration\\Version1141Date20220323143801' => $baseDir . '/../lib/Migration/Version1141Date20220323143801.php',
'OCA\\User_LDAP\\Notification\\Notifier' => $baseDir . '/../lib/Notification/Notifier.php',
- 'OCA\\User_LDAP\\PagedResults\\IAdapter' => $baseDir . '/../lib/PagedResults/IAdapter.php',
- 'OCA\\User_LDAP\\PagedResults\\Php73' => $baseDir . '/../lib/PagedResults/Php73.php',
'OCA\\User_LDAP\\PagedResults\\TLinkId' => $baseDir . '/../lib/PagedResults/TLinkId.php',
'OCA\\User_LDAP\\Proxy' => $baseDir . '/../lib/Proxy.php',
'OCA\\User_LDAP\\Settings\\Admin' => $baseDir . '/../lib/Settings/Admin.php',
diff --git a/apps/user_ldap/composer/composer/autoload_static.php b/apps/user_ldap/composer/composer/autoload_static.php
index 3ff92c350f5..53d7bcb6481 100644
--- a/apps/user_ldap/composer/composer/autoload_static.php
+++ b/apps/user_ldap/composer/composer/autoload_static.php
@@ -86,8 +86,6 @@ class ComposerStaticInitUser_LDAP
'OCA\\User_LDAP\\Migration\\Version1130Date20220110154719' => __DIR__ . '/..' . '/../lib/Migration/Version1130Date20220110154719.php',
'OCA\\User_LDAP\\Migration\\Version1141Date20220323143801' => __DIR__ . '/..' . '/../lib/Migration/Version1141Date20220323143801.php',
'OCA\\User_LDAP\\Notification\\Notifier' => __DIR__ . '/..' . '/../lib/Notification/Notifier.php',
- 'OCA\\User_LDAP\\PagedResults\\IAdapter' => __DIR__ . '/..' . '/../lib/PagedResults/IAdapter.php',
- 'OCA\\User_LDAP\\PagedResults\\Php73' => __DIR__ . '/..' . '/../lib/PagedResults/Php73.php',
'OCA\\User_LDAP\\PagedResults\\TLinkId' => __DIR__ . '/..' . '/../lib/PagedResults/TLinkId.php',
'OCA\\User_LDAP\\Proxy' => __DIR__ . '/..' . '/../lib/Proxy.php',
'OCA\\User_LDAP\\Settings\\Admin' => __DIR__ . '/..' . '/../lib/Settings/Admin.php',
diff --git a/apps/user_ldap/lib/Access.php b/apps/user_ldap/lib/Access.php
index cec192721a5..6693f058b96 100644
--- a/apps/user_ldap/lib/Access.php
+++ b/apps/user_ldap/lib/Access.php
@@ -95,8 +95,7 @@ class Access extends LDAPUtility {
private $ncUserManager;
/** @var LoggerInterface */
private $logger;
- /** @var string */
- private $lastCookie = '';
+ private string $lastCookie = '';
public function __construct(
Connection $connection,
@@ -194,15 +193,6 @@ class Access extends LDAPUtility {
$this->logger->debug('LDAP resource not available.', ['app' => 'user_ldap']);
return false;
}
- //Cancel possibly running Paged Results operation, otherwise we run in
- //LDAP protocol errors
- $this->abandonPagedSearch();
- // openLDAP requires that we init a new Paged Search. Not needed by AD,
- // but does not hurt either.
- $pagingSize = (int)$this->connection->ldapPagingSize;
- // 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;
$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.
@@ -211,7 +201,7 @@ class Access extends LDAPUtility {
$values = [];
$isRangeRequest = false;
do {
- $result = $this->executeRead($dn, $attrToRead, $filter, $maxResults);
+ $result = $this->executeRead($dn, $attrToRead, $filter);
if (is_bool($result)) {
// when an exists request was run and it was successful, an empty
// array must be returned
@@ -258,15 +248,7 @@ class Access extends LDAPUtility {
* returned data on a successful usual operation
* @throws ServerNotAvailableException
*/
- public function executeRead(string $dn, string $attribute, string $filter, int $maxResults) {
- try {
- $this->initPagedSearch($filter, $dn, [$attribute], $maxResults, 0);
- } catch (NoMoreResults $e) {
- // does not happen, no pagination here since offset is 0, but the
- // previous call is needed for a potential reset of the state.
- // Tools would still point out a possible NoMoreResults exception.
- return false;
- }
+ public function executeRead(string $dn, string $attribute, string $filter) {
$dn = $this->helper->DNasBaseParameter($dn);
$rr = @$this->invokeLDAPMethod('read', $dn, $filter, [$attribute]);
if (!$this->ldap->isResource($rr)) {
@@ -1098,7 +1080,7 @@ class Access extends LDAPUtility {
string $filter,
string $base,
?array &$attr,
- ?int $limit,
+ ?int $pageSize,
?int $offset
) {
// See if we have a resource, in case not cancel with message
@@ -1112,13 +1094,13 @@ class Access extends LDAPUtility {
//check whether paged search should be attempted
try {
- $pagedSearchOK = $this->initPagedSearch($filter, $base, $attr, (int)$limit, (int)$offset);
+ [$pagedSearchOK, $pageSize, $cookie] = $this->initPagedSearch($filter, $base, $attr, (int)$pageSize, (int)$offset);
} catch (NoMoreResults $e) {
// beyond last results page
return false;
}
- $sr = $this->invokeLDAPMethod('search', $base, $filter, $attr);
+ $sr = $this->invokeLDAPMethod('search', $base, $filter, $attr, 0, 0, $pageSize, $cookie);
$error = $this->ldap->errno($this->connection->getConnectionResource());
if (!$this->ldap->isResource($sr) || $error !== 0) {
$this->logger->error('Attempt for Paging? ' . print_r($pagedSearchOK, true), ['app' => 'user_ldap']);
@@ -1910,7 +1892,6 @@ class Access extends LDAPUtility {
if ($this->lastCookie === '') {
return;
}
- $this->invokeLDAPMethod('controlPagedResult', 0, false);
$this->getPagedSearchResultState();
$this->lastCookie = '';
}
@@ -1927,7 +1908,7 @@ class Access extends LDAPUtility {
* @return bool
*/
public function hasMoreResults() {
- if (empty($this->lastCookie) && $this->lastCookie !== '0') {
+ if ($this->lastCookie === '') {
// as in RFC 2696, when all results are returned, the cookie will
// be empty.
return false;
@@ -1951,11 +1932,11 @@ class Access extends LDAPUtility {
* Prepares a paged search, if possible
*
* @param string $filter the LDAP filter for the search
- * @param string[] $bases an array containing the LDAP subtree(s) that shall be searched
+ * @param string $base the LDAP subtree that shall be searched
* @param string[] $attr optional, when a certain attribute shall be filtered outside
* @param int $limit
* @param int $offset
- * @return bool|true
+ * @return array{bool, int, string}
* @throws ServerNotAvailableException
* @throws NoMoreResults
*/
@@ -1963,29 +1944,29 @@ class Access extends LDAPUtility {
string $filter,
string $base,
?array $attr,
- int $limit,
+ int $pageSize,
int $offset
- ): bool {
+ ): array {
$pagedSearchOK = false;
- if ($limit !== 0) {
+ if ($pageSize !== 0) {
$this->logger->debug(
- 'initializing paged search for filter {filter}, base {base}, attr {attr}, limit {limit}, offset {offset}',
+ 'initializing paged search for filter {filter}, base {base}, attr {attr}, pageSize {pageSize}, offset {offset}',
[
'app' => 'user_ldap',
'filter' => $filter,
'base' => $base,
'attr' => $attr,
- 'limit' => $limit,
+ 'pageSize' => $pageSize,
'offset' => $offset
]
);
- //get the cookie from the search for the previous search, required by LDAP
- if (empty($this->lastCookie) && $this->lastCookie !== "0" && ($offset > 0)) {
+ // Get the cookie from the search for the previous search, required by LDAP
+ if (($this->lastCookie === '') && ($offset > 0)) {
// no cookie known from a potential previous search. We need
// to start from 0 to come to the desired page. cookie value
// of '0' is valid, because 389ds
- $reOffset = ($offset - $limit) < 0 ? 0 : $offset - $limit;
- $this->search($filter, $base, $attr, $limit, $reOffset, true);
+ $reOffset = ($offset - $pageSize) < 0 ? 0 : $offset - $pageSize;
+ $this->search($filter, $base, $attr, $pageSize, $reOffset, true);
if (!$this->hasMoreResults()) {
// when the cookie is reset with != 0 offset, there are no further
// results, so stop.
@@ -1996,19 +1977,15 @@ class Access extends LDAPUtility {
//since offset = 0, this is a new search. We abandon other searches that might be ongoing.
$this->abandonPagedSearch();
}
- $pagedSearchOK = true === $this->invokeLDAPMethod(
- 'controlPagedResult', $limit, false
- );
- if ($pagedSearchOK) {
- $this->logger->debug('Ready for a paged search', ['app' => 'user_ldap']);
- }
- /* ++ Fixing RHDS searches with pages with zero results ++
- * We couldn't get paged searches working with our RHDS for login ($limit = 0),
- * due to pages with zero results.
- * So we added "&& !empty($this->lastCookie)" to this test to ignore pagination
- * if we don't have a previous paged search.
- */
- } elseif (!empty($this->lastCookie)) {
+ $this->logger->debug('Ready for a paged search', ['app' => 'user_ldap']);
+ return [true, $pageSize, $this->lastCookie];
+ /* ++ Fixing RHDS searches with pages with zero results ++
+ * We couldn't get paged searches working with our RHDS for login ($limit = 0),
+ * due to pages with zero results.
+ * So we added "&& !empty($this->lastCookie)" to this test to ignore pagination
+ * if we don't have a previous paged search.
+ */
+ } elseif ($this->lastCookie !== '') {
// a search without limit was requested. However, if we do use
// Paged Search once, we always must do it. This requires us to
// initialize it with the configured page size.
@@ -2016,11 +1993,10 @@ class Access extends LDAPUtility {
// in case someone set it to 0 … use 500, otherwise no results will
// be returned.
$pageSize = (int)$this->connection->ldapPagingSize > 0 ? (int)$this->connection->ldapPagingSize : 500;
- $pagedSearchOK = $this->invokeLDAPMethod('controlPagedResult',
- $pageSize, false);
+ return [true, $pageSize, $this->lastCookie];
}
- return $pagedSearchOK;
+ return [false, $pageSize, ''];
}
/**
diff --git a/apps/user_ldap/lib/ILDAPWrapper.php b/apps/user_ldap/lib/ILDAPWrapper.php
index e72d85ac2b9..b5c5568348e 100644
--- a/apps/user_ldap/lib/ILDAPWrapper.php
+++ b/apps/user_ldap/lib/ILDAPWrapper.php
@@ -53,16 +53,6 @@ interface ILDAPWrapper {
public function connect($host, $port);
/**
- * Send LDAP pagination control
- * @param resource|\LDAP\Connection $link LDAP link resource
- * @param int $pageSize number of results per page
- * @param bool $isCritical Indicates whether the pagination is critical of not.
- * @param string $cookie structure sent by LDAP server
- * @return bool true on success, false otherwise
- */
- public function controlPagedResult($link, $pageSize, $isCritical);
-
- /**
* Retrieve the LDAP pagination cookie
* @param resource|\LDAP\Connection $link LDAP link resource
* @param resource|\LDAP\Result $result LDAP result resource
@@ -164,7 +154,7 @@ interface ILDAPWrapper {
* @param int $limit optional, limits the result entries
* @return resource|\LDAP\Result|false an LDAP search result resource, false on error
*/
- public function search($link, $baseDN, $filter, $attr, $attrsOnly = 0, $limit = 0);
+ public function search($link, string $baseDN, string $filter, array $attr, int $attrsOnly = 0, int $limit = 0, int $pageSize = 0, string $cookie = '');
/**
* Replace the value of a userPassword by $password
@@ -176,6 +166,13 @@ interface ILDAPWrapper {
public function modReplace($link, $userDN, $password);
/**
+ * Performs a PASSWD extended operation.
+ * @param resource|\LDAP\Connection $link LDAP link resource
+ * @return bool|string The generated password if new_password is empty or omitted. Otherwise true on success and false on failure.
+ */
+ public function exopPasswd($link, string $userDN, string $oldPassword, string $password);
+
+ /**
* Sets the value of the specified option to be $value
* @param resource|\LDAP\Connection $link LDAP link resource
* @param int $option a defined LDAP Server option
diff --git a/apps/user_ldap/lib/LDAP.php b/apps/user_ldap/lib/LDAP.php
index 545a09ca464..c03337a9e51 100644
--- a/apps/user_ldap/lib/LDAP.php
+++ b/apps/user_ldap/lib/LDAP.php
@@ -37,21 +37,16 @@ use OCP\Profiler\IProfiler;
use OC\ServerNotAvailableException;
use OCA\User_LDAP\DataCollector\LdapDataCollector;
use OCA\User_LDAP\Exceptions\ConstraintViolationException;
-use OCA\User_LDAP\PagedResults\IAdapter;
-use OCA\User_LDAP\PagedResults\Php73;
+use Psr\Log\LoggerInterface;
class LDAP implements ILDAPWrapper {
- protected $logFile = '';
- protected $curFunc = '';
- protected $curArgs = [];
-
- /** @var IAdapter */
- protected $pagedResultsAdapter;
+ protected string $logFile = '';
+ protected array $curArgs = [];
+ protected LoggerInterface $logger;
private ?LdapDataCollector $dataCollector = null;
public function __construct(string $logFile = '') {
- $this->pagedResultsAdapter = new Php73();
$this->logFile = $logFile;
/** @var IProfiler $profiler */
@@ -60,6 +55,8 @@ class LDAP implements ILDAPWrapper {
$this->dataCollector = new LdapDataCollector();
$profiler->add($this->dataCollector);
}
+
+ $this->logger = \OCP\Server::get(LoggerInterface::class);
}
/**
@@ -73,10 +70,12 @@ class LDAP implements ILDAPWrapper {
* {@inheritDoc}
*/
public function connect($host, $port) {
- if (strpos($host, '://') === false) {
+ $pos = strpos($host, '://');
+ if ($pos === false) {
$host = 'ldap://' . $host;
+ $pos = 4;
}
- if (strpos($host, ':', strpos($host, '://') + 1) === false) {
+ if (strpos($host, ':', $pos + 1) === false) {
//ldap_connect ignores port parameter when URLs are passed
$host .= ':' . $port;
}
@@ -87,39 +86,30 @@ class LDAP implements ILDAPWrapper {
* {@inheritDoc}
*/
public function controlPagedResultResponse($link, $result, &$cookie): bool {
- $this->preFunctionCall(
- $this->pagedResultsAdapter->getResponseCallFunc(),
- $this->pagedResultsAdapter->getResponseCallArgs([$link, $result, &$cookie])
- );
-
- $result = $this->pagedResultsAdapter->responseCall($link);
- $cookie = $this->pagedResultsAdapter->getCookie($link);
-
- if ($this->isResultFalse($result)) {
- $this->postFunctionCall();
+ $errorCode = 0;
+ $errorMsg = '';
+ $controls = [];
+ $matchedDn = null;
+ $referrals = [];
+
+ /** Cannot use invokeLDAPMethod because arguments are passed by reference */
+ $this->preFunctionCall('ldap_parse_result', [$link, $result]);
+ $success = ldap_parse_result($link, $result,
+ $errorCode,
+ $matchedDn,
+ $errorMsg,
+ $referrals,
+ $controls);
+ if ($errorCode !== 0) {
+ $this->processLDAPError($link, 'ldap_parse_result', $errorCode, $errorMsg);
}
-
- return $result;
- }
-
- /**
- * {@inheritDoc}
- */
- public function controlPagedResult($link, $pageSize, $isCritical) {
- $fn = $this->pagedResultsAdapter->getRequestCallFunc();
- $this->pagedResultsAdapter->setRequestParameters($link, $pageSize, $isCritical);
- if ($fn === null) {
- return true;
+ if ($this->dataCollector !== null) {
+ $this->dataCollector->stopLastLdapRequest();
}
- $this->preFunctionCall($fn, $this->pagedResultsAdapter->getRequestCallArgs($link));
- $result = $this->pagedResultsAdapter->requestCall($link);
-
- if ($this->isResultFalse($result)) {
- $this->postFunctionCall();
- }
+ $cookie = $controls[LDAP_CONTROL_PAGEDRESULTS]['value']['cookie'] ?? '';
- return $result;
+ return $success;
}
/**
@@ -146,7 +136,7 @@ class LDAP implements ILDAPWrapper {
/**
* Splits DN into its component parts
* @param string $dn
- * @param int @withAttrib
+ * @param int $withAttrib
* @return array|false
* @link https://www.php.net/manual/en/function.ldap-explode-dn.php
*/
@@ -193,14 +183,22 @@ class LDAP implements ILDAPWrapper {
* {@inheritDoc}
*/
public function read($link, $baseDN, $filter, $attr) {
- $this->pagedResultsAdapter->setReadArgs($link, $baseDN, $filter, $attr);
- return $this->invokeLDAPMethod('read', ...$this->pagedResultsAdapter->getReadArgs($link));
+ return $this->invokeLDAPMethod('read', $link, $baseDN, $filter, $attr, 0, -1);
}
/**
* {@inheritDoc}
*/
- public function search($link, $baseDN, $filter, $attr, $attrsOnly = 0, $limit = 0) {
+ public function search($link, $baseDN, $filter, $attr, $attrsOnly = 0, $limit = 0, int $pageSize = 0, string $cookie = '') {
+ $serverControls = [[
+ 'oid' => LDAP_CONTROL_PAGEDRESULTS,
+ 'value' => [
+ 'size' => $pageSize,
+ 'cookie' => $cookie,
+ ],
+ 'iscritical' => false,
+ ]];
+
$oldHandler = set_error_handler(function ($no, $message, $file, $line) use (&$oldHandler) {
if (strpos($message, 'Partial search results returned: Sizelimit exceeded') !== false) {
return true;
@@ -209,8 +207,7 @@ class LDAP implements ILDAPWrapper {
return true;
});
try {
- $this->pagedResultsAdapter->setSearchArgs($link, $baseDN, $filter, $attr, $attrsOnly, $limit);
- $result = $this->invokeLDAPMethod('search', ...$this->pagedResultsAdapter->getSearchArgs($link));
+ $result = $this->invokeLDAPMethod('search', $link, $baseDN, $filter, $attr, $attrsOnly, $limit, -1, LDAP_DEREF_NEVER, $serverControls);
restore_error_handler();
return $result;
@@ -230,7 +227,7 @@ class LDAP implements ILDAPWrapper {
/**
* {@inheritDoc}
*/
- public function exopPasswd($link, $userDN, $oldPassword, $password) {
+ public function exopPasswd($link, string $userDN, string $oldPassword, string $password) {
return $this->invokeLDAPMethod('exop_passwd', $link, $userDN, $oldPassword, $password);
}
@@ -276,15 +273,14 @@ class LDAP implements ILDAPWrapper {
* When using ldap_search we provide an array, in case multiple bases are
* configured. Thus, we need to check the array elements.
*
- * @param $result
- * @return bool
+ * @param mixed $result
*/
- protected function isResultFalse($result) {
+ protected function isResultFalse(string $functionName, $result): bool {
if ($result === false) {
return true;
}
- if ($this->curFunc === 'ldap_search' && is_array($result)) {
+ if ($functionName === 'ldap_search' && is_array($result)) {
foreach ($result as $singleResult) {
if ($singleResult === false) {
return true;
@@ -296,16 +292,16 @@ class LDAP implements ILDAPWrapper {
}
/**
+ * @param array $arguments
* @return mixed
*/
- protected function invokeLDAPMethod() {
- $arguments = func_get_args();
- $func = 'ldap_' . array_shift($arguments);
+ protected function invokeLDAPMethod(string $func, ...$arguments) {
+ $func = 'ldap_' . $func;
if (function_exists($func)) {
$this->preFunctionCall($func, $arguments);
$result = call_user_func_array($func, $arguments);
- if ($this->isResultFalse($result)) {
- $this->postFunctionCall();
+ if ($this->isResultFalse($func, $result)) {
+ $this->postFunctionCall($func);
}
if ($this->dataCollector !== null) {
$this->dataCollector->stopLastLdapRequest();
@@ -316,8 +312,12 @@ class LDAP implements ILDAPWrapper {
}
private function preFunctionCall(string $functionName, array $args): void {
- $this->curFunc = $functionName;
$this->curArgs = $args;
+ $this->logger->debug('Calling LDAP function {func} with parameters {args}', [
+ 'app' => 'user_ldap',
+ 'func' => $functionName,
+ 'args' => json_encode($args),
+ ]);
if ($this->dataCollector !== null) {
$args = array_map(function ($item) {
@@ -330,14 +330,14 @@ class LDAP implements ILDAPWrapper {
return $item;
}, $this->curArgs);
- $this->dataCollector->startLdapRequest($this->curFunc, $args);
+ $this->dataCollector->startLdapRequest($functionName, $args);
}
if ($this->logFile !== '' && is_writable(dirname($this->logFile)) && (!file_exists($this->logFile) || is_writable($this->logFile))) {
$args = array_map(fn ($item) => (!$this->isResource($item) ? $item : '(resource)'), $this->curArgs);
file_put_contents(
$this->logFile,
- $this->curFunc . '::' . json_encode($args) . "\n",
+ $functionName . '::' . json_encode($args) . "\n",
FILE_APPEND
);
}
@@ -351,14 +351,14 @@ class LDAP implements ILDAPWrapper {
* @throws ServerNotAvailableException
* @throws \Exception
*/
- private function processLDAPError($resource) {
- $errorCode = ldap_errno($resource);
- if ($errorCode === 0) {
- return;
- }
- $errorMsg = ldap_error($resource);
-
- if ($this->curFunc === 'ldap_get_entries'
+ private function processLDAPError($resource, string $functionName, int $errorCode, string $errorMsg): void {
+ $this->logger->debug('LDAP error {message} ({code}) after calling {func}', [
+ 'app' => 'user_ldap',
+ 'message' => $errorMsg,
+ 'code' => $errorCode,
+ 'func' => $functionName,
+ ]);
+ if ($functionName === 'ldap_get_entries'
&& $errorCode === -4) {
} elseif ($errorCode === 32) {
//for now
@@ -373,15 +373,8 @@ class LDAP implements ILDAPWrapper {
} elseif ($errorCode === 1) {
throw new \Exception('LDAP Operations error', $errorCode);
} elseif ($errorCode === 19) {
- ldap_get_option($this->curArgs[0], LDAP_OPT_ERROR_STRING, $extended_error);
- throw new ConstraintViolationException(!empty($extended_error)?$extended_error:$errorMsg, $errorCode);
- } else {
- \OC::$server->getLogger()->debug('LDAP error {message} ({code}) after calling {func}', [
- 'app' => 'user_ldap',
- 'message' => $errorMsg,
- 'code' => $errorCode,
- 'func' => $this->curFunc,
- ]);
+ ldap_get_option($resource, LDAP_OPT_ERROR_STRING, $extended_error);
+ throw new ConstraintViolationException(!empty($extended_error) ? $extended_error : $errorMsg, $errorCode);
}
}
@@ -389,11 +382,11 @@ class LDAP implements ILDAPWrapper {
* Called after an ldap method is run to act on LDAP error if necessary
* @throw \Exception
*/
- private function postFunctionCall() {
+ private function postFunctionCall(string $functionName): void {
if ($this->isResource($this->curArgs[0])) {
$resource = $this->curArgs[0];
} elseif (
- $this->curFunc === 'ldap_search'
+ $functionName === 'ldap_search'
&& is_array($this->curArgs[0])
&& $this->isResource($this->curArgs[0][0])
) {
@@ -404,9 +397,14 @@ class LDAP implements ILDAPWrapper {
return;
}
- $this->processLDAPError($resource);
+ $errorCode = ldap_errno($resource);
+ if ($errorCode === 0) {
+ return;
+ }
+ $errorMsg = ldap_error($resource);
+
+ $this->processLDAPError($resource, $functionName, $errorCode, $errorMsg);
- $this->curFunc = '';
$this->curArgs = [];
}
}
diff --git a/apps/user_ldap/lib/LDAPUtility.php b/apps/user_ldap/lib/LDAPUtility.php
index 0b16f74333b..a8e4c16fac7 100644
--- a/apps/user_ldap/lib/LDAPUtility.php
+++ b/apps/user_ldap/lib/LDAPUtility.php
@@ -25,7 +25,7 @@
namespace OCA\User_LDAP;
abstract class LDAPUtility {
- protected $ldap;
+ protected ILDAPWrapper $ldap;
/**
* constructor, make sure the subclasses call this one!
diff --git a/apps/user_ldap/lib/PagedResults/IAdapter.php b/apps/user_ldap/lib/PagedResults/IAdapter.php
deleted file mode 100644
index 62a0d0440c5..00000000000
--- a/apps/user_ldap/lib/PagedResults/IAdapter.php
+++ /dev/null
@@ -1,130 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-/**
- * @copyright Copyright (c) 2020 Arthur Schiwon <blizzz@arthur-schiwon.de>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-namespace OCA\User_LDAP\PagedResults;
-
-interface IAdapter {
-
- /**
- * Methods for initiating Paged Results Control
- */
-
- /**
- * The adapter receives paged result parameters from the client. It may
- * store the parameters for later use.
- */
- public function setRequestParameters($link, int $pageSize, bool $isCritical): void;
-
- /**
- * The adapter is asked for an function that is being explicitly called to
- * send the control parameters to LDAP. If not function has to be called,
- * null shall be returned.
- *
- * It will used by the callee for diagnosis and error handling.
- */
- public function getRequestCallFunc(): ?string;
-
- /**
- * The adapter is asked to provide the arguments it would pass to the
- * function returned by getRequestCallFunc(). If none shall be called, an
- * empty array should be returned.
- *
- * It will used by the callee for diagnosis and error handling.
- */
- public function getRequestCallArgs($link): array;
-
- /**
- * The adapter is asked to do the necessary calls to LDAP, if
- * getRequestCallFunc returned a function. If none, it will not be called
- * so the return value is best set to false. Otherwise it shall respond
- * whether setting the controls was successful.
- */
- public function requestCall($link): bool;
-
- /**
- * The adapter shall report which PHP function will be called to process
- * the paged results call
- *
- * It will used by the callee for diagnosis and error handling.
- */
- public function getResponseCallFunc(): string;
-
- /**
- * The adapter shall report with arguments will be provided to the LDAP
- * function it will call
- *
- * It will used by the callee for diagnosis and error handling.
- */
- public function getResponseCallArgs(array $originalArgs): array;
-
- /**
- * the adapter should do its LDAP function call and return success state
- *
- * @param resource|\LDAP\Connection $link LDAP resource
- * @return bool
- */
- public function responseCall($link): bool;
-
- /**
- * The adapter receives the parameters that were passed to a search
- * operation. Typically it wants to save the them for the call proper later
- * on.
- */
- public function setSearchArgs(
- $link,
- string $baseDN,
- string $filter,
- array $attr,
- int $attrsOnly,
- int $limit
- ): void;
-
- /**
- * The adapter shall report which arguments shall be passed to the
- * ldap_search function.
- */
- public function getSearchArgs($link): array;
-
- /**
- * The adapter receives the parameters that were passed to a read
- * operation. Typically it wants to save the them for the call proper later
- * on.
- */
- public function setReadArgs($link, string $baseDN, string $filter, array $attr): void;
-
- /**
- * The adapter shall report which arguments shall be passed to the
- * ldap_read function.
- */
- public function getReadArgs($link): array;
-
- /**
- * Returns the current paged results cookie
- *
- * @param resource|\LDAP\Connection $link LDAP resource
- * @return string
- */
- public function getCookie($link): string;
-}
diff --git a/apps/user_ldap/lib/PagedResults/Php73.php b/apps/user_ldap/lib/PagedResults/Php73.php
deleted file mode 100644
index 1fc1fcdbab8..00000000000
--- a/apps/user_ldap/lib/PagedResults/Php73.php
+++ /dev/null
@@ -1,173 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-/**
- * @copyright Copyright (c) 2020 Arthur Schiwon <blizzz@arthur-schiwon.de>
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-namespace OCA\User_LDAP\PagedResults;
-
-/**
- * Class Php73
- *
- * implements paged results support with PHP APIs available from PHP 7.3
- *
- * @package OCA\User_LDAP\PagedResults
- */
-class Php73 implements IAdapter {
- use TLinkId;
-
- /** @var array */
- protected $linkData = [];
-
- public function getResponseCallFunc(): string {
- return 'ldap_parse_result';
- }
-
- public function responseCall($link): bool {
- $linkId = $this->getLinkId($link);
- return ldap_parse_result(...$this->linkData[$linkId]['responseArgs']);
- }
-
- public function getResponseCallArgs(array $originalArgs): array {
- $link = array_shift($originalArgs);
- $linkId = $this->getLinkId($link);
-
- if (!isset($this->linkData[$linkId])) {
- $this->linkData[$linkId] = [];
- }
-
- $this->linkData[$linkId]['responseErrorCode'] = 0;
- $this->linkData[$linkId]['responseErrorMessage'] = '';
- $this->linkData[$linkId]['serverControls'] = [];
- $matchedDn = null;
- $referrals = [];
-
- $this->linkData[$linkId]['responseArgs'] = [
- $link,
- array_shift($originalArgs),
- &$this->linkData[$linkId]['responseErrorCode'],
- $matchedDn,
- &$this->linkData[$linkId]['responseErrorMessage'],
- $referrals,
- &$this->linkData[$linkId]['serverControls']
- ];
-
-
- return $this->linkData[$linkId]['responseArgs'];
- }
-
- public function getCookie($link): string {
- $linkId = $this->getLinkId($link);
- return $this->linkData[$linkId]['serverControls'][LDAP_CONTROL_PAGEDRESULTS]['value']['cookie'] ?? '';
- }
-
- private function resetCookie(int $linkId): void {
- if (isset($this->linkData[$linkId]['serverControls'][LDAP_CONTROL_PAGEDRESULTS]['value']['cookie'])) {
- $this->linkData[$linkId]['serverControls'][LDAP_CONTROL_PAGEDRESULTS]['value']['cookie'] = '';
- }
- }
-
- public function getRequestCallFunc(): ?string {
- return null;
- }
-
- public function setRequestParameters($link, int $pageSize, bool $isCritical): void {
- $linkId = $this->getLinkId($link);
- if (!isset($this->linkData[$linkId])) {
- $this->linkData[$linkId] = [];
- }
- $this->linkData[$linkId]['requestArgs'] = [];
- $this->linkData[$linkId]['requestArgs']['pageSize'] = $pageSize;
- $this->linkData[$linkId]['requestArgs']['isCritical'] = $isCritical;
-
- if ($pageSize === 0) {
- $this->resetCookie($linkId);
- }
- }
-
- public function getRequestCallArgs($link): array {
- // no separate call
- return [];
- }
-
- public function requestCall($link): bool {
- // no separate call
- return false;
- }
-
- public function setSearchArgs(
- $link,
- string $baseDN,
- string $filter,
- array $attr,
- int $attrsOnly,
- int $limit
- ): void {
- $linkId = $this->getLinkId($link);
- if (!isset($this->linkData[$linkId])) {
- $this->linkData[$linkId] = [];
- }
-
- $this->linkData[$linkId]['searchArgs'] = func_get_args();
- $this->preparePagesResultsArgs($linkId, 'searchArgs');
- }
-
- public function getSearchArgs($link): array {
- $linkId = $this->getLinkId($link);
- return $this->linkData[$linkId]['searchArgs'];
- }
-
- public function setReadArgs($link, string $baseDN, string $filter, array $attr): void {
- $linkId = $this->getLinkId($link);
- if (!isset($this->linkData[$linkId])) {
- $this->linkData[$linkId] = [];
- }
-
- $this->linkData[$linkId]['readArgs'] = func_get_args();
- $this->linkData[$linkId]['readArgs'][] = 0; // $attrsonly default
- $this->linkData[$linkId]['readArgs'][] = -1; // $sizelimit default
- }
-
- public function getReadArgs($link): array {
- $linkId = $this->getLinkId($link);
- return $this->linkData[$linkId]['readArgs'];
- }
-
- protected function preparePagesResultsArgs(int $linkId, string $methodKey): void {
- if (!isset($this->linkData[$linkId]['requestArgs'])) {
- return;
- }
-
- $serverControls = [[
- 'oid' => LDAP_CONTROL_PAGEDRESULTS,
- 'value' => [
- 'size' => $this->linkData[$linkId]['requestArgs']['pageSize'],
- 'cookie' => $this->linkData[$linkId]['serverControls'][LDAP_CONTROL_PAGEDRESULTS]['value']['cookie'] ?? '',
- ]
- ]];
-
- $this->linkData[$linkId][$methodKey][] = -1; // timelimit
- $this->linkData[$linkId][$methodKey][] = LDAP_DEREF_NEVER;
- $this->linkData[$linkId][$methodKey][] = $serverControls;
- }
-}