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
Loading
Loading
Loading
Loading
+27 −9
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@

namespace UnicaenAuth\Controller;

use Doctrine\ORM\NoResultException;
use DomainException;
use UnicaenApp\Controller\Plugin\AppInfos;
use UnicaenApp\Controller\Plugin\Mail;
use UnicaenApp\Exception\RuntimeException;
@@ -135,24 +135,42 @@ class AuthController extends AbstractActionController
            $form->setData($data);
            if ($form->isValid()) {
                $email = $data['email'];
                try {
                    $this->processPasswordResetRequest($email);

                    $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;
    }

    /**
     * @param string $email
     */
    private function processPasswordResetRequest($email)
    {
        try {
            $token = $this->userService->updateUserPasswordResetToken($email);
        } catch (NoResultException $nre) {
            // aucun utilisateur trouvé tel que username = $email
        // Recherche de l'utilisateur ayant pour *username* (login) l'email spécifié
        $user = $this->userService->getUserMapper()->findOneByUsername($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;
        }
        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
        $app = $this->appInfos()->getNom();
+19 −18
Original line number Diff line number Diff line
@@ -3,11 +3,11 @@
namespace UnicaenAuth\Service;

use DateTime;
use Doctrine\ORM\NoResultException;
use Ramsey\Uuid\Uuid;
use UnicaenApp\Entity\Ldap\People;
use UnicaenApp\Exception\RuntimeException;
use UnicaenApp\Mapper\Ldap\People as LdapPeopleMapper;
use UnicaenAuth\Entity\Db\AbstractUser;
use UnicaenAuth\Entity\Shibboleth\ShibUser;
use UnicaenAuth\Event\UserAuthenticatedEvent;
use UnicaenAuth\Options\ModuleOptions;
@@ -22,11 +22,11 @@ use Zend\Form\Form;
use Zend\InputFilter\Input;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorAwareTrait;
use Zend\Validator\EmailAddress;
use Zend\Validator\Identical;
use ZfcUser\Entity\UserInterface;
use ZfcUser\Options\AuthenticationOptionsInterface;
use ZfcUser\Options\ModuleOptions as ZfcUserModuleOptions;
use UnicaenAuth\Entity\Db\AbstractUser;

/**
 * Service traitant des utilisateurs locaux de l'application.
@@ -81,13 +81,13 @@ class User implements ServiceLocatorAwareInterface, EventManagerAwareInterface
            case $userData instanceof People:
                $username = $userData->getData($this->getOptions()->getLdapUsername());
                $email = $userData->getMail();
                $password = 'ldap';
                $password = AbstractUser::PASSWORD_LDAP;
                $state = in_array('deactivated', ldap_explode_dn($userData->getDn(), 1)) ? 0 : 1;
                break;
            case $userData instanceof ShibUser:
                $username = $userData->getUsername();
                $email = $userData->getEmail();
                $password = 'shib';
                $password = AbstractUser::PASSWORD_SHIB;
                $state = 1;
                break;
            default:
@@ -260,7 +260,11 @@ class User implements ServiceLocatorAwareInterface, EventManagerAwareInterface
        $form->add((new Text('email'))->setLabel("Adresse électronique :"));
        $form->add((new Csrf('csrf')));
        $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;
    }
@@ -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 cet utilisateur à changer son mot de passe.
     * Génère puis enregistre le token permettant d'autoriser un 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é
     * @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.
        $token = $this->generatePasswordResetToken();

@@ -344,8 +339,14 @@ class User implements ServiceLocatorAwareInterface, EventManagerAwareInterface
     */
    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.
        $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

        return $token;
+17 −3
Original line number Diff line number Diff line
@@ -2,18 +2,32 @@

namespace UnicaenAuth\Service;

use UnicaenAuth\Entity\Db\User;
use UnicaenAuth\Entity\Db\AbstractUser;
use ZfcUserDoctrineORM\Mapper\User as 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
     * @return User
     * @return AbstractUser
     */
    public function findOneByPasswordResetToken($token)
    {
        /** @var User $user */
        /** @var AbstractUser $user */
        $user = $this->em->getRepository($this->options->getUserEntityClass())->findOneBy(['passwordResetToken' => $token]);

        return $user;