 includes/common.inc                           |    9 +---
 modules/simpletest/drupal_web_test_case.php   |    7 +--
 modules/simpletest/tests/common.test          |   10 +---
 modules/simpletest/tests/file.test            |   11 ++---
 modules/simpletest/tests/upgrade/upgrade.test |   10 ++--
 modules/user/user.module                      |   59 +++++++++++++++++++++++
 modules/user/user.test                        |   64 +++++++++++++++++++++++++
 7 files changed, 138 insertions(+), 32 deletions(-)

diff --git includes/common.inc includes/common.inc
index 405c98a..11567f0 100644
--- includes/common.inc
+++ includes/common.inc
@@ -4583,13 +4583,9 @@ function drupal_cron_run() {
   // Allow execution to continue even if the request gets canceled.
   @ignore_user_abort(TRUE);
 
-  // Prevent session information from being saved while cron is running.
-  drupal_save_session(FALSE);
-
   // Force the current user to anonymous to ensure consistent permissions on
   // cron runs.
-  $original_user = $GLOBALS['user'];
-  $GLOBALS['user'] = drupal_anonymous_user();
+  user_impersonate_user(drupal_anonymous_user());
 
   // Try to allocate enough time to run all the hook_cron implementations.
   drupal_set_time_limit(240);
@@ -4652,8 +4648,7 @@ function drupal_cron_run() {
     }
   }
   // Restore the user.
-  $GLOBALS['user'] = $original_user;
-  drupal_save_session(TRUE);
+  user_revert_user();
 
   return $return;
 }
diff --git modules/simpletest/drupal_web_test_case.php modules/simpletest/drupal_web_test_case.php
index ed29d29..1ed3263 100644
--- modules/simpletest/drupal_web_test_case.php
+++ modules/simpletest/drupal_web_test_case.php
@@ -1234,9 +1234,7 @@ class DrupalWebTestCase extends DrupalTestCase {
     drupal_cron_run();
 
     // Log in with a clean $user.
-    $this->originalUser = $user;
-    drupal_save_session(FALSE);
-    $user = user_load(1);
+    user_impersonate_user(user_load(1));
 
     // Restore necessary variables.
     variable_set('install_task', 'done');
@@ -1319,8 +1317,7 @@ class DrupalWebTestCase extends DrupalTestCase {
     $callbacks = $this->originalShutdownCallbacks;
 
     // Return the user to the original one.
-    $user = $this->originalUser;
-    drupal_save_session(TRUE);
+    user_revert_user();
 
     // Ensure that internal logged in variable and cURL options are reset.
     $this->loggedInUser = FALSE;
diff --git modules/simpletest/tests/common.test modules/simpletest/tests/common.test
index 3b14e9f..bd0a6b9 100644
--- modules/simpletest/tests/common.test
+++ modules/simpletest/tests/common.test
@@ -1819,11 +1819,8 @@ class FormatDateUnitTest extends DrupalWebTestCase {
     $edit = array('language' => self::LANGCODE, 'mail' => $test_user->mail, 'timezone' => 'America/Los_Angeles');
     $this->drupalPost('user/' . $test_user->uid . '/edit', $edit, t('Save'));
 
-    // Disable session saving as we are about to modify the global $user.
-    drupal_save_session(FALSE);
-    // Save the original user and language and then replace it with the test user and language.
-    $real_user = $user;
-    $user = user_load($test_user->uid, TRUE);
+    // Switch to test user.
+    user_impersonate_user(user_load($test_user->uid));
     $real_language = $language->language;
     $language->language = $user->language;
     // Simulate a Drupal bootstrap with the logged-in user.
@@ -1838,11 +1835,10 @@ class FormatDateUnitTest extends DrupalWebTestCase {
     $this->assertIdentical(format_date($timestamp), '25. marzo 2007 - 17:00', t('Test default date format.'));
 
     // Restore the original user and language, and enable session saving.
-    $user = $real_user;
+    user_revert_user();
     $language->language = $real_language;
     // Restore default time zone.
     date_default_timezone_set(drupal_get_user_timezone());
-    drupal_save_session(TRUE);
   }
 }
 
diff --git modules/simpletest/tests/file.test modules/simpletest/tests/file.test
index 192674f..ca3a1a1 100644
--- modules/simpletest/tests/file.test
+++ modules/simpletest/tests/file.test
@@ -462,20 +462,18 @@ class FileValidatorTest extends DrupalWebTestCase {
    * Test file_validate_size().
    */
   function testFileValidateSize() {
-    global $user;
-    $original_user = $user;
-    drupal_save_session(FALSE);
-
     // Run these test as uid = 1.
-    $user = user_load(1);
+    user_impersonate_user(user_load(1));
 
     $file = new stdClass();
     $file->filesize = 999999;
     $errors = file_validate_size($file, 1, 1);
     $this->assertEqual(count($errors), 0, t('No size limits enforced on uid=1.'), 'File');
+    user_revert_user();
 
     // Run these tests as a regular user.
     $user = $this->drupalCreateUser();
+    user_impersonate_user($user);
 
     // Create a file with a size of 1000 bytes, and quotas of only 1 byte.
     $file = new stdClass();
@@ -489,8 +487,7 @@ class FileValidatorTest extends DrupalWebTestCase {
     $errors = file_validate_size($file, 1, 1);
     $this->assertEqual(count($errors), 2, t('Errors for both the file and their limit.'), 'File');
 
-    $user = $original_user;
-    drupal_save_session(TRUE);
+    user_revert_user();
   }
 }
 
diff --git modules/simpletest/tests/upgrade/upgrade.test modules/simpletest/tests/upgrade/upgrade.test
index 7f2ddd2..1442cfc 100644
--- modules/simpletest/tests/upgrade/upgrade.test
+++ modules/simpletest/tests/upgrade/upgrade.test
@@ -90,10 +90,9 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
 
     $this->pass('Finished loading the dump.');
 
-    // Load user 1.
-    $this->originalUser = $user;
-    drupal_save_session(FALSE);
-    $user = db_query('SELECT * FROM {users} WHERE uid = :uid', array(':uid' => 1))->fetchObject();
+    // A direct query is used instead of user_load() because the latter would
+    // trigger hook_entity_info(), which is not available yet.
+    user_impersonate_user(db_query('SELECT * FROM {users} WHERE uid = :uid', array(':uid' => 1))->fetchObject());
 
     // Generate and set a session cookie.
     $this->curlInitialize();
@@ -136,8 +135,7 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
     }
 
     // Return the user to the original one.
-    $user = $this->originalUser;
-    drupal_save_session(TRUE);
+    user_revert_user();
 
     // Ensure that internal logged in variable and cURL options are reset.
     $this->loggedInUser = FALSE;
diff --git modules/user/user.module modules/user/user.module
index 9561345..c0cd3f4 100644
--- modules/user/user.module
+++ modules/user/user.module
@@ -3635,3 +3635,62 @@ function user_rdf_mapping() {
     ),
   );
 }
+
+/**
+ * Impersonates another user.
+ *
+ * Each time this function is called, the active user is saved and $new_user
+ * becomes the active user. Multiple calls to this function can be nested,
+ * and session saving will be disabled until all impersonation attempts have
+ * been reverted using user_revert_user().
+ *
+ * @param $new_user
+ *   User to impersonate, either a UID or a user object.
+ *
+ * @return
+ *   Current user object.
+ *
+ * @see user_revert_user()
+ */
+function user_impersonate_user($new_user = NULL) {
+  global $user;
+  $user_original = &drupal_static(__FUNCTION__);
+
+  if (!isset($new_user)) {
+    if (isset($user_original) && !empty($user_original)) {
+      // Restore the previous user from the stack.
+      $user = array_pop($user_original);
+
+      // Re-enable session saving if we are no longer impersonating a user.
+      if (empty($user_original)) {
+        drupal_save_session(TRUE);
+      }
+    }
+  }
+  else {
+    // Push the original user onto the stack and prevent session saving.
+    $user_original[] = $user;
+    drupal_save_session(FALSE);
+
+    if (is_numeric($new_user)) {
+      $user = user_load($new_user);
+    }
+    else {
+      $user = is_object($new_user) ? $new_user : (object) $new_user;
+    }
+  }
+
+  return $user;
+}
+
+/**
+ * Reverts to the previous user after impersonating.
+ *
+ * @return
+ *   Current user.
+ *
+ * @see user_impersonate_user()
+ */
+function user_revert_user() {
+  return user_impersonate_user();
+}
diff --git modules/user/user.test modules/user/user.test
index 75da3a9..6db8303 100644
--- modules/user/user.test
+++ modules/user/user.test
@@ -1618,6 +1618,70 @@ class UserRoleAdminTestCase extends DrupalWebTestCase {
 }
 
 /**
+ * Test case for impersonating users.
+ */
+class UserImpersonatingUserTestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Impersonate users',
+      'description' => 'Temporarily impersonate another user, and then restore the original user.',
+      'group' => 'User',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+  }
+
+  function testUserImpersonateUser() {
+    global $user;
+    $original_user = $user;
+
+    // If not currently logged in, use user_user_impersonate_user() to switch to
+    // user 1. If logged in, switch to the anonymous user instead.
+    if (user_is_anonymous()) {
+      user_impersonate_user(1);
+    }
+    else {
+      user_impersonate_user(0);
+    }
+
+    // Verify that the active user has changed, and that session saving is
+    // disabled.
+    $this->assertEqual($user->uid, ($original_user->uid == 0 ? 1 : 0), t('User switched'));
+    $this->assertFalse(drupal_save_session(), t('Session saving is disabled.'));
+
+    // Perform a second (nested) impersonation.
+    user_impersonate_user(1);
+    $this->assertEqual($user->uid, 1, t('User switched.'));
+
+    // Revert to the user which was active between the first and second
+    // impersonation attempt.
+    user_revert_user();
+
+    // Since we are still impersonating the user from the first attempt,
+    // session handling still needs to be disabled.
+    $this->assertEqual($user->uid, ($original_user->uid == 0 ? 1 : 0), t('User switched.'));
+    $this->assertFalse(drupal_save_session(), t('Session saving is disabled.'));
+
+    // Revert to the original user which was active before the first
+    // impersonation attempt.
+    user_revert_user();
+
+    // Assert that the original user is the active user again, and that session
+    // saving has been re-enabled.
+    $this->assertEqual($user->uid, $original_user->uid, t('Original user successfully restored.'));
+
+    // Simpletest uses user_impersonate_user() too, revert the impersonation by
+    // Simpletest to enable session saving again. This is safe because calling
+    // user_revert_user() too often simply results in returning the active user.
+    user_revert_user();
+    $this->assertTrue(drupal_save_session(), t('Session saving is enabled.'));
+  }
+}
+
+/**
  * Test user token replacement in strings.
  */
 class UserTokenReplaceTestCase extends DrupalWebTestCase {
