diff --git a/auth/cas/config.html b/auth/cas/config.html
index c0ea85b7148..62799508612 100644
--- a/auth/cas/config.html
+++ b/auth/cas/config.html
@@ -99,9 +99,31 @@ if (!isset($config->removeuser)) {
$yesno = array( get_string('no'), get_string('yes') );
$disabled = '';
-if (!ldap_paged_results_supported($config->ldap_version)) {
+$pagedresultssupported = false;
+if ($config->host_url !== '') {
+ /**
+ * We try to connect each and every time we open the config, because we want to set the Page
+ * Size setting as enabled or disabled depending on the configured LDAP server supporting
+ * pagination or not, and to notify the user about it. If the user changed the LDAP server (or
+ * the LDAP protocol version) last time, it might happen that paged results are no longer
+ * available and we want to show that to the user the next time she goes to the settings page.
+ */
+ try {
+ $ldapconn = $this->ldap_connect();
+ $pagedresultssupported = ldap_paged_results_supported($config->ldap_version, $ldapconn);
+ } catch (Exception $e) {
+ // If we couldn't connect and get the supported options, we can only assume we don't support paged results.
+ $pagedresultssupported = false;
+ }
+}
+/* Make sure we only disable the paged result size setting and show the notification about it if
+ * there is a configured server that we tried to contact. Othersiwe, if someone's LDAP server does
+ * support paged results, they won't be able to turn it on the first time they set it up (because
+ * the field will be disabled).
+ */
+if (($config->host_url !== '') && (!$pagedresultssupported)) {
$disabled = ' disabled="disabled"';
- echo $OUTPUT->notification(get_string('pagedresultsnotsupp', 'auth_ldap'));
+ echo $OUTPUT->notification(get_string('pagedresultsnotsupp', 'auth_ldap'), \core\output\notification::NOTIFY_INFO);
}
?>
diff --git a/auth/ldap/auth.php b/auth/ldap/auth.php
index f80d6b3cdbb..b23c9ec99be 100644
--- a/auth/ldap/auth.php
+++ b/auth/ldap/auth.php
@@ -695,7 +695,7 @@ class auth_plugin_ldap extends auth_plugin_base {
array_push($contexts, $this->config->create_context);
}
- $ldap_pagedresults = ldap_paged_results_supported($this->config->ldap_version);
+ $ldap_pagedresults = ldap_paged_results_supported($this->config->ldap_version, $ldapconnection);
$ldap_cookie = '';
foreach ($contexts as $context) {
$context = trim($context);
@@ -1540,7 +1540,7 @@ class auth_plugin_ldap extends auth_plugin_base {
}
$ldap_cookie = '';
- $ldap_pagedresults = ldap_paged_results_supported($this->config->ldap_version);
+ $ldap_pagedresults = ldap_paged_results_supported($this->config->ldap_version, $ldapconnection);
foreach ($contexts as $context) {
$context = trim($context);
if (empty($context)) {
diff --git a/auth/ldap/config.html b/auth/ldap/config.html
index 422f474b680..883eb9d0b52 100644
--- a/auth/ldap/config.html
+++ b/auth/ldap/config.html
@@ -117,9 +117,31 @@ $fastpathoptions = array(AUTH_NTLM_FASTPATH_YESFORM => get_string('auth_ntlmsso_
AUTH_NTLM_FASTPATH_ATTEMPT => get_string('auth_ntlmsso_ie_fastpath_attempt', 'auth_ldap'));
$disabled = '';
-if (!ldap_paged_results_supported($config->ldap_version)) {
+$pagedresultssupported = false;
+if ($config->host_url !== '') {
+ /**
+ * We try to connect each and every time we open the config, because we want to set the Page
+ * Size setting as enabled or disabled depending on the configured LDAP server supporting
+ * pagination or not, and to notify the user about it. If the user changed the LDAP server (or
+ * the LDAP protocol version) last time, it might happen that paged results are no longer
+ * available and we want to show that to the user the next time she goes to the settings page.
+ */
+ try {
+ $ldapconn = $this->ldap_connect();
+ $pagedresultssupported = ldap_paged_results_supported($config->ldap_version, $ldapconn);
+ } catch (Exception $e) {
+ // If we couldn't connect and get the supported options, we can only assume we don't support paged results.
+ $pagedresultssupported = false;
+ }
+}
+/* Make sure we only disable the paged result size setting and show the notification about it if
+ * there is a configured server that we tried to contact. Othersiwe, if someone's LDAP server does
+ * support paged results, they won't be able to turn it on the first time they set it up (because
+ * the field will be disabled).
+ */
+if (($config->host_url !== '') && (!$pagedresultssupported)) {
$disabled = ' disabled="disabled"';
- echo $OUTPUT->notification(get_string('pagedresultsnotsupp', 'auth_ldap'));
+ echo $OUTPUT->notification(get_string('pagedresultsnotsupp', 'auth_ldap'), \core\output\notification::NOTIFY_INFO);
}
?>
diff --git a/auth/ldap/lang/en/auth_ldap.php b/auth/ldap/lang/en/auth_ldap.php
index 6f235233b31..a5285dda7f9 100644
--- a/auth/ldap/lang/en/auth_ldap.php
+++ b/auth/ldap/lang/en/auth_ldap.php
@@ -132,7 +132,7 @@ $string['ntlmsso_attempting'] = 'Attempting Single Sign On via NTLM...';
$string['ntlmsso_failed'] = 'Auto-login failed, try the normal login page...';
$string['ntlmsso_isdisabled'] = 'NTLM SSO is disabled.';
$string['ntlmsso_unknowntype'] = 'Unknown ntlmsso type!';
-$string['pagedresultsnotsupp'] = 'LDAP paged results not supported (either your PHP version lacks support or you have configured Moodle to use LDAP protocol version 2)';
+$string['pagedresultsnotsupp'] = 'LDAP paged results not supported (either your PHP version lacks support, you have configured Moodle to use LDAP protocol version 2 or Moodle cannot contact your LDAP server to see if paged support is available.)';
$string['pagesize'] = 'Make sure this value is smaller than your LDAP server result set size limit (the maximum number of entries that can be returned in a single query)';
$string['pagesize_key'] = 'Page size';
$string['pluginname'] = 'LDAP server';
diff --git a/lib/ldaplib.php b/lib/ldaplib.php
index ec18f6f23a7..8a5c9652b9b 100644
--- a/lib/ldaplib.php
+++ b/lib/ldaplib.php
@@ -22,6 +22,11 @@ if (!defined('ROOTDSE')) {
define ('ROOTDSE', '');
}
+// Paged results control OID value.
+if (!defined('LDAP_PAGED_RESULTS_CONTROL')) {
+ define ('LDAP_PAGED_RESULTS_CONTROL', '1.2.840.113556.1.4.319');
+}
+
// Default page size when using LDAP paged results
if (!defined('LDAP_DEFAULT_PAGESIZE')) {
define('LDAP_DEFAULT_PAGESIZE', 250);
@@ -452,14 +457,39 @@ function ldap_stripslashes($text) {
/**
- * Check if we use LDAP version 3, otherwise the server cannot use them.
+ * Check if we can use paged results (see RFC 2696). We need to use
+ * LDAP version 3 (or later), otherwise the server cannot use them. If
+ * we also pass in a valid LDAP connection handle, we also check
+ * whether the server actually supports them.
*
* @param ldapversion integer The LDAP protocol version we use.
+ * @param ldapconnection resource An existing LDAP connection (optional).
*
* @return boolean true is paged results can be used, false otherwise.
*/
-function ldap_paged_results_supported($ldapversion) {
- if ((int)$ldapversion === 3) {
+function ldap_paged_results_supported($ldapversion, $ldapconnection = null) {
+ if ((int)$ldapversion < 3) {
+ // Minimun required version: LDAP v3.
+ return false;
+ }
+
+ if ($ldapconnection === null) {
+ // Can't verify it, so assume it isn't supported.
+ return false;
+ }
+
+ // Connect to the rootDSE and get the supported controls.
+ $sr = ldap_read($ldapconnection, ROOTDSE, '(objectClass=*)', array('supportedControl'));
+ if (!$sr) {
+ return false;
+ }
+
+ $entries = ldap_get_entries_moodle($ldapconnection, $sr);
+ if (empty($entries)) {
+ return false;
+ }
+ $info = array_change_key_case($entries[0], CASE_LOWER);
+ if (isset($info['supportedcontrol']) && in_array(LDAP_PAGED_RESULTS_CONTROL, $info['supportedcontrol'])) {
return true;
}