Example #1
0
 /**
  * syncronizes user fron external db to moodle user table
  *
  * Sync is now using username attribute.
  *
  * Syncing users removes or suspends users that dont exists anymore in external db.
  * Creates new users and updates coursecreator status of users.
  *
  * @param int $bulk_insert_records will insert $bulkinsert_records per insert statement
  *                         valid only with $unsafe. increase to a couple thousand for
  *                         blinding fast inserts -- but test it: you may hit mysqld's
  *                         max_allowed_packet limit.
  * @param bool $do_updates will do pull in data updates from ldap if relevant
  */
 function sync_users($bulk_insert_records = 1000, $do_updates = true)
 {
     global $CFG;
     $textlib = textlib_get_instance();
     $droptablesql = array();
     /// sql commands to drop the table (because session scope could be a problem for
     /// some persistent drivers like ODBTP (mssql) or if this function is invoked
     /// from within a PHP application using persistent connections
     $temptable = $CFG->prefix . 'extuser';
     $createtemptablesql = '';
     // configure a temp table
     print "Configuring temp table\n";
     switch (strtolower($CFG->dbfamily)) {
         case 'mysql':
             $droptablesql[] = 'DROP TEMPORARY TABLE ' . $temptable;
             // sql command to drop the table (because session scope could be a problem)
             $createtemptablesql = 'CREATE TEMPORARY TABLE ' . $temptable . ' (username VARCHAR(64), PRIMARY KEY (username)) TYPE=MyISAM';
             break;
         case 'postgres':
             $droptablesql[] = 'DROP TABLE ' . $temptable;
             // sql command to drop the table (because session scope could be a problem)
             $bulk_insert_records = 1;
             // no support for multiple sets of values
             $createtemptablesql = 'CREATE TEMPORARY TABLE ' . $temptable . ' (username VARCHAR(64), PRIMARY KEY (username))';
             break;
         case 'mssql':
             $temptable = '#' . $temptable;
             /// MSSQL temp tables begin with #
             $droptablesql[] = 'DROP TABLE ' . $temptable;
             // sql command to drop the table (because session scope could be a problem)
             $bulk_insert_records = 1;
             // no support for multiple sets of values
             $createtemptablesql = 'CREATE TABLE ' . $temptable . ' (username VARCHAR(64), PRIMARY KEY (username))';
             break;
         case 'oracle':
             $droptablesql[] = 'TRUNCATE TABLE ' . $temptable;
             // oracle requires truncate before being able to drop a temp table
             $droptablesql[] = 'DROP TABLE ' . $temptable;
             // sql command to drop the table (because session scope could be a problem)
             $bulk_insert_records = 1;
             // no support for multiple sets of values
             $createtemptablesql = 'CREATE GLOBAL TEMPORARY TABLE ' . $temptable . ' (username VARCHAR(64), PRIMARY KEY (username)) ON COMMIT PRESERVE ROWS';
             break;
     }
     execute_sql_arr($droptablesql, true, false);
     /// Drop temp table to avoid persistence problems later
     echo "Creating temp table {$temptable}\n";
     if (!execute_sql($createtemptablesql, false)) {
         print "Failed to create temporary users table - aborting\n";
         exit;
     }
     print "Connecting to ldap...\n";
     $ldapconnection = $this->ldap_connect();
     if (!$ldapconnection) {
         $this->ldap_close();
         print get_string('auth_ldap_noconnect', 'auth', $this->config->host_url);
         exit;
     }
     ////
     //// get user's list from ldap to sql in a scalable fashion
     ////
     // prepare some data we'll need
     $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);
     }
     $fresult = array();
     foreach ($contexts as $context) {
         $context = trim($context);
         if (empty($context)) {
             continue;
         }
         begin_sql();
         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 ($entry = ldap_first_entry($ldapconnection, $ldap_result)) {
             do {
                 $value = ldap_get_values_len($ldapconnection, $entry, $this->config->user_attribute);
                 $value = $textlib->convert($value[0], $this->config->ldapencoding, 'utf-8');
                 // usernames are __always__ lowercase.
                 array_push($fresult, moodle_strtolower($value));
                 if (count($fresult) >= $bulk_insert_records) {
                     $this->ldap_bulk_insert($fresult, $temptable);
                     $fresult = array();
                 }
             } while ($entry = ldap_next_entry($ldapconnection, $entry));
         }
         unset($ldap_result);
         // free mem
         // insert any remaining users and release mem
         if (count($fresult)) {
             $this->ldap_bulk_insert($fresult, $temptable);
             $fresult = array();
         }
         commit_sql();
     }
     /// 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 = get_record_sql('SELECT COUNT(username) AS count, 1 FROM ' . $temptable);
     $count = $count->{'count'};
     if ($count < 1) {
         print "Did not get any users from LDAP -- error? -- exiting\n";
         exit;
     } else {
         print "Got {$count} records from LDAP\n\n";
     }
     /// 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 (!empty($this->config->removeuser)) {
         $sql = "SELECT u.id, u.username, u.email, u.auth \n                    FROM {$CFG->prefix}user u\n                        LEFT JOIN {$temptable} e ON u.username = e.username\n                    WHERE u.auth='ldap'\n                        AND u.deleted=0\n                        AND e.username IS NULL";
         $remove_users = get_records_sql($sql);
         if (!empty($remove_users)) {
             print "User entries to remove: " . count($remove_users) . "\n";
             foreach ($remove_users as $user) {
                 if ($this->config->removeuser == 2) {
                     if (delete_user($user)) {
                         echo "\t";
                         print_string('auth_dbdeleteuser', 'auth', array($user->username, $user->id));
                         echo "\n";
                     } else {
                         echo "\t";
                         print_string('auth_dbdeleteusererror', 'auth', $user->username);
                         echo "\n";
                     }
                 } else {
                     if ($this->config->removeuser == 1) {
                         $updateuser = new object();
                         $updateuser->id = $user->id;
                         $updateuser->auth = 'nologin';
                         if (update_record('user', $updateuser)) {
                             echo "\t";
                             print_string('auth_dbsuspenduser', 'auth', array($user->username, $user->id));
                             echo "\n";
                         } else {
                             echo "\t";
                             print_string('auth_dbsuspendusererror', 'auth', $user->username);
                             echo "\n";
                         }
                     }
                 }
             }
         } else {
             print "No user entries to be removed\n";
         }
         unset($remove_users);
         // free mem!
     }
     /// Revive suspended users
     if (!empty($this->config->removeuser) and $this->config->removeuser == 1) {
         $sql = "SELECT u.id, u.username\n                    FROM {$temptable} e, {$CFG->prefix}user u\n                    WHERE e.username=u.username\n                        AND u.auth='nologin'";
         $revive_users = get_records_sql($sql);
         if (!empty($revive_users)) {
             print "User entries to be revived: " . count($revive_users) . "\n";
             begin_sql();
             foreach ($revive_users as $user) {
                 $updateuser = new object();
                 $updateuser->id = $user->id;
                 $updateuser->auth = 'ldap';
                 if (update_record('user', $updateuser)) {
                     echo "\t";
                     print_string('auth_dbreviveser', 'auth', array($user->username, $user->id));
                     echo "\n";
                 } else {
                     echo "\t";
                     print_string('auth_dbreviveusererror', 'auth', $user->username);
                     echo "\n";
                 }
             }
             commit_sql();
         } else {
             print "No user entries to be revived\n";
         }
         unset($revive_users);
     }
     /// User Updates - time-consuming (optional)
     if ($do_updates) {
         // narrow down what fields we need to update
         $all_keys = array_keys(get_object_vars($this->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($this->config->{'field_map_' . $match[1]}) and $this->config->{$match[0]} === 'onlogin') {
                     array_push($updatekeys, $match[1]);
                     // the actual key name
                 }
             }
         }
         // print_r($all_keys); print_r($updatekeys);
         unset($all_keys);
         unset($key);
     } else {
         print "No updates to be done\n";
     }
     if ($do_updates and !empty($updatekeys)) {
         // run updates only if relevant
         $users = get_records_sql("SELECT u.username, u.id\n                                      FROM {$CFG->prefix}user u\n                                      WHERE u.deleted=0 AND u.auth='ldap'");
         if (!empty($users)) {
             print "User entries to update: " . count($users) . "\n";
             $sitecontext = get_context_instance(CONTEXT_SYSTEM);
             if (!empty($this->config->creators) and !empty($this->config->memberattribute) and $roles = get_roles_with_capability('moodle/legacy:coursecreator', CAP_ALLOW)) {
                 $creatorrole = array_shift($roles);
                 // We can only use one, let's use the first one
             } else {
                 $creatorrole = false;
             }
             begin_sql();
             $xcount = 0;
             $maxxcount = 100;
             foreach ($users as $user) {
                 echo "\t";
                 print_string('auth_dbupdatinguser', 'auth', array($user->username, $user->id));
                 if (!$this->update_user_record(addslashes($user->username), $updatekeys)) {
                     echo " - " . get_string('skipped');
                 }
                 echo "\n";
                 $xcount++;
                 // update course creators if needed
                 if ($creatorrole !== false) {
                     if ($this->iscreator($user->username)) {
                         role_assign($creatorrole->id, $user->id, 0, $sitecontext->id, 0, 0, 0, 'ldap');
                     } else {
                         role_unassign($creatorrole->id, $user->id, 0, $sitecontext->id, 'ldap');
                     }
                 }
                 if ($xcount++ > $maxxcount) {
                     commit_sql();
                     begin_sql();
                     $xcount = 0;
                 }
             }
             commit_sql();
             unset($users);
             // free mem
         }
     } else {
         // end do updates
         print "No updates to be done\n";
     }
     /// User Additions
     // find users missing in DB that are in LDAP
     // note that get_records_sql wants at least 2 fields returned,
     // 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
     $sql = "SELECT e.username, e.username\n                FROM {$temptable} e LEFT JOIN {$CFG->prefix}user u ON e.username = u.username\n                WHERE u.id IS NULL";
     $add_users = get_records_sql($sql);
     // get rid of the fat
     if (!empty($add_users)) {
         print "User entries to add: " . count($add_users) . "\n";
         $sitecontext = get_context_instance(CONTEXT_SYSTEM);
         if (!empty($this->config->creators) and !empty($this->config->memberattribute) and $roles = get_roles_with_capability('moodle/legacy:coursecreator', CAP_ALLOW)) {
             $creatorrole = array_shift($roles);
             // We can only use one, let's use the first one
         } else {
             $creatorrole = false;
         }
         begin_sql();
         foreach ($add_users as $user) {
             $user = $this->get_userinfo_asobj(addslashes($user->username));
             // prep a few params
             $user->modified = time();
             $user->confirmed = 1;
             $user->auth = 'ldap';
             $user->mnethostid = $CFG->mnet_localhost_id;
             // 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(moodle_strtolower($user->username));
             if (empty($user->lang)) {
                 $user->lang = $CFG->lang;
             }
             $user = addslashes_recursive($user);
             if ($id = insert_record('user', $user)) {
                 echo "\t";
                 print_string('auth_dbinsertuser', 'auth', array(stripslashes($user->username), $id));
                 echo "\n";
                 $userobj = $this->update_user_record($user->username);
                 if (!empty($this->config->forcechangepassword)) {
                     set_user_preference('auth_forcepasswordchange', 1, $userobj->id);
                 }
             } else {
                 echo "\t";
                 print_string('auth_dbinsertusererror', 'auth', $user->username);
                 echo "\n";
             }
             // add course creators if needed
             if ($creatorrole !== false and $this->iscreator(stripslashes($user->username))) {
                 role_assign($creatorrole->id, $user->id, 0, $sitecontext->id, 0, 0, 0, 'ldap');
             }
         }
         commit_sql();
         unset($add_users);
         // free mem
     } else {
         print "No users to be added\n";
     }
     $this->ldap_close();
     return true;
 }
Example #2
0
/**
 * This function will rename the index in the table passed as arguments
 * Before renaming the index, the function will check it exists
 * Experimental. Shouldn't be used at all!
 *
 * @uses $CFG, $db
 * @param XMLDBTable table object (just the name is mandatory)
 * @param XMLDBIndex index object (full specs are required)
 * @param string new name of the index
 * @param boolean continue to specify if must continue on error (true) or stop (false)
 * @param boolean feedback to specify to show status info (true) or not (false)
 * @return boolean true on success, false on error
 */
function rename_index($table, $index, $newname, $continue = true, $feedback = true)
{
    global $CFG, $db;
    debugging('rename_index() is one experimental feature. You must not use it in production!', DEBUG_DEVELOPER);
    $status = true;
    if (strtolower(get_class($table)) != 'xmldbtable') {
        return false;
    }
    if (strtolower(get_class($index)) != 'xmldbindex') {
        return false;
    }
    /// Check index exists
    if (!index_exists($table, $index)) {
        debugging('Index ' . $table->getName() . '->' . $index->getName() . ' does not exist. Rename skipped', DEBUG_DEVELOPER);
        return true;
        //Index doesn't exist, nothing to do
    }
    /// Check newname isn't empty
    if (!$newname) {
        debugging('New name for index ' . $table->getName() . '->' . $index->getName() . ' is empty! Rename skipped', DEBUG_DEVELOPER);
        return true;
        //Index doesn't exist, nothing to do
    }
    if (!($sqlarr = $table->getRenameIndexSQL($CFG->dbtype, $CFG->prefix, $index, $newname, false))) {
        debugging('Some DBs do not support index renaming (MySQL). Rename skipped', DEBUG_DEVELOPER);
        return true;
        //Empty array = nothing to do = no error
    }
    return execute_sql_arr($sqlarr, $continue, $feedback);
}
Example #3
0
 /**
  * syncronizes user fron external db to moodle user table
  *
  * Sync is now using username attribute.
  *
  * Syncing users removes or suspends users that dont exists anymore in external db.
  * Creates new users and updates coursecreator status of users.
  *
  * @param int $bulk_insert_records will insert $bulkinsert_records per insert statement
  *                         valid only with $unsafe. increase to a couple thousand for
  *                         blinding fast inserts -- but test it: you may hit mysqld's
  *                         max_allowed_packet limit.
  * @param bool $do_updates will do pull in data updates from ldap if relevant
  */
 function sync_users($bulk_insert_records = 1000, $do_updates = true)
 {
     global $CFG;
     // Set Debugging Mode
     ini_set('log_errors', true);
     $origdebug = $CFG->debug;
     $CFG->debug = DEBUG_DEVELOPER;
     // DEBUG_ALL, DEBUG_MINIMAL, DEBUG_DEVELOPER
     $CFG->debugdisplay = true;
     error_reporting($CFG->debug);
     // Debug All Errors Only
     $CFG->dblogerror = true;
     @set_time_limit(7200);
     // 2 hours should be enough
     @raise_memory_limit('512M');
     $textlib = textlib_get_instance();
     $droptablesql = array();
     /// sql commands to drop the table (because session scope could be a problem for
     /// some persistent drivers like ODBTP (mssql) or if this function is invoked
     /// from within a PHP application using persistent connections
     $temptable = $CFG->prefix . 'extuser';
     $createtemptablesql = '';
     // configure a temp table
     print "Configuring temp table\n";
     switch (strtolower($CFG->dbfamily)) {
         case 'mysql':
             $droptablesql[] = 'DROP TEMPORARY TABLE IF EXISTS ' . $temptable;
             // sql command to drop the table (because session scope could be a problem)
             $createtemptablesql = 'CREATE TEMPORARY TABLE ' . $temptable . ' (username VARCHAR(100), mnethostid BIGINT(10), PRIMARY KEY (username, mnethostid)) TYPE=MyISAM COLLATE utf8_general_ci';
             break;
         case 'postgres':
             $droptablesql[] = 'DROP TABLE ' . $temptable;
             // sql command to drop the table (because session scope could be a problem)
             $bulk_insert_records = 1;
             // no support for multiple sets of values
             $createtemptablesql = 'CREATE TEMPORARY TABLE ' . $temptable . '  (username VARCHAR(100), mnethostid INT(10), PRIMARY KEY (username, mnethostid)) COLLATE utf8_general_ci';
             break;
         case 'mssql':
             $temptable = '#' . $temptable;
             /// MSSQL temp tables begin with #
             $droptablesql[] = 'DROP TABLE ' . $temptable;
             // sql command to drop the table (because session scope could be a problem)
             $bulk_insert_records = 1;
             // no support for multiple sets of values
             $createtemptablesql = 'CREATE TABLE ' . $temptable . ' (username VARCHAR(100), mnethostid INT(10), PRIMARY KEY (username, mnethostid)) COLLATE utf8_general_ci';
             break;
         case 'oracle':
             $droptablesql[] = 'TRUNCATE TABLE ' . $temptable;
             // oracle requires truncate before being able to drop a temp table
             $droptablesql[] = 'DROP TABLE ' . $temptable;
             // sql command to drop the table (because session scope could be a problem)
             $bulk_insert_records = 1;
             // no support for multiple sets of values
             $createtemptablesql = 'CREATE GLOBAL TEMPORARY TABLE ' . $temptable . ' (username VARCHAR(100), mnethostid INT(10), PRIMARY KEY (username, mnethostid)) ON COMMIT PRESERVE ROWS';
             break;
     }
     print "Staring LDAP URL SSO Cron Sync - " . date(DATE_RFC822) . "\n";
     execute_sql_arr($droptablesql, true, false);
     /// Drop temp table to avoid persistence problems later
     echo "Creating temp table {$temptable}\n";
     if (!execute_sql($createtemptablesql, false)) {
         print "Failed to create temporary users table - aborting\n";
         exit;
     }
     print "Connecting to ldap...\n";
     $ldapconnection = $this->ldap_connect();
     if (!$ldapconnection) {
         @ldap_close($ldapconnection);
         print get_string('auth_ldap_noconnect', 'auth', $this->config->host_url);
         exit;
     }
     ////
     //// get user's list from ldap to sql in a scalable fashion
     ////
     // prepare some data we'll need
     $filter = '(&(' . $this->config->user_attribute . '=*) (' . $this->config->objectclass . '))';
     echo "filter: " . $filter . "\n";
     $contexts = explode(";", $this->config->contexts);
     if (!empty($this->config->create_context)) {
         array_push($contexts, $this->config->create_context);
     }
     $fresult = array();
     foreach ($contexts as $context) {
         $context = trim($context);
         if (empty($context)) {
             continue;
         }
         echo "Searching Context: " . $context . "\n";
         begin_sql();
         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 ($entry = ldap_first_entry($ldapconnection, $ldap_result)) {
             do {
                 $value = ldap_get_values_len($ldapconnection, $entry, $this->config->user_attribute);
                 $value = $textlib->convert($value[0], $this->config->ldapencoding, 'utf-8');
                 // usernames are __always__ lowercase.
                 if (strpos($value[0], '$') && $this->config->user_type == 'ad') {
                     // Eliminiate AD Service Accounts
                     continue;
                 }
                 // Skip AD Service Account Entries
                 array_push($fresult, moodle_strtolower($value));
                 if (count($fresult) >= $bulk_insert_records) {
                     $this->ldap_bulk_insert($fresult, $temptable);
                     $fresult = array();
                 }
             } while ($entry = ldap_next_entry($ldapconnection, $entry));
         }
         unset($ldap_result);
         // free mem
         // insert any remaining users and release mem
         if (count($fresult)) {
             $this->ldap_bulk_insert($fresult, $temptable);
             $fresult = array();
         }
         commit_sql();
     }
     /// 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 = get_record_sql('SELECT COUNT(username) AS count, 1 FROM ' . $temptable);
     $count = $count->{'count'};
     if ($count < 1) {
         print "Did not get any users from LDAP -- error? -- exiting\n";
         exit;
     } else {
         print "Got {$count} records from LDAP\n\n";
     }
     /// 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 (!empty($this->config->removeuser)) {
         $sql = "SELECT u.id, u.username, u.email, u.auth\r\n                    FROM {$CFG->prefix}user u\r\n                       LEFT JOIN {$temptable} e ON u.username = e.username\r\n                       AND u.mnethostid = e.mnethostid\r\n                    WHERE u.auth='ldapsso'\r\n                        AND u.deleted=0\r\n                        AND e.username IS NULL";
         $remove_users = get_records_sql($sql);
         if (!empty($remove_users)) {
             print "User entries to remove: " . count($remove_users) . "\n";
             foreach ($remove_users as $user) {
                 if ($this->config->removeuser == 2) {
                     if (delete_user($user)) {
                         echo "\t";
                         print_string('auth_dbdeleteuser', 'auth', array($user->username, $user->id));
                         echo "\n";
                     } else {
                         echo "\t";
                         print_string('auth_dbdeleteusererror', 'auth', $user->username);
                         echo "\n";
                     }
                 } else {
                     if ($this->config->removeuser == 1) {
                         $updateuser = new object();
                         $updateuser->id = $user->id;
                         $updateuser->auth = 'nologin';
                         if (update_record('user', $updateuser)) {
                             echo "\t";
                             print_string('auth_dbsuspenduser', 'auth', array($user->username, $user->id));
                             echo "\n";
                         } else {
                             echo "\t";
                             print_string('auth_dbsuspendusererror', 'auth', $user->username);
                             echo "\n";
                         }
                     }
                 }
             }
         } else {
             print "No user entries to be removed\n";
         }
         unset($remove_users);
         // free mem!
     }
     /// Revive suspended users
     if (!empty($this->config->removeuser) and $this->config->removeuser == 1) {
         $sql = "SELECT u.id, u.username\r\n                    FROM {$temptable} e, {$CFG->prefix}user u\r\n                    WHERE e.username=u.username\r\n                    AND e.mnethostid=u.mnethostid\r\n                    AND u.auth='nologin'";
         $revive_users = get_records_sql($sql);
         if (!empty($revive_users)) {
             print "User entries to be revived: " . count($revive_users) . "\n";
             begin_sql();
             foreach ($revive_users as $user) {
                 $updateuser = new object();
                 $updateuser->id = $user->id;
                 $updateuser->auth = 'ldap';
                 if (update_record('user', $updateuser)) {
                     echo "\t";
                     print_string('auth_ldap_sso_dbreviveuser', 'auth_ldapsso', array($user->username, $user->id));
                     echo "\n";
                 } else {
                     echo "\t";
                     print_string('auth_ldap_sso_dbreviveusererror', 'auth_ldapsso', $user->username);
                     echo "\n";
                 }
             }
             commit_sql();
         } else {
             print "No user entries to be revived\n";
         }
         unset($revive_users);
     }
     /// User Updates - time-consuming (optional)
     if ($do_updates) {
         // narrow down what fields we need to update
         $all_keys = array_keys(get_object_vars($this->config));
         $updatekeys = array();
         // $updatekeys = array('firstname','lastname','idnumber');
         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($this->config->{'field_map_' . $match[1]}) and $this->config->{$match[0]} === 'onlogin') {
                     array_push($updatekeys, $match[1]);
                     // the actual key name
                 }
             }
         }
         // print_r($all_keys); print_r($updatekeys);
         unset($all_keys);
         unset($key);
     } else {
         print "No updates to be done\n";
     }
     if ($do_updates and !empty($updatekeys)) {
         // run updates only if relevant
         $users = get_records_sql("SELECT u.username, u.id\r\n                                      FROM {$CFG->prefix}user u\r\n                                      WHERE u.deleted=0 AND u.auth='ldapsso'");
         if (!empty($users)) {
             print "User entries to update: " . count($users) . "\n";
             $sitecontext = get_context_instance(CONTEXT_SYSTEM);
             if (!empty($this->config->creators) and !empty($this->config->memberattribute) and $roles = get_roles_with_capability('moodle/legacy:coursecreator', CAP_ALLOW)) {
                 $creatorrole = array_shift($roles);
                 // We can only use one, let's use the first one
             } else {
                 $creatorrole = false;
             }
             begin_sql();
             $xcount = 0;
             $maxxcount = 100;
             foreach ($users as $user) {
                 echo "\t";
                 print_string('auth_dbupdatinguser', 'auth', array($user->username, $user->id));
                 if (!$this->update_user_record(addslashes($user->username), $updatekeys)) {
                     echo " - " . get_string('skipped');
                 }
                 echo "\n";
                 $xcount++;
                 // update course creators if needed
                 if ($creatorrole !== false) {
                     if ($this->iscreator($user->username)) {
                         role_assign($creatorrole->id, $user->id, 0, $sitecontext->id, 0, 0, 0, 'ldap');
                     } else {
                         role_unassign($creatorrole->id, $user->id, 0, $sitecontext->id, 'ldap');
                     }
                 }
                 if ($xcount++ > $maxxcount) {
                     commit_sql();
                     begin_sql();
                     $xcount = 0;
                 }
             }
             commit_sql();
             unset($users);
             // free mem
         }
     }
     /// Switch users that exist in extauth and Moodle that are currently using alternate login
     print "Validating user authentication method for LDAP SSO users.\n";
     $sql = "SELECT u.id, u.auth, e.username\r\n              FROM {$temptable} e JOIN {$CFG->prefix}user u ON e.username = u.username\r\n              AND e.mnethostid = u.mnethostid\r\n              WHERE u.id IS NOT NULL AND u.auth!='ldapsso'";
     $mauth_users = get_records_sql($sql);
     if (!empty($mauth_users)) {
         print "Users entries to update with ldap auth: " . count($mauth_users) . "\n";
         begin_sql();
         foreach ($mauth_users as $user) {
             echo "\t";
             print_string('auth_dbupdatinguser', 'auth', array($user->username, $user->id));
             echo "\n";
             // get the current user record
             $user = get_record('user', 'username', addslashes($user->username), 'auth', $user->auth);
             if (!empty($user)) {
                 set_field('user', 'auth', 'ldapsso', 'id', $user->id);
             } else {
                 echo "\t";
                 print 'Cannot switch $user->auth user : '******' to LDAP auth!';
                 echo "\n";
             }
         }
         commit_sql();
         unset($mauth_users);
         // free mem
     } else {
         print "No users found to be updated!\n";
     }
     /// User Additions
     // find users missing in DB that are in LDAP
     // note that get_records_sql wants at least 2 fields returned,
     // 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
     print "Checking for User Additions\n";
     $sql = "SELECT e.username AS user, e.username\r\n                FROM {$temptable} e LEFT JOIN {$CFG->prefix}user u ON e.username = u.username\r\n                AND e.mnethostid = u.mnethostid\r\n                WHERE u.id IS NULL";
     $add_users = get_records_sql($sql);
     // get rid of the fat
     if (!empty($add_users)) {
         print "User entries to add: " . count($add_users) . "\n";
         $sitecontext = get_context_instance(CONTEXT_SYSTEM);
         if (!empty($this->config->creators) and !empty($this->config->memberattribute) and $roles = get_roles_with_capability('moodle/legacy:coursecreator', CAP_ALLOW)) {
             $creatorrole = array_shift($roles);
             // We can only use one, let's use the first one
         } else {
             $creatorrole = false;
         }
         begin_sql();
         foreach ($add_users as $user) {
             if (!isset($user->username) || empty($user->username)) {
                 continue;
             }
             $username = $user->username;
             // Allow . (dot char) in name
             $user = $this->get_userinfo_asobj(addslashes($user->username));
             // prep a few params
             $user->modified = time();
             $user->confirmed = 1;
             $user->auth = 'ldapsso';
             $user->username = $username;
             $user->mnethostid = $CFG->mnet_localhost_id;
             if (empty($user->lang)) {
                 $user->lang = $CFG->lang;
             }
             $user = addslashes_recursive($user);
             if ($id = insert_record('user', $user)) {
                 echo "\t";
                 print_string('auth_dbinsertuser', 'auth', array(stripslashes($user->username), $id));
                 echo "\n";
                 $userobj = $this->update_user_record($user->username);
                 if (!empty($this->config->forcechangepassword)) {
                     set_user_preference('auth_forcepasswordchange', 1, $userobj->id);
                 }
             } else {
                 echo "\t";
                 print_string('auth_dbinsertusererror', 'auth', $user->username);
                 echo "\n";
             }
             // add course creators if needed
             if ($creatorrole !== false and $this->iscreator(stripslashes($user->username))) {
                 role_assign($creatorrole->id, $user->id, 0, $sitecontext->id, 0, 0, 0, 'ldap');
             }
         }
         commit_sql();
         unset($add_users);
         // free mem
     } else {
         print "No users to be added\n";
     }
     // Return to original debugging level
     $CFG->debug = $origdebug;
     error_reporting($CFG->debug);
     $CFG->dblogerror = false;
     @ldap_close($ldapconnection);
     print "LDAP URL SSO Cron Sync completed - " . date(DATE_RFC822) . "\n";
     return true;
 }