Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1150
diff -u -r1.1150 common.inc
--- includes/common.inc	22 Apr 2010 22:36:01 -0000	1.1150
+++ includes/common.inc	24 Apr 2010 01:24:06 -0000
@@ -4442,13 +4442,9 @@
   // 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);
@@ -4511,8 +4507,7 @@
     }
   }
   // Restore the user.
-  $GLOBALS['user'] = $original_user;
-  drupal_save_session(TRUE);
+  user_revert_user();
 
   return $return;
 }
@@ -4833,7 +4828,7 @@
  * using uasort(). Since this is expensive, when passing already sorted
  * elements to drupal_render(), for example from a database query, set
  * $elements['#sorted'] = TRUE to avoid sorting them a second time.
- * 
+ *
  * drupal_render() flags each element with a '#printed' status to indicate that
  * the element has been rendered, which allows individual elements of a given
  * array to be rendered independently and prevents them from being rendered
Index: modules/user/user.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.test,v
retrieving revision 1.89
diff -u -r1.89 user.test
--- modules/user/user.test	20 Apr 2010 09:48:06 -0000	1.89
+++ modules/user/user.test	24 Apr 2010 01:24:07 -0000
@@ -1522,6 +1522,65 @@
 }
 
 /**
+ * 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() 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.'));
+    $this->assertTrue(drupal_save_session(), t('Session saving is enabled.'));
+  }
+}
+
+/**
  * Test user token replacement in strings.
  */
 class UserTokenReplaceTestCase extends DrupalWebTestCase {
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.1161
diff -u -r1.1161 user.module
--- modules/user/user.module	23 Apr 2010 05:39:43 -0000	1.1161
+++ modules/user/user.module	24 Apr 2010 01:24:07 -0000
@@ -3542,3 +3542,62 @@
     ),
   );
 }
+
+/**
+ * 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();
+}
