Skip to content

Commit eadbca2

Browse files
author
Mateu Aguiló Bosch
committed
[FEATURE] Switch user during RESTful execution
Allow other code to rely on the global $user object when reacting to a RESTful thread execution.
1 parent 7e21136 commit eadbca2

12 files changed

+249
-31
lines changed

restful.module

+3-1
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ function restful_menu_process_callback($resource_name, $version = NULL) {
273273

274274
try {
275275
$resource->setPath(implode('/', $path));
276-
return $resource->process();
276+
$result = $resource->process();
277277
}
278278
catch (RestfulException $e) {
279279
$result = _restful_build_http_api_error($e);
@@ -286,6 +286,8 @@ function restful_menu_process_callback($resource_name, $version = NULL) {
286286
);
287287
}
288288

289+
// If the user was switched during the execution thread, then switch it back.
290+
$resource->switchUserBack();
289291
return $result;
290292
}
291293

src/Authentication/AuthenticationManager.php

+24-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
use Drupal\restful\Plugin\AuthenticationPluginManager;
1313
use Drupal\restful\RestfulManager;
1414

15+
/**
16+
* Class AuthenticationManager.
17+
*
18+
* @package Drupal\restful\Authentication
19+
*/
1520
class AuthenticationManager implements AuthenticationManagerInterface {
1621

1722
/**
@@ -38,14 +43,22 @@ class AuthenticationManager implements AuthenticationManagerInterface {
3843
*/
3944
protected $isOptional = FALSE;
4045

46+
/**
47+
* User session state to switch user for the Drupal thread.
48+
*
49+
* @var UserSessionStateInterface
50+
*/
51+
protected $userSessionState;
52+
4153
/**
4254
* Constructs a new AuthenticationManager object.
4355
*
4456
* @param AuthenticationPluginManager $manager
4557
* The authentication plugin manager.
4658
*/
47-
public function __construct(AuthenticationPluginManager $manager = NULL) {
59+
public function __construct(AuthenticationPluginManager $manager = NULL, UserSessionStateInterface $user_session_state = NULL) {
4860
$this->plugins = new AuthenticationPluginCollection($manager ?: AuthenticationPluginManager::create());
61+
$this->userSessionState = $user_session_state ?: new UserSessionState();
4962
}
5063

5164
/**
@@ -143,6 +156,16 @@ public function getAccount(RequestInterface $request, $cache = TRUE) {
143156
*/
144157
public function setAccount($account) {
145158
$this->account = $account;
159+
if (!empty($account->uid)) {
160+
$this->userSessionState->switchUser($account);
161+
}
162+
}
163+
164+
/**
165+
* {@inheritdoc}
166+
*/
167+
public function switchUserBack() {
168+
return $this->userSessionState->switchUserBack();
146169
}
147170

148171
/**

src/Authentication/AuthenticationManagerInterface.php

+11-5
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,15 @@ public function addAllAuthenticationProviders();
4747
/**
4848
* Get the user account for the request.
4949
*
50-
* @param array $request
50+
* @param RequestInterface $request
5151
* The request.
52-
* @param string $method
53-
* The HTTP method.
54-
* @param boolean $cache
52+
* @param bool $cache
5553
* Boolean indicating if the resolved user should be cached for next calls.
5654
*
5755
* @throws UnauthorizedException
58-
* @return \stdClass
56+
* When bad credentials are provided.
57+
*
58+
* @return object
5959
* The user object.
6060
*/
6161
public function getAccount(RequestInterface $request, $cache = TRUE);
@@ -68,10 +68,16 @@ public function getAccount(RequestInterface $request, $cache = TRUE);
6868
*/
6969
public function setAccount($account);
7070

71+
/**
72+
* Switches the user back from the original user for the session.
73+
*/
74+
public function switchUserBack();
75+
7176
/**
7277
* Gets the plugin collection for this plugin manager.
7378
*
7479
* @return AuthenticationPluginManager
80+
* The plugin manager.
7581
*/
7682
public function getPlugins();
7783

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php
2+
3+
/**
4+
* @file
5+
* Contains \Drupal\restful\Authentication\UserSessionState.
6+
*/
7+
8+
namespace Drupal\restful\Authentication;
9+
10+
/**
11+
* Class UserSessionState.
12+
*
13+
* @package Drupal\restful\Authentication
14+
*/
15+
class UserSessionState implements UserSessionStateInterface {
16+
17+
/**
18+
* Boolean holding if this is the first switch.
19+
*
20+
* @var bool
21+
*/
22+
protected static $isSwitched = FALSE;
23+
24+
/**
25+
* Boolean holding if the session needs to be saved.
26+
*
27+
* @var bool
28+
*/
29+
protected $needsSaving = FALSE;
30+
31+
/**
32+
* Object holding the original user.
33+
*
34+
* This is saved for switch back purposes.
35+
*
36+
* @var object
37+
*/
38+
protected $originalUser;
39+
40+
/**
41+
* {@inheritdoc}
42+
*/
43+
public static function isSwitched() {
44+
return static::$isSwitched;
45+
}
46+
47+
/**
48+
* {@inheritdoc}
49+
*/
50+
public function switchUser($account) {
51+
global $user;
52+
53+
if (!static::isSwitched() && !$this->originalUser && !$this->needsSaving) {
54+
// This is the first time a user switched, and there isn't an original
55+
// user session.
56+
$this->needsSaving = drupal_save_session();
57+
$this->originalUser = $user;
58+
59+
// Don't allow a session to be saved. Provider that require a session to
60+
// be saved, like the cookie provider, need to explicitly set
61+
// drupal_save_session(TRUE).
62+
// @see LoginCookie__1_0::loginUser().
63+
drupal_save_session(FALSE);
64+
}
65+
66+
// Set the global user.
67+
$user = $account;
68+
}
69+
70+
/**
71+
* Switch the user to the authenticated user, and back.
72+
*
73+
* This should be called only for an API call. It should not be used for calls
74+
* via the menu system, as it might be a login request, so we avoid switching
75+
* back to the anonymous user.
76+
*/
77+
public function switchUserBack() {
78+
global $user;
79+
if (!$this->originalUser) {
80+
return;
81+
}
82+
83+
$user = $this->originalUser;
84+
drupal_save_session($this->needsSaving);
85+
$this->reset();
86+
}
87+
88+
/**
89+
* Reset the initial values.
90+
*/
91+
protected function reset() {
92+
// Reset initial values.
93+
static::$isSwitched = FALSE;
94+
$this->originalUser = NULL;
95+
$this->needsSaving = FALSE;
96+
}
97+
98+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/**
4+
* @file
5+
* Contains \Drupal\restful\Authentication\UserSessionStateInterface.
6+
*/
7+
8+
namespace Drupal\restful\Authentication;
9+
10+
/**
11+
* Class UserSessionStateInterface.
12+
*
13+
* @package Drupal\restful\Authentication
14+
*/
15+
interface UserSessionStateInterface {
16+
17+
/**
18+
* Check if the user has already been switched.
19+
*
20+
* We need this information to perform additional actions the first time a
21+
* user is switched.
22+
*
23+
* @return bool
24+
* TRUE if the user has been switched previously. FALSE otherwise.
25+
*/
26+
public static function isSwitched();
27+
28+
/**
29+
* Make the passed in user to be the account for the Drupal thread.
30+
*
31+
* @param object $account
32+
* The account to switch to.
33+
*/
34+
public function switchUser($account);
35+
36+
/**
37+
* Switch the user to the authenticated user, and back.
38+
*
39+
* This should be called only for an API call. It should not be used for calls
40+
* via the menu system, as it might be a login request, so we avoid switching
41+
* back to the anonymous user.
42+
*/
43+
public function switchUserBack();
44+
45+
}

src/Plugin/resource/Decorators/CacheDecoratedResource.php

-7
Original file line numberDiff line numberDiff line change
@@ -286,13 +286,6 @@ public function isEnabled() {
286286
return $this->subject->isEnabled();
287287
}
288288

289-
/**
290-
* {@inheritdoc}
291-
*/
292-
public function discover($path = NULL) {
293-
return $this->subject->discover($path);
294-
}
295-
296289
/**
297290
* {@inheritdoc}
298291
*/

src/Plugin/resource/Decorators/RateLimitDecoratedResource.php

+5-10
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77

88
namespace Drupal\restful\Plugin\resource\Decorators;
99

10-
use Drupal\restful\Http\RequestInterface;
11-
use Drupal\restful\Plugin\resource\DataProvider\DataProviderInterface;
12-
use Drupal\restful\Plugin\resource\Field\ResourceFieldCollection;
1310
use Drupal\restful\Plugin\resource\ResourceInterface;
1411
use Drupal\restful\RateLimit\RateLimitManager;
1512

13+
/**
14+
* Class RateLimitDecoratedResource.
15+
*
16+
* @package Drupal\restful\Plugin\resource\Decorators
17+
*/
1618
class RateLimitDecoratedResource extends ResourceDecoratorBase implements ResourceDecoratorInterface {
1719

1820
/**
@@ -83,11 +85,4 @@ public function setAccount($account) {
8385
$this->rateLimitManager->setAccount($account);
8486
}
8587

86-
/**
87-
* {@inheritdoc}
88-
*/
89-
public function discover($path = NULL) {
90-
return $this->subject->discover($path);
91-
}
92-
9388
}

src/Plugin/resource/Decorators/ResourceDecoratorBase.php

+19-4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
use Drupal\restful\Plugin\resource\Field\ResourceFieldCollectionInterface;
1717
use Drupal\restful\Plugin\resource\ResourceInterface;
1818

19+
/**
20+
* Class ResourceDecoratorBase.
21+
*
22+
* @package Drupal\restful\Plugin\resource\Decorators
23+
*/
1924
abstract class ResourceDecoratorBase extends PluginBase implements ResourceDecoratorInterface {
2025

2126
/**
@@ -51,24 +56,34 @@ public function dataProviderFactory() {
5156
}
5257

5358
/**
54-
* Proxy method to get the account from the rateLimitManager.
55-
*
5659
* {@inheritdoc}
5760
*/
5861
public function getAccount($cache = TRUE) {
5962
return $this->subject->getAccount($cache);
6063
}
6164

6265
/**
63-
* Proxy method to get the account from the rateLimitManager.
64-
*
6566
* {@inheritdoc}
6667
*/
6768
public function setAccount($account) {
6869
$this->subject->setAccount($account);
6970
$this->getDataProvider()->setAccount($account);
7071
}
7172

73+
/**
74+
* {@inheritdoc}
75+
*/
76+
public function switchUserBack() {
77+
$this->subject->switchUserBack();
78+
}
79+
80+
/**
81+
* {@inheritdoc}
82+
*/
83+
public function discover($path = NULL) {
84+
return $this->subject->discover($path);
85+
}
86+
7287
/**
7388
* {@inheritdoc}
7489
*/

src/Plugin/resource/LoginCookie__1_0.php

+15
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@ public function loginAndRespondWithCookie() {
9292
*/
9393
public function loginUser($account) {
9494
global $user;
95+
96+
$this->authenticationManager->switchUserBack();
97+
// Explicitly allow a session to be saved, as it was disabled in
98+
// UserSessionState::switchUser. However this resource is a special one, in
99+
// the sense that we want to keep the user authenticated after login.
100+
drupal_save_session(TRUE);
101+
95102
// Override the global user.
96103
$user = user_load($account->uid);
97104

@@ -110,4 +117,12 @@ public static function getCSRFTokenValue() {
110117
return reset($token);
111118
}
112119

120+
/**
121+
* {@inheritdoc}
122+
*/
123+
public function switchUserBack() {
124+
// We don't want to switch back in this case!
125+
drupal_save_session(TRUE);
126+
}
127+
113128
}

src/Plugin/resource/Resource.php

+7
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,13 @@ public function getAccount($cache = TRUE) {
121121
return $this->authenticationManager->getAccount($this->getRequest(), $cache);
122122
}
123123

124+
/**
125+
* {@inheritdoc}
126+
*/
127+
public function switchUserBack() {
128+
return $this->authenticationManager->switchUserBack();
129+
}
130+
124131
/**
125132
* {@inheritdoc}
126133
*/

0 commit comments

Comments
 (0)