Example #1
0
 /**
  * Return multidimensional array with details of user courses (at
  * least dn and idnumber).
  *
  * @param string $memberuid user idnumber (without magic quotes).
  * @param object role is a record from the mdl_role table.
  * @return array
  */
 protected function find_ext_enrolments($memberuid, $role)
 {
     global $CFG;
     require_once $CFG->libdir . '/ldaplib.php';
     if (empty($memberuid)) {
         // No "idnumber" stored for this user, so no LDAP enrolments
         return array();
     }
     $ldap_contexts = trim($this->get_config('contexts_role' . $role->id));
     if (empty($ldap_contexts)) {
         // No role contexts, so no LDAP enrolments
         return array();
     }
     $extmemberuid = core_text::convert($memberuid, 'utf-8', $this->get_config('ldapencoding'));
     if ($this->get_config('memberattribute_isdn')) {
         if (!($extmemberuid = $this->ldap_find_userdn($extmemberuid))) {
             return array();
         }
     }
     $ldap_search_pattern = '';
     if ($this->get_config('nested_groups')) {
         $usergroups = $this->ldap_find_user_groups($extmemberuid);
         if (count($usergroups) > 0) {
             foreach ($usergroups as $group) {
                 $ldap_search_pattern .= '(' . $this->get_config('memberattribute_role' . $role->id) . '=' . $group . ')';
             }
         }
     }
     // Default return value
     $courses = array();
     // Get all the fields we will want for the potential course creation
     // as they are light. don't get membership -- potentially a lot of data.
     $ldap_fields_wanted = array('dn', $this->get_config('course_idnumber'));
     $fullname = $this->get_config('course_fullname');
     $shortname = $this->get_config('course_shortname');
     $summary = $this->get_config('course_summary');
     if (isset($fullname)) {
         array_push($ldap_fields_wanted, $fullname);
     }
     if (isset($shortname)) {
         array_push($ldap_fields_wanted, $shortname);
     }
     if (isset($summary)) {
         array_push($ldap_fields_wanted, $summary);
     }
     // Define the search pattern
     if (empty($ldap_search_pattern)) {
         $ldap_search_pattern = '(' . $this->get_config('memberattribute_role' . $role->id) . '=' . ldap_filter_addslashes($extmemberuid) . ')';
     } else {
         $ldap_search_pattern = '(|' . $ldap_search_pattern . '(' . $this->get_config('memberattribute_role' . $role->id) . '=' . ldap_filter_addslashes($extmemberuid) . ')' . ')';
     }
     $ldap_search_pattern = '(&' . $this->get_config('objectclass') . $ldap_search_pattern . ')';
     // Get all contexts and look for first matching user
     $ldap_contexts = explode(';', $ldap_contexts);
     $ldap_pagedresults = ldap_paged_results_supported($this->get_config('ldap_version'));
     foreach ($ldap_contexts as $context) {
         $context = trim($context);
         if (empty($context)) {
             continue;
         }
         $ldap_cookie = '';
         $flat_records = array();
         do {
             if ($ldap_pagedresults) {
                 ldap_control_paged_result($this->ldapconnection, $this->config->pagesize, true, $ldap_cookie);
             }
             if ($this->get_config('course_search_sub')) {
                 // Use ldap_search to find first user from subtree
                 $ldap_result = @ldap_search($this->ldapconnection, $context, $ldap_search_pattern, $ldap_fields_wanted);
             } else {
                 // Search only in this context
                 $ldap_result = @ldap_list($this->ldapconnection, $context, $ldap_search_pattern, $ldap_fields_wanted);
             }
             if (!$ldap_result) {
                 continue;
             }
             if ($ldap_pagedresults) {
                 ldap_control_paged_result_response($this->ldapconnection, $ldap_result, $ldap_cookie);
             }
             // Check and push results. ldap_get_entries() already
             // lowercases the attribute index, so there's no need to
             // use array_change_key_case() later.
             $records = ldap_get_entries($this->ldapconnection, $ldap_result);
             // LDAP libraries return an odd array, really. Fix it.
             for ($c = 0; $c < $records['count']; $c++) {
                 array_push($flat_records, $records[$c]);
             }
             // Free some mem
             unset($records);
         } while ($ldap_pagedresults && !empty($ldap_cookie));
         // 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) {
             $this->ldap_close();
             $this->ldap_connect();
         }
         if (count($flat_records)) {
             $courses = array_merge($courses, $flat_records);
         }
     }
     return $courses;
 }
Example #2
0
 /**
  * Returns all usernames from LDAP
  *
  * @param $filter An LDAP search filter to select desired users
  * @return array of LDAP user names converted to UTF-8
  */
 function ldap_get_userlist($filter = '*')
 {
     $fresult = array();
     $ldapconnection = $this->ldap_connect();
     if ($filter == '*') {
         $filter = '(&(' . $this->config->user_attribute . '=*)' . $this->config->objectclass . ')';
     }
     $contexts = explode(';', $this->config->contexts);
     if (!empty($this->config->create_context)) {
         array_push($contexts, $this->config->create_context);
     }
     $ldap_cookie = '';
     $ldap_pagedresults = ldap_paged_results_supported($this->config->ldap_version);
     foreach ($contexts as $context) {
         $context = trim($context);
         if (empty($context)) {
             continue;
         }
         do {
             if ($ldap_pagedresults) {
                 ldap_control_paged_result($ldapconnection, $this->config->pagesize, true, $ldap_cookie);
             }
             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));
             } else {
                 // Search only in this context.
                 $ldap_result = ldap_list($ldapconnection, $context, $filter, array($this->config->user_attribute));
             }
             if (!$ldap_result) {
                 continue;
             }
             if ($ldap_pagedresults) {
                 ldap_control_paged_result_response($ldapconnection, $ldap_result, $ldap_cookie);
             }
             $users = ldap_get_entries_moodle($ldapconnection, $ldap_result);
             // Add found users to list.
             for ($i = 0; $i < count($users); $i++) {
                 $extuser = core_text::convert($users[$i][$this->config->user_attribute][0], $this->config->ldapencoding, 'utf-8');
                 array_push($fresult, $extuser);
             }
             unset($ldap_result);
             // Free mem.
         } while ($ldap_pagedresults && !empty($ldap_cookie));
     }
     // If paged results were used, make sure the current connection is completely closed
     $this->ldap_close($ldap_pagedresults);
     return $fresult;
 }
Example #3
0
 /**
  * Prints a form for configuring this authentication plugin.
  *
  * This function is called from admin/auth.php, and outputs a full page with
  * a form for configuring this plugin.
  *
  * @param array $page An object containing all the data for this page.
  */
 function config_form($config, $err, $user_fields)
 {
     global $CFG, $OUTPUT;
     if (!function_exists('ldap_connect')) {
         // Is php-ldap really there?
         echo $OUTPUT->notification(get_string('auth_ldap_noextension', 'auth_ldap'));
         // Don't return here, like we do in auth/ldap. We cas use CAS without LDAP.
         // So just warn the user (done above) and define the LDAP constants we use
         // in config.html, to silence the warnings.
         if (!defined('LDAP_DEREF_NEVER')) {
             define('LDAP_DEREF_NEVER', 0);
         }
         if (!defined('LDAP_DEREF_ALWAYS')) {
             define('LDAP_DEREF_ALWAYS', 3);
         }
     }
     if (!ldap_paged_results_supported($this->config->ldap_version)) {
         echo $OUTPUT->notification(get_string('pagedresultsnotsupp', 'auth_ldap'));
     }
     include $CFG->dirroot . '/auth/cas/config.html';
 }
Example #4
0
 /**
  * Prints a form for configuring this authentication plugin.
  *
  * This function is called from admin/auth.php, and outputs a full page with
  * a form for configuring this plugin.
  *
  * @param array $page An object containing all the data for this page.
  */
 function config_form($config, $err, $user_fields)
 {
     global $CFG, $OUTPUT;
     if (!function_exists('ldap_connect')) {
         // Is php-ldap really there?
         echo $OUTPUT->notification(get_string('auth_ldap_noextension', 'auth_ldap'));
         return;
     }
     if (!ldap_paged_results_supported($this->config->ldap_version)) {
         echo $OUTPUT->notification(get_string('pagedresultsnotsupp', 'auth_ldap'));
     }
     include $CFG->dirroot . '/auth/ldap/config.html';
 }
Example #5
0
/**
 * Syncronizes user from external LDAP server to moodle user table
 *
 * Sync is now using username attribute.
 *
 * Syncing users removes or suspends users that dont exists anymore in external LDAP.
 * Creates new users and updates coursecreator status of users.
 *
 * @param bool $do_updates will do pull in data updates from LDAP if relevant
 */
function local_ent_installer_sync_users($ldapauth, $options)
{
    global $CFG, $DB, $MATCH_STATUS;
    mtrace('');
    $enable = get_config('local_ent_installer', 'sync_enable');
    if (!$enable) {
        mtrace(get_string('syncdisabled', 'local_ent_installer'));
        return;
    }
    $USERFIELDS = local_ent_installer_load_user_fields();
    $lastrun = get_config('local_ent_installer', 'last_sync_date');
    mtrace(get_string('lastrun', 'local_ent_installer', userdate($lastrun)));
    mtrace(get_string('connectingldap', 'auth_ldap'));
    if (isset($CFG->auth_ldap_sync_user_type)) {
        $ldapauth->config->user_type = $CFG->auth_ldap_sync_user_type;
    }
    if (isset($CFG->auth_ldap_sync_search_contexts)) {
        $ldapauth->config->contexts = $CFG->auth_ldap_sync_search_contexts;
    }
    $ldapconnection = $ldapauth->ldap_connect();
    $dbman = $DB->get_manager();
    list($usec, $sec) = explode(' ', microtime());
    $starttick = (double) $sec + (double) $usec;
    // Define table user to be created.
    $table = new xmldb_table('tmp_extuser');
    $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
    $table->add_field('username', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
    $table->add_field('mnethostid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
    $table->add_field('usertype', XMLDB_TYPE_CHAR, '16', null, null, null, null);
    $table->add_field('lastmodified', XMLDB_TYPE_INTEGER, '11', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
    $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
    $table->add_index('userprofile', XMLDB_INDEX_UNIQUE, array('mnethostid', 'username', 'usertype'));
    mtrace(get_string('creatingtemptable', 'auth_ldap', 'tmp_extuser'));
    if ($dbman->table_exists($table)) {
        $dbman->drop_table($table);
    }
    $dbman->create_temp_table($table);
    //
    // get user's list from ldap to sql in a scalable fashion from different user profiles
    // defined as LDAP filters
    //
    // prepare some data we'll need
    $filters = array();
    $institutionid = get_config('local_ent_installer', 'institution_id');
    // Students.
    if (empty($options['role']) || preg_match('/eleve/', $options['role'])) {
        $filterdef = new StdClass();
        $filterdef->institution = '(ENTPersonEtablissements=' . $institutionid . ')';
        $filterdef->usertype = '(ENTPersonProfils=Eleves)';
        $filterdef->userfield = 'eleve';
        $filters[] = $filterdef;
    }
    // Teaching staff.
    if (empty($options['role']) || preg_match('/enseignant/', $options['role'])) {
        $filterdef = new StdClass();
        $filterdef->institution = '(ENTPersonEtablissements=' . $institutionid . ')';
        $filterdef->usertype = '(|(ENTPersonProfils=Professeurs)(ENTPersonProfils=Documentalistes)(ENTPersonProfils=ProfesseursVacataires)(ENTPersonProfils=Inspecteur))';
        $filterdef->userfield = 'enseignant';
        $filters[] = $filterdef;
    }
    // Non teaching staff.
    if (empty($options['role']) || preg_match('/administration/', $options['role'])) {
        $filterdef = new StdClass();
        $filterdef->institution = '(ENTPersonEtablissements=' . $institutionid . ')';
        $filterdef->usertype = '(|(ENTPersonProfils=Administrateurs)(ENTPersonProfils=Direction)(ENTPersonProfils=PersonnelAdministratif)(ENTPersonProfils=PersonnelNonEnseignant)' . '(ENTPersonProfils=AdministrateurITOP)(ENTPersonProfils=PersonnelVieScolaire)(ENTPersonProfils=ATOS)(ENTPersonProfils=PersonnelRectorat)(ENTPersonProfils=MissionTice)' . '(ENTPersonProfils=TuteurEntreprise)(ENTPersonProfils=CollectivitesLocales)(ENTPersonProfils=AccompagnementEducatif)(ENTPersonProfils=CPE)(ENTPersonProfils=Invite)' . '(ENTPersonProfils=AdminProjet))';
        $filterdef->userfield = 'administration';
        $filters[] = $filterdef;
    }
    $contexts = explode(';', $ldapauth->config->contexts);
    if (!empty($ldapauth->config->create_context)) {
        array_push($contexts, $ldapauth->config->create_context);
    }
    $ldap_pagedresults = ldap_paged_results_supported($ldapauth->config->ldap_version);
    $ldap_cookie = '';
    foreach ($filters as $filterdef) {
        $filter = '(&(' . $ldapauth->config->user_attribute . '=*)' . $filterdef->usertype . $filterdef->institution . ')';
        foreach ($contexts as $context) {
            $context = trim($context);
            if (empty($context)) {
                continue;
            }
            do {
                if ($ldap_pagedresults) {
                    ldap_control_paged_result($ldapconnection, $ldapauth->config->pagesize, true, $ldap_cookie);
                }
                if ($ldapauth->config->search_sub) {
                    // Use ldap_search to find first user from subtree.
                    mtrace("ldapsearch {$context}, {$filter} for " . $ldapauth->config->user_attribute);
                    $ldap_result = ldap_search($ldapconnection, $context, $filter, array($ldapauth->config->user_attribute, 'whenChanged'));
                } else {
                    // Search only in this context.
                    mtrace("ldaplist {$context}, {$filter} for " . $ldapauth->config->user_attribute);
                    $ldap_result = ldap_list($ldapconnection, $context, $filter, array($ldapauth->config->user_attribute, 'whenChanged'));
                }
                if (!$ldap_result) {
                    continue;
                }
                if ($ldap_pagedresults) {
                    ldap_control_paged_result_response($ldapconnection, $ldap_result, $ldap_cookie);
                }
                if ($entry = @ldap_first_entry($ldapconnection, $ldap_result)) {
                    do {
                        $value = ldap_get_values_len($ldapconnection, $entry, $ldapauth->config->user_attribute);
                        $value = core_text::convert($value[0], $ldapauth->config->ldapencoding, 'utf-8');
                        $modify = ldap_get_values_len($ldapconnection, $entry, 'whenChanged');
                        // $modify = strtotime($modify[0]); // OpenLDAP version
                        if (preg_match('/(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})/', $modify[0], $matches)) {
                            $year = $matches[1];
                            $month = $matches[2];
                            $day = $matches[3];
                            $hour = $matches[4];
                            $min = $matches[5];
                            $sec = $matches[6];
                            $modify = mktime($hour, $min, $sec, $month, $day, $year);
                        } else {
                            $modify = time();
                        }
                        local_ent_installer_ldap_bulk_insert($value, $filterdef->userfield, $modify);
                    } while ($entry = ldap_next_entry($ldapconnection, $entry));
                }
                unset($ldap_result);
                // Free mem.
            } while ($ldap_pagedresults && !empty($ldap_cookie));
        }
    }
    /*
     * 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) {
        $ldapauth->ldap_close(true);
        $ldapconnection = $ldapauth->ldap_connect();
    }
    /*
     * preserve our user database
     * if the temp table is empty, it probably means that something went wrong, exit
     * so as to avoid mass deletion of users; which is hard to undo
     */
    $count = $DB->count_records_sql('SELECT COUNT(username) AS count, 1 FROM {tmp_extuser}');
    if ($count < 1) {
        mtrace(get_string('didntgetusersfromldap', 'auth_ldap'));
        $dbman->drop_table($table);
        $ldapauth->ldap_close(true);
        return false;
    } else {
        mtrace(get_string('gotcountrecordsfromldap', 'auth_ldap', $count));
    }
    /********************** User removal. *****************************/
    /*
     * Find users in DB that aren't in ldap -- to be removed!
     * this is still not as scalable (but how often do we mass delete?)
     */
    if ($ldapauth->config->removeuser != AUTH_REMOVEUSER_KEEP) {
        $sql = '
            SELECT
                u.*
            FROM 
                {user} u
            LEFT JOIN 
                {tmp_extuser} e
            ON 
                (u.username = e.username AND u.mnethostid = e.mnethostid)
            WHERE
                u.auth = ? AND 
                u.deleted = 0 AND 
                e.username IS NULL
        ';
        $real_user_auth = get_config('local_ent_installer', 'real_used_auth');
        $remove_users = $DB->get_records_sql($sql, array($real_user_auth));
        if (!empty($remove_users)) {
            mtrace(get_string('userentriestoremove', 'auth_ldap', count($remove_users)));
            foreach ($remove_users as $user) {
                if ($ldapauth->config->removeuser == AUTH_REMOVEUSER_FULLDELETE) {
                    if (empty($options['simulate'])) {
                        if (empty($options['fulldelete'])) {
                            // Make a light delete of users, but keeping data for revival.
                            $user->deleted = 1;
                            try {
                                $DB->update_record('user', $user);
                                // Subscription does not work if it contains a user marked as deleted
                                $DB->delete_records('cohort_members', array('userid' => $user->id));
                                mtrace(get_string('auth_dbdeleteuser', 'auth_db', array('name' => $user->username, 'id' => $user->id)));
                            } catch (Exception $e) {
                                mtrace(get_string('auth_dbdeleteusererror', 'auth_db', $user->username));
                            }
                        } else {
                            // Make a complete delete of users, enrols, grades and data
                            if (delete_user($user)) {
                                echo "\t";
                                mtrace(get_string('auth_dbdeleteuser', 'auth_db', array('name' => $user->username, 'id' => $user->id)));
                            } else {
                                echo "\t";
                                mtrace(get_string('auth_dbdeleteusererror', 'auth_db', $user->username));
                            }
                        }
                    } else {
                        mtrace(get_string('simulateuserdelete', 'ent_installer', $user->username));
                    }
                } elseif ($ldapauth->config->removeuser == AUTH_REMOVEUSER_SUSPEND) {
                    if (empty($options['simulate'])) {
                        $updateuser = new stdClass();
                        $updateuser->id = $user->id;
                        $updateuser->auth = 'nologin';
                        $DB->update_record('user', $updateuser);
                        echo "\t";
                        mtrace(get_string('auth_dbsuspenduser', 'auth_db', array('name' => $user->username, 'id' => $user->id)));
                        $euser = $DB->get_record('user', array('id' => $user->id));
                        //events_trigger('user_updated', $euser); => deprecated !
                        $event = core\event\user_updated::create_from_userid($euser->id);
                        $event->trigger();
                    } else {
                        mtrace(get_string('simulateusersuspend', 'ent_installer', $user->username));
                    }
                }
            }
        } else {
            mtrace(get_string('nouserentriestoremove', 'auth_ldap'));
        }
        unset($remove_users);
        // free mem!
    } else {
        mtrace("AUTH_REMOVEUSER_KEEP is on or ldapauth config removeuser is off");
    }
    /********************* Revive suspended users. *********************************/
    if (!empty($ldapauth->config->removeuser) && $ldapauth->config->removeuser == AUTH_REMOVEUSER_SUSPEND) {
        $sql = "\n            SELECT\n                u.id, u.username\n            FROM\n                {user} u\n            JOIN\n                {tmp_extuser} e\n            ON\n                (u.username = e.username AND u.mnethostid = e.mnethostid)\n            WHERE\n                u.auth = 'nologin' AND u.deleted = 0\n        ";
        $revive_users = $DB->get_records_sql($sql);
        if (!empty($revive_users)) {
            mtrace(get_string('userentriestorevive', 'auth_ldap', count($revive_users)));
            foreach ($revive_users as $user) {
                $updateuser = new stdClass();
                $updateuser->id = $user->id;
                $updateuser->auth = $ldapauth->authtype;
                $DB->update_record('user', $updateuser);
                echo "\t";
                mtrace(get_string('auth_dbreviveduser', 'auth_db', array('name' => $user->username, 'id' => $user->id)));
                $euser = $DB->get_record('user', array('id' => $user->id));
                //events_trigger('user_updated', $euser); => deprecated !
                $event = core\event\user_updated::create_from_userid($euser->id);
                $event->trigger();
            }
        } else {
            mtrace(get_string('nouserentriestorevive', 'auth_ldap'));
        }
        unset($revive_users);
    }
    /****************************** User Updates - time-consuming (optional). ***************************/
    // This might be an OBSOLETE code, regarding the updat ecapability of the create process.
    if (!empty($options['doupdates'])) {
        // Narrow down what fields we need to update.
        $all_keys = array_keys(get_object_vars($ldapauth->config));
        $updatekeys = array();
        foreach ($all_keys as $key) {
            if (preg_match('/^field_updatelocal_(.+)$/', $key, $match)) {
                /*
                 * If we have a field to update it from
                 * and it must be updated 'onlogin' we
                 * update it on cron
                 */
                if (!empty($ldapauth->config->{'field_map_' . $match[1]}) && $ldapauth->config->{$match[0]} === 'onlogin') {
                    // The actual key name.
                    array_push($updatekeys, $match[1]);
                }
            }
        }
        unset($all_keys);
        unset($key);
    } else {
        mtrace(get_string('noupdatestobedone', 'auth_ldap'));
    }
    if (@$options['doupdates'] and !empty($updatekeys)) {
        // Run updates only if relevant.
        $users = $DB->get_records_sql('SELECT u.username, u.id
                                         FROM {user} u
                                        WHERE u.deleted = 0 AND u.auth = ? AND u.mnethostid = ?', array($ldapauth->authtype, $CFG->mnet_localhost_id));
        if (!empty($users)) {
            mtrace(get_string('userentriestoupdate', 'auth_ldap', count($users)));
            $sitecontext = context_system::instance();
            if (!empty($ldapauth->config->creators) and !empty($ldapauth->config->memberattribute) && ($roles = get_archetype_roles('coursecreator'))) {
                // We can only use one, let's use the first one.
                $creatorrole = array_shift($roles);
            } else {
                $creatorrole = false;
            }
            $transaction = $DB->start_delegated_transaction();
            $xcount = 0;
            $maxxcount = 100;
            foreach ($users as $user) {
                echo "\t";
                $tracestr = get_string('auth_dbupdatinguser', 'auth_db', array('name' => $user->username, 'id' => $user->id));
                if (!$ldapauth->update_user_record($user->username, $updatekeys)) {
                    $tracestr .= ' - ' . get_string('skipped');
                }
                mtrace($tracestr);
                $xcount++;
                // Update course creators if needed.
                if ($creatorrole !== false) {
                    if ($ldapauth->iscreator($user->username)) {
                        role_assign($creatorrole->id, $user->id, $sitecontext->id, $ldapauth->roleauth);
                    } else {
                        role_unassign($creatorrole->id, $user->id, $sitecontext->id, $ldapauth->roleauth);
                    }
                }
            }
            $transaction->allow_commit();
            unset($users);
            // free mem
        }
    } else {
        // End do updates.
        mtrace(get_string('noupdatestobedone', 'auth_ldap'));
    }
    /***************************** User Additions or full profile update. **********************************/
    /*
     * Find users missing in DB that are in LDAP or users that have been modified since last run
     * and gives me a nifty object I don't want.
     * note: we do not care about deleted accounts anymore, this feature was replaced by suspending to nologin auth plugin
     */
    if (empty($options['force'])) {
        $sql = 'SELECT e.id, e.username, e.usertype
                  FROM {tmp_extuser} e
                  LEFT JOIN {user} u ON (e.username = u.username AND e.mnethostid = u.mnethostid)
                 WHERE u.id IS NULL OR (
                 e.lastmodified > ? ) ORDER BY e.username';
        $params = array($lastrun);
    } else {
        $sql = 'SELECT e.id, e.username, e.usertype
                  FROM {tmp_extuser} e ORDER BY e.username';
        $params = array();
    }
    $add_users = $DB->get_records_sql($sql, $params);
    if (!empty($add_users)) {
        mtrace(get_string('userentriestoadd', 'auth_ldap', count($add_users)));
        $sitecontext = context_system::instance();
        if (!empty($ldapauth->config->creators) && !empty($ldapauth->config->memberattribute) && ($roles = get_archetype_roles('coursecreator'))) {
            // We can only use one, let's use the first one.
            $creatorrole = array_shift($roles);
        } else {
            $creatorrole = false;
        }
        $inserterrorcount = 0;
        $updateerrorcount = 0;
        $insertcount = 0;
        $updatecount = 0;
        // Before processing each user we check that every common hosts access keys fields are correctly set up
        if (file_exists($CFG->dirroot . '/blocks/user_mnet_hosts/locallib.php')) {
            require_once $CFG->dirroot . '/blocks/user_mnet_hosts/locallib.php';
            $like = $DB->sql_like('wwwroot', ':wwwroot', false, false);
            $mainhosts = explode(',', @$CFG->mainhostprefix);
            if (!empty($mainhosts)) {
                foreach ($mainhosts as $hostwww) {
                    // Check if common hosts exist
                    if ($commonroots = $DB->get_records_select('mnet_host', " {$like} AND deleted <> 1 ", array('wwwroot' => $hostwww . '%'))) {
                        foreach ($commonroots as $root) {
                            // Check if current common host access key field exists, if not create one
                            $expectedname = user_mnet_hosts_make_accesskey($root->wwwroot, true);
                            $accessmainhost = $DB->get_records('user_info_field', array('shortname' => $expectedname), '', 'id');
                            if (!$accessmainhost) {
                                mtrace("Common host access key '{$expectedname}' not found");
                                if (!isset($CFG->accesscategory)) {
                                    $accesscategory = new stdClass();
                                    $accesscategory->name = get_string('accesscategory', 'block_user_mnet_hosts');
                                    $accesscategory->sortorder = 1;
                                    $id = $DB->insert_record('user_info_category', $accesscategory);
                                    set_config('accesscategory', $id);
                                }
                                $hostkey = user_mnet_hosts_make_accesskey($root->wwwroot, false);
                                $newfield = new stdClass();
                                $newfield->shortname = $expectedname;
                                $newfield->name = get_string('fieldname', 'block_user_mnet_hosts') . ' ' . $hostkey;
                                $newfield->datatype = 'checkbox';
                                $newfield->locked = 1;
                                $newfield->categoryid = $CFG->accesscategory;
                                if ($DB->insert_record('user_info_field', $newfield)) {
                                    mtrace("Common host access key created : {$expectedname}");
                                } else {
                                    mtrace("Common host access key creation error : {$expectedname}");
                                }
                            }
                        }
                    }
                }
            }
        }
        // Before processing each user we check that custom field to store ENT profil & guid are correctly set up
        $infoscateg = $DB->get_records('user_info_category', array('name' => 'Informations académiques'), '', 'id');
        if (!empty($infoscateg)) {
            $infoscateg = array_values($infoscateg);
            $idinfocateg = $infoscateg[0]->id;
            $profilent = $DB->get_records('user_info_field', array('shortname' => 'profilent'), '', 'id');
            if (empty($profilent)) {
                $maxsortorder = $DB->get_records_sql('SELECT MAX(sortorder) AS maxsortorder FROM {user_info_field} WHERE categoryid = ?', array($idinfocateg));
                $maxsortorder = array_values($maxsortorder);
                $sortordertoinsert = $maxsortorder[0]->maxsortorder + 1;
                $user_info_field = new stdClass();
                $user_info_field->shortname = 'profilent';
                $user_info_field->name = 'Profil ENT';
                $user_info_field->datatype = 'text';
                $user_info_field->description = '<p>Les profils ENT sont utilisés pour traitements spécifiques post-synchronisation et proviennent du champ ENTPersonProfils du SDET<br></p>';
                $user_info_field->descriptionformat = 1;
                $user_info_field->categoryid = $idinfocateg;
                $user_info_field->sortorder = $sortordertoinsert;
                $user_info_field->required = 0;
                $user_info_field->locked = 1;
                $user_info_field->visible = 0;
                $user_info_field->forceunique = 0;
                $user_info_field->signup = 0;
                $user_info_field->defaultdata = '';
                $user_info_field->defaultdataformat = 0;
                $user_info_field->param1 = '30';
                $user_info_field->param2 = '2048';
                $user_info_field->param3 = '0';
                $user_info_field->param4 = '';
                $user_info_field->param5 = '';
                $fieldid = $DB->insert_record('user_info_field', $user_info_field);
                mtrace("user_info_field : 'profilent' inserted !");
                if ($USERFIELDS['profilent'] == 0 && $fieldid != 0) {
                    $USERFIELDS['profilent'] = $fieldid;
                } else {
                    mtrace("user_info_field : cannot get id for 'profilent' !");
                }
            }
            $guident = $DB->get_records('user_info_field', array('shortname' => 'guident'), '', 'id');
            if (empty($guident)) {
                $maxsortorder = $DB->get_records_sql('SELECT MAX(sortorder) AS maxsortorder FROM {user_info_field} WHERE categoryid = ?', array($idinfocateg));
                $maxsortorder = array_values($maxsortorder);
                $sortordertoinsert = $maxsortorder[0]->maxsortorder + 1;
                $user_info_field = new stdClass();
                $user_info_field->shortname = 'guident';
                $user_info_field->name = 'Guid ENT';
                $user_info_field->datatype = 'text';
                $user_info_field->description = '<p><p>Le GUID ENT est utilisé pour la synchronisation des comptes et provient du champ AD objectGuid<br></p></p>';
                $user_info_field->descriptionformat = 1;
                $user_info_field->categoryid = $idinfocateg;
                $user_info_field->sortorder = $sortordertoinsert;
                $user_info_field->required = 0;
                $user_info_field->locked = 1;
                $user_info_field->visible = 0;
                $user_info_field->forceunique = 0;
                $user_info_field->signup = 0;
                $user_info_field->defaultdata = '';
                $user_info_field->defaultdataformat = 0;
                $user_info_field->param1 = '36';
                $user_info_field->param2 = '2048';
                $user_info_field->param3 = '0';
                $user_info_field->param4 = '';
                $user_info_field->param5 = '';
                $fieldid = $DB->insert_record('user_info_field', $user_info_field);
                mtrace("user_info_field : 'guident' inserted !");
                if ($USERFIELDS['guident'] == 0 && $fieldid != 0) {
                    $USERFIELDS['guident'] = $fieldid;
                } else {
                    mtrace("user_info_field : cannot get id for 'guident' !");
                }
            }
        } else {
            mtrace("user_info_category : 'Informations académiques' missing !");
        }
        // We scan new proposed users from LDAP.
        foreach ($add_users as $user) {
            // Save usertype.
            $usertype = $user->usertype;
            $user = local_ent_installer_get_userinfo_asobj($ldapauth, $user->username, $options);
            // Restore usertype in user.
            $user->usertype = $usertype;
            // Post filter of idnumber
            list($foosdetprefix, $user->idnumber) = explode('$', $user->idnumber);
            if (empty($user->firstname)) {
                mtrace('ERROR : Missing firstname in incoming record ' . $user->username);
                $updateerrorcount++;
                continue;
            }
            if (empty($user->lastname)) {
                mtrace('ERROR : Missing lastname in incoming record ' . $user->username);
                $updateerrorcount++;
                continue;
            }
            if (empty($user->email)) {
                $user->email = local_ent_installer_generate_email($user);
            }
            // Prep a few params.
            $user->modified = time();
            $user->confirmed = 1;
            $user->deleted = 0;
            $user->suspended = 0;
            // Authentication is the ldap plugin or a real auth plugin defined in setup.
            $realauth = get_config('local_ent_installer', 'real_used_auth');
            $user->auth = empty($realauth) ? $ldapauth->authtype : $realauth;
            $user->mnethostid = $CFG->mnet_localhost_id;
            $user->country = $CFG->country;
            /*
             * Get_userinfo_asobj() might have replaced $user->username with the value.
             * from the LDAP server (which can be mixed-case). Make sure it's lowercase
             */
            $user->username = trim(core_text::strtolower($user->username));
            if (empty($user->lang)) {
                $user->lang = $CFG->lang;
            }
            /*
             * Process additional info for student :
             * extra information fields transport and regime.
             */
            if ($user->usertype == 'eleve') {
                // Transport.
                $user->profile_field_transport = 'Y' == @$user->ENTEleveTransport ? '1' : 0;
                // Regime.
                $user->profile_field_regime = @$user->ENTEleveRegime;
                // Cohort (must have).
                $user->profile_field_cohort = $user->ENTEleveClasses;
            }
            // Profil ENT
            $user->profile_field_profil = $user->ENTPersonProfils;
            // Guid ENT
            $user->profile_field_guid = $user->objectGUID;
            $personfunction = @$user->ENTPersonFonctions;
            unset($user->ENTPersonFonctions);
            // Get the last term of personfunction and set it as department.
            if (!empty($personfunction)) {
                preg_match('/\\$([^\\$]+)$/', $personfunction, $matches);
                $user->department = $matches[1];
            }
            if (empty($options['simulate'])) {
                // Creation/full update sequence.
                $a = clone $user;
                $a->function = $personfunction;
                /*
                 * Special case : si there a matching policy possible for previous accounts NOT being
                 * created by this system ? 
                 */
                try {
                    $oldrec = local_ent_installer_guess_old_record($user, $status);
                } catch (Exception $e) {
                    mtrace('ERROR : Fail to bind user ' . $user->username);
                    if ($options['verbose']) {
                        trace_debug($e);
                    }
                    $inserterrorcount++;
                    continue;
                }
                if ($oldrec) {
                    $a->status = $MATCH_STATUS[$status];
                    $id = $user->id = $oldrec->id;
                    try {
                        $DB->update_record('user', $user);
                        mtrace(get_string('dbupdateuser', 'local_ent_installer', $a));
                        $updatecount++;
                    } catch (Exception $e) {
                        mtrace('ERROR : Fail to update user ' . $user->username);
                        if ($options['verbose']) {
                            trace_debug($e);
                        }
                        $updateerrorcount++;
                        continue;
                    }
                } else {
                    try {
                        $id = $DB->insert_record('user', $user);
                        mtrace(get_string('dbinsertuser', 'local_ent_installer', $a));
                        $insertcount++;
                    } catch (Exception $e) {
                        mtrace('ERROR : Fail to insert user ' . $user->username);
                        if ($options['verbose']) {
                            trace_debug($e);
                        }
                        $inserterrorcount++;
                        continue;
                    }
                }
            } else {
                $a = clone $user;
                $a->function = $personfunction;
                if (!($oldrec = local_ent_installer_guess_old_record($user, $status))) {
                    mtrace(get_string('dbinsertusersimul', 'local_ent_installer', $a));
                } else {
                    $a->status = $MATCH_STATUS[$status];
                    mtrace(get_string('dbupdateusersimul', 'local_ent_installer', $a));
                }
            }
            if (empty($options['simulate'])) {
                $euser = $DB->get_record('user', array('id' => $id));
                //events_trigger('user_created', $euser); => deprecated !
                $event = core\event\user_created::create_from_userid($euser->id);
                $event->trigger();
                if (!empty($ldapauth->config->forcechangepassword)) {
                    set_user_preference('auth_forcepasswordchange', 1, $id);
                }
                // Cohort information / create/update cohorts.
                if ($user->usertype == 'eleve') {
                    // Adds user to cohort and create cohort if missing.
                    $cohortshort = local_ent_installer_check_cohort($id, $user->profile_field_cohort);
                    local_ent_installer_update_info_data($id, $USERFIELDS['transport'], $user->profile_field_transport);
                    local_ent_installer_update_info_data($id, $USERFIELDS['regime'], $user->profile_field_regime);
                    local_ent_installer_update_info_data($id, $USERFIELDS['cohort'], $cohortshort);
                    if (isset($user->ENTEleveGroupes) && count($user->ENTEleveGroupes) > 0) {
                        foreach ($user->ENTEleveGroupes as $group) {
                            $cohortshort = local_ent_installer_check_cohort($id, $group, false, $options['verbose']);
                        }
                    }
                }
                // Create profil cohort to allow enrollment by profil
                $cohortshort = local_ent_installer_check_cohort($id, $user->profile_field_profil, false, $options['verbose']);
                if ($user->profile_field_profil == 'Administrateurs') {
                    if (isset($id)) {
                        $managerrole = $DB->get_record('role', array('shortname' => 'manager'));
                        role_assign($managerrole->id, $id, 1);
                        // 1=system context
                    } else {
                        mtrace('WARNING : Cannot assign role manager to ' . $user->username);
                    }
                }
                local_ent_installer_update_info_data($id, $USERFIELDS['profilent'], $user->profile_field_profil);
                local_ent_installer_update_info_data($id, $USERFIELDS['guident'], $user->profile_field_guid);
                // Add course creators if needed.
                if ($creatorrole !== false and $ldapauth->iscreator($user->username)) {
                    role_assign($creatorrole->id, $id, $sitecontext->id, $ldapauth->roleauth);
                }
                // Process user_fields setup.
                if (preg_match('#\\$CTR\\$#', $personfunction)) {
                    // Special case.
                    local_ent_installer_update_info_data($id, $USERFIELDS['cdt'], 1);
                } else {
                    // Other user types.
                    local_ent_installer_update_info_data($id, $USERFIELDS[$user->usertype], 1);
                }
                if (file_exists($CFG->dirroot . '/blocks/user_mnet_hosts/locallib.php')) {
                    // user_mnet_hosts local library has already been included upstream of this script
                    // All users have access marked on self.
                    user_mnet_hosts_set_access($id, true);
                    // Setting default access field policy for powered users.
                    if ($user->usertype == 'enseignant' || $user->usertype == 'administration') {
                        $like = $DB->sql_like('wwwroot', ':wwwroot', false, false);
                        $mainhosts = explode(',', @$CFG->mainhostprefix);
                        $given = false;
                        if (!empty($mainhosts)) {
                            foreach ($mainhosts as $hostwww) {
                                if ($commonroots = $DB->get_records_select('mnet_host', " {$like} AND deleted <> 1 ", array('wwwroot' => $hostwww . '%'))) {
                                    foreach ($commonroots as $root) {
                                        $given = true;
                                        user_mnet_hosts_set_access($id, true, $root->wwwroot);
                                    }
                                }
                            }
                        }
                        if (!$given) {
                            mtrace('Giving teacher access : no common host found ');
                        }
                    }
                }
                // Add a workplace to teachers.
                if ($user->usertype == 'enseignant') {
                    if (get_config('local_ent_installer', 'build_teacher_category')) {
                        if (isset($options['unassignteachercategoryrole'])) {
                            local_ent_installer_make_teacher_category($euser, $options['unassignteachercategoryrole']);
                        } else {
                            local_ent_installer_make_teacher_category($euser);
                        }
                    }
                }
                // Identify librarians and give library enabled role at system level.
                if (preg_match('#\\$DOC\\$#', $personfunction)) {
                    if ($role = $DB->get_record('role', array('shortname' => 'librarian'))) {
                        $systemcontext = context_system::instance();
                        role_assign($role->id, $id, $systemcontext->id);
                    }
                }
            }
        }
        unset($add_users);
        // free mem
    } else {
        mtrace(get_string('nouserstobeadded', 'auth_ldap'));
    }
    $ldapauth->ldap_close();
    list($usec, $sec) = explode(' ', microtime());
    $stoptick = (double) $sec + (double) $usec;
    $deltatime = $stoptick - $starttick;
    mtrace('Execution time : ' . $deltatime);
    $benchrec = new StdClass();
    $benchrec->timestart = floor($starttick);
    $benchrec->timerun = ceil($deltatime);
    $benchrec->added = 0 + @$insertcount;
    $benchrec->updated = 0 + @$updatecount;
    $benchrec->updateerrors = 0 + @$inserterrorcount;
    $benchrec->inserterrors = 0 + @$updateerrorcount;
    $DB->insert_record('local_ent_installer', $benchrec);
    // Mark last time the user sync was run.
    set_config('last_sync_date', time(), 'local_ent_installer');
    try {
        $dbman->drop_table($table);
    } catch (Exception $e) {
    }
    return true;
}