Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 05af97c

Browse filesBrowse files
committed
Initial commit (but after some polished work) of the new Guard authentication system
1 parent 330aa7f commit 05af97c
Copy full SHA for 05af97c

File tree

Expand file treeCollapse file tree

14 files changed

+1459
-0
lines changed
Filter options
Expand file treeCollapse file tree

14 files changed

+1459
-0
lines changed
+113Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?php
2+
3+
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
4+
5+
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
6+
use Symfony\Component\DependencyInjection\ContainerBuilder;
7+
use Symfony\Component\DependencyInjection\DefinitionDecorator;
8+
use Symfony\Component\DependencyInjection\Reference;
9+
10+
/**
11+
* Configures the "guard" authentication provider key under a firewall
12+
*
13+
* @author Ryan Weaver <weaverryan@gmail.com>
14+
*/
15+
class GuardAuthenticationFactory implements SecurityFactoryInterface
16+
{
17+
public function getPosition()
18+
{
19+
return 'pre_auth';
20+
}
21+
22+
public function getKey()
23+
{
24+
return 'guard';
25+
}
26+
27+
public function addConfiguration(NodeDefinition $node)
28+
{
29+
$node
30+
->fixXmlConfig('authenticator')
31+
->children()
32+
->scalarNode('provider')
33+
->info('A key from the "providers" section of your security config, in case your user provider is different than the firewall')
34+
->end()
35+
->scalarNode('entry_point')
36+
->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')
37+
->defaultValue(null)
38+
->end()
39+
->arrayNode('authenticators')
40+
->info('An array of service ids for all of your "authenticators"')
41+
->requiresAtLeastOneElement()
42+
->prototype('scalar')->end()
43+
->end()
44+
->end()
45+
;
46+
}
47+
48+
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
49+
{
50+
$authenticatorIds = $config['authenticators'];
51+
$authenticatorReferences = array();
52+
foreach ($authenticatorIds as $authenticatorId) {
53+
$authenticatorReferences[] = new Reference($authenticatorId);
54+
}
55+
56+
// configure the GuardAuthenticationFactory to have the dynamic constructor arguments
57+
$providerId = 'security.authentication.provider.guard.'.$id;
58+
$container
59+
->setDefinition($providerId, new DefinitionDecorator('security.authentication.provider.guard'))
60+
->replaceArgument(0, $authenticatorReferences)
61+
->replaceArgument(1, new Reference($userProvider))
62+
->replaceArgument(2, $id)
63+
;
64+
65+
// listener
66+
$listenerId = 'security.authentication.listener.guard.'.$id;
67+
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('security.authentication.listener.guard'));
68+
$listener->replaceArgument(2, $id);
69+
$listener->replaceArgument(3, $authenticatorReferences);
70+
71+
// determine the entryPointId to use
72+
$entryPointId = $this->determineEntryPoint($defaultEntryPoint, $config);
73+
74+
// this is always injected - then the listener decides if it should be used
75+
$container
76+
->getDefinition($listenerId)
77+
->addTag('security.remember_me_aware', array('id' => $id, 'provider' => $userProvider));
78+
79+
return array($providerId, $listenerId, $entryPointId);
80+
}
81+
82+
private function determineEntryPoint($defaultEntryPointId, array $config)
83+
{
84+
if ($defaultEntryPointId) {
85+
// explode if they've configured the entry_point, but there is already one
86+
if ($config['entry_point']) {
87+
throw new \LogicException(sprintf(
88+
'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',
89+
$config['entry_point']
90+
));
91+
}
92+
93+
return $defaultEntryPointId;
94+
}
95+
96+
if ($config['entry_point']) {
97+
// if it's configured explicitly, use it!
98+
return $config['entry_point'];
99+
}
100+
101+
$authenticatorIds = $config['authenticators'];
102+
if (count($authenticatorIds) == 1) {
103+
// if there is only one authenticator, use that as the entry point
104+
return array_shift($authenticatorIds);
105+
}
106+
107+
// we have multiple entry points - we must ask them to configure one
108+
throw new \LogicException(sprintf(
109+
'Because you have multiple guard configurators, you need to set the "guard.entry_point" key to one of you configurators (%s)',
110+
implode(', ', $authenticatorIds)
111+
));
112+
}
113+
}

‎src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public function load(array $configs, ContainerBuilder $container)
6565
$loader->load('templating_php.xml');
6666
$loader->load('templating_twig.xml');
6767
$loader->load('collectors.xml');
68+
$loader->load('guard.xml');
6869

6970
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
7071
$container->removeDefinition('security.expression_language');
+40Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?xml version="1.0" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
6+
7+
<services>
8+
<service id="security.authentication.guard_handler"
9+
class="Symfony\Component\Security\Guard\GuardAuthenticatorHandler"
10+
>
11+
<argument type="service" id="security.token_storage" />
12+
<argument type="service" id="event_dispatcher" on-invalid="null" />
13+
</service>
14+
15+
<!-- See GuardAuthenticationFactory -->
16+
<service id="security.authentication.provider.guard"
17+
class="Symfony\Component\Security\Guard\Provider\GuardAuthenticationProvider"
18+
abstract="true"
19+
public="false"
20+
>
21+
<argument /> <!-- Simple Authenticator -->
22+
<argument /> <!-- User Provider -->
23+
<argument /> <!-- Provider-shared Key -->
24+
<argument type="service" id="security.user_checker" />
25+
</service>
26+
27+
<service id="security.authentication.listener.guard"
28+
class="Symfony\Component\Security\Guard\Firewall\GuardAuthenticationListener"
29+
public="false"
30+
abstract="true"
31+
>
32+
<tag name="monolog.logger" channel="security" />
33+
<argument type="service" id="security.authentication.guard_handler" />
34+
<argument type="service" id="security.authentication.manager" />
35+
<argument /> <!-- Provider-shared Key -->
36+
<argument /> <!-- Authenticator -->
37+
<argument type="service" id="logger" on-invalid="null" />
38+
</service>
39+
</services>
40+
</container>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Security\Factory;
13+
14+
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\GuardAuthenticationFactory;
15+
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
16+
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
use Symfony\Component\DependencyInjection\Reference;
18+
19+
class GuardAuthenticationFactoryTest extends \PHPUnit_Framework_TestCase
20+
{
21+
/**
22+
* @dataProvider getValidConfigurationTests
23+
*/
24+
public function testAddValidConfiguration(array $inputConfig, array $expectedConfig)
25+
{
26+
$factory = new GuardAuthenticationFactory();
27+
$nodeDefinition = new ArrayNodeDefinition('guard');
28+
$factory->addConfiguration($nodeDefinition);
29+
30+
$node = $nodeDefinition->getNode();
31+
$normalizedConfig = $node->normalize($inputConfig);
32+
$finalizedConfig = $node->finalize($normalizedConfig);
33+
34+
$this->assertEquals($expectedConfig, $finalizedConfig);
35+
}
36+
37+
/**
38+
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
39+
* @dataProvider getInvalidConfigurationTests
40+
*/
41+
public function testAddInvalidConfiguration(array $inputConfig)
42+
{
43+
$factory = new GuardAuthenticationFactory();
44+
$nodeDefinition = new ArrayNodeDefinition('guard');
45+
$factory->addConfiguration($nodeDefinition);
46+
47+
$node = $nodeDefinition->getNode();
48+
$normalizedConfig = $node->normalize($inputConfig);
49+
// will validate and throw an exception on invalid
50+
$node->finalize($normalizedConfig);
51+
}
52+
53+
public function getValidConfigurationTests()
54+
{
55+
$tests = array();
56+
57+
// completely basic
58+
$tests[] = array(
59+
array(
60+
'authenticators' => array('authenticator1', 'authenticator2'),
61+
'provider' => 'some_provider',
62+
'entry_point' => 'the_entry_point'
63+
),
64+
array(
65+
'authenticators' => array('authenticator1', 'authenticator2'),
66+
'provider' => 'some_provider',
67+
'entry_point' => 'the_entry_point'
68+
)
69+
);
70+
71+
// testing xml config fix: authenticator -> authenticators
72+
$tests[] = array(
73+
array(
74+
'authenticator' => array('authenticator1', 'authenticator2'),
75+
),
76+
array(
77+
'authenticators' => array('authenticator1', 'authenticator2'),
78+
'entry_point' => null,
79+
)
80+
);
81+
82+
return $tests;
83+
}
84+
85+
public function getInvalidConfigurationTests()
86+
{
87+
$tests = array();
88+
89+
// testing not empty
90+
$tests[] = array(
91+
array('authenticators' => array())
92+
);
93+
94+
return $tests;
95+
}
96+
97+
public function testBasicCreate()
98+
{
99+
// simple configuration
100+
$config = array(
101+
'authenticators' => array('authenticator123'),
102+
'entry_point' => null,
103+
);
104+
list($container, $entryPointId) = $this->executeCreate($config, null);
105+
$this->assertEquals('authenticator123', $entryPointId);
106+
107+
$providerDefinition = $container->getDefinition('security.authentication.provider.guard.my_firewall');
108+
$this->assertEquals(array(
109+
'index_0' => array(new Reference('authenticator123')),
110+
'index_1' => new Reference('my_user_provider'),
111+
'index_2' => 'my_firewall'
112+
), $providerDefinition->getArguments());
113+
114+
$listenerDefinition = $container->getDefinition('security.authentication.listener.guard.my_firewall');
115+
$this->assertEquals('my_firewall', $listenerDefinition->getArgument(2));
116+
$this->assertEquals(array(new Reference('authenticator123')), $listenerDefinition->getArgument(3));
117+
}
118+
119+
public function testExistingDefaultEntryPointUsed()
120+
{
121+
// any existing default entry point is used
122+
$config = array(
123+
'authenticators' => array('authenticator123'),
124+
'entry_point' => null,
125+
);
126+
list($container, $entryPointId) = $this->executeCreate($config, 'some_default_entry_point');
127+
$this->assertEquals('some_default_entry_point', $entryPointId);
128+
}
129+
130+
/**
131+
* @expectedException \LogicException
132+
*/
133+
public function testCannotOverrideDefaultEntryPoint()
134+
{
135+
// any existing default entry point is used
136+
$config = array(
137+
'authenticators' => array('authenticator123'),
138+
'entry_point' => 'authenticator123',
139+
);
140+
$this->executeCreate($config, 'some_default_entry_point');
141+
}
142+
143+
/**
144+
* @expectedException \LogicException
145+
*/
146+
public function testMultipleAuthenticatorsRequiresEntryPoint()
147+
{
148+
// any existing default entry point is used
149+
$config = array(
150+
'authenticators' => array('authenticator123', 'authenticatorABC'),
151+
'entry_point' => null,
152+
);
153+
$this->executeCreate($config, null);
154+
}
155+
156+
157+
public function testCreateWithEntryPoint()
158+
{
159+
// any existing default entry point is used
160+
$config = array(
161+
'authenticators' => array('authenticator123', 'authenticatorABC'),
162+
'entry_point' => 'authenticatorABC',
163+
);
164+
list($container, $entryPointId) = $this->executeCreate($config, null);
165+
$this->assertEquals('authenticatorABC', $entryPointId);
166+
}
167+
168+
private function executeCreate(array $config, $defaultEntryPointId)
169+
{
170+
$container = new ContainerBuilder();
171+
$container->register('security.authentication.provider.guard');
172+
$container->register('security.authentication.listener.guard');
173+
$id = 'my_firewall';
174+
$userProviderId = 'my_user_provider';
175+
176+
$factory = new GuardAuthenticationFactory();
177+
list($providerId, $listenerId, $entryPointId) = $factory->create($container, $id, $config, $userProviderId, $defaultEntryPointId);
178+
179+
return array($container, $entryPointId);
180+
}
181+
}
+31Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Symfony\Component\Security\Guard;
4+
5+
use Symfony\Component\Security\Core\User\UserInterface;
6+
use Symfony\Component\Security\Guard\Token\GenericGuardToken;
7+
8+
/**
9+
* An optional base class that creates a GenericGuardToken for you
10+
*
11+
* @author Ryan Weaver <weaverryan@gmail.com>
12+
*/
13+
abstract class AbstractGuardAuthenticator implements GuardAuthenticatorInterface
14+
{
15+
/**
16+
* Shortcut to create a GenericGuardToken for you, if you don't really
17+
* care about which authenticated token you're using
18+
*
19+
* @param UserInterface $user
20+
* @param string $providerKey
21+
* @return GenericGuardToken
22+
*/
23+
public function createAuthenticatedToken(UserInterface $user, $providerKey)
24+
{
25+
return new GenericGuardToken(
26+
$user,
27+
$providerKey,
28+
$user->getRoles()
29+
);
30+
}
31+
}

0 commit comments

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