Skip to content

Commit 2476020

Browse files
committed
Bugfix | Initialize OAuthAuthenticatorFactory when possible as security factory
1 parent fda73cb commit 2476020

4 files changed

Lines changed: 227 additions & 8 deletions

File tree

DependencyInjection/Security/Factory/OAuthAuthenticatorFactory.php

Lines changed: 225 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,21 @@
1212
namespace HWI\Bundle\OAuthBundle\DependencyInjection\Security\Factory;
1313

1414
use HWI\Bundle\OAuthBundle\Security\Http\Authenticator\OAuthAuthenticator;
15+
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AbstractFactory;
1516
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AuthenticatorFactoryInterface;
17+
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
18+
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
19+
use Symfony\Component\DependencyInjection\ChildDefinition;
1620
use Symfony\Component\DependencyInjection\ContainerBuilder;
21+
use Symfony\Component\DependencyInjection\Parameter;
1722
use Symfony\Component\DependencyInjection\Reference;
1823

1924
/**
25+
* @author Geoffrey Bachelet <geoffrey.bachelet@gmail.com>
26+
* @author Alexander <iam.asm89@gmail.com>
2027
* @author Vadim Borodavko <vadim.borodavko@gmail.com>
2128
*/
22-
final class OAuthAuthenticatorFactory extends OAuthFactory implements AuthenticatorFactoryInterface
29+
final class OAuthAuthenticatorFactory extends AbstractFactory implements AuthenticatorFactoryInterface
2330
{
2431
/**
2532
* {@inheritdoc}
@@ -48,4 +55,221 @@ public function createAuthenticator(
4855

4956
return $authenticatorId;
5057
}
58+
59+
public function getPriority(): int
60+
{
61+
return 0;
62+
}
63+
64+
/**
65+
* @param ArrayNodeDefinition $node
66+
*/
67+
public function addConfiguration(NodeDefinition $node): void
68+
{
69+
parent::addConfiguration($node);
70+
71+
$builder = $node->children();
72+
$builder
73+
->scalarNode('login_path')->cannotBeEmpty()->isRequired()->end()
74+
;
75+
76+
$this->addOAuthProviderConfiguration($node);
77+
$this->addResourceOwnersConfiguration($node);
78+
}
79+
80+
/**
81+
* {@inheritdoc}
82+
*/
83+
public function getKey(): string
84+
{
85+
return 'oauth';
86+
}
87+
88+
/**
89+
* {@inheritdoc}
90+
*/
91+
public function getPosition(): string
92+
{
93+
return 'http';
94+
}
95+
96+
/**
97+
* Creates a resource owner map for the given configuration.
98+
*
99+
* @param ContainerBuilder $container Container to build for
100+
* @param string $id Firewall id
101+
* @param array $config Configuration
102+
*/
103+
protected function createResourceOwnerMap(ContainerBuilder $container, string $id, array $config): void
104+
{
105+
$resourceOwnersMap = [];
106+
foreach ($config['resource_owners'] as $name => $checkPath) {
107+
$resourceOwnersMap[$name] = $checkPath;
108+
}
109+
$container->setParameter('hwi_oauth.resource_ownermap.configured.'.$id, $resourceOwnersMap);
110+
111+
$container
112+
->setDefinition($this->getResourceOwnerMapReference($id), new ChildDefinition('hwi_oauth.abstract_resource_ownermap'))
113+
->replaceArgument('$resourceOwners', new Parameter('hwi_oauth.resource_ownermap.configured.'.$id))
114+
->setPublic(true)
115+
;
116+
}
117+
118+
/**
119+
* Gets a reference to the resource owner map.
120+
*/
121+
protected function getResourceOwnerMapReference(string $id): Reference
122+
{
123+
return new Reference('hwi_oauth.resource_ownermap.'.$id);
124+
}
125+
126+
/**
127+
* {@inheritdoc}
128+
*/
129+
protected function createAuthProvider(ContainerBuilder $container, string $id, array $config, string $userProviderId): string
130+
{
131+
$providerId = 'hwi_oauth.authentication.provider.oauth.'.$id;
132+
133+
$this->createResourceOwnerMap($container, $id, $config);
134+
135+
$container
136+
->setDefinition($providerId, new ChildDefinition('hwi_oauth.authentication.provider.oauth'))
137+
->addArgument($this->createOAuthAwareUserProvider($container, $id, $config['oauth_user_provider']))
138+
->addArgument($this->getResourceOwnerMapReference($id))
139+
->addArgument(new Reference('hwi_oauth.user_checker'))
140+
->addArgument(new Reference('security.token_storage'))
141+
;
142+
143+
return $providerId;
144+
}
145+
146+
protected function createOAuthAwareUserProvider(ContainerBuilder $container, $id, $config): Reference
147+
{
148+
$serviceId = 'hwi_oauth.user.provider.entity.'.$id;
149+
150+
// todo: move this to factories?
151+
switch (key($config)) {
152+
case 'oauth':
153+
$container
154+
->setDefinition($serviceId, new ChildDefinition('hwi_oauth.user.provider'))
155+
;
156+
break;
157+
case 'orm':
158+
$container
159+
->setDefinition($serviceId, new ChildDefinition('hwi_oauth.user.provider.entity'))
160+
->addArgument($config['orm']['class'])
161+
->addArgument($config['orm']['properties'])
162+
->addArgument($config['orm']['manager_name'])
163+
;
164+
break;
165+
case 'service':
166+
$container
167+
->setAlias($serviceId, $config['service']);
168+
break;
169+
}
170+
171+
return new Reference($serviceId);
172+
}
173+
174+
/**
175+
* {@inheritdoc}
176+
*/
177+
protected function createEntryPoint($container, $id, $config, ?string $defaultEntryPointId): ?string
178+
{
179+
$entryPointId = 'hwi_oauth.authentication.entry_point.oauth.'.$id;
180+
181+
$container
182+
->setDefinition($entryPointId, new ChildDefinition('hwi_oauth.authentication.entry_point.oauth'))
183+
->addArgument($config['login_path'])
184+
->addArgument($config['use_forward'])
185+
;
186+
187+
return $entryPointId;
188+
}
189+
190+
/**
191+
* {@inheritdoc}
192+
*/
193+
protected function createListener(ContainerBuilder $container, string $id, array $config, string $userProvider): string
194+
{
195+
$listenerId = parent::createListener($container, $id, $config, $userProvider);
196+
197+
$checkPaths = $config['resource_owners'];
198+
199+
$container
200+
->getDefinition($listenerId)
201+
->addMethodCall('setResourceOwnerMap', [$this->getResourceOwnerMapReference($id)])
202+
->addMethodCall('setCheckPaths', [$checkPaths])
203+
;
204+
205+
return $listenerId;
206+
}
207+
208+
/**
209+
* {@inheritdoc}
210+
*/
211+
protected function getListenerId(): string
212+
{
213+
return 'hwi_oauth.authentication.listener.oauth';
214+
}
215+
216+
private function addOAuthProviderConfiguration(ArrayNodeDefinition $node): void
217+
{
218+
$builder = $node->children();
219+
$builder
220+
->arrayNode('oauth_user_provider')
221+
->isRequired()
222+
->children()
223+
->arrayNode('orm')
224+
->children()
225+
->scalarNode('class')->isRequired()->cannotBeEmpty()->end()
226+
->scalarNode('manager_name')->defaultNull()->end()
227+
->arrayNode('properties')
228+
->isRequired()
229+
->useAttributeAsKey('name')
230+
->prototype('scalar')
231+
->end()
232+
->end()
233+
->end()
234+
->end()
235+
->scalarNode('service')->cannotBeEmpty()->end()
236+
->scalarNode('oauth')->end()
237+
->end()
238+
->validate()
239+
->ifTrue(function ($c) {
240+
return 1 !== \count($c) || !\in_array(key($c), ['oauth', 'orm', 'service'], true);
241+
})
242+
->thenInvalid("You should configure (only) one of: 'oauth', 'orm', 'service'.")
243+
->end()
244+
->end()
245+
;
246+
}
247+
248+
private function addResourceOwnersConfiguration(ArrayNodeDefinition $node): void
249+
{
250+
$builder = $node->children();
251+
$builder
252+
->arrayNode('resource_owners')
253+
->isRequired()
254+
->useAttributeAsKey('name')
255+
->prototype('scalar')
256+
->end()
257+
->validate()
258+
->ifTrue(function ($c) {
259+
$checkPaths = [];
260+
foreach ($c as $checkPath) {
261+
if (\in_array($checkPath, $checkPaths, true)) {
262+
return true;
263+
}
264+
265+
$checkPaths[] = $checkPath;
266+
}
267+
268+
return false;
269+
})
270+
->thenInvalid('Each resource owner should have a unique "check_path".')
271+
->end()
272+
->end()
273+
;
274+
}
51275
}

DependencyInjection/Security/Factory/OAuthFactory.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@
1919
use Symfony\Component\DependencyInjection\Parameter;
2020
use Symfony\Component\DependencyInjection\Reference;
2121

22-
/**
23-
* @author Geoffrey Bachelet <geoffrey.bachelet@gmail.com>
24-
* @author Alexander <iam.asm89@gmail.com>
25-
*/
2622
class OAuthFactory extends AbstractFactory
2723
{
2824
/**

HWIOAuthBundle.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ public function build(ContainerBuilder $container)
3737
/** @var SecurityExtension $extension */
3838
$extension = $container->getExtension('security');
3939

40-
// Symfony < 5.1 BC layer: support new Authenticator-based security system in Symfony 5.1+
41-
// and old security system in all Symfony versions.
40+
// Symfony < 5.4 BC layer
4241
if (interface_exists(AuthenticatorFactoryInterface::class)) {
4342
$extension->addAuthenticatorFactory(new OAuthAuthenticatorFactory());
4443
} else {

Resources/config/oauth.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
Hey there, welcome to this bundle's DIC configuration !
1010
1111
Did you know that most of these services definition
12-
are modified by the OAuthFactory? Or setup in the Extension?
12+
are modified by the OAuthAuthenticatorFactory? Or setup in the Extension?
1313
1414
Cheers!
1515
-->

0 commit comments

Comments
 (0)