Commit 1e2106db authored by Bertrand Gauthier's avatar Bertrand Gauthier
Browse files

Mot de passe oublié : correction de la recherche de l'utilisateur par username...

Mot de passe oublié : correction de la recherche de l'utilisateur par username ; validateur d'adresse mail sur le formulaire ; refactorisations.
parent 258638bc
Pipeline #3389 failed with stages
in 2 minutes and 4 seconds
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
namespace UnicaenAuth\Controller; namespace UnicaenAuth\Controller;
use Doctrine\ORM\NoResultException; use DomainException;
use UnicaenApp\Controller\Plugin\AppInfos; use UnicaenApp\Controller\Plugin\AppInfos;
use UnicaenApp\Controller\Plugin\Mail; use UnicaenApp\Controller\Plugin\Mail;
use UnicaenApp\Exception\RuntimeException; use UnicaenApp\Exception\RuntimeException;
...@@ -135,24 +135,42 @@ class AuthController extends AbstractActionController ...@@ -135,24 +135,42 @@ class AuthController extends AbstractActionController
$form->setData($data); $form->setData($data);
if ($form->isValid()) { if ($form->isValid()) {
$email = $data['email']; $email = $data['email'];
$this->processPasswordResetRequest($email); try {
$this->processPasswordResetRequest($email);
$view->setVariable('email', $email);
$view->setTemplate('unicaen-auth/auth/request-password-reset-success'); $view->setVariable('email', $email);
$view->setTemplate('unicaen-auth/auth/request-password-reset-success');
} catch (DomainException $de) {
// affichage de l'erreur comme une erreur de validation
$form->get('email')->setMessages([$de->getMessage()]);
}
} }
} }
return $view; return $view;
} }
/**
* @param string $email
*/
private function processPasswordResetRequest($email) private function processPasswordResetRequest($email)
{ {
try { // Recherche de l'utilisateur ayant pour *username* (login) l'email spécifié
$token = $this->userService->updateUserPasswordResetToken($email); $user = $this->userService->getUserMapper()->findOneByUsername($email);
} catch (NoResultException $nre) {
// aucun utilisateur trouvé tel que username = $email if ($user === null) {
// Aucun utilisateur trouvé ayant l'email spécifié :
// on ne fait rien mais on ne le signale pas sinon le formulaire permettrait
// de tester si des emails potentiellement valides existent dans la base.
return; return;
} }
if (! $user->isLocal()) {
// L'email spécifié appartient à un utilisateur non local : on signale l'impossibilité de changer le mdp.
throw new DomainException("Le changement de mot de passe n'est pas possible pour cet utilisateur.");
}
// génération/enregistrement d'un token
$token = $this->userService->updateUserPasswordResetToken($user);
// envoi du mail contenant le lien de changement de mdp // envoi du mail contenant le lien de changement de mdp
$app = $this->appInfos()->getNom(); $app = $this->appInfos()->getNom();
......
...@@ -3,11 +3,11 @@ ...@@ -3,11 +3,11 @@
namespace UnicaenAuth\Service; namespace UnicaenAuth\Service;
use DateTime; use DateTime;
use Doctrine\ORM\NoResultException;
use Ramsey\Uuid\Uuid; use Ramsey\Uuid\Uuid;
use UnicaenApp\Entity\Ldap\People; use UnicaenApp\Entity\Ldap\People;
use UnicaenApp\Exception\RuntimeException; use UnicaenApp\Exception\RuntimeException;
use UnicaenApp\Mapper\Ldap\People as LdapPeopleMapper; use UnicaenApp\Mapper\Ldap\People as LdapPeopleMapper;
use UnicaenAuth\Entity\Db\AbstractUser;
use UnicaenAuth\Entity\Shibboleth\ShibUser; use UnicaenAuth\Entity\Shibboleth\ShibUser;
use UnicaenAuth\Event\UserAuthenticatedEvent; use UnicaenAuth\Event\UserAuthenticatedEvent;
use UnicaenAuth\Options\ModuleOptions; use UnicaenAuth\Options\ModuleOptions;
...@@ -22,11 +22,11 @@ use Zend\Form\Form; ...@@ -22,11 +22,11 @@ use Zend\Form\Form;
use Zend\InputFilter\Input; use Zend\InputFilter\Input;
use Zend\ServiceManager\ServiceLocatorAwareInterface; use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorAwareTrait; use Zend\ServiceManager\ServiceLocatorAwareTrait;
use Zend\Validator\EmailAddress;
use Zend\Validator\Identical; use Zend\Validator\Identical;
use ZfcUser\Entity\UserInterface; use ZfcUser\Entity\UserInterface;
use ZfcUser\Options\AuthenticationOptionsInterface; use ZfcUser\Options\AuthenticationOptionsInterface;
use ZfcUser\Options\ModuleOptions as ZfcUserModuleOptions; use ZfcUser\Options\ModuleOptions as ZfcUserModuleOptions;
use UnicaenAuth\Entity\Db\AbstractUser;
/** /**
* Service traitant des utilisateurs locaux de l'application. * Service traitant des utilisateurs locaux de l'application.
...@@ -81,13 +81,13 @@ class User implements ServiceLocatorAwareInterface, EventManagerAwareInterface ...@@ -81,13 +81,13 @@ class User implements ServiceLocatorAwareInterface, EventManagerAwareInterface
case $userData instanceof People: case $userData instanceof People:
$username = $userData->getData($this->getOptions()->getLdapUsername()); $username = $userData->getData($this->getOptions()->getLdapUsername());
$email = $userData->getMail(); $email = $userData->getMail();
$password = 'ldap'; $password = AbstractUser::PASSWORD_LDAP;
$state = in_array('deactivated', ldap_explode_dn($userData->getDn(), 1)) ? 0 : 1; $state = in_array('deactivated', ldap_explode_dn($userData->getDn(), 1)) ? 0 : 1;
break; break;
case $userData instanceof ShibUser: case $userData instanceof ShibUser:
$username = $userData->getUsername(); $username = $userData->getUsername();
$email = $userData->getEmail(); $email = $userData->getEmail();
$password = 'shib'; $password = AbstractUser::PASSWORD_SHIB;
$state = 1; $state = 1;
break; break;
default: default:
...@@ -260,7 +260,11 @@ class User implements ServiceLocatorAwareInterface, EventManagerAwareInterface ...@@ -260,7 +260,11 @@ class User implements ServiceLocatorAwareInterface, EventManagerAwareInterface
$form->add((new Text('email'))->setLabel("Adresse électronique :")); $form->add((new Text('email'))->setLabel("Adresse électronique :"));
$form->add((new Csrf('csrf'))); $form->add((new Csrf('csrf')));
$form->add((new Submit('submit'))->setLabel("Envoyer le lien")); $form->add((new Submit('submit'))->setLabel("Envoyer le lien"));
$form->getInputFilter()->add((new Input('email'))->setRequired(true));
$emailInput = new Input('email');
$emailInput->setRequired(true);
$emailInput->getValidatorChain()->attach(new EmailAddress());
$form->getInputFilter()->add($emailInput);
return $form; return $form;
} }
...@@ -287,22 +291,13 @@ class User implements ServiceLocatorAwareInterface, EventManagerAwareInterface ...@@ -287,22 +291,13 @@ class User implements ServiceLocatorAwareInterface, EventManagerAwareInterface
} }
/** /**
* Si l'utilisateur dont le username égale l'email spécifié est trouvé, * Génère puis enregistre le token permettant d'autoriser un utilisateur à changer son mot de passe.
* génère puis enregistre le token permettant d'autoriser cet utilisateur à changer son mot de passe.
* *
* @param string $email Email de l'utilisateur qui doit être aussi son username * @param AbstractUser $user Utilisateur concerné
* @return string|null Token généré * @return string|null Token généré
* @throws NoResultException Aucun utilisateur trouvé avec cet email
*/ */
public function updateUserPasswordResetToken($email) public function updateUserPasswordResetToken(AbstractUser $user)
{ {
// Si l'email est inconnu, on ne fera rien mais on ne le signale pas sinon le formulaire permettrait
// de tester si des emails potentiellement valides existent dans la base.
$user = $this->getUserMapper()->findByEmail($email); /** @var User $user */
if ($user === null) {
throw new NoResultException();
}
// Génération du token. // Génération du token.
$token = $this->generatePasswordResetToken(); $token = $this->generatePasswordResetToken();
...@@ -344,8 +339,14 @@ class User implements ServiceLocatorAwareInterface, EventManagerAwareInterface ...@@ -344,8 +339,14 @@ class User implements ServiceLocatorAwareInterface, EventManagerAwareInterface
*/ */
public function generatePasswordResetToken() public function generatePasswordResetToken()
{ {
try {
$uuid = Uuid::uuid4();
} catch (\Exception $e) {
throw new RuntimeException("Erreur rencontrée lors de la génération du UUID.", null, $e);
}
// NB: la date de fin de vie du token est concaténée à la fin. // NB: la date de fin de vie du token est concaténée à la fin.
$token = Uuid::uuid4()->toString() . self::PASSWORD_RESET_TOKEN_SEP . date('YmdHis', time() + 3600*24); $token = $uuid->toString() . self::PASSWORD_RESET_TOKEN_SEP . date('YmdHis', time() + 3600 * 24);
// durée de vie = 24h // durée de vie = 24h
return $token; return $token;
......
...@@ -2,18 +2,32 @@ ...@@ -2,18 +2,32 @@
namespace UnicaenAuth\Service; namespace UnicaenAuth\Service;
use UnicaenAuth\Entity\Db\User; use UnicaenAuth\Entity\Db\AbstractUser;
use ZfcUserDoctrineORM\Mapper\User as ZfcUserDoctrineORMUserMapper; use ZfcUserDoctrineORM\Mapper\User as ZfcUserDoctrineORMUserMapper;
class UserMapper extends ZfcUserDoctrineORMUserMapper class UserMapper extends ZfcUserDoctrineORMUserMapper
{ {
/**
* Recherche un utilisateur par son username (identifiant de connexion).
*
* @param string $username
* @return AbstractUser|null
*/
public function findOneByUsername($username)
{
/** @var AbstractUser $user */
$user = $this->em->getRepository($this->options->getUserEntityClass())->findOneBy(['username' => $username]);
return $user;
}
/** /**
* @param string $token * @param string $token
* @return User * @return AbstractUser
*/ */
public function findOneByPasswordResetToken($token) public function findOneByPasswordResetToken($token)
{ {
/** @var User $user */ /** @var AbstractUser $user */
$user = $this->em->getRepository($this->options->getUserEntityClass())->findOneBy(['passwordResetToken' => $token]); $user = $this->em->getRepository($this->options->getUserEntityClass())->findOneBy(['passwordResetToken' => $token]);
return $user; return $user;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment