Skip to content

Commit

Permalink
MDL-50625 auth_ldap: Better check for paged results support
Browse files Browse the repository at this point in the history
There is at least one LDAP server (Sun Directory Server) that doesn't
support Paged Results extension, even if it supports LDAP version 3. So
checking just for LDAP version is not enough.

If possible, we check the supportedControl attribute of the LDAP rootDSE
and see if the paged results control is available. This needs an LDAP
connection, which might not be possible to establish before we configure
some essential LDAP settings (server, bind user, password, etc.). Thus
we try to establish the connection and check the supportedControl
attribute. But if we fail, we perform only basic checks that are less
accurate and err on the side of cautiousness.
  • Loading branch information
iarenaza committed Jan 19, 2017
1 parent 26c76fc commit 52c14af
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 10 deletions.
26 changes: 24 additions & 2 deletions auth/cas/config.html
Expand Up @@ -99,9 +99,31 @@
$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);
}

?>
Expand Down
4 changes: 2 additions & 2 deletions auth/ldap/auth.php
Expand Up @@ -695,7 +695,7 @@ function sync_users($do_updates=true) {
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);
Expand Down Expand Up @@ -1540,7 +1540,7 @@ function ldap_get_userlist($filter='*') {
}

$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)) {
Expand Down
26 changes: 24 additions & 2 deletions auth/ldap/config.html
Expand Up @@ -117,9 +117,31 @@
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);
}

?>
Expand Down
2 changes: 1 addition & 1 deletion auth/ldap/lang/en/auth_ldap.php
Expand Up @@ -132,7 +132,7 @@
$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';
Expand Down
36 changes: 33 additions & 3 deletions lib/ldaplib.php
Expand Up @@ -22,6 +22,11 @@
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);
Expand Down Expand Up @@ -452,14 +457,39 @@ function ($match) use ($specialchars) {


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

Expand Down

0 comments on commit 52c14af

Please sign in to comment.