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 dabd7c3

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

File tree

8 files changed

+158
-36
lines changed
Filter options

8 files changed

+158
-36
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
@@ -18,6 +18,7 @@
1818
use Symfony\Component\HttpFoundation\Response;
1919
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
2020
use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
21+
use Symfony\Component\Security\Core\Role\SwitchUserRole;
2122
use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator;
2223
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
2324
use Symfony\Component\Security\Core\Authorization\TraceableAccessDecisionManager;
@@ -68,6 +69,9 @@ public function collect(Request $request, Response $response, \Exception $except
6869
$this->data = array(
6970
'enabled' => false,
7071
'authenticated' => false,
72+
'impersonated' => false,
73+
'impersonated_source_user' => null,
74+
'impersonated_exit_path' => null,
7175
'token' => null,
7276
'token_class' => null,
7377
'logout_url' => null,
@@ -80,6 +84,9 @@ public function collect(Request $request, Response $response, \Exception $except
8084
$this->data = array(
8185
'enabled' => true,
8286
'authenticated' => false,
87+
'impersonated' => false,
88+
'impersonated_source_user' => null,
89+
'impersonated_exit_path' => null,
8390
'token' => null,
8491
'token_class' => null,
8592
'logout_url' => null,
@@ -92,6 +99,14 @@ public function collect(Request $request, Response $response, \Exception $except
9299
$inheritedRoles = array();
93100
$assignedRoles = $token->getRoles();
94101

102+
$impersonatedSourceUser = null;
103+
foreach ($assignedRoles as $role) {
104+
if ($role instanceof SwitchUserRole) {
105+
$impersonatedSourceUser = $role->getSource()->getUsername();
106+
break;
107+
}
108+
}
109+
95110
if (null !== $this->roleHierarchy) {
96111
$allRoles = $this->roleHierarchy->getReachableRoles($assignedRoles);
97112
foreach ($allRoles as $role) {
@@ -113,6 +128,9 @@ public function collect(Request $request, Response $response, \Exception $except
113128
$this->data = array(
114129
'enabled' => true,
115130
'authenticated' => $token->isAuthenticated(),
131+
'impersonated' => null !== $impersonatedSourceUser,
132+
'impersonated_source_user' => $impersonatedSourceUser,
133+
'impersonated_exit_path' => null,
116134
'token' => $token,
117135
'token_class' => $this->hasVarDumper ? new ClassStub(get_class($token)) : get_class($token),
118136
'logout_url' => $logoutUrl,
@@ -156,6 +174,15 @@ public function collect(Request $request, Response $response, \Exception $except
156174
'user_checker' => $firewallConfig->getUserChecker(),
157175
'listeners' => $firewallConfig->getListeners(),
158176
);
177+
178+
// generate exit impersonation path from current request
179+
if ($this->data['impersonated'] && null !== $switchUserConfig = $firewallConfig->getSwitchUser()) {
180+
$exitPath = $request->getRequestUri();
181+
$exitPath .= false === strpos($exitPath, '?') ? '?' : '&';
182+
$exitPath .= $switchUserConfig['parameter'].'=_exit';
183+
184+
$this->data['impersonated_exit_path'] = $exitPath;
185+
}
159186
}
160187
}
161188
}
@@ -226,6 +253,36 @@ public function isAuthenticated()
226253
return $this->data['authenticated'];
227254
}
228255

256+
/**
257+
* Checks if the user is impersonated or not.
258+
*
259+
* @return bool true if the user is impersonated, false otherwise
260+
*/
261+
public function isImpersonated()
262+
{
263+
return $this->data['impersonated'];
264+
}
265+
266+
/**
267+
* Returns the impersonation source user.
268+
*
269+
* @return string The username
270+
*/
271+
public function getImpersonatedSourceUser()
272+
{
273+
return $this->data['impersonated_source_user'];
274+
}
275+
276+
/**
277+
* Returns the exit impersonation path.
278+
*
279+
* @return string The URI path
280+
*/
281+
public function getImpersonatedExitPath()
282+
{
283+
return $this->data['impersonated_exit_path'];
284+
}
285+
229286
/**
230287
* Get the class name of the security token.
231288
*

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+5-3Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,8 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
399399
if (isset($firewall['switch_user'])) {
400400
$listenerKeys[] = 'switch_user';
401401
$listeners[] = new Reference($this->createSwitchUserListener($container, $id, $firewall['switch_user'], $defaultProvider));
402+
403+
$config->replaceArgument(8, $firewall['switch_user']);
402404
}
403405

404406
// Access listener
@@ -407,8 +409,8 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
407409
// Exception listener
408410
$exceptionListener = new Reference($this->createExceptionListener($container, $firewall, $id, $configuredEntryPoint ?: $defaultEntryPoint, $firewall['stateless']));
409411

410-
$config->replaceArgument(8, isset($firewall['access_denied_handler']) ? $firewall['access_denied_handler'] : null);
411-
$config->replaceArgument(9, isset($firewall['access_denied_url']) ? $firewall['access_denied_url'] : null);
412+
$config->replaceArgument(9, isset($firewall['access_denied_handler']) ? $firewall['access_denied_handler'] : null);
413+
$config->replaceArgument(10, isset($firewall['access_denied_url']) ? $firewall['access_denied_url'] : null);
412414

413415
$container->setAlias('security.user_checker.'.$id, new Alias($firewall['user_checker'], false));
414416

@@ -425,7 +427,7 @@ private function createFirewall(ContainerBuilder $container, $id, $firewall, &$a
425427
$listenerKeys[] = 'anonymous';
426428
}
427429

428-
$config->replaceArgument(10, $listenerKeys);
430+
$config->replaceArgument(11, $listenerKeys);
429431

430432
return array($matcher, $listeners, $exceptionListener);
431433
}

‎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
@@ -133,6 +133,7 @@
133133
<argument /> <!-- provider -->
134134
<argument /> <!-- context -->
135135
<argument /> <!-- entry_point -->
136+
<argument /> <!-- switch_user -->
136137
<argument /> <!-- access_denied_handler -->
137138
<argument /> <!-- access_denied_url -->
138139
<argument type="collection" /> <!-- listeners -->

‎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>Impersonating as</b>
23+
<span>{{ collector.impersonatedSourceUser }}</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.impersonatedExitPath %}
65+
| <a href="{{ collector.impersonatedExitPath }}">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
@@ -24,6 +24,7 @@ final class FirewallConfig
2424
private $provider;
2525
private $context;
2626
private $entryPoint;
27+
private $switchUser;
2728
private $accessDeniedHandler;
2829
private $accessDeniedUrl;
2930
private $listeners;
@@ -37,11 +38,12 @@ final class FirewallConfig
3738
* @param string|null $provider
3839
* @param string|null $context
3940
* @param string|null $entryPoint
41+
* @param array|null $switchUser
4042
* @param string|null $accessDeniedHandler
4143
* @param string|null $accessDeniedUrl
4244
* @param string[] $listeners
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, $switchUser = null, $accessDeniedHandler = null, $accessDeniedUrl = null, $listeners = array())
4547
{
4648
$this->name = $name;
4749
$this->userChecker = $userChecker;
@@ -51,6 +53,7 @@ public function __construct($name, $userChecker, $requestMatcher = null, $securi
5153
$this->provider = $provider;
5254
$this->context = $context;
5355
$this->entryPoint = $entryPoint;
56+
$this->switchUser = $switchUser;
5457
$this->accessDeniedHandler = $accessDeniedHandler;
5558
$this->accessDeniedUrl = $accessDeniedUrl;
5659
$this->listeners = $listeners;
@@ -109,6 +112,14 @@ public function getEntryPoint()
109112
return $this->entryPoint;
110113
}
111114

115+
/**
116+
* @return array|null The switch_user config if enabled, null otherwise
117+
*/
118+
public function getSwitchUser()
119+
{
120+
return $this->switchUser;
121+
}
122+
112123
/**
113124
* @return string The user_checker service id
114125
*/

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php
+31Lines changed: 31 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,7 @@ public function testCollectWhenSecurityIsDisabled()
3132
$this->assertSame('security', $collector->getName());
3233
$this->assertFalse($collector->isEnabled());
3334
$this->assertFalse($collector->isAuthenticated());
35+
$this->assertFalse($collector->isImpersonated());
3436
$this->assertNull($collector->getTokenClass());
3537
$this->assertFalse($collector->supportsRoleHierarchy());
3638
$this->assertCount(0, $collector->getRoles());
@@ -47,6 +49,7 @@ public function testCollectWhenAuthenticationTokenIsNull()
4749

4850
$this->assertTrue($collector->isEnabled());
4951
$this->assertFalse($collector->isAuthenticated());
52+
$this->assertFalse($collector->isImpersonated());
5053
$this->assertNull($collector->getTokenClass());
5154
$this->assertTrue($collector->supportsRoleHierarchy());
5255
$this->assertCount(0, $collector->getRoles());
@@ -67,13 +70,41 @@ public function testCollectAuthenticationTokenAndRoles(array $roles, array $norm
6770

6871
$this->assertTrue($collector->isEnabled());
6972
$this->assertTrue($collector->isAuthenticated());
73+
$this->assertFalse($collector->isImpersonated());
7074
$this->assertSame('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $collector->getTokenClass()->getValue());
7175
$this->assertTrue($collector->supportsRoleHierarchy());
7276
$this->assertSame($normalizedRoles, $collector->getRoles()->getValue(true));
7377
$this->assertSame($inheritedRoles, $collector->getInheritedRoles()->getValue(true));
7478
$this->assertSame('hhamon', $collector->getUser());
7579
}
7680

81+
public function testCollectImpersonatedToken()
82+
{
83+
$adminToken = new UsernamePasswordToken('yceruto', 'P4$$w0rD', 'provider', array('ROLE_ADMIN'));
84+
85+
$userRoles = array(
86+
'ROLE_USER',
87+
new SwitchUserRole('ROLE_PREVIOUS_ADMIN', $adminToken),
88+
);
89+
90+
$tokenStorage = new TokenStorage();
91+
$tokenStorage->setToken(new UsernamePasswordToken('hhamon', 'P4$$w0rD', 'provider', $userRoles));
92+
93+
$collector = new SecurityDataCollector($tokenStorage, $this->getRoleHierarchy());
94+
$collector->collect($this->getRequest(), $this->getResponse());
95+
$collector->lateCollect();
96+
97+
$this->assertTrue($collector->isEnabled());
98+
$this->assertTrue($collector->isAuthenticated());
99+
$this->assertTrue($collector->isImpersonated());
100+
$this->assertSame('yceruto', $collector->getImpersonatedSourceUser());
101+
$this->assertSame('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $collector->getTokenClass()->getValue());
102+
$this->assertTrue($collector->supportsRoleHierarchy());
103+
$this->assertSame(array('ROLE_USER', 'ROLE_PREVIOUS_ADMIN'), $collector->getRoles()->getValue(true));
104+
$this->assertSame(array(), $collector->getInheritedRoles()->getValue(true));
105+
$this->assertSame('hhamon', $collector->getUser());
106+
}
107+
77108
public function testGetFirewall()
78109
{
79110
$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
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ public function testFirewalls()
9494
'security.user.provider.concrete.default',
9595
null,
9696
'security.authentication.form_entry_point.secure',
97+
array('parameter' => '_switch_user', 'role' => 'ROLE_ALLOWED_TO_SWITCH'),
9798
null,
9899
null,
99100
array(

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Tests/Security/FirewallConfigTest.php
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public function testGetters()
2626
'provider' => 'foo_provider',
2727
'context' => 'foo_context',
2828
'entry_point' => 'foo_entry_point',
29+
'switch_user' => array('provider' => null, 'parameter' => '_switch_user', 'role' => 'ROLE_ALLOWED_TO_SWITCH'),
2930
'access_denied_url' => 'foo_access_denied_url',
3031
'access_denied_handler' => 'foo_access_denied_handler',
3132
'user_checker' => 'foo_user_checker',
@@ -40,6 +41,7 @@ public function testGetters()
4041
$options['provider'],
4142
$options['context'],
4243
$options['entry_point'],
44+
$options['switch_user'],
4345
$options['access_denied_handler'],
4446
$options['access_denied_url'],
4547
$listeners
@@ -52,6 +54,7 @@ public function testGetters()
5254
$this->assertSame($options['provider'], $config->getProvider());
5355
$this->assertSame($options['context'], $config->getContext());
5456
$this->assertSame($options['entry_point'], $config->getEntryPoint());
57+
$this->assertSame($options['switch_user'], $config->getSwitchUser());
5558
$this->assertSame($options['access_denied_handler'], $config->getAccessDeniedHandler());
5659
$this->assertSame($options['access_denied_url'], $config->getAccessDeniedUrl());
5760
$this->assertSame($options['user_checker'], $config->getUserChecker());

0 commit comments

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