Merge branch '3.4'

* 3.4:
  Adding a shortcuts for the main security functionality
  [DI] Reference tagged services in config
This commit is contained in:
Nicolas Grekas 2017-09-28 17:44:33 +02:00
commit 3fde0f0644
34 changed files with 480 additions and 16 deletions

View File

@ -17,6 +17,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`

View File

@ -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" />

View File

@ -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());
}
}

View File

@ -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(),
);

View File

@ -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: ~

View File

@ -0,0 +1,37 @@
<?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\DependencyInjection\Argument;
/**
* Represents a collection of services found by tag name to lazily iterate over.
*
* @author Roland Franssen <franssen.roland@gmail.com>
*/
class TaggedIteratorArgument extends IteratorArgument
{
private $tag;
/**
* @param string $tag
*/
public function __construct($tag)
{
parent::__construct(array());
$this->tag = (string) $tag;
}
public function getTag()
{
return $this->tag;
}
}

View File

@ -35,6 +35,7 @@ CHANGELOG
* deprecated support for top-level anonymous services in XML
* deprecated case insensitivity of parameter names
* deprecated the `ResolveDefinitionTemplatesPass` class in favor of `ResolveChildDefinitionsPass`
* added `TaggedIteratorArgument` with YAML (`!tagged foo`) and XML (`<service type="tagged"/>`) support
3.3.0
-----

View File

@ -60,6 +60,7 @@ class PassConfig
new AutowireRequiredMethodsPass(),
new ResolveBindingsPass(),
new AutowirePass(false),
new ResolveTaggedIteratorArgumentPass(),
new ResolveServiceSubscribersPass(),
new ResolveReferencesToAliasesPass(),
new ResolveInvalidReferencesPass(),

View File

@ -0,0 +1,38 @@
<?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\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
/**
* Resolves all TaggedIteratorArgument arguments.
*
* @author Roland Franssen <franssen.roland@gmail.com>
*/
class ResolveTaggedIteratorArgumentPass extends AbstractRecursivePass
{
use PriorityTaggedServiceTrait;
/**
* {@inheritdoc}
*/
protected function processValue($value, $isRoot = false)
{
if (!$value instanceof TaggedIteratorArgument) {
return parent::processValue($value, $isRoot);
}
$value->setValues($this->findAndSortTaggedServices($value->getTag(), $this->container));
return $value;
}
}

View File

@ -13,6 +13,7 @@ namespace Symfony\Component\DependencyInjection\Dumper;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\Reference;
@ -291,6 +292,9 @@ class XmlDumper extends Dumper
if (is_array($value)) {
$element->setAttribute('type', 'collection');
$this->convertParameters($value, $type, $element, 'key');
} elseif ($value instanceof TaggedIteratorArgument) {
$element->setAttribute('type', 'tagged');
$element->setAttribute('tag', $value->getTag());
} elseif ($value instanceof IteratorArgument) {
$element->setAttribute('type', 'iterator');
$this->convertParameters($value->getValues(), $type, $element, 'key');

View File

@ -19,6 +19,7 @@ use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Parameter;
@ -255,6 +256,9 @@ class YamlDumper extends Dumper
$value = $value->getValues()[0];
}
if ($value instanceof ArgumentInterface) {
if ($value instanceof TaggedIteratorArgument) {
return new TaggedValue('tagged', $value->getTag());
}
if ($value instanceof IteratorArgument) {
$tag = 'iterator';
} else {

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
@ -115,6 +116,18 @@ function iterator(array $values)
return new IteratorArgument(AbstractConfigurator::processValue($values, true));
}
/**
* Creates a lazy iterator by tag name.
*
* @param string $tag
*
* @return TaggedIteratorArgument
*/
function tagged($tag)
{
return new TaggedIteratorArgument($tag);
}
/**
* Creates an expression.
*

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\Config\Util\XmlUtils;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
@ -500,6 +501,12 @@ class XmlFileLoader extends FileLoader
throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="iterator" only accepts collections of type="service" references in "%s".', $name, $file));
}
break;
case 'tagged':
if (!$arg->getAttribute('tag')) {
throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="tagged" has no or empty "tag" attribute in "%s".', $name, $file));
}
$arguments[$key] = new TaggedIteratorArgument($arg->getAttribute('tag'));
break;
case 'string':
$arguments[$key] = $arg->nodeValue;
break;

View File

@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
@ -708,6 +709,13 @@ class YamlFileLoader extends FileLoader
throw new InvalidArgumentException(sprintf('"!iterator" tag only accepts arrays of "@service" references in "%s".', $file));
}
}
if ('tagged' === $value->getTag()) {
if (!is_string($argument) || !$argument) {
throw new InvalidArgumentException(sprintf('"!tagged" tag only accepts non empty string in "%s".', $file));
}
return new TaggedIteratorArgument($argument);
}
if ('service' === $value->getTag()) {
if ($isParameter) {
throw new InvalidArgumentException(sprintf('Using an anonymous service in a parameter is not allowed in "%s".', $file));

View File

@ -206,6 +206,7 @@
<xsd:attribute name="key" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="on-invalid" type="invalid_sequence" />
<xsd:attribute name="tag" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="bind" mixed="true">
@ -230,6 +231,7 @@
<xsd:attribute name="key" type="xsd:string" />
<xsd:attribute name="index" type="xsd:integer" />
<xsd:attribute name="on-invalid" type="invalid_sequence" />
<xsd:attribute name="tag" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="call">
@ -255,6 +257,7 @@
<xsd:enumeration value="string" />
<xsd:enumeration value="constant" />
<xsd:enumeration value="iterator" />
<xsd:enumeration value="tagged" />
</xsd:restriction>
</xsd:simpleType>

View File

@ -0,0 +1,40 @@
<?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\DependencyInjection\Tests\Compiler;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\Compiler\ResolveTaggedIteratorArgumentPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* @author Roland Franssen <franssen.roland@gmail.com>
*/
class ResolveTaggedIteratorArgumentPassTest extends TestCase
{
public function testProcess()
{
$container = new ContainerBuilder();
$container->register('a', 'stdClass')->addTag('foo');
$container->register('b', 'stdClass')->addTag('foo', array('priority' => 20));
$container->register('c', 'stdClass')->addTag('foo', array('priority' => 10));
$container->register('d', 'stdClass')->setProperty('foos', new TaggedIteratorArgument('foo'));
(new ResolveTaggedIteratorArgumentPass())->process($container);
$properties = $container->getDefinition('d')->getProperties();
$expected = new TaggedIteratorArgument('foo');
$expected->setValues(array(new Reference('b'), new Reference('c'), new Reference('a')));
$this->assertEquals($expected, $properties['foos']);
}
}

View File

@ -5,9 +5,7 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use App\BarService;
return function (ContainerConfigurator $c) {
$s = $c->services();
$s->set(BarService::class)
->args(array(inline('FooClass')));
};

View File

@ -5,7 +5,6 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use App\BarService;
return function (ContainerConfigurator $c) {
$c->services()
->set('bar', 'Class1')
->set(BarService::class)
@ -20,5 +19,4 @@ return function (ContainerConfigurator $c) {
->parent('bar')
->parent(BarService::class)
;
};

View File

@ -5,7 +5,6 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo;
return function (ContainerConfigurator $c) {
$c->import('basic.php');
$s = $c->services()->defaults()
@ -19,5 +18,4 @@ return function (ContainerConfigurator $c) {
$s->set(Foo::class)->args(array(ref('bar')))->public();
$s->set('bar', Foo::class)->call('setFoo')->autoconfigure(false);
};

View File

@ -6,7 +6,6 @@ use App\FooService;
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype;
return function (ContainerConfigurator $c) {
$s = $c->services();
$s->instanceof(Prototype\Foo::class)
->property('p', 0)
@ -20,5 +19,4 @@ return function (ContainerConfigurator $c) {
$s->load(Prototype::class.'\\', '../Prototype')->exclude('../Prototype/*/*');
$s->set('foo', FooService::class);
};

View File

@ -5,7 +5,6 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo;
return function (ContainerConfigurator $c) {
$c->parameters()
('foo', 'Foo')
('bar', 'Bar')
@ -17,5 +16,4 @@ return function (ContainerConfigurator $c) {
('bar', Foo::class)
->call('setFoo')
;
};

View File

@ -5,7 +5,6 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype;
return function (ContainerConfigurator $c) {
$di = $c->services()->defaults()
->tag('baz');
$di->load(Prototype::class.'\\', '../Prototype')
@ -20,5 +19,4 @@ return function (ContainerConfigurator $c) {
->parent('foo');
$di->set('foo')->lazy()->abstract();
$di->get(Prototype\Foo::class)->lazy(false);
};

View File

@ -9,7 +9,6 @@ require_once __DIR__.'/../includes/classes.php';
require_once __DIR__.'/../includes/foo.php';
return function (ContainerConfigurator $c) {
$p = $c->parameters();
$p->set('baz_class', 'BazClass');
$p->set('foo_class', FooClass::class)
@ -124,4 +123,11 @@ return function (ContainerConfigurator $c) {
$s->set('bar2', 'stdClass');
$s->set('BAR2', 'stdClass');
$s->set('tagged_iterator_foo', 'Bar')
->private()
->tag('foo');
$s->set('tagged_iterator', 'Bar')
->public()
->args(array(tagged('foo')));
};

View File

@ -4,6 +4,7 @@ require_once __DIR__.'/../includes/classes.php';
require_once __DIR__.'/../includes/foo.php';
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
@ -168,5 +169,15 @@ $container
;
$container->register('bar2', 'stdClass')->setPublic(true);
$container->register('BAR2', 'stdClass')->setPublic(true);
$container
->register('tagged_iterator_foo', 'Bar')
->addTag('foo')
->setPublic(false)
;
$container
->register('tagged_iterator', 'Bar')
->addArgument(new TaggedIteratorArgument('foo'))
->setPublic(true)
;
return $container;

View File

@ -32,6 +32,8 @@ digraph sc {
node_BAR [label="BAR\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_bar2 [label="bar2\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_BAR2 [label="BAR2\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_tagged_iterator_foo [label="tagged_iterator_foo\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_tagged_iterator [label="tagged_iterator\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"];
node_foo2 [label="foo2\n\n", shape=record, fillcolor="#ff9999", style="filled"];
node_foo3 [label="foo3\n\n", shape=record, fillcolor="#ff9999", style="filled"];
node_foobaz [label="foobaz\n\n", shape=record, fillcolor="#ff9999", style="filled"];

View File

@ -266,6 +266,27 @@ use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
return $this->services['service_from_static_method'] = \Bar\FooClass::getInstance();
[Container%s/getTaggedIteratorService.php] => <?php
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
// Returns the public 'tagged_iterator' shared service.
return $this->services['tagged_iterator'] = new \Bar(new RewindableGenerator(function () {
yield 0 => ${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->load(__DIR__.'/getFooService.php')) && false ?: '_'};
yield 1 => ${($_ = isset($this->services['tagged_iterator_foo']) ? $this->services['tagged_iterator_foo'] : $this->services['tagged_iterator_foo'] = new \Bar()) && false ?: '_'};
}, 2));
[Container%s/getTaggedIteratorFooService.php] => <?php
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
// Returns the private 'tagged_iterator_foo' shared service.
return $this->services['tagged_iterator_foo'] = new \Bar();
[Container%s/Container.php] => <?php
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
@ -326,6 +347,8 @@ class Container%s extends Container
'method_call1' => __DIR__.'/getMethodCall1Service.php',
'new_factory_service' => __DIR__.'/getNewFactoryServiceService.php',
'service_from_static_method' => __DIR__.'/getServiceFromStaticMethodService.php',
'tagged_iterator' => __DIR__.'/getTaggedIteratorService.php',
'tagged_iterator_foo' => __DIR__.'/getTaggedIteratorFooService.php',
);
$this->aliases = array(
'alias_for_alias' => 'foo',

View File

@ -52,6 +52,8 @@ class ProjectServiceContainer extends Container
'method_call1' => 'getMethodCall1Service',
'new_factory_service' => 'getNewFactoryServiceService',
'service_from_static_method' => 'getServiceFromStaticMethodService',
'tagged_iterator' => 'getTaggedIteratorService',
'tagged_iterator_foo' => 'getTaggedIteratorFooService',
);
$this->aliases = array(
'alias_for_alias' => 'foo',
@ -372,6 +374,19 @@ class ProjectServiceContainer extends Container
return $this->services['service_from_static_method'] = \Bar\FooClass::getInstance();
}
/**
* Gets the public 'tagged_iterator' shared service.
*
* @return \Bar
*/
protected function getTaggedIteratorService()
{
return $this->services['tagged_iterator'] = new \Bar(new RewindableGenerator(function () {
yield 0 => ${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->getFooService()) && false ?: '_'};
yield 1 => ${($_ = isset($this->services['tagged_iterator_foo']) ? $this->services['tagged_iterator_foo'] : $this->services['tagged_iterator_foo'] = new \Bar()) && false ?: '_'};
}, 2));
}
/**
* Gets the private 'factory_simple' shared service.
*
@ -386,6 +401,16 @@ class ProjectServiceContainer extends Container
return $this->privates['factory_simple'] = new \SimpleFactoryClass('foo');
}
/**
* Gets the private 'tagged_iterator_foo' shared service.
*
* @return \Bar
*/
protected function getTaggedIteratorFooService()
{
return $this->services['tagged_iterator_foo'] = new \Bar();
}
/**
* {@inheritdoc}
*/

View File

@ -138,6 +138,12 @@
</service>
<service id="bar2" class="stdClass" public="true"/>
<service id="BAR2" class="stdClass" public="true"/>
<service id="tagged_iterator_foo" class="Bar" public="false">
<tag name="foo"/>
</service>
<service id="tagged_iterator" class="Bar" public="true">
<argument type="tagged" tag="foo"/>
</service>
<service id="Psr\Container\ContainerInterface" alias="service_container" public="false"/>
<service id="Symfony\Component\DependencyInjection\ContainerInterface" alias="service_container" public="false"/>
<service id="alias_for_foo" alias="foo" public="true"/>

View File

@ -155,6 +155,16 @@ services:
alias_for_alias:
alias: 'foo'
public: true
tagged_iterator_foo:
class: Bar
tags:
- { name: foo }
public: false
tagged_iterator:
class: Bar
arguments:
- !tagged foo
public: true
Psr\Container\ContainerInterface:
alias: service_container
public: false

View File

@ -15,6 +15,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

View File

@ -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();
}
}

View 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;
}
}

View File

@ -19,6 +19,7 @@
"php": "^7.1.3"
},
"require-dev": {
"psr/container": "^1.0",
"symfony/event-dispatcher": "~3.4|~4.0",
"symfony/expression-language": "~3.4|~4.0",
"symfony/http-foundation": "~3.4|~4.0",
@ -27,6 +28,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",

View File

@ -29,6 +29,7 @@
"symfony/security-http": "self.version"
},
"require-dev": {
"psr/container": "^1.0",
"symfony/finder": "~3.4|~4.0",
"symfony/polyfill-intl-icu": "~1.0",
"symfony/routing": "~3.4|~4.0",
@ -38,6 +39,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",