Merge branch 'MDL-64071-master' of https://github.com/UniGe/moodle
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Wed, 6 Feb 2019 16:56:59 +0000 (17:56 +0100)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Wed, 6 Feb 2019 16:56:59 +0000 (17:56 +0100)
1  2 
auth/ldap/auth.php
auth/ldap/lang/en/auth_ldap.php

diff --combined auth/ldap/auth.php
@@@ -698,8 -698,8 +698,8 @@@ class auth_plugin_ldap extends auth_plu
              array_push($contexts, $this->config->create_context);
          }
  
 -        $ldap_pagedresults = ldap_paged_results_supported($this->config->ldap_version, $ldapconnection);
 -        $ldap_cookie = '';
 +        $ldappagedresults = ldap_paged_results_supported($this->config->ldap_version, $ldapconnection);
 +        $ldapcookie = '';
          foreach ($contexts as $context) {
              $context = trim($context);
              if (empty($context)) {
              }
  
              do {
 -                if ($ldap_pagedresults) {
 -                    ldap_control_paged_result($ldapconnection, $this->config->pagesize, true, $ldap_cookie);
 +                if ($ldappagedresults) {
 +                    ldap_control_paged_result($ldapconnection, $this->config->pagesize, true, $ldapcookie);
                  }
                  if ($this->config->search_sub) {
                      // Use ldap_search to find first user from subtree.
 -                    $ldap_result = ldap_search($ldapconnection, $context, $filter, array($this->config->user_attribute));
 +                    $ldapresult = ldap_search($ldapconnection, $context, $filter, array($this->config->user_attribute));
                  } else {
                      // Search only in this context.
 -                    $ldap_result = ldap_list($ldapconnection, $context, $filter, array($this->config->user_attribute));
 +                    $ldapresult = ldap_list($ldapconnection, $context, $filter, array($this->config->user_attribute));
                  }
 -                if(!$ldap_result) {
 +                if (!$ldapresult) {
                      continue;
                  }
 -                if ($ldap_pagedresults) {
 -                    ldap_control_paged_result_response($ldapconnection, $ldap_result, $ldap_cookie);
 +                if ($ldappagedresults) {
 +                    $pagedresp = ldap_control_paged_result_response($ldapconnection, $ldapresult, $ldapcookie);
 +                    // Function ldap_control_paged_result_response() does not overwrite $ldapcookie if it fails, by
 +                    // setting this to null we avoid an infinite loop.
 +                    if ($pagedresp === false) {
 +                        $ldapcookie = null;
 +                    }
                  }
 -                if ($entry = @ldap_first_entry($ldapconnection, $ldap_result)) {
 +                if ($entry = @ldap_first_entry($ldapconnection, $ldapresult)) {
                      do {
                          $value = ldap_get_values_len($ldapconnection, $entry, $this->config->user_attribute);
                          $value = core_text::convert($value[0], $this->config->ldapencoding, 'utf-8');
                          $this->ldap_bulk_insert($value);
                      } while ($entry = ldap_next_entry($ldapconnection, $entry));
                  }
 -                unset($ldap_result); // Free mem.
 -            } while ($ldap_pagedresults && $ldap_cookie !== null && $ldap_cookie != '');
 +                unset($ldapresult); // Free mem.
 +            } while ($ldappagedresults && $ldapcookie !== null && $ldapcookie != '');
          }
  
          // If LDAP paged results were used, the current connection must be completely
          // closed and a new one created, to work without paged results from here on.
 -        if ($ldap_pagedresults) {
 +        if ($ldappagedresults) {
              $this->ldap_close(true);
              $ldapconnection = $this->ldap_connect();
          }
          $count = $DB->count_records_sql('SELECT COUNT(username) AS count, 1 FROM {tmp_extuser}');
          if ($count < 1) {
              print_string('didntgetusersfromldap', 'auth_ldap');
 -            exit;
 +            $dbman->drop_table($table);
 +            $this->ldap_close();
 +            return false;
          } else {
              print_string('gotcountrecordsfromldap', 'auth_ldap', $count);
          }
          return (bool)$user->suspended;
      }
  
+     /**
+      * Test a DN
+      *
+      * @param resource $ldapconn
+      * @param string $dn The DN to check for existence
+      * @param string $message The identifier of a string as in get_string()
+      * @param string|object|array $a An object, string or number that can be used
+      *      within translation strings as in get_string()
+      * @return true or a message in case of error
+      */
+     private function test_dn($ldapconn, $dn, $message, $a = null) {
+         $ldapresult = @ldap_read($ldapconn, $dn, '(objectClass=*)', array());
+         if (!$ldapresult) {
+             if (ldap_errno($ldapconn) == 32) {
+                 // No such object.
+                 return get_string($message, 'auth_ldap', $a);
+             }
+             $a = array('code' => ldap_errno($ldapconn), 'subject' => $a, 'message' => ldap_error($ldapconn));
+             return get_string('diag_genericerror', 'auth_ldap', $a);
+         }
+         return true;
+     }
      /**
       * Test if settings are correct, print info to output.
       */
          global $OUTPUT;
  
          if (!function_exists('ldap_connect')) { // Is php-ldap really there?
-             echo $OUTPUT->notification(get_string('auth_ldap_noextension', 'auth_ldap'));
+             echo $OUTPUT->notification(get_string('auth_ldap_noextension', 'auth_ldap'), \core\output\notification::NOTIFY_ERROR);
              return;
          }
  
          // Check to see if this is actually configured.
-         if ((isset($this->config->host_url)) && ($this->config->host_url !== '')) {
+         if (empty($this->config->host_url)) {
+             // LDAP is not even configured.
+             echo $OUTPUT->notification(get_string('ldapnotconfigured', 'auth_ldap'), \core\output\notification::NOTIFY_ERROR);
+             return;
+         }
+         if ($this->config->ldap_version != 3) {
+             echo $OUTPUT->notification(get_string('diag_toooldversion', 'auth_ldap'), \core\output\notification::NOTIFY_WARNING);
+         }
+         try {
+             $ldapconn = $this->ldap_connect();
+         } catch (Exception $e) {
+             echo $OUTPUT->notification($e->getMessage(), \core\output\notification::NOTIFY_ERROR);
+             return;
+         }
  
-             try {
-                 $ldapconn = $this->ldap_connect();
-                 // Try to connect to the LDAP server.  See if the page size setting is supported on this server.
-                 $pagedresultssupported = ldap_paged_results_supported($this->config->ldap_version, $ldapconn);
-             } catch (Exception $e) {
+         // Display paged file results.
+         if (!ldap_paged_results_supported($this->config->ldap_version, $ldapconn)) {
+             echo $OUTPUT->notification(get_string('pagedresultsnotsupp', 'auth_ldap'), \core\output\notification::NOTIFY_INFO);
+         }
  
-                 // If we couldn't connect and get the supported options, we can only assume we don't support paged results.
-                 $pagedresultssupported = false;
+         // Check contexts.
+         foreach (explode(';', $this->config->contexts) as $context) {
+             $context = trim($context);
+             if (empty($context)) {
+                 echo $OUTPUT->notification(get_string('diag_emptycontext', 'auth_ldap'), \core\output\notification::NOTIFY_WARNING);
+                 continue;
              }
  
-             // Display paged file results.
-             if ((!$pagedresultssupported)) {
-                 echo $OUTPUT->notification(get_string('pagedresultsnotsupp', 'auth_ldap'), \core\output\notification::NOTIFY_INFO);
-             } else if ($ldapconn) {
-                 // We were able to connect successfuly.
-                 echo $OUTPUT->notification(get_string('connectingldapsuccess', 'auth_ldap'), \core\output\notification::NOTIFY_SUCCESS);
+             $message = $this->test_dn($ldapconn, $context, 'diag_contextnotfound', $context);
+             if ($message !== true) {
+                 echo $OUTPUT->notification($message, \core\output\notification::NOTIFY_WARNING);
              }
+         }
  
-         } else {
-             // LDAP is not even configured.
-             echo $OUTPUT->notification(get_string('ldapnotconfigured', 'auth_ldap'), \core\output\notification::NOTIFY_INFO);
+         // Create system role mapping field for each assignable system role.
+         $roles = get_ldap_assignable_role_names();
+         foreach ($roles as $role) {
+             foreach (explode(';', $this->config->{$role['settingname']}) as $groupdn) {
+                 if (empty($groupdn)) {
+                     continue;
+                 }
+                 $role['group'] = $groupdn;
+                 $message = $this->test_dn($ldapconn, $groupdn, 'diag_rolegroupnotfound', $role);
+                 if ($message !== true) {
+                     echo $OUTPUT->notification($message, \core\output\notification::NOTIFY_WARNING);
+                 }
+             }
          }
+         $this->ldap_close(true);
+         // We were able to connect successfuly.
+         echo $OUTPUT->notification(get_string('connectingldapsuccess', 'auth_ldap'), \core\output\notification::NOTIFY_SUCCESS);
      }
  
      /**
@@@ -48,16 -48,16 +48,16 @@@ $string['auth_ldap_expiration_warning_k
  $string['auth_ldap_expireattr_desc'] = 'Optional: Overrides the LDAP attribute that stores password expiry time.';
  $string['auth_ldap_expireattr_key'] = 'Expiry attribute';
  $string['auth_ldapextrafields'] = 'These fields are optional.  You can choose to pre-fill some Moodle user fields with information from the <b>LDAP fields</b> that you specify here. <p>If you leave these fields blank, then nothing will be transferred from LDAP and Moodle defaults will be used instead.</p><p>In either case, the user will be able to edit all of these fields after they log in.</p>';
 -$string['auth_ldap_graceattr_desc'] = 'Optional: Overrides  gracelogin attribute';
 +$string['auth_ldap_graceattr_desc'] = 'Optional: Overrides grace login attribute';
  $string['auth_ldap_gracelogin_key'] = 'Grace login attribute';
 -$string['auth_ldap_gracelogins_desc'] = 'Enable LDAP gracelogin support. After password has expired user can login until gracelogin count is 0. Enabling this setting displays grace login message if password is expired.';
 +$string['auth_ldap_gracelogins_desc'] = 'Enable LDAP grace login support. After password has expired, user can log in until grace login count is 0. Enabling this setting displays grace login message if password has expired.';
  $string['auth_ldap_gracelogins_key'] = 'Grace logins';
  $string['auth_ldap_groupecreators'] = 'List of groups or contexts whose members are allowed to create groups. Separate multiple groups with \';\'. Usually something like \'cn=teachers,ou=staff,o=myorg\'';
  $string['auth_ldap_groupecreators_key'] = 'Group creators';
  $string['auth_ldap_host_url'] = 'Specify LDAP host in URL-form like \'ldap://ldap.myorg.com/\' or \'ldaps://ldap.myorg.com/\'. Separate multiple servers with \';\' to get failover support.';
  $string['auth_ldap_host_url_key'] = 'Host URL';
  $string['auth_ldap_changepasswordurl_key'] = 'Password-change URL';
 -$string['auth_ldap_ldap_encoding'] = 'Specify encoding used by LDAP server. Most probably utf-8, MS AD v2 uses default platform encoding such as cp1252, cp1250, etc.';
 +$string['auth_ldap_ldap_encoding'] = 'Encoding used by the LDAP server, most likely utf-8. If LDAP v2 is selected, Active Directory uses its configured encoding, such as cp1252 or cp1250.';
  $string['auth_ldap_ldap_encoding_key'] = 'LDAP encoding';
  $string['auth_ldap_login_settings'] = 'Login settings';
  $string['auth_ldap_memberattribute'] = 'Optional: Overrides user member attribute, when users belongs to a group. Usually \'member\'';
@@@ -121,7 -121,7 +121,7 @@@ $string['didntfindexpiretime'] = 'passw
  $string['didntgetusersfromldap'] = "Did not get any users from LDAP -- error? -- exiting\n";
  $string['gotcountrecordsfromldap'] = "Got {\$a} records from LDAP\n";
  $string['ldapnotconfigured'] = 'The LDAP host url is currently not configured';
 -$string['morethanoneuser'] = 'Strange! More than one user record found in ldap. Only using the first one.';
 +$string['morethanoneuser'] = 'More than one user record found in LDAP. Using only the first one.';
  $string['needbcmath'] = 'You need the BCMath extension to use expired password checking with Active Directory.';
  $string['needmbstring'] = 'You need the mbstring extension to change passwords in Active Directory';
  $string['nodnforusername'] = 'Error in user_update_password(). No DN for: {$a->username}';
@@@ -152,7 -152,7 +152,7 @@@ $string['updateremfailamb'] = 'Failed t
  $string['updateremfailfield'] = 'Failed to update LDAP with non-existent field (\'{$a->ldapkey}\'). Key ({$a->key}) - old Moodle value: \'{$a->ouvalue}\' new value: \'{$a->nuvalue}\'';
  $string['updatepasserror'] = 'Error in user_update_password(). Error code: {$a->errno}; Error string: {$a->errstring}';
  $string['updatepasserrorexpire'] = 'Error in user_update_password() when reading password expiry time. Error code: {$a->errno}; Error string: {$a->errstring}';
 -$string['updatepasserrorexpiregrace'] = 'Error in user_update_password() when modifying expirationtime and/or gracelogins. Error code: {$a->errno}; Error string: {$a->errstring}';
 +$string['updatepasserrorexpiregrace'] = 'Error in user_update_password() when modifying expiry time and/or grace logins. Error code: {$a->errno}; Error string: {$a->errstring}';
  $string['updateusernotfound'] = 'Could not find user while updating externally. Details follow: search base: \'{$a->userdn}\'; search filter: \'(objectClass=*)\'; search attributes: {$a->attribs}';
  $string['user_activatenotsupportusertype'] = 'auth: ldap user_activate() does not support selected usertype: {$a}';
  $string['user_disablenotsupportusertype'] = 'auth: ldap user_disable() does not support selected usertype: {$a}';
@@@ -163,6 -163,12 +163,12 @@@ $string['userentriestoupdate'] = "User 
  $string['usernotfound'] = 'User not found in LDAP';
  $string['useracctctrlerror'] = 'Error getting userAccountControl for {$a}';
  
+ $string['diag_genericerror'] = 'LDAP error {$a->code} reading {$a->subject}: {$a->message}.';
+ $string['diag_toooldversion'] = 'Its is very unlikely a modern LDAP server uses LDAPv2 protocol. Wrong settings can corrupt values in user fields. Check with your LDAP administrator.';
+ $string['diag_emptycontext'] = 'Empty context found.';
+ $string['diag_contextnotfound'] = 'Context {$a} does not  exists or cannot be read by bind DN.';
+ $string['diag_rolegroupnotfound'] = 'Group {$a->group} for role {$a->localname} does not exists or cannot be read by bind DN.';
  // Deprecated since Moodle 3.4.
  $string['auth_ldap_creators'] = 'List of groups or contexts whose members are allowed to create new courses. Separate multiple groups with \';\'. Usually something like \'cn=teachers,ou=staff,o=myorg\'';
  $string['auth_ldap_creators_key'] = 'Creators';