feature #14673 New Guard Authentication System (e.g. putting the joy back into security) (weaverryan)
This PR was merged into the 2.8 branch. Discussion ---------- New Guard Authentication System (e.g. putting the joy back into security) | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | at least partially: #14300, #11158, #11451, #10035, #10463, #8606, probably more | License | MIT | Doc PR | symfony/symfony-docs#5265 Hi guys! Though it got much easier in 2.4 with `pre_auth`, authentication is a pain in Symfony. This introduces a new authentication provider called guard, with one goal in mind: put everything you need for *any* authentication system into one spot. ### How it works With guard, you can perform custom authentication just by implementing the [GuardAuthenticatorInterface](https://github.com/weaverryan/symfony/blob/guard/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php) and registering it as a service. It has methods for every part of a custom authentication flow I can think of. For a working example, see https://github.com/weaverryan/symfony-demo/tree/guard-auth. This uses 2 authenticators simultaneously, creating a system that handles [form login](https://github.com/weaverryan/symfony-demo/blob/guard-auth/src/AppBundle/Security/FormLoginAuthenticator.php) and [api token auth](https://github.com/weaverryan/symfony-demo/blob/guard-auth/src/AppBundle/Security/TokenAuthenticator.php) with a respectable amount of code. The [security.yml](https://github.com/weaverryan/symfony-demo/blob/guard-auth/app/config/security.yml) is also quite simple. This also supports "manual login" without jumping through hoops: https://github.com/weaverryan/symfony-demo/blob/guard-auth/src/AppBundle/Controller/SecurityController.php#L45 I've also tested with "remember me" and "switch user" - no problems with either. I hope you like it :). ### What's Needed 1) **Other Use-Cases?**: Please think about the code and try it. What use-cases are we *not* covering? I want Guard to be simple, but cover the 99.9% use-cases. 2) **Remember me** functionality cannot be triggered via manual login. That's true now, and it's not fixed, and it's tricky. ### Deprecations? This is a new feature, so no deprecations. But, creating a login form with a guard authenticator is a whole heck of a lot easier to understand than `form_login` or even `simple_form`. In a perfect world, we'd either deprecate those or make them use "guard" internally so that we have just **one** way of performing authentication. Thanks! Commits -------a01ed35
Adding the necessary files so that Guard can be its own installable componentd763134
Removing unnecessary overridee353833
fabbotdd485f4
Adding a new exception and throwing it when the User changes302235e
Fixing a bug where having an authentication failure would log you out.396a162
Tweaks thanks to Wouterc9d9430
Adding logging on this step and switching the order - not for any huge reason31f9cae
Adding a base class to assist with form login authentication0501761
Allowing for other authenticators to be checked293c8a1
meaningless author and license changes81432f9
Adding missing factory registration7a94994
Thanks again fabbot!7de05be
A few more changes thanks to @iltarffdbc66
Splitting the getting of the user and checking credentials into two steps6edb9e1
Tweaking docblock on interface thanks to @iltard693721
Adding periods at the end of exceptions, and changing one class name to LogicException thanks to @iltareb158cb
Updating interface method per suggestion - makes sense to me, Request is redundantc73c32e
Thanks fabbot!6c180c7
Adding an edge case - this should not happen anyways180e2c7
Properly handles "post auth" tokens that have become not authenticated873ed28
Renaming the tokens to be clear they are "post" and "pre" auth - also adding an interfacea0bceb4
adding Guard tests05af97c
Initial commit (but after some polished work) of the new Guard authentication system330aa7f
Improving phpdoc on AuthenticationEntryPointInterface so people that implement this understand it
This commit is contained in:
commit
5f2acfd4c7
@ -0,0 +1,122 @@
|
|||||||
|
<?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\DependencyInjection\Security\Factory;
|
||||||
|
|
||||||
|
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\DependencyInjection\DefinitionDecorator;
|
||||||
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the "guard" authentication provider key under a firewall.
|
||||||
|
*
|
||||||
|
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||||
|
*/
|
||||||
|
class GuardAuthenticationFactory implements SecurityFactoryInterface
|
||||||
|
{
|
||||||
|
public function getPosition()
|
||||||
|
{
|
||||||
|
return 'pre_auth';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKey()
|
||||||
|
{
|
||||||
|
return 'guard';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addConfiguration(NodeDefinition $node)
|
||||||
|
{
|
||||||
|
$node
|
||||||
|
->fixXmlConfig('authenticator')
|
||||||
|
->children()
|
||||||
|
->scalarNode('provider')
|
||||||
|
->info('A key from the "providers" section of your security config, in case your user provider is different than the firewall')
|
||||||
|
->end()
|
||||||
|
->scalarNode('entry_point')
|
||||||
|
->info('A service id (of one of your authenticators) whose start() method should be called when an anonymous user hits a page that requires authentication')
|
||||||
|
->defaultValue(null)
|
||||||
|
->end()
|
||||||
|
->arrayNode('authenticators')
|
||||||
|
->info('An array of service ids for all of your "authenticators"')
|
||||||
|
->requiresAtLeastOneElement()
|
||||||
|
->prototype('scalar')->end()
|
||||||
|
->end()
|
||||||
|
->end()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
|
||||||
|
{
|
||||||
|
$authenticatorIds = $config['authenticators'];
|
||||||
|
$authenticatorReferences = array();
|
||||||
|
foreach ($authenticatorIds as $authenticatorId) {
|
||||||
|
$authenticatorReferences[] = new Reference($authenticatorId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// configure the GuardAuthenticationFactory to have the dynamic constructor arguments
|
||||||
|
$providerId = 'security.authentication.provider.guard.'.$id;
|
||||||
|
$container
|
||||||
|
->setDefinition($providerId, new DefinitionDecorator('security.authentication.provider.guard'))
|
||||||
|
->replaceArgument(0, $authenticatorReferences)
|
||||||
|
->replaceArgument(1, new Reference($userProvider))
|
||||||
|
->replaceArgument(2, $id)
|
||||||
|
;
|
||||||
|
|
||||||
|
// listener
|
||||||
|
$listenerId = 'security.authentication.listener.guard.'.$id;
|
||||||
|
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.guard'));
|
||||||
|
$listener->replaceArgument(2, $id);
|
||||||
|
$listener->replaceArgument(3, $authenticatorReferences);
|
||||||
|
|
||||||
|
// determine the entryPointId to use
|
||||||
|
$entryPointId = $this->determineEntryPoint($defaultEntryPoint, $config);
|
||||||
|
|
||||||
|
// this is always injected - then the listener decides if it should be used
|
||||||
|
$container
|
||||||
|
->getDefinition($listenerId)
|
||||||
|
->addTag('security.remember_me_aware', array('id' => $id, 'provider' => $userProvider));
|
||||||
|
|
||||||
|
return array($providerId, $listenerId, $entryPointId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function determineEntryPoint($defaultEntryPointId, array $config)
|
||||||
|
{
|
||||||
|
if ($defaultEntryPointId) {
|
||||||
|
// explode if they've configured the entry_point, but there is already one
|
||||||
|
if ($config['entry_point']) {
|
||||||
|
throw new \LogicException(sprintf(
|
||||||
|
'The guard authentication provider cannot use the "%s" entry_point because another entry point is already configured by another provider! Either remove the other provider or move the entry_point configuration as a root key under your firewall',
|
||||||
|
$config['entry_point']
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $defaultEntryPointId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($config['entry_point']) {
|
||||||
|
// if it's configured explicitly, use it!
|
||||||
|
return $config['entry_point'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$authenticatorIds = $config['authenticators'];
|
||||||
|
if (count($authenticatorIds) == 1) {
|
||||||
|
// if there is only one authenticator, use that as the entry point
|
||||||
|
return array_shift($authenticatorIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have multiple entry points - we must ask them to configure one
|
||||||
|
throw new \LogicException(sprintf(
|
||||||
|
'Because you have multiple guard configurators, you need to set the "guard.entry_point" key to one of you configurators (%s)',
|
||||||
|
implode(', ', $authenticatorIds)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
@ -65,6 +65,7 @@ class SecurityExtension extends Extension
|
|||||||
$loader->load('templating_php.xml');
|
$loader->load('templating_php.xml');
|
||||||
$loader->load('templating_twig.xml');
|
$loader->load('templating_twig.xml');
|
||||||
$loader->load('collectors.xml');
|
$loader->load('collectors.xml');
|
||||||
|
$loader->load('guard.xml');
|
||||||
|
|
||||||
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
|
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
|
||||||
$container->removeDefinition('security.expression_language');
|
$container->removeDefinition('security.expression_language');
|
||||||
|
40
src/Symfony/Bundle/SecurityBundle/Resources/config/guard.xml
Normal file
40
src/Symfony/Bundle/SecurityBundle/Resources/config/guard.xml
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
|
||||||
|
<container xmlns="http://symfony.com/schema/dic/services"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||||
|
|
||||||
|
<services>
|
||||||
|
<service id="security.authentication.guard_handler"
|
||||||
|
class="Symfony\Component\Security\Guard\GuardAuthenticatorHandler"
|
||||||
|
>
|
||||||
|
<argument type="service" id="security.token_storage" />
|
||||||
|
<argument type="service" id="event_dispatcher" on-invalid="null" />
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<!-- See GuardAuthenticationFactory -->
|
||||||
|
<service id="security.authentication.provider.guard"
|
||||||
|
class="Symfony\Component\Security\Guard\Provider\GuardAuthenticationProvider"
|
||||||
|
abstract="true"
|
||||||
|
public="false"
|
||||||
|
>
|
||||||
|
<argument /> <!-- Simple Authenticator -->
|
||||||
|
<argument /> <!-- User Provider -->
|
||||||
|
<argument /> <!-- Provider-shared Key -->
|
||||||
|
<argument type="service" id="security.user_checker" />
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<service id="security.authentication.listener.guard"
|
||||||
|
class="Symfony\Component\Security\Guard\Firewall\GuardAuthenticationListener"
|
||||||
|
public="false"
|
||||||
|
abstract="true"
|
||||||
|
>
|
||||||
|
<tag name="monolog.logger" channel="security" />
|
||||||
|
<argument type="service" id="security.authentication.guard_handler" />
|
||||||
|
<argument type="service" id="security.authentication.manager" />
|
||||||
|
<argument /> <!-- Provider-shared Key -->
|
||||||
|
<argument /> <!-- Authenticator -->
|
||||||
|
<argument type="service" id="logger" on-invalid="null" />
|
||||||
|
</service>
|
||||||
|
</services>
|
||||||
|
</container>
|
@ -23,6 +23,7 @@ use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\RemoteUse
|
|||||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SimplePreAuthenticationFactory;
|
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SimplePreAuthenticationFactory;
|
||||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SimpleFormFactory;
|
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SimpleFormFactory;
|
||||||
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\InMemoryFactory;
|
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\InMemoryFactory;
|
||||||
|
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\GuardAuthenticationFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bundle.
|
* Bundle.
|
||||||
@ -44,6 +45,7 @@ class SecurityBundle extends Bundle
|
|||||||
$extension->addSecurityListenerFactory(new RemoteUserFactory());
|
$extension->addSecurityListenerFactory(new RemoteUserFactory());
|
||||||
$extension->addSecurityListenerFactory(new SimplePreAuthenticationFactory());
|
$extension->addSecurityListenerFactory(new SimplePreAuthenticationFactory());
|
||||||
$extension->addSecurityListenerFactory(new SimpleFormFactory());
|
$extension->addSecurityListenerFactory(new SimpleFormFactory());
|
||||||
|
$extension->addSecurityListenerFactory(new GuardAuthenticationFactory());
|
||||||
|
|
||||||
$extension->addUserProviderFactory(new InMemoryFactory());
|
$extension->addUserProviderFactory(new InMemoryFactory());
|
||||||
$container->addCompilerPass(new AddSecurityVotersPass());
|
$container->addCompilerPass(new AddSecurityVotersPass());
|
||||||
|
@ -0,0 +1,180 @@
|
|||||||
|
<?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\DependencyInjection\Security\Factory;
|
||||||
|
|
||||||
|
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\GuardAuthenticationFactory;
|
||||||
|
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
|
|
||||||
|
class GuardAuthenticationFactoryTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @dataProvider getValidConfigurationTests
|
||||||
|
*/
|
||||||
|
public function testAddValidConfiguration(array $inputConfig, array $expectedConfig)
|
||||||
|
{
|
||||||
|
$factory = new GuardAuthenticationFactory();
|
||||||
|
$nodeDefinition = new ArrayNodeDefinition('guard');
|
||||||
|
$factory->addConfiguration($nodeDefinition);
|
||||||
|
|
||||||
|
$node = $nodeDefinition->getNode();
|
||||||
|
$normalizedConfig = $node->normalize($inputConfig);
|
||||||
|
$finalizedConfig = $node->finalize($normalizedConfig);
|
||||||
|
|
||||||
|
$this->assertEquals($expectedConfig, $finalizedConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
|
||||||
|
* @dataProvider getInvalidConfigurationTests
|
||||||
|
*/
|
||||||
|
public function testAddInvalidConfiguration(array $inputConfig)
|
||||||
|
{
|
||||||
|
$factory = new GuardAuthenticationFactory();
|
||||||
|
$nodeDefinition = new ArrayNodeDefinition('guard');
|
||||||
|
$factory->addConfiguration($nodeDefinition);
|
||||||
|
|
||||||
|
$node = $nodeDefinition->getNode();
|
||||||
|
$normalizedConfig = $node->normalize($inputConfig);
|
||||||
|
// will validate and throw an exception on invalid
|
||||||
|
$node->finalize($normalizedConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValidConfigurationTests()
|
||||||
|
{
|
||||||
|
$tests = array();
|
||||||
|
|
||||||
|
// completely basic
|
||||||
|
$tests[] = array(
|
||||||
|
array(
|
||||||
|
'authenticators' => array('authenticator1', 'authenticator2'),
|
||||||
|
'provider' => 'some_provider',
|
||||||
|
'entry_point' => 'the_entry_point',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'authenticators' => array('authenticator1', 'authenticator2'),
|
||||||
|
'provider' => 'some_provider',
|
||||||
|
'entry_point' => 'the_entry_point',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// testing xml config fix: authenticator -> authenticators
|
||||||
|
$tests[] = array(
|
||||||
|
array(
|
||||||
|
'authenticator' => array('authenticator1', 'authenticator2'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'authenticators' => array('authenticator1', 'authenticator2'),
|
||||||
|
'entry_point' => null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $tests;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInvalidConfigurationTests()
|
||||||
|
{
|
||||||
|
$tests = array();
|
||||||
|
|
||||||
|
// testing not empty
|
||||||
|
$tests[] = array(
|
||||||
|
array('authenticators' => array()),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $tests;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBasicCreate()
|
||||||
|
{
|
||||||
|
// simple configuration
|
||||||
|
$config = array(
|
||||||
|
'authenticators' => array('authenticator123'),
|
||||||
|
'entry_point' => null,
|
||||||
|
);
|
||||||
|
list($container, $entryPointId) = $this->executeCreate($config, null);
|
||||||
|
$this->assertEquals('authenticator123', $entryPointId);
|
||||||
|
|
||||||
|
$providerDefinition = $container->getDefinition('security.authentication.provider.guard.my_firewall');
|
||||||
|
$this->assertEquals(array(
|
||||||
|
'index_0' => array(new Reference('authenticator123')),
|
||||||
|
'index_1' => new Reference('my_user_provider'),
|
||||||
|
'index_2' => 'my_firewall',
|
||||||
|
), $providerDefinition->getArguments());
|
||||||
|
|
||||||
|
$listenerDefinition = $container->getDefinition('security.authentication.listener.guard.my_firewall');
|
||||||
|
$this->assertEquals('my_firewall', $listenerDefinition->getArgument(2));
|
||||||
|
$this->assertEquals(array(new Reference('authenticator123')), $listenerDefinition->getArgument(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExistingDefaultEntryPointUsed()
|
||||||
|
{
|
||||||
|
// any existing default entry point is used
|
||||||
|
$config = array(
|
||||||
|
'authenticators' => array('authenticator123'),
|
||||||
|
'entry_point' => null,
|
||||||
|
);
|
||||||
|
list($container, $entryPointId) = $this->executeCreate($config, 'some_default_entry_point');
|
||||||
|
$this->assertEquals('some_default_entry_point', $entryPointId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \LogicException
|
||||||
|
*/
|
||||||
|
public function testCannotOverrideDefaultEntryPoint()
|
||||||
|
{
|
||||||
|
// any existing default entry point is used
|
||||||
|
$config = array(
|
||||||
|
'authenticators' => array('authenticator123'),
|
||||||
|
'entry_point' => 'authenticator123',
|
||||||
|
);
|
||||||
|
$this->executeCreate($config, 'some_default_entry_point');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \LogicException
|
||||||
|
*/
|
||||||
|
public function testMultipleAuthenticatorsRequiresEntryPoint()
|
||||||
|
{
|
||||||
|
// any existing default entry point is used
|
||||||
|
$config = array(
|
||||||
|
'authenticators' => array('authenticator123', 'authenticatorABC'),
|
||||||
|
'entry_point' => null,
|
||||||
|
);
|
||||||
|
$this->executeCreate($config, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCreateWithEntryPoint()
|
||||||
|
{
|
||||||
|
// any existing default entry point is used
|
||||||
|
$config = array(
|
||||||
|
'authenticators' => array('authenticator123', 'authenticatorABC'),
|
||||||
|
'entry_point' => 'authenticatorABC',
|
||||||
|
);
|
||||||
|
list($container, $entryPointId) = $this->executeCreate($config, null);
|
||||||
|
$this->assertEquals('authenticatorABC', $entryPointId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function executeCreate(array $config, $defaultEntryPointId)
|
||||||
|
{
|
||||||
|
$container = new ContainerBuilder();
|
||||||
|
$container->register('security.authentication.provider.guard');
|
||||||
|
$container->register('security.authentication.listener.guard');
|
||||||
|
$id = 'my_firewall';
|
||||||
|
$userProviderId = 'my_user_provider';
|
||||||
|
|
||||||
|
$factory = new GuardAuthenticationFactory();
|
||||||
|
list($providerId, $listenerId, $entryPointId) = $factory->create($container, $id, $config, $userProviderId, $defaultEntryPointId);
|
||||||
|
|
||||||
|
return array($container, $entryPointId);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
<?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\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AuthenticationServiceException is thrown when an authenticated token becomes un-authentcated between requests.
|
||||||
|
*
|
||||||
|
* In practice, this is due to the User changing between requests (e.g. password changes),
|
||||||
|
* causes the token to become un-authenticated.
|
||||||
|
*
|
||||||
|
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||||
|
*/
|
||||||
|
class AuthenticationExpiredException extends AccountStatusException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getMessageKey()
|
||||||
|
{
|
||||||
|
return 'Authentication expired because your account information has changed.';
|
||||||
|
}
|
||||||
|
}
|
3
src/Symfony/Component/Security/Guard/.gitignore
vendored
Normal file
3
src/Symfony/Component/Security/Guard/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
vendor/
|
||||||
|
composer.lock
|
||||||
|
phpunit.xml
|
@ -0,0 +1,41 @@
|
|||||||
|
<?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\Guard;
|
||||||
|
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An optional base class that creates a PostAuthenticationGuardToken for you.
|
||||||
|
*
|
||||||
|
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||||
|
*/
|
||||||
|
abstract class AbstractGuardAuthenticator implements GuardAuthenticatorInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Shortcut to create a PostAuthenticationGuardToken for you, if you don't really
|
||||||
|
* care about which authenticated token you're using.
|
||||||
|
*
|
||||||
|
* @param UserInterface $user
|
||||||
|
* @param string $providerKey
|
||||||
|
*
|
||||||
|
* @return PostAuthenticationGuardToken
|
||||||
|
*/
|
||||||
|
public function createAuthenticatedToken(UserInterface $user, $providerKey)
|
||||||
|
{
|
||||||
|
return new PostAuthenticationGuardToken(
|
||||||
|
$user,
|
||||||
|
$providerKey,
|
||||||
|
$user->getRoles()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
<?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\Guard\Authenticator;
|
||||||
|
|
||||||
|
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
|
||||||
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A base class to make form login authentication easier!
|
||||||
|
*
|
||||||
|
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||||
|
*/
|
||||||
|
abstract class AbstractFormLoginAuthenticator extends AbstractGuardAuthenticator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Return the URL to the login page.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
abstract protected function getLoginUrl();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user will be redirected to the secure page they originally tried
|
||||||
|
* to access. But if no such page exists (i.e. the user went to the
|
||||||
|
* login page directly), this returns the URL the user should be redirected
|
||||||
|
* to after logging in successfully (e.g. your homepage).
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
abstract protected function getDefaultSuccessRedirectUrl();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override to change what happens after a bad username/password is submitted.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param AuthenticationException $exception
|
||||||
|
*
|
||||||
|
* @return RedirectResponse
|
||||||
|
*/
|
||||||
|
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
|
||||||
|
{
|
||||||
|
$request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception);
|
||||||
|
$url = $this->getLoginUrl();
|
||||||
|
|
||||||
|
return new RedirectResponse($url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override to change what happens after successful authentication.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param TokenInterface $token
|
||||||
|
* @param string $providerKey
|
||||||
|
*
|
||||||
|
* @return RedirectResponse
|
||||||
|
*/
|
||||||
|
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
|
||||||
|
{
|
||||||
|
// if the user hit a secure page and start() was called, this was
|
||||||
|
// the URL they were on, and probably where you want to redirect to
|
||||||
|
$targetPath = $request->getSession()->get('_security.'.$providerKey.'.target_path');
|
||||||
|
|
||||||
|
if (!$targetPath) {
|
||||||
|
$targetPath = $this->getDefaultSuccessRedirectUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RedirectResponse($targetPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsRememberMe()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override to control what happens when the user hits a secure page
|
||||||
|
* but isn't logged in yet.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param AuthenticationException|null $authException
|
||||||
|
*
|
||||||
|
* @return RedirectResponse
|
||||||
|
*/
|
||||||
|
public function start(Request $request, AuthenticationException $authException = null)
|
||||||
|
{
|
||||||
|
$url = $this->getLoginUrl();
|
||||||
|
|
||||||
|
return new RedirectResponse($url);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,193 @@
|
|||||||
|
<?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\Guard\Firewall;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||||
|
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
|
||||||
|
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
|
||||||
|
use Symfony\Component\Security\Guard\GuardAuthenticatorInterface;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||||
|
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
|
||||||
|
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authentication listener for the "guard" system.
|
||||||
|
*
|
||||||
|
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||||
|
*/
|
||||||
|
class GuardAuthenticationListener implements ListenerInterface
|
||||||
|
{
|
||||||
|
private $guardHandler;
|
||||||
|
private $authenticationManager;
|
||||||
|
private $providerKey;
|
||||||
|
private $guardAuthenticators;
|
||||||
|
private $logger;
|
||||||
|
private $rememberMeServices;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param GuardAuthenticatorHandler $guardHandler The Guard handler
|
||||||
|
* @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance
|
||||||
|
* @param string $providerKey The provider (i.e. firewall) key
|
||||||
|
* @param GuardAuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationProvider
|
||||||
|
* @param LoggerInterface $logger A LoggerInterface instance
|
||||||
|
*/
|
||||||
|
public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, $providerKey, array $guardAuthenticators, LoggerInterface $logger = null)
|
||||||
|
{
|
||||||
|
if (empty($providerKey)) {
|
||||||
|
throw new \InvalidArgumentException('$providerKey must not be empty.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->guardHandler = $guardHandler;
|
||||||
|
$this->authenticationManager = $authenticationManager;
|
||||||
|
$this->providerKey = $providerKey;
|
||||||
|
$this->guardAuthenticators = $guardAuthenticators;
|
||||||
|
$this->logger = $logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates over each authenticator to see if each wants to authenticate the request.
|
||||||
|
*
|
||||||
|
* @param GetResponseEvent $event
|
||||||
|
*/
|
||||||
|
public function handle(GetResponseEvent $event)
|
||||||
|
{
|
||||||
|
if (null !== $this->logger) {
|
||||||
|
$this->logger->info('Checking for guard authentication credentials.', array('firewall_key' => $this->providerKey, 'authenticators' => count($this->guardAuthenticators)));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->guardAuthenticators as $key => $guardAuthenticator) {
|
||||||
|
// get a key that's unique to *this* guard authenticator
|
||||||
|
// this MUST be the same as GuardAuthenticationProvider
|
||||||
|
$uniqueGuardKey = $this->providerKey.'_'.$key;
|
||||||
|
|
||||||
|
$this->executeGuardAuthenticator($uniqueGuardKey, $guardAuthenticator, $event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function executeGuardAuthenticator($uniqueGuardKey, GuardAuthenticatorInterface $guardAuthenticator, GetResponseEvent $event)
|
||||||
|
{
|
||||||
|
$request = $event->getRequest();
|
||||||
|
try {
|
||||||
|
if (null !== $this->logger) {
|
||||||
|
$this->logger->info('Calling getCredentials on guard configurator.', array('firewall_key' => $this->providerKey, 'authenticator' => get_class($guardAuthenticator)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// allow the authenticator to fetch authentication info from the request
|
||||||
|
$credentials = $guardAuthenticator->getCredentials($request);
|
||||||
|
|
||||||
|
// allow null to be returned to skip authentication
|
||||||
|
if (null === $credentials) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a token with the unique key, so that the provider knows which authenticator to use
|
||||||
|
$token = new PreAuthenticationGuardToken($credentials, $uniqueGuardKey);
|
||||||
|
|
||||||
|
if (null !== $this->logger) {
|
||||||
|
$this->logger->info('Passing guard token information to the GuardAuthenticationProvider', array('firewall_key' => $this->providerKey, 'authenticator' => get_class($guardAuthenticator)));
|
||||||
|
}
|
||||||
|
// pass the token into the AuthenticationManager system
|
||||||
|
// this indirectly calls GuardAuthenticationProvider::authenticate()
|
||||||
|
$token = $this->authenticationManager->authenticate($token);
|
||||||
|
|
||||||
|
if (null !== $this->logger) {
|
||||||
|
$this->logger->info('Guard authentication successful!', array('token' => $token, 'authenticator' => get_class($guardAuthenticator)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// sets the token on the token storage, etc
|
||||||
|
$this->guardHandler->authenticateWithToken($token, $request);
|
||||||
|
} catch (AuthenticationException $e) {
|
||||||
|
// oh no! Authentication failed!
|
||||||
|
|
||||||
|
if (null !== $this->logger) {
|
||||||
|
$this->logger->info('Guard authentication failed.', array('exception' => $e, 'authenticator' => get_class($guardAuthenticator)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->guardHandler->handleAuthenticationFailure($e, $request, $guardAuthenticator, $this->providerKey);
|
||||||
|
|
||||||
|
if ($response instanceof Response) {
|
||||||
|
$event->setResponse($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// success!
|
||||||
|
$response = $this->guardHandler->handleAuthenticationSuccess($token, $request, $guardAuthenticator, $this->providerKey);
|
||||||
|
if ($response instanceof Response) {
|
||||||
|
if (null !== $this->logger) {
|
||||||
|
$this->logger->info('Guard authenticator set success response.', array('response' => $response, 'authenticator' => get_class($guardAuthenticator)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$event->setResponse($response);
|
||||||
|
} else {
|
||||||
|
if (null !== $this->logger) {
|
||||||
|
$this->logger->info('Guard authenticator set no success response: request continues.', array('authenticator' => get_class($guardAuthenticator)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to trigger the remember me functionality
|
||||||
|
$this->triggerRememberMe($guardAuthenticator, $request, $token, $response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should be called if this listener will support remember me.
|
||||||
|
*
|
||||||
|
* @param RememberMeServicesInterface $rememberMeServices
|
||||||
|
*/
|
||||||
|
public function setRememberMeServices(RememberMeServicesInterface $rememberMeServices)
|
||||||
|
{
|
||||||
|
$this->rememberMeServices = $rememberMeServices;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to see if remember me is supported in the authenticator and
|
||||||
|
* on the firewall. If it is, the RememberMeServicesInterface is notified.
|
||||||
|
*
|
||||||
|
* @param GuardAuthenticatorInterface $guardAuthenticator
|
||||||
|
* @param Request $request
|
||||||
|
* @param TokenInterface $token
|
||||||
|
* @param Response $response
|
||||||
|
*/
|
||||||
|
private function triggerRememberMe(GuardAuthenticatorInterface $guardAuthenticator, Request $request, TokenInterface $token, Response $response = null)
|
||||||
|
{
|
||||||
|
if (null === $this->rememberMeServices) {
|
||||||
|
if (null !== $this->logger) {
|
||||||
|
$this->logger->info('Remember me skipped: it is not configured for the firewall.', array('authenticator' => get_class($guardAuthenticator)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$guardAuthenticator->supportsRememberMe()) {
|
||||||
|
if (null !== $this->logger) {
|
||||||
|
$this->logger->info('Remember me skipped: your authenticator does not support it.', array('authenticator' => get_class($guardAuthenticator)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$response instanceof Response) {
|
||||||
|
throw new \LogicException(sprintf(
|
||||||
|
'%s::onAuthenticationSuccess *must* return a Response if you want to use the remember me functionality. Return a Response, or set remember_me to false under the guard configuration.',
|
||||||
|
get_class($guardAuthenticator)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->rememberMeServices->loginSuccess($request, $response, $token);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,139 @@
|
|||||||
|
<?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\Guard;
|
||||||
|
|
||||||
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
|
||||||
|
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
|
||||||
|
use Symfony\Component\Security\Http\SecurityEvents;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility class that does much of the *work* during the guard authentication process.
|
||||||
|
*
|
||||||
|
* By having the logic here instead of the listener, more of the process
|
||||||
|
* can be called directly (e.g. for manual authentication) or overridden.
|
||||||
|
*
|
||||||
|
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||||
|
*/
|
||||||
|
class GuardAuthenticatorHandler
|
||||||
|
{
|
||||||
|
private $tokenStorage;
|
||||||
|
|
||||||
|
private $dispatcher;
|
||||||
|
|
||||||
|
public function __construct(TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher = null)
|
||||||
|
{
|
||||||
|
$this->tokenStorage = $tokenStorage;
|
||||||
|
$this->dispatcher = $eventDispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authenticates the given token in the system.
|
||||||
|
*
|
||||||
|
* @param TokenInterface $token
|
||||||
|
* @param Request $request
|
||||||
|
*/
|
||||||
|
public function authenticateWithToken(TokenInterface $token, Request $request)
|
||||||
|
{
|
||||||
|
$this->tokenStorage->setToken($token);
|
||||||
|
|
||||||
|
if (null !== $this->dispatcher) {
|
||||||
|
$loginEvent = new InteractiveLoginEvent($request, $token);
|
||||||
|
$this->dispatcher->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $loginEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the "on success" response for the given GuardAuthenticator.
|
||||||
|
*
|
||||||
|
* @param TokenInterface $token
|
||||||
|
* @param Request $request
|
||||||
|
* @param GuardAuthenticatorInterface $guardAuthenticator
|
||||||
|
* @param string $providerKey The provider (i.e. firewall) key
|
||||||
|
*
|
||||||
|
* @return null|Response
|
||||||
|
*/
|
||||||
|
public function handleAuthenticationSuccess(TokenInterface $token, Request $request, GuardAuthenticatorInterface $guardAuthenticator, $providerKey)
|
||||||
|
{
|
||||||
|
$response = $guardAuthenticator->onAuthenticationSuccess($request, $token, $providerKey);
|
||||||
|
|
||||||
|
// check that it's a Response or null
|
||||||
|
if ($response instanceof Response || null === $response) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \UnexpectedValueException(sprintf(
|
||||||
|
'The %s::onAuthenticationSuccess method must return null or a Response object. You returned %s.',
|
||||||
|
get_class($guardAuthenticator),
|
||||||
|
is_object($response) ? get_class($response) : gettype($response)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method for authenticating the user and returning the
|
||||||
|
* Response *if any* for success.
|
||||||
|
*
|
||||||
|
* @param UserInterface $user
|
||||||
|
* @param Request $request
|
||||||
|
* @param GuardAuthenticatorInterface $authenticator
|
||||||
|
* @param string $providerKey The provider (i.e. firewall) key
|
||||||
|
*
|
||||||
|
* @return Response|null
|
||||||
|
*/
|
||||||
|
public function authenticateUserAndHandleSuccess(UserInterface $user, Request $request, GuardAuthenticatorInterface $authenticator, $providerKey)
|
||||||
|
{
|
||||||
|
// create an authenticated token for the User
|
||||||
|
$token = $authenticator->createAuthenticatedToken($user, $providerKey);
|
||||||
|
// authenticate this in the system
|
||||||
|
$this->authenticateWithToken($token, $request);
|
||||||
|
|
||||||
|
// return the success metric
|
||||||
|
return $this->handleAuthenticationSuccess($token, $request, $authenticator, $providerKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles an authentication failure and returns the Response for the
|
||||||
|
* GuardAuthenticator.
|
||||||
|
*
|
||||||
|
* @param AuthenticationException $authenticationException
|
||||||
|
* @param Request $request
|
||||||
|
* @param GuardAuthenticatorInterface $guardAuthenticator
|
||||||
|
* @param string $providerKey The key of the firewall
|
||||||
|
*
|
||||||
|
* @return null|Response
|
||||||
|
*/
|
||||||
|
public function handleAuthenticationFailure(AuthenticationException $authenticationException, Request $request, GuardAuthenticatorInterface $guardAuthenticator, $providerKey)
|
||||||
|
{
|
||||||
|
$token = $this->tokenStorage->getToken();
|
||||||
|
if ($token instanceof PostAuthenticationGuardToken && $providerKey === $token->getProviderKey()) {
|
||||||
|
$this->tokenStorage->setToken(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $guardAuthenticator->onAuthenticationFailure($request, $authenticationException);
|
||||||
|
if ($response instanceof Response || null === $response) {
|
||||||
|
// returning null is ok, it means they want the request to continue
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \UnexpectedValueException(sprintf(
|
||||||
|
'The %s::onAuthenticationFailure method must return null or a Response object. You returned %s.',
|
||||||
|
get_class($guardAuthenticator),
|
||||||
|
is_object($response) ? get_class($response) : gettype($response)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,150 @@
|
|||||||
|
<?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\Guard;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||||
|
use Symfony\Component\Security\Guard\Token\GuardTokenInterface;
|
||||||
|
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interface for all "guard" authenticators.
|
||||||
|
*
|
||||||
|
* The methods on this interface are called throughout the guard authentication
|
||||||
|
* process to give you the power to control most parts of the process from
|
||||||
|
* one location.
|
||||||
|
*
|
||||||
|
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||||
|
*/
|
||||||
|
interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the authentication credentials from the request and return them
|
||||||
|
* as any type (e.g. an associate array). If you return null, authentication
|
||||||
|
* will be skipped.
|
||||||
|
*
|
||||||
|
* Whatever value you return here will be passed to getUser() and checkCredentials()
|
||||||
|
*
|
||||||
|
* For example, for a form login, you might:
|
||||||
|
*
|
||||||
|
* return array(
|
||||||
|
* 'username' => $request->request->get('_username'),
|
||||||
|
* 'password' => $request->request->get('_password'),
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* Or for an API token that's on a header, you might use:
|
||||||
|
*
|
||||||
|
* return array('api_key' => $request->headers->get('X-API-TOKEN'));
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return mixed|null
|
||||||
|
*/
|
||||||
|
public function getCredentials(Request $request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a UserInterface object based on the credentials.
|
||||||
|
*
|
||||||
|
* The *credentials* are the return value from getCredentials()
|
||||||
|
*
|
||||||
|
* You may throw an AuthenticationException if you wish. If you return
|
||||||
|
* null, then a UsernameNotFoundException is thrown for you.
|
||||||
|
*
|
||||||
|
* @param mixed $credentials
|
||||||
|
* @param UserProviderInterface $userProvider
|
||||||
|
*
|
||||||
|
* @throws AuthenticationException
|
||||||
|
*
|
||||||
|
* @return UserInterface|null
|
||||||
|
*/
|
||||||
|
public function getUser($credentials, UserProviderInterface $userProvider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throw an AuthenticationException if the credentials are invalid.
|
||||||
|
*
|
||||||
|
* The *credentials* are the return value from getCredentials()
|
||||||
|
*
|
||||||
|
* @param mixed $credentials
|
||||||
|
* @param UserInterface $user
|
||||||
|
*
|
||||||
|
* @throws AuthenticationException
|
||||||
|
*/
|
||||||
|
public function checkCredentials($credentials, UserInterface $user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an authenticated token for the given user.
|
||||||
|
*
|
||||||
|
* If you don't care about which token class is used or don't really
|
||||||
|
* understand what a "token" is, you can skip this method by extending
|
||||||
|
* the AbstractGuardAuthenticator class from your authenticator.
|
||||||
|
*
|
||||||
|
* @see AbstractGuardAuthenticator
|
||||||
|
*
|
||||||
|
* @param UserInterface $user
|
||||||
|
* @param string $providerKey The provider (i.e. firewall) key
|
||||||
|
*
|
||||||
|
* @return GuardTokenInterface
|
||||||
|
*/
|
||||||
|
public function createAuthenticatedToken(UserInterface $user, $providerKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when authentication executed, but failed (e.g. wrong username password).
|
||||||
|
*
|
||||||
|
* This should return the Response sent back to the user, like a
|
||||||
|
* RedirectResponse to the login page or a 403 response.
|
||||||
|
*
|
||||||
|
* If you return null, the request will continue, but the user will
|
||||||
|
* not be authenticated. This is probably not what you want to do.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param AuthenticationException $exception
|
||||||
|
*
|
||||||
|
* @return Response|null
|
||||||
|
*/
|
||||||
|
public function onAuthenticationFailure(Request $request, AuthenticationException $exception);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when authentication executed and was successful!
|
||||||
|
*
|
||||||
|
* This should return the Response sent back to the user, like a
|
||||||
|
* RedirectResponse to the last page they visited.
|
||||||
|
*
|
||||||
|
* If you return null, the current request will continue, and the user
|
||||||
|
* will be authenticated. This makes sense, for example, with an API.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param TokenInterface $token
|
||||||
|
* @param string $providerKey The provider (i.e. firewall) key
|
||||||
|
*
|
||||||
|
* @return Response|null
|
||||||
|
*/
|
||||||
|
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this method support remember me cookies?
|
||||||
|
*
|
||||||
|
* Remember me cookie will be set if *all* of the following are met:
|
||||||
|
* A) This method returns true
|
||||||
|
* B) The remember_me key under your firewall is configured
|
||||||
|
* C) The "remember me" functionality is activated. This is usually
|
||||||
|
* done by having a _remember_me checkbox in your form, but
|
||||||
|
* can be configured by the "always_remember_me" and "remember_me_parameter"
|
||||||
|
* parameters under the "remember_me" firewall key
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function supportsRememberMe();
|
||||||
|
}
|
19
src/Symfony/Component/Security/Guard/LICENSE
Normal file
19
src/Symfony/Component/Security/Guard/LICENSE
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2004-2015 Fabien Potencier
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
@ -0,0 +1,145 @@
|
|||||||
|
<?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\Guard\Provider;
|
||||||
|
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
|
||||||
|
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
|
||||||
|
use Symfony\Component\Security\Guard\GuardAuthenticatorInterface;
|
||||||
|
use Symfony\Component\Security\Guard\Token\GuardTokenInterface;
|
||||||
|
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
|
||||||
|
use Symfony\Component\Security\Core\User\UserCheckerInterface;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
|
use Symfony\Component\Security\Core\Exception\AuthenticationExpiredException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responsible for accepting the PreAuthenticationGuardToken and calling
|
||||||
|
* the correct authenticator to retrieve the authenticated token.
|
||||||
|
*
|
||||||
|
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||||
|
*/
|
||||||
|
class GuardAuthenticationProvider implements AuthenticationProviderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var GuardAuthenticatorInterface[]
|
||||||
|
*/
|
||||||
|
private $guardAuthenticators;
|
||||||
|
private $userProvider;
|
||||||
|
private $providerKey;
|
||||||
|
private $userChecker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param GuardAuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationListener
|
||||||
|
* @param UserProviderInterface $userProvider The user provider
|
||||||
|
* @param string $providerKey The provider (i.e. firewall) key
|
||||||
|
* @param UserCheckerInterface $userChecker
|
||||||
|
*/
|
||||||
|
public function __construct(array $guardAuthenticators, UserProviderInterface $userProvider, $providerKey, UserCheckerInterface $userChecker)
|
||||||
|
{
|
||||||
|
$this->guardAuthenticators = $guardAuthenticators;
|
||||||
|
$this->userProvider = $userProvider;
|
||||||
|
$this->providerKey = $providerKey;
|
||||||
|
$this->userChecker = $userChecker;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the correct authenticator for the token and calls it.
|
||||||
|
*
|
||||||
|
* @param GuardTokenInterface $token
|
||||||
|
*
|
||||||
|
* @return TokenInterface
|
||||||
|
*/
|
||||||
|
public function authenticate(TokenInterface $token)
|
||||||
|
{
|
||||||
|
if (!$this->supports($token)) {
|
||||||
|
throw new \InvalidArgumentException('GuardAuthenticationProvider only supports GuardTokenInterface.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$token instanceof PreAuthenticationGuardToken) {
|
||||||
|
/*
|
||||||
|
* The listener *only* passes PreAuthenticationGuardToken instances.
|
||||||
|
* This means that an authenticated token (e.g. PostAuthenticationGuardToken)
|
||||||
|
* is being passed here, which happens if that token becomes
|
||||||
|
* "not authenticated" (e.g. happens if the user changes between
|
||||||
|
* requests). In this case, the user should be logged out, so
|
||||||
|
* we will return an AnonymousToken to accomplish that.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// this should never happen - but technically, the token is
|
||||||
|
// authenticated... so it could just be returned
|
||||||
|
if ($token->isAuthenticated()) {
|
||||||
|
return $token;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this AccountStatusException causes the user to be logged out
|
||||||
|
throw new AuthenticationExpiredException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the *one* GuardAuthenticator that this token originated from
|
||||||
|
foreach ($this->guardAuthenticators as $key => $guardAuthenticator) {
|
||||||
|
// get a key that's unique to *this* guard authenticator
|
||||||
|
// this MUST be the same as GuardAuthenticationListener
|
||||||
|
$uniqueGuardKey = $this->providerKey.'_'.$key;
|
||||||
|
|
||||||
|
if ($uniqueGuardKey == $token->getGuardProviderKey()) {
|
||||||
|
return $this->authenticateViaGuard($guardAuthenticator, $token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no matching authenticator found - but there will be multiple GuardAuthenticationProvider
|
||||||
|
// instances that will be checked if you have multiple firewalls.
|
||||||
|
}
|
||||||
|
|
||||||
|
private function authenticateViaGuard(GuardAuthenticatorInterface $guardAuthenticator, PreAuthenticationGuardToken $token)
|
||||||
|
{
|
||||||
|
// get the user from the GuardAuthenticator
|
||||||
|
$user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider);
|
||||||
|
|
||||||
|
if (null === $user) {
|
||||||
|
throw new UsernameNotFoundException(sprintf(
|
||||||
|
'Null returned from %s::getUser()',
|
||||||
|
get_class($guardAuthenticator)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$user instanceof UserInterface) {
|
||||||
|
throw new \UnexpectedValueException(sprintf(
|
||||||
|
'The %s::getUser() method must return a UserInterface. You returned %s.',
|
||||||
|
get_class($guardAuthenticator),
|
||||||
|
is_object($user) ? get_class($user) : gettype($user)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->userChecker->checkPreAuth($user);
|
||||||
|
$guardAuthenticator->checkCredentials($token->getCredentials(), $user);
|
||||||
|
$this->userChecker->checkPostAuth($user);
|
||||||
|
|
||||||
|
// turn the UserInterface into a TokenInterface
|
||||||
|
$authenticatedToken = $guardAuthenticator->createAuthenticatedToken($user, $this->providerKey);
|
||||||
|
if (!$authenticatedToken instanceof TokenInterface) {
|
||||||
|
throw new \UnexpectedValueException(sprintf(
|
||||||
|
'The %s::createAuthenticatedToken() method must return a TokenInterface. You returned %s.',
|
||||||
|
get_class($guardAuthenticator),
|
||||||
|
is_object($authenticatedToken) ? get_class($authenticatedToken) : gettype($authenticatedToken)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $authenticatedToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supports(TokenInterface $token)
|
||||||
|
{
|
||||||
|
return $token instanceof GuardTokenInterface;
|
||||||
|
}
|
||||||
|
}
|
22
src/Symfony/Component/Security/Guard/README.md
Normal file
22
src/Symfony/Component/Security/Guard/README.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Security Component - Guard
|
||||||
|
==========================
|
||||||
|
|
||||||
|
The Guard component brings many layers of authentication together, making
|
||||||
|
it much easier to create complex authentication systems where you have
|
||||||
|
total control.
|
||||||
|
|
||||||
|
Resources
|
||||||
|
---------
|
||||||
|
|
||||||
|
Documentation:
|
||||||
|
|
||||||
|
https://symfony.com/doc/2.8/book/security.html
|
||||||
|
|
||||||
|
Tests
|
||||||
|
-----
|
||||||
|
|
||||||
|
You can run the unit tests with the following command:
|
||||||
|
|
||||||
|
$ cd path/to/Symfony/Component/Security/Guard/
|
||||||
|
$ composer.phar install --dev
|
||||||
|
$ phpunit
|
@ -0,0 +1,222 @@
|
|||||||
|
<?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\Guard\Tests\Firewall;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Security\Guard\Firewall\GuardAuthenticationListener;
|
||||||
|
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
|
||||||
|
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||||
|
*/
|
||||||
|
class GuardAuthenticationListenerTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
private $authenticationManager;
|
||||||
|
private $guardAuthenticatorHandler;
|
||||||
|
private $event;
|
||||||
|
private $logger;
|
||||||
|
private $request;
|
||||||
|
private $rememberMeServices;
|
||||||
|
|
||||||
|
public function testHandleSuccess()
|
||||||
|
{
|
||||||
|
$authenticator = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
|
||||||
|
$authenticateToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
|
||||||
|
$providerKey = 'my_firewall';
|
||||||
|
|
||||||
|
$credentials = array('username' => 'weaverryan', 'password' => 'all_your_base');
|
||||||
|
$authenticator
|
||||||
|
->expects($this->once())
|
||||||
|
->method('getCredentials')
|
||||||
|
->with($this->equalTo($this->request))
|
||||||
|
->will($this->returnValue($credentials));
|
||||||
|
|
||||||
|
// a clone of the token that should be created internally
|
||||||
|
$uniqueGuardKey = 'my_firewall_0';
|
||||||
|
$nonAuthedToken = new PreAuthenticationGuardToken($credentials, $uniqueGuardKey);
|
||||||
|
|
||||||
|
$this->authenticationManager
|
||||||
|
->expects($this->once())
|
||||||
|
->method('authenticate')
|
||||||
|
->with($this->equalTo($nonAuthedToken))
|
||||||
|
->will($this->returnValue($authenticateToken));
|
||||||
|
|
||||||
|
$this->guardAuthenticatorHandler
|
||||||
|
->expects($this->once())
|
||||||
|
->method('authenticateWithToken')
|
||||||
|
->with($authenticateToken, $this->request);
|
||||||
|
|
||||||
|
$this->guardAuthenticatorHandler
|
||||||
|
->expects($this->once())
|
||||||
|
->method('handleAuthenticationSuccess')
|
||||||
|
->with($authenticateToken, $this->request, $authenticator, $providerKey);
|
||||||
|
|
||||||
|
$listener = new GuardAuthenticationListener(
|
||||||
|
$this->guardAuthenticatorHandler,
|
||||||
|
$this->authenticationManager,
|
||||||
|
$providerKey,
|
||||||
|
array($authenticator),
|
||||||
|
$this->logger
|
||||||
|
);
|
||||||
|
|
||||||
|
$listener->setRememberMeServices($this->rememberMeServices);
|
||||||
|
// should never be called - our handleAuthenticationSuccess() does not return a Response
|
||||||
|
$this->rememberMeServices
|
||||||
|
->expects($this->never())
|
||||||
|
->method('loginSuccess');
|
||||||
|
|
||||||
|
$listener->handle($this->event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHandleSuccessWithRememberMe()
|
||||||
|
{
|
||||||
|
$authenticator = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
|
||||||
|
$authenticateToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
|
||||||
|
$providerKey = 'my_firewall_with_rememberme';
|
||||||
|
|
||||||
|
$authenticator
|
||||||
|
->expects($this->once())
|
||||||
|
->method('getCredentials')
|
||||||
|
->with($this->equalTo($this->request))
|
||||||
|
->will($this->returnValue(array('username' => 'anything_not_empty')));
|
||||||
|
|
||||||
|
$this->authenticationManager
|
||||||
|
->expects($this->once())
|
||||||
|
->method('authenticate')
|
||||||
|
->will($this->returnValue($authenticateToken));
|
||||||
|
|
||||||
|
$successResponse = new Response('Success!');
|
||||||
|
$this->guardAuthenticatorHandler
|
||||||
|
->expects($this->once())
|
||||||
|
->method('handleAuthenticationSuccess')
|
||||||
|
->will($this->returnValue($successResponse));
|
||||||
|
|
||||||
|
$listener = new GuardAuthenticationListener(
|
||||||
|
$this->guardAuthenticatorHandler,
|
||||||
|
$this->authenticationManager,
|
||||||
|
$providerKey,
|
||||||
|
array($authenticator),
|
||||||
|
$this->logger
|
||||||
|
);
|
||||||
|
|
||||||
|
$listener->setRememberMeServices($this->rememberMeServices);
|
||||||
|
$authenticator->expects($this->once())
|
||||||
|
->method('supportsRememberMe')
|
||||||
|
->will($this->returnValue(true));
|
||||||
|
// should be called - we do have a success Response
|
||||||
|
$this->rememberMeServices
|
||||||
|
->expects($this->once())
|
||||||
|
->method('loginSuccess');
|
||||||
|
|
||||||
|
$listener->handle($this->event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHandleCatchesAuthenticationException()
|
||||||
|
{
|
||||||
|
$authenticator = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
|
||||||
|
$providerKey = 'my_firewall2';
|
||||||
|
|
||||||
|
$authException = new AuthenticationException('Get outta here crazy user with a bad password!');
|
||||||
|
$authenticator
|
||||||
|
->expects($this->once())
|
||||||
|
->method('getCredentials')
|
||||||
|
->will($this->throwException($authException));
|
||||||
|
|
||||||
|
// this is not called
|
||||||
|
$this->authenticationManager
|
||||||
|
->expects($this->never())
|
||||||
|
->method('authenticate');
|
||||||
|
|
||||||
|
$this->guardAuthenticatorHandler
|
||||||
|
->expects($this->once())
|
||||||
|
->method('handleAuthenticationFailure')
|
||||||
|
->with($authException, $this->request, $authenticator, $providerKey);
|
||||||
|
|
||||||
|
$listener = new GuardAuthenticationListener(
|
||||||
|
$this->guardAuthenticatorHandler,
|
||||||
|
$this->authenticationManager,
|
||||||
|
$providerKey,
|
||||||
|
array($authenticator),
|
||||||
|
$this->logger
|
||||||
|
);
|
||||||
|
|
||||||
|
$listener->handle($this->event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReturnNullToSkipAuth()
|
||||||
|
{
|
||||||
|
$authenticatorA = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
|
||||||
|
$authenticatorB = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
|
||||||
|
$providerKey = 'my_firewall3';
|
||||||
|
|
||||||
|
$authenticatorA
|
||||||
|
->expects($this->once())
|
||||||
|
->method('getCredentials')
|
||||||
|
->will($this->returnValue(null));
|
||||||
|
$authenticatorB
|
||||||
|
->expects($this->once())
|
||||||
|
->method('getCredentials')
|
||||||
|
->will($this->returnValue(null));
|
||||||
|
|
||||||
|
// this is not called
|
||||||
|
$this->authenticationManager
|
||||||
|
->expects($this->never())
|
||||||
|
->method('authenticate');
|
||||||
|
|
||||||
|
$this->guardAuthenticatorHandler
|
||||||
|
->expects($this->never())
|
||||||
|
->method('handleAuthenticationSuccess');
|
||||||
|
|
||||||
|
$listener = new GuardAuthenticationListener(
|
||||||
|
$this->guardAuthenticatorHandler,
|
||||||
|
$this->authenticationManager,
|
||||||
|
$providerKey,
|
||||||
|
array($authenticatorA, $authenticatorB),
|
||||||
|
$this->logger
|
||||||
|
);
|
||||||
|
|
||||||
|
$listener->handle($this->event);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
$this->authenticationManager = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$this->guardAuthenticatorHandler = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorHandler')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$this->request = new Request(array(), array(), array(), array(), array(), array());
|
||||||
|
|
||||||
|
$this->event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
|
||||||
|
$this->event
|
||||||
|
->expects($this->any())
|
||||||
|
->method('getRequest')
|
||||||
|
->will($this->returnValue($this->request));
|
||||||
|
|
||||||
|
$this->logger = $this->getMock('Psr\Log\LoggerInterface');
|
||||||
|
$this->rememberMeServices = $this->getMock('Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tearDown()
|
||||||
|
{
|
||||||
|
$this->authenticationManager = null;
|
||||||
|
$this->guardAuthenticatorHandler = null;
|
||||||
|
$this->event = null;
|
||||||
|
$this->logger = null;
|
||||||
|
$this->request = null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,141 @@
|
|||||||
|
<?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\Guard\Tests;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
|
||||||
|
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||||
|
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
|
||||||
|
use Symfony\Component\Security\Http\SecurityEvents;
|
||||||
|
|
||||||
|
class GuardAuthenticatorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
private $tokenStorage;
|
||||||
|
private $dispatcher;
|
||||||
|
private $token;
|
||||||
|
private $request;
|
||||||
|
private $guardAuthenticator;
|
||||||
|
|
||||||
|
public function testAuthenticateWithToken()
|
||||||
|
{
|
||||||
|
$this->tokenStorage->expects($this->once())
|
||||||
|
->method('setToken')
|
||||||
|
->with($this->token);
|
||||||
|
|
||||||
|
$loginEvent = new InteractiveLoginEvent($this->request, $this->token);
|
||||||
|
|
||||||
|
$this->dispatcher
|
||||||
|
->expects($this->once())
|
||||||
|
->method('dispatch')
|
||||||
|
->with($this->equalTo(SecurityEvents::INTERACTIVE_LOGIN), $this->equalTo($loginEvent))
|
||||||
|
;
|
||||||
|
|
||||||
|
$handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher);
|
||||||
|
$handler->authenticateWithToken($this->token, $this->request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHandleAuthenticationSuccess()
|
||||||
|
{
|
||||||
|
$providerKey = 'my_handleable_firewall';
|
||||||
|
$response = new Response('Guard all the things!');
|
||||||
|
$this->guardAuthenticator->expects($this->once())
|
||||||
|
->method('onAuthenticationSuccess')
|
||||||
|
->with($this->request, $this->token, $providerKey)
|
||||||
|
->will($this->returnValue($response));
|
||||||
|
|
||||||
|
$handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher);
|
||||||
|
$actualResponse = $handler->handleAuthenticationSuccess($this->token, $this->request, $this->guardAuthenticator, $providerKey);
|
||||||
|
$this->assertSame($response, $actualResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHandleAuthenticationFailure()
|
||||||
|
{
|
||||||
|
// setToken() not called - getToken() will return null, so there's nothing to clear
|
||||||
|
$this->tokenStorage->expects($this->never())
|
||||||
|
->method('setToken')
|
||||||
|
->with(null);
|
||||||
|
$authException = new AuthenticationException('Bad password!');
|
||||||
|
|
||||||
|
$response = new Response('Try again, but with the right password!');
|
||||||
|
$this->guardAuthenticator->expects($this->once())
|
||||||
|
->method('onAuthenticationFailure')
|
||||||
|
->with($this->request, $authException)
|
||||||
|
->will($this->returnValue($response));
|
||||||
|
|
||||||
|
$handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher);
|
||||||
|
$actualResponse = $handler->handleAuthenticationFailure($authException, $this->request, $this->guardAuthenticator, 'firewall_provider_key');
|
||||||
|
$this->assertSame($response, $actualResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getTokenClearingTests
|
||||||
|
*/
|
||||||
|
public function testHandleAuthenticationClearsToken($tokenClass, $tokenProviderKey, $actualProviderKey, $shouldTokenBeCleared)
|
||||||
|
{
|
||||||
|
$token = $this->getMockBuilder($tokenClass)
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
$token->expects($this->any())
|
||||||
|
->method('getProviderKey')
|
||||||
|
->will($this->returnValue($tokenProviderKey));
|
||||||
|
|
||||||
|
// make the $token be the current token
|
||||||
|
$this->tokenStorage->expects($this->once())
|
||||||
|
->method('getToken')
|
||||||
|
->will($this->returnValue($token));
|
||||||
|
|
||||||
|
$this->tokenStorage->expects($shouldTokenBeCleared ? $this->once() : $this->never())
|
||||||
|
->method('setToken')
|
||||||
|
->with(null);
|
||||||
|
$authException = new AuthenticationException('Bad password!');
|
||||||
|
|
||||||
|
$response = new Response('Try again, but with the right password!');
|
||||||
|
$this->guardAuthenticator->expects($this->once())
|
||||||
|
->method('onAuthenticationFailure')
|
||||||
|
->with($this->request, $authException)
|
||||||
|
->will($this->returnValue($response));
|
||||||
|
|
||||||
|
$handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher);
|
||||||
|
$actualResponse = $handler->handleAuthenticationFailure($authException, $this->request, $this->guardAuthenticator, $actualProviderKey);
|
||||||
|
$this->assertSame($response, $actualResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTokenClearingTests()
|
||||||
|
{
|
||||||
|
$tests = array();
|
||||||
|
// correct token class and matching firewall => clear the token
|
||||||
|
$tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'the_firewall_key', true);
|
||||||
|
$tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'different_key', false);
|
||||||
|
$tests[] = array('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', 'the_firewall_key', 'the_firewall_key', false);
|
||||||
|
|
||||||
|
return $tests;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
$this->tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
|
||||||
|
$this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
|
||||||
|
$this->token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
|
||||||
|
$this->request = new Request(array(), array(), array(), array(), array(), array());
|
||||||
|
$this->guardAuthenticator = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tearDown()
|
||||||
|
{
|
||||||
|
$this->tokenStorage = null;
|
||||||
|
$this->dispatcher = null;
|
||||||
|
$this->token = null;
|
||||||
|
$this->request = null;
|
||||||
|
$this->guardAuthenticator = null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,115 @@
|
|||||||
|
<?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\Guard\Tests\Provider;
|
||||||
|
|
||||||
|
use Symfony\Component\Security\Guard\Provider\GuardAuthenticationProvider;
|
||||||
|
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Ryan Weaver <weaverryan@gmail.com>
|
||||||
|
*/
|
||||||
|
class GuardAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
private $userProvider;
|
||||||
|
private $userChecker;
|
||||||
|
private $preAuthenticationToken;
|
||||||
|
|
||||||
|
public function testAuthenticate()
|
||||||
|
{
|
||||||
|
$providerKey = 'my_cool_firewall';
|
||||||
|
|
||||||
|
$authenticatorA = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
|
||||||
|
$authenticatorB = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
|
||||||
|
$authenticatorC = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface');
|
||||||
|
$authenticators = array($authenticatorA, $authenticatorB, $authenticatorC);
|
||||||
|
|
||||||
|
// called 2 times - for authenticator A and B (stops on B because of match)
|
||||||
|
$this->preAuthenticationToken->expects($this->exactly(2))
|
||||||
|
->method('getGuardProviderKey')
|
||||||
|
// it will return the "1" index, which will match authenticatorB
|
||||||
|
->will($this->returnValue('my_cool_firewall_1'));
|
||||||
|
|
||||||
|
$enteredCredentials = array(
|
||||||
|
'username' => '_weaverryan_test_user',
|
||||||
|
'password' => 'guard_auth_ftw',
|
||||||
|
);
|
||||||
|
$this->preAuthenticationToken->expects($this->atLeastOnce())
|
||||||
|
->method('getCredentials')
|
||||||
|
->will($this->returnValue($enteredCredentials));
|
||||||
|
|
||||||
|
// authenticators A and C are never called
|
||||||
|
$authenticatorA->expects($this->never())
|
||||||
|
->method('getUser');
|
||||||
|
$authenticatorC->expects($this->never())
|
||||||
|
->method('getUser');
|
||||||
|
|
||||||
|
$mockedUser = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
|
||||||
|
$authenticatorB->expects($this->once())
|
||||||
|
->method('getUser')
|
||||||
|
->with($enteredCredentials, $this->userProvider)
|
||||||
|
->will($this->returnValue($mockedUser));
|
||||||
|
// checkCredentials is called
|
||||||
|
$authenticatorB->expects($this->once())
|
||||||
|
->method('checkCredentials')
|
||||||
|
->with($enteredCredentials, $mockedUser);
|
||||||
|
$authedToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
|
||||||
|
$authenticatorB->expects($this->once())
|
||||||
|
->method('createAuthenticatedToken')
|
||||||
|
->with($mockedUser, $providerKey)
|
||||||
|
->will($this->returnValue($authedToken));
|
||||||
|
|
||||||
|
// user checker should be called
|
||||||
|
$this->userChecker->expects($this->once())
|
||||||
|
->method('checkPreAuth')
|
||||||
|
->with($mockedUser);
|
||||||
|
$this->userChecker->expects($this->once())
|
||||||
|
->method('checkPostAuth')
|
||||||
|
->with($mockedUser);
|
||||||
|
|
||||||
|
$provider = new GuardAuthenticationProvider($authenticators, $this->userProvider, $providerKey, $this->userChecker);
|
||||||
|
$actualAuthedToken = $provider->authenticate($this->preAuthenticationToken);
|
||||||
|
$this->assertSame($authedToken, $actualAuthedToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationExpiredException
|
||||||
|
*/
|
||||||
|
public function testGuardWithNoLongerAuthenticatedTriggersLogout()
|
||||||
|
{
|
||||||
|
$providerKey = 'my_firewall_abc';
|
||||||
|
|
||||||
|
// create a token and mark it as NOT authenticated anymore
|
||||||
|
// this mimics what would happen if a user "changed" between request
|
||||||
|
$mockedUser = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
|
||||||
|
$token = new PostAuthenticationGuardToken($mockedUser, $providerKey, array('ROLE_USER'));
|
||||||
|
$token->setAuthenticated(false);
|
||||||
|
|
||||||
|
$provider = new GuardAuthenticationProvider(array(), $this->userProvider, $providerKey, $this->userChecker);
|
||||||
|
$actualToken = $provider->authenticate($token);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
$this->userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface');
|
||||||
|
$this->userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface');
|
||||||
|
$this->preAuthenticationToken = $this->getMockBuilder('Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tearDown()
|
||||||
|
{
|
||||||
|
$this->userProvider = null;
|
||||||
|
$this->userChecker = null;
|
||||||
|
$this->preAuthenticationToken = null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
<?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\Guard\Token;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A marker interface that both guard tokens implement.
|
||||||
|
*
|
||||||
|
* Any tokens passed to GuardAuthenticationProvider (i.e. any tokens that
|
||||||
|
* are handled by the guard auth system) must implement this
|
||||||
|
* interface.
|
||||||
|
*
|
||||||
|
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||||
|
*/
|
||||||
|
interface GuardTokenInterface
|
||||||
|
{
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
<?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\Guard\Token;
|
||||||
|
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
|
||||||
|
use Symfony\Component\Security\Core\Role\RoleInterface;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used as an "authenticated" token, though it could be set to not-authenticated later.
|
||||||
|
*
|
||||||
|
* If you're using Guard authentication, you *must* use a class that implements
|
||||||
|
* GuardTokenInterface as your authenticated token (like this class).
|
||||||
|
*
|
||||||
|
* @author Ryan Weaver <ryan@knpuniversity.com>n@gmail.com>
|
||||||
|
*/
|
||||||
|
class PostAuthenticationGuardToken extends AbstractToken implements GuardTokenInterface
|
||||||
|
{
|
||||||
|
private $providerKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param UserInterface $user The user!
|
||||||
|
* @param string $providerKey The provider (firewall) key
|
||||||
|
* @param RoleInterface[]|string[] $roles An array of roles
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function __construct(UserInterface $user, $providerKey, array $roles)
|
||||||
|
{
|
||||||
|
parent::__construct($roles);
|
||||||
|
|
||||||
|
if (empty($providerKey)) {
|
||||||
|
throw new \InvalidArgumentException('$providerKey (i.e. firewall key) must not be empty.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setUser($user);
|
||||||
|
$this->providerKey = $providerKey;
|
||||||
|
|
||||||
|
// this token is meant to be used after authentication success, so it is always authenticated
|
||||||
|
// you could set it as non authenticated later if you need to
|
||||||
|
parent::setAuthenticated(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is meant to be only an authenticated token, where credentials
|
||||||
|
* have already been used and are thus cleared.
|
||||||
|
*
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getCredentials()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the provider (firewall) key.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getProviderKey()
|
||||||
|
{
|
||||||
|
return $this->providerKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function serialize()
|
||||||
|
{
|
||||||
|
return serialize(array($this->providerKey, parent::serialize()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function unserialize($serialized)
|
||||||
|
{
|
||||||
|
list($this->providerKey, $parentStr) = unserialize($serialized);
|
||||||
|
parent::unserialize($parentStr);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
<?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\Guard\Token;
|
||||||
|
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The token used by the guard auth system before authentication.
|
||||||
|
*
|
||||||
|
* The GuardAuthenticationListener creates this, which is then consumed
|
||||||
|
* immediately by the GuardAuthenticationProvider. If authentication is
|
||||||
|
* successful, a different authenticated token is returned
|
||||||
|
*
|
||||||
|
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||||
|
*/
|
||||||
|
class PreAuthenticationGuardToken extends AbstractToken implements GuardTokenInterface
|
||||||
|
{
|
||||||
|
private $credentials;
|
||||||
|
private $guardProviderKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $credentials
|
||||||
|
* @param string $guardProviderKey Unique key that bind this token to a specific GuardAuthenticatorInterface
|
||||||
|
*/
|
||||||
|
public function __construct($credentials, $guardProviderKey)
|
||||||
|
{
|
||||||
|
$this->credentials = $credentials;
|
||||||
|
$this->guardProviderKey = $guardProviderKey;
|
||||||
|
|
||||||
|
parent::__construct(array());
|
||||||
|
|
||||||
|
// never authenticated
|
||||||
|
parent::setAuthenticated(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGuardProviderKey()
|
||||||
|
{
|
||||||
|
return $this->guardProviderKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the user credentials, which might be an array of anything you
|
||||||
|
* wanted to put in there (e.g. username, password, favoriteColor).
|
||||||
|
*
|
||||||
|
* @return mixed The user credentials
|
||||||
|
*/
|
||||||
|
public function getCredentials()
|
||||||
|
{
|
||||||
|
return $this->credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAuthenticated($authenticated)
|
||||||
|
{
|
||||||
|
throw new \LogicException('The PreAuthenticationGuardToken is *never* authenticated.');
|
||||||
|
}
|
||||||
|
}
|
36
src/Symfony/Component/Security/Guard/composer.json
Normal file
36
src/Symfony/Component/Security/Guard/composer.json
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"name": "symfony/security-guard",
|
||||||
|
"type": "library",
|
||||||
|
"description": "Symfony Security Component - Guard",
|
||||||
|
"keywords": [],
|
||||||
|
"homepage": "https://symfony.com",
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Fabien Potencier",
|
||||||
|
"email": "fabien@symfony.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "https://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.9",
|
||||||
|
"symfony/security-core": "~2.8|~3.0.0",
|
||||||
|
"symfony/security-http": "~2.8|~3.0.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"symfony/phpunit-bridge": "~2.8|~3.0.0",
|
||||||
|
"psr/log": "~1.0"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": { "Symfony\\Component\\Security\\Guard\\": "" }
|
||||||
|
},
|
||||||
|
"minimum-stability": "dev",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "2.8-dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
src/Symfony/Component/Security/Guard/phpunit.xml.dist
Normal file
33
src/Symfony/Component/Security/Guard/phpunit.xml.dist
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<phpunit backupGlobals="false"
|
||||||
|
backupStaticAttributes="false"
|
||||||
|
colors="true"
|
||||||
|
convertErrorsToExceptions="true"
|
||||||
|
convertNoticesToExceptions="true"
|
||||||
|
convertWarningsToExceptions="true"
|
||||||
|
processIsolation="false"
|
||||||
|
stopOnFailure="false"
|
||||||
|
syntaxCheck="false"
|
||||||
|
bootstrap="vendor/autoload.php"
|
||||||
|
>
|
||||||
|
<php>
|
||||||
|
<ini name="error_reporting" value="-1" />
|
||||||
|
</php>
|
||||||
|
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Symfony Security Component Guard Suite">
|
||||||
|
<directory>./Tests/</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
|
||||||
|
<filter>
|
||||||
|
<whitelist>
|
||||||
|
<directory>./</directory>
|
||||||
|
<exclude>
|
||||||
|
<directory>./vendor</directory>
|
||||||
|
<directory>./Tests</directory>
|
||||||
|
</exclude>
|
||||||
|
</whitelist>
|
||||||
|
</filter>
|
||||||
|
</phpunit>
|
@ -16,15 +16,25 @@ use Symfony\Component\HttpFoundation\Request;
|
|||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AuthenticationEntryPointInterface is the interface used to start the
|
* Implement this interface for any classes that will be called to "start"
|
||||||
* authentication scheme.
|
* the authentication process (see method for more details).
|
||||||
*
|
*
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
*/
|
*/
|
||||||
interface AuthenticationEntryPointInterface
|
interface AuthenticationEntryPointInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Starts the authentication scheme.
|
* Returns a response that directs the user to authenticate.
|
||||||
|
*
|
||||||
|
* This is called when an anonymous request accesses a resource that
|
||||||
|
* requires authentication. The job of this method is to return some
|
||||||
|
* response that "helps" the user start into the authentication process.
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
* A) For a form login, you might redirect to the login page
|
||||||
|
* return new Response('/login');
|
||||||
|
* B) For an API token authentication system, you return a 401 response
|
||||||
|
* return new Response('Auth header required', 401);
|
||||||
*
|
*
|
||||||
* @param Request $request The request that resulted in an AuthenticationException
|
* @param Request $request The request that resulted in an AuthenticationException
|
||||||
* @param AuthenticationException $authException The exception that started the authentication process
|
* @param AuthenticationException $authException The exception that started the authentication process
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
<directory>./Acl/Tests/</directory>
|
<directory>./Acl/Tests/</directory>
|
||||||
<directory>./Core/Tests/</directory>
|
<directory>./Core/Tests/</directory>
|
||||||
<directory>./Http/Tests/</directory>
|
<directory>./Http/Tests/</directory>
|
||||||
|
<directory>./Guard/Tests/</directory>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user