Fix LDAP pagination for PHP7.3+ (#4901)

* Ldap: Read and update cookie values for paged requests also with PHP 7.3+

* Ldap: Provide server-side sort control directly with the request with PHP 7.3+
This commit is contained in:
Eric Lippmann 2022-10-10 09:55:47 +02:00 committed by GitHub
parent ad827395af
commit c133cbe4f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -882,7 +882,19 @@ class LdapConnection implements Selectable, Inspectable
} }
} }
$controls = [];
$legacyControlHandling = version_compare(PHP_VERSION, '7.3.0') < 0; $legacyControlHandling = version_compare(PHP_VERSION, '7.3.0') < 0;
if ($serverSorting && $query->hasOrder()) {
$control = [
'oid' => LDAP_CONTROL_SORTREQUEST,
'value' => $this->encodeSortRules($query->getOrder())
];
if ($legacyControlHandling) {
ldap_set_option($ds, LDAP_OPT_SERVER_CONTROLS, [$control]);
} else {
$controls[LDAP_CONTROL_SORTREQUEST] = $control;
}
}
$count = 0; $count = 0;
$cookie = ''; $cookie = '';
@ -892,15 +904,15 @@ class LdapConnection implements Selectable, Inspectable
// Do not request the pagination control as a critical extension, as we want the // Do not request the pagination control as a critical extension, as we want the
// server to return results even if the paged search request cannot be satisfied // server to return results even if the paged search request cannot be satisfied
ldap_control_paged_result($ds, $pageSize, false, $cookie); ldap_control_paged_result($ds, $pageSize, false, $cookie);
} } else {
$controls[LDAP_CONTROL_PAGEDRESULTS] = [
if ($serverSorting && $query->hasOrder()) { 'oid' => LDAP_CONTROL_PAGEDRESULTS,
ldap_set_option($ds, LDAP_OPT_SERVER_CONTROLS, array( 'iscritical' => false, // See above
array( 'value' => [
'oid' => LdapCapabilities::LDAP_SERVER_SORT_OID, 'size' => $pageSize,
'value' => $this->encodeSortRules($query->getOrder()) 'cookie' => $cookie
) ]
)); ];
} }
$results = $this->ldapSearch( $results = $this->ldapSearch(
@ -910,7 +922,7 @@ class LdapConnection implements Selectable, Inspectable
($serverSorting || ! $query->hasOrder()) && $limit ? $offset + $limit : 0, ($serverSorting || ! $query->hasOrder()) && $limit ? $offset + $limit : 0,
0, 0,
LDAP_DEREF_NEVER, LDAP_DEREF_NEVER,
$legacyControlHandling ? null : $pageSize empty($controls) ? null : $controls
); );
if ($results === false) { if ($results === false) {
if (ldap_errno($ds) === self::LDAP_NO_SUCH_OBJECT) { if (ldap_errno($ds) === self::LDAP_NO_SUCH_OBJECT) {
@ -976,18 +988,23 @@ class LdapConnection implements Selectable, Inspectable
&& ($entry = ldap_next_entry($ds, $entry)) && ($entry = ldap_next_entry($ds, $entry))
); );
if ($legacyControlHandling && false === @ldap_control_paged_result_response($ds, $results, $cookie)) { if ($legacyControlHandling) {
// If the page size is greater than or equal to the sizeLimit value, the server should ignore the if (false === @ldap_control_paged_result_response($ds, $results, $cookie)) {
// control as the request can be satisfied in a single page: https://www.ietf.org/rfc/rfc2696.txt // If the page size is greater than or equal to the sizeLimit value, the server should ignore the
// This applies no matter whether paged search requests are permitted or not. You're done once you // control as the request can be satisfied in a single page: https://www.ietf.org/rfc/rfc2696.txt
// got everything you were out for. // This applies no matter whether paged search requests are permitted or not. You're done once you
if ($serverSorting && count($entries) !== $limit) { // got everything you were out for.
// The server does not support pagination, but still returned a response by ignoring the if ($serverSorting && count($entries) !== $limit) {
// pagedResultsControl. We output a warning to indicate that the pagination control was ignored. // The server does not support pagination, but still returned a response by ignoring the
Logger::warning( // pagedResultsControl. We output a warning to indicate that the pagination control was ignored.
'Unable to request paged LDAP results. Does the server allow paged search requests?' Logger::warning(
); 'Unable to request paged LDAP results. Does the server allow paged search requests?'
);
}
} }
} else {
ldap_parse_result($ds, $results, $errno, $dn, $errmsg, $refs, $controlsReturned);
$cookie = $controlsReturned[LDAP_CONTROL_PAGEDRESULTS]['value']['cookie'];
} }
ldap_free_result($results); ldap_free_result($results);
@ -1219,7 +1236,7 @@ class LdapConnection implements Selectable, Inspectable
* @param int $sizelimit Enables you to limit the count of entries fetched * @param int $sizelimit Enables you to limit the count of entries fetched
* @param int $timelimit Sets the number of seconds how long is spend on the search * @param int $timelimit Sets the number of seconds how long is spend on the search
* @param int $deref * @param int $deref
* @param int $pageSize The page size to request (Only supported with PHP v7.3+) * @param array $controls LDAP Controls to send with the request (Only supported with PHP v7.3+)
* *
* @return resource|bool A search result identifier or false on error * @return resource|bool A search result identifier or false on error
* *
@ -1232,7 +1249,7 @@ class LdapConnection implements Selectable, Inspectable
$sizelimit = 0, $sizelimit = 0,
$timelimit = 0, $timelimit = 0,
$deref = LDAP_DEREF_NEVER, $deref = LDAP_DEREF_NEVER,
$pageSize = null $controls = null
) { ) {
$queryString = (string) $query; $queryString = (string) $query;
$baseDn = $query->getBase() ?: $this->getDn(); $baseDn = $query->getBase() ?: $this->getDn();
@ -1287,18 +1304,11 @@ class LdapConnection implements Selectable, Inspectable
throw new LogicException('LDAP scope %s not supported by ldapSearch', $scope); throw new LogicException('LDAP scope %s not supported by ldapSearch', $scope);
} }
if ($pageSize !== null) { // Explicit calls with and without controls,
$serverctrls[] = [ // because the parameter is only supported since PHP 7.3.
'oid' => LDAP_CONTROL_PAGEDRESULTS, // Since it is a public method,
// Do not request the pagination control as a critical extension, as we want the // providing controls will naturally fail if the parameter is not supported by PHP.
// server to return results even if the paged search request cannot be satisfied if ($controls !== null) {
'iscritical' => false,
'value' => [
'size' => $pageSize,
'cookie' => ''
]
];
return @$function( return @$function(
$this->getConnection(), $this->getConnection(),
$baseDn, $baseDn,
@ -1308,7 +1318,7 @@ class LdapConnection implements Selectable, Inspectable
$sizelimit, $sizelimit,
$timelimit, $timelimit,
$deref, $deref,
$serverctrls $controls
); );
} else { } else {
return @$function( return @$function(