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; }