diff --git a/core/core.services.yml b/core/core.services.yml index ea6cbe4..d0d73c6 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -162,7 +162,7 @@ services: arguments: ['@typed_data_manager'] cron: class: Drupal\Core\Cron - arguments: ['@module_handler', '@lock', '@queue', '@state', '@impersonation', '@logger.channel.cron', '@plugin.manager.queue_worker'] + arguments: ['@module_handler', '@lock', '@queue', '@state', '@account_switcher', '@logger.channel.cron', '@plugin.manager.queue_worker'] diff.formatter: class: Drupal\Core\Diff\DiffFormatter arguments: ['@config.factory'] @@ -996,12 +996,12 @@ services: tags: - { name: event_subscriber } arguments: ['@authentication'] + account_switcher: + class: Drupal\Core\Session\AccountSwitcher + arguments: ['@current_user', '@session_manager'] current_user: class: Drupal\Core\Session\AccountProxy arguments: ['@authentication', '@request_stack'] - impersonation: - class: Drupal\Core\Session\AccountImpersonation - arguments: ['@current_user', '@session_manager'] session_manager: class: Drupal\Core\Session\SessionManager arguments: ['@request_stack', '@database', '@session_manager.metadata_bag', '@settings'] diff --git a/core/lib/Drupal/Core/Cron.php b/core/lib/Drupal/Core/Cron.php index 2076ca2..1c17c94 100644 --- a/core/lib/Drupal/Core/Cron.php +++ b/core/lib/Drupal/Core/Cron.php @@ -13,7 +13,7 @@ use Drupal\Core\Lock\LockBackendInterface; use Drupal\Core\Queue\QueueFactory; use Drupal\Core\Session\AnonymousUserSession; -use Drupal\Core\Session\AccountImpersonationInterface; +use Drupal\Core\Session\AccountSwitcherInterface; use Drupal\Core\Queue\SuspendQueueException; use Psr\Log\LoggerInterface; @@ -51,11 +51,11 @@ class Cron implements CronInterface { protected $state; /** - * The account impersonation service. + * The account switcher service. * - * @var \Drupal\Core\Session\AccountImpersonationInterface + * @var \Drupal\Core\Session\AccountSwitcherInterface */ - protected $impersonation; + protected $accountSwitcher; /** * A logger instance. @@ -82,19 +82,19 @@ class Cron implements CronInterface { * The queue service. * @param \Drupal\Core\State\StateInterface $state * The state service. - * @param \Drupal\Core\Session\AccountImpersonationInterface $impersonation - * The account impersonation service. + * @param \Drupal\Core\Session\AccountSwitcherInterface $account_switcher + * The account switching service. * @param \Psr\Log\LoggerInterface $logger * A logger instance. * @param \Drupal\Core\Queue\QueueWorkerManagerInterface * The queue plugin manager. */ - public function __construct(ModuleHandlerInterface $module_handler, LockBackendInterface $lock, QueueFactory $queue_factory, StateInterface $state, AccountImpersonationInterface $impersonation, LoggerInterface $logger, QueueWorkerManagerInterface $queue_manager) { + public function __construct(ModuleHandlerInterface $module_handler, LockBackendInterface $lock, QueueFactory $queue_factory, StateInterface $state, AccountSwitcherInterface $account_switcher, LoggerInterface $logger, QueueWorkerManagerInterface $queue_manager) { $this->moduleHandler = $module_handler; $this->lock = $lock; $this->queueFactory = $queue_factory; $this->state = $state; - $this->impersonation = $impersonation; + $this->accountSwitcher = $account_switcher; $this->logger = $logger; $this->queueManager = $queue_manager; } @@ -108,7 +108,7 @@ public function run() { // Force the current user to anonymous to ensure consistent permissions on // cron runs. - $this->impersonation->impersonateAccount(new AnonymousUserSession()); + $this->accountSwitcher->switchTo(new AnonymousUserSession()); // Try to allocate enough time to run all the hook_cron implementations. drupal_set_time_limit(240); @@ -135,7 +135,7 @@ public function run() { $this->processQueues(); // Restore the user. - $this->impersonation->revertAccount(); + $this->accountSwitcher->switchBack(); return $return; } diff --git a/core/lib/Drupal/Core/Session/AccountImpersonation.php b/core/lib/Drupal/Core/Session/AccountSwitcher.php similarity index 54% rename from core/lib/Drupal/Core/Session/AccountImpersonation.php rename to core/lib/Drupal/Core/Session/AccountSwitcher.php index 41dba73..6b56a67 100644 --- a/core/lib/Drupal/Core/Session/AccountImpersonation.php +++ b/core/lib/Drupal/Core/Session/AccountSwitcher.php @@ -2,19 +2,19 @@ /** * @file - * Contains \Drupal\Core\Session\AccountImpersonation. + * Contains \Drupal\Core\Session\AccountSwitcher. */ namespace Drupal\Core\Session; /** - * An implementation of AccountImpersonationInterface. + * An implementation of AccountSwitcherInterface. * - * This allows for safe impersonation of user accounts by ensuring that session + * This allows for safe switching of user accounts by ensuring that session * data for one user is not leaked in to others. It also provides a stack that - * allows reverting to a previous user that was impersonated. + * allows reverting to a previous user after switching. */ -class AccountImpersonation implements AccountImpersonationInterface { +class AccountSwitcher implements AccountSwitcherInterface { /** * A stack of previous overridden accounts. @@ -38,21 +38,21 @@ class AccountImpersonation implements AccountImpersonationInterface { protected $sessionManager; /** - * The original state of session saving prior to user impersonations. + * The original state of session saving prior to account switching. * * @var bool */ protected $originalSessionSaving; /** - * Constructs a new AccountImpersonation. + * Constructs a new AccountSwitcher. * * @param \Drupal\Core\Session\AccountProxyInterface $current_user * The current user service. - * @param \Drupal\Core\Session\SessionManager $session_manager + * @param \Drupal\Core\Session\SessionManagerInterface $session_manager * The session manager. */ - public function __construct(AccountProxyInterface $current_user, SessionManager $session_manager) { + public function __construct(AccountProxyInterface $current_user, SessionManagerInterface $session_manager) { $this->currentUser = $current_user; $this->sessionManager = $session_manager; } @@ -60,8 +60,8 @@ public function __construct(AccountProxyInterface $current_user, SessionManager /** * {@inheritdoc} */ - public function impersonateAccount(AccountInterface $account) { - // Prevent session information from being saved and push the previous account. + public function switchTo(AccountInterface $account) { + // Prevent session information from being saved and push previous account. if (!isset($this->originalSessionSaving)) { // Ensure that only the first session saving status is saved. $this->originalSessionSaving = $this->sessionManager->isEnabled(); @@ -75,15 +75,16 @@ public function impersonateAccount(AccountInterface $account) { /** * {@inheritdoc} */ - public function revertAccount() { + public function switchBack() { // Restore the previous account from the stack. if (!empty($this->accountStack)) { $this->currentUser->setAccount(array_pop($this->accountStack)); } else { - throw new \RuntimeException('No more account impersonations to revert.'); + throw new \RuntimeException('No more accounts to revert to.'); } - // Restore original session saving status if all impersonations are reverted. + // Restore original session saving status if all account switches are + // reverted. if (empty($this->accountStack)) { if ($this->originalSessionSaving) { $this->sessionManager->enable(); @@ -92,24 +93,4 @@ public function revertAccount() { return $this; } - /** - * {@inheritdoc} - */ - public function revertAll() { - // Restore the original account from the stack. - if (!empty($this->accountStack)) { - $this->currentUser->setAccount(array_shift($this->accountStack)); - $this->accountStack = array(); - } - else { - throw new \RuntimeException('No more account impersonations to revert.'); - } - // Restore original session saving status if all impersonations are reverted. - if ($this->originalSessionSaving) { - $this->sessionManager->enable(); - } - return $this; - } - } - diff --git a/core/lib/Drupal/Core/Session/AccountImpersonationInterface.php b/core/lib/Drupal/Core/Session/AccountSwitcherInterface.php similarity index 19% rename from core/lib/Drupal/Core/Session/AccountImpersonationInterface.php rename to core/lib/Drupal/Core/Session/AccountSwitcherInterface.php index b022042..8ba8b8e 100644 --- a/core/lib/Drupal/Core/Session/AccountImpersonationInterface.php +++ b/core/lib/Drupal/Core/Session/AccountSwitcherInterface.php @@ -2,53 +2,43 @@ /** * @file - * Contains \Drupal\Core\Session\AccountImpersonationInterface. + * Contains \Drupal\Core\Session\AccountSwitcherInterface. */ namespace Drupal\Core\Session; /** - * Defines an interface for a service for safe account impersonations. + * Defines an interface for a service for safe account switching. * * @ingroup user_api */ -interface AccountImpersonationInterface { +interface AccountSwitcherInterface { /** - * Sets the currently wrapped account to impersonate another account. + * Safely switches to another account. * - * Always remember to call AccountImpersonationInterface::revertAccount() after this - * call! + * Each invocation of AccountSwitcherInterface::switchTo() must be + * matched by a corresponding invocation of + * AccountSwitcherInterface::switchBack() in the same function. * * @param \Drupal\Core\Session\AccountInterface - * $this. - */ - public function impersonateAccount(AccountInterface $account); - - /** - * Reverts from impersonating another account. + * The account to switch to. * - * @return \Drupal\Core\Session\AccountInterface + * @return \Drupal\Core\Session\AccountSwitcherInterface * $this. - * - * @throws \RuntimeException - * When there are no more account impersonations to revert. */ - public function revertAccount(); + public function switchTo(AccountInterface $account); /** - * Reverts all account impersonations to original account. - * - * This is useful for when there is a failure or an exception and there has - * been possibility of multiple impersonations. + * Reverts back to a previous account after switching. * - * @return \Drupal\Core\Session\AccountInterface + * @return \Drupal\Core\Session\AccountSwitcherInterface * $this. * * @throws \RuntimeException - * When there are no more account impersonations to revert. + * When there are no more account switches to revert. */ - public function revertAll(); + public function switchBack(); } diff --git a/core/modules/system/src/Tests/Session/UserImpersonatingUserTest.php b/core/modules/system/src/Tests/Session/AccountSwitcherTest.php similarity index 28% rename from core/modules/system/src/Tests/Session/UserImpersonatingUserTest.php rename to core/modules/system/src/Tests/Session/AccountSwitcherTest.php index ce8f68a..b2cc60e 100644 --- a/core/modules/system/src/Tests/Session/UserImpersonatingUserTest.php +++ b/core/modules/system/src/Tests/Session/AccountSwitcherTest.php @@ -2,7 +2,7 @@ /** * @file - * Contains Drupal\system\Tests\Session\UserImpersonatingUserTest. + * Contains Drupal\system\Tests\Session\AccountSwitcherTest. */ namespace Drupal\system\Tests\Session; @@ -11,78 +11,57 @@ use Drupal\simpletest\KernelTestBase; /** - * Test case for impersonating users. + * Test case for account switching. + * + * @group Session */ -class UserImpersonatingUserTest extends KernelTestBase { +class AccountSwitcherTest extends KernelTestBase { - public static function getInfo() { - return array( - 'name' => 'Impersonate users', - 'description' => 'Temporarily impersonate another user account, and then restore the original account.', - 'group' => 'Session', - ); - } - - function testUserImpersonateUser() { + public function testAccountSwitching() { $session_manager = $this->container->get('session_manager'); $user = $this->container->get('current_user'); - $impersonation = $this->container->get('impersonation'); + $switcher = $this->container->get('account_switcher'); $original_user = clone $user; $original_session_saving = $session_manager->isEnabled(); - // If not currently logged in, use - // AccountImpersonationInterface::impersonateAccount() to switch to user 1. - $impersonation->impersonateAccount(new UserSession(array('uid' => 2))); + // Switch to user with uid 2. + $switcher->switchTo(new UserSession(array('uid' => 2))); // Verify that the active user has changed, and that session saving is // disabled. - $this->assertEqual($user->id(), 2, 'User 2 impersonated.'); + $this->assertEqual($user->id(), 2, 'Switched to user 2.'); $this->assertFalse($session_manager->isEnabled(), 'Session saving is disabled.'); - // Perform a second (nested) impersonation. - $impersonation->impersonateAccount(new UserSession(array('uid' => 3))); - $this->assertEqual($user->id(), 3, 'User 3 impersonated.'); + // Perform a second (nested) user account switch. + $switcher->switchTo(new UserSession(array('uid' => 3))); + $this->assertEqual($user->id(), 3, 'Switched to user 3.'); - // Revert to the user which was active between the first and second - // impersonation attempt. - $impersonation->revertAccount(); + // Revert to the user session that was active between the first and second + // switch. + $switcher->switchBack(); - // Since we are still impersonating the user from the first attempt, - // session handling still needs to be disabled. - $this->assertEqual($user->id(), 2, 'Reverted to user 2.'); + // Since we are still in the account from the first switch, session handling + // still needs to be disabled. + $this->assertEqual($user->id(), 2, 'Reverted back to user 2.'); $this->assertFalse($session_manager->isEnabled(), 'Session saving still disabled.'); - // Revert to the original user which was active before the first - // impersonation attempt. - $impersonation->revertAccount(); + // Revert to the original account which was active before the first switch. + $switcher->switchBack(); - // Assert that the original user is the active user again, and that session - // saving has been re-enabled. + // Assert that the original account is active again, and that session saving + // has been re-enabled. $this->assertEqual($user->id(), $original_user->id(), 'Original user correctly restored.'); $this->assertEqual($session_manager->isEnabled(), $original_session_saving, 'Original session saving correctly restored.'); - // Verify that AccountImpersonationInterface::revertAccount and - // AccountImpersonationInterface::revertAll() will throw exceptions if there - // is no accounts left in the stack. - try { - $impersonation->revertAccount(); - $this->fail('::revertAccount() throws exception if called without previous impersonation.'); - } - catch (\RuntimeException $e) { - if ($e->getMessage() == 'No more account impersonations to revert.') { - $this->pass('::revertAccount() throws exception if called without previous impersonation.'); - } - else { - $this->fail($e->getMessage()); - } - } + // Verify that AccountSwitcherInterface::switchBack() will throw + // an exception if there are no accounts left in the stack. try { - $impersonation->revertAll(); - $this->fail('::revertAll() throws exception if called without previous impersonation.'); + $switcher->switchBack(); + $this->fail('::switchBack() throws exception if called without previous switch.'); } catch (\RuntimeException $e) { - if ($e->getMessage() == 'No more account impersonations to revert.') { - $this->pass('::revertAll() throws exception if called without previous impersonation.'); + if ($e->getMessage() == 'No more accounts to revert to.') { + $this->pass('::switchBack() throws exception if called without previous switch.'); } else { $this->fail($e->getMessage());