src/EventSubscriber/Api/ChangedPasswordSubscriber.php line 86

Open in your IDE?
  1. <?php
  2. namespace App\EventSubscriber\Api;
  3. use App\Controller\Api\AbstractApiController;
  4. use App\Controller\Api\CoreModule\AuthenticationController;
  5. use App\Controller\Api\CoreModule\SessionController;
  6. use App\Entity\Api\CoreModule\User;
  7. use App\Helper\Api\JsonFormatter\Throwable\Formatter;
  8. use App\Helper\Api\Translator\ApiTranslator;
  9. use App\Validator\Constraints\Api\CoreModule\User\Attributes\NewPasswordNeededCompound;
  10. use Symfony\Component\HttpFoundation\Response;
  11. use Symfony\Component\HttpKernel\Event\ControllerEvent;
  12. use Symfony\Component\HttpKernel\Exception\HttpException;
  13. use Symfony\Component\HttpKernel\KernelEvents;
  14. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  15. /**
  16.  * Subscriber for kernel controller event to force the user to change his temporary password.
  17.  *
  18.  * @package API
  19.  * @internal
  20.  */
  21. class ChangedPasswordSubscriber extends AbstractSubscriber
  22. {
  23.     /**
  24.      * Token storage to retrieve user.
  25.      *
  26.      * @var TokenStorageInterface
  27.      */
  28.     protected TokenStorageInterface $tokenStorage;
  29.     /**
  30.      * Returns an array of event names this subscriber wants to listen to.
  31.      *
  32.      * The array keys are event names and the value can be:
  33.      *
  34.      *  * The method name to call (priority defaults to 0)
  35.      *  * An array composed of the method name to call and the priority
  36.      *  * An array of arrays composed of the method names to call and respective
  37.      *    priorities, or 0 if unset
  38.      *
  39.      * For instance:
  40.      *
  41.      *  * ['eventName' => 'methodName']
  42.      *  * ['eventName' => ['methodName', $priority]]
  43.      *  * ['eventName' => [['methodName1', $priority], ['methodName2']]]
  44.      *
  45.      * The code must not depend on runtime state as it will only be called at compile time.
  46.      * All logic depending on runtime state must be put into the individual methods handling the events.
  47.      *
  48.      * @return array<string, string|array{0: string, 1: int}|list<array{0: string, 1?: int}>>
  49.      * @noinspection PhpArrayShapeAttributeCanBeAddedInspection
  50.      * @noinspection PhpUnused
  51.      */
  52.     public static function getSubscribedEvents(): array
  53.     {
  54.         return [
  55.             KernelEvents::CONTROLLER => [
  56.                 ['ensureCustomPassword'16]
  57.             ]
  58.         ];
  59.     }
  60.     /**
  61.      * Constructor.
  62.      *
  63.      * @param Formatter $formatter
  64.      * @param ApiTranslator $translator
  65.      * @param TokenStorageInterface $tokenStorage
  66.      */
  67.     public function __construct(Formatter $formatterApiTranslator $translatorTokenStorageInterface $tokenStorage)
  68.     {
  69.         parent::__construct($formatter$translator);
  70.         $this->tokenStorage $tokenStorage;
  71.     }
  72.     /**
  73.      * Force users to change their temporary password.
  74.      *
  75.      * @param ControllerEvent $event The controller event.
  76.      * @return void
  77.      * @noinspection PhpUnused
  78.      */
  79.     public function ensureCustomPassword(ControllerEvent $event): void
  80.     {
  81.         $method null;
  82.         $controller $event->getController();
  83.         // when a controller class defines multiple action methods, the controller
  84.         // is returned as [$controllerInstance, 'methodName']
  85.         if (is_array($controller)) {
  86.             $method $controller[1];
  87.             $controller $controller[0];
  88.         }
  89.         // limit to API controllers
  90.         if ($controller instanceof AbstractApiController) {
  91.             // ignore for authentication controller to allow login, logout, ping and password reset
  92.             if ($controller instanceof AuthenticationController) {
  93.                 return;
  94.             }
  95.             // ignore for session controller and certain methods to allow password and language change
  96.             if ($controller instanceof SessionController &&
  97.                 ($method === 'userPasswordUpdate' || $method === 'userLanguageUpdate')) {
  98.                 return;
  99.             }
  100.             $currentUser $this->tokenStorage->getToken()->getUser();
  101.             /* @var User $currentUser */
  102.             /* @noinspection PhpClassConstantAccessedViaChildClassInspection */
  103.             if ($currentUser->getNewPasswordNeeded() === NewPasswordNeededCompound::YES) {
  104.                 throw new HttpException(
  105.                     Response::HTTP_FORBIDDEN,
  106.                     'User needs to change his temporary password first.'
  107.                 );
  108.             }
  109.         }
  110.     }
  111. }