<?php
/**
* Copyright (C) SUEZ Smart Solutions - All Rights Reserved
* On’Connect Gateway Management, 2018
* Unauthorized copying of this file, via any medium is strictly prohibited
* Proprietary and confidential
* For the full copyright and license information, please report to the LICENSE CONTRACT.
*/
namespace TSMS\UserBundle\EventListener;
use DateTime;
use FOS\UserBundle\Model\UserManagerInterface;
use FOS\UserBundle\Util\TokenGenerator;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use TSMS\CoreBundle\Entity\User;
use Exception;
class PasswordValidityListener implements EventSubscriberInterface
{
/**
* @var UserManagerInterface
*/
protected $userManager;
/**
* @var RouterInterface
*/
protected $router;
/**
* @var TokenStorageInterface
*/
protected $tokenStorage;
/**
* @var TokenGenerator
*/
protected $tokenGenerator;
/**
* @var int Password validity
*/
protected $passwordValidity;
/**
* @var array
*/
protected $whitelistCheckValidityPassword;
/**
* @param UserManagerInterface $userManager
* @param RouterInterface $router
* @param TokenStorageInterface $tokenStorage
* @param TokenGenerator $tokenGenerator
* @param int $passwordValidity
* @param array $whitelistCheckValidityPassword
*/
public function __construct(
UserManagerInterface $userManager,
RouterInterface $router,
TokenStorageInterface $tokenStorage,
TokenGenerator $tokenGenerator,
$passwordValidity,
$whitelistCheckValidityPassword
) {
$this->userManager = $userManager;
$this->router = $router;
$this->tokenStorage = $tokenStorage;
$this->tokenGenerator = $tokenGenerator;
$this->passwordValidity = $passwordValidity;
$this->whitelistCheckValidityPassword = $whitelistCheckValidityPassword;
}
/**
* @return array
*/
public static function getSubscribedEvents()
{
return array(
'kernel.request' => 'onKernelRequest',
);
}
/**
* @param GetResponseEvent $event
* @throws Exception
*/
public function onKernelRequest(GetResponseEvent $event)
{
if (!$this->tokenStorage->getToken()) {
return;
}
/** @var User $user */
$user = $this->tokenStorage->getToken()->getUser();
if (!($user instanceof User)) {
return;
}
if (!$this->isPasswordNeedsChange($user)) {
return;
}
$user->setPasswordRequestedAt(new DateTime());
$token = $user->getConfirmationToken();
if (!$token) {
$token = $this->tokenGenerator->generateToken();
$user->setConfirmationToken($token);
}
$this->userManager->updateUser($user);
$redirectUrl = $this->router->generate('fos_user_resetting_reset', ['token' => $token]);
$this->addFlashBagError($event);
if ($event->getRequest()->getRequestUri() !== $redirectUrl) {
$event->setResponse(new RedirectResponse($redirectUrl));
}
}
/**
* Adds flashbag notice to notify user their password is expired.
*
* Also I didn't want to have to mock a million stuff in my test.
*
* @param GetResponseEvent $event
*/
protected function addFlashBagError($event)
{
$event->getRequest()->getSession()->getFlashBag()->set('error', 'resetting.reset.password_expired');
}
/**
* Checks if user's password is fresh enough. If not, it must be redefined.
*
* @param User $user User logging in
*
* @return bool Does password need to be changed
* @throws Exception
*/
protected function isPasswordNeedsChange(User $user)
{
// We don't check whitelist
if (in_array($user->getUsername(), $this->whitelistCheckValidityPassword )) {
return false;
}
// We don't check internal users' password expiration because they can not change their passwords
if ($user->isInternal()) {
return false;
}
// not datetime => not redefined => need to change
if (!($user->getPasswordChangedAt() instanceof DateTime)) {
return true;
}
$diff = $user->getPasswordChangedAt()->diff(new \DateTime());
return $diff->format('%a') > $this->passwordValidity;
}
}