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,33 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authenticator;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Token\PostAuthenticationToken;
/**
* An optional base class that creates the necessary tokens for you.
*
* @author Ryan Weaver <ryan@symfonycasts.com>
*/
abstract class AbstractAuthenticator implements AuthenticatorInterface
{
/**
* Shortcut to create a PostAuthenticationToken for you, if you don't really
* care about which authenticated token you're using.
*/
public function createToken(Passport $passport, string $firewallName): TokenInterface
{
return new PostAuthenticationToken($passport->getUser(), $firewallName, $passport->getUser()->getRoles());
}
}

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\Component\Security\Http\Authenticator;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Symfony\Component\Security\Http\SecurityRequestAttributes;
/**
* A base class to make form login authentication easier!
*
* @author Ryan Weaver <ryan@symfonycasts.com>
*/
abstract class AbstractLoginFormAuthenticator extends AbstractAuthenticator implements AuthenticationEntryPointInterface, InteractiveAuthenticatorInterface
{
/**
* Return the URL to the login page.
*/
abstract protected function getLoginUrl(Request $request): string;
/**
* Override to change the request conditions that have to be
* matched in order to handle the login form submit.
*
* This default implementation handles all POST requests to the
* login path (@see getLoginUrl()).
*/
public function supports(Request $request): bool
{
return $request->isMethod('POST') && $this->getLoginUrl($request) === $request->getBaseUrl().$request->getPathInfo();
}
/**
* Override to change what happens after a bad username/password is submitted.
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): Response
{
if ($request->hasSession()) {
$request->getSession()->set(SecurityRequestAttributes::AUTHENTICATION_ERROR, $exception);
}
$url = $this->getLoginUrl($request);
return new RedirectResponse($url);
}
/**
* Override to control what happens when the user hits a secure page
* but isn't logged in yet.
*/
public function start(Request $request, ?AuthenticationException $authException = null): Response
{
$url = $this->getLoginUrl($request);
return new RedirectResponse($url);
}
public function isInteractive(): bool
{
return true;
}
}

View File

@@ -0,0 +1,130 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authenticator;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PreAuthenticatedUserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
/**
* The base authenticator for authenticators to use pre-authenticated
* requests (e.g. using certificates).
*
* @author Wouter de Jong <wouter@wouterj.nl>
* @author Fabien Potencier <fabien@symfony.com>
*
* @internal
*/
abstract class AbstractPreAuthenticatedAuthenticator implements InteractiveAuthenticatorInterface
{
private UserProviderInterface $userProvider;
private TokenStorageInterface $tokenStorage;
private string $firewallName;
private ?LoggerInterface $logger;
public function __construct(UserProviderInterface $userProvider, TokenStorageInterface $tokenStorage, string $firewallName, ?LoggerInterface $logger = null)
{
$this->userProvider = $userProvider;
$this->tokenStorage = $tokenStorage;
$this->firewallName = $firewallName;
$this->logger = $logger;
}
/**
* Returns the username of the pre-authenticated user.
*
* This authenticator is skipped if null is returned or a custom
* BadCredentialsException is thrown.
*/
abstract protected function extractUsername(Request $request): ?string;
public function supports(Request $request): ?bool
{
try {
$username = $this->extractUsername($request);
} catch (BadCredentialsException $e) {
$this->clearToken($e);
$this->logger?->debug('Skipping pre-authenticated authenticator as a BadCredentialsException is thrown.', ['exception' => $e, 'authenticator' => static::class]);
return false;
}
if (null === $username) {
$this->logger?->debug('Skipping pre-authenticated authenticator no username could be extracted.', ['authenticator' => static::class]);
return false;
}
// do not overwrite already stored tokens from the same user (i.e. from the session)
$token = $this->tokenStorage->getToken();
if ($token instanceof PreAuthenticatedToken && $this->firewallName === $token->getFirewallName() && $token->getUserIdentifier() === $username) {
$this->logger?->debug('Skipping pre-authenticated authenticator as the user already has an existing session.', ['authenticator' => static::class]);
return false;
}
$request->attributes->set('_pre_authenticated_username', $username);
return true;
}
public function authenticate(Request $request): Passport
{
$userBadge = new UserBadge($request->attributes->get('_pre_authenticated_username'), $this->userProvider->loadUserByIdentifier(...));
return new SelfValidatingPassport($userBadge, [new PreAuthenticatedUserBadge()]);
}
public function createToken(Passport $passport, string $firewallName): TokenInterface
{
return new PreAuthenticatedToken($passport->getUser(), $firewallName, $passport->getUser()->getRoles());
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
return null; // let the original request continue
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
$this->clearToken($exception);
return null;
}
public function isInteractive(): bool
{
return true;
}
private function clearToken(AuthenticationException $exception): void
{
$token = $this->tokenStorage->getToken();
if ($token instanceof PreAuthenticatedToken && $this->firewallName === $token->getFirewallName()) {
$this->tokenStorage->setToken(null);
$this->logger?->info('Cleared pre-authenticated token due to an exception.', ['exception' => $exception]);
}
}
}

View File

@@ -0,0 +1,126 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authenticator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\AccessToken\AccessTokenExtractorInterface;
use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
use Symfony\Component\Security\Http\Authenticator\Token\PostAuthenticationToken;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* Provides an implementation of the RFC6750 of an authentication via
* an access token.
*
* @author Florent Morselli <florent.morselli@spomky-labs.com>
*/
class AccessTokenAuthenticator implements AuthenticatorInterface
{
private ?TranslatorInterface $translator = null;
public function __construct(
private readonly AccessTokenHandlerInterface $accessTokenHandler,
private readonly AccessTokenExtractorInterface $accessTokenExtractor,
private readonly ?UserProviderInterface $userProvider = null,
private readonly ?AuthenticationSuccessHandlerInterface $successHandler = null,
private readonly ?AuthenticationFailureHandlerInterface $failureHandler = null,
private readonly ?string $realm = null,
) {
}
public function supports(Request $request): ?bool
{
return null === $this->accessTokenExtractor->extractAccessToken($request) ? false : null;
}
public function authenticate(Request $request): Passport
{
$accessToken = $this->accessTokenExtractor->extractAccessToken($request);
if (!$accessToken) {
throw new BadCredentialsException('Invalid credentials.');
}
$userBadge = $this->accessTokenHandler->getUserBadgeFrom($accessToken);
if ($this->userProvider && (null === $userBadge->getUserLoader() || $userBadge->getUserLoader() instanceof FallbackUserLoader)) {
$userBadge->setUserLoader($this->userProvider->loadUserByIdentifier(...));
}
return new SelfValidatingPassport($userBadge);
}
public function createToken(Passport $passport, string $firewallName): TokenInterface
{
return new PostAuthenticationToken($passport->getUser(), $firewallName, $passport->getUser()->getRoles());
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
return $this->successHandler?->onAuthenticationSuccess($request, $token);
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): Response
{
if (null !== $this->failureHandler) {
return $this->failureHandler->onAuthenticationFailure($request, $exception);
}
if (null !== $this->translator) {
$errorMessage = $this->translator->trans($exception->getMessageKey(), $exception->getMessageData(), 'security');
} else {
$errorMessage = strtr($exception->getMessageKey(), $exception->getMessageData());
}
return new Response(
null,
Response::HTTP_UNAUTHORIZED,
['WWW-Authenticate' => $this->getAuthenticateHeader($errorMessage)]
);
}
/**
* @return void
*/
public function setTranslator(?TranslatorInterface $translator)
{
$this->translator = $translator;
}
/**
* @see https://datatracker.ietf.org/doc/html/rfc6750#section-3
*/
private function getAuthenticateHeader(?string $errorDescription = null): string
{
$data = [
'realm' => $this->realm,
'error' => 'invalid_token',
'error_description' => $errorDescription,
];
$values = [];
foreach ($data as $k => $v) {
if (null === $v || '' === $v) {
continue;
}
$values[] = \sprintf('%s="%s"', $k, $v);
}
return \sprintf('Bearer %s', implode(',', $values));
}
}

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\Component\Security\Http\Authenticator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
/**
* The interface for all authenticators.
*
* @author Ryan Weaver <ryan@symfonycasts.com>
* @author Amaury Leroux de Lens <amaury@lerouxdelens.com>
* @author Wouter de Jong <wouter@wouterj.nl>
*/
interface AuthenticatorInterface
{
/**
* Does the authenticator support the given Request?
*
* If this returns true, authenticate() will be called. If false, the authenticator will be skipped.
*
* Returning null means authenticate() can be called lazily when accessing the token storage.
*/
public function supports(Request $request): ?bool;
/**
* Create a passport for the current request.
*
* The passport contains the user, credentials and any additional information
* that has to be checked by the Symfony Security system. For example, a login
* form authenticator will probably return a passport containing the user, the
* presented password and the CSRF token value.
*
* You may throw any AuthenticationException in this method in case of error (e.g.
* a UserNotFoundException when the user cannot be found).
*
* @throws AuthenticationException
*/
public function authenticate(Request $request): Passport;
/**
* Create an authenticated token for the given user.
*
* If you don't care about which token class is used or don't really
* understand what a "token" is, you can skip this method by extending
* the AbstractAuthenticator class from your authenticator.
*
* @see AbstractAuthenticator
*
* @param Passport $passport The passport returned from authenticate()
*/
public function createToken(Passport $passport, string $firewallName): TokenInterface;
/**
* Called when authentication executed and was successful!
*
* This should return the Response sent back to the user, like a
* RedirectResponse to the last page they visited.
*
* If you return null, the current request will continue, and the user
* will be authenticated. This makes sense, for example, with an API.
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response;
/**
* Called when authentication executed, but failed (e.g. wrong username password).
*
* This should return the Response sent back to the user, like a
* RedirectResponse to the login page or a 403 response.
*
* If you return null, the request will continue, but the user will
* not be authenticated. This is probably not what you want to do.
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response;
}

View File

@@ -0,0 +1,118 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authenticator\Debug;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
use Symfony\Component\Security\Http\Authenticator\InteractiveAuthenticatorInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\BadgeInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Symfony\Component\Security\Http\EntryPoint\Exception\NotAnEntryPointException;
use Symfony\Component\VarDumper\Caster\ClassStub;
/**
* Collects info about an authenticator for debugging purposes.
*
* @author Robin Chalas <robin.chalas@gmail.com>
*/
final class TraceableAuthenticator implements AuthenticatorInterface, InteractiveAuthenticatorInterface, AuthenticationEntryPointInterface
{
private ?Passport $passport = null;
private ?float $duration = null;
private ClassStub|string $stub;
private ?bool $authenticated = null;
public function __construct(private AuthenticatorInterface $authenticator)
{
}
public function getInfo(): array
{
return [
'supports' => true,
'passport' => $this->passport,
'duration' => $this->duration,
'stub' => $this->stub ??= class_exists(ClassStub::class) ? new ClassStub($this->authenticator::class) : $this->authenticator::class,
'authenticated' => $this->authenticated,
'badges' => array_map(
static function (BadgeInterface $badge): array {
return [
'stub' => class_exists(ClassStub::class) ? new ClassStub($badge::class) : $badge::class,
'resolved' => $badge->isResolved(),
];
},
$this->passport?->getBadges() ?? [],
),
];
}
public function supports(Request $request): ?bool
{
return $this->authenticator->supports($request);
}
public function authenticate(Request $request): Passport
{
$startTime = microtime(true);
$this->passport = $this->authenticator->authenticate($request);
$this->duration = microtime(true) - $startTime;
return $this->passport;
}
public function createToken(Passport $passport, string $firewallName): TokenInterface
{
return $this->authenticator->createToken($passport, $firewallName);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
$this->authenticated = true;
return $this->authenticator->onAuthenticationSuccess($request, $token, $firewallName);
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
$this->authenticated = false;
return $this->authenticator->onAuthenticationFailure($request, $exception);
}
public function start(Request $request, ?AuthenticationException $authException = null): Response
{
if (!$this->authenticator instanceof AuthenticationEntryPointInterface) {
throw new NotAnEntryPointException();
}
return $this->authenticator->start($request, $authException);
}
public function isInteractive(): bool
{
return $this->authenticator instanceof InteractiveAuthenticatorInterface && $this->authenticator->isInteractive();
}
public function getAuthenticator(): AuthenticatorInterface
{
return $this->authenticator;
}
public function __call($method, $args): mixed
{
return $this->authenticator->{$method}(...$args);
}
}

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\Component\Security\Http\Authenticator\Debug;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\Security\Http\Firewall\AbstractListener;
use Symfony\Component\Security\Http\Firewall\AuthenticatorManagerListener;
use Symfony\Component\VarDumper\Caster\ClassStub;
use Symfony\Contracts\Service\ResetInterface;
/**
* Decorates the AuthenticatorManagerListener to collect information about security authenticators.
*
* @author Robin Chalas <robin.chalas@gmail.com>
*/
final class TraceableAuthenticatorManagerListener extends AbstractListener implements ResetInterface
{
private AuthenticatorManagerListener $authenticationManagerListener;
private array $authenticatorsInfo = [];
private bool $hasVardumper;
public function __construct(AuthenticatorManagerListener $authenticationManagerListener)
{
$this->authenticationManagerListener = $authenticationManagerListener;
$this->hasVardumper = class_exists(ClassStub::class);
}
public function supports(Request $request): ?bool
{
return $this->authenticationManagerListener->supports($request);
}
public function authenticate(RequestEvent $event): void
{
$request = $event->getRequest();
if (!$authenticators = $request->attributes->get('_security_authenticators')) {
return;
}
foreach ($request->attributes->get('_security_skipped_authenticators') as $skippedAuthenticator) {
$this->authenticatorsInfo[] = [
'supports' => false,
'stub' => $this->hasVardumper ? new ClassStub($skippedAuthenticator::class) : $skippedAuthenticator::class,
'passport' => null,
'duration' => 0,
'authenticated' => null,
'badges' => [],
];
}
foreach ($authenticators as $key => $authenticator) {
$authenticators[$key] = new TraceableAuthenticator($authenticator);
}
$request->attributes->set('_security_authenticators', $authenticators);
$this->authenticationManagerListener->authenticate($event);
foreach ($authenticators as $authenticator) {
$this->authenticatorsInfo[] = $authenticator->getInfo();
}
}
public function getAuthenticatorManagerListener(): AuthenticatorManagerListener
{
return $this->authenticationManagerListener;
}
public function getAuthenticatorsInfo(): array
{
return $this->authenticatorsInfo;
}
public function reset(): void
{
$this->authenticatorsInfo = [];
}
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authenticator;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* This wrapper serves as a marker interface to indicate badge user loaders that should not be overridden by the
* default user provider.
*
* @internal
*/
final class FallbackUserLoader
{
public function __construct(private $inner)
{
}
public function __invoke(mixed ...$args): ?UserInterface
{
return ($this->inner)(...$args);
}
}

View File

@@ -0,0 +1,164 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authenticator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\Security\Http\ParameterBagUtils;
use Symfony\Component\Security\Http\SecurityRequestAttributes;
/**
* @author Wouter de Jong <wouter@wouterj.nl>
* @author Fabien Potencier <fabien@symfony.com>
*
* @final
*/
class FormLoginAuthenticator extends AbstractLoginFormAuthenticator
{
private HttpUtils $httpUtils;
private UserProviderInterface $userProvider;
private AuthenticationSuccessHandlerInterface $successHandler;
private AuthenticationFailureHandlerInterface $failureHandler;
private array $options;
private HttpKernelInterface $httpKernel;
public function __construct(HttpUtils $httpUtils, UserProviderInterface $userProvider, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options)
{
$this->httpUtils = $httpUtils;
$this->userProvider = $userProvider;
$this->successHandler = $successHandler;
$this->failureHandler = $failureHandler;
$this->options = array_merge([
'username_parameter' => '_username',
'password_parameter' => '_password',
'check_path' => '/login_check',
'post_only' => true,
'form_only' => false,
'enable_csrf' => false,
'csrf_parameter' => '_csrf_token',
'csrf_token_id' => 'authenticate',
], $options);
}
protected function getLoginUrl(Request $request): string
{
return $this->httpUtils->generateUri($request, $this->options['login_path']);
}
public function supports(Request $request): bool
{
return ($this->options['post_only'] ? $request->isMethod('POST') : true)
&& $this->httpUtils->checkRequestPath($request, $this->options['check_path'])
&& ($this->options['form_only'] ? 'form' === $request->getContentTypeFormat() : true);
}
public function authenticate(Request $request): Passport
{
$credentials = $this->getCredentials($request);
$userBadge = new UserBadge($credentials['username'], $this->userProvider->loadUserByIdentifier(...));
$passport = new Passport($userBadge, new PasswordCredentials($credentials['password']), [new RememberMeBadge()]);
if ($this->options['enable_csrf']) {
$passport->addBadge(new CsrfTokenBadge($this->options['csrf_token_id'], $credentials['csrf_token']));
}
if ($this->userProvider instanceof PasswordUpgraderInterface) {
$passport->addBadge(new PasswordUpgradeBadge($credentials['password'], $this->userProvider));
}
return $passport;
}
public function createToken(Passport $passport, string $firewallName): TokenInterface
{
return new UsernamePasswordToken($passport->getUser(), $firewallName, $passport->getUser()->getRoles());
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
return $this->successHandler->onAuthenticationSuccess($request, $token);
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): Response
{
return $this->failureHandler->onAuthenticationFailure($request, $exception);
}
private function getCredentials(Request $request): array
{
$credentials = [];
$credentials['csrf_token'] = ParameterBagUtils::getRequestParameterValue($request, $this->options['csrf_parameter']);
if ($this->options['post_only']) {
$credentials['username'] = ParameterBagUtils::getParameterBagValue($request->request, $this->options['username_parameter']);
$credentials['password'] = ParameterBagUtils::getParameterBagValue($request->request, $this->options['password_parameter']) ?? '';
} else {
$credentials['username'] = ParameterBagUtils::getRequestParameterValue($request, $this->options['username_parameter']);
$credentials['password'] = ParameterBagUtils::getRequestParameterValue($request, $this->options['password_parameter']) ?? '';
}
if (!\is_string($credentials['username']) && !$credentials['username'] instanceof \Stringable) {
throw new BadRequestHttpException(\sprintf('The key "%s" must be a string, "%s" given.', $this->options['username_parameter'], \gettype($credentials['username'])));
}
$credentials['username'] = trim($credentials['username']);
$request->getSession()->set(SecurityRequestAttributes::LAST_USERNAME, $credentials['username']);
if (!\is_string($credentials['password']) && (!\is_object($credentials['password']) || !method_exists($credentials['password'], '__toString'))) {
throw new BadRequestHttpException(\sprintf('The key "%s" must be a string, "%s" given.', $this->options['password_parameter'], \gettype($credentials['password'])));
}
if (!\is_string($credentials['csrf_token'] ?? '') && (!\is_object($credentials['csrf_token']) || !method_exists($credentials['csrf_token'], '__toString'))) {
throw new BadRequestHttpException(\sprintf('The key "%s" must be a string, "%s" given.', $this->options['csrf_parameter'], \gettype($credentials['csrf_token'])));
}
return $credentials;
}
public function setHttpKernel(HttpKernelInterface $httpKernel): void
{
$this->httpKernel = $httpKernel;
}
public function start(Request $request, ?AuthenticationException $authException = null): Response
{
if (!$this->options['use_forward']) {
return parent::start($request, $authException);
}
$subRequest = $this->httpUtils->createRequest($request, $this->options['login_path']);
$response = $this->httpKernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
if (200 === $response->getStatusCode()) {
$response->setStatusCode(401);
}
return $response;
}
}

View File

@@ -0,0 +1,92 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authenticator;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
/**
* @author Wouter de Jong <wouter@wouterj.nl>
* @author Fabien Potencier <fabien@symfony.com>
*
* @final
*/
class HttpBasicAuthenticator implements AuthenticatorInterface, AuthenticationEntryPointInterface
{
private string $realmName;
private UserProviderInterface $userProvider;
private ?LoggerInterface $logger;
public function __construct(string $realmName, UserProviderInterface $userProvider, ?LoggerInterface $logger = null)
{
$this->realmName = $realmName;
$this->userProvider = $userProvider;
$this->logger = $logger;
}
public function start(Request $request, ?AuthenticationException $authException = null): Response
{
$response = new Response();
$response->headers->set('WWW-Authenticate', \sprintf('Basic realm="%s"', $this->realmName));
$response->setStatusCode(401);
return $response;
}
public function supports(Request $request): ?bool
{
return $request->headers->has('PHP_AUTH_USER');
}
public function authenticate(Request $request): Passport
{
$username = $request->headers->get('PHP_AUTH_USER');
$password = $request->headers->get('PHP_AUTH_PW', '');
$userBadge = new UserBadge($username, $this->userProvider->loadUserByIdentifier(...));
$passport = new Passport($userBadge, new PasswordCredentials($password));
if ($this->userProvider instanceof PasswordUpgraderInterface) {
$passport->addBadge(new PasswordUpgradeBadge($password, $this->userProvider));
}
return $passport;
}
public function createToken(Passport $passport, string $firewallName): TokenInterface
{
return new UsernamePasswordToken($passport->getUser(), $firewallName, $passport->getUser()->getRoles());
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
return null;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
$this->logger?->info('Basic authentication failed for user.', ['username' => $request->headers->get('PHP_AUTH_USER'), 'exception' => $exception]);
return $this->start($request, $exception);
}
}

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\Component\Security\Http\Authenticator;
/**
* This is an extension of the authenticator interface that may
* be used by interactive authenticators.
*
* Interactive login requires explicit user action (e.g. a login
* form). Implementing this interface will dispatch the InteractiveLoginEvent
* upon successful login.
*
* @author Wouter de Jong <wouter@wouterj.nl>
*/
interface InteractiveAuthenticatorInterface extends AuthenticatorInterface
{
/**
* Should return true to make this authenticator perform
* an interactive login.
*/
public function isInteractive(): bool;
}

View File

@@ -0,0 +1,175 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authenticator;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\PropertyAccess\Exception\AccessException;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* Provides a stateless implementation of an authentication via
* a JSON document composed of a username and a password.
*
* @author Kévin Dunglas <dunglas@gmail.com>
* @author Wouter de Jong <wouter@wouterj.nl>
*
* @final
*/
class JsonLoginAuthenticator implements InteractiveAuthenticatorInterface
{
private array $options;
private HttpUtils $httpUtils;
private UserProviderInterface $userProvider;
private PropertyAccessorInterface $propertyAccessor;
private ?AuthenticationSuccessHandlerInterface $successHandler;
private ?AuthenticationFailureHandlerInterface $failureHandler;
private ?TranslatorInterface $translator = null;
public function __construct(HttpUtils $httpUtils, UserProviderInterface $userProvider, ?AuthenticationSuccessHandlerInterface $successHandler = null, ?AuthenticationFailureHandlerInterface $failureHandler = null, array $options = [], ?PropertyAccessorInterface $propertyAccessor = null)
{
$this->options = array_merge(['username_path' => 'username', 'password_path' => 'password'], $options);
$this->httpUtils = $httpUtils;
$this->successHandler = $successHandler;
$this->failureHandler = $failureHandler;
$this->userProvider = $userProvider;
$this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
}
public function supports(Request $request): ?bool
{
if (
!str_contains($request->getRequestFormat() ?? '', 'json')
&& !str_contains($request->getContentTypeFormat() ?? '', 'json')
) {
return false;
}
if (isset($this->options['check_path']) && !$this->httpUtils->checkRequestPath($request, $this->options['check_path'])) {
return false;
}
return true;
}
public function authenticate(Request $request): Passport
{
try {
$data = json_decode($request->getContent());
if (!$data instanceof \stdClass) {
throw new BadRequestHttpException('Invalid JSON.');
}
$credentials = $this->getCredentials($data);
} catch (BadRequestHttpException $e) {
$request->setRequestFormat('json');
throw $e;
}
$userBadge = new UserBadge($credentials['username'], $this->userProvider->loadUserByIdentifier(...));
$passport = new Passport($userBadge, new PasswordCredentials($credentials['password']), [new RememberMeBadge((array) $data)]);
if ($this->userProvider instanceof PasswordUpgraderInterface) {
$passport->addBadge(new PasswordUpgradeBadge($credentials['password'], $this->userProvider));
}
return $passport;
}
public function createToken(Passport $passport, string $firewallName): TokenInterface
{
return new UsernamePasswordToken($passport->getUser(), $firewallName, $passport->getUser()->getRoles());
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
if (null === $this->successHandler) {
return null; // let the original request continue
}
return $this->successHandler->onAuthenticationSuccess($request, $token);
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
if (null === $this->failureHandler) {
if (null !== $this->translator) {
$errorMessage = $this->translator->trans($exception->getMessageKey(), $exception->getMessageData(), 'security');
} else {
$errorMessage = strtr($exception->getMessageKey(), $exception->getMessageData());
}
return new JsonResponse(['error' => $errorMessage], JsonResponse::HTTP_UNAUTHORIZED);
}
return $this->failureHandler->onAuthenticationFailure($request, $exception);
}
public function isInteractive(): bool
{
return true;
}
public function setTranslator(TranslatorInterface $translator): void
{
$this->translator = $translator;
}
private function getCredentials(\stdClass $data): array
{
$credentials = [];
try {
$credentials['username'] = $this->propertyAccessor->getValue($data, $this->options['username_path']);
if (!\is_string($credentials['username'])) {
throw new BadRequestHttpException(\sprintf('The key "%s" must be a string.', $this->options['username_path']));
}
} catch (AccessException $e) {
throw new BadRequestHttpException(\sprintf('The key "%s" must be provided.', $this->options['username_path']), $e);
}
try {
$credentials['password'] = $this->propertyAccessor->getValue($data, $this->options['password_path']);
$this->propertyAccessor->setValue($data, $this->options['password_path'], null);
if (!\is_string($credentials['password'])) {
throw new BadRequestHttpException(\sprintf('The key "%s" must be a string.', $this->options['password_path']));
}
} catch (AccessException $e) {
throw new BadRequestHttpException(\sprintf('The key "%s" must be provided.', $this->options['password_path']), $e);
}
if ('' === $credentials['username'] || '' === $credentials['password']) {
trigger_deprecation('symfony/security', '6.2', 'Passing an empty string as username or password parameter is deprecated.');
}
return $credentials;
}
}

View File

@@ -0,0 +1,88 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authenticator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\Security\Http\LoginLink\Exception\InvalidLoginLinkAuthenticationException;
use Symfony\Component\Security\Http\LoginLink\Exception\InvalidLoginLinkExceptionInterface;
use Symfony\Component\Security\Http\LoginLink\LoginLinkHandlerInterface;
/**
* @author Ryan Weaver <ryan@symfonycasts.com>
*/
final class LoginLinkAuthenticator extends AbstractAuthenticator implements InteractiveAuthenticatorInterface
{
private LoginLinkHandlerInterface $loginLinkHandler;
private HttpUtils $httpUtils;
private AuthenticationSuccessHandlerInterface $successHandler;
private AuthenticationFailureHandlerInterface $failureHandler;
private array $options;
public function __construct(LoginLinkHandlerInterface $loginLinkHandler, HttpUtils $httpUtils, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options)
{
$this->loginLinkHandler = $loginLinkHandler;
$this->httpUtils = $httpUtils;
$this->successHandler = $successHandler;
$this->failureHandler = $failureHandler;
$this->options = $options + ['check_post_only' => false];
}
public function supports(Request $request): ?bool
{
return ($this->options['check_post_only'] ? $request->isMethod('POST') : true)
&& $this->httpUtils->checkRequestPath($request, $this->options['check_route']);
}
public function authenticate(Request $request): Passport
{
if (!$username = $request->get('user')) {
throw new InvalidLoginLinkAuthenticationException('Missing user from link.');
}
$userBadge = new UserBadge($username, function () use ($request) {
try {
$user = $this->loginLinkHandler->consumeLoginLink($request);
} catch (InvalidLoginLinkExceptionInterface $e) {
throw new InvalidLoginLinkAuthenticationException('Login link could not be validated.', 0, $e);
}
return $user;
});
return new SelfValidatingPassport($userBadge, [new RememberMeBadge()]);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
return $this->successHandler->onAuthenticationSuccess($request, $token);
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): Response
{
return $this->failureHandler->onAuthenticationFailure($request, $exception);
}
public function isInteractive(): bool
{
return true;
}
}

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\Component\Security\Http\Authenticator\Passport\Badge;
/**
* Passport badges allow to add more information to a passport (e.g. a CSRF token).
*
* @author Wouter de Jong <wouter@wouterj.nl>
*/
interface BadgeInterface
{
/**
* Checks if this badge is resolved by the security system.
*
* After authentication, all badges must return `true` in this method in order
* for the authentication to succeed.
*/
public function isResolved(): bool;
}

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\Component\Security\Http\Authenticator\Passport\Badge;
use Symfony\Component\Security\Http\EventListener\CsrfProtectionListener;
/**
* Adds automatic CSRF tokens checking capabilities to this authenticator.
*
* @see CsrfProtectionListener
*
* @author Wouter de Jong <wouter@wouterj.nl>
*
* @final
*/
class CsrfTokenBadge implements BadgeInterface
{
private bool $resolved = false;
private string $csrfTokenId;
private ?string $csrfToken;
/**
* @param string $csrfTokenId An arbitrary string used to generate the value of the CSRF token.
* Using a different string for each authenticator improves its security.
* @param string|null $csrfToken The CSRF token presented in the request, if any
*/
public function __construct(string $csrfTokenId, #[\SensitiveParameter] ?string $csrfToken)
{
$this->csrfTokenId = $csrfTokenId;
$this->csrfToken = $csrfToken;
}
public function getCsrfTokenId(): string
{
return $this->csrfTokenId;
}
public function getCsrfToken(): ?string
{
return $this->csrfToken;
}
/**
* @internal
*/
public function markResolved(): void
{
$this->resolved = true;
}
public function isResolved(): bool
{
return $this->resolved;
}
}

View File

@@ -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\Component\Security\Http\Authenticator\Passport\Badge;
use Symfony\Component\Security\Core\Exception\LogicException;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
/**
* Adds automatic password migration, if enabled and required in the password encoder.
*
* @see PasswordUpgraderInterface
*
* @author Wouter de Jong <wouter@wouterj.nl>
*
* @final
*/
class PasswordUpgradeBadge implements BadgeInterface
{
private ?string $plaintextPassword = null;
private ?PasswordUpgraderInterface $passwordUpgrader;
/**
* @param string $plaintextPassword The presented password, used in the rehash
* @param PasswordUpgraderInterface|null $passwordUpgrader The password upgrader, defaults to the UserProvider if null
*/
public function __construct(#[\SensitiveParameter] string $plaintextPassword, ?PasswordUpgraderInterface $passwordUpgrader = null)
{
$this->plaintextPassword = $plaintextPassword;
$this->passwordUpgrader = $passwordUpgrader;
}
public function getAndErasePlaintextPassword(): string
{
$password = $this->plaintextPassword;
if (null === $password) {
throw new LogicException('The password is erased as another listener already used this badge.');
}
$this->plaintextPassword = null;
return $password;
}
public function getPasswordUpgrader(): ?PasswordUpgraderInterface
{
return $this->passwordUpgrader;
}
public function isResolved(): bool
{
return true;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authenticator\Passport\Badge;
use Symfony\Component\Security\Http\Authenticator\AbstractPreAuthenticatedAuthenticator;
/**
* Marks the authentication as being pre-authenticated.
*
* This disables pre-authentication user checkers.
*
* @see AbstractPreAuthenticatedAuthenticator
*
* @author Wouter de Jong <wouter@wouterj.nl>
*
* @final
*/
class PreAuthenticatedUserBadge implements BadgeInterface
{
public function isResolved(): bool
{
return true;
}
}

View File

@@ -0,0 +1,76 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authenticator\Passport\Badge;
use Symfony\Component\Security\Http\EventListener\CheckRememberMeConditionsListener;
/**
* Adds support for remember me to this authenticator.
*
* The presence of this badge doesn't create the remember-me cookie. The actual
* cookie is only created if this badge is enabled. By default, this is done
* by the {@see CheckRememberMeConditionsListener} if all conditions are met.
*
* @author Wouter de Jong <wouter@wouterj.nl>
*
* @final
*/
class RememberMeBadge implements BadgeInterface
{
private bool $enabled = false;
public function __construct(
public readonly array $parameters = [],
) {
}
/**
* Enables remember-me cookie creation.
*
* In most cases, {@see CheckRememberMeConditionsListener} enables this
* automatically if always_remember_me is true or the remember_me_parameter
* exists in the request.
*
* @return $this
*/
public function enable(): static
{
$this->enabled = true;
return $this;
}
/**
* Disables remember-me cookie creation.
*
* The default is disabled, this can be called to suppress creation
* after it was enabled.
*
* @return $this
*/
public function disable(): static
{
$this->enabled = false;
return $this;
}
public function isEnabled(): bool
{
return $this->enabled;
}
public function isResolved(): bool
{
return true; // remember me does not need to be explicitly resolved
}
}

View File

@@ -0,0 +1,121 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authenticator\Passport\Badge;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\AuthenticationServiceException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Http\EventListener\UserProviderListener;
/**
* Represents the user in the authentication process.
*
* It uses an identifier (e.g. email, or username) and
* "user loader" to load the related User object.
*
* @author Wouter de Jong <wouter@wouterj.nl>
*/
class UserBadge implements BadgeInterface
{
public const MAX_USERNAME_LENGTH = 4096;
private string $userIdentifier;
/** @var callable|null */
private $userLoader;
private UserInterface $user;
private ?array $attributes;
/**
* Initializes the user badge.
*
* You must provide a $userIdentifier. This is a unique string representing the
* user for this authentication (e.g. the email if authentication is done using
* email + password; or a string combining email+company if authentication is done
* based on email *and* company name). This string can be used for e.g. login throttling.
*
* Optionally, you may pass a user loader. This callable receives the $userIdentifier
* as argument and must return a UserInterface object (otherwise an AuthenticationServiceException
* is thrown). If this is not set, the default user provider will be used with
* $userIdentifier as username.
*/
public function __construct(string $userIdentifier, ?callable $userLoader = null, ?array $attributes = null)
{
if (\strlen($userIdentifier) > self::MAX_USERNAME_LENGTH) {
throw new BadCredentialsException('Username too long.');
}
$this->userIdentifier = $userIdentifier;
$this->userLoader = $userLoader;
$this->attributes = $attributes;
}
public function getUserIdentifier(): string
{
return $this->userIdentifier;
}
public function getAttributes(): ?array
{
return $this->attributes;
}
/**
* @throws AuthenticationException when the user cannot be found
*/
public function getUser(): UserInterface
{
if (isset($this->user)) {
return $this->user;
}
if (null === $this->userLoader) {
throw new \LogicException(\sprintf('No user loader is configured, did you forget to register the "%s" listener?', UserProviderListener::class));
}
if (null === $this->getAttributes()) {
$user = ($this->userLoader)($this->userIdentifier);
} else {
$user = ($this->userLoader)($this->userIdentifier, $this->getAttributes());
}
// No user has been found via the $this->userLoader callback
if (null === $user) {
$exception = new UserNotFoundException();
$exception->setUserIdentifier($this->userIdentifier);
throw $exception;
}
if (!$user instanceof UserInterface) {
throw new AuthenticationServiceException(\sprintf('The user provider must return a UserInterface object, "%s" given.', get_debug_type($user)));
}
return $this->user = $user;
}
public function getUserLoader(): ?callable
{
return $this->userLoader;
}
public function setUserLoader(callable $userLoader): void
{
$this->userLoader = $userLoader;
}
public function isResolved(): bool
{
return true;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authenticator\Passport\Credentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\BadgeInterface;
/**
* Credentials are a special badge used to explicitly mark the
* credential check of an authenticator.
*
* @author Wouter de Jong <wouter@wouterj.nl>
*/
interface CredentialsInterface extends BadgeInterface
{
}

View File

@@ -0,0 +1,56 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authenticator\Passport\Credentials;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* Implements credentials checking using a custom checker function.
*
* @author Wouter de Jong <wouter@wouterj.nl>
*
* @final
*/
class CustomCredentials implements CredentialsInterface
{
private \Closure $customCredentialsChecker;
private mixed $credentials;
private bool $resolved = false;
/**
* @param callable $customCredentialsChecker the check function. If this function does not return `true`, a
* BadCredentialsException is thrown. You may also throw a more
* specific exception in the function.
*/
public function __construct(callable $customCredentialsChecker, mixed $credentials)
{
$this->customCredentialsChecker = $customCredentialsChecker(...);
$this->credentials = $credentials;
}
public function executeCustomChecker(UserInterface $user): void
{
$checker = $this->customCredentialsChecker;
if (true !== $checker($this->credentials, $user)) {
throw new BadCredentialsException('Credentials check failed as the callable passed to CustomCredentials did not return "true".');
}
$this->resolved = true;
}
public function isResolved(): bool
{
return $this->resolved;
}
}

View File

@@ -0,0 +1,58 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authenticator\Passport\Credentials;
use Symfony\Component\Security\Core\Exception\LogicException;
/**
* Implements password credentials.
*
* These plaintext passwords are checked by the UserPasswordHasher during
* authentication.
*
* @author Wouter de Jong <wouter@wouterj.nl>
*
* @final
*/
class PasswordCredentials implements CredentialsInterface
{
private ?string $password = null;
private bool $resolved = false;
public function __construct(#[\SensitiveParameter] string $password)
{
$this->password = $password;
}
public function getPassword(): string
{
if (null === $this->password) {
throw new LogicException('The credentials are erased as another listener already verified these credentials.');
}
return $this->password;
}
/**
* @internal
*/
public function markResolved(): void
{
$this->resolved = true;
$this->password = null;
}
public function isResolved(): bool
{
return $this->resolved;
}
}

View File

@@ -0,0 +1,129 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authenticator\Passport;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\BadgeInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CredentialsInterface;
/**
* A Passport contains all security-related information that needs to be
* validated during authentication.
*
* A passport badge can be used to add any additional information to the
* passport.
*
* @author Wouter de Jong <wouter@wouterj.nl>
*/
class Passport
{
protected $user;
private array $badges = [];
private array $attributes = [];
/**
* @param CredentialsInterface $credentials The credentials to check for this authentication, use
* SelfValidatingPassport if no credentials should be checked
* @param BadgeInterface[] $badges
*/
public function __construct(UserBadge $userBadge, CredentialsInterface $credentials, array $badges = [])
{
$this->addBadge($userBadge);
$this->addBadge($credentials);
foreach ($badges as $badge) {
$this->addBadge($badge);
}
}
public function getUser(): UserInterface
{
if (!isset($this->user)) {
if (!$this->hasBadge(UserBadge::class)) {
throw new \LogicException('Cannot get the Security user, no username or UserBadge configured for this passport.');
}
$this->user = $this->getBadge(UserBadge::class)->getUser();
}
return $this->user;
}
/**
* Adds a new security badge.
*
* A passport can hold only one instance of the same security badge.
* This method replaces the current badge if it is already set on this
* passport.
*
* @param string|null $badgeFqcn A FQCN to which the badge should be mapped to.
* This allows replacing a built-in badge by a custom one using
* e.g. addBadge(new MyCustomUserBadge(), UserBadge::class)
*
* @return $this
*/
public function addBadge(BadgeInterface $badge/* , string $badgeFqcn = null */): static
{
$badgeFqcn = $badge::class;
if (2 === \func_num_args()) {
$badgeFqcn = func_get_arg(1);
if (!\is_string($badgeFqcn)) {
throw new \LogicException(\sprintf('Second argument of "%s" must be a string.', __METHOD__));
}
}
$this->badges[$badgeFqcn] = $badge;
return $this;
}
public function hasBadge(string $badgeFqcn): bool
{
return isset($this->badges[$badgeFqcn]);
}
/**
* @template TBadge of BadgeInterface
*
* @param class-string<TBadge> $badgeFqcn
*
* @return TBadge|null
*/
public function getBadge(string $badgeFqcn): ?BadgeInterface
{
return $this->badges[$badgeFqcn] ?? null;
}
/**
* @return array<class-string<BadgeInterface>, BadgeInterface>
*/
public function getBadges(): array
{
return $this->badges;
}
public function setAttribute(string $name, mixed $value): void
{
$this->attributes[$name] = $value;
}
public function getAttribute(string $name, mixed $default = null): mixed
{
return $this->attributes[$name] ?? $default;
}
public function getAttributes(): array
{
return $this->attributes;
}
}

View File

@@ -0,0 +1,35 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authenticator\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\BadgeInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
/**
* An implementation used when there are no credentials to be checked (e.g.
* API token authentication).
*
* @author Wouter de Jong <wouter@wouterj.nl>
*/
class SelfValidatingPassport extends Passport
{
/**
* @param BadgeInterface[] $badges
*/
public function __construct(UserBadge $userBadge, array $badges = [])
{
$this->addBadge($userBadge);
foreach ($badges as $badge) {
$this->addBadge($badge);
}
}
}

View File

@@ -0,0 +1,129 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authenticator;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CookieTheftException;
use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
use Symfony\Component\Security\Http\RememberMe\RememberMeDetails;
use Symfony\Component\Security\Http\RememberMe\RememberMeHandlerInterface;
use Symfony\Component\Security\Http\RememberMe\ResponseListener;
/**
* The RememberMe *Authenticator* performs remember me authentication.
*
* This authenticator is executed whenever a user's session
* expired and a remember-me cookie was found. This authenticator
* then "re-authenticates" the user using the information in the
* cookie.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
* @author Wouter de Jong <wouter@wouterj.nl>
*
* @final
*/
class RememberMeAuthenticator implements InteractiveAuthenticatorInterface
{
private RememberMeHandlerInterface $rememberMeHandler;
private string $secret;
private TokenStorageInterface $tokenStorage;
private string $cookieName;
private ?LoggerInterface $logger;
public function __construct(RememberMeHandlerInterface $rememberMeHandler, #[\SensitiveParameter] string $secret, TokenStorageInterface $tokenStorage, string $cookieName, ?LoggerInterface $logger = null)
{
if (!$secret) {
throw new InvalidArgumentException('A non-empty secret is required.');
}
$this->rememberMeHandler = $rememberMeHandler;
$this->secret = $secret;
$this->tokenStorage = $tokenStorage;
$this->cookieName = $cookieName;
$this->logger = $logger;
}
public function supports(Request $request): ?bool
{
// do not overwrite already stored tokens (i.e. from the session)
if (null !== $this->tokenStorage->getToken()) {
return false;
}
if (($cookie = $request->attributes->get(ResponseListener::COOKIE_ATTR_NAME)) && null === $cookie->getValue()) {
return false;
}
if (!$request->cookies->has($this->cookieName) || !\is_scalar($request->cookies->all()[$this->cookieName] ?: null)) {
return false;
}
$this->logger?->debug('Remember-me cookie detected.');
// the `null` return value indicates that this authenticator supports lazy firewalls
return null;
}
public function authenticate(Request $request): Passport
{
if (!$rawCookie = $request->cookies->get($this->cookieName)) {
throw new \LogicException('No remember-me cookie is found.');
}
$rememberMeCookie = RememberMeDetails::fromRawCookie($rawCookie);
$userBadge = new UserBadge($rememberMeCookie->getUserIdentifier(), fn () => $this->rememberMeHandler->consumeRememberMeCookie($rememberMeCookie));
return new SelfValidatingPassport($userBadge);
}
public function createToken(Passport $passport, string $firewallName): TokenInterface
{
return new RememberMeToken($passport->getUser(), $firewallName, $this->secret);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
return null; // let the original request continue
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
if (null !== $this->logger) {
if ($exception instanceof UserNotFoundException) {
$this->logger->info('User for remember-me cookie not found.', ['exception' => $exception]);
} elseif ($exception instanceof UnsupportedUserException) {
$this->logger->warning('User class for remember-me cookie not supported.', ['exception' => $exception]);
} elseif (!$exception instanceof CookieTheftException) {
$this->logger->debug('Remember me authentication failed.', ['exception' => $exception]);
}
}
return null;
}
public function isInteractive(): bool
{
return true;
}
}

View File

@@ -0,0 +1,50 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Http\Authenticator;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
/**
* This authenticator authenticates a remote user.
*
* @author Wouter de Jong <wouter@wouterj.nl>
* @author Fabien Potencier <fabien@symfony.com>
* @author Maxime Douailin <maxime.douailin@gmail.com>
*
* @final
*
* @internal in Symfony 5.1
*/
class RemoteUserAuthenticator extends AbstractPreAuthenticatedAuthenticator
{
private string $userKey;
public function __construct(UserProviderInterface $userProvider, TokenStorageInterface $tokenStorage, string $firewallName, string $userKey = 'REMOTE_USER', ?LoggerInterface $logger = null)
{
parent::__construct($userProvider, $tokenStorage, $firewallName, $logger);
$this->userKey = $userKey;
}
protected function extractUsername(Request $request): ?string
{
if (!$request->server->has($this->userKey)) {
throw new BadCredentialsException(\sprintf('User key was not found: "%s".', $this->userKey));
}
return $request->server->get($this->userKey) ?: null;
}
}

View File

@@ -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\Component\Security\Http\Authenticator\Token;
use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
use Symfony\Component\Security\Core\User\UserInterface;
class PostAuthenticationToken extends AbstractToken
{
private string $firewallName;
/**
* @param string[] $roles An array of roles
*
* @throws \InvalidArgumentException
*/
public function __construct(UserInterface $user, string $firewallName, array $roles)
{
parent::__construct($roles);
if ('' === $firewallName) {
throw new \InvalidArgumentException('$firewallName must not be empty.');
}
$this->setUser($user);
$this->firewallName = $firewallName;
// required for compatibility with Symfony 5.4
if (method_exists($this, 'setAuthenticated')) {
// this token is meant to be used after authentication success, so it is always authenticated
$this->setAuthenticated(true, false);
}
}
/**
* This is meant to be only a token, where credentials
* have already been used and are thus cleared.
*/
public function getCredentials(): mixed
{
return [];
}
public function getFirewallName(): string
{
return $this->firewallName;
}
public function __serialize(): array
{
return [$this->firewallName, parent::__serialize()];
}
public function __unserialize(array $data): void
{
[$this->firewallName, $parentData] = $data;
parent::__unserialize($parentData);
}
}

View File

@@ -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\Component\Security\Http\Authenticator;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
/**
* This authenticator authenticates pre-authenticated (by the
* webserver) X.509 certificates.
*
* @author Wouter de Jong <wouter@wouterj.nl>
* @author Fabien Potencier <fabien@symfony.com>
*
* @final
*/
class X509Authenticator extends AbstractPreAuthenticatedAuthenticator
{
private string $userKey;
private string $credentialsKey;
private string $credentialUserIdentifier;
public function __construct(UserProviderInterface $userProvider, TokenStorageInterface $tokenStorage, string $firewallName, string $userKey = 'SSL_CLIENT_S_DN_Email', string $credentialsKey = 'SSL_CLIENT_S_DN', ?LoggerInterface $logger = null, string $credentialUserIdentifier = 'emailAddress')
{
parent::__construct($userProvider, $tokenStorage, $firewallName, $logger);
$this->userKey = $userKey;
$this->credentialsKey = $credentialsKey;
$this->credentialUserIdentifier = $credentialUserIdentifier;
}
protected function extractUsername(Request $request): string
{
$username = null;
if ($request->server->has($this->userKey)) {
$username = $request->server->get($this->userKey);
} elseif (
$request->server->has($this->credentialsKey)
&& preg_match('#'.preg_quote($this->credentialUserIdentifier, '#').'=([^,/]++)#', $request->server->get($this->credentialsKey), $matches)
) {
$username = trim($matches[1]);
}
if (null === $username) {
throw new BadCredentialsException(\sprintf('SSL credentials not found: "%s", "%s".', $this->userKey, $this->credentialsKey));
}
return $username;
}
}