Skip to content
Snippets Groups Projects
Select Git revision
  • f38c4ce07b60c9d94b54f74142e0c9a8ff94e210
  • master default protected
  • release-1.3.10
  • popover-bootstrap-3.4
  • zf-3.x
  • 3.0.9
  • 3.0.8
  • 1.3.10
  • 3.0.7
  • 3.0.6
  • 3.0.5
  • 3.0.4
  • 3.0.3
  • 3.0.2
  • 3.0.1
  • 3.0.0
  • 1.3.9
  • 1.3.8
  • 1.3.7
  • 1.3.6
  • 1.3.5
  • 1.3.4
  • 1.3.3
  • 1.3.2
  • 1.3.1
25 results

AuthController.php

Blame
  • Forked from lib / unicaen / auth
    Source project has a limited visibility.
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    AuthController.php 9.05 KiB
    <?php
    
    namespace UnicaenAuth\Controller;
    
    use DomainException;
    use UnicaenApp\Controller\Plugin\AppInfos;
    use UnicaenApp\Controller\Plugin\Mail;
    use UnicaenApp\Exception\RuntimeException;
    use UnicaenAuth\Service\Traits\ShibServiceAwareTrait;
    use UnicaenAuth\Service\Traits\UserServiceAwareTrait;
    use Zend\Authentication\AuthenticationService;
    use Zend\Authentication\Exception\ExceptionInterface;
    use Zend\Http\Request;
    use Zend\Http\Response;
    use Zend\Mvc\Controller\AbstractActionController;
    use Zend\View\Model\ViewModel;
    use ZfcUser\Controller\Plugin\ZfcUserAuthentication;
    
    /**
     * Classe ajoutée lors de l'implémentation de l'auth Shibboleth.
     *
     * @method ZfcUserAuthentication zfcUserAuthentication()
     * @method AppInfos appInfos()
     * @method Mail mail()
     *
     * @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr>
     */
    class AuthController extends AbstractActionController
    {
        use ShibServiceAwareTrait;
        use UserServiceAwareTrait;
    
        /**
         * Cette action peut être appelée lorsque l'authentification Shibboleth est activée
         * (unicaen-auth.shibboleth.enable === true).
         *
         * > Si la config Apache de Shibboleth est correcte, une requête à l'adresse correspondant à cette action
         * (suite au clic sur le bouton "Authentification Shibboleth", typiquement)
         * est détournée par Apache pour réaliser l'authentification Shibboleth.
         * Ce n'est qu'une fois l'authentification réalisée avec succès que cette action est appelée.
         *
         * > Si la config Apache de Shibboleth est incorrecte ou absente (localhost par exemple), et que la simulation
         * Shibboleth est activée dans la config (unicaen-auth.shibboleth.simulate), cette action est appelée et
         * la simulation est enclenchée.
         *
         * @return Response|array
         */
        public function shibbolethAction()
        {
            $operation = $this->params()->fromRoute('operation');
    
            if ($operation === 'deconnexion') {
                return $this->shibbolethLogout();
            }
    
            $redirectUrl = $this->params()->fromQuery('redirect', '/');
    
            // enclenchement de la simulation shibboleth éventuellement activée dans la config
            if ($simulate = $this->shibService->getShibbolethSimulate()) {
                $this->setStoredAuthenticatedUsername($simulate['eppn']); // tout simplement!
    
                return $this->redirect()->toUrl($redirectUrl);
            }
    
            $shibUser = $this->shibService->getAuthenticatedUser();
            if ($shibUser === null) {
                return []; // une page d'aide s'affichera si les données issues de Shibboleth attendues sont absentes
            }
    
            // arrivé ici, l'authentification shibboleth a été faite en bonne et due forme et a réussie.
    
            $this->setStoredAuthenticatedUsername($shibUser->getUsername());
            $this->userService->userAuthenticated($shibUser);
    
            return $this->redirect()->toUrl($redirectUrl);
        }
    
        /**
         * Déconnexion Shibboleth.
         *
         * @return array|Response
         */
        private function shibbolethLogout()
        {
            // déconnexion applicative quoiqu'il arrive
            $this->zfcUserAuthentication()->getAuthAdapter()->resetAdapters();
            $this->zfcUserAuthentication()->getAuthAdapter()->logoutAdapters();
            $this->zfcUserAuthentication()->getAuthService()->clearIdentity();
    
            // déconnexion Shibboleth le cas échéant
            if ($this->shibService->isShibbolethEnabled()) {
                // désactivation de l'usurpation d'identité éventuelle
                $this->shibService->deactivateUsurpation();
    
                // URL par défaut vers laquelle on redirige après déconnexion : accueil
                $homeUrl = $this->url()->fromRoute('home', [], ['force_canonical' => true]);
                $returnAbsoluteUrl = $this->params()->fromQuery('return', $homeUrl);
    
                return $this->redirect()->toUrl($this->shibService->getLogoutUrl($returnAbsoluteUrl));
            } else {
                return []; // une page d'aide s'affichera
            }
        }
    
        /**
         * @param string $username
         */
        private function setStoredAuthenticatedUsername($username)
        {
            /** @var AuthenticationService $authService */
            $authService = $this->getServiceLocator()->get('zfcuser_auth_service');
            try {
                $authService->getStorage()->write($username);
            } catch (ExceptionInterface $e) {
                throw new RuntimeException("Impossible d'écrire dans le storage");
            }
        }
    
        /**
         * @return Response|ViewModel
         */
        public function requestPasswordResetAction()
        {
            $form = $this->userService->createResetPasswordEmailForm();
    
            $view = new ViewModel();
            $view->setVariable('form', $form);
            $view->setTemplate('unicaen-auth/auth/request-password-reset-form');
    
            /** @var Request $request */
            $request = $this->getRequest();
    
            if ($request->isPost()) {
                $data = $request->getPost();
                $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)
        {
            // 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();
            $subject = "[$app] Demande de changement de mot de passe";
            $changePasswordUrl = $this->url()->fromRoute('auth/changePassword', ['token' => $token], ['force_canonical' => true]);
            $body = <<<EOS
    <p>Une demande de changement de mot de passe a été faite sur l'application $app.</p>
    <p>Si vous n'en êtes pas l'auteur, vous pouvez ignorer ce message.</p>
    <p>Cliquez sur le lien suivant pour accéder au formulaire de changement de votre mot de passe :<br><a href='$changePasswordUrl'>$changePasswordUrl</a></p>
    EOS;
            $message = $this->mail()->createNewMessage($body, $subject);
            $message->setTo($email);
            $this->mail()->send($message);
        }
    
        /**
         * @return array|ViewModel
         */
        public function changePasswordAction()
        {
            $token = $this->params()->fromRoute('token');
            $view = new ViewModel();
    
            // recherche du token spécifié dans table utilisateur
            $user = $this->userService->getUserMapper()->findOneByPasswordResetToken($token);
            if ($user === null) {
                // token inexistant
                $view->setVariable('result', 'unknown_token');
                $view->setTemplate('unicaen-auth/auth/change-password-result');
    
                return $view;
            }
    
            $form = $this->userService->createPasswordChangeForm();
    
            /** @var Request $request */
            $request = $this->getRequest();
    
            if ($request->isPost()) {
                $data = $request->getPost();
                $form->setData($data);
                if ($form->isValid()) {
                    // màj password
                    $password = $this->params()->fromPost('password');
                    $this->userService->updateUserPassword($user, $password);
    
                    $view->setVariable('result', 'success');
                    $view->setTemplate('unicaen-auth/auth/change-password-result');
    
                    // todo: faut-il déconnecter l'utilisateur (attention au logout shib différent) ?
    
                    return $view;
                }
            }
    
            // test durée de vie du token
            $date = $this->userService->extractDateFromResetPasswordToken($token);
            if ($date < date_create()) {
                // token expiré, on le raz
                $this->userService->clearUserPasswordResetToken($user);
    
                $view->setVariable('result', 'dead_token');
                $view->setTemplate('unicaen-auth/auth/change-password-result');
    
                return $view;
            }
    
            $view->setVariable('form', $form);
            $view->setTemplate('unicaen-auth/auth/change-password-form');
    
            return $view;
        }
    }