Subida del módulo y tema de PrestaShop

This commit is contained in:
Kaloyan
2026-04-09 18:31:51 +02:00
parent 12c253296f
commit 16b3ff9424
39262 changed files with 7418797 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
<?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\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Reference;
/**
* 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-core', Algorithm::class, ['symfony/security-bundle'])) {
throw new LogicException('You cannot use the "oidc" token handler since "web-token/jwt-core" is not installed. Try running "composer require web-token/jwt-core".');
}
// @see Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SignatureAlgorithmFactory
// for supported algorithms
if (\in_array($config['algorithm'], ['ES256', 'ES384', 'ES512'], true)) {
$tokenHandlerDefinition->replaceArgument(0, new Reference('security.access_token_handler.oidc.signature.'.$config['algorithm']));
} else {
$tokenHandlerDefinition->replaceArgument(0, (new ChildDefinition('security.access_token_handler.oidc.signature'))
->replaceArgument(0, $config['algorithm'])
);
}
$tokenHandlerDefinition->replaceArgument(1, (new ChildDefinition('security.access_token_handler.oidc.jwk'))
->replaceArgument(0, $config['key'])
);
}
public function getKey(): string
{
return 'oidc';
}
public function addConfiguration(NodeBuilder $node): void
{
$node
->arrayNode($this->getKey())
->fixXmlConfig($this->getKey())
->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()
->prototype('scalar')->end()
->end()
->scalarNode('algorithm')
->info('Algorithm used to sign the token.')
->isRequired()
->end()
->scalarNode('key')
->info('JSON-encoded JWK used to sign the token (must contain a "kty" key).')
->isRequired()
->end()
->end()
->end()
;
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,137 @@
<?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 $options = [
'check_path' => '/login_check',
'use_forward' => false,
'require_previous_session' => false,
'login_path' => '/login',
];
protected $defaultSuccessHandlerOptions = [
'always_use_default_target_path' => false,
'default_target_path' => '/',
'login_path' => '/login',
'target_path_parameter' => '_target_path',
'use_referer' => false,
];
protected $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;
}
/**
* @return void
*/
public function addConfiguration(NodeDefinition $node)
{
$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 ('require_previous_session' === $name) {
$builder
->booleanNode($name)
->setDeprecated('symfony/security-bundle', '6.4', 'Option "%node%" at "%path%" is deprecated, it will be removed in version 7.0. Setting it has no effect anymore.')
->defaultValue($default);
} elseif (\is_bool($default)) {
$builder->booleanNode($name)->defaultValue($default);
} else {
$builder->scalarNode($name)->defaultValue($default);
}
}
}
/**
* @return string
*/
protected function createAuthenticationSuccessHandler(ContainerBuilder $container, string $id, array $config)
{
$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;
}
/**
* @return string
*/
protected function createAuthenticationFailureHandler(ContainerBuilder $container, string $id, array $config)
{
$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;
}
/**
* @return string
*/
protected function getSuccessHandlerId(string $id)
{
return 'security.authentication.success_handler.'.$id.'.'.str_replace('-', '_', $this->getKey());
}
/**
* @return string
*/
protected function getFailureHandlerId(string $id)
{
return 'security.authentication.failure_handler.'.$id.'.'.str_replace('-', '_', $this->getKey());
}
}

View File

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

View File

@@ -0,0 +1,46 @@
<?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;
/**
* @return void
*/
public function addConfiguration(NodeDefinition $builder);
/**
* 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;
}

View File

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

View File

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

View File

@@ -0,0 +1,85 @@
<?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\Config\Definition\Exception\InvalidConfigurationException;
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 addConfiguration(NodeDefinition $node): void
{
parent::addConfiguration($node);
$node
->children()
->scalarNode('csrf_token_generator')->cannotBeEmpty()->end()
->end()
;
}
public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): string
{
if (isset($config['csrf_token_generator'])) {
throw new InvalidConfigurationException('The "csrf_token_generator" on "form_login" does not exist, use "enable_csrf" instead.');
}
$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;
}
}

View File

@@ -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('{username}')->end()
->scalarNode('query_string')->end()
->scalarNode('search_dn')->defaultValue('')->end()
->scalarNode('search_password')->defaultValue('')->end()
->end()
;
}
}

View File

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

View File

@@ -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('{username}')->end()
->scalarNode('query_string')->end()
->scalarNode('search_dn')->defaultValue('')->end()
->scalarNode('search_password')->defaultValue('')->end()
->end()
;
}
}

View File

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

View File

@@ -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('{username}')->end()
->scalarNode('query_string')->end()
->scalarNode('search_dn')->defaultValue('')->end()
->scalarNode('search_password')->defaultValue('')->end()
->end()
;
}
}

View File

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

View File

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

View File

@@ -0,0 +1,119 @@
<?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\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('%kernel.secret%')
;
}
$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');
}
}

View File

@@ -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 $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;
}
}

View File

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

View File

@@ -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 Jose\Component\Core\Algorithm as AlgorithmInterface;
use Jose\Component\Signature\Algorithm;
use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
use Symfony\Component\Security\Http\AccessToken\Oidc\OidcTokenHandler;
/**
* Creates a signature algorithm for {@see OidcTokenHandler}.
*
* @internal
*/
final class SignatureAlgorithmFactory
{
public static function create(string $algorithm): AlgorithmInterface
{
switch ($algorithm) {
case 'ES256':
case 'ES384':
case 'ES512':
if (!class_exists(Algorithm::class.'\\'.$algorithm)) {
throw new \LogicException(\sprintf('You cannot use the "%s" signature algorithm since "web-token/jwt-signature-algorithm-ecdsa" is not installed. Try running "composer require web-token/jwt-signature-algorithm-ecdsa".', $algorithm));
}
$algorithm = Algorithm::class.'\\'.$algorithm;
return new $algorithm();
}
throw new InvalidArgumentException(\sprintf('Unsupported signature algorithm "%s". Only ES* algorithms are supported. If you want to use another algorithm, create your TokenHandler as a service.', $algorithm));
}
}

View File

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

View File

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

View File

@@ -0,0 +1,75 @@
<?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
{
/**
* @return void
*/
public function create(ContainerBuilder $container, string $id, array $config)
{
$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);
}
/**
* @return string
*/
public function getKey()
{
return 'memory';
}
/**
* @return void
*/
public function addConfiguration(NodeDefinition $node)
{
$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()
;
}
}

View File

@@ -0,0 +1,81 @@
<?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
{
/**
* @return void
*/
public function create(ContainerBuilder $container, string $id, array $config)
{
$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'])
;
}
/**
* @return string
*/
public function getKey()
{
return 'ldap';
}
/**
* @return void
*/
public function addConfiguration(NodeDefinition $node)
{
$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}={username})')->end()
->scalarNode('password_attribute')->defaultNull()->end()
->end()
;
}
}

View File

@@ -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\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
{
/**
* @return void
*/
public function create(ContainerBuilder $container, string $id, array $config);
/**
* @return string
*/
public function getKey();
/**
* @return void
*/
public function addConfiguration(NodeDefinition $builder);
}