Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a0d7807

Browse filesBrowse files
committed
Added user impersonation info and exit action
1 parent e4e1b81 commit a0d7807
Copy full SHA for a0d7807

File tree

8 files changed

+166
-34
lines changed
Filter options

8 files changed

+166
-34
lines changed

‎src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php
+57Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
2020
use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
2121
use Symfony\Component\Security\Core\Role\RoleInterface;
22+
use Symfony\Component\Security\Core\Role\SwitchUserRole;
2223
use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator;
2324
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
2425
use Symfony\Component\Security\Core\Authorization\TraceableAccessDecisionManager;
@@ -69,6 +70,9 @@ public function collect(Request $request, Response $response, \Exception $except
6970
$this->data = array(
7071
'enabled' => false,
7172
'authenticated' => false,
73+
'impersonated' => false,
74+
'impersonator_user' => null,
75+
'impersonation_exit_path' => null,
7276
'token' => null,
7377
'token_class' => null,
7478
'logout_url' => null,
@@ -81,6 +85,9 @@ public function collect(Request $request, Response $response, \Exception $except
8185
$this->data = array(
8286
'enabled' => true,
8387
'authenticated' => false,
88+
'impersonated' => false,
89+
'impersonator_user' => null,
90+
'impersonation_exit_path' => null,
8491
'token' => null,
8592
'token_class' => null,
8693
'logout_url' => null,
@@ -93,6 +100,14 @@ public function collect(Request $request, Response $response, \Exception $except
93100
$inheritedRoles = array();
94101
$assignedRoles = $token->getRoles();
95102

103+
$impersonatorUser = null;
104+
foreach ($assignedRoles as $role) {
105+
if ($role instanceof SwitchUserRole) {
106+
$impersonatorUser = $role->getSource()->getUsername();
107+
break;
108+
}
109+
}
110+
96111
if (null !== $this->roleHierarchy) {
97112
$allRoles = $this->roleHierarchy->getReachableRoles($assignedRoles);
98113
foreach ($allRoles as $role) {
@@ -122,6 +137,9 @@ public function collect(Request $request, Response $response, \Exception $except
122137
$this->data = array(
123138
'enabled' => true,
124139
'authenticated' => $token->isAuthenticated(),
140+
'impersonated' => null !== $impersonatorUser,
141+
'impersonator_user' => $impersonatorUser,
142+
'impersonation_exit_path' => null,
125143
'token' => $token,
126144
'token_class' => $this->hasVarDumper ? new ClassStub(get_class($token)) : get_class($token),
127145
'logout_url' => $logoutUrl,
@@ -165,6 +183,15 @@ public function collect(Request $request, Response $response, \Exception $except
165183
'user_checker' => $firewallConfig->getUserChecker(),
166184
'listeners' => $firewallConfig->getListeners(),
167185
);
186+
187+
// generate exit impersonation path from current request
188+
if ($this->data['impersonated'] && null !== $switchUserConfig = $firewallConfig->getSwitchUser()) {
189+
$exitPath = $request->getRequestUri();
190+
$exitPath .= null === $request->getQueryString() ? '?' : '&';
191+
$exitPath .= urlencode($switchUserConfig['parameter']).'=_exit';
192+
193+
$this->data['impersonation_exit_path'] = $exitPath;
194+
}
168195
}
169196
}
170197
}
@@ -235,6 +262,36 @@ public function isAuthenticated()
235262
return $this->data['authenticated'];
236263
}
237264

265+
/**
266+
* Checks if the user is impersonated or not.
267+
*
268+
* @return bool true if the user is impersonated, false otherwise
269+
*/
270+
public function isImpersonated()
271+
{
272+
return $this->data['impersonated'];
273+
}
274+
275+
/**
276+
* Returns the impersonation source user.
277+
*
278+
* @return string The username
279+
*/
280+
public function getImpersonatorUser()
281+
{
282+
return $this->data['impersonator_user'];
283+
}
284+
285+
/**
286+
* Returns the exit impersonation path.
287+
*
288+
* @return string The URI path
289+
*/
290+
public function getImpersonationExitPath()
291+
{
292+
return $this->data['impersonation_exit_path'];
293+
}
294+
238295
/**
239296
* Get the class name of the security token.
240297
*

‎src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
448448
}
449449

450450
$config->replaceArgument(10, $listenerKeys);
451+
$config->replaceArgument(11, isset($firewall['switch_user']) ? $firewall['switch_user'] : null);
451452

452453
return array($matcher, $listeners, $exceptionListener);
453454
}

‎src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@
136136
<argument /> <!-- access_denied_handler -->
137137
<argument /> <!-- access_denied_url -->
138138
<argument type="collection" /> <!-- listeners -->
139+
<argument /> <!-- switch_user -->
139140
</service>
140141

141142
<service id="security.logout_url_generator" class="Symfony\Component\Security\Http\Logout\LogoutUrlGenerator">

‎src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig
+48-32Lines changed: 48 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,47 +16,63 @@
1616
{% endset %}
1717

1818
{% set text %}
19-
{% if collector.enabled %}
20-
{% if collector.token %}
19+
{% if collector.impersonated %}
20+
<div class="sf-toolbar-info-group">
2121
<div class="sf-toolbar-info-piece">
22-
<b>Logged in as</b>
23-
<span>{{ collector.user }}</span>
22+
<b>Impersonator</b>
23+
<span>{{ collector.impersonatorUser }}</span>
2424
</div>
25+
</div>
26+
{% endif %}
2527

26-
<div class="sf-toolbar-info-piece">
27-
<b>Authenticated</b>
28-
<span class="sf-toolbar-status sf-toolbar-status-{{ is_authenticated ? 'green' : 'red' }}">{{ is_authenticated ? 'Yes' : 'No' }}</span>
29-
</div>
28+
<div class="sf-toolbar-info-group">
29+
{% if collector.enabled %}
30+
{% if collector.token %}
31+
<div class="sf-toolbar-info-piece">
32+
<b>Logged in as</b>
33+
<span>{{ collector.user }}</span>
34+
</div>
3035

31-
<div class="sf-toolbar-info-piece">
32-
<b>Token class</b>
33-
<span>{{ collector.tokenClass|abbr_class }}</span>
34-
</div>
35-
{% else %}
36-
<div class="sf-toolbar-info-piece">
37-
<b>Authenticated</b>
38-
<span class="sf-toolbar-status sf-toolbar-status-red">No</span>
39-
</div>
40-
{% endif %}
36+
<div class="sf-toolbar-info-piece">
37+
<b>Authenticated</b>
38+
<span class="sf-toolbar-status sf-toolbar-status-{{ is_authenticated ? 'green' : 'red' }}">{{ is_authenticated ? 'Yes' : 'No' }}</span>
39+
</div>
4140

42-
{% if collector.firewall %}
43-
<div class="sf-toolbar-info-piece">
44-
<b>Firewall name</b>
45-
<span>{{ collector.firewall.name }}</span>
46-
</div>
47-
{% endif %}
41+
<div class="sf-toolbar-info-piece">
42+
<b>Token class</b>
43+
<span>{{ collector.tokenClass|abbr_class }}</span>
44+
</div>
45+
{% else %}
46+
<div class="sf-toolbar-info-piece">
47+
<b>Authenticated</b>
48+
<span class="sf-toolbar-status sf-toolbar-status-red">No</span>
49+
</div>
50+
{% endif %}
4851

49-
{% if collector.token and collector.logoutUrl %}
52+
{% if collector.firewall %}
53+
<div class="sf-toolbar-info-piece">
54+
<b>Firewall name</b>
55+
<span>{{ collector.firewall.name }}</span>
56+
</div>
57+
{% endif %}
58+
59+
{% if collector.token and collector.logoutUrl %}
60+
<div class="sf-toolbar-info-piece">
61+
<b>Actions</b>
62+
<span>
63+
<a href="{{ collector.logoutUrl }}">Logout</a>
64+
{% if collector.impersonated and collector.impersonationExitPath %}
65+
| <a href="{{ collector.impersonationExitPath }}">Exit impersonation</a>
66+
{% endif %}
67+
</span>
68+
</div>
69+
{% endif %}
70+
{% else %}
5071
<div class="sf-toolbar-info-piece">
51-
<b>Actions</b>
52-
<span><a href="{{ collector.logoutUrl }}">Logout</a></span>
72+
<span>The security is disabled.</span>
5373
</div>
5474
{% endif %}
55-
{% else %}
56-
<div class="sf-toolbar-info-piece">
57-
<span>The security is disabled.</span>
58-
</div>
59-
{% endif %}
75+
</div>
6076
{% endset %}
6177

6278
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: color_code }) }}

‎src/Symfony/Bundle/SecurityBundle/Security/FirewallConfig.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Security/FirewallConfig.php
+12-1Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ final class FirewallConfig
2727
private $accessDeniedHandler;
2828
private $accessDeniedUrl;
2929
private $listeners;
30+
private $switchUser;
3031

3132
/**
3233
* @param string $name
@@ -40,8 +41,9 @@ final class FirewallConfig
4041
* @param string|null $accessDeniedHandler
4142
* @param string|null $accessDeniedUrl
4243
* @param string[] $listeners
44+
* @param array|null $switchUser
4345
*/
44-
public function __construct($name, $userChecker, $requestMatcher = null, $securityEnabled = true, $stateless = false, $provider = null, $context = null, $entryPoint = null, $accessDeniedHandler = null, $accessDeniedUrl = null, $listeners = array())
46+
public function __construct($name, $userChecker, $requestMatcher = null, $securityEnabled = true, $stateless = false, $provider = null, $context = null, $entryPoint = null, $accessDeniedHandler = null, $accessDeniedUrl = null, $listeners = array(), $switchUser = null)
4547
{
4648
$this->name = $name;
4749
$this->userChecker = $userChecker;
@@ -54,6 +56,7 @@ public function __construct($name, $userChecker, $requestMatcher = null, $securi
5456
$this->accessDeniedHandler = $accessDeniedHandler;
5557
$this->accessDeniedUrl = $accessDeniedUrl;
5658
$this->listeners = $listeners;
59+
$this->switchUser = $switchUser;
5760
}
5861

5962
public function getName()
@@ -140,4 +143,12 @@ public function getListeners()
140143
{
141144
return $this->listeners;
142145
}
146+
147+
/**
148+
* @return array|null The switch_user config if enabled, null otherwise
149+
*/
150+
public function getSwitchUser()
151+
{
152+
return $this->switchUser;
153+
}
143154
}

‎src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php
+37Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
2020
use Symfony\Component\Security\Core\Role\Role;
2121
use Symfony\Component\Security\Core\Role\RoleHierarchy;
22+
use Symfony\Component\Security\Core\Role\SwitchUserRole;
2223
use Symfony\Component\Security\Http\FirewallMapInterface;
2324

2425
class SecurityDataCollectorTest extends TestCase
@@ -31,6 +32,9 @@ public function testCollectWhenSecurityIsDisabled()
3132
$this->assertSame('security', $collector->getName());
3233
$this->assertFalse($collector->isEnabled());
3334
$this->assertFalse($collector->isAuthenticated());
35+
$this->assertFalse($collector->isImpersonated());
36+
$this->assertNull($collector->getImpersonatorUser());
37+
$this->assertNull($collector->getImpersonationExitPath());
3438
$this->assertNull($collector->getTokenClass());
3539
$this->assertFalse($collector->supportsRoleHierarchy());
3640
$this->assertCount(0, $collector->getRoles());
@@ -47,6 +51,9 @@ public function testCollectWhenAuthenticationTokenIsNull()
4751

4852
$this->assertTrue($collector->isEnabled());
4953
$this->assertFalse($collector->isAuthenticated());
54+
$this->assertFalse($collector->isImpersonated());
55+
$this->assertNull($collector->getImpersonatorUser());
56+
$this->assertNull($collector->getImpersonationExitPath());
5057
$this->assertNull($collector->getTokenClass());
5158
$this->assertTrue($collector->supportsRoleHierarchy());
5259
$this->assertCount(0, $collector->getRoles());
@@ -67,13 +74,43 @@ public function testCollectAuthenticationTokenAndRoles(array $roles, array $norm
6774

6875
$this->assertTrue($collector->isEnabled());
6976
$this->assertTrue($collector->isAuthenticated());
77+
$this->assertFalse($collector->isImpersonated());
78+
$this->assertNull($collector->getImpersonatorUser());
79+
$this->assertNull($collector->getImpersonationExitPath());
7080
$this->assertSame('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $collector->getTokenClass()->getValue());
7181
$this->assertTrue($collector->supportsRoleHierarchy());
7282
$this->assertSame($normalizedRoles, $collector->getRoles()->getValue(true));
7383
$this->assertSame($inheritedRoles, $collector->getInheritedRoles()->getValue(true));
7484
$this->assertSame('hhamon', $collector->getUser());
7585
}
7686

87+
public function testCollectImpersonatedToken()
88+
{
89+
$adminToken = new UsernamePasswordToken('yceruto', 'P4$$w0rD', 'provider', array('ROLE_ADMIN'));
90+
91+
$userRoles = array(
92+
'ROLE_USER',
93+
new SwitchUserRole('ROLE_PREVIOUS_ADMIN', $adminToken),
94+
);
95+
96+
$tokenStorage = new TokenStorage();
97+
$tokenStorage->setToken(new UsernamePasswordToken('hhamon', 'P4$$w0rD', 'provider', $userRoles));
98+
99+
$collector = new SecurityDataCollector($tokenStorage, $this->getRoleHierarchy());
100+
$collector->collect($this->getRequest(), $this->getResponse());
101+
$collector->lateCollect();
102+
103+
$this->assertTrue($collector->isEnabled());
104+
$this->assertTrue($collector->isAuthenticated());
105+
$this->assertTrue($collector->isImpersonated());
106+
$this->assertSame('yceruto', $collector->getImpersonatorUser());
107+
$this->assertSame('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $collector->getTokenClass()->getValue());
108+
$this->assertTrue($collector->supportsRoleHierarchy());
109+
$this->assertSame(array('ROLE_USER', 'ROLE_PREVIOUS_ADMIN'), $collector->getRoles()->getValue(true));
110+
$this->assertSame(array(), $collector->getInheritedRoles()->getValue(true));
111+
$this->assertSame('hhamon', $collector->getUser());
112+
}
113+
77114
public function testGetFirewall()
78115
{
79116
$firewallConfig = new FirewallConfig('dummy', 'security.request_matcher.dummy', 'security.user_checker.dummy');

‎src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ public function testFirewalls()
107107
'remember_me',
108108
'anonymous',
109109
),
110+
array(
111+
'parameter' => '_switch_user',
112+
'role' => 'ROLE_ALLOWED_TO_SWITCH',
113+
),
110114
),
111115
array(
112116
'host',
@@ -123,6 +127,7 @@ public function testFirewalls()
123127
'http_basic',
124128
'anonymous',
125129
),
130+
null,
126131
),
127132
array(
128133
'with_user_checker',
@@ -139,6 +144,7 @@ public function testFirewalls()
139144
'http_basic',
140145
'anonymous',
141146
),
147+
null,
142148
),
143149
), $configs);
144150

‎src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallConfigTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallConfigTest.php
+4-1Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public function testGetters()
2929
'access_denied_url' => 'foo_access_denied_url',
3030
'access_denied_handler' => 'foo_access_denied_handler',
3131
'user_checker' => 'foo_user_checker',
32+
'switch_user' => array('provider' => null, 'parameter' => '_switch_user', 'role' => 'ROLE_ALLOWED_TO_SWITCH'),
3233
);
3334

3435
$config = new FirewallConfig(
@@ -42,7 +43,8 @@ public function testGetters()
4243
$options['entry_point'],
4344
$options['access_denied_handler'],
4445
$options['access_denied_url'],
45-
$listeners
46+
$listeners,
47+
$options['switch_user']
4648
);
4749

4850
$this->assertSame('foo_firewall', $config->getName());
@@ -57,5 +59,6 @@ public function testGetters()
5759
$this->assertSame($options['user_checker'], $config->getUserChecker());
5860
$this->assertTrue($config->allowsAnonymous());
5961
$this->assertSame($listeners, $config->getListeners());
62+
$this->assertSame($options['switch_user'], $config->getSwitchUser());
6063
}
6164
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.