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