Skip to content

[DX] Added CurrentUserProvider service #14407

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 4 additions & 12 deletions src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ protected function addFlash($type, $message)
* @param mixed $object The object
*
* @throws \LogicException
*
* @return bool
*/
protected function isGranted($attributes, $object = null)
Expand Down Expand Up @@ -305,20 +306,11 @@ public function getDoctrine()
*/
public function getUser()
{
if (!$this->container->has('security.token_storage')) {
if (!$this->container->has('security.current_user_provider')) {
throw new \LogicException('The SecurityBundle is not registered in your application.');
}

if (null === $token = $this->container->get('security.token_storage')->getToken()) {
return;
}

if (!is_object($user = $token->getUser())) {
// e.g. anonymous authentication
return;
}

return $user;
return $this->container->get('security.current_user_provider')->getUser();
}

/**
Expand Down Expand Up @@ -362,7 +354,7 @@ protected function getParameter($name)
}

/**
* Checks the validity of a CSRF token
* Checks the validity of a CSRF token.
*
* @param string $id The id used when generating the token
* @param string $token The actual token sent with the request that should be validated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,9 @@

use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\User\User;

class ControllerTest extends TestCase
{
Expand Down Expand Up @@ -50,31 +46,30 @@ public function testForward()

public function testGetUser()
{
$user = new User('user', 'pass');
$token = new UsernamePasswordToken($user, 'pass', 'default', array('ROLE_USER'));

$controller = new TestController();
$controller->setContainer($this->getContainerWithTokenStorage($token));

$this->assertSame($controller->getUser(), $user);
}

public function testGetUserAnonymousUserConvertedToNull()
{
$token = new AnonymousToken('default', 'anon.');
$currentUserProvider = $this->getMockBuilder('Symfony\Component\Security\Core\User\CurrentUserProvider')
->disableOriginalConstructor()
->getMock();
$currentUserProvider
->expects($this->once())
->method('getUser');

$controller = new TestController();
$controller->setContainer($this->getContainerWithTokenStorage($token));
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
$container
->expects($this->once())
->method('has')
->with('security.current_user_provider')
->will($this->returnValue(true));

$this->assertNull($controller->getUser());
}
$container
->expects($this->once())
->method('get')
->with('security.current_user_provider')
->will($this->returnValue($currentUserProvider));

public function testGetUserWithEmptyTokenStorage()
{
$controller = new TestController();
$controller->setContainer($this->getContainerWithTokenStorage(null));
$controller->setContainer($container);

$this->assertNull($controller->getUser());
$controller->getUser();
}

/**
Expand All @@ -87,42 +82,14 @@ public function testGetUserWithEmptyContainer()
$container
->expects($this->once())
->method('has')
->with('security.token_storage')
->with('security.current_user_provider')
->will($this->returnValue(false));

$controller = new TestController();
$controller->setContainer($container);

$controller->getUser();
}

/**
* @param $token
* @return ContainerInterface
*/
private function getContainerWithTokenStorage($token = null)
{
$tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage');
$tokenStorage
->expects($this->once())
->method('getToken')
->will($this->returnValue($token));

$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
$container
->expects($this->once())
->method('has')
->with('security.token_storage')
->will($this->returnValue(true));

$container
->expects($this->once())
->method('get')
->with('security.token_storage')
->will($this->returnValue($tokenStorage));

return $container;
}
}

class TestController extends Controller
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@
<argument type="service" id="request_stack" />
</service>

<service id="security.current_user_provider" class="Symfony\Component\Security\Core\User\CurrentUserProvider">
<argument type="service" id="security.token_storage" />
</service>

<!-- Authorization related services -->
<service id="security.access.decision_manager" class="%security.access.decision_manager.class%" public="false">
<argument type="collection" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Security\Core\Tests\User;

use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\User\CurrentUserProvider;
use Symfony\Component\Security\Core\User\User;

class CurrentUserProviderTest extends \PHPUnit_Framework_TestCase
{
public function testGetUser()
{
$user = new User('user', 'pass');
$token = new UsernamePasswordToken($user, 'pass', 'default', array('ROLE_USER'));

$service = new CurrentUserProvider($this->getTokenStorage($token));

$this->assertSame($service->getUser(), $user);
}

public function testGetUserAnonymousUserConvertedToNull()
{
$token = new AnonymousToken('default', 'anon.');

$service = new CurrentUserProvider($this->getTokenStorage($token));

$this->assertNull($service->getUser());
}

public function testGetUserWithEmptyTokenStorage()
{
$service = new CurrentUserProvider($this->getTokenStorage(null));

$this->assertNull($service->getUser());
}

/**
* @param $token
*
* @return TokenStorageInterface
*/
private function getTokenStorage($token = null)
{
$tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage');
$tokenStorage
->expects($this->once())
->method('getToken')
->will($this->returnValue($token));

return $tokenStorage;
}
}
49 changes: 49 additions & 0 deletions src/Symfony/Component/Security/Core/User/CurrentUserProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace Symfony\Component\Security\Core\User;

use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

/**
* Current User Provider.
*
* This provider gives you the current logged in user.
*
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
class CurrentUserProvider
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if it's a good idea to add a class just to wrap the TokenStorage.

In my opinion, the token/user shouldn't be called in your application controller, they should only be used to identify the current user and can be used to fetch the information of the current user.

Next to all this, there is already a PR open in the sensio framework-extra-bundle repo regarding a ParamConverter to inject the current user. This is a cleaner solution in my opinion: sensiolabs/SensioFrameworkExtraBundle/pull/327

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not aware of that PR on SensiolabsFrameworkExtraBundle. But that is just for Controllers. This CurrentUserProvider service is meant to make it easier to get the logged in user in your other services.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion you should not bring that state to your services. You'll still need to check if the user is there and act on it. I think it's a far better solution to just inject the user if you need it in the method that you call. If you really need the user in a deep level of your services, then maybe you shouldn't be using the UserInterface for that.

public function someAction(UserInterface $user)
{
    $this->someService($user);
}

Right now you're adding a class that does nothing more than removing some boiler plating code. It also makes services harder to use on the commandline. The current name CurrentUserProvider also implies that the service has a state, which it shouldn't have.

I have a very big 👎 for this class as there are already other (better) solutions available.

{
/**
* @var TokenStorageInterface tokenStorage
*/
private $tokenStorage;

/**
* @param TokenStorageInterface $tokenStorage
*/
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}

/**
* Get a user from the Security Token Storage.
*
* @return mixed
*
* @see TokenInterface::getUser()
*/
public function getUser()
{
if (null === $token = $this->tokenStorage->getToken()) {
return;
}

if (!is_object($user = $token->getUser())) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-1 on this logic, even if user is not a string, it should return it. anon. is a user too.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep a user is not necessarily represented by an object, it depend of implementation of token. (for example UsernamePasswordToken accept string representation of user to handle InMemoryUserProvider).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that some user representation might be a string. This PR is a refactor of the FrameworkBundle's Controller::getUser. I've just put it to a service.
The CurrentUserProvider::getUser function do have the same logic as in the FramworkBundle

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a mistake from FrameworkBundle so, this is why when we retrieve the current user from different way we obtains 'anon.' or null.

// e.g. anonymous authentication
return;
}

return $user;
}
}
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy