diff --git a/config/module.config.php b/config/module.config.php index b6144118d697fc96b5dc1b80767fa5748d150e4a..da392d13a35ae4bd9abf2a4120ed8d0e3c598213 100644 --- a/config/module.config.php +++ b/config/module.config.php @@ -1,5 +1,7 @@ <?php +use UnicaenAuth\Authentication\Adapter\ShibSimulatorAdapter; +use UnicaenAuth\Authentication\Storage\ShibSimulatorStorage; use UnicaenAuth\Controller\AuthControllerFactory; use UnicaenAuth\Service\ShibService; use UnicaenAuth\Service\ShibServiceFactory; @@ -374,6 +376,7 @@ return [ 'UnicaenAuth\Authentication\Storage\Db' => 'UnicaenAuth\Authentication\Storage\Db', 'UnicaenAuth\Authentication\Storage\Ldap' => 'UnicaenAuth\Authentication\Storage\Ldap', 'UnicaenAuth\Authentication\Storage\Shib' => 'UnicaenAuth\Authentication\Storage\Shib', + 'UnicaenAuth\Authentication\Storage\ShibSimulatorStorage' => ShibSimulatorStorage::class, 'UnicaenAuth\View\RedirectionStrategy' => 'UnicaenAuth\View\RedirectionStrategy', 'UnicaenAuth\Service\User' => 'UnicaenAuth\Service\User', 'UnicaenAuth\Service\CategoriePrivilege' => 'UnicaenAuth\Service\CategoriePrivilegeService', diff --git a/src/UnicaenAuth/Authentication/Storage/Shib.php b/src/UnicaenAuth/Authentication/Storage/Shib.php index 2e9600d7b0b3303a1fd34ed1a8c993b6ef16c621..8d8a81c9178bf4d255581983de3db12fd6adb290 100644 --- a/src/UnicaenAuth/Authentication/Storage/Shib.php +++ b/src/UnicaenAuth/Authentication/Storage/Shib.php @@ -3,7 +3,6 @@ namespace UnicaenAuth\Authentication\Storage; use UnicaenAuth\Entity\Shibboleth\ShibUser; -use UnicaenAuth\Options\ModuleOptions; use UnicaenAuth\Service\ShibService; use Zend\Authentication\Storage\Session; use Zend\Authentication\Storage\StorageInterface; @@ -24,11 +23,6 @@ class Shib implements ChainableStorage, ServiceLocatorAwareInterface * @var StorageInterface */ protected $storage; - - /** - * @var ModuleOptions - */ - protected $options; /** * @var ShibUser @@ -63,10 +57,6 @@ class Shib implements ChainableStorage, ServiceLocatorAwareInterface */ private function getAuthenticatedUser() { - if (! $this->isShibbolethEnabled()) { - return null; - } - if (null !== $this->resolvedIdentity) { return $this->resolvedIdentity; } @@ -79,29 +69,6 @@ class Shib implements ChainableStorage, ServiceLocatorAwareInterface return $this->resolvedIdentity; } - /** - * @return bool - */ - private function isShibbolethEnabled() - { - $options = $this->getModuleOptions(); - $shibboleth = $options->getShibboleth(); - - return isset($shibboleth['enable']) && (bool) $shibboleth['enable']; - } - - /** - * @return ModuleOptions - */ - private function getModuleOptions() - { - if (null === $this->options) { - $this->options = $this->getServiceLocator()->get('unicaen-auth_module_options'); - } - - return $this->options; - } - /** * Writes $contents to storage * diff --git a/src/UnicaenAuth/Authentication/Storage/ShibSimulatorStorage.php b/src/UnicaenAuth/Authentication/Storage/ShibSimulatorStorage.php new file mode 100644 index 0000000000000000000000000000000000000000..ffb1eabc5d211b1f44c1d7b0d45c9fae3dfaee30 --- /dev/null +++ b/src/UnicaenAuth/Authentication/Storage/ShibSimulatorStorage.php @@ -0,0 +1,70 @@ +<?php + +namespace UnicaenAuth\Authentication\Storage; + +use UnicaenAuth\Service\ShibService; +use Zend\ServiceManager\ServiceLocatorAwareInterface; +use Zend\ServiceManager\ServiceLocatorAwareTrait; + +/** + * Storage permettant de simuler un utilisateur authentifié via Shibboleth. + * + * @author Unicaen + */ +class ShibSimulatorStorage implements ChainableStorage, ServiceLocatorAwareInterface +{ + use ServiceLocatorAwareTrait; + + /** + * @var ShibService + */ + protected $shibService; + + /** + * @var array + */ + protected $shibbolethOptions; + + /** + * {@inheritdoc} + */ + public function read(ChainEvent $e) + { + if (! $this->getShibService()->isShibbolethEnabled()) { + return; + } + if (! $this->getShibService()->isSimulationActive()) { + return; + } + + $this->getShibService()->handleSimulation(); + } + + /** + * {@inheritdoc} + */ + public function write(ChainEvent $e) + { + // nop + } + + /** + * {@inheritdoc} + */ + public function clear(ChainEvent $e) + { + // nop + } + + /** + * @return ShibService + */ + private function getShibService() + { + if ($this->shibService === null) { + $this->shibService = $this->serviceLocator->get(ShibService::class); + } + + return $this->shibService; + } +} \ No newline at end of file diff --git a/src/UnicaenAuth/Controller/AuthController.php b/src/UnicaenAuth/Controller/AuthController.php index 51fc785de01762fca73aa092b0aab8ce412a9585..045136805e05183b05717d49000a2284a1e95182 100644 --- a/src/UnicaenAuth/Controller/AuthController.php +++ b/src/UnicaenAuth/Controller/AuthController.php @@ -3,7 +3,6 @@ namespace UnicaenAuth\Controller; use UnicaenApp\Exception\RuntimeException; -use UnicaenAuth\Service\ShibService; use UnicaenAuth\Service\Traits\ShibServiceAwareTrait; use UnicaenAuth\Service\Traits\UserServiceAwareTrait; use Zend\Authentication\AuthenticationService; @@ -24,15 +23,17 @@ class AuthController extends AbstractActionController use UserServiceAwareTrait; /** - * Cette action n'est exécutée qu'une fois l'authentification Shibboleth réalisée avec succès. + * Cette action peut être appelée lorsque l'authentification Shibboleth est activée + * (unicaen-auth.shibboleth.enable === true). * - * Lorsque l'authentification Shibboleth est activée (unicaen-auth.shibboleth.enable === true), - * et que la config Apache est correcte, une requête à l'adresse correspondant à cette action - * (suite au clic sur le bouton "Authentification Shibboleth, typiquement) - * est détournée pour réaliser l'authentification. - * Ce n'est qu'une fois l'authentification réalisée avec succès que cette action entre en jeu. + * > 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. * - * @see ShibService::apacheConfigSnippet() + * > 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 */ @@ -41,44 +42,69 @@ class AuthController extends AbstractActionController $operation = $this->params()->fromRoute('operation'); if ($operation === 'deconnexion') { - // 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 - } + return $this->shibbolethLogout(); } - $shibUser = $this->shibService->getAuthenticatedUser(); + $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($shibUser->getUsername()); + $authService->getStorage()->write($username); } catch (ExceptionInterface $e) { throw new RuntimeException("Impossible d'écrire dans le storage"); } - - $this->userService->userAuthenticated($shibUser); - - $redirectUrl = $this->params()->fromQuery('redirect', '/'); - - return $this->redirect()->toUrl($redirectUrl); } } \ No newline at end of file diff --git a/src/UnicaenAuth/Service/ShibService.php b/src/UnicaenAuth/Service/ShibService.php index 45cdd52de3fba4187dffdcc5cc07b22ac27af073..c21da8671ba60f24fa38d84a5209365e576442e8 100644 --- a/src/UnicaenAuth/Service/ShibService.php +++ b/src/UnicaenAuth/Service/ShibService.php @@ -58,25 +58,11 @@ EOS; } if ($this->authenticatedUser === null) { - if (! $this->getShibbolethSimulate() && ! isset($_SERVER['REMOTE_USER'])) { - try { - Assertion::keyIsset($_SERVER, 'REMOTE_USER'); - } catch (AssertionFailedException $e) { - throw new RuntimeException("La clé suivante est introuvable ou sans valeur dans \$_SERVER : 'REMOTE_USER'."); - } - } - // gestion de l'usurpation éventuelle - if ($this->isUsurpationActive()) { - $this->handleUsurpation(); - } + $this->handleUsurpation(); if (empty($_SERVER['REMOTE_USER'])) { - if ($this->isSimulationActive()) { - $this->handleSimulation(); - } else { - return null; - } + return null; } $this->authenticatedUser = $this->createShibUserFromServerArrayData(); @@ -114,7 +100,7 @@ EOS; * * @return bool */ - private function isSimulationActive() + public function isSimulationActive() { $options = $this->options->getShibboleth(); @@ -126,10 +112,17 @@ EOS; } /** - * @return $this + * @return ShibUser|null */ - private function handleSimulation() + public function handleSimulation() { + if (! $this->isSimulationActive()) { + return null; + } + + // si nécessaire + $this->handleUsurpation(); + $simulate = $this->getShibbolethSimulate(); try { @@ -154,9 +147,9 @@ EOS; $shibUser->setNom('Shibboleth'); $shibUser->setPrenom('Simulation'); - ShibService::simulateAuthenticatedUser($shibUser); + $this->simulateAuthenticatedUser($shibUser); - return $this; + return $shibUser; } /** @@ -205,8 +198,12 @@ EOS; /** * @return $this */ - private function handleUsurpation() + public function handleUsurpation() { + if (! $this->isUsurpationActive()) { + return $this; + } + $session = $this->getSessionContainer(); /** @var ShibUser|null $toShibUser */ @@ -215,7 +212,7 @@ EOS; throw new RuntimeException("Anomalie: 'toShibUser' introuvable"); } - static::simulateAuthenticatedUser($toShibUser, 'supannEmpId'); + $this->simulateAuthenticatedUser($toShibUser, 'supannEmpId'); return $this; } @@ -236,7 +233,7 @@ EOS; * @param string $keyForId Clé du tableau $_SERVER dans laquelle mettre l'id de l'utilsateur spécifié. * Ex: 'supannEmpId', 'supannEtuId'. */ - public static function simulateAuthenticatedUser(ShibUser $shibUser, $keyForId = 'supannEmpId') + public function simulateAuthenticatedUser(ShibUser $shibUser, $keyForId = 'supannEmpId') { $_SERVER['REMOTE_USER'] = $shibUser->getEppn(); $_SERVER[$keyForId] = $shibUser->getId(); diff --git a/view/unicaen-auth/auth/shibboleth.phtml b/view/unicaen-auth/auth/shibboleth.phtml index 63addc60275602b3b9b0d546d1d8adce1b6543ee..aeb1c638e32a4865322cae6c4692ebb5ceddd833 100644 --- a/view/unicaen-auth/auth/shibboleth.phtml +++ b/view/unicaen-auth/auth/shibboleth.phtml @@ -1,23 +1,41 @@ <h1 class="page-header">Authentification Shibboleth</h1> - <p> Si vous arrivez sur cette page, c'est sans doute que vous cherchez à utiliser l'authentification Shibboleth, mais qu'elle est mal configurée ! </p> +<h2>Configuration pour Shibboleth</h2> <p> - Vous devez activer l'authentification Shibboleth dans la config : + Déjà, vous devez activer l'authentification Shibboleth dans la config de UnicaenAuth : </p> <pre> 'unicaen-auth' => [ ... 'shibboleth' => [ - 'enable' => true + 'enable' => true, ], - ]; + ], </pre> - <p> Et voici ce que vous devez ajouter dans la configuration Apache de votre site : </p> -<pre><?php echo htmlspecialchars(\UnicaenAuth\Service\ShibService::apacheConfigSnippet()) ?></pre> \ No newline at end of file +<pre><?php echo htmlspecialchars(\UnicaenAuth\Service\ShibService::apacheConfigSnippet()) ?></pre> + + +<h2>Autre solution : simuler une authentification Shibboleth</h2> +<p> + Il est possible de faire comme si un utilisateur s'était connecté via Shibboleth. + Cela se fait via la clé de config 'simulate' : +</p> +<pre> + 'unicaen-auth' => [ + ... + 'shibboleth' => [ + 'enable' => true, + 'simulate' => [ + 'eppn' => 'gauthierb@unicaen.fr', + 'supannEmpId' => '00021237', + ], + ], + ], +</pre> \ No newline at end of file