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 d341889

Browse filesBrowse files
author
Iltar van der Berg
committed
Added a SecurityUserValueResolver for controllers
1 parent 2e811cb commit d341889
Copy full SHA for d341889

File tree

Expand file treeCollapse file tree

13 files changed

+206
-7
lines changed
Filter options
Expand file treeCollapse file tree

13 files changed

+206
-7
lines changed

‎UPGRADE-3.2.md

Copy file name to clipboardExpand all lines: UPGRADE-3.2.md
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
UPGRADE FROM 3.1 to 3.2
22
=======================
33

4+
FrameworkBundle
5+
---------------
6+
7+
* The `Controller::getUser()` method has been deprecated and will be removed in
8+
Symfony 4.0; typehint the security user object in the action instead.
9+
410
DependencyInjection
511
-------------------
612

‎UPGRADE-4.0.md

Copy file name to clipboardExpand all lines: UPGRADE-4.0.md
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ FrameworkBundle
117117
* The `framework.serializer.cache` option and the services
118118
`serializer.mapping.cache.apc` and `serializer.mapping.cache.doctrine.apc`
119119
have been removed. APCu should now be automatically used when available.
120+
121+
* The `Controller::getUser()` method has been removed in favor of the ability
122+
to typehint the security user object in the action.
120123

121124
HttpKernel
122125
----------

‎src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
CHANGELOG
22
=========
33

4+
3.2.0
5+
-----
6+
7+
* The `Controller::getUser()` method has been deprecated and will be removed in
8+
Symfony 4.0; typehint the security user object in the action instead.
9+
410
3.1.0
511
-----
612

‎src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
2323
use Symfony\Component\HttpKernel\HttpKernelInterface;
2424
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
25+
use Symfony\Component\Security\Core\User\UserInterface;
2526
use Symfony\Component\Security\Csrf\CsrfToken;
2627
use Symfony\Component\Form\Extension\Core\Type\FormType;
2728
use Symfony\Component\Form\Form;
@@ -362,12 +363,16 @@ protected function getDoctrine()
362363
*
363364
* @return mixed
364365
*
366+
* @deprecated as of 3.2 and will be removed in 4.0. You can typehint your method argument with Symfony\Component\Security\Core\User\UserInterface instead.
367+
*
365368
* @throws \LogicException If SecurityBundle is not available
366369
*
367370
* @see TokenInterface::getUser()
368371
*/
369372
protected function getUser()
370373
{
374+
@trigger_error(sprintf('%s() is deprecated as of 3.2 and will be removed in 4.0. You can typehint your method argument with %s instead.', __METHOD__, UserInterface::class), E_USER_DEPRECATED);
375+
371376
if (!$this->container->has('security.token_storage')) {
372377
throw new \LogicException('The SecurityBundle is not registered in your application.');
373378
}

‎src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ public function testForward()
5656
$this->assertEquals('xml--fr', $response->getContent());
5757
}
5858

59+
/**
60+
* @group legacy
61+
*/
5962
public function testGetUser()
6063
{
6164
$user = new User('user', 'pass');
@@ -67,6 +70,9 @@ public function testGetUser()
6770
$this->assertSame($controller->getUser(), $user);
6871
}
6972

73+
/**
74+
* @group legacy
75+
*/
7076
public function testGetUserAnonymousUserConvertedToNull()
7177
{
7278
$token = new AnonymousToken('default', 'anon.');
@@ -77,6 +83,9 @@ public function testGetUserAnonymousUserConvertedToNull()
7783
$this->assertNull($controller->getUser());
7884
}
7985

86+
/**
87+
* @group legacy
88+
*/
8089
public function testGetUserWithEmptyTokenStorage()
8190
{
8291
$controller = new TestController();
@@ -86,6 +95,7 @@ public function testGetUserWithEmptyTokenStorage()
8695
}
8796

8897
/**
98+
* @group legacy
8999
* @expectedException \LogicException
90100
* @expectedExceptionMessage The SecurityBundle is not registered in your application.
91101
*/

‎src/Symfony/Bundle/SecurityBundle/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/CHANGELOG.md
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
CHANGELOG
22
=========
33

4+
3.2.0
5+
-----
6+
7+
* Added the `SecurityUserValueResolver` to inject the security users in actions via
8+
`Symfony\Component\Security\Core\User\UserInterface` in the method signature.
9+
410
3.0.0
511
-----
612

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020

2121
<service id="security.token_storage" class="Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage" />
2222

23+
<service id="security.user_value_resolver" class="Symfony\Bundle\SecurityBundle\SecurityUserValueResolver" public="false">
24+
<argument type="service" id="security.token_storage" />
25+
<tag name="controller.argument_value_resolver" priority="40" />
26+
</service>
27+
2328
<!-- Authentication related services -->
2429
<service id="security.authentication.manager" class="Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager" public="false">
2530
<argument type="collection" />
+57Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle;
13+
14+
use Symfony\Component\HttpFoundation\Request;
15+
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
16+
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
17+
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
18+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
19+
use Symfony\Component\Security\Core\User\UserInterface;
20+
21+
/**
22+
* Supports the argument type of {@see UserInterface}.
23+
*
24+
* @author Iltar van der Berg <kjarli@gmail.com>
25+
*/
26+
final class SecurityUserValueResolver implements ArgumentValueResolverInterface
27+
{
28+
private $tokenStorage;
29+
30+
public function __construct(TokenStorageInterface $tokenStorage)
31+
{
32+
$this->tokenStorage = $tokenStorage;
33+
}
34+
35+
public function supports(Request $request, ArgumentMetadata $argument)
36+
{
37+
// only security user implementations are supported
38+
if (UserInterface::class !== $argument->getType()) {
39+
return false;
40+
}
41+
42+
$token = $this->tokenStorage->getToken();
43+
if (!$token instanceof TokenInterface) {
44+
return false;
45+
}
46+
47+
$user = $token->getUser();
48+
49+
// in case it's not an object we cannot do anything with it; E.g. "anon."
50+
return $user instanceof UserInterface;
51+
}
52+
53+
public function resolve(Request $request, ArgumentMetadata $argument)
54+
{
55+
yield $this->tokenStorage->getToken()->getUser();
56+
}
57+
}

‎src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LoginController.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LoginController.php
+4-3Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@
1717
use Symfony\Component\HttpFoundation\Request;
1818
use Symfony\Component\HttpFoundation\Response;
1919
use Symfony\Component\Security\Core\Security;
20+
use Symfony\Component\Security\Core\User\UserInterface;
2021

2122
class LoginController implements ContainerAwareInterface
2223
{
2324
use ContainerAwareTrait;
2425

25-
public function loginAction(Request $request)
26+
public function loginAction(Request $request, UserInterface $user = null)
2627
{
2728
// get the login error if there is one
2829
if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) {
@@ -38,9 +39,9 @@ public function loginAction(Request $request)
3839
));
3940
}
4041

41-
public function afterLoginAction()
42+
public function afterLoginAction(UserInterface $user)
4243
{
43-
return $this->container->get('templating')->renderResponse('FormLoginBundle:Login:after_login.html.twig');
44+
return $this->container->get('templating')->renderResponse('FormLoginBundle:Login:after_login.html.twig', array('user' => $user));
4445
}
4546

4647
public function loginCheckAction()

‎src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/after_login.html.twig

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/views/Login/after_login.html.twig
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{% extends "::base.html.twig" %}
22

33
{% block body %}
4-
Hello {{ app.user.username }}!<br /><br />
4+
Hello {{ user.username }}!<br /><br />
55
You're browsing to path "{{ app.request.pathInfo }}".
66

77
<a href="{{ logout_path('default') }}">Log out</a>.
+100Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\Tests;
13+
14+
use Symfony\Bundle\SecurityBundle\SecurityUserValueResolver;
15+
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
17+
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\DefaultValueResolver;
18+
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
19+
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
20+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
21+
use Symfony\Component\Security\Core\User\UserInterface;
22+
23+
class SecurityUserValueResolverTest extends \PHPUnit_Framework_TestCase
24+
{
25+
public function testResolveNoToken()
26+
{
27+
$tokenStorage = new TokenStorage();
28+
$resolver = new SecurityUserValueResolver($tokenStorage);
29+
$metadata = new ArgumentMetadata('foo', UserInterface::class, false, false, null);
30+
31+
$this->assertFalse($resolver->supports(Request::create('/'), $metadata));
32+
}
33+
34+
public function testResolveNoUser()
35+
{
36+
$mock = $this->getMock(UserInterface::class);
37+
$token = $this->getMock(TokenInterface::class);
38+
$tokenStorage = new TokenStorage();
39+
$tokenStorage->setToken($token);
40+
41+
$resolver = new SecurityUserValueResolver($tokenStorage);
42+
$metadata = new ArgumentMetadata('foo', get_class($mock), false, false, null);
43+
44+
$this->assertFalse($resolver->supports(Request::create('/'), $metadata));
45+
}
46+
47+
public function testResolveWrongType()
48+
{
49+
$tokenStorage = new TokenStorage();
50+
$resolver = new SecurityUserValueResolver($tokenStorage);
51+
$metadata = new ArgumentMetadata('foo', null, false, false, null);
52+
53+
$this->assertFalse($resolver->supports(Request::create('/'), $metadata));
54+
}
55+
56+
public function testResolve()
57+
{
58+
$user = $this->getMock(UserInterface::class);
59+
$token = $this->getMock(TokenInterface::class);
60+
$token->expects($this->any())->method('getUser')->willReturn($user);
61+
$tokenStorage = new TokenStorage();
62+
$tokenStorage->setToken($token);
63+
64+
$resolver = new SecurityUserValueResolver($tokenStorage);
65+
$metadata = new ArgumentMetadata('foo', UserInterface::class, false, false, null);
66+
67+
$this->assertTrue($resolver->supports(Request::create('/'), $metadata));
68+
$this->assertSame(array($user), iterator_to_array($resolver->resolve(Request::create('/'), $metadata)));
69+
}
70+
71+
public function testIntegration()
72+
{
73+
$user = $this->getMock(UserInterface::class);
74+
$token = $this->getMock(TokenInterface::class);
75+
$token->expects($this->any())->method('getUser')->willReturn($user);
76+
$tokenStorage = new TokenStorage();
77+
$tokenStorage->setToken($token);
78+
79+
$argumentResolver = new ArgumentResolver(null, array(new SecurityUserValueResolver($tokenStorage)));
80+
$this->assertSame(array($user), $argumentResolver->getArguments(Request::create('/'), function (UserInterface $user) {}));
81+
}
82+
83+
public function testIntegrationNoUser()
84+
{
85+
$token = $this->getMock(TokenInterface::class);
86+
$tokenStorage = new TokenStorage();
87+
$tokenStorage->setToken($token);
88+
89+
$argumentResolver = new ArgumentResolver(null, array(new SecurityUserValueResolver($tokenStorage), new DefaultValueResolver()));
90+
$this->assertSame(array(null), $argumentResolver->getArguments(Request::create('/'), function (UserInterface $user = null) {}));
91+
}
92+
}
93+
94+
abstract class DummyUser implements UserInterface
95+
{
96+
}
97+
98+
abstract class DummySubUser extends DummyUser
99+
{
100+
}

‎src/Symfony/Bundle/SecurityBundle/composer.json

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/composer.json
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"require": {
1919
"php": ">=5.5.9",
2020
"symfony/security": "~3.1,>=3.1.2",
21-
"symfony/http-kernel": "~2.8|~3.0",
21+
"symfony/http-kernel": "~3.1",
2222
"symfony/polyfill-php70": "~1.0"
2323
},
2424
"require-dev": {
@@ -27,7 +27,7 @@
2727
"symfony/css-selector": "~2.8|~3.0",
2828
"symfony/dom-crawler": "~2.8|~3.0",
2929
"symfony/form": "~2.8|~3.0",
30-
"symfony/framework-bundle": "~2.8|~3.0",
30+
"symfony/framework-bundle": "~3.1",
3131
"symfony/http-foundation": "~2.8|~3.0",
3232
"symfony/security-acl": "~2.8|~3.0",
3333
"symfony/twig-bundle": "~2.8|~3.0",

‎src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestValueResolver.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestValueResolver.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ final class RequestValueResolver implements ArgumentValueResolverInterface
2727
*/
2828
public function supports(Request $request, ArgumentMetadata $argument)
2929
{
30-
return $argument->getType() === Request::class || is_subclass_of($argument->getType(), Request::class);
30+
return Request::class === $argument->getType() || is_subclass_of($argument->getType(), Request::class);
3131
}
3232

3333
/**

0 commit comments

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