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 9b9344f

Browse filesBrowse files
committed
Added user impersonation info and exit action
1 parent b223241 commit 9b9344f
Copy full SHA for 9b9344f

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
@@ -20,6 +20,7 @@
2020
use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
2121
use Symfony\Component\Security\Core\Role\RoleInterface;
2222
use Symfony\Bundle\SecurityBundle\Debug\TraceableFirewallListener;
23+
use Symfony\Component\Security\Core\Role\SwitchUserRole;
2324
use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator;
2425
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
2526
use Symfony\Component\Security\Core\Authorization\TraceableAccessDecisionManager;
@@ -73,6 +74,9 @@ public function collect(Request $request, Response $response, \Exception $except
7374
$this->data = array(
7475
'enabled' => false,
7576
'authenticated' => false,
77+
'impersonated' => false,
78+
'impersonator_user' => null,
79+
'impersonation_exit_path' => null,
7680
'token' => null,
7781
'token_class' => null,
7882
'logout_url' => null,
@@ -85,6 +89,9 @@ public function collect(Request $request, Response $response, \Exception $except
8589
$this->data = array(
8690
'enabled' => true,
8791
'authenticated' => false,
92+
'impersonated' => false,
93+
'impersonator_user' => null,
94+
'impersonation_exit_path' => null,
8895
'token' => null,
8996
'token_class' => null,
9097
'logout_url' => null,
@@ -97,6 +104,14 @@ public function collect(Request $request, Response $response, \Exception $except
97104
$inheritedRoles = array();
98105
$assignedRoles = $token->getRoles();
99106

107+
$impersonatorUser = null;
108+
foreach ($assignedRoles as $role) {
109+
if ($role instanceof SwitchUserRole) {
110+
$impersonatorUser = $role->getSource()->getUsername();
111+
break;
112+
}
113+
}
114+
100115
if (null !== $this->roleHierarchy) {
101116
$allRoles = $this->roleHierarchy->getReachableRoles($assignedRoles);
102117
foreach ($allRoles as $role) {
@@ -126,6 +141,9 @@ public function collect(Request $request, Response $response, \Exception $except
126141
$this->data = array(
127142
'enabled' => true,
128143
'authenticated' => $token->isAuthenticated(),
144+
'impersonated' => null !== $impersonatorUser,
145+
'impersonator_user' => $impersonatorUser,
146+
'impersonation_exit_path' => null,
129147
'token' => $token,
130148
'token_class' => $this->hasVarDumper ? new ClassStub(get_class($token)) : get_class($token),
131149
'logout_url' => $logoutUrl,
@@ -169,6 +187,15 @@ public function collect(Request $request, Response $response, \Exception $except
169187
'user_checker' => $firewallConfig->getUserChecker(),
170188
'listeners' => $firewallConfig->getListeners(),
171189
);
190+
191+
// generate exit impersonation path from current request
192+
if ($this->data['impersonated'] && null !== $switchUserConfig = $firewallConfig->getSwitchUser()) {
193+
$exitPath = $request->getRequestUri();
194+
$exitPath .= null === $request->getQueryString() ? '?' : '&';
195+
$exitPath .= urlencode($switchUserConfig['parameter']).'=_exit';
196+
197+
$this->data['impersonation_exit_path'] = $exitPath;
198+
}
172199
}
173200
}
174201

@@ -245,6 +272,36 @@ public function isAuthenticated()
245272
return $this->data['authenticated'];
246273
}
247274

275+
/**
276+
* Checks if the user is impersonated or not.
277+
*
278+
* @return bool true if the user is impersonated, false otherwise
279+
*/
280+
public function isImpersonated()
281+
{
282+
return $this->data['impersonated'];
283+
}
284+
285+
/**
286+
* Returns the impersonation source user.
287+
*
288+
* @return string The username
289+
*/
290+
public function getImpersonatorUser()
291+
{
292+
return $this->data['impersonator_user'];
293+
}
294+
295+
/**
296+
* Returns the exit impersonation path.
297+
*
298+
* @return string The URI path
299+
*/
300+
public function getImpersonationExitPath()
301+
{
302+
return $this->data['impersonation_exit_path'];
303+
}
304+
248305
/**
249306
* Get the class name of the security token.
250307
*

‎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 %}
51+
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 %}
4858

49-
{% if collector.token and collector.logoutUrl %}
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
@@ -24,6 +24,7 @@
2424
use Symfony\Component\Security\Core\Role\Role;
2525
use Symfony\Component\Security\Core\Role\RoleHierarchy;
2626
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
27+
use Symfony\Component\Security\Core\Role\SwitchUserRole;
2728
use Symfony\Component\Security\Http\FirewallMapInterface;
2829
use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator;
2930

@@ -37,6 +38,9 @@ public function testCollectWhenSecurityIsDisabled()
3738
$this->assertSame('security', $collector->getName());
3839
$this->assertFalse($collector->isEnabled());
3940
$this->assertFalse($collector->isAuthenticated());
41+
$this->assertFalse($collector->isImpersonated());
42+
$this->assertNull($collector->getImpersonatorUser());
43+
$this->assertNull($collector->getImpersonationExitPath());
4044
$this->assertNull($collector->getTokenClass());
4145
$this->assertFalse($collector->supportsRoleHierarchy());
4246
$this->assertCount(0, $collector->getRoles());
@@ -53,6 +57,9 @@ public function testCollectWhenAuthenticationTokenIsNull()
5357

5458
$this->assertTrue($collector->isEnabled());
5559
$this->assertFalse($collector->isAuthenticated());
60+
$this->assertFalse($collector->isImpersonated());
61+
$this->assertNull($collector->getImpersonatorUser());
62+
$this->assertNull($collector->getImpersonationExitPath());
5663
$this->assertNull($collector->getTokenClass());
5764
$this->assertTrue($collector->supportsRoleHierarchy());
5865
$this->assertCount(0, $collector->getRoles());
@@ -73,13 +80,43 @@ public function testCollectAuthenticationTokenAndRoles(array $roles, array $norm
7380

7481
$this->assertTrue($collector->isEnabled());
7582
$this->assertTrue($collector->isAuthenticated());
83+
$this->assertFalse($collector->isImpersonated());
84+
$this->assertNull($collector->getImpersonatorUser());
85+
$this->assertNull($collector->getImpersonationExitPath());
7686
$this->assertSame('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $collector->getTokenClass()->getValue());
7787
$this->assertTrue($collector->supportsRoleHierarchy());
7888
$this->assertSame($normalizedRoles, $collector->getRoles()->getValue(true));
7989
$this->assertSame($inheritedRoles, $collector->getInheritedRoles()->getValue(true));
8090
$this->assertSame('hhamon', $collector->getUser());
8191
}
8292

93+
public function testCollectImpersonatedToken()
94+
{
95+
$adminToken = new UsernamePasswordToken('yceruto', 'P4$$w0rD', 'provider', array('ROLE_ADMIN'));
96+
97+
$userRoles = array(
98+
'ROLE_USER',
99+
new SwitchUserRole('ROLE_PREVIOUS_ADMIN', $adminToken),
100+
);
101+
102+
$tokenStorage = new TokenStorage();
103+
$tokenStorage->setToken(new UsernamePasswordToken('hhamon', 'P4$$w0rD', 'provider', $userRoles));
104+
105+
$collector = new SecurityDataCollector($tokenStorage, $this->getRoleHierarchy());
106+
$collector->collect($this->getRequest(), $this->getResponse());
107+
$collector->lateCollect();
108+
109+
$this->assertTrue($collector->isEnabled());
110+
$this->assertTrue($collector->isAuthenticated());
111+
$this->assertTrue($collector->isImpersonated());
112+
$this->assertSame('yceruto', $collector->getImpersonatorUser());
113+
$this->assertSame('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $collector->getTokenClass()->getValue());
114+
$this->assertTrue($collector->supportsRoleHierarchy());
115+
$this->assertSame(array('ROLE_USER', 'ROLE_PREVIOUS_ADMIN'), $collector->getRoles()->getValue(true));
116+
$this->assertSame(array(), $collector->getInheritedRoles()->getValue(true));
117+
$this->assertSame('hhamon', $collector->getUser());
118+
}
119+
83120
public function testGetFirewall()
84121
{
85122
$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.