The start of something beautiful
This commit is contained in:
Vendored
+62
@@ -0,0 +1,62 @@
|
||||
<?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\AccessToken;
|
||||
|
||||
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\Security\Http\AccessToken\Cas\Cas2Handler;
|
||||
|
||||
class CasTokenHandlerFactory implements TokenHandlerFactoryInterface
|
||||
{
|
||||
public function create(ContainerBuilder $container, string $id, array|string $config): void
|
||||
{
|
||||
$container->setDefinition($id, new ChildDefinition('security.access_token_handler.cas'));
|
||||
|
||||
$container
|
||||
->register('security.access_token_handler.cas', Cas2Handler::class)
|
||||
->setArguments([
|
||||
new Reference('request_stack'),
|
||||
$config['validation_url'],
|
||||
$config['prefix'],
|
||||
$config['http_client'] ? new Reference($config['http_client']) : null,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return 'cas';
|
||||
}
|
||||
|
||||
public function addConfiguration(NodeBuilder $node): void
|
||||
{
|
||||
$node
|
||||
->arrayNode($this->getKey())
|
||||
->fixXmlConfig($this->getKey())
|
||||
->children()
|
||||
->scalarNode('validation_url')
|
||||
->info('CAS server validation URL')
|
||||
->isRequired()
|
||||
->end()
|
||||
->scalarNode('prefix')
|
||||
->info('CAS prefix')
|
||||
->defaultValue('cas')
|
||||
->end()
|
||||
->scalarNode('http_client')
|
||||
->info('HTTP Client service')
|
||||
->defaultNull()
|
||||
->end()
|
||||
->end()
|
||||
->end();
|
||||
}
|
||||
}
|
||||
Vendored
+122
@@ -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\AccessToken;
|
||||
|
||||
use Jose\Component\Core\Algorithm;
|
||||
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
|
||||
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
|
||||
/**
|
||||
* Configures a token handler for decoding and validating an OIDC token.
|
||||
*/
|
||||
class OidcTokenHandlerFactory implements TokenHandlerFactoryInterface
|
||||
{
|
||||
public function create(ContainerBuilder $container, string $id, array|string $config): void
|
||||
{
|
||||
$tokenHandlerDefinition = $container->setDefinition($id, (new ChildDefinition('security.access_token_handler.oidc'))
|
||||
->replaceArgument(2, $config['audience'])
|
||||
->replaceArgument(3, $config['issuers'])
|
||||
->replaceArgument(4, $config['claim'])
|
||||
);
|
||||
|
||||
if (!ContainerBuilder::willBeAvailable('web-token/jwt-library', Algorithm::class, ['symfony/security-bundle'])) {
|
||||
throw new LogicException('You cannot use the "oidc" token handler since "web-token/jwt-library" is not installed. Try running "composer require web-token/jwt-library".');
|
||||
}
|
||||
|
||||
$tokenHandlerDefinition->replaceArgument(0, (new ChildDefinition('security.access_token_handler.oidc.signature'))
|
||||
->replaceArgument(0, $config['algorithms']));
|
||||
|
||||
$tokenHandlerDefinition->replaceArgument(1, (new ChildDefinition('security.access_token_handler.oidc.jwkset'))
|
||||
->replaceArgument(0, $config['keyset'])
|
||||
);
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return 'oidc';
|
||||
}
|
||||
|
||||
public function addConfiguration(NodeBuilder $node): void
|
||||
{
|
||||
$node
|
||||
->arrayNode($this->getKey())
|
||||
->fixXmlConfig($this->getKey())
|
||||
->validate()
|
||||
->ifTrue(static fn ($v) => !isset($v['algorithm']) && !isset($v['algorithms']))
|
||||
->thenInvalid('You must set either "algorithm" or "algorithms".')
|
||||
->end()
|
||||
->validate()
|
||||
->ifTrue(static fn ($v) => !isset($v['key']) && !isset($v['keyset']))
|
||||
->thenInvalid('You must set either "key" or "keyset".')
|
||||
->end()
|
||||
->beforeNormalization()
|
||||
->ifTrue(static fn ($v) => isset($v['algorithm']) && \is_string($v['algorithm']))
|
||||
->then(static function ($v) {
|
||||
if (isset($v['algorithms'])) {
|
||||
throw new InvalidConfigurationException('You cannot use both "algorithm" and "algorithms" at the same time.');
|
||||
}
|
||||
$v['algorithms'] = [$v['algorithm']];
|
||||
unset($v['algorithm']);
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end()
|
||||
->beforeNormalization()
|
||||
->ifTrue(static fn ($v) => isset($v['key']) && \is_string($v['key']))
|
||||
->then(static function ($v) {
|
||||
if (isset($v['keyset'])) {
|
||||
throw new InvalidConfigurationException('You cannot use both "key" and "keyset" at the same time.');
|
||||
}
|
||||
$v['keyset'] = sprintf('{"keys":[%s]}', $v['key']);
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end()
|
||||
->children()
|
||||
->scalarNode('claim')
|
||||
->info('Claim which contains the user identifier (e.g.: sub, email..).')
|
||||
->defaultValue('sub')
|
||||
->end()
|
||||
->scalarNode('audience')
|
||||
->info('Audience set in the token, for validation purpose.')
|
||||
->isRequired()
|
||||
->end()
|
||||
->arrayNode('issuers')
|
||||
->info('Issuers allowed to generate the token, for validation purpose.')
|
||||
->isRequired()
|
||||
->scalarPrototype()->end()
|
||||
->end()
|
||||
->arrayNode('algorithm')
|
||||
->info('Algorithm used to sign the token.')
|
||||
->setDeprecated('symfony/security-bundle', '7.1', 'The "%node%" option is deprecated and will be removed in 8.0. Use the "algorithms" option instead.')
|
||||
->end()
|
||||
->arrayNode('algorithms')
|
||||
->info('Algorithms used to sign the token.')
|
||||
->isRequired()
|
||||
->scalarPrototype()->end()
|
||||
->end()
|
||||
->scalarNode('key')
|
||||
->info('JSON-encoded JWK used to sign the token (must contain a "kty" key).')
|
||||
->setDeprecated('symfony/security-bundle', '7.1', 'The "%node%" option is deprecated and will be removed in 8.0. Use the "keyset" option instead.')
|
||||
->end()
|
||||
->scalarNode('keyset')
|
||||
->info('JSON-encoded JWKSet used to sign the token (must contain a list of valid keys).')
|
||||
->isRequired()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
<?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\AccessToken;
|
||||
|
||||
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
|
||||
/**
|
||||
* Configures a token handler for an OIDC server.
|
||||
*/
|
||||
class OidcUserInfoTokenHandlerFactory implements TokenHandlerFactoryInterface
|
||||
{
|
||||
public function create(ContainerBuilder $container, string $id, array|string $config): void
|
||||
{
|
||||
$clientDefinition = (new ChildDefinition('security.access_token_handler.oidc_user_info.http_client'))
|
||||
->replaceArgument(0, ['base_uri' => $config['base_uri']]);
|
||||
|
||||
if (isset($config['client'])) {
|
||||
$clientDefinition->setFactory([new Reference($config['client']), 'withOptions']);
|
||||
} elseif (!ContainerBuilder::willBeAvailable('symfony/http-client', HttpClientInterface::class, ['symfony/security-bundle'])) {
|
||||
throw new LogicException('You cannot use the "oidc_user_info" token handler since the HttpClient component is not installed. Try running "composer require symfony/http-client".');
|
||||
}
|
||||
|
||||
$container->setDefinition($id, new ChildDefinition('security.access_token_handler.oidc_user_info'))
|
||||
->replaceArgument(0, $clientDefinition)
|
||||
->replaceArgument(2, $config['claim']);
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return 'oidc_user_info';
|
||||
}
|
||||
|
||||
public function addConfiguration(NodeBuilder $node): void
|
||||
{
|
||||
$node
|
||||
->arrayNode($this->getKey())
|
||||
->fixXmlConfig($this->getKey())
|
||||
->beforeNormalization()
|
||||
->ifString()
|
||||
->then(fn ($v) => ['claim' => 'sub', 'base_uri' => $v])
|
||||
->end()
|
||||
->children()
|
||||
->scalarNode('base_uri')
|
||||
->info('Base URI of the userinfo endpoint on the OIDC server.')
|
||||
->isRequired()
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->scalarNode('claim')
|
||||
->info('Claim which contains the user identifier (e.g. sub, email, etc.).')
|
||||
->defaultValue('sub')
|
||||
->cannotBeEmpty()
|
||||
->end()
|
||||
->scalarNode('client')
|
||||
->info('HttpClient service id to use to call the OIDC server.')
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
<?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\AccessToken;
|
||||
|
||||
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* Configures a token handler from a service id.
|
||||
*
|
||||
* @see \Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Security\Factory\AccessTokenFactoryTest
|
||||
*/
|
||||
class ServiceTokenHandlerFactory implements TokenHandlerFactoryInterface
|
||||
{
|
||||
public function create(ContainerBuilder $container, string $id, array|string $config): void
|
||||
{
|
||||
$container->setDefinition($id, new ChildDefinition($config));
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return 'id';
|
||||
}
|
||||
|
||||
public function addConfiguration(NodeBuilder $node): void
|
||||
{
|
||||
$node->scalarNode($this->getKey())->end();
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<?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\AccessToken;
|
||||
|
||||
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* Allows creating configurable token handlers.
|
||||
*/
|
||||
interface TokenHandlerFactoryInterface
|
||||
{
|
||||
/**
|
||||
* Creates a generic token handler service.
|
||||
*/
|
||||
public function create(ContainerBuilder $container, string $id, array|string $config): void;
|
||||
|
||||
/**
|
||||
* Gets a generic token handler configuration key.
|
||||
*/
|
||||
public function getKey(): string;
|
||||
|
||||
/**
|
||||
* Adds a generic token handler configuration.
|
||||
*/
|
||||
public function addConfiguration(NodeBuilder $node): void;
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
<?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\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Lukas Kahwe Smith <smith@pooteeweet.org>
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
abstract class AbstractFactory implements AuthenticatorFactoryInterface
|
||||
{
|
||||
protected array $options = [
|
||||
'check_path' => '/login_check',
|
||||
'use_forward' => false,
|
||||
'login_path' => '/login',
|
||||
];
|
||||
|
||||
protected array $defaultSuccessHandlerOptions = [
|
||||
'always_use_default_target_path' => false,
|
||||
'default_target_path' => '/',
|
||||
'login_path' => '/login',
|
||||
'target_path_parameter' => '_target_path',
|
||||
'use_referer' => false,
|
||||
];
|
||||
|
||||
protected array $defaultFailureHandlerOptions = [
|
||||
'failure_path' => null,
|
||||
'failure_forward' => false,
|
||||
'login_path' => '/login',
|
||||
'failure_path_parameter' => '_failure_path',
|
||||
];
|
||||
|
||||
final public function addOption(string $name, mixed $default = null): void
|
||||
{
|
||||
$this->options[$name] = $default;
|
||||
}
|
||||
|
||||
public function addConfiguration(NodeDefinition $node): void
|
||||
{
|
||||
$builder = $node->children();
|
||||
|
||||
$builder
|
||||
->scalarNode('provider')->end()
|
||||
->booleanNode('remember_me')->defaultTrue()->end()
|
||||
->scalarNode('success_handler')->end()
|
||||
->scalarNode('failure_handler')->end()
|
||||
;
|
||||
|
||||
foreach (array_merge($this->options, $this->defaultSuccessHandlerOptions, $this->defaultFailureHandlerOptions) as $name => $default) {
|
||||
if (\is_bool($default)) {
|
||||
$builder->booleanNode($name)->defaultValue($default);
|
||||
} else {
|
||||
$builder->scalarNode($name)->defaultValue($default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function createAuthenticationSuccessHandler(ContainerBuilder $container, string $id, array $config): string
|
||||
{
|
||||
$successHandlerId = $this->getSuccessHandlerId($id);
|
||||
$options = array_intersect_key($config, $this->defaultSuccessHandlerOptions);
|
||||
|
||||
if (isset($config['success_handler'])) {
|
||||
$successHandler = $container->setDefinition($successHandlerId, new ChildDefinition('security.authentication.custom_success_handler'));
|
||||
$successHandler->replaceArgument(0, new ChildDefinition($config['success_handler']));
|
||||
$successHandler->replaceArgument(1, $options);
|
||||
$successHandler->replaceArgument(2, $id);
|
||||
} else {
|
||||
$successHandler = $container->setDefinition($successHandlerId, new ChildDefinition('security.authentication.success_handler'));
|
||||
$successHandler->addMethodCall('setOptions', [$options]);
|
||||
$successHandler->addMethodCall('setFirewallName', [$id]);
|
||||
}
|
||||
|
||||
return $successHandlerId;
|
||||
}
|
||||
|
||||
protected function createAuthenticationFailureHandler(ContainerBuilder $container, string $id, array $config): string
|
||||
{
|
||||
$id = $this->getFailureHandlerId($id);
|
||||
$options = array_intersect_key($config, $this->defaultFailureHandlerOptions);
|
||||
|
||||
if (isset($config['failure_handler'])) {
|
||||
$failureHandler = $container->setDefinition($id, new ChildDefinition('security.authentication.custom_failure_handler'));
|
||||
$failureHandler->replaceArgument(0, new ChildDefinition($config['failure_handler']));
|
||||
$failureHandler->replaceArgument(1, $options);
|
||||
} else {
|
||||
$failureHandler = $container->setDefinition($id, new ChildDefinition('security.authentication.failure_handler'));
|
||||
$failureHandler->addMethodCall('setOptions', [$options]);
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
protected function getSuccessHandlerId(string $id): string
|
||||
{
|
||||
return 'security.authentication.success_handler.'.$id.'.'.str_replace('-', '_', $this->getKey());
|
||||
}
|
||||
|
||||
protected function getFailureHandlerId(string $id): string
|
||||
{
|
||||
return 'security.authentication.failure_handler.'.$id.'.'.str_replace('-', '_', $this->getKey());
|
||||
}
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
<?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\Bundle\SecurityBundle\DependencyInjection\Security\AccessToken\TokenHandlerFactoryInterface;
|
||||
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
|
||||
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* AccessTokenFactory creates services for Access Token authentication.
|
||||
*
|
||||
* @author Florent Morselli <florent.morselli@spomky-labs.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class AccessTokenFactory extends AbstractFactory implements StatelessAuthenticatorFactoryInterface
|
||||
{
|
||||
private const PRIORITY = -40;
|
||||
|
||||
/**
|
||||
* @param array<TokenHandlerFactoryInterface> $tokenHandlerFactories
|
||||
*/
|
||||
public function __construct(private readonly array $tokenHandlerFactories)
|
||||
{
|
||||
$this->options = [];
|
||||
$this->defaultFailureHandlerOptions = [];
|
||||
$this->defaultSuccessHandlerOptions = [];
|
||||
}
|
||||
|
||||
public function addConfiguration(NodeDefinition $node): void
|
||||
{
|
||||
parent::addConfiguration($node);
|
||||
|
||||
$builder = $node->children();
|
||||
$builder
|
||||
->scalarNode('realm')->defaultNull()->end()
|
||||
->arrayNode('token_extractors')
|
||||
->fixXmlConfig('token_extractors')
|
||||
->beforeNormalization()
|
||||
->ifString()
|
||||
->then(fn ($v) => [$v])
|
||||
->end()
|
||||
->cannotBeEmpty()
|
||||
->defaultValue([
|
||||
'security.access_token_extractor.header',
|
||||
])
|
||||
->scalarPrototype()->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
$tokenHandlerNodeBuilder = $builder
|
||||
->arrayNode('token_handler')
|
||||
->example([
|
||||
'id' => 'App\Security\CustomTokenHandler',
|
||||
])
|
||||
|
||||
->beforeNormalization()
|
||||
->ifString()
|
||||
->then(fn ($v) => ['id' => $v])
|
||||
->end()
|
||||
|
||||
->beforeNormalization()
|
||||
->ifTrue(fn ($v) => \is_array($v) && 1 < \count($v))
|
||||
->then(fn () => throw new InvalidConfigurationException('You cannot configure multiple token handlers.'))
|
||||
->end()
|
||||
|
||||
// "isRequired" must be set otherwise the following custom validation is not called
|
||||
->isRequired()
|
||||
->beforeNormalization()
|
||||
->ifTrue(fn ($v) => \is_array($v) && !$v)
|
||||
->then(fn () => throw new InvalidConfigurationException('You must set a token handler.'))
|
||||
->end()
|
||||
|
||||
->children()
|
||||
;
|
||||
|
||||
foreach ($this->tokenHandlerFactories as $factory) {
|
||||
$factory->addConfiguration($tokenHandlerNodeBuilder);
|
||||
}
|
||||
|
||||
$tokenHandlerNodeBuilder->end();
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return self::PRIORITY;
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return 'access_token';
|
||||
}
|
||||
|
||||
public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, ?string $userProviderId): string
|
||||
{
|
||||
$successHandler = isset($config['success_handler']) ? new Reference($this->createAuthenticationSuccessHandler($container, $firewallName, $config)) : null;
|
||||
$failureHandler = isset($config['failure_handler']) ? new Reference($this->createAuthenticationFailureHandler($container, $firewallName, $config)) : null;
|
||||
$authenticatorId = sprintf('security.authenticator.access_token.%s', $firewallName);
|
||||
$extractorId = $this->createExtractor($container, $firewallName, $config['token_extractors']);
|
||||
$tokenHandlerId = $this->createTokenHandler($container, $firewallName, $config['token_handler'], $userProviderId);
|
||||
|
||||
$container
|
||||
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.access_token'))
|
||||
->replaceArgument(0, new Reference($tokenHandlerId))
|
||||
->replaceArgument(1, new Reference($extractorId))
|
||||
->replaceArgument(2, $userProviderId ? new Reference($userProviderId) : null)
|
||||
->replaceArgument(3, $successHandler)
|
||||
->replaceArgument(4, $failureHandler)
|
||||
->replaceArgument(5, $config['realm'])
|
||||
;
|
||||
|
||||
return $authenticatorId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string> $extractors
|
||||
*/
|
||||
private function createExtractor(ContainerBuilder $container, string $firewallName, array $extractors): string
|
||||
{
|
||||
$aliases = [
|
||||
'query_string' => 'security.access_token_extractor.query_string',
|
||||
'request_body' => 'security.access_token_extractor.request_body',
|
||||
'header' => 'security.access_token_extractor.header',
|
||||
];
|
||||
$extractors = array_map(fn ($extractor) => $aliases[$extractor] ?? $extractor, $extractors);
|
||||
|
||||
if (1 === \count($extractors)) {
|
||||
return current($extractors);
|
||||
}
|
||||
$extractorId = sprintf('security.authenticator.access_token.chain_extractor.%s', $firewallName);
|
||||
$container
|
||||
->setDefinition($extractorId, new ChildDefinition('security.authenticator.access_token.chain_extractor'))
|
||||
->replaceArgument(0, array_map(fn (string $extractorId): Reference => new Reference($extractorId), $extractors))
|
||||
;
|
||||
|
||||
return $extractorId;
|
||||
}
|
||||
|
||||
private function createTokenHandler(ContainerBuilder $container, string $firewallName, array $config, ?string $userProviderId): string
|
||||
{
|
||||
$key = array_keys($config)[0];
|
||||
$id = sprintf('security.access_token_handler.%s', $firewallName);
|
||||
|
||||
foreach ($this->tokenHandlerFactories as $factory) {
|
||||
if ($key !== $factory->getKey()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$factory->create($container, $id, $config[$key], $userProviderId);
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
interface AuthenticatorFactoryInterface
|
||||
{
|
||||
/**
|
||||
* Defines the priority at which the authenticator is called.
|
||||
*/
|
||||
public function getPriority(): int;
|
||||
|
||||
/**
|
||||
* Defines the configuration key used to reference the provider
|
||||
* in the firewall configuration.
|
||||
*/
|
||||
public function getKey(): string;
|
||||
|
||||
public function addConfiguration(NodeDefinition $builder): void;
|
||||
|
||||
/**
|
||||
* Creates the authenticator service(s) for the provided configuration.
|
||||
*
|
||||
* @param array<string, mixed> $config
|
||||
*
|
||||
* @return string|string[] The authenticator service ID(s) to be used by the firewall
|
||||
*/
|
||||
public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): string|array;
|
||||
}
|
||||
Vendored
+64
@@ -0,0 +1,64 @@
|
||||
<?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\ArrayNodeDefinition;
|
||||
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class CustomAuthenticatorFactory implements AuthenticatorFactoryInterface
|
||||
{
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return 'custom_authenticators';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayNodeDefinition $builder
|
||||
*/
|
||||
public function addConfiguration(NodeDefinition $builder): void
|
||||
{
|
||||
$builder
|
||||
->info('An array of service ids for all of your "authenticators"')
|
||||
->requiresAtLeastOneElement()
|
||||
->prototype('scalar')->end();
|
||||
|
||||
// get the parent array node builder ("firewalls") from inside the children builder
|
||||
$factoryRootNode = $builder->end()->end();
|
||||
$factoryRootNode
|
||||
->fixXmlConfig('custom_authenticator')
|
||||
->validate()
|
||||
->ifTrue(fn ($v) => isset($v['custom_authenticators']) && empty($v['custom_authenticators']))
|
||||
->then(function ($v) {
|
||||
unset($v['custom_authenticators']);
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end()
|
||||
;
|
||||
}
|
||||
|
||||
public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): array
|
||||
{
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
+31
@@ -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\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* Can be implemented by a security factory to add a listener to the firewall.
|
||||
*
|
||||
* @author Christian Scheb <me@christianscheb.de>
|
||||
*/
|
||||
interface FirewallListenerFactoryInterface
|
||||
{
|
||||
/**
|
||||
* Creates the firewall listener services for the provided configuration.
|
||||
*
|
||||
* @param array<string, mixed> $config
|
||||
*
|
||||
* @return string[] The listener service IDs to be used by the firewall
|
||||
*/
|
||||
public function createListeners(ContainerBuilder $container, string $firewallName, array $config): array;
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
<?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\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* FormLoginFactory creates services for form login authentication.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class FormLoginFactory extends AbstractFactory
|
||||
{
|
||||
public const PRIORITY = -30;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->addOption('username_parameter', '_username');
|
||||
$this->addOption('password_parameter', '_password');
|
||||
$this->addOption('csrf_parameter', '_csrf_token');
|
||||
$this->addOption('csrf_token_id', 'authenticate');
|
||||
$this->addOption('enable_csrf', false);
|
||||
$this->addOption('post_only', true);
|
||||
$this->addOption('form_only', false);
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return self::PRIORITY;
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return 'form-login';
|
||||
}
|
||||
|
||||
public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): string
|
||||
{
|
||||
$authenticatorId = 'security.authenticator.form_login.'.$firewallName;
|
||||
$options = array_intersect_key($config, $this->options);
|
||||
$authenticator = $container
|
||||
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.form_login'))
|
||||
->replaceArgument(1, new Reference($userProviderId))
|
||||
->replaceArgument(2, new Reference($this->createAuthenticationSuccessHandler($container, $firewallName, $config)))
|
||||
->replaceArgument(3, new Reference($this->createAuthenticationFailureHandler($container, $firewallName, $config)))
|
||||
->replaceArgument(4, $options);
|
||||
|
||||
if ($options['use_forward'] ?? false) {
|
||||
$authenticator->addMethodCall('setHttpKernel', [new Reference('http_kernel')]);
|
||||
}
|
||||
|
||||
return $authenticatorId;
|
||||
}
|
||||
}
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* FormLoginLdapFactory creates services for form login ldap authentication.
|
||||
*
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
* @author Charles Sarrazin <charles@sarraz.in>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class FormLoginLdapFactory extends FormLoginFactory
|
||||
{
|
||||
use LdapFactoryTrait;
|
||||
|
||||
public function addConfiguration(NodeDefinition $node): void
|
||||
{
|
||||
parent::addConfiguration($node);
|
||||
|
||||
$node
|
||||
->children()
|
||||
->scalarNode('service')->defaultValue('ldap')->end()
|
||||
->scalarNode('dn_string')->defaultValue('{user_identifier}')->end()
|
||||
->scalarNode('query_string')->end()
|
||||
->scalarNode('search_dn')->defaultValue('')->end()
|
||||
->scalarNode('search_password')->defaultValue('')->end()
|
||||
->end()
|
||||
;
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
<?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\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* HttpBasicFactory creates services for HTTP basic authentication.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class HttpBasicFactory implements AuthenticatorFactoryInterface
|
||||
{
|
||||
public const PRIORITY = -50;
|
||||
|
||||
public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): string
|
||||
{
|
||||
$authenticatorId = 'security.authenticator.http_basic.'.$firewallName;
|
||||
$container
|
||||
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.http_basic'))
|
||||
->replaceArgument(0, $config['realm'])
|
||||
->replaceArgument(1, new Reference($userProviderId));
|
||||
|
||||
return $authenticatorId;
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return self::PRIORITY;
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return 'http-basic';
|
||||
}
|
||||
|
||||
public function addConfiguration(NodeDefinition $node): void
|
||||
{
|
||||
$node
|
||||
->children()
|
||||
->scalarNode('provider')->end()
|
||||
->scalarNode('realm')->defaultValue('Secured Area')->end()
|
||||
->end()
|
||||
;
|
||||
}
|
||||
}
|
||||
Vendored
+87
@@ -0,0 +1,87 @@
|
||||
<?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\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\Security\Core\Exception\LogicException;
|
||||
|
||||
/**
|
||||
* HttpBasicFactory creates services for HTTP basic authentication.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
* @author Charles Sarrazin <charles@sarraz.in>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class HttpBasicLdapFactory extends HttpBasicFactory
|
||||
{
|
||||
use LdapFactoryTrait;
|
||||
|
||||
public function create(ContainerBuilder $container, string $id, array $config, string $userProvider, ?string $defaultEntryPoint): array
|
||||
{
|
||||
$provider = 'security.authentication.provider.ldap_bind.'.$id;
|
||||
$definition = $container
|
||||
->setDefinition($provider, new ChildDefinition('security.authentication.provider.ldap_bind'))
|
||||
->replaceArgument(0, new Reference($userProvider))
|
||||
->replaceArgument(1, new Reference('security.user_checker.'.$id))
|
||||
->replaceArgument(2, $id)
|
||||
->replaceArgument(3, new Reference($config['service']))
|
||||
->replaceArgument(4, $config['dn_string'])
|
||||
->replaceArgument(6, $config['search_dn'])
|
||||
->replaceArgument(7, $config['search_password'])
|
||||
;
|
||||
|
||||
// entry point
|
||||
$entryPointId = $defaultEntryPoint;
|
||||
|
||||
if (null === $entryPointId) {
|
||||
$entryPointId = 'security.authentication.basic_entry_point.'.$id;
|
||||
$container
|
||||
->setDefinition($entryPointId, new ChildDefinition('security.authentication.basic_entry_point'))
|
||||
->addArgument($config['realm']);
|
||||
}
|
||||
|
||||
if (!empty($config['query_string'])) {
|
||||
if ('' === $config['search_dn'] || '' === $config['search_password']) {
|
||||
throw new LogicException('Using the "query_string" config without using a "search_dn" and a "search_password" is not supported.');
|
||||
}
|
||||
$definition->addMethodCall('setQueryString', [$config['query_string']]);
|
||||
}
|
||||
|
||||
// listener
|
||||
$listenerId = 'security.authentication.listener.basic.'.$id;
|
||||
$listener = $container->setDefinition($listenerId, new ChildDefinition('security.authentication.listener.basic'));
|
||||
$listener->replaceArgument(2, $id);
|
||||
$listener->replaceArgument(3, new Reference($entryPointId));
|
||||
|
||||
return [$provider, $listenerId, $entryPointId];
|
||||
}
|
||||
|
||||
public function addConfiguration(NodeDefinition $node): void
|
||||
{
|
||||
parent::addConfiguration($node);
|
||||
|
||||
$node
|
||||
->children()
|
||||
->scalarNode('service')->defaultValue('ldap')->end()
|
||||
->scalarNode('dn_string')->defaultValue('{user_identifier}')->end()
|
||||
->scalarNode('query_string')->end()
|
||||
->scalarNode('search_dn')->defaultValue('')->end()
|
||||
->scalarNode('search_password')->defaultValue('')->end()
|
||||
->end()
|
||||
;
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
<?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\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* JsonLoginFactory creates services for JSON login authentication.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class JsonLoginFactory extends AbstractFactory
|
||||
{
|
||||
public const PRIORITY = -40;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->addOption('username_path', 'username');
|
||||
$this->addOption('password_path', 'password');
|
||||
$this->defaultFailureHandlerOptions = [];
|
||||
$this->defaultSuccessHandlerOptions = [];
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return self::PRIORITY;
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return 'json-login';
|
||||
}
|
||||
|
||||
public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): string
|
||||
{
|
||||
$authenticatorId = 'security.authenticator.json_login.'.$firewallName;
|
||||
$options = array_intersect_key($config, $this->options);
|
||||
$container
|
||||
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.json_login'))
|
||||
->replaceArgument(1, new Reference($userProviderId))
|
||||
->replaceArgument(2, isset($config['success_handler']) ? new Reference($this->createAuthenticationSuccessHandler($container, $firewallName, $config)) : null)
|
||||
->replaceArgument(3, isset($config['failure_handler']) ? new Reference($this->createAuthenticationFailureHandler($container, $firewallName, $config)) : null)
|
||||
->replaceArgument(4, $options);
|
||||
|
||||
return $authenticatorId;
|
||||
}
|
||||
}
|
||||
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* JsonLoginLdapFactory creates services for json login ldap authentication.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class JsonLoginLdapFactory extends JsonLoginFactory
|
||||
{
|
||||
use LdapFactoryTrait;
|
||||
|
||||
public function addConfiguration(NodeDefinition $node): void
|
||||
{
|
||||
parent::addConfiguration($node);
|
||||
|
||||
$node
|
||||
->children()
|
||||
->scalarNode('service')->defaultValue('ldap')->end()
|
||||
->scalarNode('dn_string')->defaultValue('{user_identifier}')->end()
|
||||
->scalarNode('query_string')->end()
|
||||
->scalarNode('search_dn')->defaultValue('')->end()
|
||||
->scalarNode('search_password')->defaultValue('')->end()
|
||||
->end()
|
||||
;
|
||||
}
|
||||
}
|
||||
+65
@@ -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\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
|
||||
|
||||
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\Ldap\Security\CheckLdapCredentialsListener;
|
||||
use Symfony\Component\Ldap\Security\LdapAuthenticator;
|
||||
|
||||
/**
|
||||
* A trait decorating the authenticator with LDAP functionality.
|
||||
*
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait LdapFactoryTrait
|
||||
{
|
||||
public function getKey(): string
|
||||
{
|
||||
return parent::getKey().'-ldap';
|
||||
}
|
||||
|
||||
public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): string
|
||||
{
|
||||
$key = str_replace('-', '_', $this->getKey());
|
||||
$authenticatorId = parent::createAuthenticator($container, $firewallName, $config, $userProviderId);
|
||||
|
||||
$container->setDefinition('security.listener.'.$key.'.'.$firewallName, new Definition(CheckLdapCredentialsListener::class))
|
||||
->addTag('kernel.event_subscriber', ['dispatcher' => 'security.event_dispatcher.'.$firewallName])
|
||||
->addArgument(new Reference('security.ldap_locator'))
|
||||
;
|
||||
|
||||
$ldapAuthenticatorId = 'security.authenticator.'.$key.'.'.$firewallName;
|
||||
$definition = $container->setDefinition($ldapAuthenticatorId, new Definition(LdapAuthenticator::class))
|
||||
->setArguments([
|
||||
new Reference($authenticatorId),
|
||||
$config['service'],
|
||||
$config['dn_string'],
|
||||
$config['search_dn'],
|
||||
$config['search_password'],
|
||||
]);
|
||||
|
||||
if (!empty($config['query_string'])) {
|
||||
if ('' === $config['search_dn'] || '' === $config['search_password']) {
|
||||
throw new InvalidConfigurationException('Using the "query_string" config without using a "search_dn" and a "search_password" is not supported.');
|
||||
}
|
||||
|
||||
$definition->addArgument($config['query_string']);
|
||||
}
|
||||
|
||||
return $ldapAuthenticatorId;
|
||||
}
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
<?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\NodeBuilder;
|
||||
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class LoginLinkFactory extends AbstractFactory
|
||||
{
|
||||
public const PRIORITY = -20;
|
||||
|
||||
public function addConfiguration(NodeDefinition $node): void
|
||||
{
|
||||
/** @var NodeBuilder $builder */
|
||||
$builder = $node->fixXmlConfig('signature_property', 'signature_properties')->children();
|
||||
|
||||
$builder
|
||||
->scalarNode('check_route')
|
||||
->isRequired()
|
||||
->info('Route that will validate the login link - e.g. "app_login_link_verify".')
|
||||
->end()
|
||||
->scalarNode('check_post_only')
|
||||
->defaultFalse()
|
||||
->info('If true, only HTTP POST requests to "check_route" will be handled by the authenticator.')
|
||||
->end()
|
||||
->arrayNode('signature_properties')
|
||||
->isRequired()
|
||||
->prototype('scalar')->end()
|
||||
->requiresAtLeastOneElement()
|
||||
->info('An array of properties on your User that are used to sign the link. If any of these change, all existing links will become invalid.')
|
||||
->example(['email', 'password'])
|
||||
->end()
|
||||
->integerNode('lifetime')
|
||||
->defaultValue(600)
|
||||
->info('The lifetime of the login link in seconds.')
|
||||
->end()
|
||||
->integerNode('max_uses')
|
||||
->defaultNull()
|
||||
->info('Max number of times a login link can be used - null means unlimited within lifetime.')
|
||||
->end()
|
||||
->scalarNode('used_link_cache')
|
||||
->info('Cache service id used to expired links of max_uses is set.')
|
||||
->end()
|
||||
->scalarNode('success_handler')
|
||||
->info(sprintf('A service id that implements %s.', AuthenticationSuccessHandlerInterface::class))
|
||||
->end()
|
||||
->scalarNode('failure_handler')
|
||||
->info(sprintf('A service id that implements %s.', AuthenticationFailureHandlerInterface::class))
|
||||
->end()
|
||||
->scalarNode('provider')
|
||||
->info('The user provider to load users from.')
|
||||
->end()
|
||||
;
|
||||
|
||||
foreach (array_merge($this->defaultSuccessHandlerOptions, $this->defaultFailureHandlerOptions) as $name => $default) {
|
||||
if (\is_bool($default)) {
|
||||
$builder->booleanNode($name)->defaultValue($default);
|
||||
} else {
|
||||
$builder->scalarNode($name)->defaultValue($default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return 'login-link';
|
||||
}
|
||||
|
||||
public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): string
|
||||
{
|
||||
if (!$container->hasDefinition('security.authenticator.login_link')) {
|
||||
$loader = new PhpFileLoader($container, new FileLocator(\dirname(__DIR__).'/../../Resources/config'));
|
||||
$loader->load('security_authenticator_login_link.php');
|
||||
}
|
||||
|
||||
if (null !== $config['max_uses'] && !isset($config['used_link_cache'])) {
|
||||
$config['used_link_cache'] = 'security.authenticator.cache.expired_links';
|
||||
$defaultCacheDefinition = $container->getDefinition($config['used_link_cache']);
|
||||
if (!$defaultCacheDefinition->hasTag('cache.pool')) {
|
||||
$defaultCacheDefinition->addTag('cache.pool');
|
||||
}
|
||||
}
|
||||
|
||||
$expiredStorageId = null;
|
||||
if (isset($config['used_link_cache'])) {
|
||||
$expiredStorageId = 'security.authenticator.expired_login_link_storage.'.$firewallName;
|
||||
$container
|
||||
->setDefinition($expiredStorageId, new ChildDefinition('security.authenticator.expired_login_link_storage'))
|
||||
->replaceArgument(0, new Reference($config['used_link_cache']))
|
||||
->replaceArgument(1, $config['lifetime']);
|
||||
}
|
||||
|
||||
$signatureHasherId = 'security.authenticator.login_link_signature_hasher.'.$firewallName;
|
||||
$container
|
||||
->setDefinition($signatureHasherId, new ChildDefinition('security.authenticator.abstract_login_link_signature_hasher'))
|
||||
->replaceArgument(1, $config['signature_properties'])
|
||||
->replaceArgument(3, $expiredStorageId ? new Reference($expiredStorageId) : null)
|
||||
->replaceArgument(4, $config['max_uses'] ?? null)
|
||||
;
|
||||
|
||||
$linkerId = 'security.authenticator.login_link_handler.'.$firewallName;
|
||||
$linkerOptions = [
|
||||
'route_name' => $config['check_route'],
|
||||
'lifetime' => $config['lifetime'],
|
||||
];
|
||||
$container
|
||||
->setDefinition($linkerId, new ChildDefinition('security.authenticator.abstract_login_link_handler'))
|
||||
->replaceArgument(1, new Reference($userProviderId))
|
||||
->replaceArgument(2, new Reference($signatureHasherId))
|
||||
->replaceArgument(3, $linkerOptions)
|
||||
->addTag('security.authenticator.login_linker', ['firewall' => $firewallName])
|
||||
;
|
||||
|
||||
$authenticatorId = 'security.authenticator.login_link.'.$firewallName;
|
||||
$container
|
||||
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.login_link'))
|
||||
->replaceArgument(0, new Reference($linkerId))
|
||||
->replaceArgument(2, new Reference($this->createAuthenticationSuccessHandler($container, $firewallName, $config)))
|
||||
->replaceArgument(3, new Reference($this->createAuthenticationFailureHandler($container, $firewallName, $config)))
|
||||
->replaceArgument(4, [
|
||||
'check_route' => $config['check_route'],
|
||||
'check_post_only' => $config['check_post_only'],
|
||||
]);
|
||||
|
||||
return $authenticatorId;
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return self::PRIORITY;
|
||||
}
|
||||
}
|
||||
Vendored
+120
@@ -0,0 +1,120 @@
|
||||
<?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\ArrayNodeDefinition;
|
||||
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Parameter;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\HttpFoundation\RateLimiter\RequestRateLimiterInterface;
|
||||
use Symfony\Component\Lock\LockInterface;
|
||||
use Symfony\Component\RateLimiter\RateLimiterFactory;
|
||||
use Symfony\Component\RateLimiter\Storage\CacheStorage;
|
||||
use Symfony\Component\Security\Http\RateLimiter\DefaultLoginRateLimiter;
|
||||
|
||||
/**
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class LoginThrottlingFactory implements AuthenticatorFactoryInterface
|
||||
{
|
||||
public function getPriority(): int
|
||||
{
|
||||
// this factory doesn't register any authenticators, this priority doesn't matter
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return 'login_throttling';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayNodeDefinition $builder
|
||||
*/
|
||||
public function addConfiguration(NodeDefinition $builder): void
|
||||
{
|
||||
$builder
|
||||
->children()
|
||||
->scalarNode('limiter')->info(sprintf('A service id implementing "%s".', RequestRateLimiterInterface::class))->end()
|
||||
->integerNode('max_attempts')->defaultValue(5)->end()
|
||||
->scalarNode('interval')->defaultValue('1 minute')->end()
|
||||
->scalarNode('lock_factory')->info('The service ID of the lock factory used by the login rate limiter (or null to disable locking)')->defaultNull()->end()
|
||||
->end();
|
||||
}
|
||||
|
||||
public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): array
|
||||
{
|
||||
if (!class_exists(RateLimiterFactory::class)) {
|
||||
throw new \LogicException('Login throttling requires the Rate Limiter component. Try running "composer require symfony/rate-limiter".');
|
||||
}
|
||||
|
||||
if (!isset($config['limiter'])) {
|
||||
$limiterOptions = [
|
||||
'policy' => 'fixed_window',
|
||||
'limit' => $config['max_attempts'],
|
||||
'interval' => $config['interval'],
|
||||
'lock_factory' => $config['lock_factory'],
|
||||
];
|
||||
$this->registerRateLimiter($container, $localId = '_login_local_'.$firewallName, $limiterOptions);
|
||||
|
||||
$limiterOptions['limit'] = 5 * $config['max_attempts'];
|
||||
$this->registerRateLimiter($container, $globalId = '_login_global_'.$firewallName, $limiterOptions);
|
||||
|
||||
$container->register($config['limiter'] = 'security.login_throttling.'.$firewallName.'.limiter', DefaultLoginRateLimiter::class)
|
||||
->addArgument(new Reference('limiter.'.$globalId))
|
||||
->addArgument(new Reference('limiter.'.$localId))
|
||||
->addArgument(new Parameter('container.build_hash'))
|
||||
;
|
||||
}
|
||||
|
||||
$container
|
||||
->setDefinition('security.listener.login_throttling.'.$firewallName, new ChildDefinition('security.listener.login_throttling'))
|
||||
->replaceArgument(1, new Reference($config['limiter']))
|
||||
->addTag('kernel.event_subscriber', ['dispatcher' => 'security.event_dispatcher.'.$firewallName]);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private function registerRateLimiter(ContainerBuilder $container, string $name, array $limiterConfig): void
|
||||
{
|
||||
// default configuration (when used by other DI extensions)
|
||||
$limiterConfig += ['lock_factory' => 'lock.factory', 'cache_pool' => 'cache.rate_limiter'];
|
||||
|
||||
$limiter = $container->setDefinition($limiterId = 'limiter.'.$name, new ChildDefinition('limiter'));
|
||||
|
||||
if (null !== $limiterConfig['lock_factory']) {
|
||||
if (!interface_exists(LockInterface::class)) {
|
||||
throw new LogicException(sprintf('Rate limiter "%s" requires the Lock component to be installed. Try running "composer require symfony/lock".', $name));
|
||||
}
|
||||
|
||||
$limiter->replaceArgument(2, new Reference($limiterConfig['lock_factory']));
|
||||
}
|
||||
unset($limiterConfig['lock_factory']);
|
||||
|
||||
if (null === $storageId = $limiterConfig['storage_service'] ?? null) {
|
||||
$container->register($storageId = 'limiter.storage.'.$name, CacheStorage::class)->addArgument(new Reference($limiterConfig['cache_pool']));
|
||||
}
|
||||
|
||||
$limiter->replaceArgument(1, new Reference($storageId));
|
||||
unset($limiterConfig['storage_service'], $limiterConfig['cache_pool']);
|
||||
|
||||
$limiterConfig['id'] = $name;
|
||||
$limiter->replaceArgument(0, $limiterConfig);
|
||||
|
||||
$container->registerAliasForArgument($limiterId, RateLimiterFactory::class, $name.'.limiter');
|
||||
}
|
||||
}
|
||||
+247
@@ -0,0 +1,247 @@
|
||||
<?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\Bridge\Doctrine\Security\RememberMe\DoctrineTokenProvider;
|
||||
use Symfony\Bundle\SecurityBundle\RememberMe\DecoratedRememberMeHandler;
|
||||
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
|
||||
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
||||
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
use Symfony\Component\Security\Core\Authentication\RememberMe\CacheTokenVerifier;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class RememberMeFactory implements AuthenticatorFactoryInterface, PrependExtensionInterface
|
||||
{
|
||||
public const PRIORITY = -50;
|
||||
|
||||
protected array $options = [
|
||||
'name' => 'REMEMBERME',
|
||||
'lifetime' => 31536000,
|
||||
'path' => '/',
|
||||
'domain' => null,
|
||||
'secure' => false,
|
||||
'httponly' => true,
|
||||
'samesite' => null,
|
||||
'always_remember_me' => false,
|
||||
'remember_me_parameter' => '_remember_me',
|
||||
];
|
||||
|
||||
public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): string
|
||||
{
|
||||
if (!$container->hasDefinition('security.authenticator.remember_me')) {
|
||||
$loader = new PhpFileLoader($container, new FileLocator(\dirname(__DIR__).'/../../Resources/config'));
|
||||
$loader->load('security_authenticator_remember_me.php');
|
||||
}
|
||||
|
||||
if ('auto' === $config['secure']) {
|
||||
$config['secure'] = null;
|
||||
}
|
||||
|
||||
// create remember me handler (which manage the remember-me cookies)
|
||||
$rememberMeHandlerId = 'security.authenticator.remember_me_handler.'.$firewallName;
|
||||
if (isset($config['service']) && isset($config['token_provider'])) {
|
||||
throw new InvalidConfigurationException(sprintf('You cannot use both "service" and "token_provider" in "security.firewalls.%s.remember_me".', $firewallName));
|
||||
}
|
||||
|
||||
if (isset($config['service'])) {
|
||||
$container->register($rememberMeHandlerId, DecoratedRememberMeHandler::class)
|
||||
->addArgument(new Reference($config['service']))
|
||||
->addTag('security.remember_me_handler', ['firewall' => $firewallName]);
|
||||
} elseif (isset($config['token_provider'])) {
|
||||
$tokenProviderId = $this->createTokenProvider($container, $firewallName, $config['token_provider']);
|
||||
$tokenVerifier = $this->createTokenVerifier($container, $firewallName, $config['token_verifier'] ?? null);
|
||||
$container->setDefinition($rememberMeHandlerId, new ChildDefinition('security.authenticator.persistent_remember_me_handler'))
|
||||
->replaceArgument(0, new Reference($tokenProviderId))
|
||||
->replaceArgument(1, new Reference($userProviderId))
|
||||
->replaceArgument(3, $config)
|
||||
->replaceArgument(5, $tokenVerifier)
|
||||
->addTag('security.remember_me_handler', ['firewall' => $firewallName]);
|
||||
} else {
|
||||
$signatureHasherId = 'security.authenticator.remember_me_signature_hasher.'.$firewallName;
|
||||
$container->setDefinition($signatureHasherId, new ChildDefinition('security.authenticator.remember_me_signature_hasher'))
|
||||
->replaceArgument(1, $config['signature_properties'])
|
||||
->replaceArgument(2, $config['secret'])
|
||||
;
|
||||
|
||||
$container->setDefinition($rememberMeHandlerId, new ChildDefinition('security.authenticator.signature_remember_me_handler'))
|
||||
->replaceArgument(0, new Reference($signatureHasherId))
|
||||
->replaceArgument(1, new Reference($userProviderId))
|
||||
->replaceArgument(3, $config)
|
||||
->addTag('security.remember_me_handler', ['firewall' => $firewallName]);
|
||||
}
|
||||
|
||||
// create check remember me conditions listener (which checks if a remember-me cookie is supported and requested)
|
||||
$rememberMeConditionsListenerId = 'security.listener.check_remember_me_conditions.'.$firewallName;
|
||||
$container->setDefinition($rememberMeConditionsListenerId, new ChildDefinition('security.listener.check_remember_me_conditions'))
|
||||
->replaceArgument(0, array_intersect_key($config, ['always_remember_me' => true, 'remember_me_parameter' => true]))
|
||||
->addTag('kernel.event_subscriber', ['dispatcher' => 'security.event_dispatcher.'.$firewallName])
|
||||
;
|
||||
|
||||
// create remember me listener (which executes the remember me services for other authenticators and logout)
|
||||
$rememberMeListenerId = 'security.listener.remember_me.'.$firewallName;
|
||||
$container->setDefinition($rememberMeListenerId, new ChildDefinition('security.listener.remember_me'))
|
||||
->replaceArgument(0, new Reference($rememberMeHandlerId))
|
||||
->addTag('kernel.event_subscriber', ['dispatcher' => 'security.event_dispatcher.'.$firewallName])
|
||||
;
|
||||
|
||||
// create remember me authenticator (which re-authenticates the user based on the remember-me cookie)
|
||||
$authenticatorId = 'security.authenticator.remember_me.'.$firewallName;
|
||||
$container
|
||||
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.remember_me'))
|
||||
->replaceArgument(0, new Reference($rememberMeHandlerId))
|
||||
->replaceArgument(3, $config['name'] ?? $this->options['name'])
|
||||
;
|
||||
|
||||
return $authenticatorId;
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return self::PRIORITY;
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return 'remember-me';
|
||||
}
|
||||
|
||||
public function addConfiguration(NodeDefinition $node): void
|
||||
{
|
||||
$builder = $node
|
||||
->fixXmlConfig('user_provider')
|
||||
->children()
|
||||
;
|
||||
|
||||
$builder
|
||||
->scalarNode('secret')
|
||||
->cannotBeEmpty()
|
||||
->defaultValue('%kernel.secret%')
|
||||
->end()
|
||||
->scalarNode('service')->end()
|
||||
->arrayNode('user_providers')
|
||||
->beforeNormalization()
|
||||
->ifString()->then(fn ($v) => [$v])
|
||||
->end()
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->booleanNode('catch_exceptions')->defaultTrue()->end()
|
||||
->arrayNode('signature_properties')
|
||||
->prototype('scalar')->end()
|
||||
->requiresAtLeastOneElement()
|
||||
->info('An array of properties on your User that are used to sign the remember-me cookie. If any of these change, all existing cookies will become invalid.')
|
||||
->example(['email', 'password'])
|
||||
->defaultValue(['password'])
|
||||
->end()
|
||||
->arrayNode('token_provider')
|
||||
->beforeNormalization()
|
||||
->ifString()->then(fn ($v) => ['service' => $v])
|
||||
->end()
|
||||
->children()
|
||||
->scalarNode('service')->info('The service ID of a custom rememberme token provider.')->end()
|
||||
->arrayNode('doctrine')
|
||||
->canBeEnabled()
|
||||
->children()
|
||||
->scalarNode('connection')->defaultNull()->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->scalarNode('token_verifier')
|
||||
->info('The service ID of a custom rememberme token verifier.')
|
||||
->end();
|
||||
|
||||
foreach ($this->options as $name => $value) {
|
||||
if ('secure' === $name) {
|
||||
$builder->enumNode($name)->values([true, false, 'auto'])->defaultValue('auto' === $value ? null : $value);
|
||||
} elseif ('samesite' === $name) {
|
||||
$builder->enumNode($name)->values([null, Cookie::SAMESITE_LAX, Cookie::SAMESITE_STRICT, Cookie::SAMESITE_NONE])->defaultValue($value);
|
||||
} elseif (\is_bool($value)) {
|
||||
$builder->booleanNode($name)->defaultValue($value);
|
||||
} elseif (\is_int($value)) {
|
||||
$builder->integerNode($name)->defaultValue($value);
|
||||
} else {
|
||||
$builder->scalarNode($name)->defaultValue($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function createTokenProvider(ContainerBuilder $container, string $firewallName, array $config): string
|
||||
{
|
||||
$tokenProviderId = $config['service'] ?? false;
|
||||
if ($config['doctrine']['enabled'] ?? false) {
|
||||
if (!class_exists(DoctrineTokenProvider::class)) {
|
||||
throw new InvalidConfigurationException('Cannot use the "doctrine" token provider for "remember_me" because the Doctrine Bridge is not installed. Try running "composer require symfony/doctrine-bridge".');
|
||||
}
|
||||
|
||||
if (null === $config['doctrine']['connection']) {
|
||||
$connectionId = 'database_connection';
|
||||
} else {
|
||||
$connectionId = 'doctrine.dbal.'.$config['doctrine']['connection'].'_connection';
|
||||
}
|
||||
|
||||
$tokenProviderId = 'security.remember_me.doctrine_token_provider.'.$firewallName;
|
||||
$container->register($tokenProviderId, DoctrineTokenProvider::class)
|
||||
->addArgument(new Reference($connectionId));
|
||||
}
|
||||
|
||||
if (!$tokenProviderId) {
|
||||
throw new InvalidConfigurationException(sprintf('No token provider was set for firewall "%s". Either configure a service ID or set "remember_me.token_provider.doctrine" to true.', $firewallName));
|
||||
}
|
||||
|
||||
return $tokenProviderId;
|
||||
}
|
||||
|
||||
private function createTokenVerifier(ContainerBuilder $container, string $firewallName, ?string $serviceId): Reference
|
||||
{
|
||||
if ($serviceId) {
|
||||
return new Reference($serviceId);
|
||||
}
|
||||
|
||||
$tokenVerifierId = 'security.remember_me.token_verifier.'.$firewallName;
|
||||
|
||||
$container->register($tokenVerifierId, CacheTokenVerifier::class)
|
||||
->addArgument(new Reference('cache.security_token_verifier', ContainerInterface::NULL_ON_INVALID_REFERENCE))
|
||||
->addArgument(60)
|
||||
->addArgument('rememberme-'.$firewallName.'-stale-');
|
||||
|
||||
return new Reference($tokenVerifierId, ContainerInterface::NULL_ON_INVALID_REFERENCE);
|
||||
}
|
||||
|
||||
public function prepend(ContainerBuilder $container): void
|
||||
{
|
||||
$rememberMeSecureDefault = false;
|
||||
$rememberMeSameSiteDefault = null;
|
||||
|
||||
if (!isset($container->getExtensions()['framework'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($container->getExtensionConfig('framework') as $config) {
|
||||
if (isset($config['session']) && \is_array($config['session'])) {
|
||||
$rememberMeSecureDefault = $config['session']['cookie_secure'] ?? $rememberMeSecureDefault;
|
||||
$rememberMeSameSiteDefault = \array_key_exists('cookie_samesite', $config['session']) ? $config['session']['cookie_samesite'] : $rememberMeSameSiteDefault;
|
||||
}
|
||||
}
|
||||
|
||||
$this->options['secure'] = $rememberMeSecureDefault;
|
||||
$this->options['samesite'] = $rememberMeSameSiteDefault;
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
<?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\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* RemoteUserFactory creates services for REMOTE_USER based authentication.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Maxime Douailin <maxime.douailin@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class RemoteUserFactory implements AuthenticatorFactoryInterface
|
||||
{
|
||||
public const PRIORITY = -10;
|
||||
|
||||
public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): string
|
||||
{
|
||||
$authenticatorId = 'security.authenticator.remote_user.'.$firewallName;
|
||||
$container
|
||||
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.remote_user'))
|
||||
->replaceArgument(0, new Reference($userProviderId))
|
||||
->replaceArgument(2, $firewallName)
|
||||
->replaceArgument(3, $config['user'])
|
||||
;
|
||||
|
||||
return $authenticatorId;
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return self::PRIORITY;
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return 'remote-user';
|
||||
}
|
||||
|
||||
public function addConfiguration(NodeDefinition $node): void
|
||||
{
|
||||
$node
|
||||
->children()
|
||||
->scalarNode('provider')->end()
|
||||
->scalarNode('user')->defaultValue('REMOTE_USER')->end()
|
||||
->end()
|
||||
;
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
<?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\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* Stateless authenticators are authenticators that can work without a user provider.
|
||||
*
|
||||
* This situation can only occur in stateless firewalls, as statefull firewalls
|
||||
* need the user provider to refresh the user in each subsequent request. A
|
||||
* stateless authenticator can be used on both stateless and statefull authenticators.
|
||||
*
|
||||
* @author Wouter de Jong <wouter@wouterj.nl>
|
||||
*/
|
||||
interface StatelessAuthenticatorFactoryInterface extends AuthenticatorFactoryInterface
|
||||
{
|
||||
public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, ?string $userProviderId): string|array;
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
<?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\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* X509Factory creates services for X509 certificate authentication.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class X509Factory implements AuthenticatorFactoryInterface
|
||||
{
|
||||
public const PRIORITY = -10;
|
||||
|
||||
public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): string
|
||||
{
|
||||
$authenticatorId = 'security.authenticator.x509.'.$firewallName;
|
||||
$container
|
||||
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.x509'))
|
||||
->replaceArgument(0, new Reference($userProviderId))
|
||||
->replaceArgument(2, $firewallName)
|
||||
->replaceArgument(3, $config['user'])
|
||||
->replaceArgument(4, $config['credentials'])
|
||||
->replaceArgument(6, $config['user_identifier'])
|
||||
;
|
||||
|
||||
return $authenticatorId;
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return self::PRIORITY;
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return 'x509';
|
||||
}
|
||||
|
||||
public function addConfiguration(NodeDefinition $node): void
|
||||
{
|
||||
$node
|
||||
->children()
|
||||
->scalarNode('provider')->end()
|
||||
->scalarNode('user')->defaultValue('SSL_CLIENT_S_DN_Email')->end()
|
||||
->scalarNode('credentials')->defaultValue('SSL_CLIENT_S_DN')->end()
|
||||
->scalarNode('user_identifier')->defaultValue('emailAddress')->end()
|
||||
->end()
|
||||
;
|
||||
}
|
||||
}
|
||||
Vendored
+66
@@ -0,0 +1,66 @@
|
||||
<?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\UserProvider;
|
||||
|
||||
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Parameter;
|
||||
|
||||
/**
|
||||
* InMemoryFactory creates services for the memory provider.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Christophe Coevoet <stof@notk.org>
|
||||
*/
|
||||
class InMemoryFactory implements UserProviderFactoryInterface
|
||||
{
|
||||
public function create(ContainerBuilder $container, string $id, array $config): void
|
||||
{
|
||||
$definition = $container->setDefinition($id, new ChildDefinition('security.user.provider.in_memory'));
|
||||
$defaultPassword = new Parameter('container.build_id');
|
||||
$users = [];
|
||||
|
||||
foreach ($config['users'] as $username => $user) {
|
||||
$users[$username] = ['password' => null !== $user['password'] ? (string) $user['password'] : $defaultPassword, 'roles' => $user['roles']];
|
||||
}
|
||||
|
||||
$definition->addArgument($users);
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return 'memory';
|
||||
}
|
||||
|
||||
public function addConfiguration(NodeDefinition $node): void
|
||||
{
|
||||
$node
|
||||
->fixXmlConfig('user')
|
||||
->children()
|
||||
->arrayNode('users')
|
||||
->useAttributeAsKey('identifier')
|
||||
->normalizeKeys(false)
|
||||
->prototype('array')
|
||||
->children()
|
||||
->scalarNode('password')->defaultNull()->end()
|
||||
->arrayNode('roles')
|
||||
->beforeNormalization()->ifString()->then(fn ($v) => preg_split('/\s*,\s*/', $v))->end()
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
<?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\UserProvider;
|
||||
|
||||
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* LdapFactory creates services for Ldap user provider.
|
||||
*
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
* @author Charles Sarrazin <charles@sarraz.in>
|
||||
*/
|
||||
class LdapFactory implements UserProviderFactoryInterface
|
||||
{
|
||||
public function create(ContainerBuilder $container, string $id, array $config): void
|
||||
{
|
||||
$container
|
||||
->setDefinition($id, new ChildDefinition('security.user.provider.ldap'))
|
||||
->replaceArgument(0, new Reference($config['service']))
|
||||
->replaceArgument(1, $config['base_dn'])
|
||||
->replaceArgument(2, $config['search_dn'])
|
||||
->replaceArgument(3, $config['search_password'])
|
||||
->replaceArgument(4, $config['default_roles'])
|
||||
->replaceArgument(5, $config['uid_key'])
|
||||
->replaceArgument(6, $config['filter'])
|
||||
->replaceArgument(7, $config['password_attribute'])
|
||||
->replaceArgument(8, $config['extra_fields'])
|
||||
;
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return 'ldap';
|
||||
}
|
||||
|
||||
public function addConfiguration(NodeDefinition $node): void
|
||||
{
|
||||
$node
|
||||
->fixXmlConfig('extra_field')
|
||||
->fixXmlConfig('default_role')
|
||||
->children()
|
||||
->scalarNode('service')->isRequired()->cannotBeEmpty()->defaultValue('ldap')->end()
|
||||
->scalarNode('base_dn')->isRequired()->cannotBeEmpty()->end()
|
||||
->scalarNode('search_dn')->defaultNull()->end()
|
||||
->scalarNode('search_password')->defaultNull()->end()
|
||||
->arrayNode('extra_fields')
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->arrayNode('default_roles')
|
||||
->beforeNormalization()->ifString()->then(fn ($v) => preg_split('/\s*,\s*/', $v))->end()
|
||||
->requiresAtLeastOneElement()
|
||||
->prototype('scalar')->end()
|
||||
->end()
|
||||
->scalarNode('uid_key')->defaultValue('sAMAccountName')->end()
|
||||
->scalarNode('filter')->defaultValue('({uid_key}={user_identifier})')->end()
|
||||
->scalarNode('password_attribute')->defaultNull()->end()
|
||||
->end()
|
||||
;
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<?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\UserProvider;
|
||||
|
||||
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* UserProviderFactoryInterface is the interface for all user provider factories.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Christophe Coevoet <stof@notk.org>
|
||||
*/
|
||||
interface UserProviderFactoryInterface
|
||||
{
|
||||
public function create(ContainerBuilder $container, string $id, array $config): void;
|
||||
|
||||
public function getKey(): string;
|
||||
|
||||
public function addConfiguration(NodeDefinition $builder): void;
|
||||
}
|
||||
Reference in New Issue
Block a user