Skip to content

Commit

Permalink
[FEATURE] Switch user during RESTful execution
Browse files Browse the repository at this point in the history
Allow other code to rely on the global $user object when reacting
to a RESTful thread execution.
  • Loading branch information
Mateu Aguiló Bosch committed Feb 20, 2016
1 parent 7e21136 commit eadbca2
Show file tree
Hide file tree
Showing 12 changed files with 249 additions and 31 deletions.
4 changes: 3 additions & 1 deletion restful.module
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}

Expand Down
25 changes: 24 additions & 1 deletion src/Authentication/AuthenticationManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
use Drupal\restful\Plugin\AuthenticationPluginManager;
use Drupal\restful\RestfulManager;

/**
* Class AuthenticationManager.
*
* @package Drupal\restful\Authentication
*/
class AuthenticationManager implements AuthenticationManagerInterface {

/**
Expand All @@ -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();
}

/**
Expand Down Expand Up @@ -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();
}

/**
Expand Down
16 changes: 11 additions & 5 deletions src/Authentication/AuthenticationManagerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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();

Expand Down
98 changes: 98 additions & 0 deletions src/Authentication/UserSessionState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

/**
* @file
* Contains \Drupal\restful\Authentication\UserSessionState.
*/

namespace Drupal\restful\Authentication;

/**
* Class UserSessionState.
*
* @package Drupal\restful\Authentication
*/
class UserSessionState implements UserSessionStateInterface {

/**
* Boolean holding if this is the first switch.
*
* @var bool
*/
protected static $isSwitched = FALSE;

/**
* Boolean holding if the session needs to be saved.
*
* @var bool
*/
protected $needsSaving = FALSE;

/**
* Object holding the original user.
*
* This is saved for switch back purposes.
*
* @var object
*/
protected $originalUser;

/**
* {@inheritdoc}
*/
public static function isSwitched() {
return static::$isSwitched;
}

/**
* {@inheritdoc}
*/
public function switchUser($account) {
global $user;

if (!static::isSwitched() && !$this->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;
}

}
45 changes: 45 additions & 0 deletions src/Authentication/UserSessionStateInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

/**
* @file
* Contains \Drupal\restful\Authentication\UserSessionStateInterface.
*/

namespace Drupal\restful\Authentication;

/**
* Class UserSessionStateInterface.
*
* @package Drupal\restful\Authentication
*/
interface UserSessionStateInterface {

/**
* Check if the user has already been switched.
*
* We need this information to perform additional actions the first time a
* user is switched.
*
* @return bool
* TRUE if the user has been switched previously. FALSE otherwise.
*/
public static function isSwitched();

/**
* Make the passed in user to be the account for the Drupal thread.
*
* @param object $account
* The account to switch to.
*/
public function switchUser($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();

}
7 changes: 0 additions & 7 deletions src/Plugin/resource/Decorators/CacheDecoratedResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -286,13 +286,6 @@ public function isEnabled() {
return $this->subject->isEnabled();
}

/**
* {@inheritdoc}
*/
public function discover($path = NULL) {
return $this->subject->discover($path);
}

/**
* {@inheritdoc}
*/
Expand Down
15 changes: 5 additions & 10 deletions src/Plugin/resource/Decorators/RateLimitDecoratedResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

/**
Expand Down Expand Up @@ -83,11 +85,4 @@ public function setAccount($account) {
$this->rateLimitManager->setAccount($account);
}

/**
* {@inheritdoc}
*/
public function discover($path = NULL) {
return $this->subject->discover($path);
}

}
23 changes: 19 additions & 4 deletions src/Plugin/resource/Decorators/ResourceDecoratorBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

/**
Expand Down Expand Up @@ -51,24 +56,34 @@ public function dataProviderFactory() {
}

/**
* Proxy method to get the account from the rateLimitManager.
*
* {@inheritdoc}
*/
public function getAccount($cache = TRUE) {
return $this->subject->getAccount($cache);
}

/**
* Proxy method to get the account from the rateLimitManager.
*
* {@inheritdoc}
*/
public function setAccount($account) {
$this->subject->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}
*/
Expand Down
15 changes: 15 additions & 0 deletions src/Plugin/resource/LoginCookie__1_0.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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);
}

}
7 changes: 7 additions & 0 deletions src/Plugin/resource/Resource.php
Original file line number Diff line number Diff line change
Expand Up @@ -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}
*/
Expand Down
Loading

0 comments on commit eadbca2

Please sign in to comment.