From 4029d090970fb44a5d09590407b4fa0d3089cd03 Mon Sep 17 00:00:00 2001 From: Eloy Lafuente Date: Wed, 30 Dec 2009 17:17:09 +0000 Subject: [PATCH] MDL-16658 restore - new restore:createuser cap + pre-check users before restoring --- backup/restorelib.php | 112 +++++++++++++++------------------------- config-dist.php | 15 ++++++ lang/en_utf8/moodle.php | 4 ++ lang/en_utf8/role.php | 1 + lib/db/access.php | 11 ++++ version.php | 2 +- 6 files changed, 75 insertions(+), 70 deletions(-) diff --git a/backup/restorelib.php b/backup/restorelib.php index 860ff29275e..5ed1cf237ce 100644 --- a/backup/restorelib.php +++ b/backup/restorelib.php @@ -746,7 +746,7 @@ define('RESTORE_GROUPS_GROUPINGS', 3); } // Handle checks from same site backups - if (backup_is_same_site($restore)) { + if (backup_is_same_site($restore) && empty($CFG->forcedifferentsitecheckingusersonrestore)) { // 1A - If match by id and username and mnethost => ok, return target user if ($rec = get_record('user', 'id', $user->id, 'username', addslashes($user->username), 'mnethostid', $user->mnethostid)) { @@ -766,7 +766,7 @@ define('RESTORE_GROUPS_GROUPINGS', 3); AND mnethostid = $user->mnethostid AND deleted = 1 AND ( - username LIKE '$user->email.%' + username LIKE '".addslashes($user->email).".%' OR ( ".sql_isnotempty('user', 'email', false, false)." AND email = '".md5($user->username)."' @@ -787,7 +787,7 @@ define('RESTORE_GROUPS_GROUPINGS', 3); FROM {$CFG->prefix}user u WHERE id = $user->id AND mnethostid = $user->mnethostid - AND email = '$trimemail'")) { + AND email = '".addslashes($trimemail)."'")) { return $rec; // Matching user, deleted in backup file found, return it } } @@ -806,10 +806,10 @@ define('RESTORE_GROUPS_GROUPINGS', 3); // (email or non-zero firstaccess) => ok, return target user if ($rec = get_record_sql("SELECT * FROM {$CFG->prefix}user u - WHERE username = '$user->username' + WHERE username = '".addslashes($user->username)."' AND mnethostid = $user->mnethostid AND ( - email = '$user->email' + email = '".addslashes($user->email)."' OR ( firstaccess != 0 AND firstaccess = $user->firstaccess @@ -832,7 +832,7 @@ define('RESTORE_GROUPS_GROUPINGS', 3); AND ".sql_isnotempty('user', 'email', false, false)." AND email = '".md5($user->username)."' AND ( - username LIKE '$user->email.%' + username LIKE '".addslashes($user->email).".%' OR ( firstaccess != 0 AND firstaccess = $user->firstaccess @@ -849,7 +849,7 @@ define('RESTORE_GROUPS_GROUPINGS', 3); FROM {$CFG->prefix}user u WHERE mnethostid = $user->mnethostid AND deleted = 1 - AND username LIKE '$user->email.%' + AND username LIKE '".addslashes($user->email).".%' AND firstaccess != 0 AND firstaccess = $user->firstaccess")) { return $rec; // Matching user found, return it @@ -866,7 +866,7 @@ define('RESTORE_GROUPS_GROUPINGS', 3); if ($rec = get_record_sql("SELECT * FROM {$CFG->prefix}user u WHERE mnethostid = $user->mnethostid - AND email = '$trimemail' + AND email = '".addslashes($trimemail)."' AND firstaccess != 0 AND firstaccess = $user->firstaccess")) { return $rec; // Matching user, deleted in backup file found, return it @@ -876,10 +876,10 @@ define('RESTORE_GROUPS_GROUPINGS', 3); // 2D - If match by username and mnethost and not by (email or non-zero firstaccess) => conflict, return false if ($rec = get_record_sql("SELECT * FROM {$CFG->prefix}user u - WHERE username = '$user->username' + WHERE username = '".addslashes($user->username)."' AND mnethostid = $user->mnethostid AND NOT ( - email = '$user->email' + email = '".addslashes($user->email)."' OR ( firstaccess != 0 AND firstaccess = $user->firstaccess @@ -947,7 +947,7 @@ define('RESTORE_GROUPS_GROUPINGS', 3); $user = $rec->info; // Find the correct mnethostid for user before performing any further check - if (empty($user->mnethosturl) || $user->mnethosturl===$CFG->wwwroot) { + if (empty($user->mnethosturl) || $user->mnethosturl === $CFG->wwwroot) { $user->mnethostid = $CFG->mnet_localhost_id; } else { // fast url-to-id lookups @@ -962,13 +962,18 @@ define('RESTORE_GROUPS_GROUPINGS', 3); $usercheck = restore_check_user($restore, $user); if (is_object($usercheck)) { // No problem, we have found one user in DB to be mapped to + // Annotate it, for later process by restore_create_users(). Set new_id to mapping user->id + backup_putid($restore->backup_unique_code, 'user', $userid, $usercheck->id, $user); } else if ($usercheck === false) { // Found conflict, report it as problem $problems[] = get_string('restoreuserconflict', '', $user->username); $status = false; } else if ($usercheck === true) { // User needs to be created, check if we are able - if (!$cancreateuser) { // Cannot create, report as problem + if ($cancreateuser) { // Can create user, annotate it, for later process by restore_create_users(). Set new_id to 0 + backup_putid($restore->backup_unique_code, 'user', $userid, 0, $user); + + } else { // Cannot create user, report it as problem $problems[] = get_string('restorecannotcreateuser', '', $user->username); $status = false; @@ -2766,25 +2771,17 @@ define('RESTORE_GROUPS_GROUPINGS', 3); $authcache = array(); // Cache to get some bits from authentication plugins $status = true; - //Check it exists - if (!file_exists($xml_file)) { - $status = false; - } - //Get info from xml - if ($status) { - //info will contain the old_id of every user - //in backup_ids->info will be the real info (serialized) - $info = restore_read_xml_users($restore,$xml_file); - } - //Now, get evey user_id from $info and user data from $backup_ids - //and create the necessary db structures + // Users have already been checked by restore_precheck_users() so they are loaded + // in backup_ids table. They don't need to be loaded (parsed) from XML again. Also, note + // the same function has performed the needed modifications in the $user->mnethostid field + // so we don't need to do it again here at all. Just some checks. - if (!empty($info->users)) { + // Get users ids from backup_ids table + $userids = get_fieldset_select('backup_ids', 'old_id', "backup_code = $restore->backup_unique_code AND table_name = 'user'"); - /// Grab mnethosts keyed by wwwroot, to map to id - $mnethosts = get_records('mnet_host', '', '', - 'wwwroot', 'wwwroot, id'); + // Have users to process, proceed with them + if (!empty($userids)) { /// Get languages for quick search later $languages = get_list_of_languages(); @@ -2794,9 +2791,22 @@ define('RESTORE_GROUPS_GROUPINGS', 3); /// Init trailing messages $messages = array(); - foreach ($info->users as $userid) { - $rec = backup_getid($restore->backup_unique_code,"user",$userid); - $user = $rec->info; + foreach ($userids as $userid) { + // Defaults + $user_exists = false; // By default user does not exist + $newid = null; // By default, there is not newid + + // Get record from backup_ids + $useridsdbrec = backup_getid($restore->backup_unique_code, 'user', $userid); + + // Based in restore_precheck_users() calculations, if the user exists + // new_id must contain the id of the matching user + if (!empty($useridsdbrec->new_id)) { + $user_exists = true; + $newid = $useridsdbrec->new_id; + } + + $user = $useridsdbrec->info; foreach (array_keys(get_object_vars($user)) as $field) { if (!is_array($user->$field)) { $user->$field = backup_todb($user->$field); @@ -2844,31 +2854,12 @@ define('RESTORE_GROUPS_GROUPINGS', 3); //Has role teacher or student or needed $is_course_user = ($is_teacher or $is_student or $is_needed); - // in case we are restoring to same server, look for user by id and username - // it should return record always, but in sites rebuilt from scratch - // and being reconstructed using course backups - $user_data = false; - if (backup_is_same_site($restore)) { - $user_data = get_record('user', 'id', $user->id, 'username', addslashes($user->username)); - } - // Only try to perform mnethost/auth modifications if restoring to another server // or if, while restoring to same server, the user doesn't exists yet (rebuilt site) // // So existing user data in same server *won't be modified by restore anymore*, // under any circumpstance. If somehting is wrong with existing data, it's server fault. - if (!backup_is_same_site($restore) || (backup_is_same_site($restore) && !$user_data)) { - //Calculate mnethostid - if (empty($user->mnethosturl) || $user->mnethosturl===$CFG->wwwroot) { - $user->mnethostid = $CFG->mnet_localhost_id; - } else { - // fast url-to-id lookups - if (isset($mnethosts[$user->mnethosturl])) { - $user->mnethostid = $mnethosts[$user->mnethosturl]->id; - } else { - $user->mnethostid = $CFG->mnet_localhost_id; - } - } + if (!backup_is_same_site($restore) || (backup_is_same_site($restore) && !$user_exists)) { //Arriving here, any user with mnet auth and using $CFG->mnet_localhost_id is wrong //as own server cannot be accesed over mnet. Change auth to manual and inform about the switch if ($user->auth == 'mnet' && $user->mnethostid == $CFG->mnet_localhost_id) { @@ -2887,21 +2878,6 @@ define('RESTORE_GROUPS_GROUPINGS', 3); } unset($user->mnethosturl); - //To store user->id along all the iteration - $newid=null; - //check if it exists (by username) and get its id - $user_exists = true; - if (!backup_is_same_site($restore) || !$user_data) { /// Restoring to another server, or rebuilding site (failed id& - /// login search above), look for existing user based on fields - $user_data = get_record('user', 'username', addslashes($user->username), 'mnethostid', $user->mnethostid); - } - - if (!$user_data) { - $user_exists = false; - } else { - $newid = $user_data->id; - } - //Flags to see what parts are we going to restore $create_user = true; $create_roles = true; @@ -3182,7 +3158,7 @@ define('RESTORE_GROUPS_GROUPINGS', 3); } backup_flush(300); } - } /// End of loop over all the users loaded from xml + } /// End of loop over all the users loaded from backup_ids table /// Inform about all the messages geerated while restoring users if (!defined('RESTORE_SILENTLY')) { @@ -8264,7 +8240,6 @@ define('RESTORE_GROUPS_GROUPINGS', 3); // Precheck the users section, detecting various situations that can lead to problems, so // we stop restore before performing any further action - /* if (!defined('RESTORE_SILENTLY')) { echo '
  • '.get_string('restoreusersprecheck').'
  • '; } @@ -8278,7 +8253,6 @@ define('RESTORE_GROUPS_GROUPINGS', 3); } return false; } - */ //If we've selected to restore into new course //create it (course) diff --git a/config-dist.php b/config-dist.php index 71dbc3c5c19..170ec518094 100644 --- a/config-dist.php +++ b/config-dist.php @@ -176,6 +176,21 @@ $CFG->admin = 'admin'; // shared out from your site/institution! // $CFG->includeuserpasswordsinbackup = true; // +// Completely disable user creation on restore, bypassing any right granted +// via roles/capabilities. By enabling this setting any restore process +// requiring users to be created will be stopped before any further action. +// $CFG->disableusercreationonrestore = true; +// +// Modify the restore process in order to force the "user checks" to assume +// that the backup was originated in a different site so detection of matching +// users is performed with some different (more "relaxed") rules. *Only* useful +// if the backup file has been generated with Moodle < 1.9.4 and the site has +// been rebuilt from scratch using backup files (not the best way, btw). If you +// are getting user conflicts on restore, try to restore the backup to a different +// site, backup it again and then restore on target server instead of enabling this +// setting permanently! +// $CFG->forcedifferentsitecheckingusersonrestore = true; +// // Prevent stats processing and hide the GUI // $CFG->disablestatsprocessing = true; // diff --git a/lang/en_utf8/moodle.php b/lang/en_utf8/moodle.php index d5dd1b7f7c6..0e234262eac 100644 --- a/lang/en_utf8/moodle.php +++ b/lang/en_utf8/moodle.php @@ -1306,6 +1306,7 @@ $string['resource'] = 'Resource'; $string['resources'] = 'Resources'; $string['restore'] = 'Restore'; $string['restorecancelled'] = 'Restore canceled'; +$string['restorecannotcreateuser'] = 'restore needs to create user \'$a\' from backup file and you are not allowed to do that'; $string['restorecoursenow'] = 'Restore this course now!'; $string['restoredaccount'] = 'Restored account'; $string['restoredaccountinfo'] = 'This account was imported from another server and the password has been lost. To set a new password by email, please click \"Continue\"'; @@ -1313,7 +1314,10 @@ $string['restorefinished'] = 'Restore completed successfully'; $string['restoreto'] = 'Restore to'; $string['restoretositeadding'] = 'Warning: You are about to restore to the site front page, adding data to it!'; $string['restoretositedeleting'] = 'Warning: You are about to restore to the site front page, deleting data from it first!'; +$string['restoreuserconflict'] = 'trying to restore user \'$a\' from backup file will cause conflict'; $string['restoreuserinfofailed'] = 'The restore process has stopped because you don\'t have permission to restore user data.'; +$string['restoreusersprecheck'] = 'Checking user information'; +$string['restoreusersprecheckerror'] = 'Some problems were detected checking user information'; $string['restricted'] = 'Restricted'; $string['restrictmodules'] = 'Restrict activity modules?'; $string['returningtosite'] = 'Returning to this web site?'; diff --git a/lang/en_utf8/role.php b/lang/en_utf8/role.php index 8abb6b20eea..71be2fd306b 100644 --- a/lang/en_utf8/role.php +++ b/lang/en_utf8/role.php @@ -130,6 +130,7 @@ $string['resetrolenolegacy'] = 'Clear permissions'; $string['resetrolesure'] = 'Are you sure that you want to reset role \"$a->name ($a->shortname)\" to defaults?

    The defaults are taken from the selected legacy capability ($a->legacytype).'; $string['resetrolesurenolegacy'] = 'Are you sure that you want to clear all permissions defined in this role \"$a->name ($a->shortname)\"?'; $string['restore:rolldates'] = 'Allowed to roll activity configuration dates on restore'; +$string['restore:createuser'] = 'Create users on restore'; $string['restore:userinfo'] = 'Restore user data'; $string['risks'] = 'Risks'; $string['role:assign'] = 'Assign roles to users'; diff --git a/lib/db/access.php b/lib/db/access.php index 2d904be4aed..43285e3fa82 100644 --- a/lib/db/access.php +++ b/lib/db/access.php @@ -191,6 +191,17 @@ $moodle_capabilities = array( ) ), + 'moodle/restore:createuser' => array( + + 'riskbitmask' => RISK_SPAM | RISK_PERSONAL, + + 'captype' => 'write', + 'contextlevel' => CONTEXT_SYSTEM, + 'legacy' => array( + 'admin' => CAP_ALLOW + ) + ), + 'moodle/restore:userinfo' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS | RISK_CONFIG, diff --git a/version.php b/version.php index 261493a2435..fbf05070c40 100644 --- a/version.php +++ b/version.php @@ -6,7 +6,7 @@ // This is compared against the values stored in the database to determine // whether upgrades should be performed (see lib/db/*.php) - $version = 2007101570; // YYYYMMDD = date of the 1.9 branch (don't change) + $version = 2007101571; // YYYYMMDD = date of the 1.9 branch (don't change) // X = release number 1.9.[0,1,2,3,4,5...] // Y.YY = micro-increments between releases