feature #24337 Adding a shortcuts for the main security functionality (weaverryan, javiereguiluz)
This PR was squashed before being merged into the 3.4 branch (closes #24337).
Discussion
----------
Adding a shortcuts for the main security functionality
| Q | A
| ------------- | ---
| Branch? | 3.4
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | none
| License | MIT
| Doc PR | Big ol' TODO
I'd like one class that I can inject (especially with autowiring) to get access to the User and `isGranted()` methods. This is *really* important... because to get the User currently, you need to type-hint `TokenStorageInterface`... and there are *two*! That's really bad DX!
Questions:
A) I hi-jacked the existing `Security` class... I wanted a simple class called Security
B) I called the service `security.helper`... for lack of a better id.
C) I did not make `Security` implement the 2 other interfaces (`TokenStorageInterface`, `AuthorizationCheckerInterface`... but I suppose we could?)
Cheers!
Commits
-------
0851189
Adding a shortcuts for the main security functionality
This commit is contained in:
commit
3b5742e6b5
@ -4,6 +4,8 @@ CHANGELOG
|
||||
3.4.0
|
||||
-----
|
||||
|
||||
* Added new `security.helper` service that is an instance of `Symfony\Component\Security\Core\Security`
|
||||
and provides shortcuts for common security tasks.
|
||||
* Tagging voters with the `security.voter` tag without implementing the
|
||||
`VoterInterface` on the class is now deprecated and will be removed in 4.0.
|
||||
* [BC BREAK] `FirewallContext::getListeners()` now returns `\Traversable|array`
|
||||
|
@ -26,6 +26,19 @@
|
||||
</service>
|
||||
<service id="Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface" alias="security.token_storage" />
|
||||
|
||||
<service id="security.helper" class="Symfony\Component\Security\Core\Security">
|
||||
<argument type="service">
|
||||
<service class="Symfony\Component\DependencyInjection\ServiceLocator">
|
||||
<tag name="container.service_locator" />
|
||||
<argument type="collection">
|
||||
<argument key="security.token_storage" type="service" id="security.token_storage" />
|
||||
<argument key="security.authorization_checker" type="service" id="security.authorization_checker" />
|
||||
</argument>
|
||||
</service>
|
||||
</argument>
|
||||
</service>
|
||||
<service id="Symfony\Component\Security\Core\Security" alias="security.helper" />
|
||||
|
||||
<service id="security.user_value_resolver" class="Symfony\Bundle\SecurityBundle\SecurityUserValueResolver">
|
||||
<argument type="service" id="security.token_storage" />
|
||||
<tag name="controller.argument_value_resolver" priority="40" />
|
||||
|
@ -0,0 +1,34 @@
|
||||
<?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\Bundle\SecurityBundle\Tests\Functional;
|
||||
|
||||
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
|
||||
use Symfony\Component\Security\Core\User\User;
|
||||
|
||||
class SecurityTest extends WebTestCase
|
||||
{
|
||||
public function testServiceIsFunctional()
|
||||
{
|
||||
$kernel = self::createKernel(array('test_case' => 'SecurityHelper', 'root_config' => 'config.yml'));
|
||||
$kernel->boot();
|
||||
$container = $kernel->getContainer();
|
||||
|
||||
// put a token into the storage so the final calls can function
|
||||
$user = new User('foo', 'pass');
|
||||
$token = new UsernamePasswordToken($user, '', 'provider', array('ROLE_USER'));
|
||||
$container->get('security.token_storage')->setToken($token);
|
||||
|
||||
$security = $container->get('functional_test.security.helper');
|
||||
$this->assertTrue($security->isGranted('ROLE_USER'));
|
||||
$this->assertSame($token, $security->getToken());
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
use Symfony\Bundle\TwigBundle\TwigBundle;
|
||||
use Symfony\Bundle\SecurityBundle\SecurityBundle;
|
||||
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
|
||||
|
||||
return array(
|
||||
new FrameworkBundle(),
|
||||
new SecurityBundle(),
|
||||
new TwigBundle(),
|
||||
);
|
@ -0,0 +1,18 @@
|
||||
imports:
|
||||
- { resource: ./../config/default.yml }
|
||||
|
||||
services:
|
||||
# alias the service so we can access it in the tests
|
||||
functional_test.security.helper:
|
||||
alias: security.helper
|
||||
public: true
|
||||
|
||||
security:
|
||||
providers:
|
||||
in_memory:
|
||||
memory:
|
||||
users: []
|
||||
|
||||
firewalls:
|
||||
default:
|
||||
anonymous: ~
|
@ -4,6 +4,7 @@ CHANGELOG
|
||||
3.4.0
|
||||
-----
|
||||
|
||||
* Added `getUser`, `getToken` and `isGranted` methods to `Security`.
|
||||
* added a `setToken()` method to the `SwitchUserEvent` class to allow to replace the created token while switching users
|
||||
when custom token generation is required by application.
|
||||
* Using voters that do not implement the `VoterInterface`is now deprecated in
|
||||
|
@ -11,10 +11,12 @@
|
||||
|
||||
namespace Symfony\Component\Security\Core;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
/**
|
||||
* This class holds security information.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
* Helper class for commonly-needed security tasks.
|
||||
*/
|
||||
final class Security
|
||||
{
|
||||
@ -22,4 +24,50 @@ final class Security
|
||||
const AUTHENTICATION_ERROR = '_security.last_error';
|
||||
const LAST_USERNAME = '_security.last_username';
|
||||
const MAX_USERNAME_LENGTH = 4096;
|
||||
|
||||
private $container;
|
||||
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return UserInterface|null
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
if (!$token = $this->getToken()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$user = $token->getUser();
|
||||
if (!is_object($user)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the attributes are granted against the current authentication token and optionally supplied subject.
|
||||
*
|
||||
* @param mixed $attributes
|
||||
* @param mixed $subject
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isGranted($attributes, $subject = null)
|
||||
{
|
||||
return $this->container->get('security.authorization_checker')
|
||||
->isGranted($attributes, $subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TokenInterface|null
|
||||
*/
|
||||
public function getToken()
|
||||
{
|
||||
return $this->container->get('security.token_storage')->getToken();
|
||||
}
|
||||
}
|
||||
|
97
src/Symfony/Component/Security/Core/Tests/SecurityTest.php
Normal file
97
src/Symfony/Component/Security/Core/Tests/SecurityTest.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?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;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Security\Core\User\User;
|
||||
|
||||
class SecurityTest extends TestCase
|
||||
{
|
||||
public function testGetToken()
|
||||
{
|
||||
$token = new UsernamePasswordToken('foo', 'bar', 'provider');
|
||||
$tokenStorage = $this->getMockBuilder(TokenStorageInterface::class)->getMock();
|
||||
|
||||
$tokenStorage->expects($this->once())
|
||||
->method('getToken')
|
||||
->will($this->returnValue($token));
|
||||
|
||||
$container = $this->createContainer('security.token_storage', $tokenStorage);
|
||||
|
||||
$security = new Security($container);
|
||||
$this->assertSame($token, $security->getToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getUserTests
|
||||
*/
|
||||
public function testGetUser($userInToken, $expectedUser)
|
||||
{
|
||||
$token = $this->getMockBuilder(TokenInterface::class)->getMock();
|
||||
$token->expects($this->any())
|
||||
->method('getUser')
|
||||
->will($this->returnValue($userInToken));
|
||||
$tokenStorage = $this->getMockBuilder(TokenStorageInterface::class)->getMock();
|
||||
|
||||
$tokenStorage->expects($this->once())
|
||||
->method('getToken')
|
||||
->will($this->returnValue($token));
|
||||
|
||||
$container = $this->createContainer('security.token_storage', $tokenStorage);
|
||||
|
||||
$security = new Security($container);
|
||||
$this->assertSame($expectedUser, $security->getUser());
|
||||
}
|
||||
|
||||
public function getUserTests()
|
||||
{
|
||||
yield array(null, null);
|
||||
|
||||
yield array('string_username', null);
|
||||
|
||||
$user = new User('nice_user', 'foo');
|
||||
yield array($user, $user);
|
||||
}
|
||||
|
||||
public function testIsGranted()
|
||||
{
|
||||
$authorizationChecker = $this->getMockBuilder(AuthorizationCheckerInterface::class)->getMock();
|
||||
|
||||
$authorizationChecker->expects($this->once())
|
||||
->method('isGranted')
|
||||
->with('SOME_ATTRIBUTE', 'SOME_SUBJECT')
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$container = $this->createContainer('security.authorization_checker', $authorizationChecker);
|
||||
|
||||
$security = new Security($container);
|
||||
$this->assertTrue($security->isGranted('SOME_ATTRIBUTE', 'SOME_SUBJECT'));
|
||||
}
|
||||
|
||||
private function createContainer($serviceId, $serviceObject)
|
||||
{
|
||||
$container = $this->getMockBuilder(ContainerInterface::class)->getMock();
|
||||
|
||||
$container->expects($this->atLeastOnce())
|
||||
->method('get')
|
||||
->with($serviceId)
|
||||
->will($this->returnValue($serviceObject));
|
||||
|
||||
return $container;
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@
|
||||
"symfony/polyfill-php56": "~1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/container": "^1.0",
|
||||
"symfony/event-dispatcher": "~2.8|~3.0|~4.0",
|
||||
"symfony/expression-language": "~2.8|~3.0|~4.0",
|
||||
"symfony/http-foundation": "~2.8|~3.0|~4.0",
|
||||
@ -28,6 +29,7 @@
|
||||
"psr/log": "~1.0"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/container": "To instantiate the Security class",
|
||||
"symfony/event-dispatcher": "",
|
||||
"symfony/http-foundation": "",
|
||||
"symfony/validator": "For using the user password constraint",
|
||||
|
@ -32,6 +32,7 @@
|
||||
"symfony/security-http": "self.version"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/container": "^1.0",
|
||||
"symfony/finder": "~2.8|~3.0|~4.0",
|
||||
"symfony/polyfill-intl-icu": "~1.0",
|
||||
"symfony/routing": "~2.8|~3.0|~4.0",
|
||||
@ -41,6 +42,7 @@
|
||||
"psr/log": "~1.0"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/container": "To instantiate the Security class",
|
||||
"symfony/form": "",
|
||||
"symfony/validator": "For using the user password constraint",
|
||||
"symfony/routing": "For using the HttpUtils class to create sub-requests, redirect the user, and match URLs",
|
||||
|
Reference in New Issue
Block a user