Deprecated: Constant E_STRICT is deprecated in /home/pastorz/old-espace-client/vendor/symfony/error-handler/ErrorHandler.php on line 58

Deprecated: Constant E_STRICT is deprecated in /home/pastorz/old-espace-client/vendor/symfony/error-handler/ErrorHandler.php on line 76
Symfony Profiler

vendor/symfonycasts/reset-password-bundle/src/ResetPasswordHelper.php line 162

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the SymfonyCasts ResetPasswordBundle package.
  4.  * Copyright (c) SymfonyCasts <https://symfonycasts.com/>
  5.  * For the full copyright and license information, please view the LICENSE
  6.  * file that was distributed with this source code.
  7.  */
  8. namespace SymfonyCasts\Bundle\ResetPassword;
  9. use SymfonyCasts\Bundle\ResetPassword\Exception\ExpiredResetPasswordTokenException;
  10. use SymfonyCasts\Bundle\ResetPassword\Exception\InvalidResetPasswordTokenException;
  11. use SymfonyCasts\Bundle\ResetPassword\Exception\TooManyPasswordRequestsException;
  12. use SymfonyCasts\Bundle\ResetPassword\Generator\ResetPasswordTokenGenerator;
  13. use SymfonyCasts\Bundle\ResetPassword\Model\ResetPasswordRequestInterface;
  14. use SymfonyCasts\Bundle\ResetPassword\Model\ResetPasswordToken;
  15. use SymfonyCasts\Bundle\ResetPassword\Persistence\ResetPasswordRequestRepositoryInterface;
  16. use SymfonyCasts\Bundle\ResetPassword\Util\ResetPasswordCleaner;
  17. /**
  18.  * @author Jesse Rushlow <jr@rushlow.dev>
  19.  * @author Ryan Weaver   <ryan@symfonycasts.com>
  20.  */
  21. class ResetPasswordHelper implements ResetPasswordHelperInterface
  22. {
  23.     /**
  24.      * The first 20 characters of the token are a "selector".
  25.      */
  26.     private const SELECTOR_LENGTH 20;
  27.     private $tokenGenerator;
  28.     private $resetPasswordCleaner;
  29.     private $repository;
  30.     /**
  31.      * @var int How long a token is valid in seconds
  32.      */
  33.     private $resetRequestLifetime;
  34.     /**
  35.      * @var int Another password reset cannot be made faster than this throttle time in seconds
  36.      */
  37.     private $requestThrottleTime;
  38.     public function __construct(ResetPasswordTokenGenerator $generatorResetPasswordCleaner $cleanerResetPasswordRequestRepositoryInterface $repositoryint $resetRequestLifetimeint $requestThrottleTime)
  39.     {
  40.         $this->tokenGenerator $generator;
  41.         $this->resetPasswordCleaner $cleaner;
  42.         $this->repository $repository;
  43.         $this->resetRequestLifetime $resetRequestLifetime;
  44.         $this->requestThrottleTime $requestThrottleTime;
  45.     }
  46.     /**
  47.      * {@inheritdoc}
  48.      *
  49.      * Some of the cryptographic strategies were taken from
  50.      * https://paragonie.com/blog/2017/02/split-tokens-token-based-authentication-protocols-without-side-channels
  51.      *
  52.      * @throws TooManyPasswordRequestsException
  53.      */
  54.     public function generateResetToken(object $userint $resetRequestLifetime null): ResetPasswordToken
  55.     {
  56.         $this->resetPasswordCleaner->handleGarbageCollection();
  57.         if ($availableAt $this->hasUserHitThrottling($user)) {
  58.             throw new TooManyPasswordRequestsException($availableAt);
  59.         }
  60.         $resetRequestLifetime $resetRequestLifetime ?: $this->resetRequestLifetime;
  61.         $expiresAt = new \DateTimeImmutable(sprintf('+%d seconds'$resetRequestLifetime));
  62.         $generatedAt = ($expiresAt->getTimestamp() - $resetRequestLifetime);
  63.         $tokenComponents $this->tokenGenerator->createToken($expiresAt$this->repository->getUserIdentifier($user));
  64.         $passwordResetRequest $this->repository->createResetPasswordRequest(
  65.             $user,
  66.             $expiresAt,
  67.             $tokenComponents->getSelector(),
  68.             $tokenComponents->getHashedToken()
  69.         );
  70.         $this->repository->persistResetPasswordRequest($passwordResetRequest);
  71.         // final "public" token is the selector + non-hashed verifier token
  72.         return new ResetPasswordToken(
  73.             $tokenComponents->getPublicToken(),
  74.             $expiresAt,
  75.             $generatedAt
  76.         );
  77.     }
  78.     /**
  79.      * @throws ExpiredResetPasswordTokenException
  80.      * @throws InvalidResetPasswordTokenException
  81.      */
  82.     public function validateTokenAndFetchUser(string $fullToken): object
  83.     {
  84.         $this->resetPasswordCleaner->handleGarbageCollection();
  85.         if (40 !== \strlen($fullToken)) {
  86.             throw new InvalidResetPasswordTokenException();
  87.         }
  88.         $resetRequest $this->findResetPasswordRequest($fullToken);
  89.         if (null === $resetRequest) {
  90.             throw new InvalidResetPasswordTokenException();
  91.         }
  92.         if ($resetRequest->isExpired()) {
  93.             throw new ExpiredResetPasswordTokenException();
  94.         }
  95.         $user $resetRequest->getUser();
  96.         $hashedVerifierToken $this->tokenGenerator->createToken(
  97.             $resetRequest->getExpiresAt(),
  98.             $this->repository->getUserIdentifier($user),
  99.             substr($fullTokenself::SELECTOR_LENGTH)
  100.         );
  101.         if (false === hash_equals($resetRequest->getHashedToken(), $hashedVerifierToken->getHashedToken())) {
  102.             throw new InvalidResetPasswordTokenException();
  103.         }
  104.         return $user;
  105.     }
  106.     /**
  107.      * @throws InvalidResetPasswordTokenException
  108.      */
  109.     public function removeResetRequest(string $fullToken): void
  110.     {
  111.         $request $this->findResetPasswordRequest($fullToken);
  112.         if (null === $request) {
  113.             throw new InvalidResetPasswordTokenException();
  114.         }
  115.         $this->repository->removeResetPasswordRequest($request);
  116.     }
  117.     public function getTokenLifetime(): int
  118.     {
  119.         return $this->resetRequestLifetime;
  120.     }
  121.     /**
  122.      * Generate a fake reset token.
  123.      *
  124.      * Use this to generate a fake token so that you can, for example, show a
  125.      * "reset confirmation email sent" page that includes a valid "expiration date",
  126.      * even if the email was not actually found (and so, a true ResetPasswordToken
  127.      * was not actually created).
  128.      *
  129.      * This method should not be used when timing attacks are a concern.
  130.      */
  131.     public function generateFakeResetToken(int $resetRequestLifetime null): ResetPasswordToken
  132.     {
  133.         $resetRequestLifetime $resetRequestLifetime ?: $this->resetRequestLifetime;
  134.         $expiresAt = new \DateTimeImmutable(sprintf('+%d seconds'$resetRequestLifetime));
  135.         $generatedAt = ($expiresAt->getTimestamp() - $resetRequestLifetime);
  136.         return new ResetPasswordToken('fake-token'$expiresAt$generatedAt);
  137.     }
  138.     private function findResetPasswordRequest(string $token): ?ResetPasswordRequestInterface
  139.     {
  140.         $selector substr($token0self::SELECTOR_LENGTH);
  141.         return $this->repository->findResetPasswordRequest($selector);
  142.     }
  143.     private function hasUserHitThrottling(object $user): ?\DateTimeInterface
  144.     {
  145.         /** @var \DateTime|\DateTimeImmutable|null $lastRequestDate */
  146.         $lastRequestDate $this->repository->getMostRecentNonExpiredRequestDate($user);
  147.         if (null === $lastRequestDate) {
  148.             return null;
  149.         }
  150.         $availableAt = (clone $lastRequestDate)->add(new \DateInterval("PT{$this->requestThrottleTime}S"));
  151.         if ($availableAt > new \DateTime('now')) {
  152.             return $availableAt;
  153.         }
  154.         return null;
  155.     }
  156. }