ヤミRoot VoidGate
User / IP
:
216.73.216.143
Host / Server
:
146.88.233.70 / dev.loger.cm
System
:
Linux hybrid1120.fr.ns.planethoster.net 3.10.0-957.21.2.el7.x86_64 #1 SMP Wed Jun 5 14:26:44 UTC 2019 x86_64
Command
|
Upload
|
Create
Mass Deface
|
Jumping
|
Symlink
|
Reverse Shell
Ping
|
Port Scan
|
DNS Lookup
|
Whois
|
Header
|
cURL
:
/
home
/
logercm
/
dev.loger.cm
/
fixtures
/
assert
/
Viewing: security-http.tar
Attribute/CurrentUser.php 0000644 00000000676 15120140656 0011512 0 ustar 00 <?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\Attribute; /** * Indicates that a controller argument should receive the current logged user. */ #[\Attribute(\Attribute::TARGET_PARAMETER)] class CurrentUser { } Authentication/AuthenticationFailureHandlerInterface.php 0000644 00000002201 15120140656 0017635 0 ustar 00 <?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\Authentication; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Exception\AuthenticationException; /** * Interface for custom authentication failure handlers. * * If you want to customize the failure handling process, instead of * overwriting the respective listener globally, you can set a custom failure * handler which implements this interface. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ interface AuthenticationFailureHandlerInterface { /** * This is called when an interactive authentication attempt fails. This is * called by authentication listeners inheriting from * AbstractAuthenticationListener. * * @return Response */ public function onAuthenticationFailure(Request $request, AuthenticationException $exception); } Authentication/AuthenticationSuccessHandlerInterface.php 0000644 00000002177 15120140656 0017672 0 ustar 00 <?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\Authentication; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; /** * Interface for a custom authentication success handler. * * If you want to customize the success handling process, instead of * overwriting the respective listener globally, you can set a custom success * handler which implements this interface. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ interface AuthenticationSuccessHandlerInterface { /** * This is called when an interactive authentication attempt succeeds. This * is called by authentication listeners inheriting from * AbstractAuthenticationListener. * * @return Response|null */ public function onAuthenticationSuccess(Request $request, TokenInterface $token); } Authentication/AuthenticationUtils.php 0000644 00000004347 15120140656 0014244 0 ustar 00 <?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\Authentication; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Security; /** * Extracts Security Errors from Request. * * @author Boris Vujicic <boris.vujicic@gmail.com> */ class AuthenticationUtils { private $requestStack; public function __construct(RequestStack $requestStack) { $this->requestStack = $requestStack; } /** * @return AuthenticationException|null */ public function getLastAuthenticationError(bool $clearSession = true) { $request = $this->getRequest(); $authenticationException = null; if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) { $authenticationException = $request->attributes->get(Security::AUTHENTICATION_ERROR); } elseif ($request->hasSession() && ($session = $request->getSession())->has(Security::AUTHENTICATION_ERROR)) { $authenticationException = $session->get(Security::AUTHENTICATION_ERROR); if ($clearSession) { $session->remove(Security::AUTHENTICATION_ERROR); } } return $authenticationException; } /** * @return string */ public function getLastUsername() { $request = $this->getRequest(); if ($request->attributes->has(Security::LAST_USERNAME)) { return $request->attributes->get(Security::LAST_USERNAME, ''); } return $request->hasSession() ? $request->getSession()->get(Security::LAST_USERNAME, '') : ''; } /** * @throws \LogicException */ private function getRequest(): Request { $request = $this->requestStack->getCurrentRequest(); if (null === $request) { throw new \LogicException('Request should exist so it can be processed for error.'); } return $request; } } Authentication/AuthenticatorManager.php 0000644 00000034563 15120140656 0014354 0 ustar 00 <?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\Authentication; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\AuthenticationEvents; use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent; use Symfony\Component\Security\Core\Exception\AccountStatusException; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\CustomUserMessageAccountStatusException; use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; use Symfony\Component\Security\Http\Authenticator\Debug\TraceableAuthenticator; use Symfony\Component\Security\Http\Authenticator\InteractiveAuthenticatorInterface; 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\PassportInterface; use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport; use Symfony\Component\Security\Http\Event\AuthenticationTokenCreatedEvent; use Symfony\Component\Security\Http\Event\CheckPassportEvent; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\Event\LoginFailureEvent; use Symfony\Component\Security\Http\Event\LoginSuccessEvent; use Symfony\Component\Security\Http\SecurityEvents; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * @author Wouter de Jong <wouter@wouterj.nl> * @author Ryan Weaver <ryan@symfonycasts.com> * @author Amaury Leroux de Lens <amaury@lerouxdelens.com> */ class AuthenticatorManager implements AuthenticatorManagerInterface, UserAuthenticatorInterface { private $authenticators; private $tokenStorage; private $eventDispatcher; private $eraseCredentials; private $logger; private $firewallName; private $hideUserNotFoundExceptions; private $requiredBadges; /** * @param iterable<mixed, AuthenticatorInterface> $authenticators */ public function __construct(iterable $authenticators, TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher, string $firewallName, LoggerInterface $logger = null, bool $eraseCredentials = true, bool $hideUserNotFoundExceptions = true, array $requiredBadges = []) { $this->authenticators = $authenticators; $this->tokenStorage = $tokenStorage; $this->eventDispatcher = $eventDispatcher; $this->firewallName = $firewallName; $this->logger = $logger; $this->eraseCredentials = $eraseCredentials; $this->hideUserNotFoundExceptions = $hideUserNotFoundExceptions; $this->requiredBadges = $requiredBadges; } /** * @param BadgeInterface[] $badges Optionally, pass some Passport badges to use for the manual login */ public function authenticateUser(UserInterface $user, AuthenticatorInterface $authenticator, Request $request, array $badges = []): ?Response { // create an authentication token for the User // @deprecated since Symfony 5.3, change to $user->getUserIdentifier() in 6.0 $passport = new SelfValidatingPassport(new UserBadge(method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(), function () use ($user) { return $user; }), $badges); $token = method_exists($authenticator, 'createToken') ? $authenticator->createToken($passport, $this->firewallName) : $authenticator->createAuthenticatedToken($passport, $this->firewallName); // announce the authentication token $token = $this->eventDispatcher->dispatch(new AuthenticationTokenCreatedEvent($token, $passport))->getAuthenticatedToken(); // authenticate this in the system return $this->handleAuthenticationSuccess($token, $passport, $request, $authenticator, $this->tokenStorage->getToken()); } public function supports(Request $request): ?bool { if (null !== $this->logger) { $context = ['firewall_name' => $this->firewallName]; if ($this->authenticators instanceof \Countable || \is_array($this->authenticators)) { $context['authenticators'] = \count($this->authenticators); } $this->logger->debug('Checking for authenticator support.', $context); } $authenticators = []; $skippedAuthenticators = []; $lazy = true; foreach ($this->authenticators as $authenticator) { if (null !== $this->logger) { $this->logger->debug('Checking support on authenticator.', ['firewall_name' => $this->firewallName, 'authenticator' => \get_class($authenticator)]); } if (false !== $supports = $authenticator->supports($request)) { $authenticators[] = $authenticator; $lazy = $lazy && null === $supports; } else { if (null !== $this->logger) { $this->logger->debug('Authenticator does not support the request.', ['firewall_name' => $this->firewallName, 'authenticator' => \get_class($authenticator)]); } $skippedAuthenticators[] = $authenticator; } } if (!$authenticators) { return false; } $request->attributes->set('_security_authenticators', $authenticators); $request->attributes->set('_security_skipped_authenticators', $skippedAuthenticators); return $lazy ? null : true; } public function authenticateRequest(Request $request): ?Response { $authenticators = $request->attributes->get('_security_authenticators'); $request->attributes->remove('_security_authenticators'); $request->attributes->remove('_security_skipped_authenticators'); if (!$authenticators) { return null; } return $this->executeAuthenticators($authenticators, $request); } /** * @param AuthenticatorInterface[] $authenticators */ private function executeAuthenticators(array $authenticators, Request $request): ?Response { foreach ($authenticators as $authenticator) { // recheck if the authenticator still supports the listener. supports() is called // eagerly (before token storage is initialized), whereas authenticate() is called // lazily (after initialization). if (false === $authenticator->supports($request)) { if (null !== $this->logger) { $this->logger->debug('Skipping the "{authenticator}" authenticator as it did not support the request.', ['authenticator' => \get_class($authenticator instanceof TraceableAuthenticator ? $authenticator->getAuthenticator() : $authenticator)]); } continue; } $response = $this->executeAuthenticator($authenticator, $request); if (null !== $response) { if (null !== $this->logger) { $this->logger->debug('The "{authenticator}" authenticator set the response. Any later authenticator will not be called', ['authenticator' => \get_class($authenticator instanceof TraceableAuthenticator ? $authenticator->getAuthenticator() : $authenticator)]); } return $response; } } return null; } private function executeAuthenticator(AuthenticatorInterface $authenticator, Request $request): ?Response { $passport = null; $previousToken = $this->tokenStorage->getToken(); try { // get the passport from the Authenticator $passport = $authenticator->authenticate($request); // check the passport (e.g. password checking) $event = new CheckPassportEvent($authenticator, $passport); $this->eventDispatcher->dispatch($event); // check if all badges are resolved $resolvedBadges = []; foreach ($passport->getBadges() as $badge) { if (!$badge->isResolved()) { throw new BadCredentialsException(sprintf('Authentication failed: Security badge "%s" is not resolved, did you forget to register the correct listeners?', get_debug_type($badge))); } $resolvedBadges[] = \get_class($badge); } $missingRequiredBadges = array_diff($this->requiredBadges, $resolvedBadges); if ($missingRequiredBadges) { throw new BadCredentialsException(sprintf('Authentication failed; Some badges marked as required by the firewall config are not available on the passport: "%s".', implode('", "', $missingRequiredBadges))); } // create the authentication token $authenticatedToken = method_exists($authenticator, 'createToken') ? $authenticator->createToken($passport, $this->firewallName) : $authenticator->createAuthenticatedToken($passport, $this->firewallName); // announce the authentication token $authenticatedToken = $this->eventDispatcher->dispatch(new AuthenticationTokenCreatedEvent($authenticatedToken, $passport))->getAuthenticatedToken(); if (true === $this->eraseCredentials) { $authenticatedToken->eraseCredentials(); } $this->eventDispatcher->dispatch(new AuthenticationSuccessEvent($authenticatedToken), AuthenticationEvents::AUTHENTICATION_SUCCESS); if (null !== $this->logger) { $this->logger->info('Authenticator successful!', ['token' => $authenticatedToken, 'authenticator' => \get_class($authenticator instanceof TraceableAuthenticator ? $authenticator->getAuthenticator() : $authenticator)]); } } catch (AuthenticationException $e) { // oh no! Authentication failed! $response = $this->handleAuthenticationFailure($e, $request, $authenticator, $passport); if ($response instanceof Response) { return $response; } return null; } // success! (sets the token on the token storage, etc) $response = $this->handleAuthenticationSuccess($authenticatedToken, $passport, $request, $authenticator, $previousToken); if ($response instanceof Response) { return $response; } if (null !== $this->logger) { $this->logger->debug('Authenticator set no success response: request continues.', ['authenticator' => \get_class($authenticator instanceof TraceableAuthenticator ? $authenticator->getAuthenticator() : $authenticator)]); } return null; } private function handleAuthenticationSuccess(TokenInterface $authenticatedToken, PassportInterface $passport, Request $request, AuthenticatorInterface $authenticator, ?TokenInterface $previousToken): ?Response { // @deprecated since Symfony 5.3 $user = $authenticatedToken->getUser(); if ($user instanceof UserInterface && !method_exists($user, 'getUserIdentifier')) { trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "getUserIdentifier(): string" in user class "%s" is deprecated. This method will replace "getUsername()" in Symfony 6.0.', get_debug_type($authenticatedToken->getUser())); } $this->tokenStorage->setToken($authenticatedToken); $response = $authenticator->onAuthenticationSuccess($request, $authenticatedToken, $this->firewallName); if ($authenticator instanceof InteractiveAuthenticatorInterface && $authenticator->isInteractive()) { $loginEvent = new InteractiveLoginEvent($request, $authenticatedToken); $this->eventDispatcher->dispatch($loginEvent, SecurityEvents::INTERACTIVE_LOGIN); } $this->eventDispatcher->dispatch($loginSuccessEvent = new LoginSuccessEvent($authenticator, $passport, $authenticatedToken, $request, $response, $this->firewallName, $previousToken)); return $loginSuccessEvent->getResponse(); } /** * Handles an authentication failure and returns the Response for the authenticator. */ private function handleAuthenticationFailure(AuthenticationException $authenticationException, Request $request, AuthenticatorInterface $authenticator, ?PassportInterface $passport): ?Response { if (null !== $this->logger) { $this->logger->info('Authenticator failed.', ['exception' => $authenticationException, 'authenticator' => \get_class($authenticator instanceof TraceableAuthenticator ? $authenticator->getAuthenticator() : $authenticator)]); } // Avoid leaking error details in case of invalid user (e.g. user not found or invalid account status) // to prevent user enumeration via response content comparison if ($this->hideUserNotFoundExceptions && ($authenticationException instanceof UserNotFoundException || ($authenticationException instanceof AccountStatusException && !$authenticationException instanceof CustomUserMessageAccountStatusException))) { $authenticationException = new BadCredentialsException('Bad credentials.', 0, $authenticationException); } $response = $authenticator->onAuthenticationFailure($request, $authenticationException); if (null !== $response && null !== $this->logger) { $this->logger->debug('The "{authenticator}" authenticator set the failure response.', ['authenticator' => \get_class($authenticator instanceof TraceableAuthenticator ? $authenticator->getAuthenticator() : $authenticator)]); } $this->eventDispatcher->dispatch($loginFailureEvent = new LoginFailureEvent($authenticationException, $authenticator, $request, $response, $this->firewallName, $passport)); // returning null is ok, it means they want the request to continue return $loginFailureEvent->getResponse(); } } Authentication/AuthenticatorManagerInterface.php 0000644 00000001743 15120140656 0016167 0 ustar 00 <?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\Authentication; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Http\Firewall\FirewallListenerInterface; /** * @author Wouter de Jong <wouter@wouterj.nl> * @author Ryan Weaver <ryan@symfonycasts.com> */ interface AuthenticatorManagerInterface { /** * Called to see if authentication should be attempted on this request. * * @see FirewallListenerInterface::supports() */ public function supports(Request $request): ?bool; /** * Tries to authenticate the request and returns a response - if any authenticator set one. */ public function authenticateRequest(Request $request): ?Response; } Authentication/CustomAuthenticationFailureHandler.php 0000644 00000002227 15120140656 0017217 0 ustar 00 <?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\Authentication; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Exception\AuthenticationException; /** * @author Fabien Potencier <fabien@symfony.com> */ class CustomAuthenticationFailureHandler implements AuthenticationFailureHandlerInterface { private $handler; /** * @param array $options Options for processing a successful authentication attempt */ public function __construct(AuthenticationFailureHandlerInterface $handler, array $options) { $this->handler = $handler; if (method_exists($handler, 'setOptions')) { $this->handler->setOptions($options); } } /** * {@inheritdoc} */ public function onAuthenticationFailure(Request $request, AuthenticationException $exception) { return $this->handler->onAuthenticationFailure($request, $exception); } } Authentication/CustomAuthenticationSuccessHandler.php 0000644 00000003123 15120140656 0017234 0 ustar 00 <?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\Authentication; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; /** * @author Fabien Potencier <fabien@symfony.com> */ class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface { private $handler; /** * @param array $options Options for processing a successful authentication attempt */ public function __construct(AuthenticationSuccessHandlerInterface $handler, array $options, string $firewallName) { $this->handler = $handler; if (method_exists($handler, 'setOptions')) { $this->handler->setOptions($options); } if (method_exists($handler, 'setFirewallName')) { $this->handler->setFirewallName($firewallName); } elseif (method_exists($handler, 'setProviderKey')) { trigger_deprecation('symfony/security-http', '5.2', 'Method "%s::setProviderKey()" is deprecated, rename the method to "setFirewallName()" instead.', \get_class($handler)); $this->handler->setProviderKey($firewallName); } } /** * {@inheritdoc} */ public function onAuthenticationSuccess(Request $request, TokenInterface $token) { return $this->handler->onAuthenticationSuccess($request, $token); } } Authentication/DefaultAuthenticationFailureHandler.php 0000644 00000006763 15120140656 0017342 0 ustar 00 <?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\Authentication; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Http\ParameterBagUtils; /** * Class with the default authentication failure handling logic. * * Can be optionally be extended from by the developer to alter the behavior * while keeping the default behavior. * * @author Fabien Potencier <fabien@symfony.com> * @author Johannes M. Schmitt <schmittjoh@gmail.com> * @author Alexander <iam.asm89@gmail.com> */ class DefaultAuthenticationFailureHandler implements AuthenticationFailureHandlerInterface { protected $httpKernel; protected $httpUtils; protected $logger; protected $options; protected $defaultOptions = [ 'failure_path' => null, 'failure_forward' => false, 'login_path' => '/login', 'failure_path_parameter' => '_failure_path', ]; public function __construct(HttpKernelInterface $httpKernel, HttpUtils $httpUtils, array $options = [], LoggerInterface $logger = null) { $this->httpKernel = $httpKernel; $this->httpUtils = $httpUtils; $this->logger = $logger; $this->setOptions($options); } /** * Gets the options. * * @return array */ public function getOptions() { return $this->options; } public function setOptions(array $options) { $this->options = array_merge($this->defaultOptions, $options); } /** * {@inheritdoc} */ public function onAuthenticationFailure(Request $request, AuthenticationException $exception) { $options = $this->options; $failureUrl = ParameterBagUtils::getRequestParameterValue($request, $options['failure_path_parameter']); if (\is_string($failureUrl) && (str_starts_with($failureUrl, '/') || str_starts_with($failureUrl, 'http'))) { $options['failure_path'] = $failureUrl; } elseif ($this->logger && $failureUrl) { $this->logger->debug(sprintf('Ignoring query parameter "%s": not a valid URL.', $options['failure_path_parameter'])); } $options['failure_path'] ?? $options['failure_path'] = $options['login_path']; if ($options['failure_forward']) { if (null !== $this->logger) { $this->logger->debug('Authentication failure, forward triggered.', ['failure_path' => $options['failure_path']]); } $subRequest = $this->httpUtils->createRequest($request, $options['failure_path']); $subRequest->attributes->set(Security::AUTHENTICATION_ERROR, $exception); return $this->httpKernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST); } if (null !== $this->logger) { $this->logger->debug('Authentication failure, redirect triggered.', ['failure_path' => $options['failure_path']]); } $request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception); return $this->httpUtils->createRedirectResponse($request, $options['failure_path']); } } Authentication/DefaultAuthenticationSuccessHandler.php 0000644 00000011751 15120140656 0017354 0 ustar 00 <?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\Authentication; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Http\ParameterBagUtils; use Symfony\Component\Security\Http\Util\TargetPathTrait; /** * Class with the default authentication success handling logic. * * @author Fabien Potencier <fabien@symfony.com> * @author Johannes M. Schmitt <schmittjoh@gmail.com> * @author Alexander <iam.asm89@gmail.com> */ class DefaultAuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface { use TargetPathTrait; protected $httpUtils; protected $logger; protected $options; /** @deprecated since Symfony 5.2, use $firewallName instead */ protected $providerKey; protected $firewallName; protected $defaultOptions = [ 'always_use_default_target_path' => false, 'default_target_path' => '/', 'login_path' => '/login', 'target_path_parameter' => '_target_path', 'use_referer' => false, ]; /** * @param array $options Options for processing a successful authentication attempt */ public function __construct(HttpUtils $httpUtils, array $options = [], LoggerInterface $logger = null) { $this->httpUtils = $httpUtils; $this->logger = $logger; $this->setOptions($options); } /** * {@inheritdoc} */ public function onAuthenticationSuccess(Request $request, TokenInterface $token) { return $this->httpUtils->createRedirectResponse($request, $this->determineTargetUrl($request)); } /** * Gets the options. * * @return array */ public function getOptions() { return $this->options; } public function setOptions(array $options) { $this->options = array_merge($this->defaultOptions, $options); } /** * Get the provider key. * * @return string * * @deprecated since Symfony 5.2, use getFirewallName() instead */ public function getProviderKey() { if (1 !== \func_num_args() || true !== func_get_arg(0)) { trigger_deprecation('symfony/security-core', '5.2', 'Method "%s()" is deprecated, use "getFirewallName()" instead.', __METHOD__); } if ($this->providerKey !== $this->firewallName) { trigger_deprecation('symfony/security-core', '5.2', 'The "%1$s::$providerKey" property is deprecated, use "%1$s::$firewallName" instead.', __CLASS__); return $this->providerKey; } return $this->firewallName; } public function setProviderKey(string $providerKey) { if (2 !== \func_num_args() || true !== func_get_arg(1)) { trigger_deprecation('symfony/security-http', '5.2', 'Method "%s" is deprecated, use "setFirewallName()" instead.', __METHOD__); } $this->providerKey = $providerKey; } public function getFirewallName(): ?string { return $this->getProviderKey(true); } public function setFirewallName(string $firewallName): void { $this->setProviderKey($firewallName, true); $this->firewallName = $firewallName; } /** * Builds the target URL according to the defined options. * * @return string */ protected function determineTargetUrl(Request $request) { if ($this->options['always_use_default_target_path']) { return $this->options['default_target_path']; } $targetUrl = ParameterBagUtils::getRequestParameterValue($request, $this->options['target_path_parameter']); if (\is_string($targetUrl) && (str_starts_with($targetUrl, '/') || str_starts_with($targetUrl, 'http'))) { return $targetUrl; } if ($this->logger && $targetUrl) { $this->logger->debug(sprintf('Ignoring query parameter "%s": not a valid URL.', $this->options['target_path_parameter'])); } $firewallName = $this->getFirewallName(); if (null !== $firewallName && $targetUrl = $this->getTargetPath($request->getSession(), $firewallName)) { $this->removeTargetPath($request->getSession(), $firewallName); return $targetUrl; } if ($this->options['use_referer'] && $targetUrl = $request->headers->get('Referer')) { if (false !== $pos = strpos($targetUrl, '?')) { $targetUrl = substr($targetUrl, 0, $pos); } if ($targetUrl && $targetUrl !== $this->httpUtils->generateUri($request, $this->options['login_path'])) { return $targetUrl; } } return $this->options['default_target_path']; } } Authentication/NoopAuthenticationManager.php 0000644 00000001574 15120140656 0015351 0 ustar 00 <?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\Authentication; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; /** * This class is used when the authenticator system is activated. * * This is used to not break AuthenticationChecker and ContextListener when * using the authenticator system. * * @author Wouter de Jong <wouter@wouterj.nl> * * @internal */ class NoopAuthenticationManager implements AuthenticationManagerInterface { public function authenticate(TokenInterface $token): TokenInterface { return $token; } } Authentication/UserAuthenticatorInterface.php 0000644 00000002072 15120140656 0015527 0 ustar 00 <?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\Authentication; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\BadgeInterface; /** * @author Wouter de Jong <wouter@wouterj.nl> */ interface UserAuthenticatorInterface { /** * Convenience method to programmatically login a user and return a * Response *if any* for success. * * @param BadgeInterface[] $badges Optionally, pass some Passport badges to use for the manual login */ public function authenticateUser(UserInterface $user, AuthenticatorInterface $authenticator, Request $request, array $badges = []): ?Response; } Authenticator/Debug/TraceableAuthenticator.php 0000644 00000010043 15120140656 0015530 0 ustar 00 <?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\Guard\Authenticator\GuardBridgeAuthenticator; use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; use Symfony\Component\Security\Http\Authenticator\InteractiveAuthenticatorInterface; use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface; 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 $authenticator; private $passport; private $duration; private $stub; public function __construct(AuthenticatorInterface $authenticator) { $this->authenticator = $authenticator; } public function getInfo(): array { $class = \get_class($this->authenticator instanceof GuardBridgeAuthenticator ? $this->authenticator->getGuardAuthenticator() : $this->authenticator); return [ 'supports' => true, 'passport' => $this->passport, 'duration' => $this->duration, 'stub' => $this->stub ?? $this->stub = class_exists(ClassStub::class) ? new ClassStub($class) : $class, ]; } public function supports(Request $request): ?bool { return $this->authenticator->supports($request); } public function authenticate(Request $request): PassportInterface { $startTime = microtime(true); $this->passport = $this->authenticator->authenticate($request); $this->duration = microtime(true) - $startTime; return $this->passport; } public function createToken(PassportInterface $passport, string $firewallName): TokenInterface { return method_exists($this->authenticator, 'createToken') ? $this->authenticator->createToken($passport, $firewallName) : $this->authenticator->createAuthenticatedToken($passport, $firewallName); } public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface { return $this->authenticator->createAuthenticatedToken($passport, $firewallName); } public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response { return $this->authenticator->onAuthenticationSuccess($request, $token, $firewallName); } public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response { 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) { return $this->authenticator->{$method}(...$args); } } Authenticator/Debug/TraceableAuthenticatorManagerListener.php 0000644 00000005154 15120140656 0020540 0 ustar 00 <?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; /** * Decorates the AuthenticatorManagerListener to collect information about security authenticators. * * @author Robin Chalas <robin.chalas@gmail.com> */ final class TraceableAuthenticatorManagerListener extends AbstractListener { private $authenticationManagerListener; private $authenticatorsInfo = []; private $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(\get_class($skippedAuthenticator)) : \get_class($skippedAuthenticator), 'passport' => null, 'duration' => 0, ]; } 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; } } Authenticator/Passport/Badge/BadgeInterface.php 0000644 00000001335 15120140656 0015531 0 ustar 00 <?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; } Authenticator/Passport/Badge/CsrfTokenBadge.php 0000644 00000003041 15120140656 0015523 0 ustar 00 <?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 $resolved = false; private $csrfTokenId; private $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, ?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; } } Authenticator/Passport/Badge/PasswordUpgradeBadge.php 0000644 00000003353 15120140656 0016745 0 ustar 00 <?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 $plaintextPassword; private $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(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; } } Authenticator/Passport/Badge/PreAuthenticatedUserBadge.php 0000644 00000001374 15120140656 0017724 0 ustar 00 <?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; } } Authenticator/Passport/Badge/RememberMeBadge.php 0000644 00000003310 15120140656 0015644 0 ustar 00 <?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 $enabled = false; /** * 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(): self { $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(): self { $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 } } Authenticator/Passport/Badge/UserBadge.php 0000644 00000006336 15120140656 0014555 0 ustar 00 <?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\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 { private $userIdentifier; private $userLoader; private $user; /** * 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) { $this->userIdentifier = $userIdentifier; $this->userLoader = $userLoader; } public function getUserIdentifier(): string { return $this->userIdentifier; } /** * @throws AuthenticationException when the user cannot be found */ public function getUser(): UserInterface { if (null !== $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)); } $user = ($this->userLoader)($this->userIdentifier); // 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; } } Authenticator/Passport/Credentials/CredentialsInterface.php 0000644 00000001152 15120140656 0020214 0 ustar 00 <?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 { } Authenticator/Passport/Credentials/CustomCredentials.php 0000644 00000003350 15120140656 0017570 0 ustar 00 <?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 $customCredentialsChecker; private $credentials; private $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. * @param mixed $credentials */ public function __construct(callable $customCredentialsChecker, $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; } } Authenticator/Passport/Credentials/PasswordCredentials.php 0000644 00000002417 15120140656 0020123 0 ustar 00 <?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 $password; private $resolved = false; public function __construct(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; } } Authenticator/Passport/Passport.php 0000644 00000006275 15120140656 0013527 0 ustar 00 <?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 implements UserPassportInterface { protected $user; private $badges = []; private $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); } } /** * {@inheritdoc} */ public function getUser(): UserInterface { if (null === $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. * * @return $this */ public function addBadge(BadgeInterface $badge): PassportInterface { $this->badges[\get_class($badge)] = $badge; return $this; } public function hasBadge(string $badgeFqcn): bool { return isset($this->badges[$badgeFqcn]); } public function getBadge(string $badgeFqcn): ?BadgeInterface { return $this->badges[$badgeFqcn] ?? null; } /** * @return array<class-string<BadgeInterface>, BadgeInterface> */ public function getBadges(): array { return $this->badges; } /** * @param mixed $value */ public function setAttribute(string $name, $value): void { $this->attributes[$name] = $value; } /** * @param mixed $default * * @return mixed */ public function getAttribute(string $name, $default = null) { return $this->attributes[$name] ?? $default; } public function getAttributes(): array { return $this->attributes; } } Authenticator/Passport/PassportInterface.php 0000644 00000002420 15120140656 0015334 0 ustar 00 <?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; /** * 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> * * @deprecated since Symfony 5.4, use {@link Passport} instead */ interface PassportInterface { /** * 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. * * @return $this */ public function addBadge(BadgeInterface $badge): self; public function hasBadge(string $badgeFqcn): bool; public function getBadge(string $badgeFqcn): ?BadgeInterface; /** * @return array<class-string<BadgeInterface>, BadgeInterface> */ public function getBadges(): array; } Authenticator/Passport/PassportTrait.php 0000644 00000002452 15120140656 0014524 0 ustar 00 <?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; trigger_deprecation('symfony/security-http', '5.4', 'The "%s" trait is deprecated, you must extend from "%s" instead.', PassportTrait::class, Passport::class); /** * @author Wouter de Jong <wouter@wouterj.nl> * * @deprecated since Symfony 5.4, use {@see Passport} instead */ trait PassportTrait { private $badges = []; /** * @return $this */ public function addBadge(BadgeInterface $badge): PassportInterface { $this->badges[\get_class($badge)] = $badge; return $this; } public function hasBadge(string $badgeFqcn): bool { return isset($this->badges[$badgeFqcn]); } public function getBadge(string $badgeFqcn): ?BadgeInterface { return $this->badges[$badgeFqcn] ?? null; } /** * @return array<class-string<BadgeInterface>, BadgeInterface> */ public function getBadges(): array { return $this->badges; } } Authenticator/Passport/SelfValidatingPassport.php 0000644 00000001653 15120140656 0016337 0 ustar 00 <?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); } } } Authenticator/Passport/UserPassportInterface.php 0000644 00000001433 15120140656 0016176 0 ustar 00 <?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\Exception\AuthenticationException; use Symfony\Component\Security\Core\User\UserInterface; /** * Represents a passport for a Security User. * * @author Wouter de Jong <wouter@wouterj.nl> * * @deprecated since Symfony 5.4, use {@link Passport} instead */ interface UserPassportInterface extends PassportInterface { /** * @throws AuthenticationException when the user cannot be found */ public function getUser(): UserInterface; } Authenticator/Token/PostAuthenticationToken.php 0000644 00000003564 15120140656 0016005 0 ustar 00 <?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 $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; // @deprecated since 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. * * {@inheritdoc} */ public function getCredentials() { return []; } public function getFirewallName(): string { return $this->firewallName; } /** * {@inheritdoc} */ public function __serialize(): array { return [$this->firewallName, parent::__serialize()]; } /** * {@inheritdoc} */ public function __unserialize(array $data): void { [$this->firewallName, $parentData] = $data; parent::__unserialize($parentData); } } Authenticator/AbstractAuthenticator.php 0000644 00000004523 15120140656 0014371 0 ustar 00 <?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\Core\Exception\LogicException; use Symfony\Component\Security\Http\Authenticator\Passport\Passport; use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface; use Symfony\Component\Security\Http\Authenticator\Passport\UserPassportInterface; 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 { if (self::class !== (new \ReflectionMethod($this, 'createAuthenticatedToken'))->getDeclaringClass()->getName() && self::class === (new \ReflectionMethod($this, 'createToken'))->getDeclaringClass()->getName()) { return $this->createAuthenticatedToken($passport, $firewallName); } return new PostAuthenticationToken($passport->getUser(), $firewallName, $passport->getUser()->getRoles()); } /** * @deprecated since Symfony 5.4, use {@link createToken()} instead */ public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface { // @deprecated since Symfony 5.4 if (!$passport instanceof UserPassportInterface) { throw new LogicException(sprintf('Passport does not contain a user, overwrite "createToken()" in "%s" to create a custom authentication token.', static::class)); } trigger_deprecation('symfony/security-http', '5.4', 'Method "%s()" is deprecated, use "%s::createToken()" instead.', __METHOD__, __CLASS__); return new PostAuthenticationToken($passport->getUser(), $firewallName, $passport->getUser()->getRoles()); } } Authenticator/AbstractLoginFormAuthenticator.php 0000644 00000004474 15120140656 0016213 0 ustar 00 <?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\Core\Security; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; /** * 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; /** * {@inheritdoc} * * 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(Security::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; } } Authenticator/AbstractPreAuthenticatedAuthenticator.php 0000644 00000013757 15120140656 0017554 0 ustar 00 <?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\PassportInterface; 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 $userProvider; private $tokenStorage; private $firewallName; private $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); if (null !== $this->logger) { $this->logger->debug('Skipping pre-authenticated authenticator as a BadCredentialsException is thrown.', ['exception' => $e, 'authenticator' => static::class]); } return false; } if (null === $username) { if (null !== $this->logger) { $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) { if (null !== $this->logger) { $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 { // @deprecated since Symfony 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0 $method = 'loadUserByIdentifier'; if (!method_exists($this->userProvider, 'loadUserByIdentifier')) { trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->userProvider)); $method = 'loadUserByUsername'; } return new SelfValidatingPassport( new UserBadge($request->attributes->get('_pre_authenticated_username'), [$this->userProvider, $method]), [new PreAuthenticatedUserBadge()] ); } /** * @deprecated since Symfony 5.4, use {@link createToken()} instead */ public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface { trigger_deprecation('symfony/security-http', '5.4', 'Method "%s()" is deprecated, use "%s::createToken()" instead.', __METHOD__, __CLASS__); return $this->createToken($passport, $firewallName); } 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); if (null !== $this->logger) { $this->logger->info('Cleared pre-authenticated token due to an exception.', ['exception' => $exception]); } } } } Authenticator/AuthenticatorInterface.php 0000644 00000007606 15120140656 0014533 0 ustar 00 <?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; use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface; /** * 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> * * @method TokenInterface createToken(Passport $passport, string $firewallName) Creates a token for the given user. * If you don't care about which token class is used, you can skip this method by extending * the AbstractAuthenticator class from your authenticator. */ 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). * * @return Passport * * @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 PassportInterface $passport The passport returned from authenticate() * * @deprecated since Symfony 5.4, use {@link createToken()} instead */ public function createAuthenticatedToken(PassportInterface $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; } Authenticator/FormLoginAuthenticator.php 0000644 00000017531 15120140656 0014525 0 ustar 00 <?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\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Security; 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\Authenticator\Passport\PassportInterface; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Http\ParameterBagUtils; /** * @author Wouter de Jong <wouter@wouterj.nl> * @author Fabien Potencier <fabien@symfony.com> * * @final */ class FormLoginAuthenticator extends AbstractLoginFormAuthenticator { private $httpUtils; private $userProvider; private $successHandler; private $failureHandler; private $options; private $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->getContentType() : true); } public function authenticate(Request $request): Passport { $credentials = $this->getCredentials($request); // @deprecated since Symfony 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0 $method = 'loadUserByIdentifier'; if (!method_exists($this->userProvider, 'loadUserByIdentifier')) { trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->userProvider)); $method = 'loadUserByUsername'; } $passport = new Passport( new UserBadge($credentials['username'], [$this->userProvider, $method]), 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; } /** * @deprecated since Symfony 5.4, use {@link createToken()} instead */ public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface { trigger_deprecation('symfony/security-http', '5.4', 'Method "%s()" is deprecated, use "%s::createToken()" instead.', __METHOD__, __CLASS__); return $this->createToken($passport, $firewallName); } 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']) && (!\is_object($credentials['username']) || !method_exists($credentials['username'], '__toString'))) { 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']); if (\strlen($credentials['username']) > Security::MAX_USERNAME_LENGTH) { throw new BadCredentialsException('Invalid username.'); } $request->getSession()->set(Security::LAST_USERNAME, $credentials['username']); 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; } } Authenticator/HttpBasicAuthenticator.php 0000644 00000010637 15120140656 0014512 0 ustar 00 <?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\Authenticator\Passport\PassportInterface; 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 $realmName; private $userProvider; private $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): PassportInterface { $username = $request->headers->get('PHP_AUTH_USER'); $password = $request->headers->get('PHP_AUTH_PW', ''); // @deprecated since Symfony 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0 $method = 'loadUserByIdentifier'; if (!method_exists($this->userProvider, 'loadUserByIdentifier')) { trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->userProvider)); $method = 'loadUserByUsername'; } $passport = new Passport( new UserBadge($username, [$this->userProvider, $method]), new PasswordCredentials($password) ); if ($this->userProvider instanceof PasswordUpgraderInterface) { $passport->addBadge(new PasswordUpgradeBadge($password, $this->userProvider)); } return $passport; } /** * @deprecated since Symfony 5.4, use {@link createToken()} instead */ public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface { trigger_deprecation('symfony/security-http', '5.4', 'Method "%s()" is deprecated, use "%s::createToken()" instead.', __METHOD__, __CLASS__); return $this->createToken($passport, $firewallName); } 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 { if (null !== $this->logger) { $this->logger->info('Basic authentication failed for user.', ['username' => $request->headers->get('PHP_AUTH_USER'), 'exception' => $exception]); } return $this->start($request, $exception); } } Authenticator/InteractiveAuthenticatorInterface.php 0000644 00000001571 15120140656 0016724 0 ustar 00 <?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 must * be used by interactive authenticators. * * Interactive login requires explicit user action (e.g. a login * form or HTTP basic authentication). 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; } Authenticator/JsonLoginAuthenticator.php 0000644 00000017577 15120140656 0014545 0 ustar 00 <?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\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Security; 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\UserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials; use Symfony\Component\Security\Http\Authenticator\Passport\Passport; use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface; 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 $options; private $httpUtils; private $userProvider; private $propertyAccessor; private $successHandler; private $failureHandler; /** * @var TranslatorInterface|null */ private $translator; 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 (false === strpos($request->getRequestFormat() ?? '', 'json') && false === strpos($request->getContentType() ?? '', '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): PassportInterface { try { $credentials = $this->getCredentials($request); } catch (BadRequestHttpException $e) { $request->setRequestFormat('json'); throw $e; } // @deprecated since Symfony 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0 $method = 'loadUserByIdentifier'; if (!method_exists($this->userProvider, 'loadUserByIdentifier')) { trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->userProvider)); $method = 'loadUserByUsername'; } $passport = new Passport( new UserBadge($credentials['username'], [$this->userProvider, $method]), new PasswordCredentials($credentials['password']) ); if ($this->userProvider instanceof PasswordUpgraderInterface) { $passport->addBadge(new PasswordUpgradeBadge($credentials['password'], $this->userProvider)); } return $passport; } /** * @deprecated since Symfony 5.4, use {@link createToken()} instead */ public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface { trigger_deprecation('symfony/security-http', '5.4', 'Method "%s()" is deprecated, use "%s::createToken()" instead.', __METHOD__, __CLASS__); return $this->createToken($passport, $firewallName); } 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) { $this->translator = $translator; } private function getCredentials(Request $request) { $data = json_decode($request->getContent()); if (!$data instanceof \stdClass) { throw new BadRequestHttpException('Invalid JSON.'); } $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'])); } if (\strlen($credentials['username']) > Security::MAX_USERNAME_LENGTH) { throw new BadCredentialsException('Invalid username.'); } } 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']); 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); } return $credentials; } } Authenticator/LoginLinkAuthenticator.php 0000644 00000007067 15120140656 0014522 0 ustar 00 <?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\PassportInterface; 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 $loginLinkHandler; private $httpUtils; private $successHandler; private $failureHandler; private $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): PassportInterface { $username = $request->get('user'); if (!$username) { throw new InvalidLoginLinkAuthenticationException('Missing user from link.'); } return new SelfValidatingPassport( 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; }), [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; } } Authenticator/RememberMeAuthenticator.php 0000644 00000012365 15120140656 0014651 0 ustar 00 <?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\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\PassportInterface; 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 $rememberMeHandler; private $secret; private $tokenStorage; private $cookieName; private $logger; public function __construct(RememberMeHandlerInterface $rememberMeHandler, string $secret, TokenStorageInterface $tokenStorage, string $cookieName, LoggerInterface $logger = null) { $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)) { return false; } if (null !== $this->logger) { $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): PassportInterface { $rawCookie = $request->cookies->get($this->cookieName); if (!$rawCookie) { throw new \LogicException('No remember-me cookie is found.'); } $rememberMeCookie = RememberMeDetails::fromRawCookie($rawCookie); return new SelfValidatingPassport(new UserBadge($rememberMeCookie->getUserIdentifier(), function () use ($rememberMeCookie) { return $this->rememberMeHandler->consumeRememberMeCookie($rememberMeCookie); })); } /** * @deprecated since Symfony 5.4, use {@link createToken()} instead */ public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface { trigger_deprecation('symfony/security-http', '5.4', 'Method "%s()" is deprecated, use "%s::createToken()" instead.', __METHOD__, __CLASS__); return $this->createToken($passport, $firewallName); } 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; } } Authenticator/RemoteUserAuthenticator.php 0000644 00000003055 15120140656 0014717 0 ustar 00 <?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 $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); } } Authenticator/X509Authenticator.php 0000644 00000003771 15120140656 0013277 0 ustar 00 <?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 $userKey; private $credentialsKey; 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) { parent::__construct($userProvider, $tokenStorage, $firewallName, $logger); $this->userKey = $userKey; $this->credentialsKey = $credentialsKey; } 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('#emailAddress=([^,/@]++@[^,/]++)#', $request->server->get($this->credentialsKey), $matches) ) { $username = $matches[1]; } if (null === $username) { throw new BadCredentialsException(sprintf('SSL credentials not found: %s, %s', $this->userKey, $this->credentialsKey)); } return $username; } } Authorization/AccessDeniedHandlerInterface.php 0000644 00000001530 15120140656 0015545 0 ustar 00 <?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\Authorization; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Exception\AccessDeniedException; /** * This is used by the ExceptionListener to translate an AccessDeniedException * to a Response object. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ interface AccessDeniedHandlerInterface { /** * Handles an access denied failure. * * @return Response|null */ public function handle(Request $request, AccessDeniedException $accessDeniedException); } Controller/UserValueResolver.php 0000644 00000003723 15120140656 0013042 0 ustar 00 <?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\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Http\Attribute\CurrentUser; /** * Supports the argument type of {@see UserInterface}. * * @author Iltar van der Berg <kjarli@gmail.com> */ final class UserValueResolver implements ArgumentValueResolverInterface { private $tokenStorage; public function __construct(TokenStorageInterface $tokenStorage) { $this->tokenStorage = $tokenStorage; } public function supports(Request $request, ArgumentMetadata $argument): bool { // with the attribute, the type can be any UserInterface implementation // otherwise, the type must be UserInterface if (UserInterface::class !== $argument->getType() && !$argument->getAttributes(CurrentUser::class, ArgumentMetadata::IS_INSTANCEOF)) { return false; } $token = $this->tokenStorage->getToken(); if (!$token instanceof TokenInterface) { return false; } $user = $token->getUser(); // in case it's not an object we cannot do anything with it; E.g. "anon." // @deprecated since 5.4 return $user instanceof UserInterface; } public function resolve(Request $request, ArgumentMetadata $argument): iterable { yield $this->tokenStorage->getToken()->getUser(); } } EntryPoint/Exception/NotAnEntryPointException.php 0000644 00000001047 15120140656 0016262 0 ustar 00 <?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\EntryPoint\Exception; /** * Thrown by generic decorators when a decorated authenticator does not implement * {@see AuthenticationEntryPointInterface}. * * @author Robin Chalas <robin.chalas@gmail.com> */ class NotAnEntryPointException extends \RuntimeException { } EntryPoint/AuthenticationEntryPointInterface.php 0000644 00000002636 15120140656 0016233 0 ustar 00 <?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\EntryPoint; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Exception\AuthenticationException; /** * Implement this interface for any classes that will be called to "start" * the authentication process (see method for more details). * * @author Fabien Potencier <fabien@symfony.com> */ interface AuthenticationEntryPointInterface { /** * Returns a response that directs the user to authenticate. * * This is called when an anonymous request accesses a resource that * requires authentication. The job of this method is to return some * response that "helps" the user start into the authentication process. * * Examples: * * - For a form login, you might redirect to the login page * * return new RedirectResponse('/login'); * * - For an API token authentication system, you return a 401 response * * return new Response('Auth header required', 401); * * @return Response */ public function start(Request $request, AuthenticationException $authException = null); } EntryPoint/BasicAuthenticationEntryPoint.php 0000644 00000002660 15120140656 0015351 0 ustar 00 <?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\EntryPoint; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\Authenticator\HttpBasicAuthenticator; trigger_deprecation('symfony/security-http', '5.4', 'The "%s" class is deprecated, use the new security system with "%s" instead.', BasicAuthenticationEntryPoint::class, HttpBasicAuthenticator::class); /** * BasicAuthenticationEntryPoint starts an HTTP Basic authentication. * * @author Fabien Potencier <fabien@symfony.com> * * @deprecated since Symfony 5.4 */ class BasicAuthenticationEntryPoint implements AuthenticationEntryPointInterface { private $realmName; public function __construct(string $realmName) { $this->realmName = $realmName; } /** * {@inheritdoc} */ public function start(Request $request, AuthenticationException $authException = null) { $response = new Response(); $response->headers->set('WWW-Authenticate', sprintf('Basic realm="%s"', $this->realmName)); $response->setStatusCode(401); return $response; } } EntryPoint/FormAuthenticationEntryPoint.php 0000644 00000004226 15120140656 0015233 0 ustar 00 <?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\EntryPoint; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\Authenticator\FormLoginAuthenticator; use Symfony\Component\Security\Http\HttpUtils; trigger_deprecation('symfony/security-http', '5.4', 'The "%s" class is deprecated, use the new security system with "%s" instead.', FormAuthenticationEntryPoint::class, FormLoginAuthenticator::class); /** * FormAuthenticationEntryPoint starts an authentication via a login form. * * @author Fabien Potencier <fabien@symfony.com> * * @deprecated since Symfony 5.4 */ class FormAuthenticationEntryPoint implements AuthenticationEntryPointInterface { private $loginPath; private $useForward; private $httpKernel; private $httpUtils; /** * @param string $loginPath The path to the login form * @param bool $useForward Whether to forward or redirect to the login form */ public function __construct(HttpKernelInterface $kernel, HttpUtils $httpUtils, string $loginPath, bool $useForward = false) { $this->httpKernel = $kernel; $this->httpUtils = $httpUtils; $this->loginPath = $loginPath; $this->useForward = $useForward; } /** * {@inheritdoc} */ public function start(Request $request, AuthenticationException $authException = null) { if ($this->useForward) { $subRequest = $this->httpUtils->createRequest($request, $this->loginPath); $response = $this->httpKernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST); if (200 === $response->getStatusCode()) { $response->setStatusCode(401); } return $response; } return $this->httpUtils->createRedirectResponse($request, $this->loginPath); } } EntryPoint/RetryAuthenticationEntryPoint.php 0000644 00000003737 15120140656 0015443 0 ustar 00 <?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\EntryPoint; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\Firewall\ChannelListener; trigger_deprecation('symfony/security-http', '5.4', 'The "%s" class is deprecated, use "%s" directly (and optionally configure the HTTP(s) ports there).', RetryAuthenticationEntryPoint::class, ChannelListener::class); /** * RetryAuthenticationEntryPoint redirects URL based on the configured scheme. * * This entry point is not intended to work with HTTP post requests. * * @author Fabien Potencier <fabien@symfony.com> * * @deprecated since Symfony 5.4 */ class RetryAuthenticationEntryPoint implements AuthenticationEntryPointInterface { private $httpPort; private $httpsPort; public function __construct(int $httpPort = 80, int $httpsPort = 443) { $this->httpPort = $httpPort; $this->httpsPort = $httpsPort; } /** * {@inheritdoc} */ public function start(Request $request, AuthenticationException $authException = null) { $scheme = $request->isSecure() ? 'http' : 'https'; if ('http' === $scheme && 80 != $this->httpPort) { $port = ':'.$this->httpPort; } elseif ('https' === $scheme && 443 != $this->httpsPort) { $port = ':'.$this->httpsPort; } else { $port = ''; } $qs = $request->getQueryString(); if (null !== $qs) { $qs = '?'.$qs; } $url = $scheme.'://'.$request->getHost().$port.$request->getBaseUrl().$request->getPathInfo().$qs; return new RedirectResponse($url, 301); } } Event/AuthenticationTokenCreatedEvent.php 0000644 00000003251 15120140656 0014611 0 ustar 00 <?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\Event; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Passport; use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface; use Symfony\Contracts\EventDispatcher\Event; /** * When a newly authenticated security token was created, before it becomes effective in the security system. * * @author Christian Scheb <me@christianscheb.de> */ class AuthenticationTokenCreatedEvent extends Event { private $authenticatedToken; private $passport; /** * @param Passport $passport */ public function __construct(TokenInterface $token, PassportInterface $passport) { if (!$passport instanceof Passport) { trigger_deprecation('symfony/security-http', '5.4', 'Not passing an instance of "%s" as "$passport" argument of "%s()" is deprecated, "%s" given.', Passport::class, __METHOD__, get_debug_type($passport)); } $this->authenticatedToken = $token; $this->passport = $passport; } public function getAuthenticatedToken(): TokenInterface { return $this->authenticatedToken; } public function setAuthenticatedToken(TokenInterface $authenticatedToken): void { $this->authenticatedToken = $authenticatedToken; } public function getPassport(): PassportInterface { return $this->passport; } } Event/CheckPassportEvent.php 0000644 00000003165 15120140656 0012116 0 ustar 00 <?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\Event; use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Passport; use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface; use Symfony\Contracts\EventDispatcher\Event; /** * This event is dispatched when the credentials have to be checked. * * Listeners to this event must validate the user and the * credentials (e.g. default listeners do password verification and * user checking) * * @author Wouter de Jong <wouter@wouterj.nl> */ class CheckPassportEvent extends Event { private $authenticator; private $passport; /** * @param Passport $passport */ public function __construct(AuthenticatorInterface $authenticator, PassportInterface $passport) { if (!$passport instanceof Passport) { trigger_deprecation('symfony/security-http', '5.4', 'Not passing an instance of "%s" as "$passport" argument of "%s()" is deprecated, "%s" given.', Passport::class, __METHOD__, get_debug_type($passport)); } $this->authenticator = $authenticator; $this->passport = $passport; } public function getAuthenticator(): AuthenticatorInterface { return $this->authenticator; } public function getPassport(): PassportInterface { return $this->passport; } } Event/DeauthenticatedEvent.php 0000644 00000003441 15120140656 0012435 0 ustar 00 <?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\Event; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Contracts\EventDispatcher\Event; /** * Deauthentication happens in case the user has changed when trying to * refresh the token. * * Use {@see TokenDeauthenticatedEvent} if you want to cover all cases where * a session is deauthenticated. * * @author Hamza Amrouche <hamza.simperfit@gmail.com> * * @deprecated since Symfony 5.4, use TokenDeauthenticatedEvent instead */ final class DeauthenticatedEvent extends Event { private $originalToken; private $refreshedToken; public function __construct(TokenInterface $originalToken, TokenInterface $refreshedToken, bool $triggerDeprecation = true) { if ($triggerDeprecation) { @trigger_deprecation('symfony/security-http', '5.4', 'Class "%s" is deprecated, use "%s" instead.', __CLASS__, TokenDeauthenticatedEvent::class); } $this->originalToken = $originalToken; $this->refreshedToken = $refreshedToken; } public function getRefreshedToken(): TokenInterface { @trigger_deprecation('symfony/security-http', '5.4', 'Class "%s" is deprecated, use "%s" instead.', __CLASS__, TokenDeauthenticatedEvent::class); return $this->refreshedToken; } public function getOriginalToken(): TokenInterface { @trigger_deprecation('symfony/security-http', '5.4', 'Class "%s" is deprecated, use "%s" instead.', __CLASS__, TokenDeauthenticatedEvent::class); return $this->originalToken; } } Event/InteractiveLoginEvent.php 0000644 00000001767 15120140656 0012621 0 ustar 00 <?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\Event; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Contracts\EventDispatcher\Event; /** * @author Fabien Potencier <fabien@symfony.com> */ final class InteractiveLoginEvent extends Event { private $request; private $authenticationToken; public function __construct(Request $request, TokenInterface $authenticationToken) { $this->request = $request; $this->authenticationToken = $authenticationToken; } public function getRequest(): Request { return $this->request; } public function getAuthenticationToken(): TokenInterface { return $this->authenticationToken; } } Event/LazyResponseEvent.php 0000644 00000003550 15120140656 0012001 0 ustar 00 <?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\Event; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\Security\Core\Exception\LazyResponseException; /** * Wraps a lazily computed response in a signaling exception. * * @author Nicolas Grekas <p@tchwork.com> */ final class LazyResponseEvent extends RequestEvent { private $event; public function __construct(parent $event) { $this->event = $event; } /** * {@inheritdoc} */ public function setResponse(Response $response) { $this->stopPropagation(); $this->event->stopPropagation(); throw new LazyResponseException($response); } /** * {@inheritdoc} */ public function getKernel(): HttpKernelInterface { return $this->event->getKernel(); } /** * {@inheritdoc} */ public function getRequest(): Request { return $this->event->getRequest(); } /** * {@inheritdoc} */ public function getRequestType(): int { return $this->event->getRequestType(); } /** * {@inheritdoc} */ public function isMainRequest(): bool { return $this->event->isMainRequest(); } /** * {@inheritdoc} */ public function isMasterRequest(): bool { trigger_deprecation('symfony/security-http', '5.3', '"%s()" is deprecated, use "isMainRequest()" instead.', __METHOD__); return $this->event->isMainRequest(); } } Event/LoginFailureEvent.php 0000644 00000005144 15120140656 0011724 0 ustar 00 <?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\Event; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Passport; use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface; use Symfony\Contracts\EventDispatcher\Event; /** * This event is dispatched after an error during authentication. * * Listeners to this event can change state based on authentication * failure (e.g. to implement login throttling). * * @author Wouter de Jong <wouter@wouterj.nl> */ class LoginFailureEvent extends Event { private $exception; private $authenticator; private $request; private $response; private $firewallName; private $passport; /** * @param Passport|null $passport */ public function __construct(AuthenticationException $exception, AuthenticatorInterface $authenticator, Request $request, ?Response $response, string $firewallName, PassportInterface $passport = null) { if (null !== $passport && !$passport instanceof Passport) { trigger_deprecation('symfony/security-http', '5.4', 'Not passing an instance of "%s" or "null" as "$passport" argument of "%s()" is deprecated, "%s" given.', Passport::class, __METHOD__, get_debug_type($passport)); } $this->exception = $exception; $this->authenticator = $authenticator; $this->request = $request; $this->response = $response; $this->firewallName = $firewallName; $this->passport = $passport; } public function getException(): AuthenticationException { return $this->exception; } public function getAuthenticator(): AuthenticatorInterface { return $this->authenticator; } public function getFirewallName(): string { return $this->firewallName; } public function getRequest(): Request { return $this->request; } public function setResponse(?Response $response) { $this->response = $response; } public function getResponse(): ?Response { return $this->response; } public function getPassport(): ?PassportInterface { return $this->passport; } } Event/LoginSuccessEvent.php 0000644 00000007035 15120140656 0011746 0 ustar 00 <?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\Event; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\LogicException; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Passport; use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface; use Symfony\Component\Security\Http\Authenticator\Passport\UserPassportInterface; use Symfony\Contracts\EventDispatcher\Event; /** * This event is dispatched after authentication has successfully completed. * * At this stage, the authenticator created a token and * generated an authentication success response. Listeners to * this event can do actions related to successful authentication * (such as migrating the password). * * @author Wouter de Jong <wouter@wouterj.nl> */ class LoginSuccessEvent extends Event { private $authenticator; private $passport; private $authenticatedToken; private $previousToken; private $request; private $response; private $firewallName; /** * @param Passport $passport */ public function __construct(AuthenticatorInterface $authenticator, PassportInterface $passport, TokenInterface $authenticatedToken, Request $request, ?Response $response, string $firewallName, TokenInterface $previousToken = null) { if (!$passport instanceof Passport) { trigger_deprecation('symfony/security-http', '5.4', 'Not passing an instance of "%s" as "$passport" argument of "%s()" is deprecated, "%s" given.', Passport::class, __METHOD__, get_debug_type($passport)); } $this->authenticator = $authenticator; $this->passport = $passport; $this->authenticatedToken = $authenticatedToken; $this->previousToken = $previousToken; $this->request = $request; $this->response = $response; $this->firewallName = $firewallName; } public function getAuthenticator(): AuthenticatorInterface { return $this->authenticator; } public function getPassport(): PassportInterface { return $this->passport; } public function getUser(): UserInterface { // @deprecated since Symfony 5.4, passport will always have a user in 6.0 if (!$this->passport instanceof UserPassportInterface) { throw new LogicException(sprintf('Cannot call "%s" as the authenticator ("%s") did not set a user.', __METHOD__, \get_class($this->authenticator))); } return $this->passport->getUser(); } public function getAuthenticatedToken(): TokenInterface { return $this->authenticatedToken; } public function getPreviousToken(): ?TokenInterface { return $this->previousToken; } public function getRequest(): Request { return $this->request; } public function getFirewallName(): string { return $this->firewallName; } public function setResponse(?Response $response): void { $this->response = $response; } public function getResponse(): ?Response { return $this->response; } } Event/LogoutEvent.php 0000644 00000002236 15120140656 0010614 0 ustar 00 <?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\Event; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Contracts\EventDispatcher\Event; /** * @author Wouter de Jong <wouter@wouterj.nl> */ class LogoutEvent extends Event { private $request; private $response; private $token; public function __construct(Request $request, ?TokenInterface $token) { $this->request = $request; $this->token = $token; } public function getRequest(): Request { return $this->request; } public function getToken(): ?TokenInterface { return $this->token; } public function setResponse(Response $response): void { $this->response = $response; } public function getResponse(): ?Response { return $this->response; } } Event/SwitchUserEvent.php 0000644 00000002423 15120140656 0011441 0 ustar 00 <?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\Event; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Contracts\EventDispatcher\Event; /** * SwitchUserEvent. * * @author Fabien Potencier <fabien@symfony.com> */ final class SwitchUserEvent extends Event { private $request; private $targetUser; private $token; public function __construct(Request $request, UserInterface $targetUser, TokenInterface $token = null) { $this->request = $request; $this->targetUser = $targetUser; $this->token = $token; } public function getRequest(): Request { return $this->request; } public function getTargetUser(): UserInterface { return $this->targetUser; } public function getToken(): ?TokenInterface { return $this->token; } public function setToken(TokenInterface $token) { $this->token = $token; } } Event/TokenDeauthenticatedEvent.php 0000644 00000002646 15120140656 0013444 0 ustar 00 <?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\Event; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Contracts\EventDispatcher\Event; /** * This event is dispatched when the current security token is deauthenticated * when trying to reference the token. * * This includes changes in the user ({@see DeauthenticatedEvent}), but * also cases where there is no user provider available to refresh the user. * * Use this event if you want to trigger some actions whenever a user is * deauthenticated and redirected back to the authentication entry point * (e.g. clearing all remember-me cookies). * * @author Wouter de Jong <wouter@wouterj.nl> */ final class TokenDeauthenticatedEvent extends Event { private $originalToken; private $request; public function __construct(TokenInterface $originalToken, Request $request) { $this->originalToken = $originalToken; $this->request = $request; } public function getOriginalToken(): TokenInterface { return $this->originalToken; } public function getRequest(): Request { return $this->request; } } EventListener/CheckCredentialsListener.php 0000644 00000011533 15120140656 0014750 0 ustar 00 <?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\EventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface; use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\User\LegacyPasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials; use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials; use Symfony\Component\Security\Http\Authenticator\Passport\UserPassportInterface; use Symfony\Component\Security\Http\Event\CheckPassportEvent; /** * This listeners uses the interfaces of authenticators to * determine how to check credentials. * * @author Wouter de Jong <wouter@driveamber.com> * * @final */ class CheckCredentialsListener implements EventSubscriberInterface { private $hasherFactory; /** * @param PasswordHasherFactoryInterface $hasherFactory */ public function __construct($hasherFactory) { if ($hasherFactory instanceof EncoderFactoryInterface) { trigger_deprecation('symfony/security-core', '5.3', 'Passing a "%s" instance to the "%s" constructor is deprecated, use "%s" instead.', EncoderFactoryInterface::class, __CLASS__, PasswordHasherFactoryInterface::class); } $this->hasherFactory = $hasherFactory; } public function checkPassport(CheckPassportEvent $event): void { $passport = $event->getPassport(); if ($passport instanceof UserPassportInterface && $passport->hasBadge(PasswordCredentials::class)) { // Use the password hasher to validate the credentials $user = $passport->getUser(); if (!$user instanceof PasswordAuthenticatedUserInterface) { trigger_deprecation('symfony/security-http', '5.3', 'Not implementing the "%s" interface in class "%s" while using password-based authentication is deprecated.', PasswordAuthenticatedUserInterface::class, get_debug_type($user)); } /** @var PasswordCredentials $badge */ $badge = $passport->getBadge(PasswordCredentials::class); if ($badge->isResolved()) { return; } $presentedPassword = $badge->getPassword(); if ('' === $presentedPassword) { throw new BadCredentialsException('The presented password cannot be empty.'); } if (null === $user->getPassword()) { throw new BadCredentialsException('The presented password is invalid.'); } $salt = method_exists($user, 'getSalt') ? $user->getSalt() : ''; if ($salt && !$user instanceof LegacyPasswordAuthenticatedUserInterface) { trigger_deprecation('symfony/security-http', '5.3', 'Returning a string from "getSalt()" without implementing the "%s" interface is deprecated, the "%s" class should implement it.', LegacyPasswordAuthenticatedUserInterface::class, get_debug_type($user)); } // @deprecated since Symfony 5.3 if ($this->hasherFactory instanceof EncoderFactoryInterface) { if (!$this->hasherFactory->getEncoder($user)->isPasswordValid($user->getPassword(), $presentedPassword, $salt)) { throw new BadCredentialsException('The presented password is invalid.'); } } else { if (!$this->hasherFactory->getPasswordHasher($user)->verify($user->getPassword(), $presentedPassword, $salt)) { throw new BadCredentialsException('The presented password is invalid.'); } } $badge->markResolved(); if (!$passport->hasBadge(PasswordUpgradeBadge::class)) { $passport->addBadge(new PasswordUpgradeBadge($presentedPassword)); } return; } if ($passport->hasBadge(CustomCredentials::class)) { /** @var CustomCredentials $badge */ $badge = $passport->getBadge(CustomCredentials::class); if ($badge->isResolved()) { return; } $badge->executeCustomChecker($passport->getUser()); return; } } public static function getSubscribedEvents(): array { return [CheckPassportEvent::class => 'checkPassport']; } } EventListener/CheckRememberMeConditionsListener.php 0000644 00000005206 15120140656 0016565 0 ustar 00 <?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\EventListener; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge; use Symfony\Component\Security\Http\Event\LoginSuccessEvent; use Symfony\Component\Security\Http\ParameterBagUtils; /** * Checks if all conditions are met for remember me. * * The conditions that must be met for this listener to enable remember me: * A) This badge is present in the Passport * B) The remember_me key under your firewall is configured * C) The "remember me" functionality is activated. This is usually * done by having a _remember_me checkbox in your form, but * can be configured by the "always_remember_me" and "remember_me_parameter" * parameters under the "remember_me" firewall key (or "always_remember_me" * is enabled) * * @author Wouter de Jong <wouter@wouterj.nl> * * @final */ class CheckRememberMeConditionsListener implements EventSubscriberInterface { private $options; private $logger; public function __construct(array $options = [], LoggerInterface $logger = null) { $this->options = $options + ['always_remember_me' => false, 'remember_me_parameter' => '_remember_me']; $this->logger = $logger; } public function onSuccessfulLogin(LoginSuccessEvent $event): void { $passport = $event->getPassport(); if (!$passport->hasBadge(RememberMeBadge::class)) { return; } /** @var RememberMeBadge $badge */ $badge = $passport->getBadge(RememberMeBadge::class); if (!$this->options['always_remember_me']) { $parameter = ParameterBagUtils::getRequestParameterValue($event->getRequest(), $this->options['remember_me_parameter']); if (!('true' === $parameter || 'on' === $parameter || '1' === $parameter || 'yes' === $parameter || true === $parameter)) { if (null !== $this->logger) { $this->logger->debug('Remember me disabled; request does not contain remember me parameter ("{parameter}").', ['parameter' => $this->options['remember_me_parameter']]); } return; } } $badge->enable(); } public static function getSubscribedEvents(): array { return [LoginSuccessEvent::class => ['onSuccessfulLogin', -32]]; } } EventListener/CookieClearingLogoutListener.php 0000644 00000002617 15120140656 0015630 0 ustar 00 <?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\EventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Http\Event\LogoutEvent; /** * This listener clears the passed cookies when a user logs out. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> * * @final */ class CookieClearingLogoutListener implements EventSubscriberInterface { private $cookies; /** * @param array $cookies An array of cookies (keys are names, values contain path and domain) to unset */ public function __construct(array $cookies) { $this->cookies = $cookies; } public function onLogout(LogoutEvent $event): void { if (!$response = $event->getResponse()) { return; } foreach ($this->cookies as $cookieName => $cookieData) { $response->headers->clearCookie($cookieName, $cookieData['path'], $cookieData['domain'], $cookieData['secure'] ?? false, true, $cookieData['samesite'] ?? null); } } public static function getSubscribedEvents(): array { return [ LogoutEvent::class => ['onLogout', -255], ]; } } EventListener/CsrfProtectionListener.php 0000644 00000003404 15120140656 0014517 0 ustar 00 <?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\EventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException; use Symfony\Component\Security\Csrf\CsrfToken; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge; use Symfony\Component\Security\Http\Event\CheckPassportEvent; /** * @author Wouter de Jong <wouter@wouterj.nl> * * @final */ class CsrfProtectionListener implements EventSubscriberInterface { private $csrfTokenManager; public function __construct(CsrfTokenManagerInterface $csrfTokenManager) { $this->csrfTokenManager = $csrfTokenManager; } public function checkPassport(CheckPassportEvent $event): void { $passport = $event->getPassport(); if (!$passport->hasBadge(CsrfTokenBadge::class)) { return; } /** @var CsrfTokenBadge $badge */ $badge = $passport->getBadge(CsrfTokenBadge::class); if ($badge->isResolved()) { return; } $csrfToken = new CsrfToken($badge->getCsrfTokenId(), $badge->getCsrfToken()); if (false === $this->csrfTokenManager->isTokenValid($csrfToken)) { throw new InvalidCsrfTokenException('Invalid CSRF token.'); } $badge->markResolved(); } public static function getSubscribedEvents(): array { return [CheckPassportEvent::class => ['checkPassport', 512]]; } } EventListener/CsrfTokenClearingLogoutListener.php 0000644 00000002107 15120140656 0016307 0 ustar 00 <?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\EventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Csrf\TokenStorage\ClearableTokenStorageInterface; use Symfony\Component\Security\Http\Event\LogoutEvent; /** * @author Christian Flothmann <christian.flothmann@sensiolabs.de> * * @final */ class CsrfTokenClearingLogoutListener implements EventSubscriberInterface { private $csrfTokenStorage; public function __construct(ClearableTokenStorageInterface $csrfTokenStorage) { $this->csrfTokenStorage = $csrfTokenStorage; } public function onLogout(LogoutEvent $event): void { $this->csrfTokenStorage->clear(); } public static function getSubscribedEvents(): array { return [ LogoutEvent::class => 'onLogout', ]; } } EventListener/DefaultLogoutListener.php 0000644 00000002473 15120140656 0014336 0 ustar 00 <?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\EventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Http\Event\LogoutEvent; use Symfony\Component\Security\Http\HttpUtils; /** * Default logout listener will redirect users to a configured path. * * @author Fabien Potencier <fabien@symfony.com> * @author Alexander <iam.asm89@gmail.com> * * @final */ class DefaultLogoutListener implements EventSubscriberInterface { private $httpUtils; private $targetUrl; public function __construct(HttpUtils $httpUtils, string $targetUrl = '/') { $this->httpUtils = $httpUtils; $this->targetUrl = $targetUrl; } public function onLogout(LogoutEvent $event): void { if (null !== $event->getResponse()) { return; } $event->setResponse($this->httpUtils->createRedirectResponse($event->getRequest(), $this->targetUrl)); } public static function getSubscribedEvents(): array { return [ LogoutEvent::class => ['onLogout', 64], ]; } } EventListener/LoginThrottlingListener.php 0000644 00000004174 15120140656 0014707 0 ustar 00 <?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\EventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\RateLimiter\RequestRateLimiterInterface; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Security\Core\Exception\TooManyLoginAttemptsAuthenticationException; use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Event\CheckPassportEvent; use Symfony\Component\Security\Http\Event\LoginSuccessEvent; /** * @author Wouter de Jong <wouter@wouterj.nl> */ final class LoginThrottlingListener implements EventSubscriberInterface { private $requestStack; private $limiter; public function __construct(RequestStack $requestStack, RequestRateLimiterInterface $limiter) { $this->requestStack = $requestStack; $this->limiter = $limiter; } public function checkPassport(CheckPassportEvent $event): void { $passport = $event->getPassport(); if (!$passport->hasBadge(UserBadge::class)) { return; } $request = $this->requestStack->getMainRequest(); $request->attributes->set(Security::LAST_USERNAME, $passport->getBadge(UserBadge::class)->getUserIdentifier()); $limit = $this->limiter->consume($request); if (!$limit->isAccepted()) { throw new TooManyLoginAttemptsAuthenticationException(ceil(($limit->getRetryAfter()->getTimestamp() - time()) / 60)); } } public function onSuccessfulLogin(LoginSuccessEvent $event): void { $this->limiter->reset($event->getRequest()); } public static function getSubscribedEvents(): array { return [ CheckPassportEvent::class => ['checkPassport', 2080], LoginSuccessEvent::class => 'onSuccessfulLogin', ]; } } EventListener/PasswordMigratingListener.php 0000644 00000007076 15120140656 0015230 0 ustar 00 <?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\EventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface; use Symfony\Component\PasswordHasher\PasswordHasherInterface; use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; 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\UserPassportInterface; use Symfony\Component\Security\Http\Event\LoginSuccessEvent; /** * @author Wouter de Jong <wouter@wouterj.nl> * * @final */ class PasswordMigratingListener implements EventSubscriberInterface { private $hasherFactory; /** * @param PasswordHasherFactoryInterface $hasherFactory */ public function __construct($hasherFactory) { if ($hasherFactory instanceof EncoderFactoryInterface) { trigger_deprecation('symfony/security-core', '5.3', 'Passing a "%s" instance to the "%s" constructor is deprecated, use "%s" instead.', EncoderFactoryInterface::class, __CLASS__, PasswordHasherFactoryInterface::class); } $this->hasherFactory = $hasherFactory; } public function onLoginSuccess(LoginSuccessEvent $event): void { $passport = $event->getPassport(); if (!$passport instanceof UserPassportInterface || !$passport->hasBadge(PasswordUpgradeBadge::class)) { return; } /** @var PasswordUpgradeBadge $badge */ $badge = $passport->getBadge(PasswordUpgradeBadge::class); $plaintextPassword = $badge->getAndErasePlaintextPassword(); if ('' === $plaintextPassword) { return; } $user = $passport->getUser(); if (null === $user->getPassword()) { return; } $passwordHasher = $this->hasherFactory instanceof EncoderFactoryInterface ? $this->hasherFactory->getEncoder($user) : $this->hasherFactory->getPasswordHasher($user); if (!$passwordHasher->needsRehash($user->getPassword())) { return; } $passwordUpgrader = $badge->getPasswordUpgrader(); if (null === $passwordUpgrader) { if (!$passport->hasBadge(UserBadge::class)) { return; } /** @var UserBadge $userBadge */ $userBadge = $passport->getBadge(UserBadge::class); $userLoader = $userBadge->getUserLoader(); if (\is_array($userLoader) && $userLoader[0] instanceof PasswordUpgraderInterface) { $passwordUpgrader = $userLoader[0]; } elseif (!$userLoader instanceof \Closure || !($passwordUpgrader = (new \ReflectionFunction($userLoader))->getClosureThis()) instanceof PasswordUpgraderInterface ) { return; } } $passwordUpgrader->upgradePassword($user, $passwordHasher instanceof PasswordHasherInterface ? $passwordHasher->hash($plaintextPassword, $user->getSalt()) : $passwordHasher->encodePassword($plaintextPassword, $user->getSalt())); } public static function getSubscribedEvents(): array { return [LoginSuccessEvent::class => 'onLoginSuccess']; } } EventListener/RememberMeListener.php 0000644 00000005705 15120140656 0013601 0 ustar 00 <?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\EventListener; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge; use Symfony\Component\Security\Http\Event\LoginFailureEvent; use Symfony\Component\Security\Http\Event\LoginSuccessEvent; use Symfony\Component\Security\Http\Event\LogoutEvent; use Symfony\Component\Security\Http\Event\TokenDeauthenticatedEvent; use Symfony\Component\Security\Http\RememberMe\RememberMeHandlerInterface; /** * The RememberMe *listener* creates and deletes remember-me cookies. * * Upon login success or failure and support for remember me * in the firewall and authenticator, this listener will create * a remember-me cookie. * Upon login failure, all remember-me cookies are removed. * * @author Wouter de Jong <wouter@wouterj.nl> * * @final */ class RememberMeListener implements EventSubscriberInterface { private $rememberMeHandler; private $logger; public function __construct(RememberMeHandlerInterface $rememberMeHandler, LoggerInterface $logger = null) { $this->rememberMeHandler = $rememberMeHandler; $this->logger = $logger; } public function onSuccessfulLogin(LoginSuccessEvent $event): void { $passport = $event->getPassport(); if (!$passport->hasBadge(RememberMeBadge::class)) { if (null !== $this->logger) { $this->logger->debug('Remember me skipped: your authenticator does not support it.', ['authenticator' => \get_class($event->getAuthenticator())]); } return; } // Make sure any old remember-me cookies are cancelled $this->rememberMeHandler->clearRememberMeCookie(); /** @var RememberMeBadge $badge */ $badge = $passport->getBadge(RememberMeBadge::class); if (!$badge->isEnabled()) { if (null !== $this->logger) { $this->logger->debug('Remember me skipped: the RememberMeBadge is not enabled.'); } return; } if (null !== $this->logger) { $this->logger->debug('Remember-me was requested; setting cookie.'); } $this->rememberMeHandler->createRememberMeCookie($event->getUser()); } public function clearCookie(): void { $this->rememberMeHandler->clearRememberMeCookie(); } public static function getSubscribedEvents(): array { return [ LoginSuccessEvent::class => ['onSuccessfulLogin', -64], LoginFailureEvent::class => 'clearCookie', LogoutEvent::class => 'clearCookie', TokenDeauthenticatedEvent::class => 'clearCookie', ]; } } EventListener/RememberMeLogoutListener.php 0000644 00000004140 15120140656 0014763 0 ustar 00 <?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\EventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Core\Exception\LogicException; use Symfony\Component\Security\Http\Event\LogoutEvent; use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; trigger_deprecation('symfony/security-http', '5.4', 'The "%s" class is deprecated.', RememberMeLogoutListener::class); /** * @author Wouter de Jong <wouter@wouterj.nl> * * @final * * @deprecated since Symfony 5.4 */ class RememberMeLogoutListener implements EventSubscriberInterface { private $rememberMeServices; public function __construct(RememberMeServicesInterface $rememberMeServices) { if (!method_exists($rememberMeServices, 'logout')) { trigger_deprecation('symfony/security-core', '5.1', '"%s" should implement the "logout(Request $request, Response $response, TokenInterface $token)" method, this method will be added to the "%s" in version 6.0.', \get_class($rememberMeServices), RememberMeServicesInterface::class); } $this->rememberMeServices = $rememberMeServices; } public function onLogout(LogoutEvent $event): void { if (!method_exists($this->rememberMeServices, 'logout')) { return; } if (!$event->getToken()) { return; } if (null === $event->getResponse()) { throw new LogicException(sprintf('No response was set for this logout action. Make sure the DefaultLogoutListener or another listener has set the response before "%s" is called.', __CLASS__)); } $this->rememberMeServices->logout($event->getRequest(), $event->getResponse(), $event->getToken()); } public static function getSubscribedEvents(): array { return [ LogoutEvent::class => 'onLogout', ]; } } EventListener/SessionLogoutListener.php 0000644 00000001670 15120140656 0014373 0 ustar 00 <?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\EventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Http\Event\LogoutEvent; /** * Handler for clearing invalidating the current session. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> * * @final */ class SessionLogoutListener implements EventSubscriberInterface { public function onLogout(LogoutEvent $event): void { if ($event->getRequest()->hasSession()) { $event->getRequest()->getSession()->invalidate(); } } public static function getSubscribedEvents(): array { return [ LogoutEvent::class => 'onLogout', ]; } } EventListener/SessionStrategyListener.php 0000644 00000004153 15120140656 0014723 0 ustar 00 <?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\EventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Http\Event\LoginSuccessEvent; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; /** * Migrates/invalidates the session after successful login. * * This should be registered as subscriber to any "stateful" firewalls. * * @see SessionAuthenticationStrategy * * @author Wouter de Jong <wouter@wouterj.nl> */ class SessionStrategyListener implements EventSubscriberInterface { private $sessionAuthenticationStrategy; public function __construct(SessionAuthenticationStrategyInterface $sessionAuthenticationStrategy) { $this->sessionAuthenticationStrategy = $sessionAuthenticationStrategy; } public function onSuccessfulLogin(LoginSuccessEvent $event): void { $request = $event->getRequest(); $token = $event->getAuthenticatedToken(); if (!$request->hasSession() || !$request->hasPreviousSession()) { return; } if ($previousToken = $event->getPreviousToken()) { // @deprecated since Symfony 5.3, change to $token->getUserIdentifier() in 6.0 $user = method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername(); $previousUser = method_exists($previousToken, 'getUserIdentifier') ? $previousToken->getUserIdentifier() : $previousToken->getUsername(); if ('' !== ($user ?? '') && $user === $previousUser) { return; } } $this->sessionAuthenticationStrategy->onAuthentication($request, $token); } public static function getSubscribedEvents(): array { return [LoginSuccessEvent::class => 'onSuccessfulLogin']; } } EventListener/UserCheckerListener.php 0000644 00000003624 15120140656 0013762 0 ustar 00 <?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\EventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PreAuthenticatedUserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\UserPassportInterface; use Symfony\Component\Security\Http\Event\CheckPassportEvent; /** * @author Wouter de Jong <wouter@wouterj.nl> * * @final */ class UserCheckerListener implements EventSubscriberInterface { private $userChecker; public function __construct(UserCheckerInterface $userChecker) { $this->userChecker = $userChecker; } public function preCheckCredentials(CheckPassportEvent $event): void { $passport = $event->getPassport(); if (!$passport instanceof UserPassportInterface || $passport->hasBadge(PreAuthenticatedUserBadge::class)) { return; } $this->userChecker->checkPreAuth($passport->getUser()); } public function postCheckCredentials(AuthenticationSuccessEvent $event): void { $user = $event->getAuthenticationToken()->getUser(); if (!$user instanceof UserInterface) { return; } $this->userChecker->checkPostAuth($user); } public static function getSubscribedEvents(): array { return [ CheckPassportEvent::class => ['preCheckCredentials', 256], AuthenticationSuccessEvent::class => ['postCheckCredentials', 256], ]; } } EventListener/UserProviderListener.php 0000644 00000003470 15120140656 0014207 0 ustar 00 <?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\EventListener; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Event\CheckPassportEvent; /** * Configures the user provider as user loader, if no user load * has been explicitly set. * * @author Wouter de Jong <wouter@wouterj.nl> * * @final */ class UserProviderListener { private $userProvider; public function __construct(UserProviderInterface $userProvider) { $this->userProvider = $userProvider; } public function checkPassport(CheckPassportEvent $event): void { $passport = $event->getPassport(); if (!$passport->hasBadge(UserBadge::class)) { return; } /** @var UserBadge $badge */ $badge = $passport->getBadge(UserBadge::class); if (null !== $badge->getUserLoader()) { return; } // @deprecated since Symfony 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0 if (method_exists($this->userProvider, 'loadUserByIdentifier')) { $badge->setUserLoader([$this->userProvider, 'loadUserByIdentifier']); } else { trigger_deprecation('symfony/security-http', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->userProvider)); $badge->setUserLoader([$this->userProvider, 'loadUserByUsername']); } } } Firewall/AbstractAuthenticationListener.php 0000644 00000022727 15120140656 0015205 0 ustar 00 <?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\Firewall; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; 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\Exception\SessionUnavailableException; use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; use Symfony\Component\Security\Http\SecurityEvents; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; trigger_deprecation('symfony/security-http', '5.3', 'The "%s" class is deprecated, use the new authenticator system instead.', AbstractAuthenticationListener::class); /** * The AbstractAuthenticationListener is the preferred base class for all * browser-/HTTP-based authentication requests. * * Subclasses likely have to implement the following: * - an TokenInterface to hold authentication related data * - an AuthenticationProvider to perform the actual authentication of the * token, retrieve the UserInterface implementation from a database, and * perform the specific account checks using the UserChecker * * By default, this listener only is active for a specific path, e.g. * /login_check. If you want to change this behavior, you can overwrite the * requiresAuthentication() method. * * @author Fabien Potencier <fabien@symfony.com> * @author Johannes M. Schmitt <schmittjoh@gmail.com> * * @deprecated since Symfony 5.3, use the new authenticator system instead */ abstract class AbstractAuthenticationListener extends AbstractListener { protected $options; protected $logger; protected $authenticationManager; protected $providerKey; protected $httpUtils; private $tokenStorage; private $sessionStrategy; private $dispatcher; private $successHandler; private $failureHandler; private $rememberMeServices; /** * @throws \InvalidArgumentException */ public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, string $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = [], LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) { if (empty($providerKey)) { throw new \InvalidArgumentException('$providerKey must not be empty.'); } $this->tokenStorage = $tokenStorage; $this->authenticationManager = $authenticationManager; $this->sessionStrategy = $sessionStrategy; $this->providerKey = $providerKey; $this->successHandler = $successHandler; $this->failureHandler = $failureHandler; $this->options = array_merge([ 'check_path' => '/login_check', 'login_path' => '/login', 'always_use_default_target_path' => false, 'default_target_path' => '/', 'target_path_parameter' => '_target_path', 'use_referer' => false, 'failure_path' => null, 'failure_forward' => false, 'require_previous_session' => true, ], $options); $this->logger = $logger; $this->dispatcher = $dispatcher; $this->httpUtils = $httpUtils; } /** * Sets the RememberMeServices implementation to use. */ public function setRememberMeServices(RememberMeServicesInterface $rememberMeServices) { $this->rememberMeServices = $rememberMeServices; } /** * {@inheritdoc} */ public function supports(Request $request): ?bool { return $this->requiresAuthentication($request); } /** * Handles form based authentication. * * @throws \RuntimeException * @throws SessionUnavailableException */ public function authenticate(RequestEvent $event) { $request = $event->getRequest(); if (!$request->hasSession()) { throw new \RuntimeException('This authentication method requires a session.'); } try { if ($this->options['require_previous_session'] && !$request->hasPreviousSession()) { throw new SessionUnavailableException('Your session has timed out, or you have disabled cookies.'); } $previousToken = $this->tokenStorage->getToken(); if (null === $returnValue = $this->attemptAuthentication($request)) { return; } if ($returnValue instanceof TokenInterface) { $this->migrateSession($request, $returnValue, $previousToken); $response = $this->onSuccess($request, $returnValue); } elseif ($returnValue instanceof Response) { $response = $returnValue; } else { throw new \RuntimeException('attemptAuthentication() must either return a Response, an implementation of TokenInterface, or null.'); } } catch (AuthenticationException $e) { $response = $this->onFailure($request, $e); } $event->setResponse($response); } /** * Whether this request requires authentication. * * The default implementation only processes requests to a specific path, * but a subclass could change this to only authenticate requests where a * certain parameters is present. * * @return bool */ protected function requiresAuthentication(Request $request) { return $this->httpUtils->checkRequestPath($request, $this->options['check_path']); } /** * Performs authentication. * * @return TokenInterface|Response|null The authenticated token, null if full authentication is not possible, or a Response * * @throws AuthenticationException if the authentication fails */ abstract protected function attemptAuthentication(Request $request); private function onFailure(Request $request, AuthenticationException $failed): Response { if (null !== $this->logger) { $this->logger->info('Authentication request failed.', ['exception' => $failed]); } $token = $this->tokenStorage->getToken(); if ($token instanceof UsernamePasswordToken && $this->providerKey === $token->getFirewallName()) { $this->tokenStorage->setToken(null); } $response = $this->failureHandler->onAuthenticationFailure($request, $failed); if (!$response instanceof Response) { throw new \RuntimeException('Authentication Failure Handler did not return a Response.'); } return $response; } private function onSuccess(Request $request, TokenInterface $token): Response { if (null !== $this->logger) { // @deprecated since Symfony 5.3, change to $token->getUserIdentifier() in 6.0 $this->logger->info('User has been authenticated successfully.', ['username' => method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername()]); } $this->tokenStorage->setToken($token); $session = $request->getSession(); $session->remove(Security::AUTHENTICATION_ERROR); $session->remove(Security::LAST_USERNAME); if (null !== $this->dispatcher) { $loginEvent = new InteractiveLoginEvent($request, $token); $this->dispatcher->dispatch($loginEvent, SecurityEvents::INTERACTIVE_LOGIN); } $response = $this->successHandler->onAuthenticationSuccess($request, $token); if (!$response instanceof Response) { throw new \RuntimeException('Authentication Success Handler did not return a Response.'); } if (null !== $this->rememberMeServices) { $this->rememberMeServices->loginSuccess($request, $response, $token); } return $response; } private function migrateSession(Request $request, TokenInterface $token, ?TokenInterface $previousToken) { if ($previousToken) { $user = method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername(); $previousUser = method_exists($previousToken, 'getUserIdentifier') ? $previousToken->getUserIdentifier() : $previousToken->getUsername(); if ('' !== ($user ?? '') && $user === $previousUser) { return; } } $this->sessionStrategy->onAuthentication($request, $token); } } Firewall/AbstractListener.php 0000644 00000001505 15120140656 0012274 0 ustar 00 <?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\Firewall; use Symfony\Component\HttpKernel\Event\RequestEvent; /** * A base class for listeners that can tell whether they should authenticate incoming requests. * * @author Nicolas Grekas <p@tchwork.com> */ abstract class AbstractListener implements FirewallListenerInterface { final public function __invoke(RequestEvent $event) { if (false !== $this->supports($event->getRequest())) { $this->authenticate($event); } } public static function getPriority(): int { return 0; // Default } } Firewall/AbstractPreAuthenticatedListener.php 0000644 00000014401 15120140656 0015445 0 ustar 00 <?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\Firewall; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; 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\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\SecurityEvents; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; trigger_deprecation('symfony/security-http', '5.3', 'The "%s" class is deprecated, use the new authenticator system instead.', AbstractPreAuthenticatedListener::class); /** * AbstractPreAuthenticatedListener is the base class for all listener that * authenticates users based on a pre-authenticated request (like a certificate * for instance). * * @author Fabien Potencier <fabien@symfony.com> * * @internal * * @deprecated since Symfony 5.3, use the new authenticator system instead */ abstract class AbstractPreAuthenticatedListener extends AbstractListener { protected $logger; private $tokenStorage; private $authenticationManager; private $providerKey; private $dispatcher; private $sessionStrategy; public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, string $providerKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) { $this->tokenStorage = $tokenStorage; $this->authenticationManager = $authenticationManager; $this->providerKey = $providerKey; $this->logger = $logger; $this->dispatcher = $dispatcher; } /** * {@inheritdoc} */ public function supports(Request $request): ?bool { try { $request->attributes->set('_pre_authenticated_data', $this->getPreAuthenticatedData($request)); } catch (BadCredentialsException $e) { $this->clearToken($e); return false; } return true; } /** * Handles pre-authentication. */ public function authenticate(RequestEvent $event) { $request = $event->getRequest(); [$user, $credentials] = $request->attributes->get('_pre_authenticated_data'); $request->attributes->remove('_pre_authenticated_data'); if (null !== $this->logger) { $this->logger->debug('Checking current security token.', ['token' => (string) $this->tokenStorage->getToken()]); } if (null !== $token = $this->tokenStorage->getToken()) { // @deprecated since Symfony 5.3, change to $token->getUserIdentifier() in 6.0 if ($token instanceof PreAuthenticatedToken && $this->providerKey == $token->getFirewallName() && $token->isAuthenticated() && (method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername()) === $user) { return; } } if (null !== $this->logger) { $this->logger->debug('Trying to pre-authenticate user.', ['username' => (string) $user]); } try { $previousToken = $token; $token = $this->authenticationManager->authenticate(new PreAuthenticatedToken($user, $credentials, $this->providerKey)); if (null !== $this->logger) { $this->logger->info('Pre-authentication successful.', ['token' => (string) $token]); } $this->migrateSession($request, $token, $previousToken); $this->tokenStorage->setToken($token); if (null !== $this->dispatcher) { $loginEvent = new InteractiveLoginEvent($request, $token); $this->dispatcher->dispatch($loginEvent, SecurityEvents::INTERACTIVE_LOGIN); } } catch (AuthenticationException $e) { $this->clearToken($e); } } /** * Call this method if your authentication token is stored to a session. * * @final */ public function setSessionAuthenticationStrategy(SessionAuthenticationStrategyInterface $sessionStrategy) { $this->sessionStrategy = $sessionStrategy; } /** * Clears a PreAuthenticatedToken for this provider (if present). */ private function clearToken(AuthenticationException $exception) { $token = $this->tokenStorage->getToken(); if ($token instanceof PreAuthenticatedToken && $this->providerKey === $token->getFirewallName()) { $this->tokenStorage->setToken(null); if (null !== $this->logger) { $this->logger->info('Cleared security token due to an exception.', ['exception' => $exception]); } } } /** * Gets the user and credentials from the Request. * * @return array An array composed of the user and the credentials */ abstract protected function getPreAuthenticatedData(Request $request); private function migrateSession(Request $request, TokenInterface $token, ?TokenInterface $previousToken) { if (!$this->sessionStrategy || !$request->hasSession() || !$request->hasPreviousSession()) { return; } if ($previousToken) { $user = method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername(); $previousUser = method_exists($previousToken, 'getUserIdentifier') ? $previousToken->getUserIdentifier() : $previousToken->getUsername(); if ('' !== ($user ?? '') && $user === $previousUser) { return; } } $this->sessionStrategy->onAuthentication($request, $token); } } Firewall/AccessListener.php 0000644 00000012756 15120140656 0011744 0 ustar 00 <?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\Firewall; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\Token\NullToken; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException; use Symfony\Component\Security\Http\AccessMapInterface; use Symfony\Component\Security\Http\Authentication\NoopAuthenticationManager; use Symfony\Component\Security\Http\Event\LazyResponseEvent; /** * AccessListener enforces access control rules. * * @author Fabien Potencier <fabien@symfony.com> * * @final */ class AccessListener extends AbstractListener { private $tokenStorage; private $accessDecisionManager; private $map; private $authManager; private $exceptionOnNoToken; public function __construct(TokenStorageInterface $tokenStorage, AccessDecisionManagerInterface $accessDecisionManager, AccessMapInterface $map, /* bool */ $exceptionOnNoToken = true) { if ($exceptionOnNoToken instanceof AuthenticationManagerInterface) { trigger_deprecation('symfony/security-http', '5.4', 'The $authManager argument of "%s" is deprecated.', __METHOD__); $authManager = $exceptionOnNoToken; $exceptionOnNoToken = \func_num_args() > 4 ? func_get_arg(4) : true; } if (false !== $exceptionOnNoToken) { trigger_deprecation('symfony/security-http', '5.4', 'Not setting the $exceptionOnNoToken argument of "%s" to "false" is deprecated.', __METHOD__); } $this->tokenStorage = $tokenStorage; $this->accessDecisionManager = $accessDecisionManager; $this->map = $map; $this->authManager = $authManager ?? (class_exists(AuthenticationManagerInterface::class) ? new NoopAuthenticationManager() : null); $this->exceptionOnNoToken = $exceptionOnNoToken; } /** * {@inheritdoc} */ public function supports(Request $request): ?bool { [$attributes] = $this->map->getPatterns($request); $request->attributes->set('_access_control_attributes', $attributes); if ($attributes && ( (\defined(AuthenticatedVoter::class.'::IS_AUTHENTICATED_ANONYMOUSLY') ? [AuthenticatedVoter::IS_AUTHENTICATED_ANONYMOUSLY] !== $attributes : true) && [AuthenticatedVoter::PUBLIC_ACCESS] !== $attributes )) { return true; } return null; } /** * Handles access authorization. * * @throws AccessDeniedException * @throws AuthenticationCredentialsNotFoundException when the token storage has no authentication token and $exceptionOnNoToken is set to true */ public function authenticate(RequestEvent $event) { if (!$event instanceof LazyResponseEvent && null === ($token = $this->tokenStorage->getToken()) && $this->exceptionOnNoToken) { throw new AuthenticationCredentialsNotFoundException('A Token was not found in the TokenStorage.'); } $request = $event->getRequest(); $attributes = $request->attributes->get('_access_control_attributes'); $request->attributes->remove('_access_control_attributes'); if (!$attributes || (( (\defined(AuthenticatedVoter::class.'::IS_AUTHENTICATED_ANONYMOUSLY') ? [AuthenticatedVoter::IS_AUTHENTICATED_ANONYMOUSLY] === $attributes : false) || [AuthenticatedVoter::PUBLIC_ACCESS] === $attributes ) && $event instanceof LazyResponseEvent)) { return; } if ($event instanceof LazyResponseEvent) { $token = $this->tokenStorage->getToken(); } if (null === $token) { if ($this->exceptionOnNoToken) { throw new AuthenticationCredentialsNotFoundException('A Token was not found in the TokenStorage.'); } $token = new NullToken(); } // @deprecated since Symfony 5.4 if (method_exists($token, 'isAuthenticated') && !$token->isAuthenticated(false)) { trigger_deprecation('symfony/core', '5.4', 'Returning false from "%s::isAuthenticated()" is deprecated, return null from "getUser()" instead.', get_debug_type($token)); if ($this->authManager) { $token = $this->authManager->authenticate($token); $this->tokenStorage->setToken($token); } } if (!$this->accessDecisionManager->decide($token, $attributes, $request, true)) { throw $this->createAccessDeniedException($request, $attributes); } } private function createAccessDeniedException(Request $request, array $attributes) { $exception = new AccessDeniedException(); $exception->setAttributes($attributes); $exception->setSubject($request); return $exception; } public static function getPriority(): int { return -255; } } Firewall/AnonymousAuthenticationListener.php 0000644 00000005367 15120140656 0015433 0 ustar 00 <?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\Firewall; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; trigger_deprecation('symfony/security-http', '5.3', 'The "%s" class is deprecated, use the new authenticator system instead.', AnonymousAuthenticationListener::class); // Help opcache.preload discover always-needed symbols class_exists(AnonymousToken::class); /** * AnonymousAuthenticationListener automatically adds a Token if none is * already present. * * @author Fabien Potencier <fabien@symfony.com> * * @deprecated since Symfony 5.3, use the new authenticator system instead */ class AnonymousAuthenticationListener extends AbstractListener { private $tokenStorage; private $secret; private $authenticationManager; private $logger; public function __construct(TokenStorageInterface $tokenStorage, string $secret, LoggerInterface $logger = null, AuthenticationManagerInterface $authenticationManager = null) { $this->tokenStorage = $tokenStorage; $this->secret = $secret; $this->authenticationManager = $authenticationManager; $this->logger = $logger; } /** * {@inheritdoc} */ public function supports(Request $request): ?bool { return null; // always run authenticate() lazily with lazy firewalls } /** * Handles anonymous authentication. */ public function authenticate(RequestEvent $event) { if (null !== $this->tokenStorage->getToken()) { return; } try { $token = new AnonymousToken($this->secret, 'anon.', []); if (null !== $this->authenticationManager) { $token = $this->authenticationManager->authenticate($token); } $this->tokenStorage->setToken($token); if (null !== $this->logger) { $this->logger->info('Populated the TokenStorage with an anonymous Token.'); } } catch (AuthenticationException $failed) { if (null !== $this->logger) { $this->logger->info('Anonymous authentication failed.', ['exception' => $failed]); } } } } Firewall/AuthenticatorManagerListener.php 0000644 00000002424 15120140656 0014637 0 ustar 00 <?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\Firewall; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\Security\Http\Authentication\AuthenticatorManagerInterface; /** * Firewall authentication listener that delegates to the authenticator system. * * @author Wouter de Jong <wouter@wouterj.nl> */ class AuthenticatorManagerListener extends AbstractListener { private $authenticatorManager; public function __construct(AuthenticatorManagerInterface $authenticationManager) { $this->authenticatorManager = $authenticationManager; } public function supports(Request $request): ?bool { return $this->authenticatorManager->supports($request); } public function authenticate(RequestEvent $event): void { $request = $event->getRequest(); $response = $this->authenticatorManager->authenticateRequest($request); if (null === $response) { return; } $event->setResponse($response); } } Firewall/BasicAuthenticationListener.php 0000644 00000012323 15120140656 0014452 0 ustar 00 <?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\Firewall; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; 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\Http\EntryPoint\AuthenticationEntryPointInterface; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; trigger_deprecation('symfony/security-http', '5.3', 'The "%s" class is deprecated, use the new authenticator system instead.', AnonymousAuthenticationListener::class); /** * BasicAuthenticationListener implements Basic HTTP authentication. * * @author Fabien Potencier <fabien@symfony.com> * * @final * * @deprecated since Symfony 5.3, use the new authenticator system instead */ class BasicAuthenticationListener extends AbstractListener { private $tokenStorage; private $authenticationManager; private $providerKey; private $authenticationEntryPoint; private $logger; private $ignoreFailure; private $sessionStrategy; public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, string $providerKey, AuthenticationEntryPointInterface $authenticationEntryPoint, LoggerInterface $logger = null) { if (empty($providerKey)) { throw new \InvalidArgumentException('$providerKey must not be empty.'); } $this->tokenStorage = $tokenStorage; $this->authenticationManager = $authenticationManager; $this->providerKey = $providerKey; $this->authenticationEntryPoint = $authenticationEntryPoint; $this->logger = $logger; $this->ignoreFailure = false; } /** * {@inheritdoc} */ public function supports(Request $request): ?bool { return null !== $request->headers->get('PHP_AUTH_USER'); } /** * Handles basic authentication. */ public function authenticate(RequestEvent $event) { $request = $event->getRequest(); if (null === $username = $request->headers->get('PHP_AUTH_USER')) { return; } if (null !== $token = $this->tokenStorage->getToken()) { // @deprecated since Symfony 5.3, change to $token->getUserIdentifier() in 6.0 if ($token instanceof UsernamePasswordToken && $token->isAuthenticated(false) && (method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername()) === $username) { return; } } if (null !== $this->logger) { $this->logger->info('Basic authentication Authorization header found for user.', ['username' => $username]); } try { $previousToken = $token; $token = $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $request->headers->get('PHP_AUTH_PW'), $this->providerKey)); $this->migrateSession($request, $token, $previousToken); $this->tokenStorage->setToken($token); } catch (AuthenticationException $e) { $token = $this->tokenStorage->getToken(); if ($token instanceof UsernamePasswordToken && $this->providerKey === $token->getFirewallName()) { $this->tokenStorage->setToken(null); } if (null !== $this->logger) { $this->logger->info('Basic authentication failed for user.', ['username' => $username, 'exception' => $e]); } if ($this->ignoreFailure) { return; } $event->setResponse($this->authenticationEntryPoint->start($request, $e)); } } /** * Call this method if your authentication token is stored to a session. * * @final */ public function setSessionAuthenticationStrategy(SessionAuthenticationStrategyInterface $sessionStrategy) { $this->sessionStrategy = $sessionStrategy; } private function migrateSession(Request $request, TokenInterface $token, ?TokenInterface $previousToken) { if (!$this->sessionStrategy || !$request->hasSession() || !$request->hasPreviousSession()) { return; } if ($previousToken) { $user = method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername(); $previousUser = method_exists($previousToken, 'getUserIdentifier') ? $previousToken->getUserIdentifier() : $previousToken->getUsername(); if ('' !== ($user ?? '') && $user === $previousUser) { return; } } $this->sessionStrategy->onAuthentication($request, $token); } } Firewall/ChannelListener.php 0000644 00000010125 15120140656 0012077 0 ustar 00 <?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\Firewall; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\Security\Http\AccessMapInterface; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; /** * ChannelListener switches the HTTP protocol based on the access control * configuration. * * @author Fabien Potencier <fabien@symfony.com> * * @final */ class ChannelListener extends AbstractListener { private $map; private $authenticationEntryPoint = null; private $logger; private $httpPort; private $httpsPort; public function __construct(AccessMapInterface $map, /* LoggerInterface */ $logger = null, /* int */ $httpPort = 80, /* int */ $httpsPort = 443) { if ($logger instanceof AuthenticationEntryPointInterface) { trigger_deprecation('symfony/security-http', '5.4', 'The "$authenticationEntryPoint" argument of "%s()" is deprecated.', __METHOD__); $this->authenticationEntryPoint = $logger; $nrOfArgs = \func_num_args(); $logger = $nrOfArgs > 2 ? func_get_arg(2) : null; $httpPort = $nrOfArgs > 3 ? func_get_arg(3) : 80; $httpsPort = $nrOfArgs > 4 ? func_get_arg(4) : 443; } if (null !== $logger && !$logger instanceof LoggerInterface) { throw new \TypeError(sprintf('Argument "$logger" of "%s()" must be instance of "%s", "%s" given.', __METHOD__, LoggerInterface::class, get_debug_type($logger))); } $this->map = $map; $this->logger = $logger; $this->httpPort = $httpPort; $this->httpsPort = $httpsPort; } /** * Handles channel management. */ public function supports(Request $request): ?bool { [, $channel] = $this->map->getPatterns($request); if ('https' === $channel && !$request->isSecure()) { if (null !== $this->logger) { if ('https' === $request->headers->get('X-Forwarded-Proto')) { $this->logger->info('Redirecting to HTTPS. ("X-Forwarded-Proto" header is set to "https" - did you set "trusted_proxies" correctly?)'); } elseif (str_contains($request->headers->get('Forwarded', ''), 'proto=https')) { $this->logger->info('Redirecting to HTTPS. ("Forwarded" header is set to "proto=https" - did you set "trusted_proxies" correctly?)'); } else { $this->logger->info('Redirecting to HTTPS.'); } } return true; } if ('http' === $channel && $request->isSecure()) { if (null !== $this->logger) { $this->logger->info('Redirecting to HTTP.'); } return true; } return false; } public function authenticate(RequestEvent $event) { $request = $event->getRequest(); $event->setResponse($this->createRedirectResponse($request)); } private function createRedirectResponse(Request $request): RedirectResponse { if (null !== $this->authenticationEntryPoint) { return $this->authenticationEntryPoint->start($request); } $scheme = $request->isSecure() ? 'http' : 'https'; if ('http' === $scheme && 80 != $this->httpPort) { $port = ':'.$this->httpPort; } elseif ('https' === $scheme && 443 != $this->httpsPort) { $port = ':'.$this->httpsPort; } else { $port = ''; } $qs = $request->getQueryString(); if (null !== $qs) { $qs = '?'.$qs; } $url = $scheme.'://'.$request->getHost().$port.$request->getBaseUrl().$request->getPathInfo().$qs; return new RedirectResponse($url, 301); } } Firewall/ContextListener.php 0000644 00000037330 15120140656 0012162 0 ustar 00 <?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\Firewall; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\Event; use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\Event\ResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver; use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface; use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\EquatableInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Event\DeauthenticatedEvent; use Symfony\Component\Security\Http\Event\TokenDeauthenticatedEvent; use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * ContextListener manages the SecurityContext persistence through a session. * * @author Fabien Potencier <fabien@symfony.com> * @author Johannes M. Schmitt <schmittjoh@gmail.com> * * @final */ class ContextListener extends AbstractListener { private $tokenStorage; private $sessionKey; private $logger; private $userProviders; private $dispatcher; private $registered; private $trustResolver; private $rememberMeServices; private $sessionTrackerEnabler; /** * @param iterable<mixed, UserProviderInterface> $userProviders */ public function __construct(TokenStorageInterface $tokenStorage, iterable $userProviders, string $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, AuthenticationTrustResolverInterface $trustResolver = null, callable $sessionTrackerEnabler = null) { if (empty($contextKey)) { throw new \InvalidArgumentException('$contextKey must not be empty.'); } $this->tokenStorage = $tokenStorage; $this->userProviders = $userProviders; $this->sessionKey = '_security_'.$contextKey; $this->logger = $logger; $this->dispatcher = class_exists(Event::class) ? LegacyEventDispatcherProxy::decorate($dispatcher) : $dispatcher; $this->trustResolver = $trustResolver ?? new AuthenticationTrustResolver(AnonymousToken::class, RememberMeToken::class); $this->sessionTrackerEnabler = $sessionTrackerEnabler; } /** * {@inheritdoc} */ public function supports(Request $request): ?bool { return null; // always run authenticate() lazily with lazy firewalls } /** * Reads the Security Token from the session. */ public function authenticate(RequestEvent $event) { if (!$this->registered && null !== $this->dispatcher && $event->isMainRequest()) { $this->dispatcher->addListener(KernelEvents::RESPONSE, [$this, 'onKernelResponse']); $this->registered = true; } $request = $event->getRequest(); $session = $request->hasPreviousSession() && $request->hasSession() ? $request->getSession() : null; $request->attributes->set('_security_firewall_run', $this->sessionKey); if (null !== $session) { $usageIndexValue = $session instanceof Session ? $usageIndexReference = &$session->getUsageIndex() : 0; $usageIndexReference = \PHP_INT_MIN; $sessionId = $request->cookies->all()[$session->getName()] ?? null; $token = $session->get($this->sessionKey); // sessionId = true is used in the tests if ($this->sessionTrackerEnabler && \in_array($sessionId, [true, $session->getId()], true)) { $usageIndexReference = $usageIndexValue; } else { $usageIndexReference = $usageIndexReference - \PHP_INT_MIN + $usageIndexValue; } } if (null === $session || null === $token) { if ($this->sessionTrackerEnabler) { ($this->sessionTrackerEnabler)(); } $this->tokenStorage->setToken(null); return; } $token = $this->safelyUnserialize($token); if (null !== $this->logger) { $this->logger->debug('Read existing security token from the session.', [ 'key' => $this->sessionKey, 'token_class' => \is_object($token) ? \get_class($token) : null, ]); } if ($token instanceof TokenInterface) { $originalToken = $token; $token = $this->refreshUser($token); if (!$token) { if ($this->logger) { $this->logger->debug('Token was deauthenticated after trying to refresh it.'); } if ($this->dispatcher) { $this->dispatcher->dispatch(new TokenDeauthenticatedEvent($originalToken, $request)); } if ($this->rememberMeServices) { $this->rememberMeServices->loginFail($request); } } } elseif (null !== $token) { if (null !== $this->logger) { $this->logger->warning('Expected a security token from the session, got something else.', ['key' => $this->sessionKey, 'received' => $token]); } $token = null; } if ($this->sessionTrackerEnabler) { ($this->sessionTrackerEnabler)(); } $this->tokenStorage->setToken($token); } /** * Writes the security token into the session. */ public function onKernelResponse(ResponseEvent $event) { if (!$event->isMainRequest()) { return; } $request = $event->getRequest(); if (!$request->hasSession() || $request->attributes->get('_security_firewall_run') !== $this->sessionKey) { return; } if ($this->dispatcher) { $this->dispatcher->removeListener(KernelEvents::RESPONSE, [$this, 'onKernelResponse']); } $this->registered = false; $session = $request->getSession(); $sessionId = $session->getId(); $usageIndexValue = $session instanceof Session ? $usageIndexReference = &$session->getUsageIndex() : null; $token = $this->tokenStorage->getToken(); // @deprecated always use isAuthenticated() in 6.0 $notAuthenticated = method_exists($this->trustResolver, 'isAuthenticated') ? !$this->trustResolver->isAuthenticated($token) : (null === $token || $this->trustResolver->isAnonymous($token)); if ($notAuthenticated) { if ($request->hasPreviousSession()) { $session->remove($this->sessionKey); } } else { $session->set($this->sessionKey, serialize($token)); if (null !== $this->logger) { $this->logger->debug('Stored the security token in the session.', ['key' => $this->sessionKey]); } } if ($this->sessionTrackerEnabler && $session->getId() === $sessionId) { $usageIndexReference = $usageIndexValue; } } /** * Refreshes the user by reloading it from the user provider. * * @throws \RuntimeException */ protected function refreshUser(TokenInterface $token): ?TokenInterface { $user = $token->getUser(); if (!$user instanceof UserInterface) { return $token; } $userNotFoundByProvider = false; $userDeauthenticated = false; $userClass = \get_class($user); foreach ($this->userProviders as $provider) { if (!$provider instanceof UserProviderInterface) { throw new \InvalidArgumentException(sprintf('User provider "%s" must implement "%s".', get_debug_type($provider), UserProviderInterface::class)); } if (!$provider->supportsClass($userClass)) { continue; } try { $refreshedUser = $provider->refreshUser($user); $newToken = clone $token; $newToken->setUser($refreshedUser, false); // tokens can be deauthenticated if the user has been changed. if ($token instanceof AbstractToken && $this->hasUserChanged($user, $newToken)) { $userDeauthenticated = true; // @deprecated since Symfony 5.4 if (method_exists($newToken, 'setAuthenticated')) { $newToken->setAuthenticated(false, false); } if (null !== $this->logger) { // @deprecated since Symfony 5.3, change to $refreshedUser->getUserIdentifier() in 6.0 $this->logger->debug('Cannot refresh token because user has changed.', ['username' => method_exists($refreshedUser, 'getUserIdentifier') ? $refreshedUser->getUserIdentifier() : $refreshedUser->getUsername(), 'provider' => \get_class($provider)]); } continue; } $token->setUser($refreshedUser); if (null !== $this->logger) { // @deprecated since Symfony 5.3, change to $refreshedUser->getUserIdentifier() in 6.0 $context = ['provider' => \get_class($provider), 'username' => method_exists($refreshedUser, 'getUserIdentifier') ? $refreshedUser->getUserIdentifier() : $refreshedUser->getUsername()]; if ($token instanceof SwitchUserToken) { $originalToken = $token->getOriginalToken(); // @deprecated since Symfony 5.3, change to $originalToken->getUserIdentifier() in 6.0 $context['impersonator_username'] = method_exists($originalToken, 'getUserIdentifier') ? $originalToken->getUserIdentifier() : $originalToken->getUsername(); } $this->logger->debug('User was reloaded from a user provider.', $context); } return $token; } catch (UnsupportedUserException $e) { // let's try the next user provider } catch (UserNotFoundException $e) { if (null !== $this->logger) { $this->logger->warning('Username could not be found in the selected user provider.', ['username' => method_exists($e, 'getUserIdentifier') ? $e->getUserIdentifier() : $e->getUsername(), 'provider' => \get_class($provider)]); } $userNotFoundByProvider = true; } } if ($userDeauthenticated) { // @deprecated since Symfony 5.4 if ($this->dispatcher) { $this->dispatcher->dispatch(new DeauthenticatedEvent($token, $newToken, false), DeauthenticatedEvent::class); } return null; } if ($userNotFoundByProvider) { return null; } throw new \RuntimeException(sprintf('There is no user provider for user "%s". Shouldn\'t the "supportsClass()" method of your user provider return true for this classname?', $userClass)); } private function safelyUnserialize(string $serializedToken) { $token = null; $prevUnserializeHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback'); $prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) use (&$prevErrorHandler) { if (__FILE__ === $file) { throw new \ErrorException($msg, 0x37313BC, $type, $file, $line); } return $prevErrorHandler ? $prevErrorHandler($type, $msg, $file, $line, $context) : false; }); try { $token = unserialize($serializedToken); } catch (\ErrorException $e) { if (0x37313BC !== $e->getCode()) { throw $e; } if ($this->logger) { $this->logger->warning('Failed to unserialize the security token from the session.', ['key' => $this->sessionKey, 'received' => $serializedToken, 'exception' => $e]); } } finally { restore_error_handler(); ini_set('unserialize_callback_func', $prevUnserializeHandler); } return $token; } /** * @param string|\Stringable|UserInterface $originalUser */ private static function hasUserChanged($originalUser, TokenInterface $refreshedToken): bool { $refreshedUser = $refreshedToken->getUser(); if ($originalUser instanceof UserInterface) { if (!$refreshedUser instanceof UserInterface) { return true; } else { // noop } } elseif ($refreshedUser instanceof UserInterface) { return true; } else { return (string) $originalUser !== (string) $refreshedUser; } if ($originalUser instanceof EquatableInterface) { return !(bool) $originalUser->isEqualTo($refreshedUser); } // @deprecated since Symfony 5.3, check for PasswordAuthenticatedUserInterface on both user objects before comparing passwords if ($originalUser->getPassword() !== $refreshedUser->getPassword()) { return true; } // @deprecated since Symfony 5.3, check for LegacyPasswordAuthenticatedUserInterface on both user objects before comparing salts if ($originalUser->getSalt() !== $refreshedUser->getSalt()) { return true; } $userRoles = array_map('strval', (array) $refreshedUser->getRoles()); if ($refreshedToken instanceof SwitchUserToken) { $userRoles[] = 'ROLE_PREVIOUS_ADMIN'; } if ( \count($userRoles) !== \count($refreshedToken->getRoleNames()) || \count($userRoles) !== \count(array_intersect($userRoles, $refreshedToken->getRoleNames())) ) { return true; } // @deprecated since Symfony 5.3, drop getUsername() in 6.0 $userIdentifier = function ($refreshedUser) { return method_exists($refreshedUser, 'getUserIdentifier') ? $refreshedUser->getUserIdentifier() : $refreshedUser->getUsername(); }; if ($userIdentifier($originalUser) !== $userIdentifier($refreshedUser)) { return true; } return false; } /** * @internal */ public static function handleUnserializeCallback(string $class) { throw new \ErrorException('Class not found: '.$class, 0x37313BC); } /** * @deprecated since Symfony 5.4 */ public function setRememberMeServices(RememberMeServicesInterface $rememberMeServices) { trigger_deprecation('symfony/security-http', '5.4', 'Method "%s()" is deprecated, use the new remember me handlers instead.', __METHOD__); $this->rememberMeServices = $rememberMeServices; } } Firewall/ExceptionListener.php 0000644 00000023634 15120140656 0012476 0 ustar 00 <?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\Firewall; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\ExceptionEvent; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\Exception\AccountStatusException; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\InsufficientAuthenticationException; use Symfony\Component\Security\Core\Exception\LazyResponseException; use Symfony\Component\Security\Core\Exception\LogoutException; use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; use Symfony\Component\Security\Http\EntryPoint\Exception\NotAnEntryPointException; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Http\Util\TargetPathTrait; /** * ExceptionListener catches authentication exception and converts them to * Response instances. * * @author Fabien Potencier <fabien@symfony.com> * * @final */ class ExceptionListener { use TargetPathTrait; private $tokenStorage; private $firewallName; private $accessDeniedHandler; private $authenticationEntryPoint; private $authenticationTrustResolver; private $errorPage; private $logger; private $httpUtils; private $stateless; public function __construct(TokenStorageInterface $tokenStorage, AuthenticationTrustResolverInterface $trustResolver, HttpUtils $httpUtils, string $firewallName, AuthenticationEntryPointInterface $authenticationEntryPoint = null, string $errorPage = null, AccessDeniedHandlerInterface $accessDeniedHandler = null, LoggerInterface $logger = null, bool $stateless = false) { $this->tokenStorage = $tokenStorage; $this->accessDeniedHandler = $accessDeniedHandler; $this->httpUtils = $httpUtils; $this->firewallName = $firewallName; $this->authenticationEntryPoint = $authenticationEntryPoint; $this->authenticationTrustResolver = $trustResolver; $this->errorPage = $errorPage; $this->logger = $logger; $this->stateless = $stateless; } /** * Registers a onKernelException listener to take care of security exceptions. */ public function register(EventDispatcherInterface $dispatcher) { $dispatcher->addListener(KernelEvents::EXCEPTION, [$this, 'onKernelException'], 1); } /** * Unregisters the dispatcher. */ public function unregister(EventDispatcherInterface $dispatcher) { $dispatcher->removeListener(KernelEvents::EXCEPTION, [$this, 'onKernelException']); } /** * Handles security related exceptions. */ public function onKernelException(ExceptionEvent $event) { $exception = $event->getThrowable(); do { if ($exception instanceof AuthenticationException) { $this->handleAuthenticationException($event, $exception); return; } if ($exception instanceof AccessDeniedException) { $this->handleAccessDeniedException($event, $exception); return; } if ($exception instanceof LazyResponseException) { $event->setResponse($exception->getResponse()); return; } if ($exception instanceof LogoutException) { $this->handleLogoutException($event, $exception); return; } } while (null !== $exception = $exception->getPrevious()); } private function handleAuthenticationException(ExceptionEvent $event, AuthenticationException $exception): void { if (null !== $this->logger) { $this->logger->info('An AuthenticationException was thrown; redirecting to authentication entry point.', ['exception' => $exception]); } try { $event->setResponse($this->startAuthentication($event->getRequest(), $exception)); $event->allowCustomResponseCode(); } catch (\Exception $e) { $event->setThrowable($e); } } private function handleAccessDeniedException(ExceptionEvent $event, AccessDeniedException $exception) { $event->setThrowable(new AccessDeniedHttpException($exception->getMessage(), $exception)); $token = $this->tokenStorage->getToken(); if (!$this->authenticationTrustResolver->isFullFledged($token)) { if (null !== $this->logger) { $this->logger->debug('Access denied, the user is not fully authenticated; redirecting to authentication entry point.', ['exception' => $exception]); } try { $insufficientAuthenticationException = new InsufficientAuthenticationException('Full authentication is required to access this resource.', 0, $exception); if (null !== $token) { $insufficientAuthenticationException->setToken($token); } $event->setResponse($this->startAuthentication($event->getRequest(), $insufficientAuthenticationException)); } catch (\Exception $e) { $event->setThrowable($e); } return; } if (null !== $this->logger) { $this->logger->debug('Access denied, the user is neither anonymous, nor remember-me.', ['exception' => $exception]); } try { if (null !== $this->accessDeniedHandler) { $response = $this->accessDeniedHandler->handle($event->getRequest(), $exception); if ($response instanceof Response) { $event->setResponse($response); } } elseif (null !== $this->errorPage) { $subRequest = $this->httpUtils->createRequest($event->getRequest(), $this->errorPage); $subRequest->attributes->set(Security::ACCESS_DENIED_ERROR, $exception); $event->setResponse($event->getKernel()->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true)); $event->allowCustomResponseCode(); } } catch (\Exception $e) { if (null !== $this->logger) { $this->logger->error('An exception was thrown when handling an AccessDeniedException.', ['exception' => $e]); } $event->setThrowable(new \RuntimeException('Exception thrown when handling an exception.', 0, $e)); } } private function handleLogoutException(ExceptionEvent $event, LogoutException $exception): void { $event->setThrowable(new AccessDeniedHttpException($exception->getMessage(), $exception)); if (null !== $this->logger) { $this->logger->info('A LogoutException was thrown; wrapping with AccessDeniedHttpException', ['exception' => $exception]); } } private function startAuthentication(Request $request, AuthenticationException $authException): Response { if (null === $this->authenticationEntryPoint) { $this->throwUnauthorizedException($authException); } if (null !== $this->logger) { $this->logger->debug('Calling Authentication entry point.'); } if (!$this->stateless) { $this->setTargetPath($request); } if ($authException instanceof AccountStatusException) { // remove the security token to prevent infinite redirect loops $this->tokenStorage->setToken(null); if (null !== $this->logger) { $this->logger->info('The security token was removed due to an AccountStatusException.', ['exception' => $authException]); } } try { $response = $this->authenticationEntryPoint->start($request, $authException); } catch (NotAnEntryPointException $e) { $this->throwUnauthorizedException($authException); } if (!$response instanceof Response) { $given = get_debug_type($response); throw new \LogicException(sprintf('The "%s::start()" method must return a Response object ("%s" returned).', get_debug_type($this->authenticationEntryPoint), $given)); } return $response; } protected function setTargetPath(Request $request) { // session isn't required when using HTTP basic authentication mechanism for example if ($request->hasSession() && $request->isMethodSafe() && !$request->isXmlHttpRequest()) { $this->saveTargetPath($request->getSession(), $this->firewallName, $request->getUri()); } } private function throwUnauthorizedException(AuthenticationException $authException) { if (null !== $this->logger) { $this->logger->notice(sprintf('No Authentication entry point configured, returning a %s HTTP response. Configure "entry_point" on the firewall "%s" if you want to modify the response.', Response::HTTP_UNAUTHORIZED, $this->firewallName)); } throw new HttpException(Response::HTTP_UNAUTHORIZED, $authException->getMessage(), $authException, [], $authException->getCode()); } } Firewall/FirewallListenerInterface.php 0000644 00000002367 15120140656 0014126 0 ustar 00 <?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\Firewall; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\RequestEvent; /** * Can be implemented by firewall listeners. * * @author Christian Scheb <me@christianscheb.de> * @author Nicolas Grekas <p@tchwork.com> * @author Robin Chalas <robin.chalas@gmail.com> */ interface FirewallListenerInterface { /** * Tells whether the authenticate() method should be called or not depending on the incoming request. * * Returning null means authenticate() can be called lazily when accessing the token storage. */ public function supports(Request $request): ?bool; /** * Does whatever is required to authenticate the request, typically calling $event->setResponse() internally. */ public function authenticate(RequestEvent $event); /** * Defines the priority of the listener. * The higher the number, the earlier a listener is executed. */ public static function getPriority(): int; } Firewall/LogoutListener.php 0000644 00000013466 15120140656 0012013 0 ustar 00 <?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\Firewall; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Exception\LogicException; use Symfony\Component\Security\Core\Exception\LogoutException; use Symfony\Component\Security\Csrf\CsrfToken; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Component\Security\Http\Event\LogoutEvent; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface; use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface; use Symfony\Component\Security\Http\ParameterBagUtils; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * LogoutListener logout users. * * @author Fabien Potencier <fabien@symfony.com> * * @final */ class LogoutListener extends AbstractListener { private $tokenStorage; private $options; private $httpUtils; private $csrfTokenManager; private $eventDispatcher; /** * @param EventDispatcherInterface $eventDispatcher * @param array $options An array of options to process a logout attempt */ public function __construct(TokenStorageInterface $tokenStorage, HttpUtils $httpUtils, $eventDispatcher, array $options = [], CsrfTokenManagerInterface $csrfTokenManager = null) { if (!$eventDispatcher instanceof EventDispatcherInterface) { trigger_deprecation('symfony/security-http', '5.1', 'Passing a logout success handler to "%s" is deprecated, pass an instance of "%s" instead.', __METHOD__, EventDispatcherInterface::class); if (!$eventDispatcher instanceof LogoutSuccessHandlerInterface) { throw new \TypeError(sprintf('Argument 3 of "%s" must be instance of "%s" or "%s", "%s" given.', __METHOD__, EventDispatcherInterface::class, LogoutSuccessHandlerInterface::class, get_debug_type($eventDispatcher))); } $successHandler = $eventDispatcher; $eventDispatcher = new EventDispatcher(); $eventDispatcher->addListener(LogoutEvent::class, function (LogoutEvent $event) use ($successHandler) { $event->setResponse($r = $successHandler->onLogoutSuccess($event->getRequest())); }); } $this->tokenStorage = $tokenStorage; $this->httpUtils = $httpUtils; $this->options = array_merge([ 'csrf_parameter' => '_csrf_token', 'csrf_token_id' => 'logout', 'logout_path' => '/logout', ], $options); $this->csrfTokenManager = $csrfTokenManager; $this->eventDispatcher = $eventDispatcher; } /** * @deprecated since Symfony 5.1 */ public function addHandler(LogoutHandlerInterface $handler) { trigger_deprecation('symfony/security-http', '5.1', 'Calling "%s" is deprecated, register a listener on the "%s" event instead.', __METHOD__, LogoutEvent::class); $this->eventDispatcher->addListener(LogoutEvent::class, function (LogoutEvent $event) use ($handler) { if (null === $event->getResponse()) { throw new LogicException(sprintf('No response was set for this logout action. Make sure the DefaultLogoutListener or another listener has set the response before "%s" is called.', __CLASS__)); } $handler->logout($event->getRequest(), $event->getResponse(), $event->getToken()); }); } /** * {@inheritdoc} */ public function supports(Request $request): ?bool { return $this->requiresLogout($request); } /** * Performs the logout if requested. * * If a CsrfTokenManagerInterface instance is available, it will be used to * validate the request. * * @throws LogoutException if the CSRF token is invalid * @throws \RuntimeException if the LogoutSuccessHandlerInterface instance does not return a response */ public function authenticate(RequestEvent $event) { $request = $event->getRequest(); if (null !== $this->csrfTokenManager) { $csrfToken = ParameterBagUtils::getRequestParameterValue($request, $this->options['csrf_parameter']); if (!\is_string($csrfToken) || false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['csrf_token_id'], $csrfToken))) { throw new LogoutException('Invalid CSRF token.'); } } $logoutEvent = new LogoutEvent($request, $this->tokenStorage->getToken()); $this->eventDispatcher->dispatch($logoutEvent); if (!$response = $logoutEvent->getResponse()) { throw new \RuntimeException('No logout listener set the Response, make sure at least the DefaultLogoutListener is registered.'); } $this->tokenStorage->setToken(null); $event->setResponse($response); } /** * Whether this request is asking for logout. * * The default implementation only processed requests to a specific path, * but a subclass could change this to logout requests where * certain parameters is present. */ protected function requiresLogout(Request $request): bool { return isset($this->options['logout_path']) && $this->httpUtils->checkRequestPath($request, $this->options['logout_path']); } public static function getPriority(): int { return -127; } } Firewall/RememberMeListener.php 0000644 00000011472 15120140656 0012555 0 ustar 00 <?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\Firewall; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; use Symfony\Component\Security\Http\SecurityEvents; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; trigger_deprecation('symfony/security-http', '5.3', 'The "%s" class is deprecated, use the new authenticator system instead.', RememberMeListener::class); /** * RememberMeListener implements authentication capabilities via a cookie. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> * * @final * * @deprecated since Symfony 5.3, use the new authenticator system instead */ class RememberMeListener extends AbstractListener { private $tokenStorage; private $rememberMeServices; private $authenticationManager; private $logger; private $dispatcher; private $catchExceptions = true; private $sessionStrategy; public function __construct(TokenStorageInterface $tokenStorage, RememberMeServicesInterface $rememberMeServices, AuthenticationManagerInterface $authenticationManager, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, bool $catchExceptions = true, SessionAuthenticationStrategyInterface $sessionStrategy = null) { $this->tokenStorage = $tokenStorage; $this->rememberMeServices = $rememberMeServices; $this->authenticationManager = $authenticationManager; $this->logger = $logger; $this->dispatcher = $dispatcher; $this->catchExceptions = $catchExceptions; $this->sessionStrategy = $sessionStrategy ?? new SessionAuthenticationStrategy(SessionAuthenticationStrategy::MIGRATE); } /** * {@inheritdoc} */ public function supports(Request $request): ?bool { return null; // always run authenticate() lazily with lazy firewalls } /** * Handles remember-me cookie based authentication. */ public function authenticate(RequestEvent $event) { if (null !== $this->tokenStorage->getToken()) { return; } $request = $event->getRequest(); try { if (null === $token = $this->rememberMeServices->autoLogin($request)) { return; } } catch (AuthenticationException $e) { if (null !== $this->logger) { $this->logger->warning( 'The token storage was not populated with remember-me token as the' .' RememberMeServices was not able to create a token from the remember' .' me information.', ['exception' => $e] ); } $this->rememberMeServices->loginFail($request); if (!$this->catchExceptions) { throw $e; } return; } try { $token = $this->authenticationManager->authenticate($token); if ($request->hasSession() && $request->getSession()->isStarted()) { $this->sessionStrategy->onAuthentication($request, $token); } $this->tokenStorage->setToken($token); if (null !== $this->dispatcher) { $loginEvent = new InteractiveLoginEvent($request, $token); $this->dispatcher->dispatch($loginEvent, SecurityEvents::INTERACTIVE_LOGIN); } if (null !== $this->logger) { $this->logger->debug('Populated the token storage with a remember-me token.'); } } catch (AuthenticationException $e) { if (null !== $this->logger) { $this->logger->warning( 'The token storage was not populated with remember-me token as the' .' AuthenticationManager rejected the AuthenticationToken returned' .' by the RememberMeServices.', ['exception' => $e] ); } $this->rememberMeServices->loginFail($request, $e); if (!$this->catchExceptions) { throw $e; } } } } Firewall/RemoteUserAuthenticationListener.php 0000644 00000003621 15120140656 0015524 0 ustar 00 <?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\Firewall; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; trigger_deprecation('symfony/security-http', '5.3', 'The "%s" class is deprecated, use the new authenticator system instead.', RemoteUserAuthenticationListener::class); /** * REMOTE_USER authentication listener. * * @author Fabien Potencier <fabien@symfony.com> * @author Maxime Douailin <maxime.douailin@gmail.com> * * @deprecated since Symfony 5.3, use the new authenticator system instead */ class RemoteUserAuthenticationListener extends AbstractPreAuthenticatedListener { private $userKey; public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, string $providerKey, string $userKey = 'REMOTE_USER', LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) { parent::__construct($tokenStorage, $authenticationManager, $providerKey, $logger, $dispatcher); $this->userKey = $userKey; } /** * {@inheritdoc} */ protected function getPreAuthenticatedData(Request $request) { 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]; } } Firewall/SwitchUserListener.php 0000644 00000021757 15120140656 0012644 0 ustar 00 <?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\Firewall; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Event\SwitchUserEvent; use Symfony\Component\Security\Http\SecurityEvents; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * SwitchUserListener allows a user to impersonate another one temporarily * (like the Unix su command). * * @author Fabien Potencier <fabien@symfony.com> * * @final */ class SwitchUserListener extends AbstractListener { public const EXIT_VALUE = '_exit'; private $tokenStorage; private $provider; private $userChecker; private $firewallName; private $accessDecisionManager; private $usernameParameter; private $role; private $logger; private $dispatcher; private $stateless; public function __construct(TokenStorageInterface $tokenStorage, UserProviderInterface $provider, UserCheckerInterface $userChecker, string $firewallName, AccessDecisionManagerInterface $accessDecisionManager, LoggerInterface $logger = null, string $usernameParameter = '_switch_user', string $role = 'ROLE_ALLOWED_TO_SWITCH', EventDispatcherInterface $dispatcher = null, bool $stateless = false) { if ('' === $firewallName) { throw new \InvalidArgumentException('$firewallName must not be empty.'); } $this->tokenStorage = $tokenStorage; $this->provider = $provider; $this->userChecker = $userChecker; $this->firewallName = $firewallName; $this->accessDecisionManager = $accessDecisionManager; $this->usernameParameter = $usernameParameter; $this->role = $role; $this->logger = $logger; $this->dispatcher = $dispatcher; $this->stateless = $stateless; } /** * {@inheritdoc} */ public function supports(Request $request): ?bool { // usernames can be falsy $username = $request->get($this->usernameParameter); if (null === $username || '' === $username) { $username = $request->headers->get($this->usernameParameter); } // if it's still "empty", nothing to do. if (null === $username || '' === $username) { return false; } $request->attributes->set('_switch_user_username', $username); return true; } /** * Handles the switch to another user. * * @throws \LogicException if switching to a user failed */ public function authenticate(RequestEvent $event) { $request = $event->getRequest(); $username = $request->attributes->get('_switch_user_username'); $request->attributes->remove('_switch_user_username'); if (null === $this->tokenStorage->getToken()) { throw new AuthenticationCredentialsNotFoundException('Could not find original Token object.'); } if (self::EXIT_VALUE === $username) { $this->tokenStorage->setToken($this->attemptExitUser($request)); } else { try { $this->tokenStorage->setToken($this->attemptSwitchUser($request, $username)); } catch (AuthenticationException $e) { // Generate 403 in any conditions to prevent user enumeration vulnerabilities throw new AccessDeniedException('Switch User failed: '.$e->getMessage(), $e); } } if (!$this->stateless) { $request->query->remove($this->usernameParameter); $request->server->set('QUERY_STRING', http_build_query($request->query->all(), '', '&')); $response = new RedirectResponse($request->getUri(), 302); $event->setResponse($response); } } /** * Attempts to switch to another user and returns the new token if successfully switched. * * @throws \LogicException * @throws AccessDeniedException */ private function attemptSwitchUser(Request $request, string $username): ?TokenInterface { $token = $this->tokenStorage->getToken(); $originalToken = $this->getOriginalToken($token); if (null !== $originalToken) { // @deprecated since Symfony 5.3, change to $token->getUserIdentifier() in 6.0 if ((method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername()) === $username) { return $token; } // User already switched, exit before seamlessly switching to another user $token = $this->attemptExitUser($request); } // @deprecated since Symfony 5.3, change to $token->getUserIdentifier() in 6.0 $currentUsername = method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername(); $nonExistentUsername = '_'.md5(random_bytes(8).$username); // To protect against user enumeration via timing measurements // we always load both successfully and unsuccessfully $methodName = 'loadUserByIdentifier'; if (!method_exists($this->provider, $methodName)) { trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->provider)); $methodName = 'loadUserByUsername'; } try { $user = $this->provider->$methodName($username); try { $this->provider->$methodName($nonExistentUsername); } catch (\Exception $e) { } } catch (AuthenticationException $e) { $this->provider->$methodName($currentUsername); throw $e; } if (false === $this->accessDecisionManager->decide($token, [$this->role], $user)) { $exception = new AccessDeniedException(); $exception->setAttributes($this->role); throw $exception; } if (null !== $this->logger) { $this->logger->info('Attempting to switch to user.', ['username' => $username]); } $this->userChecker->checkPostAuth($user); $roles = $user->getRoles(); $roles[] = 'ROLE_PREVIOUS_ADMIN'; $originatedFromUri = str_replace('/&', '/?', preg_replace('#[&?]'.$this->usernameParameter.'=[^&]*#', '', $request->getRequestUri())); $token = new SwitchUserToken($user, $this->firewallName, $roles, $token, $originatedFromUri); if (null !== $this->dispatcher) { $switchEvent = new SwitchUserEvent($request, $token->getUser(), $token); $this->dispatcher->dispatch($switchEvent, SecurityEvents::SWITCH_USER); // use the token from the event in case any listeners have replaced it. $token = $switchEvent->getToken(); } return $token; } /** * Attempts to exit from an already switched user and returns the original token. * * @throws AuthenticationCredentialsNotFoundException */ private function attemptExitUser(Request $request): TokenInterface { if (null === ($currentToken = $this->tokenStorage->getToken()) || null === $original = $this->getOriginalToken($currentToken)) { throw new AuthenticationCredentialsNotFoundException('Could not find original Token object.'); } if (null !== $this->dispatcher && $original->getUser() instanceof UserInterface) { $user = $this->provider->refreshUser($original->getUser()); $original->setUser($user); $switchEvent = new SwitchUserEvent($request, $user, $original); $this->dispatcher->dispatch($switchEvent, SecurityEvents::SWITCH_USER); $original = $switchEvent->getToken(); } return $original; } private function getOriginalToken(TokenInterface $token): ?TokenInterface { if ($token instanceof SwitchUserToken) { return $token->getOriginalToken(); } return null; } } Firewall/UsernamePasswordFormAuthenticationListener.php 0000644 00000012177 15120140656 0017566 0 ustar 00 <?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\Firewall; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException; use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Csrf\CsrfToken; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Http\ParameterBagUtils; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; trigger_deprecation('symfony/security-http', '5.3', 'The "%s" class is deprecated, use the new authenticator system instead.', UsernamePasswordFormAuthenticationListener::class); /** * UsernamePasswordFormAuthenticationListener is the default implementation of * an authentication via a simple form composed of a username and a password. * * @author Fabien Potencier <fabien@symfony.com> * * @deprecated since Symfony 5.3, use the new authenticator system instead */ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationListener { private $csrfTokenManager; public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, string $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = [], LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfTokenManagerInterface $csrfTokenManager = null) { parent::__construct($tokenStorage, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, $failureHandler, array_merge([ 'username_parameter' => '_username', 'password_parameter' => '_password', 'csrf_parameter' => '_csrf_token', 'csrf_token_id' => 'authenticate', 'post_only' => true, ], $options), $logger, $dispatcher); $this->csrfTokenManager = $csrfTokenManager; } /** * {@inheritdoc} */ protected function requiresAuthentication(Request $request) { if ($this->options['post_only'] && !$request->isMethod('POST')) { return false; } return parent::requiresAuthentication($request); } /** * {@inheritdoc} */ protected function attemptAuthentication(Request $request) { if (null !== $this->csrfTokenManager) { $csrfToken = ParameterBagUtils::getRequestParameterValue($request, $this->options['csrf_parameter']); if (!\is_string($csrfToken) || false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['csrf_token_id'], $csrfToken))) { throw new InvalidCsrfTokenException('Invalid CSRF token.'); } } if ($this->options['post_only']) { $username = ParameterBagUtils::getParameterBagValue($request->request, $this->options['username_parameter']); $password = ParameterBagUtils::getParameterBagValue($request->request, $this->options['password_parameter']); } else { $username = ParameterBagUtils::getRequestParameterValue($request, $this->options['username_parameter']); $password = ParameterBagUtils::getRequestParameterValue($request, $this->options['password_parameter']); } if (!\is_string($username) && (!\is_object($username) || !method_exists($username, '__toString'))) { throw new BadRequestHttpException(sprintf('The key "%s" must be a string, "%s" given.', $this->options['username_parameter'], get_debug_type($username))); } $username = trim($username); if (\strlen($username) > Security::MAX_USERNAME_LENGTH) { throw new BadCredentialsException('Invalid username.'); } if (null === $password) { throw new \LogicException(sprintf('The key "%s" cannot be null; check that the password field name of the form matches.', $this->options['password_parameter'])); } $request->getSession()->set(Security::LAST_USERNAME, $username); return $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $password, $this->providerKey)); } } Firewall/UsernamePasswordJsonAuthenticationListener.php 0000644 00000023223 15120140656 0017566 0 ustar 00 <?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\Firewall; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\RequestEvent; 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\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; 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\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Http\SecurityEvents; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\Translation\TranslatorInterface; trigger_deprecation('symfony/security-http', '5.3', 'The "%s" class is deprecated, use the new authenticator system instead.', UsernamePasswordJsonAuthenticationListener::class); /** * UsernamePasswordJsonAuthenticationListener is a stateless implementation of * an authentication via a JSON document composed of a username and a password. * * @author Kévin Dunglas <dunglas@gmail.com> * * @deprecated since Symfony 5.3, use the new authenticator system instead */ class UsernamePasswordJsonAuthenticationListener extends AbstractListener { private $tokenStorage; private $authenticationManager; private $httpUtils; private $providerKey; private $successHandler; private $failureHandler; private $options; private $logger; private $eventDispatcher; private $propertyAccessor; private $sessionStrategy; /** * @var TranslatorInterface|null */ private $translator; public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, HttpUtils $httpUtils, string $providerKey, AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, array $options = [], LoggerInterface $logger = null, EventDispatcherInterface $eventDispatcher = null, PropertyAccessorInterface $propertyAccessor = null) { $this->tokenStorage = $tokenStorage; $this->authenticationManager = $authenticationManager; $this->httpUtils = $httpUtils; $this->providerKey = $providerKey; $this->successHandler = $successHandler; $this->failureHandler = $failureHandler; $this->logger = $logger; $this->eventDispatcher = $eventDispatcher; $this->options = array_merge(['username_path' => 'username', 'password_path' => 'password'], $options); $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor(); } public function supports(Request $request): ?bool { if (!str_contains($request->getRequestFormat() ?? '', 'json') && !str_contains($request->getContentType() ?? '', 'json') ) { return false; } if (isset($this->options['check_path']) && !$this->httpUtils->checkRequestPath($request, $this->options['check_path'])) { return false; } return true; } /** * {@inheritdoc} */ public function authenticate(RequestEvent $event) { $request = $event->getRequest(); $data = json_decode($request->getContent()); $previousToken = $this->tokenStorage->getToken(); try { if (!$data instanceof \stdClass) { throw new BadRequestHttpException('Invalid JSON.'); } try { $username = $this->propertyAccessor->getValue($data, $this->options['username_path']); } catch (AccessException $e) { throw new BadRequestHttpException(sprintf('The key "%s" must be provided.', $this->options['username_path']), $e); } try { $password = $this->propertyAccessor->getValue($data, $this->options['password_path']); } catch (AccessException $e) { throw new BadRequestHttpException(sprintf('The key "%s" must be provided.', $this->options['password_path']), $e); } if (!\is_string($username)) { throw new BadRequestHttpException(sprintf('The key "%s" must be a string.', $this->options['username_path'])); } if (\strlen($username) > Security::MAX_USERNAME_LENGTH) { throw new BadCredentialsException('Invalid username.'); } if (!\is_string($password)) { throw new BadRequestHttpException(sprintf('The key "%s" must be a string.', $this->options['password_path'])); } $token = new UsernamePasswordToken($username, $password, $this->providerKey); $authenticatedToken = $this->authenticationManager->authenticate($token); $response = $this->onSuccess($request, $authenticatedToken, $previousToken); } catch (AuthenticationException $e) { $response = $this->onFailure($request, $e); } catch (BadRequestHttpException $e) { $request->setRequestFormat('json'); throw $e; } if (null === $response) { return; } $event->setResponse($response); } private function onSuccess(Request $request, TokenInterface $token, ?TokenInterface $previousToken): ?Response { if (null !== $this->logger) { // @deprecated since Symfony 5.3, change to $token->getUserIdentifier() in 6.0 $this->logger->info('User has been authenticated successfully.', ['username' => method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername()]); } $this->migrateSession($request, $token, $previousToken); $this->tokenStorage->setToken($token); if (null !== $this->eventDispatcher) { $loginEvent = new InteractiveLoginEvent($request, $token); $this->eventDispatcher->dispatch($loginEvent, SecurityEvents::INTERACTIVE_LOGIN); } if (!$this->successHandler) { return null; // let the original request succeeds } $response = $this->successHandler->onAuthenticationSuccess($request, $token); if (!$response instanceof Response) { throw new \RuntimeException('Authentication Success Handler did not return a Response.'); } return $response; } private function onFailure(Request $request, AuthenticationException $failed): Response { if (null !== $this->logger) { $this->logger->info('Authentication request failed.', ['exception' => $failed]); } $token = $this->tokenStorage->getToken(); if ($token instanceof UsernamePasswordToken && $this->providerKey === $token->getFirewallName()) { $this->tokenStorage->setToken(null); } if (!$this->failureHandler) { if (null !== $this->translator) { $errorMessage = $this->translator->trans($failed->getMessageKey(), $failed->getMessageData(), 'security'); } else { $errorMessage = strtr($failed->getMessageKey(), $failed->getMessageData()); } return new JsonResponse(['error' => $errorMessage], 401); } $response = $this->failureHandler->onAuthenticationFailure($request, $failed); if (!$response instanceof Response) { throw new \RuntimeException('Authentication Failure Handler did not return a Response.'); } return $response; } /** * Call this method if your authentication token is stored to a session. * * @final */ public function setSessionAuthenticationStrategy(SessionAuthenticationStrategyInterface $sessionStrategy) { $this->sessionStrategy = $sessionStrategy; } public function setTranslator(TranslatorInterface $translator) { $this->translator = $translator; } private function migrateSession(Request $request, TokenInterface $token, ?TokenInterface $previousToken) { if (!$this->sessionStrategy || !$request->hasSession() || !$request->hasPreviousSession()) { return; } if ($previousToken) { $user = method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername(); $previousUser = method_exists($previousToken, 'getUserIdentifier') ? $previousToken->getUserIdentifier() : $previousToken->getUsername(); if ('' !== ($user ?? '') && $user === $previousUser) { return; } } $this->sessionStrategy->onAuthentication($request, $token); } } Firewall/X509AuthenticationListener.php 0000644 00000004530 15120140656 0014077 0 ustar 00 <?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\Firewall; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; trigger_deprecation('symfony/security-http', '5.3', 'The "%s" class is deprecated, use the new authenticator system instead.', X509AuthenticationListener::class); /** * X509 authentication listener. * * @author Fabien Potencier <fabien@symfony.com> * * @deprecated since Symfony 5.3, use the new authenticator system instead */ class X509AuthenticationListener extends AbstractPreAuthenticatedListener { private $userKey; private $credentialKey; public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, string $providerKey, string $userKey = 'SSL_CLIENT_S_DN_Email', string $credentialKey = 'SSL_CLIENT_S_DN', LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) { parent::__construct($tokenStorage, $authenticationManager, $providerKey, $logger, $dispatcher); $this->userKey = $userKey; $this->credentialKey = $credentialKey; } /** * {@inheritdoc} */ protected function getPreAuthenticatedData(Request $request) { $user = null; if ($request->server->has($this->userKey)) { $user = $request->server->get($this->userKey); } elseif ( $request->server->has($this->credentialKey) && preg_match('#emailAddress=([^,/@]++@[^,/]++)#', $request->server->get($this->credentialKey), $matches) ) { $user = $matches[1]; } if (null === $user) { throw new BadCredentialsException(sprintf('SSL credentials not found: "%s", "%s".', $this->userKey, $this->credentialKey)); } return [$user, $request->server->get($this->credentialKey, '')]; } } Impersonate/ImpersonateUrlGenerator.php 0000644 00000004705 15120140656 0014371 0 ustar 00 <?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\Impersonate; use Symfony\Bundle\SecurityBundle\Security\FirewallMap; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken; use Symfony\Component\Security\Http\Firewall\SwitchUserListener; /** * Provides generator functions for the impersonate url exit. * * @author Amrouche Hamza <hamza.simperfit@gmail.com> * @author Damien Fayet <damienf1521@gmail.com> */ class ImpersonateUrlGenerator { private $requestStack; private $tokenStorage; private $firewallMap; public function __construct(RequestStack $requestStack, FirewallMap $firewallMap, TokenStorageInterface $tokenStorage) { $this->requestStack = $requestStack; $this->tokenStorage = $tokenStorage; $this->firewallMap = $firewallMap; } public function generateExitPath(string $targetUri = null): string { return $this->buildExitPath($targetUri); } public function generateExitUrl(string $targetUri = null): string { if (null === $request = $this->requestStack->getCurrentRequest()) { return ''; } return $request->getUriForPath($this->buildExitPath($targetUri)); } private function isImpersonatedUser(): bool { return $this->tokenStorage->getToken() instanceof SwitchUserToken; } private function buildExitPath(string $targetUri = null): string { if (null === ($request = $this->requestStack->getCurrentRequest()) || !$this->isImpersonatedUser()) { return ''; } if (null === $switchUserConfig = $this->firewallMap->getFirewallConfig($request)->getSwitchUser()) { throw new \LogicException('Unable to generate the impersonate exit URL without a firewall configured for the user switch.'); } if (null === $targetUri) { $targetUri = $request->getRequestUri(); } $targetUri .= (parse_url($targetUri, \PHP_URL_QUERY) ? '&' : '?').http_build_query([$switchUserConfig['parameter'] => SwitchUserListener::EXIT_VALUE], '', '&'); return $targetUri; } } LoginLink/Exception/ExpiredLoginLinkException.php 0000644 00000001055 15120140656 0016170 0 ustar 00 <?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\LoginLink\Exception; use Symfony\Component\Security\Core\Signature\Exception\ExpiredSignatureException; /** * @author Ryan Weaver <ryan@symfonycasts.com> */ class ExpiredLoginLinkException extends ExpiredSignatureException implements InvalidLoginLinkExceptionInterface { } LoginLink/Exception/InvalidLoginLinkAuthenticationException.php 0000644 00000001260 15120140656 0021054 0 ustar 00 <?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\LoginLink\Exception; use Symfony\Component\Security\Core\Exception\AuthenticationException; /** * Thrown when a login link is invalid. * * @author Ryan Weaver <ryan@symfonycasts.com> */ class InvalidLoginLinkAuthenticationException extends AuthenticationException { /** * {@inheritdoc} */ public function getMessageKey() { return 'Invalid or expired login link.'; } } LoginLink/Exception/InvalidLoginLinkException.php 0000644 00000000721 15120140656 0016155 0 ustar 00 <?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\LoginLink\Exception; /** * @author Ryan Weaver <ryan@symfonycasts.com> */ class InvalidLoginLinkException extends \RuntimeException implements InvalidLoginLinkExceptionInterface { } LoginLink/Exception/InvalidLoginLinkExceptionInterface.php 0000644 00000000651 15120140656 0020000 0 ustar 00 <?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\LoginLink\Exception; /** * @author Ryan Weaver <ryan@symfonycasts.com> */ interface InvalidLoginLinkExceptionInterface extends \Throwable { } LoginLink/LoginLinkDetails.php 0000644 00000001466 15120140656 0012346 0 ustar 00 <?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\LoginLink; /** * @author Ryan Weaver <ryan@symfonycasts.com> */ class LoginLinkDetails { private $url; private $expiresAt; public function __construct(string $url, \DateTimeImmutable $expiresAt) { $this->url = $url; $this->expiresAt = $expiresAt; } public function getUrl(): string { return $this->url; } public function getExpiresAt(): \DateTimeImmutable { return $this->expiresAt; } public function __toString() { return $this->url; } } LoginLink/LoginLinkHandler.php 0000644 00000011231 15120140656 0012325 0 ustar 00 <?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\LoginLink; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\Signature\Exception\ExpiredSignatureException; use Symfony\Component\Security\Core\Signature\Exception\InvalidSignatureException; use Symfony\Component\Security\Core\Signature\SignatureHasher; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\LoginLink\Exception\ExpiredLoginLinkException; use Symfony\Component\Security\Http\LoginLink\Exception\InvalidLoginLinkException; /** * @author Ryan Weaver <ryan@symfonycasts.com> */ final class LoginLinkHandler implements LoginLinkHandlerInterface { private $urlGenerator; private $userProvider; private $options; private $signatureHasher; public function __construct(UrlGeneratorInterface $urlGenerator, UserProviderInterface $userProvider, SignatureHasher $signatureHasher, array $options) { $this->urlGenerator = $urlGenerator; $this->userProvider = $userProvider; $this->signatureHasher = $signatureHasher; $this->options = array_merge([ 'route_name' => null, 'lifetime' => 600, ], $options); } public function createLoginLink(UserInterface $user, Request $request = null): LoginLinkDetails { $expires = time() + $this->options['lifetime']; $expiresAt = new \DateTimeImmutable('@'.$expires); $parameters = [ // @deprecated since Symfony 5.3, change to $user->getUserIdentifier() in 6.0 'user' => method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(), 'expires' => $expires, 'hash' => $this->signatureHasher->computeSignatureHash($user, $expires), ]; if ($request) { $currentRequestContext = $this->urlGenerator->getContext(); $this->urlGenerator->setContext( (new RequestContext()) ->fromRequest($request) ->setParameter('_locale', $request->getLocale()) ); } try { $url = $this->urlGenerator->generate( $this->options['route_name'], $parameters, UrlGeneratorInterface::ABSOLUTE_URL ); } finally { if ($request) { $this->urlGenerator->setContext($currentRequestContext); } } return new LoginLinkDetails($url, $expiresAt); } public function consumeLoginLink(Request $request): UserInterface { $userIdentifier = $request->get('user'); if (!$hash = $request->get('hash')) { throw new InvalidLoginLinkException('Missing "hash" parameter.'); } if (!$expires = $request->get('expires')) { throw new InvalidLoginLinkException('Missing "expires" parameter.'); } try { $this->signatureHasher->acceptSignatureHash($userIdentifier, $expires, $hash); // @deprecated since Symfony 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0 if (method_exists($this->userProvider, 'loadUserByIdentifier')) { $user = $this->userProvider->loadUserByIdentifier($userIdentifier); } else { trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->userProvider)); $user = $this->userProvider->loadUserByUsername($userIdentifier); } $this->signatureHasher->verifySignatureHash($user, $expires, $hash); } catch (UserNotFoundException $e) { throw new InvalidLoginLinkException('User not found.', 0, $e); } catch (ExpiredSignatureException $e) { throw new ExpiredLoginLinkException(ucfirst(str_ireplace('signature', 'login link', $e->getMessage())), 0, $e); } catch (InvalidSignatureException $e) { throw new InvalidLoginLinkException(ucfirst(str_ireplace('signature', 'login link', $e->getMessage())), 0, $e); } return $user; } } LoginLink/LoginLinkHandlerInterface.php 0000644 00000001756 15120140656 0014161 0 ustar 00 <?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\LoginLink; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\User\UserInterface; /** * A class that is able to create and handle "magic" login links. * * @author Ryan Weaver <ryan@symfonycasts.com> */ interface LoginLinkHandlerInterface { /** * Generate a link that can be used to authenticate as the given user. */ public function createLoginLink(UserInterface $user, Request $request = null): LoginLinkDetails; /** * Validates if this request contains a login link and returns the associated User. * * Throw InvalidLoginLinkExceptionInterface if the link is invalid. */ public function consumeLoginLink(Request $request): UserInterface; } LoginLink/LoginLinkNotification.php 0000644 00000005227 15120140656 0013406 0 ustar 00 <?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\LoginLink; use Symfony\Bridge\Twig\Mime\NotificationEmail; use Symfony\Component\Notifier\Message\EmailMessage; use Symfony\Component\Notifier\Message\SmsMessage; use Symfony\Component\Notifier\Notification\EmailNotificationInterface; use Symfony\Component\Notifier\Notification\Notification; use Symfony\Component\Notifier\Notification\SmsNotificationInterface; use Symfony\Component\Notifier\Recipient\EmailRecipientInterface; use Symfony\Component\Notifier\Recipient\SmsRecipientInterface; /** * Use this notification to ease sending login link * emails/SMS using the Notifier component. * * @author Wouter de Jong <wouter@wouterj.nl> */ class LoginLinkNotification extends Notification implements EmailNotificationInterface, SmsNotificationInterface { private $loginLinkDetails; public function __construct(LoginLinkDetails $loginLinkDetails, string $subject, array $channels = []) { parent::__construct($subject, $channels); $this->loginLinkDetails = $loginLinkDetails; } public function asEmailMessage(EmailRecipientInterface $recipient, string $transport = null): ?EmailMessage { if (!class_exists(NotificationEmail::class)) { throw new \LogicException(sprintf('The "%s" method requires "symfony/twig-bridge:>4.4".', __METHOD__)); } $email = NotificationEmail::asPublicEmail() ->to($recipient->getEmail()) ->subject($this->getSubject()) ->content($this->getContent() ?: $this->getDefaultContent('button below')) ->action('Sign in', $this->loginLinkDetails->getUrl()) ; return new EmailMessage($email); } public function asSmsMessage(SmsRecipientInterface $recipient, string $transport = null): ?SmsMessage { return new SmsMessage($recipient->getPhone(), $this->getDefaultContent('link').' '.$this->loginLinkDetails->getUrl()); } private function getDefaultContent(string $target): string { $duration = $this->loginLinkDetails->getExpiresAt()->getTimestamp() - time(); $durationString = floor($duration / 60).' minute'.($duration > 60 ? 's' : ''); if (($hours = $duration / 3600) >= 1) { $durationString = floor($hours).' hour'.($hours >= 2 ? 's' : ''); } return sprintf('Click on the %s to confirm you want to sign in. This link will expire in %s.', $target, $durationString); } } Logout/CookieClearingLogoutHandler.php 0000644 00000003160 15120140656 0014074 0 ustar 00 <?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\Logout; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Http\EventListener\CookieClearingLogoutListener; trigger_deprecation('symfony/security-http', '5.4', 'The "%s" class is deprecated, use "%s" instead.', CookieClearingLogoutHandler::class, CookieClearingLogoutListener::class); /** * This handler clears the passed cookies when a user logs out. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> * * @deprecated since Symfony 5.4, use {@link CookieClearingLogoutListener} instead */ class CookieClearingLogoutHandler implements LogoutHandlerInterface { private $cookies; /** * @param array $cookies An array of cookie names to unset */ public function __construct(array $cookies) { $this->cookies = $cookies; } /** * Implementation for the LogoutHandlerInterface. Deletes all requested cookies. */ public function logout(Request $request, Response $response, TokenInterface $token) { foreach ($this->cookies as $cookieName => $cookieData) { $response->headers->clearCookie($cookieName, $cookieData['path'], $cookieData['domain'], $cookieData['secure'] ?? false, true, $cookieData['samesite'] ?? null); } } } Logout/CsrfTokenClearingLogoutHandler.php 0000644 00000002525 15120140656 0014565 0 ustar 00 <?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\Logout; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Csrf\TokenStorage\ClearableTokenStorageInterface; use Symfony\Component\Security\Http\EventListener\CsrfTokenClearingLogoutListener; trigger_deprecation('symfony/security-http', '5.4', 'The "%s" class is deprecated, use "%s" instead.', CsrfTokenClearingLogoutHandler::class, CsrfTokenClearingLogoutListener::class); /** * @author Christian Flothmann <christian.flothmann@sensiolabs.de> * * @deprecated since Symfony 5.4, use {@link CsrfTokenClearingLogoutListener} instead */ class CsrfTokenClearingLogoutHandler implements LogoutHandlerInterface { private $csrfTokenStorage; public function __construct(ClearableTokenStorageInterface $csrfTokenStorage) { $this->csrfTokenStorage = $csrfTokenStorage; } public function logout(Request $request, Response $response, TokenInterface $token) { $this->csrfTokenStorage->clear(); } } Logout/DefaultLogoutSuccessHandler.php 0000644 00000002464 15120140656 0014141 0 ustar 00 <?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\Logout; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Http\EventListener\DefaultLogoutListener; use Symfony\Component\Security\Http\HttpUtils; trigger_deprecation('symfony/security-http', '5.1', 'The "%s" class is deprecated, use "%s" instead.', DefaultLogoutSuccessHandler::class, DefaultLogoutListener::class); /** * Default logout success handler will redirect users to a configured path. * * @author Fabien Potencier <fabien@symfony.com> * @author Alexander <iam.asm89@gmail.com> * * @deprecated since Symfony 5.1 */ class DefaultLogoutSuccessHandler implements LogoutSuccessHandlerInterface { protected $httpUtils; protected $targetUrl; public function __construct(HttpUtils $httpUtils, string $targetUrl = '/') { $this->httpUtils = $httpUtils; $this->targetUrl = $targetUrl; } /** * {@inheritdoc} */ public function onLogoutSuccess(Request $request) { return $this->httpUtils->createRedirectResponse($request, $this->targetUrl); } } Logout/LogoutHandlerInterface.php 0000644 00000001651 15120140656 0013121 0 ustar 00 <?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\Logout; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; /** * Interface that needs to be implemented by LogoutHandlers. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> * * @deprecated since Symfony 5.1 */ interface LogoutHandlerInterface { /** * This method is called by the LogoutListener when a user has requested * to be logged out. Usually, you would unset session variables, or remove * cookies, etc. */ public function logout(Request $request, Response $response, TokenInterface $token); } Logout/LogoutSuccessHandlerInterface.php 0000644 00000002322 15120140656 0014446 0 ustar 00 <?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\Logout; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Http\Event\LogoutEvent; trigger_deprecation('symfony/security-http', '5.1', 'The "%s" interface is deprecated, create a listener for the "%s" event instead.', LogoutSuccessHandlerInterface::class, LogoutEvent::class); /** * LogoutSuccesshandlerInterface. * * In contrast to the LogoutHandlerInterface, this interface can return a response * which is then used instead of the default behavior. * * If you want to only perform some logout related clean-up task, use the * LogoutHandlerInterface instead. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> * * @deprecated since Symfony 5.1 */ interface LogoutSuccessHandlerInterface { /** * Creates a Response object to send upon a successful logout. * * @return Response */ public function onLogoutSuccess(Request $request); } Logout/LogoutUrlGenerator.php 0000644 00000014252 15120140656 0012335 0 ustar 00 <?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\Logout; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; /** * Provides generator functions for the logout URL. * * @author Fabien Potencier <fabien@symfony.com> * @author Jeremy Mikola <jmikola@gmail.com> */ class LogoutUrlGenerator { private $requestStack; private $router; private $tokenStorage; private $listeners = []; /** @var string|null */ private $currentFirewallName; /** @var string|null */ private $currentFirewallContext; public function __construct(RequestStack $requestStack = null, UrlGeneratorInterface $router = null, TokenStorageInterface $tokenStorage = null) { $this->requestStack = $requestStack; $this->router = $router; $this->tokenStorage = $tokenStorage; } /** * Registers a firewall's LogoutListener, allowing its URL to be generated. * * @param string $key The firewall key * @param string $logoutPath The path that starts the logout process * @param string|null $csrfTokenId The ID of the CSRF token * @param string|null $csrfParameter The CSRF token parameter name * @param string|null $context The listener context */ public function registerListener(string $key, string $logoutPath, ?string $csrfTokenId, ?string $csrfParameter, CsrfTokenManagerInterface $csrfTokenManager = null, string $context = null) { $this->listeners[$key] = [$logoutPath, $csrfTokenId, $csrfParameter, $csrfTokenManager, $context]; } /** * Generates the absolute logout path for the firewall. * * @return string */ public function getLogoutPath(string $key = null) { return $this->generateLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_PATH); } /** * Generates the absolute logout URL for the firewall. * * @return string */ public function getLogoutUrl(string $key = null) { return $this->generateLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_URL); } public function setCurrentFirewall(?string $key, string $context = null) { $this->currentFirewallName = $key; $this->currentFirewallContext = $context; } /** * Generates the logout URL for the firewall. */ private function generateLogoutUrl(?string $key, int $referenceType): string { [$logoutPath, $csrfTokenId, $csrfParameter, $csrfTokenManager] = $this->getListener($key); if (null === $logoutPath) { throw new \LogicException('Unable to generate the logout URL without a path.'); } $parameters = null !== $csrfTokenManager ? [$csrfParameter => (string) $csrfTokenManager->getToken($csrfTokenId)] : []; if ('/' === $logoutPath[0]) { if (!$this->requestStack) { throw new \LogicException('Unable to generate the logout URL without a RequestStack.'); } $request = $this->requestStack->getCurrentRequest(); if (!$request) { throw new \LogicException('Unable to generate the logout URL without a Request.'); } $url = UrlGeneratorInterface::ABSOLUTE_URL === $referenceType ? $request->getUriForPath($logoutPath) : $request->getBaseUrl().$logoutPath; if (!empty($parameters)) { $url .= '?'.http_build_query($parameters, '', '&'); } } else { if (!$this->router) { throw new \LogicException('Unable to generate the logout URL without a Router.'); } $url = $this->router->generate($logoutPath, $parameters, $referenceType); } return $url; } /** * @throws \InvalidArgumentException if no LogoutListener is registered for the key or could not be found automatically */ private function getListener(?string $key): array { if (null !== $key) { if (isset($this->listeners[$key])) { return $this->listeners[$key]; } throw new \InvalidArgumentException(sprintf('No LogoutListener found for firewall key "%s".', $key)); } // Fetch the current provider key from token, if possible if (null !== $this->tokenStorage) { $token = $this->tokenStorage->getToken(); // @deprecated since Symfony 5.4 if ($token instanceof AnonymousToken) { throw new \InvalidArgumentException('Unable to generate a logout url for an anonymous token.'); } if (null !== $token) { if (method_exists($token, 'getFirewallName')) { $key = $token->getFirewallName(); } elseif (method_exists($token, 'getProviderKey')) { trigger_deprecation('symfony/security-http', '5.2', 'Method "%s::getProviderKey()" has been deprecated, rename it to "getFirewallName()" instead.', \get_class($token)); $key = $token->getProviderKey(); } if (isset($this->listeners[$key])) { return $this->listeners[$key]; } } } // Fetch from injected current firewall information, if possible if (isset($this->listeners[$this->currentFirewallName])) { return $this->listeners[$this->currentFirewallName]; } foreach ($this->listeners as $listener) { if (isset($listener[4]) && $this->currentFirewallContext === $listener[4]) { return $listener; } } throw new \InvalidArgumentException('Unable to find the current firewall LogoutListener, please provide the provider key manually.'); } } Logout/SessionLogoutHandler.php 0000644 00000002211 15120140656 0012635 0 ustar 00 <?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\Logout; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Http\EventListener\SessionLogoutListener; trigger_deprecation('symfony/security-http', '5.4', 'The "%s" class is deprecated, use "%s" instead.', SessionLogoutHandler::class, SessionLogoutListener::class); /** * Handler for clearing invalidating the current session. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> * * @deprecated since Symfony 5.4, use {@link SessionLogoutListener} instead */ class SessionLogoutHandler implements LogoutHandlerInterface { /** * Invalidate the current session. */ public function logout(Request $request, Response $response, TokenInterface $token) { $request->getSession()->invalidate(); } } RateLimiter/DefaultLoginRateLimiter.php 0000644 00000002736 15120140656 0014225 0 ustar 00 <?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\RateLimiter; use Symfony\Component\HttpFoundation\RateLimiter\AbstractRequestRateLimiter; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\RateLimiter\RateLimiterFactory; use Symfony\Component\Security\Core\Security; /** * A default login throttling limiter. * * This limiter prevents breadth-first attacks by enforcing * a limit on username+IP and a (higher) limit on IP. * * @author Wouter de Jong <wouter@wouterj.nl> */ final class DefaultLoginRateLimiter extends AbstractRequestRateLimiter { private $globalFactory; private $localFactory; public function __construct(RateLimiterFactory $globalFactory, RateLimiterFactory $localFactory) { $this->globalFactory = $globalFactory; $this->localFactory = $localFactory; } protected function getLimiters(Request $request): array { $username = $request->attributes->get(Security::LAST_USERNAME, ''); $username = preg_match('//u', $username) ? mb_strtolower($username, 'UTF-8') : strtolower($username); return [ $this->globalFactory->create($request->getClientIp()), $this->localFactory->create($username.'-'.$request->getClientIp()), ]; } } RememberMe/AbstractRememberMeHandler.php 0000644 00000011252 15120140656 0014300 0 ustar 00 <?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\RememberMe; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Cookie; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; /** * @author Wouter de Jong <wouter@wouterj.nl> */ abstract class AbstractRememberMeHandler implements RememberMeHandlerInterface { private $userProvider; protected $requestStack; protected $options; protected $logger; public function __construct(UserProviderInterface $userProvider, RequestStack $requestStack, array $options = [], LoggerInterface $logger = null) { $this->userProvider = $userProvider; $this->requestStack = $requestStack; $this->options = $options + [ 'name' => 'REMEMBERME', 'lifetime' => 31536000, 'path' => '/', 'domain' => null, 'secure' => false, 'httponly' => true, 'samesite' => null, 'always_remember_me' => false, 'remember_me_parameter' => '_remember_me', ]; $this->logger = $logger; } /** * Checks if the RememberMeDetails is a valid cookie to login the given User. * * This method should also: * - Create a new remember-me cookie to be sent with the response (using {@see createCookie()}); * - If you store the token somewhere else (e.g. in a database), invalidate the stored token. * * @throws AuthenticationException If the remember-me details are not accepted */ abstract protected function processRememberMe(RememberMeDetails $rememberMeDetails, UserInterface $user): void; /** * {@inheritdoc} */ public function consumeRememberMeCookie(RememberMeDetails $rememberMeDetails): UserInterface { try { // @deprecated since Symfony 5.3, change to $this->userProvider->loadUserByIdentifier() in 6.0 $method = 'loadUserByIdentifier'; if (!method_exists($this->userProvider, 'loadUserByIdentifier')) { trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($this->userProvider)); $method = 'loadUserByUsername'; } $user = $this->userProvider->$method($rememberMeDetails->getUserIdentifier()); } catch (AuthenticationException $e) { throw $e; } if (!$user instanceof UserInterface) { throw new \LogicException(sprintf('The UserProviderInterface implementation must return an instance of UserInterface, but returned "%s".', get_debug_type($user))); } $this->processRememberMe($rememberMeDetails, $user); if (null !== $this->logger) { $this->logger->info('Remember-me cookie accepted.'); } return $user; } /** * {@inheritdoc} */ public function clearRememberMeCookie(): void { if (null !== $this->logger) { $this->logger->debug('Clearing remember-me cookie.', ['name' => $this->options['name']]); } $this->createCookie(null); } /** * Creates the remember-me cookie using the correct configuration. * * @param RememberMeDetails|null $rememberMeDetails The details for the cookie, or null to clear the remember-me cookie */ protected function createCookie(?RememberMeDetails $rememberMeDetails) { $request = $this->requestStack->getMainRequest(); if (!$request) { throw new \LogicException('Cannot create the remember-me cookie; no master request available.'); } // the ResponseListener configures the cookie saved in this attribute on the final response object $request->attributes->set(ResponseListener::COOKIE_ATTR_NAME, new Cookie( $this->options['name'], $rememberMeDetails ? $rememberMeDetails->toString() : null, $rememberMeDetails ? $rememberMeDetails->getExpires() : 1, $this->options['path'], $this->options['domain'], $this->options['secure'] ?? $request->isSecure(), $this->options['httponly'], false, $this->options['samesite'] )); } } RememberMe/AbstractRememberMeServices.php 0000644 00000025053 15120140656 0014512 0 ustar 00 <?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\RememberMe; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Cookie; 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\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\CookieTheftException; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface; use Symfony\Component\Security\Http\ParameterBagUtils; trigger_deprecation('symfony/security-http', '5.4', 'The "%s" class is deprecated, use "%s" instead.', AbstractRememberMeServices::class, AbstractRememberMeHandler::class); /** * Base class implementing the RememberMeServicesInterface. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> * * @deprecated since Symfony 5.4, use {@see AbstractRememberMeHandler} instead */ abstract class AbstractRememberMeServices implements RememberMeServicesInterface, LogoutHandlerInterface { public const COOKIE_DELIMITER = ':'; protected $logger; protected $options = [ 'secure' => false, 'httponly' => true, 'samesite' => null, 'path' => null, 'domain' => null, ]; private $firewallName; private $secret; private $userProviders; /** * @throws \InvalidArgumentException */ public function __construct(iterable $userProviders, string $secret, string $firewallName, array $options = [], LoggerInterface $logger = null) { if (empty($secret)) { throw new \InvalidArgumentException('$secret must not be empty.'); } if ('' === $firewallName) { throw new \InvalidArgumentException('$firewallName must not be empty.'); } if (!\is_array($userProviders) && !$userProviders instanceof \Countable) { $userProviders = iterator_to_array($userProviders, false); } if (0 === \count($userProviders)) { throw new \InvalidArgumentException('You must provide at least one user provider.'); } $this->userProviders = $userProviders; $this->secret = $secret; $this->firewallName = $firewallName; $this->options = array_merge($this->options, $options); $this->logger = $logger; } /** * Returns the parameter that is used for checking whether remember-me * services have been requested. * * @return string */ public function getRememberMeParameter() { return $this->options['remember_me_parameter']; } /** * @return string */ public function getSecret() { return $this->secret; } /** * Implementation of RememberMeServicesInterface. Detects whether a remember-me * cookie was set, decodes it, and hands it to subclasses for further processing. * * @throws CookieTheftException * @throws \RuntimeException */ final public function autoLogin(Request $request): ?TokenInterface { if (($cookie = $request->attributes->get(self::COOKIE_ATTR_NAME)) && null === $cookie->getValue()) { return null; } if (null === $cookie = $request->cookies->get($this->options['name'])) { return null; } if (null !== $this->logger) { $this->logger->debug('Remember-me cookie detected.'); } $cookieParts = $this->decodeCookie($cookie); try { $user = $this->processAutoLoginCookie($cookieParts, $request); if (!$user instanceof UserInterface) { throw new \RuntimeException('processAutoLoginCookie() must return a UserInterface implementation.'); } if (null !== $this->logger) { $this->logger->info('Remember-me cookie accepted.'); } return new RememberMeToken($user, $this->firewallName, $this->secret); } catch (CookieTheftException $e) { $this->loginFail($request, $e); throw $e; } catch (UserNotFoundException $e) { if (null !== $this->logger) { $this->logger->info('User for remember-me cookie not found.', ['exception' => $e]); } $this->loginFail($request, $e); } catch (UnsupportedUserException $e) { if (null !== $this->logger) { $this->logger->warning('User class for remember-me cookie not supported.', ['exception' => $e]); } $this->loginFail($request, $e); } catch (AuthenticationException $e) { if (null !== $this->logger) { $this->logger->debug('Remember-Me authentication failed.', ['exception' => $e]); } $this->loginFail($request, $e); } catch (\Exception $e) { $this->loginFail($request, $e); throw $e; } return null; } /** * Implementation for LogoutHandlerInterface. Deletes the cookie. */ public function logout(Request $request, Response $response, TokenInterface $token) { $this->cancelCookie($request); } /** * Implementation for RememberMeServicesInterface. Deletes the cookie when * an attempted authentication fails. */ final public function loginFail(Request $request, \Exception $exception = null) { $this->cancelCookie($request); $this->onLoginFail($request, $exception); } /** * Implementation for RememberMeServicesInterface. This is called when an * authentication is successful. */ final public function loginSuccess(Request $request, Response $response, TokenInterface $token) { // Make sure any old remember-me cookies are cancelled $this->cancelCookie($request); if (!$token->getUser() instanceof UserInterface) { if (null !== $this->logger) { $this->logger->debug('Remember-me ignores token since it does not contain a UserInterface implementation.'); } return; } if (!$this->isRememberMeRequested($request)) { if (null !== $this->logger) { $this->logger->debug('Remember-me was not requested.'); } return; } if (null !== $this->logger) { $this->logger->debug('Remember-me was requested; setting cookie.'); } // Remove attribute from request that sets a NULL cookie. // It was set by $this->cancelCookie() // (cancelCookie does other things too for some RememberMeServices // so we should still call it at the start of this method) $request->attributes->remove(self::COOKIE_ATTR_NAME); $this->onLoginSuccess($request, $response, $token); } /** * Subclasses should validate the cookie and do any additional processing * that is required. This is called from autoLogin(). * * @return UserInterface */ abstract protected function processAutoLoginCookie(array $cookieParts, Request $request); protected function onLoginFail(Request $request, \Exception $exception = null) { } /** * This is called after a user has been logged in successfully, and has * requested remember-me capabilities. The implementation usually sets a * cookie and possibly stores a persistent record of it. */ abstract protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token); final protected function getUserProvider(string $class): UserProviderInterface { foreach ($this->userProviders as $provider) { if ($provider->supportsClass($class)) { return $provider; } } throw new UnsupportedUserException(sprintf('There is no user provider for user "%s". Shouldn\'t the "supportsClass()" method of your user provider return true for this classname?', $class)); } /** * Decodes the raw cookie value. * * @return array */ protected function decodeCookie(string $rawCookie) { return explode(self::COOKIE_DELIMITER, base64_decode($rawCookie)); } /** * Encodes the cookie parts. * * @return string * * @throws \InvalidArgumentException When $cookieParts contain the cookie delimiter. Extending class should either remove or escape it. */ protected function encodeCookie(array $cookieParts) { foreach ($cookieParts as $cookiePart) { if (str_contains($cookiePart, self::COOKIE_DELIMITER)) { throw new \InvalidArgumentException(sprintf('$cookieParts should not contain the cookie delimiter "%s".', self::COOKIE_DELIMITER)); } } return base64_encode(implode(self::COOKIE_DELIMITER, $cookieParts)); } /** * Deletes the remember-me cookie. */ protected function cancelCookie(Request $request) { if (null !== $this->logger) { $this->logger->debug('Clearing remember-me cookie.', ['name' => $this->options['name']]); } $request->attributes->set(self::COOKIE_ATTR_NAME, new Cookie($this->options['name'], null, 1, $this->options['path'], $this->options['domain'], $this->options['secure'] ?? $request->isSecure(), $this->options['httponly'], false, $this->options['samesite'])); } /** * Checks whether remember-me capabilities were requested. * * @return bool */ protected function isRememberMeRequested(Request $request) { if (true === $this->options['always_remember_me']) { return true; } $parameter = ParameterBagUtils::getRequestParameterValue($request, $this->options['remember_me_parameter']); if (null === $parameter && null !== $this->logger) { $this->logger->debug('Did not send remember-me cookie.', ['parameter' => $this->options['remember_me_parameter']]); } return 'true' === $parameter || 'on' === $parameter || '1' === $parameter || 'yes' === $parameter || true === $parameter; } } RememberMe/PersistentRememberMeHandler.php 0000644 00000012713 15120140656 0014700 0 ustar 00 <?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\RememberMe; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; use Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface; use Symfony\Component\Security\Core\Authentication\RememberMe\TokenVerifierInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\CookieTheftException; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; /** * Implements remember-me tokens using a {@see TokenProviderInterface}. * * This requires storing remember-me tokens in a database. This allows * more control over the invalidation of remember-me tokens. See * {@see SignatureRememberMeHandler} if you don't want to use a database. * * @author Wouter de Jong <wouter@wouterj.nl> */ final class PersistentRememberMeHandler extends AbstractRememberMeHandler { private $tokenProvider; private $tokenVerifier; public function __construct(TokenProviderInterface $tokenProvider, string $secret, UserProviderInterface $userProvider, RequestStack $requestStack, array $options, LoggerInterface $logger = null, TokenVerifierInterface $tokenVerifier = null) { parent::__construct($userProvider, $requestStack, $options, $logger); if (!$tokenVerifier && $tokenProvider instanceof TokenVerifierInterface) { $tokenVerifier = $tokenProvider; } $this->tokenProvider = $tokenProvider; $this->tokenVerifier = $tokenVerifier; } /** * {@inheritdoc} */ public function createRememberMeCookie(UserInterface $user): void { $series = random_bytes(66); $tokenValue = strtr(base64_encode(substr($series, 33)), '+/=', '-_~'); $series = strtr(base64_encode(substr($series, 0, 33)), '+/=', '-_~'); $token = new PersistentToken(\get_class($user), method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(), $series, $tokenValue, new \DateTime()); $this->tokenProvider->createNewToken($token); $this->createCookie(RememberMeDetails::fromPersistentToken($token, time() + $this->options['lifetime'])); } public function consumeRememberMeCookie(RememberMeDetails $rememberMeDetails): UserInterface { if (!str_contains($rememberMeDetails->getValue(), ':')) { throw new AuthenticationException('The cookie is incorrectly formatted.'); } [$series, $tokenValue] = explode(':', $rememberMeDetails->getValue()); $persistentToken = $this->tokenProvider->loadTokenBySeries($series); if ($this->tokenVerifier) { $isTokenValid = $this->tokenVerifier->verifyToken($persistentToken, $tokenValue); } else { $isTokenValid = hash_equals($persistentToken->getTokenValue(), $tokenValue); } if (!$isTokenValid) { throw new CookieTheftException('This token was already used. The account is possibly compromised.'); } if ($persistentToken->getLastUsed()->getTimestamp() + $this->options['lifetime'] < time()) { throw new AuthenticationException('The cookie has expired.'); } return parent::consumeRememberMeCookie($rememberMeDetails->withValue($persistentToken->getLastUsed()->getTimestamp().':'.$rememberMeDetails->getValue().':'.$persistentToken->getClass())); } public function processRememberMe(RememberMeDetails $rememberMeDetails, UserInterface $user): void { [$lastUsed, $series, $tokenValue, $class] = explode(':', $rememberMeDetails->getValue(), 4); $persistentToken = new PersistentToken($class, $rememberMeDetails->getUserIdentifier(), $series, $tokenValue, new \DateTime('@'.$lastUsed)); // if a token was regenerated less than a minute ago, there is no need to regenerate it // if multiple concurrent requests reauthenticate a user we do not want to update the token several times if ($persistentToken->getLastUsed()->getTimestamp() + 60 >= time()) { return; } $tokenValue = strtr(base64_encode(random_bytes(33)), '+/=', '-_~'); $tokenLastUsed = new \DateTime(); if ($this->tokenVerifier) { $this->tokenVerifier->updateExistingToken($persistentToken, $tokenValue, $tokenLastUsed); } $this->tokenProvider->updateToken($series, $tokenValue, $tokenLastUsed); $this->createCookie($rememberMeDetails->withValue($series.':'.$tokenValue)); } /** * {@inheritdoc} */ public function clearRememberMeCookie(): void { parent::clearRememberMeCookie(); $cookie = $this->requestStack->getMainRequest()->cookies->get($this->options['name']); if (null === $cookie) { return; } $rememberMeDetails = RememberMeDetails::fromRawCookie($cookie); [$series] = explode(':', $rememberMeDetails->getValue()); $this->tokenProvider->deleteTokenBySeries($series); } /** * @internal */ public function getTokenProvider(): TokenProviderInterface { return $this->tokenProvider; } } RememberMe/PersistentTokenBasedRememberMeServices.php 0000644 00000015076 15120140656 0017053 0 ustar 00 <?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\RememberMe; use Symfony\Component\HttpFoundation\Cookie; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentTokenInterface; use Symfony\Component\Security\Core\Authentication\RememberMe\TokenProviderInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\CookieTheftException; trigger_deprecation('symfony/security-http', '5.4', 'The "%s" class is deprecated, use "%s" instead.', PersistentTokenBasedRememberMeServices::class, PersistentRememberMeHandler::class); /** * Concrete implementation of the RememberMeServicesInterface which needs * an implementation of TokenProviderInterface for providing remember-me * capabilities. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> * * @deprecated since Symfony 5.4, use {@see PersistentRememberMeHandler} instead */ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices { private const HASHED_TOKEN_PREFIX = 'sha256_'; /** @var TokenProviderInterface */ private $tokenProvider; public function setTokenProvider(TokenProviderInterface $tokenProvider) { $this->tokenProvider = $tokenProvider; } /** * {@inheritdoc} */ protected function cancelCookie(Request $request) { // Delete cookie on the client parent::cancelCookie($request); // Delete cookie from the tokenProvider if (null !== ($cookie = $request->cookies->get($this->options['name'])) && 2 === \count($parts = $this->decodeCookie($cookie)) ) { [$series] = $parts; $this->tokenProvider->deleteTokenBySeries($series); } } /** * {@inheritdoc} */ protected function processAutoLoginCookie(array $cookieParts, Request $request) { if (2 !== \count($cookieParts)) { throw new AuthenticationException('The cookie is invalid.'); } [$series, $tokenValue] = $cookieParts; $persistentToken = $this->tokenProvider->loadTokenBySeries($series); if (!$this->isTokenValueValid($persistentToken, $tokenValue)) { throw new CookieTheftException('This token was already used. The account is possibly compromised.'); } if ($persistentToken->getLastUsed()->getTimestamp() + $this->options['lifetime'] < time()) { throw new AuthenticationException('The cookie has expired.'); } $tokenValue = base64_encode(random_bytes(64)); $this->tokenProvider->updateToken($series, $this->generateHash($tokenValue), new \DateTime()); $request->attributes->set(self::COOKIE_ATTR_NAME, new Cookie( $this->options['name'], $this->encodeCookie([$series, $tokenValue]), time() + $this->options['lifetime'], $this->options['path'], $this->options['domain'], $this->options['secure'] ?? $request->isSecure(), $this->options['httponly'], false, $this->options['samesite'] ) ); $userProvider = $this->getUserProvider($persistentToken->getClass()); // @deprecated since Symfony 5.3, change to $persistentToken->getUserIdentifier() in 6.0 if (method_exists($persistentToken, 'getUserIdentifier')) { $userIdentifier = $persistentToken->getUserIdentifier(); } else { trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "getUserIdentifier()" in persistent token "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($persistentToken)); $userIdentifier = $persistentToken->getUsername(); } // @deprecated since Symfony 5.3, change to $userProvider->loadUserByIdentifier() in 6.0 if (method_exists($userProvider, 'loadUserByIdentifier')) { return $userProvider->loadUserByIdentifier($userIdentifier); } else { trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($userProvider)); return $userProvider->loadUserByUsername($userIdentifier); } } /** * {@inheritdoc} */ protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token) { $series = base64_encode(random_bytes(64)); $tokenValue = base64_encode(random_bytes(64)); $this->tokenProvider->createNewToken( new PersistentToken( \get_class($user = $token->getUser()), // @deprecated since Symfony 5.3, change to $user->getUserIdentifier() in 6.0 method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(), $series, $this->generateHash($tokenValue), new \DateTime() ) ); $response->headers->setCookie( new Cookie( $this->options['name'], $this->encodeCookie([$series, $tokenValue]), time() + $this->options['lifetime'], $this->options['path'], $this->options['domain'], $this->options['secure'] ?? $request->isSecure(), $this->options['httponly'], false, $this->options['samesite'] ) ); } private function generateHash(string $tokenValue): string { return self::HASHED_TOKEN_PREFIX.hash_hmac('sha256', $tokenValue, $this->getSecret()); } private function isTokenValueValid(PersistentTokenInterface $persistentToken, string $tokenValue): bool { if (0 === strpos($persistentToken->getTokenValue(), self::HASHED_TOKEN_PREFIX)) { return hash_equals($persistentToken->getTokenValue(), $this->generateHash($tokenValue)); } return hash_equals($persistentToken->getTokenValue(), $tokenValue); } } RememberMe/RememberMeDetails.php 0000644 00000005377 15120140657 0012640 0 ustar 00 <?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\RememberMe; use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; use Symfony\Component\Security\Core\Exception\AuthenticationException; /** * @author Wouter de Jong <wouter@wouterj.nl> */ class RememberMeDetails { public const COOKIE_DELIMITER = ':'; private $userFqcn; private $userIdentifier; private $expires; private $value; public function __construct(string $userFqcn, string $userIdentifier, int $expires, string $value) { $this->userFqcn = $userFqcn; $this->userIdentifier = $userIdentifier; $this->expires = $expires; $this->value = $value; } public static function fromRawCookie(string $rawCookie): self { if (!str_contains($rawCookie, self::COOKIE_DELIMITER)) { $rawCookie = base64_decode($rawCookie); } $cookieParts = explode(self::COOKIE_DELIMITER, $rawCookie, 4); if (4 !== \count($cookieParts)) { throw new AuthenticationException('The cookie contains invalid data.'); } if (false === $cookieParts[1] = base64_decode(strtr($cookieParts[1], '-_~', '+/='), true)) { throw new AuthenticationException('The user identifier contains a character from outside the base64 alphabet.'); } $cookieParts[0] = strtr($cookieParts[0], '.', '\\'); return new static(...$cookieParts); } public static function fromPersistentToken(PersistentToken $persistentToken, int $expires): self { return new static($persistentToken->getClass(), $persistentToken->getUserIdentifier(), $expires, $persistentToken->getSeries().':'.$persistentToken->getTokenValue()); } public function withValue(string $value): self { $details = clone $this; $details->value = $value; return $details; } public function getUserFqcn(): string { return $this->userFqcn; } public function getUserIdentifier(): string { return $this->userIdentifier; } public function getExpires(): int { return $this->expires; } public function getValue(): string { return $this->value; } public function toString(): string { // $userIdentifier is encoded because it might contain COOKIE_DELIMITER, we assume other values don't return implode(self::COOKIE_DELIMITER, [strtr($this->userFqcn, '\\', '.'), strtr(base64_encode($this->userIdentifier), '+/=', '-_~'), $this->expires, $this->value]); } } RememberMe/RememberMeHandlerInterface.php 0000644 00000003336 15120140657 0014442 0 ustar 00 <?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\RememberMe; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\User\UserInterface; /** * Handles creating and validating remember-me cookies. * * If you want to add a custom implementation, you want to extend from * {@see AbstractRememberMeHandler} instead. * * @author Wouter de Jong <wouter@wouterj.nl> */ interface RememberMeHandlerInterface { /** * Creates a remember-me cookie. * * The actual cookie should be set as an attribute on the main request, * which is transformed into a response cookie by {@see ResponseListener}. */ public function createRememberMeCookie(UserInterface $user): void; /** * Validates the remember-me cookie and returns the associated User. * * Every cookie should only be used once. This means that this method should also: * - Create a new remember-me cookie to be sent with the response (using the * {@see ResponseListener::COOKIE_ATTR_NAME} request attribute); * - If you store the token somewhere else (e.g. in a database), invalidate the * stored token. * * @throws AuthenticationException */ public function consumeRememberMeCookie(RememberMeDetails $rememberMeDetails): UserInterface; /** * Clears the remember-me cookie. * * This should set a cookie with a `null` value on the request attribute. */ public function clearRememberMeCookie(): void; } RememberMe/RememberMeServicesInterface.php 0000644 00000006136 15120140657 0014651 0 ustar 00 <?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\RememberMe; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; trigger_deprecation('symfony/security-http', '5.4', 'The "%s" interface is deprecated, use "%s" instead.', RememberMeServicesInterface::class, RememberMeHandlerInterface::class); /** * Interface that needs to be implemented by classes which provide remember-me * capabilities. * * We provide two implementations out-of-the-box: * - TokenBasedRememberMeServices (does not require a TokenProvider) * - PersistentTokenBasedRememberMeServices (requires a TokenProvider) * * @author Johannes M. Schmitt <schmittjoh@gmail.com> * * @method logout(Request $request, Response $response, TokenInterface $token) * * @deprecated since Symfony 5.4, use {@see RememberMeHandlerInterface} instead */ interface RememberMeServicesInterface { /** * This attribute name can be used by the implementation if it needs to set * a cookie on the Request when there is no actual Response, yet. */ public const COOKIE_ATTR_NAME = '_security_remember_me_cookie'; /** * This method will be called whenever the TokenStorage does not contain * a TokenInterface object and the framework wishes to provide an implementation * with an opportunity to authenticate the request using remember-me capabilities. * * No attempt whatsoever is made to determine whether the browser has requested * remember-me services or presented a valid cookie. Any and all such determinations * are left to the implementation of this method. * * If a browser has presented an unauthorised cookie for whatever reason, * make sure to throw an AuthenticationException as this will consequentially * result in a call to loginFail() and therefore an invalidation of the cookie. * * @return TokenInterface|null */ public function autoLogin(Request $request); /** * Called whenever an interactive authentication attempt was made, but the * credentials supplied by the user were missing or otherwise invalid. * * This method needs to take care of invalidating the cookie. */ public function loginFail(Request $request, \Exception $exception = null); /** * Called whenever an interactive authentication attempt is successful * (e.g. a form login). * * An implementation may always set a remember-me cookie in the Response, * although this is not recommended. * * Instead, implementations should typically look for a request parameter * (such as an HTTP POST parameter) that indicates the browser has explicitly * requested for the authentication to be remembered. */ public function loginSuccess(Request $request, Response $response, TokenInterface $token); } RememberMe/ResponseListener.php 0000644 00000002645 15120140657 0012611 0 ustar 00 <?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\RememberMe; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\ResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; /** * Adds remember-me cookies to the Response. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> * * @final */ class ResponseListener implements EventSubscriberInterface { /** * This attribute name can be used by the implementation if it needs to set * a cookie on the Request when there is no actual Response, yet. */ public const COOKIE_ATTR_NAME = '_security_remember_me_cookie'; public function onKernelResponse(ResponseEvent $event) { if (!$event->isMainRequest()) { return; } $request = $event->getRequest(); $response = $event->getResponse(); if ($request->attributes->has(self::COOKIE_ATTR_NAME)) { $response->headers->setCookie($request->attributes->get(self::COOKIE_ATTR_NAME)); } } /** * {@inheritdoc} */ public static function getSubscribedEvents(): array { return [KernelEvents::RESPONSE => 'onKernelResponse']; } } RememberMe/SignatureRememberMeHandler.php 0000644 00000006334 15120140657 0014504 0 ustar 00 <?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\RememberMe; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Signature\Exception\ExpiredSignatureException; use Symfony\Component\Security\Core\Signature\Exception\InvalidSignatureException; use Symfony\Component\Security\Core\Signature\SignatureHasher; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; /** * Implements safe remember-me cookies using the {@see SignatureHasher}. * * This handler doesn't require a database for the remember-me tokens. * However, it cannot invalidate a specific user session, all sessions for * that user will be invalidated instead. Use {@see PersistentRememberMeHandler} * if you need this. * * @author Wouter de Jong <wouter@wouterj.nl> */ final class SignatureRememberMeHandler extends AbstractRememberMeHandler { private $signatureHasher; public function __construct(SignatureHasher $signatureHasher, UserProviderInterface $userProvider, RequestStack $requestStack, array $options, LoggerInterface $logger = null) { parent::__construct($userProvider, $requestStack, $options, $logger); $this->signatureHasher = $signatureHasher; } /** * {@inheritdoc} */ public function createRememberMeCookie(UserInterface $user): void { $expires = time() + $this->options['lifetime']; $value = $this->signatureHasher->computeSignatureHash($user, $expires); $details = new RememberMeDetails(\get_class($user), method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(), $expires, $value); $this->createCookie($details); } public function consumeRememberMeCookie(RememberMeDetails $rememberMeDetails): UserInterface { try { $this->signatureHasher->acceptSignatureHash($rememberMeDetails->getUserIdentifier(), $rememberMeDetails->getExpires(), $rememberMeDetails->getValue()); } catch (InvalidSignatureException $e) { throw new AuthenticationException('The cookie\'s hash is invalid.', 0, $e); } catch (ExpiredSignatureException $e) { throw new AuthenticationException('The cookie has expired.', 0, $e); } return parent::consumeRememberMeCookie($rememberMeDetails); } public function processRememberMe(RememberMeDetails $rememberMeDetails, UserInterface $user): void { try { $this->signatureHasher->verifySignatureHash($user, $rememberMeDetails->getExpires(), $rememberMeDetails->getValue()); } catch (InvalidSignatureException $e) { throw new AuthenticationException('The cookie\'s hash is invalid.', 0, $e); } catch (ExpiredSignatureException $e) { throw new AuthenticationException('The cookie has expired.', 0, $e); } $this->createRememberMeCookie($user); } } RememberMe/TokenBasedRememberMeServices.php 0000644 00000012460 15120140657 0014765 0 ustar 00 <?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\RememberMe; use Symfony\Component\HttpFoundation\Cookie; 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\User\UserInterface; trigger_deprecation('symfony/security-http', '5.4', 'The "%s" class is deprecated, use "%s" instead.', TokenBasedRememberMeServices::class, SignatureRememberMeHandler::class); /** * Concrete implementation of the RememberMeServicesInterface providing * remember-me capabilities without requiring a TokenProvider. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> * * @deprecated since Symfony 5.4, use {@see SignatureRememberMeHandler} instead */ class TokenBasedRememberMeServices extends AbstractRememberMeServices { /** * {@inheritdoc} */ protected function processAutoLoginCookie(array $cookieParts, Request $request) { if (4 !== \count($cookieParts)) { throw new AuthenticationException('The cookie is invalid.'); } [$class, $userIdentifier, $expires, $hash] = $cookieParts; if (false === $userIdentifier = base64_decode($userIdentifier, true)) { throw new AuthenticationException('$userIdentifier contains a character from outside the base64 alphabet.'); } try { $userProvider = $this->getUserProvider($class); // @deprecated since Symfony 5.3, change to $userProvider->loadUserByIdentifier() in 6.0 if (method_exists($userProvider, 'loadUserByIdentifier')) { $user = $userProvider->loadUserByIdentifier($userIdentifier); } else { trigger_deprecation('symfony/security-core', '5.3', 'Not implementing method "loadUserByIdentifier()" in user provider "%s" is deprecated. This method will replace "loadUserByUsername()" in Symfony 6.0.', get_debug_type($userProvider)); $user = $userProvider->loadUserByUsername($userIdentifier); } } catch (\Exception $e) { if (!$e instanceof AuthenticationException) { $e = new AuthenticationException($e->getMessage(), $e->getCode(), $e); } throw $e; } if (!$user instanceof UserInterface) { throw new \RuntimeException(sprintf('The UserProviderInterface implementation must return an instance of UserInterface, but returned "%s".', get_debug_type($user))); } if (true !== hash_equals($this->generateCookieHash($class, $userIdentifier, $expires, $user->getPassword()), $hash)) { throw new AuthenticationException('The cookie\'s hash is invalid.'); } if ($expires < time()) { throw new AuthenticationException('The cookie has expired.'); } return $user; } /** * {@inheritdoc} */ protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token) { $user = $token->getUser(); $expires = time() + $this->options['lifetime']; // @deprecated since Symfony 5.3, change to $user->getUserIdentifier() in 6.0 $value = $this->generateCookieValue(\get_class($user), method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(), $expires, $user->getPassword()); $response->headers->setCookie( new Cookie( $this->options['name'], $value, $expires, $this->options['path'], $this->options['domain'], $this->options['secure'] ?? $request->isSecure(), $this->options['httponly'], false, $this->options['samesite'] ) ); } /** * Generates the cookie value. * * @param int $expires The Unix timestamp when the cookie expires * @param string|null $password The encoded password * * @return string */ protected function generateCookieValue(string $class, string $userIdentifier, int $expires, ?string $password) { // $userIdentifier is encoded because it might contain COOKIE_DELIMITER, // we assume other values don't return $this->encodeCookie([ $class, base64_encode($userIdentifier), $expires, $this->generateCookieHash($class, $userIdentifier, $expires, $password), ]); } /** * Generates a hash for the cookie to ensure it is not being tampered with. * * @param int $expires The Unix timestamp when the cookie expires * @param string|null $password The encoded password * * @return string */ protected function generateCookieHash(string $class, string $userIdentifier, int $expires, ?string $password) { return hash_hmac('sha256', $class.self::COOKIE_DELIMITER.$userIdentifier.self::COOKIE_DELIMITER.$expires.self::COOKIE_DELIMITER.$password, $this->getSecret()); } } Session/SessionAuthenticationStrategy.php 0000644 00000004004 15120140657 0014745 0 ustar 00 <?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\Session; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Csrf\TokenStorage\ClearableTokenStorageInterface; /** * The default session strategy implementation. * * Supports the following strategies: * NONE: the session is not changed * MIGRATE: the session id is updated, attributes are kept * INVALIDATE: the session id is updated, attributes are lost * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ class SessionAuthenticationStrategy implements SessionAuthenticationStrategyInterface { public const NONE = 'none'; public const MIGRATE = 'migrate'; public const INVALIDATE = 'invalidate'; private $strategy; private $csrfTokenStorage = null; public function __construct(string $strategy, ClearableTokenStorageInterface $csrfTokenStorage = null) { $this->strategy = $strategy; if (self::MIGRATE === $strategy) { $this->csrfTokenStorage = $csrfTokenStorage; } } /** * {@inheritdoc} */ public function onAuthentication(Request $request, TokenInterface $token) { switch ($this->strategy) { case self::NONE: return; case self::MIGRATE: $request->getSession()->migrate(true); if ($this->csrfTokenStorage) { $this->csrfTokenStorage->clear(); } return; case self::INVALIDATE: $request->getSession()->invalidate(); return; default: throw new \RuntimeException(sprintf('Invalid session authentication strategy "%s".', $this->strategy)); } } } Session/SessionAuthenticationStrategyInterface.php 0000644 00000001775 15120140657 0016602 0 ustar 00 <?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\Session; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; /** * SessionAuthenticationStrategyInterface. * * Implementation are responsible for updating the session after an interactive * authentication attempt was successful. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ interface SessionAuthenticationStrategyInterface { /** * This performs any necessary changes to the session. * * This method should be called before the TokenStorage is populated with a * Token. It should be used by authentication listeners when a session is used. */ public function onAuthentication(Request $request, TokenInterface $token); } Util/TargetPathTrait.php 0000644 00000002455 15120140657 0011250 0 ustar 00 <?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\Util; use Symfony\Component\HttpFoundation\Session\SessionInterface; /** * Trait to get (and set) the URL the user last visited before being forced to authenticate. */ trait TargetPathTrait { /** * Sets the target path the user should be redirected to after authentication. * * Usually, you do not need to set this directly. */ private function saveTargetPath(SessionInterface $session, string $firewallName, string $uri) { $session->set('_security.'.$firewallName.'.target_path', $uri); } /** * Returns the URL (if any) the user visited that forced them to login. */ private function getTargetPath(SessionInterface $session, string $firewallName): ?string { return $session->get('_security.'.$firewallName.'.target_path'); } /** * Removes the target path from the session. */ private function removeTargetPath(SessionInterface $session, string $firewallName) { $session->remove('_security.'.$firewallName.'.target_path'); } } AccessMap.php 0000644 00000002521 15120140657 0007115 0 ustar 00 <?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; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestMatcherInterface; /** * AccessMap allows configuration of different access control rules for * specific parts of the website. * * @author Fabien Potencier <fabien@symfony.com> */ class AccessMap implements AccessMapInterface { private $map = []; /** * @param array $attributes An array of attributes to pass to the access decision manager (like roles) * @param string|null $channel The channel to enforce (http, https, or null) */ public function add(RequestMatcherInterface $requestMatcher, array $attributes = [], string $channel = null) { $this->map[] = [$requestMatcher, $attributes, $channel]; } /** * {@inheritdoc} */ public function getPatterns(Request $request) { foreach ($this->map as $elements) { if (null === $elements[0] || $elements[0]->matches($request)) { return [$elements[1], $elements[2]]; } } return [null, null]; } } AccessMapInterface.php 0000644 00000001505 15120140657 0010737 0 ustar 00 <?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; use Symfony\Component\HttpFoundation\Request; /** * AccessMap allows configuration of different access control rules for * specific parts of the website. * * @author Fabien Potencier <fabien@symfony.com> * @author Kris Wallsmith <kris@symfony.com> */ interface AccessMapInterface { /** * Returns security attributes and required channel for the supplied request. * * @return array{0: array|null, 1: string|null} A tuple of security attributes and the required channel */ public function getPatterns(Request $request); } CHANGELOG.md 0000644 00000002465 15120140657 0006365 0 ustar 00 CHANGELOG ========= 5.4 --- * Deprecate the `$authenticationEntryPoint` argument of `ChannelListener`, and add `$httpPort` and `$httpsPort` arguments * Deprecate `RetryAuthenticationEntryPoint`, this code is now inlined in the `ChannelListener` * Deprecate `FormAuthenticationEntryPoint` and `BasicAuthenticationEntryPoint`, in the new system the `FormLoginAuthenticator` and `HttpBasicAuthenticator` should be used instead * Deprecate `AbstractRememberMeServices`, `PersistentTokenBasedRememberMeServices`, `RememberMeServicesInterface`, `TokenBasedRememberMeServices`, use the remember me handler alternatives instead * Deprecate the `$authManager` argument of `AccessListener` * Deprecate not setting the `$exceptionOnNoToken` argument of `AccessListener` to `false` * Deprecate `DeauthenticatedEvent`, use `TokenDeauthenticatedEvent` instead * Deprecate `CookieClearingLogoutHandler`, `SessionLogoutHandler` and `CsrfTokenClearingLogoutHandler`. Use `CookieClearingLogoutListener`, `SessionLogoutListener` and `CsrfTokenClearingLogoutListener` instead * Deprecate `PassportInterface`, `UserPassportInterface` and `PassportTrait`, use `Passport` instead 5.3 --- The CHANGELOG for version 5.3 and earlier can be found at https://github.com/symfony/symfony/blob/5.3/src/Symfony/Component/Security/CHANGELOG.md Firewall.php 0000644 00000010133 15120140657 0007021 0 ustar 00 <?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; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\FinishRequestEvent; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\Security\Http\Firewall\ExceptionListener; use Symfony\Component\Security\Http\Firewall\FirewallListenerInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * Firewall uses a FirewallMap to register security listeners for the given * request. * * It allows for different security strategies within the same application * (a Basic authentication for the /api, and a web based authentication for * everything else for instance). * * @author Fabien Potencier <fabien@symfony.com> */ class Firewall implements EventSubscriberInterface { private $map; private $dispatcher; /** * @var \SplObjectStorage<Request, ExceptionListener> */ private $exceptionListeners; public function __construct(FirewallMapInterface $map, EventDispatcherInterface $dispatcher) { $this->map = $map; $this->dispatcher = $dispatcher; $this->exceptionListeners = new \SplObjectStorage(); } public function onKernelRequest(RequestEvent $event) { if (!$event->isMainRequest()) { return; } // register listeners for this firewall $listeners = $this->map->getListeners($event->getRequest()); $authenticationListeners = $listeners[0]; $exceptionListener = $listeners[1]; $logoutListener = $listeners[2]; if (null !== $exceptionListener) { $this->exceptionListeners[$event->getRequest()] = $exceptionListener; $exceptionListener->register($this->dispatcher); } // Authentication listeners are pre-sorted by SortFirewallListenersPass $authenticationListeners = function () use ($authenticationListeners, $logoutListener) { if (null !== $logoutListener) { $logoutListenerPriority = $this->getListenerPriority($logoutListener); } foreach ($authenticationListeners as $listener) { $listenerPriority = $this->getListenerPriority($listener); // Yielding the LogoutListener at the correct position if (null !== $logoutListener && $listenerPriority < $logoutListenerPriority) { yield $logoutListener; $logoutListener = null; } yield $listener; } // When LogoutListener has the lowest priority of all listeners if (null !== $logoutListener) { yield $logoutListener; } }; $this->callListeners($event, $authenticationListeners()); } public function onKernelFinishRequest(FinishRequestEvent $event) { $request = $event->getRequest(); if (isset($this->exceptionListeners[$request])) { $this->exceptionListeners[$request]->unregister($this->dispatcher); unset($this->exceptionListeners[$request]); } } /** * {@inheritdoc} */ public static function getSubscribedEvents() { return [ KernelEvents::REQUEST => ['onKernelRequest', 8], KernelEvents::FINISH_REQUEST => 'onKernelFinishRequest', ]; } protected function callListeners(RequestEvent $event, iterable $listeners) { foreach ($listeners as $listener) { $listener($event); if ($event->hasResponse()) { break; } } } private function getListenerPriority(object $logoutListener): int { return $logoutListener instanceof FirewallListenerInterface ? $logoutListener->getPriority() : 0; } } FirewallMap.php 0000644 00000003031 15120140657 0007456 0 ustar 00 <?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; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestMatcherInterface; use Symfony\Component\Security\Http\Firewall\ExceptionListener; use Symfony\Component\Security\Http\Firewall\LogoutListener; /** * FirewallMap allows configuration of different firewalls for specific parts * of the website. * * @author Fabien Potencier <fabien@symfony.com> */ class FirewallMap implements FirewallMapInterface { /** * @var list<array{RequestMatcherInterface, list<callable>, ExceptionListener|null, LogoutListener|null}> */ private $map = []; /** * @param list<callable> $listeners */ public function add(RequestMatcherInterface $requestMatcher = null, array $listeners = [], ExceptionListener $exceptionListener = null, LogoutListener $logoutListener = null) { $this->map[] = [$requestMatcher, $listeners, $exceptionListener, $logoutListener]; } /** * {@inheritdoc} */ public function getListeners(Request $request) { foreach ($this->map as $elements) { if (null === $elements[0] || $elements[0]->matches($request)) { return [$elements[1], $elements[2], $elements[3]]; } } return [[], null, null]; } } FirewallMapInterface.php 0000644 00000002245 15120140657 0011305 0 ustar 00 <?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; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Http\Firewall\ExceptionListener; use Symfony\Component\Security\Http\Firewall\LogoutListener; /** * This interface must be implemented by firewall maps. * * @author Johannes M. Schmitt <schmittjoh@gmail.com> */ interface FirewallMapInterface { /** * Returns the authentication listeners, and the exception listener to use * for the given request. * * If there are no authentication listeners, the first inner array must be * empty. * * If there is no exception listener, the second element of the outer array * must be null. * * If there is no logout listener, the third element of the outer array * must be null. * * @return array{iterable<mixed, callable>, ExceptionListener, LogoutListener} */ public function getListeners(Request $request); } HttpUtils.php 0000644 00000016144 15120140657 0007224 0 ustar 00 <?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; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Exception\MethodNotAllowedException; use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\Matcher\RequestMatcherInterface; use Symfony\Component\Routing\Matcher\UrlMatcherInterface; use Symfony\Component\Security\Core\Security; /** * Encapsulates the logic needed to create sub-requests, redirect the user, and match URLs. * * @author Fabien Potencier <fabien@symfony.com> */ class HttpUtils { private $urlGenerator; private $urlMatcher; private $domainRegexp; private $secureDomainRegexp; /** * @param UrlMatcherInterface|RequestMatcherInterface $urlMatcher The URL or Request matcher * @param string|null $domainRegexp A regexp the target of HTTP redirections must match, scheme included * @param string|null $secureDomainRegexp A regexp the target of HTTP redirections must match when the scheme is "https" * * @throws \InvalidArgumentException */ public function __construct(UrlGeneratorInterface $urlGenerator = null, $urlMatcher = null, string $domainRegexp = null, string $secureDomainRegexp = null) { $this->urlGenerator = $urlGenerator; if (null !== $urlMatcher && !$urlMatcher instanceof UrlMatcherInterface && !$urlMatcher instanceof RequestMatcherInterface) { throw new \InvalidArgumentException('Matcher must either implement UrlMatcherInterface or RequestMatcherInterface.'); } $this->urlMatcher = $urlMatcher; $this->domainRegexp = $domainRegexp; $this->secureDomainRegexp = $secureDomainRegexp; } /** * Creates a redirect Response. * * @param string $path A path (an absolute path (/foo), an absolute URL (http://...), or a route name (foo)) * @param int $status The status code * * @return RedirectResponse */ public function createRedirectResponse(Request $request, string $path, int $status = 302) { if (null !== $this->secureDomainRegexp && 'https' === $this->urlMatcher->getContext()->getScheme() && preg_match('#^https?:[/\\\\]{2,}+[^/]++#i', $path, $host) && !preg_match(sprintf($this->secureDomainRegexp, preg_quote($request->getHttpHost())), $host[0])) { $path = '/'; } if (null !== $this->domainRegexp && preg_match('#^https?:[/\\\\]{2,}+[^/]++#i', $path, $host) && !preg_match(sprintf($this->domainRegexp, preg_quote($request->getHttpHost())), $host[0])) { $path = '/'; } return new RedirectResponse($this->generateUri($request, $path), $status); } /** * Creates a Request. * * @param string $path A path (an absolute path (/foo), an absolute URL (http://...), or a route name (foo)) * * @return Request */ public function createRequest(Request $request, string $path) { $newRequest = Request::create($this->generateUri($request, $path), 'get', [], $request->cookies->all(), [], $request->server->all()); static $setSession; if (null === $setSession) { $setSession = \Closure::bind(static function ($newRequest, $request) { $newRequest->session = $request->session; }, null, Request::class); } $setSession($newRequest, $request); if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) { $newRequest->attributes->set(Security::AUTHENTICATION_ERROR, $request->attributes->get(Security::AUTHENTICATION_ERROR)); } if ($request->attributes->has(Security::ACCESS_DENIED_ERROR)) { $newRequest->attributes->set(Security::ACCESS_DENIED_ERROR, $request->attributes->get(Security::ACCESS_DENIED_ERROR)); } if ($request->attributes->has(Security::LAST_USERNAME)) { $newRequest->attributes->set(Security::LAST_USERNAME, $request->attributes->get(Security::LAST_USERNAME)); } if ($request->get('_format')) { $newRequest->attributes->set('_format', $request->get('_format')); } if ($request->getDefaultLocale() !== $request->getLocale()) { $newRequest->setLocale($request->getLocale()); } return $newRequest; } /** * Checks that a given path matches the Request. * * @param string $path A path (an absolute path (/foo), an absolute URL (http://...), or a route name (foo)) * * @return bool true if the path is the same as the one from the Request, false otherwise */ public function checkRequestPath(Request $request, string $path) { if ('/' !== $path[0]) { try { // matching a request is more powerful than matching a URL path + context, so try that first if ($this->urlMatcher instanceof RequestMatcherInterface) { $parameters = $this->urlMatcher->matchRequest($request); } else { $parameters = $this->urlMatcher->match($request->getPathInfo()); } return isset($parameters['_route']) && $path === $parameters['_route']; } catch (MethodNotAllowedException $e) { return false; } catch (ResourceNotFoundException $e) { return false; } } return $path === rawurldecode($request->getPathInfo()); } /** * Generates a URI, based on the given path or absolute URL. * * @param string $path A path (an absolute path (/foo), an absolute URL (http://...), or a route name (foo)) * * @return string * * @throws \LogicException */ public function generateUri(Request $request, string $path) { if (str_starts_with($path, 'http') || !$path) { return $path; } if ('/' === $path[0]) { return $request->getUriForPath($path); } if (null === $this->urlGenerator) { throw new \LogicException('You must provide a UrlGeneratorInterface instance to be able to use routes.'); } $url = $this->urlGenerator->generate($path, $request->attributes->all(), UrlGeneratorInterface::ABSOLUTE_URL); // unnecessary query string parameters must be removed from URL // (ie. query parameters that are presents in $attributes) // fortunately, they all are, so we have to remove entire query string $position = strpos($url, '?'); if (false !== $position) { $fragment = parse_url($url, \PHP_URL_FRAGMENT); $url = substr($url, 0, $position); // fragment must be preserved if ($fragment) { $url .= "#$fragment"; } } return $url; } } LICENSE 0000644 00000002054 15120140657 0005553 0 ustar 00 Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ParameterBagUtils.php 0000644 00000004707 15120140657 0010641 0 ustar 00 <?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; use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\PropertyAccess\Exception\AccessException; use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException; use Symfony\Component\PropertyAccess\PropertyAccess; /** * @internal */ final class ParameterBagUtils { private static $propertyAccessor; /** * Returns a "parameter" value. * * Paths like foo[bar] will be evaluated to find deeper items in nested data structures. * * @return mixed * * @throws InvalidArgumentException when the given path is malformed */ public static function getParameterBagValue(ParameterBag $parameters, string $path) { if (false === $pos = strpos($path, '[')) { return $parameters->all()[$path] ?? null; } $root = substr($path, 0, $pos); if (null === $value = $parameters->all()[$root] ?? null) { return null; } if (null === self::$propertyAccessor) { self::$propertyAccessor = PropertyAccess::createPropertyAccessor(); } try { return self::$propertyAccessor->getValue($value, substr($path, $pos)); } catch (AccessException $e) { return null; } } /** * Returns a request "parameter" value. * * Paths like foo[bar] will be evaluated to find deeper items in nested data structures. * * @return mixed * * @throws InvalidArgumentException when the given path is malformed */ public static function getRequestParameterValue(Request $request, string $path) { if (false === $pos = strpos($path, '[')) { return $request->get($path); } $root = substr($path, 0, $pos); if (null === $value = $request->get($root)) { return null; } if (null === self::$propertyAccessor) { self::$propertyAccessor = PropertyAccess::createPropertyAccessor(); } try { return self::$propertyAccessor->getValue($value, substr($path, $pos)); } catch (AccessException $e) { return null; } } } README.md 0000644 00000002243 15120140657 0006025 0 ustar 00 Security Component - HTTP Integration ===================================== The Security HTTP component provides an HTTP integration of the Security Core component. It allows securing (parts of) your application using firewalls and provides authenticators to authenticate visitors. Getting Started --------------- ``` $ composer require symfony/security-http ``` Sponsor ------- The Security component for Symfony 5.4/6.0 is [backed][1] by [SymfonyCasts][2]. Learn Symfony faster by watching real projects being built and actively coding along with them. SymfonyCasts bridges that learning gap, bringing you video tutorials and coding challenges. Code on! Help Symfony by [sponsoring][3] its development! Resources --------- * [Documentation](https://symfony.com/doc/current/components/security.html) * [Contributing](https://symfony.com/doc/current/contributing/index.html) * [Report issues](https://github.com/symfony/symfony/issues) and [send Pull Requests](https://github.com/symfony/symfony/pulls) in the [main Symfony repository](https://github.com/symfony/symfony) [1]: https://symfony.com/backers [2]: https://symfonycasts.com [3]: https://symfony.com/sponsor SecurityEvents.php 0000644 00000002665 15120140657 0010263 0 ustar 00 <?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; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\Event\SwitchUserEvent; final class SecurityEvents { /** * The INTERACTIVE_LOGIN event occurs after a user has actively logged * into your website. It is important to distinguish this action from * non-interactive authentication methods, such as: * - authentication based on your session. * - authentication using an HTTP basic or HTTP digest header. * * @Event("Symfony\Component\Security\Http\Event\InteractiveLoginEvent") */ public const INTERACTIVE_LOGIN = 'security.interactive_login'; /** * The SWITCH_USER event occurs before switch to another user and * before exit from an already switched user. * * @Event("Symfony\Component\Security\Http\Event\SwitchUserEvent") */ public const SWITCH_USER = 'security.switch_user'; /** * Event aliases. * * These aliases can be consumed by RegisterListenersPass. */ public const ALIASES = [ InteractiveLoginEvent::class => self::INTERACTIVE_LOGIN, SwitchUserEvent::class => self::SWITCH_USER, ]; } composer.json 0000644 00000003300 15120140657 0007263 0 ustar 00 { "name": "symfony/security-http", "type": "library", "description": "Symfony Security Component - HTTP Integration", "keywords": [], "homepage": "https://symfony.com", "license": "MIT", "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "require": { "php": ">=7.2.5", "symfony/deprecation-contracts": "^2.1|^3", "symfony/security-core": "^5.4.19|~6.0.19|~6.1.11|^6.2.5", "symfony/http-foundation": "^5.3|^6.0", "symfony/http-kernel": "^5.3|^6.0", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php80": "^1.16", "symfony/property-access": "^4.4|^5.0|^6.0" }, "require-dev": { "symfony/cache": "^4.4|^5.0|^6.0", "symfony/rate-limiter": "^5.2|^6.0", "symfony/routing": "^4.4|^5.0|^6.0", "symfony/security-csrf": "^4.4|^5.0|^6.0", "symfony/translation": "^4.4|^5.0|^6.0", "psr/log": "^1|^2|^3" }, "conflict": { "symfony/event-dispatcher": "<4.3", "symfony/security-bundle": "<5.3", "symfony/security-csrf": "<4.4" }, "suggest": { "symfony/security-csrf": "For using tokens to protect authentication/logout attempts", "symfony/routing": "For using the HttpUtils class to create sub-requests, redirect the user, and match URLs" }, "autoload": { "psr-4": { "Symfony\\Component\\Security\\Http\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "minimum-stability": "dev" }
Coded With 💗 by
0x6ick