diff --git a/restful.module b/restful.module index 1d9b0159..923865d0 100644 --- a/restful.module +++ b/restful.module @@ -273,7 +273,7 @@ function restful_menu_process_callback($resource_name, $version = NULL) { try { $resource->setPath(implode('/', $path)); - return $resource->process(); + $result = $resource->process(); } catch (RestfulException $e) { $result = _restful_build_http_api_error($e); @@ -286,6 +286,8 @@ function restful_menu_process_callback($resource_name, $version = NULL) { ); } + // If the user was switched during the execution thread, then switch it back. + $resource->switchUserBack(); return $result; } diff --git a/src/Authentication/AuthenticationManager.php b/src/Authentication/AuthenticationManager.php index 2f0e059b..447c3177 100644 --- a/src/Authentication/AuthenticationManager.php +++ b/src/Authentication/AuthenticationManager.php @@ -12,6 +12,11 @@ use Drupal\restful\Plugin\AuthenticationPluginManager; use Drupal\restful\RestfulManager; +/** + * Class AuthenticationManager. + * + * @package Drupal\restful\Authentication + */ class AuthenticationManager implements AuthenticationManagerInterface { /** @@ -38,14 +43,22 @@ class AuthenticationManager implements AuthenticationManagerInterface { */ protected $isOptional = FALSE; + /** + * User session state to switch user for the Drupal thread. + * + * @var UserSessionStateInterface + */ + protected $userSessionState; + /** * Constructs a new AuthenticationManager object. * * @param AuthenticationPluginManager $manager * The authentication plugin manager. */ - public function __construct(AuthenticationPluginManager $manager = NULL) { + public function __construct(AuthenticationPluginManager $manager = NULL, UserSessionStateInterface $user_session_state = NULL) { $this->plugins = new AuthenticationPluginCollection($manager ?: AuthenticationPluginManager::create()); + $this->userSessionState = $user_session_state ?: new UserSessionState(); } /** @@ -143,6 +156,16 @@ public function getAccount(RequestInterface $request, $cache = TRUE) { */ public function setAccount($account) { $this->account = $account; + if (!empty($account->uid)) { + $this->userSessionState->switchUser($account); + } + } + + /** + * {@inheritdoc} + */ + public function switchUserBack() { + return $this->userSessionState->switchUserBack(); } /** diff --git a/src/Authentication/AuthenticationManagerInterface.php b/src/Authentication/AuthenticationManagerInterface.php index 018ef747..4f511b14 100644 --- a/src/Authentication/AuthenticationManagerInterface.php +++ b/src/Authentication/AuthenticationManagerInterface.php @@ -47,15 +47,15 @@ public function addAllAuthenticationProviders(); /** * Get the user account for the request. * - * @param array $request + * @param RequestInterface $request * The request. - * @param string $method - * The HTTP method. - * @param boolean $cache + * @param bool $cache * Boolean indicating if the resolved user should be cached for next calls. * * @throws UnauthorizedException - * @return \stdClass + * When bad credentials are provided. + * + * @return object * The user object. */ public function getAccount(RequestInterface $request, $cache = TRUE); @@ -68,10 +68,16 @@ public function getAccount(RequestInterface $request, $cache = TRUE); */ public function setAccount($account); + /** + * Switches the user back from the original user for the session. + */ + public function switchUserBack(); + /** * Gets the plugin collection for this plugin manager. * * @return AuthenticationPluginManager + * The plugin manager. */ public function getPlugins(); diff --git a/src/Authentication/UserSessionState.php b/src/Authentication/UserSessionState.php new file mode 100644 index 00000000..933735c9 --- /dev/null +++ b/src/Authentication/UserSessionState.php @@ -0,0 +1,98 @@ +originalUser && !$this->needsSaving) { + // This is the first time a user switched, and there isn't an original + // user session. + $this->needsSaving = drupal_save_session(); + $this->originalUser = $user; + + // Don't allow a session to be saved. Provider that require a session to + // be saved, like the cookie provider, need to explicitly set + // drupal_save_session(TRUE). + // @see LoginCookie__1_0::loginUser(). + drupal_save_session(FALSE); + } + + // Set the global user. + $user = $account; + } + + /** + * Switch the user to the authenticated user, and back. + * + * This should be called only for an API call. It should not be used for calls + * via the menu system, as it might be a login request, so we avoid switching + * back to the anonymous user. + */ + public function switchUserBack() { + global $user; + if (!$this->originalUser) { + return; + } + + $user = $this->originalUser; + drupal_save_session($this->needsSaving); + $this->reset(); + } + + /** + * Reset the initial values. + */ + protected function reset() { + // Reset initial values. + static::$isSwitched = FALSE; + $this->originalUser = NULL; + $this->needsSaving = FALSE; + } + +} diff --git a/src/Authentication/UserSessionStateInterface.php b/src/Authentication/UserSessionStateInterface.php new file mode 100644 index 00000000..e76c160f --- /dev/null +++ b/src/Authentication/UserSessionStateInterface.php @@ -0,0 +1,45 @@ +subject->isEnabled(); } - /** - * {@inheritdoc} - */ - public function discover($path = NULL) { - return $this->subject->discover($path); - } - /** * {@inheritdoc} */ diff --git a/src/Plugin/resource/Decorators/RateLimitDecoratedResource.php b/src/Plugin/resource/Decorators/RateLimitDecoratedResource.php index 1cdb7144..1842532e 100644 --- a/src/Plugin/resource/Decorators/RateLimitDecoratedResource.php +++ b/src/Plugin/resource/Decorators/RateLimitDecoratedResource.php @@ -7,12 +7,14 @@ namespace Drupal\restful\Plugin\resource\Decorators; -use Drupal\restful\Http\RequestInterface; -use Drupal\restful\Plugin\resource\DataProvider\DataProviderInterface; -use Drupal\restful\Plugin\resource\Field\ResourceFieldCollection; use Drupal\restful\Plugin\resource\ResourceInterface; use Drupal\restful\RateLimit\RateLimitManager; +/** + * Class RateLimitDecoratedResource. + * + * @package Drupal\restful\Plugin\resource\Decorators + */ class RateLimitDecoratedResource extends ResourceDecoratorBase implements ResourceDecoratorInterface { /** @@ -83,11 +85,4 @@ public function setAccount($account) { $this->rateLimitManager->setAccount($account); } - /** - * {@inheritdoc} - */ - public function discover($path = NULL) { - return $this->subject->discover($path); - } - } diff --git a/src/Plugin/resource/Decorators/ResourceDecoratorBase.php b/src/Plugin/resource/Decorators/ResourceDecoratorBase.php index 451100a2..5992b432 100644 --- a/src/Plugin/resource/Decorators/ResourceDecoratorBase.php +++ b/src/Plugin/resource/Decorators/ResourceDecoratorBase.php @@ -16,6 +16,11 @@ use Drupal\restful\Plugin\resource\Field\ResourceFieldCollectionInterface; use Drupal\restful\Plugin\resource\ResourceInterface; +/** + * Class ResourceDecoratorBase. + * + * @package Drupal\restful\Plugin\resource\Decorators + */ abstract class ResourceDecoratorBase extends PluginBase implements ResourceDecoratorInterface { /** @@ -51,8 +56,6 @@ public function dataProviderFactory() { } /** - * Proxy method to get the account from the rateLimitManager. - * * {@inheritdoc} */ public function getAccount($cache = TRUE) { @@ -60,8 +63,6 @@ public function getAccount($cache = TRUE) { } /** - * Proxy method to get the account from the rateLimitManager. - * * {@inheritdoc} */ public function setAccount($account) { @@ -69,6 +70,20 @@ public function setAccount($account) { $this->getDataProvider()->setAccount($account); } + /** + * {@inheritdoc} + */ + public function switchUserBack() { + $this->subject->switchUserBack(); + } + + /** + * {@inheritdoc} + */ + public function discover($path = NULL) { + return $this->subject->discover($path); + } + /** * {@inheritdoc} */ diff --git a/src/Plugin/resource/LoginCookie__1_0.php b/src/Plugin/resource/LoginCookie__1_0.php index 4af2df65..801c9d6d 100644 --- a/src/Plugin/resource/LoginCookie__1_0.php +++ b/src/Plugin/resource/LoginCookie__1_0.php @@ -92,6 +92,13 @@ public function loginAndRespondWithCookie() { */ public function loginUser($account) { global $user; + + $this->authenticationManager->switchUserBack(); + // Explicitly allow a session to be saved, as it was disabled in + // UserSessionState::switchUser. However this resource is a special one, in + // the sense that we want to keep the user authenticated after login. + drupal_save_session(TRUE); + // Override the global user. $user = user_load($account->uid); @@ -110,4 +117,12 @@ public static function getCSRFTokenValue() { return reset($token); } + /** + * {@inheritdoc} + */ + public function switchUserBack() { + // We don't want to switch back in this case! + drupal_save_session(TRUE); + } + } diff --git a/src/Plugin/resource/Resource.php b/src/Plugin/resource/Resource.php index 5feb3043..40b50a3e 100644 --- a/src/Plugin/resource/Resource.php +++ b/src/Plugin/resource/Resource.php @@ -121,6 +121,13 @@ public function getAccount($cache = TRUE) { return $this->authenticationManager->getAccount($this->getRequest(), $cache); } + /** + * {@inheritdoc} + */ + public function switchUserBack() { + return $this->authenticationManager->switchUserBack(); + } + /** * {@inheritdoc} */ diff --git a/src/Plugin/resource/ResourceInterface.php b/src/Plugin/resource/ResourceInterface.php index 38ba0078..5044fc30 100644 --- a/src/Plugin/resource/ResourceInterface.php +++ b/src/Plugin/resource/ResourceInterface.php @@ -20,6 +20,11 @@ use Drupal\restful\Plugin\resource\Field\ResourceFieldCollection; use Drupal\restful\Plugin\resource\Field\ResourceFieldCollectionInterface; +/** + * Interface ResourceInterface. + * + * @package Drupal\restful\Plugin\resource + */ interface ResourceInterface extends PluginInspectionInterface, ConfigurablePluginInterface { /** @@ -51,6 +56,11 @@ public function dataProviderFactory(); */ public function getAccount($cache = TRUE); + /** + * Switches the user back from the original user for the session. + */ + public function switchUserBack(); + /** * {@inheritdoc} */ diff --git a/tests/RestfulUserLoginCookieTestCase.test b/tests/RestfulUserLoginCookieTestCase.test index da933fc3..5745acf8 100644 --- a/tests/RestfulUserLoginCookieTestCase.test +++ b/tests/RestfulUserLoginCookieTestCase.test @@ -21,6 +21,9 @@ class RestfulUserLoginCookieTestCase extends RestfulCurlBaseTestCase { */ protected $originalServer; + /** + * {@inheritdoc} + */ public static function getInfo() { return array( 'name' => 'Login endpoint', @@ -29,13 +32,19 @@ class RestfulUserLoginCookieTestCase extends RestfulCurlBaseTestCase { ); } - function setUp() { + /** + * {@inheritdoc} + */ + public function setUp() { parent::setUp('restful'); $this->originalServer = $_SERVER; } - function tearDown() { + /** + * {@inheritdoc} + */ + public function tearDown() { global $user; // Put back the user object. $user = $this->originalUser; @@ -48,7 +57,7 @@ class RestfulUserLoginCookieTestCase extends RestfulCurlBaseTestCase { /** * Test login using curl via /api/login. */ - function testLogin() { + public function testLogin() { global $user; // We need to hijack the global user object in order to force it to be an // anonymous user.