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(); +}