Loading CHANGELOG.md +2 −0 Original line number Diff line number Diff line Loading @@ -3,8 +3,10 @@ CHANGELOG 3.2.1 ----- - Modifications/améliorations pour faciliter le support d'autres modes d'authentification (ex: unicaen/auth-token). - Le type d'authentification souhaité (local, shib ou cas) peut être spécifié dans l'URL de redirection via le query param 'authtype' - Ajout de la colonne CREATED_AT dans les scripts SQL de création de la table USER (non mappée dans l'entité). - [FIX] Usurpation d'un compte local (db) depuis une authentification shib - [FIX] Une chaîne vide doit être considérée comme null dans ShibService::extractShibUserIdValueForDomainFromShibData() - [FIX] Nécessité de clés littérales dans la config par domaine de 'shib_user_id_extractor' sinon doublons lors de la Loading config/module.config.php +22 −6 Original line number Diff line number Diff line <?php namespace UnicaenAuth; use UnicaenAuth\Authentication\Adapter\AdapterChainServiceFactory; use UnicaenAuth\Authentication\Adapter\Cas; use UnicaenAuth\Authentication\Adapter\CasAdapterFactory; Loading @@ -18,6 +20,7 @@ use UnicaenAuth\Authentication\Storage\LdapFactory; use UnicaenAuth\Authentication\Storage\ShibFactory; use UnicaenAuth\Authentication\Storage\Usurpation; use UnicaenAuth\Authentication\Storage\UsurpationFactory; use UnicaenAuth\Controller\AuthController; use UnicaenAuth\Controller\AuthControllerFactory; use UnicaenAuth\Controller\DroitsControllerFactory; use UnicaenAuth\Controller\UtilisateurControllerFactory; Loading @@ -30,12 +33,14 @@ use UnicaenAuth\Form\ShibLoginForm; use UnicaenAuth\Form\ShibLoginFormFactory; use UnicaenAuth\Guard\PrivilegeControllerFactory; use UnicaenAuth\Guard\PrivilegeRouteFactory; use UnicaenAuth\Options\ModuleOptions; use UnicaenAuth\ORM\Event\Listeners\HistoriqueListenerFactory; use UnicaenAuth\Provider\Rule\PrivilegeRuleProviderFactory; use UnicaenAuth\Service\CasService; use UnicaenAuth\Service\CasServiceFactory; use UnicaenAuth\Service\ShibService; use UnicaenAuth\Service\ShibServiceFactory; use UnicaenAuth\Service\UserContext; use UnicaenAuth\Service\UserContextFactory; use UnicaenAuth\Service\UserFactory; use UnicaenAuth\Service\UserMapperFactory; Loading Loading @@ -70,6 +75,15 @@ use Zend\Authentication\AuthenticationService; use Zend\ServiceManager\Proxy\LazyServiceFactory; $settings = [ /** * Tous les types d'authentification supportés par le module unicaen/auth. */ 'auth_types' => [ 'local', // càd 'ldap' et 'db' 'cas', 'shib', ], /** * Configuration de l'authentification centralisée (CAS). */ Loading Loading @@ -629,21 +643,22 @@ return [ // in /var/www/sygal/module/Application/src/Application/Controller/UtilisateurController.php on line 34 'service_manager' => [ 'aliases' => [ 'unicaen-auth_module_options' => ModuleOptions::class, 'zfcuser_login_form' => LoginForm::class, 'Zend\Authentication\AuthenticationService' => 'zfcuser_auth_service', 'UnicaenAuth\Privilege\PrivilegeProvider' => 'UnicaenAuth\Service\Privilege', '\UnicaenAuth\Guard\PrivilegeController' => 'UnicaenAuth\Guard\PrivilegeController', 'unicaen-auth_user_service' => 'UnicaenAuth\Service\User', // pour la compatibilité 'authUserContext' => 'UnicaenAuth\Service\UserContext', // pour la compatibilité 'AuthUserContext' => 'UnicaenAuth\Service\UserContext', // pour la compatibilité 'authUserContext' => UserContext::class, // pour la compatibilité 'AuthUserContext' => UserContext::class, // pour la compatibilité ], 'invokables' => [ 'UnicaenAuth\View\RedirectionStrategy' => 'UnicaenAuth\View\RedirectionStrategy', 'UnicaenAuth\Service\CategoriePrivilege' => 'UnicaenAuth\Service\CategoriePrivilegeService', ], 'factories' => [ 'unicaen-auth_module_options' => 'UnicaenAuth\Options\ModuleOptionsFactory', ModuleOptions::class => 'UnicaenAuth\Options\ModuleOptionsFactory', 'zfcuser_auth_service' => 'UnicaenAuth\Authentication\AuthenticationServiceFactory', 'UnicaenAuth\Authentication\Storage\Chain' => 'UnicaenAuth\Authentication\Storage\ChainServiceFactory', 'UnicaenAuth\Provider\Identity\Chain' => 'UnicaenAuth\Provider\Identity\ChainServiceFactory', Loading @@ -659,7 +674,7 @@ return [ 'zfcuser_redirect_callback' => 'UnicaenAuth\Authentication\RedirectCallbackFactory', // substituion CasService::class => CasServiceFactory::class, ShibService::class => ShibServiceFactory::class, 'UnicaenAuth\Service\UserContext' => UserContextFactory::class, UserContext::class => UserContextFactory::class, 'zfcuser_user_mapper' => UserMapperFactory::class, 'MouchardCompleterAuth' => 'UnicaenAuth\Mouchard\MouchardCompleterAuthFactory', LocalAdapter::class => LocalAdapterFactory::class, Loading Loading @@ -708,10 +723,11 @@ return [ ], 'controllers' => [ 'invokables' => [ 'aliases' => [ 'UnicaenAuth\Controller\Auth' => AuthController::class, ], 'factories' => [ 'UnicaenAuth\Controller\Auth' => AuthControllerFactory::class, AuthController::class => AuthControllerFactory::class, 'UnicaenAuth\Controller\Utilisateur' => UtilisateurControllerFactory::class, 'UnicaenAuth\Controller\Droits' => DroitsControllerFactory::class, ], Loading src/UnicaenAuth/Authentication/Adapter/AbstractDb.php 0 → 100644 +125 −0 Original line number Diff line number Diff line <?php namespace UnicaenAuth\Authentication\Adapter; use UnicaenAuth\Options\Traits\ModuleOptionsAwareTrait; use Zend\Authentication\Result as AuthenticationResult; use Zend\EventManager\EventInterface; use Zend\Session\Container as SessionContainer; use ZfcUser\Authentication\Adapter\AdapterChainEvent; use ZfcUser\Entity\UserInterface; use ZfcUser\Mapper\UserInterface as UserMapperInterface; /** * Classe abstraite des adpater d'authentification à partir de la base de données. * * Ajout par rapport à la classe mère : si aucune base de données ou table n'existe, * l'authentification ne plante pas (i.e. renvoit false). * * @author Bertrand GAUTHIER <bertrand.gauthier@unicaen.fr> */ abstract class AbstractDb extends AbstractAdapter { use ModuleOptionsAwareTrait; /** * @var string */ protected $type; /** * @var AdapterChainEvent */ protected $event; /** * @var UserMapperInterface */ protected $mapper; /** * @inheritDoc */ public function authenticate(EventInterface $e): bool { // NB: Dans la version 3.0.0 de zf-commons/zfc-user, cette méthode prend un EventInterface. // Mais dans la branche 3.x, c'est un AdapterChainEvent ! // Si un jour c'est un AdapterChainEvent qui est attendu, plus besoin de faire $e->getTarget(). $this->event = $e->getTarget(); if ($this->event->getIdentity()) { return true; } if ($this->isSatisfied()) { $storage = $this->getStorage()->read(); $this->event ->setIdentity($storage['identity']) ->setCode(AuthenticationResult::SUCCESS) ->setMessages(array('Authentication successful.')); return true; } $userObject = $this->fetchUserObject(); if ($userObject === null) { return false; } if ($this->moduleOptions->getEnableUserState()) { // Don't allow user to login if state is not in allowed list if (!in_array($userObject->getState(), $this->moduleOptions->getAllowedLoginStates())) { $this->event ->setCode(AuthenticationResult::FAILURE_UNCATEGORIZED) ->setMessages(["Ce compte utilisateur a été désactivé"]); $this->setSatisfied(false); return false; } } $result = $this->authenticateUserObject($userObject); if ($result === false) { return false; } // regen the id $session = new SessionContainer($this->getStorage()->getNamespace()); $session->getManager()->regenerateId(); // Success! $identity = $this->createSessionIdentity($userObject->getUsername()); $this->event->setIdentity($identity); $this->setSatisfied(true); $storage = $this->getStorage()->read(); $storage['identity'] = $this->event->getIdentity(); $this->getStorage()->write($storage); $this->event ->setCode(AuthenticationResult::SUCCESS) ->setMessages(array('Authentication successful.')); return true; } /** * @return \ZfcUser\Entity\UserInterface|null */ abstract protected function fetchUserObject(): ?UserInterface; /** * @param \ZfcUser\Entity\UserInterface $userObject * @return bool */ abstract protected function authenticateUserObject(UserInterface $userObject): bool; /** * setMapper * * @param UserMapperInterface $mapper * @return self */ public function setMapper(UserMapperInterface $mapper): self { $this->mapper = $mapper; return $this; } } No newline at end of file src/UnicaenAuth/Authentication/Adapter/AdapterChainServiceFactory.php +7 −6 Original line number Diff line number Diff line <?php namespace UnicaenAuth\Authentication\Adapter; use Interop\Container\ContainerInterface; Loading @@ -7,19 +8,19 @@ use ZfcUser\Options\ModuleOptions; class AdapterChainServiceFactory { public function __invoke(ContainerInterface $container, $requestedName, array $options = null) public function __invoke(ContainerInterface $container, $requestedName, array $options = null): AdapterChain { $chain = new AdapterChain(); $options = $this->getOptions($container); $enabledTypes = array_keys($options->getEnabledAuthTypes()); // types d'auth activés //iterate and attach multiple adapters and events if offered // on attache chaque adapter uniquement s'il est activé foreach ($options->getAuthAdapters() as $priority => $adapterName) { /** @var AbstractAdapter $adapter */ $adapter = $container->get($adapterName); if (in_array($adapter->getType(), $enabledTypes)) { $adapter->attach($chain->getEventManager()); $adapter->attach($chain->getEventManager(), $priority); } } Loading @@ -35,9 +36,9 @@ class AdapterChainServiceFactory * set options * * @param ModuleOptions $options * @return AdapterChainServiceFactory * @return self */ public function setOptions(ModuleOptions $options) public function setOptions(ModuleOptions $options): self { $this->options = $options; return $this; Loading @@ -49,7 +50,7 @@ class AdapterChainServiceFactory * @param ContainerInterface|null $container (optional) Service Locator * @return ModuleOptions $options */ public function getOptions(ContainerInterface $container = null) public function getOptions(ContainerInterface $container = null): ModuleOptions { if (!$this->options) { if (!$container) { Loading src/UnicaenAuth/Authentication/Adapter/Cas.php +0 −28 Original line number Diff line number Diff line Loading @@ -98,10 +98,6 @@ class Cas extends AbstractAdapter return true; } if (! $this->isEnabled()) { return false; } error_reporting($oldErrorReporting = error_reporting() & ~E_NOTICE); $this->getCasClient()->forceAuthentication(); Loading Loading @@ -131,23 +127,6 @@ class Cas extends AbstractAdapter return true; } /** * @return bool */ protected function isEnabled(): bool { $config = $this->moduleOptions->getCas(); if (! $config) { return false; } if (isset($config['enabled'])) { return (bool) $config['enabled']; } return true; } /** * @inheritDoc */ Loading @@ -155,10 +134,6 @@ class Cas extends AbstractAdapter { parent::logout($e); if (! $this->isEnabled()) { return; } $storage = $this->getStorage()->read(); if (! isset($storage['identity'])) { return; Loading Loading @@ -247,9 +222,6 @@ class Cas extends AbstractAdapter */ public function reconfigureRoutesForCasAuth(RouteInterface $router) { if (! $this->isEnabled()) { return; } if(!$router instanceof RouteStackInterface) { return; } Loading Loading
CHANGELOG.md +2 −0 Original line number Diff line number Diff line Loading @@ -3,8 +3,10 @@ CHANGELOG 3.2.1 ----- - Modifications/améliorations pour faciliter le support d'autres modes d'authentification (ex: unicaen/auth-token). - Le type d'authentification souhaité (local, shib ou cas) peut être spécifié dans l'URL de redirection via le query param 'authtype' - Ajout de la colonne CREATED_AT dans les scripts SQL de création de la table USER (non mappée dans l'entité). - [FIX] Usurpation d'un compte local (db) depuis une authentification shib - [FIX] Une chaîne vide doit être considérée comme null dans ShibService::extractShibUserIdValueForDomainFromShibData() - [FIX] Nécessité de clés littérales dans la config par domaine de 'shib_user_id_extractor' sinon doublons lors de la Loading
config/module.config.php +22 −6 Original line number Diff line number Diff line <?php namespace UnicaenAuth; use UnicaenAuth\Authentication\Adapter\AdapterChainServiceFactory; use UnicaenAuth\Authentication\Adapter\Cas; use UnicaenAuth\Authentication\Adapter\CasAdapterFactory; Loading @@ -18,6 +20,7 @@ use UnicaenAuth\Authentication\Storage\LdapFactory; use UnicaenAuth\Authentication\Storage\ShibFactory; use UnicaenAuth\Authentication\Storage\Usurpation; use UnicaenAuth\Authentication\Storage\UsurpationFactory; use UnicaenAuth\Controller\AuthController; use UnicaenAuth\Controller\AuthControllerFactory; use UnicaenAuth\Controller\DroitsControllerFactory; use UnicaenAuth\Controller\UtilisateurControllerFactory; Loading @@ -30,12 +33,14 @@ use UnicaenAuth\Form\ShibLoginForm; use UnicaenAuth\Form\ShibLoginFormFactory; use UnicaenAuth\Guard\PrivilegeControllerFactory; use UnicaenAuth\Guard\PrivilegeRouteFactory; use UnicaenAuth\Options\ModuleOptions; use UnicaenAuth\ORM\Event\Listeners\HistoriqueListenerFactory; use UnicaenAuth\Provider\Rule\PrivilegeRuleProviderFactory; use UnicaenAuth\Service\CasService; use UnicaenAuth\Service\CasServiceFactory; use UnicaenAuth\Service\ShibService; use UnicaenAuth\Service\ShibServiceFactory; use UnicaenAuth\Service\UserContext; use UnicaenAuth\Service\UserContextFactory; use UnicaenAuth\Service\UserFactory; use UnicaenAuth\Service\UserMapperFactory; Loading Loading @@ -70,6 +75,15 @@ use Zend\Authentication\AuthenticationService; use Zend\ServiceManager\Proxy\LazyServiceFactory; $settings = [ /** * Tous les types d'authentification supportés par le module unicaen/auth. */ 'auth_types' => [ 'local', // càd 'ldap' et 'db' 'cas', 'shib', ], /** * Configuration de l'authentification centralisée (CAS). */ Loading Loading @@ -629,21 +643,22 @@ return [ // in /var/www/sygal/module/Application/src/Application/Controller/UtilisateurController.php on line 34 'service_manager' => [ 'aliases' => [ 'unicaen-auth_module_options' => ModuleOptions::class, 'zfcuser_login_form' => LoginForm::class, 'Zend\Authentication\AuthenticationService' => 'zfcuser_auth_service', 'UnicaenAuth\Privilege\PrivilegeProvider' => 'UnicaenAuth\Service\Privilege', '\UnicaenAuth\Guard\PrivilegeController' => 'UnicaenAuth\Guard\PrivilegeController', 'unicaen-auth_user_service' => 'UnicaenAuth\Service\User', // pour la compatibilité 'authUserContext' => 'UnicaenAuth\Service\UserContext', // pour la compatibilité 'AuthUserContext' => 'UnicaenAuth\Service\UserContext', // pour la compatibilité 'authUserContext' => UserContext::class, // pour la compatibilité 'AuthUserContext' => UserContext::class, // pour la compatibilité ], 'invokables' => [ 'UnicaenAuth\View\RedirectionStrategy' => 'UnicaenAuth\View\RedirectionStrategy', 'UnicaenAuth\Service\CategoriePrivilege' => 'UnicaenAuth\Service\CategoriePrivilegeService', ], 'factories' => [ 'unicaen-auth_module_options' => 'UnicaenAuth\Options\ModuleOptionsFactory', ModuleOptions::class => 'UnicaenAuth\Options\ModuleOptionsFactory', 'zfcuser_auth_service' => 'UnicaenAuth\Authentication\AuthenticationServiceFactory', 'UnicaenAuth\Authentication\Storage\Chain' => 'UnicaenAuth\Authentication\Storage\ChainServiceFactory', 'UnicaenAuth\Provider\Identity\Chain' => 'UnicaenAuth\Provider\Identity\ChainServiceFactory', Loading @@ -659,7 +674,7 @@ return [ 'zfcuser_redirect_callback' => 'UnicaenAuth\Authentication\RedirectCallbackFactory', // substituion CasService::class => CasServiceFactory::class, ShibService::class => ShibServiceFactory::class, 'UnicaenAuth\Service\UserContext' => UserContextFactory::class, UserContext::class => UserContextFactory::class, 'zfcuser_user_mapper' => UserMapperFactory::class, 'MouchardCompleterAuth' => 'UnicaenAuth\Mouchard\MouchardCompleterAuthFactory', LocalAdapter::class => LocalAdapterFactory::class, Loading Loading @@ -708,10 +723,11 @@ return [ ], 'controllers' => [ 'invokables' => [ 'aliases' => [ 'UnicaenAuth\Controller\Auth' => AuthController::class, ], 'factories' => [ 'UnicaenAuth\Controller\Auth' => AuthControllerFactory::class, AuthController::class => AuthControllerFactory::class, 'UnicaenAuth\Controller\Utilisateur' => UtilisateurControllerFactory::class, 'UnicaenAuth\Controller\Droits' => DroitsControllerFactory::class, ], Loading
src/UnicaenAuth/Authentication/Adapter/AbstractDb.php 0 → 100644 +125 −0 Original line number Diff line number Diff line <?php namespace UnicaenAuth\Authentication\Adapter; use UnicaenAuth\Options\Traits\ModuleOptionsAwareTrait; use Zend\Authentication\Result as AuthenticationResult; use Zend\EventManager\EventInterface; use Zend\Session\Container as SessionContainer; use ZfcUser\Authentication\Adapter\AdapterChainEvent; use ZfcUser\Entity\UserInterface; use ZfcUser\Mapper\UserInterface as UserMapperInterface; /** * Classe abstraite des adpater d'authentification à partir de la base de données. * * Ajout par rapport à la classe mère : si aucune base de données ou table n'existe, * l'authentification ne plante pas (i.e. renvoit false). * * @author Bertrand GAUTHIER <bertrand.gauthier@unicaen.fr> */ abstract class AbstractDb extends AbstractAdapter { use ModuleOptionsAwareTrait; /** * @var string */ protected $type; /** * @var AdapterChainEvent */ protected $event; /** * @var UserMapperInterface */ protected $mapper; /** * @inheritDoc */ public function authenticate(EventInterface $e): bool { // NB: Dans la version 3.0.0 de zf-commons/zfc-user, cette méthode prend un EventInterface. // Mais dans la branche 3.x, c'est un AdapterChainEvent ! // Si un jour c'est un AdapterChainEvent qui est attendu, plus besoin de faire $e->getTarget(). $this->event = $e->getTarget(); if ($this->event->getIdentity()) { return true; } if ($this->isSatisfied()) { $storage = $this->getStorage()->read(); $this->event ->setIdentity($storage['identity']) ->setCode(AuthenticationResult::SUCCESS) ->setMessages(array('Authentication successful.')); return true; } $userObject = $this->fetchUserObject(); if ($userObject === null) { return false; } if ($this->moduleOptions->getEnableUserState()) { // Don't allow user to login if state is not in allowed list if (!in_array($userObject->getState(), $this->moduleOptions->getAllowedLoginStates())) { $this->event ->setCode(AuthenticationResult::FAILURE_UNCATEGORIZED) ->setMessages(["Ce compte utilisateur a été désactivé"]); $this->setSatisfied(false); return false; } } $result = $this->authenticateUserObject($userObject); if ($result === false) { return false; } // regen the id $session = new SessionContainer($this->getStorage()->getNamespace()); $session->getManager()->regenerateId(); // Success! $identity = $this->createSessionIdentity($userObject->getUsername()); $this->event->setIdentity($identity); $this->setSatisfied(true); $storage = $this->getStorage()->read(); $storage['identity'] = $this->event->getIdentity(); $this->getStorage()->write($storage); $this->event ->setCode(AuthenticationResult::SUCCESS) ->setMessages(array('Authentication successful.')); return true; } /** * @return \ZfcUser\Entity\UserInterface|null */ abstract protected function fetchUserObject(): ?UserInterface; /** * @param \ZfcUser\Entity\UserInterface $userObject * @return bool */ abstract protected function authenticateUserObject(UserInterface $userObject): bool; /** * setMapper * * @param UserMapperInterface $mapper * @return self */ public function setMapper(UserMapperInterface $mapper): self { $this->mapper = $mapper; return $this; } } No newline at end of file
src/UnicaenAuth/Authentication/Adapter/AdapterChainServiceFactory.php +7 −6 Original line number Diff line number Diff line <?php namespace UnicaenAuth\Authentication\Adapter; use Interop\Container\ContainerInterface; Loading @@ -7,19 +8,19 @@ use ZfcUser\Options\ModuleOptions; class AdapterChainServiceFactory { public function __invoke(ContainerInterface $container, $requestedName, array $options = null) public function __invoke(ContainerInterface $container, $requestedName, array $options = null): AdapterChain { $chain = new AdapterChain(); $options = $this->getOptions($container); $enabledTypes = array_keys($options->getEnabledAuthTypes()); // types d'auth activés //iterate and attach multiple adapters and events if offered // on attache chaque adapter uniquement s'il est activé foreach ($options->getAuthAdapters() as $priority => $adapterName) { /** @var AbstractAdapter $adapter */ $adapter = $container->get($adapterName); if (in_array($adapter->getType(), $enabledTypes)) { $adapter->attach($chain->getEventManager()); $adapter->attach($chain->getEventManager(), $priority); } } Loading @@ -35,9 +36,9 @@ class AdapterChainServiceFactory * set options * * @param ModuleOptions $options * @return AdapterChainServiceFactory * @return self */ public function setOptions(ModuleOptions $options) public function setOptions(ModuleOptions $options): self { $this->options = $options; return $this; Loading @@ -49,7 +50,7 @@ class AdapterChainServiceFactory * @param ContainerInterface|null $container (optional) Service Locator * @return ModuleOptions $options */ public function getOptions(ContainerInterface $container = null) public function getOptions(ContainerInterface $container = null): ModuleOptions { if (!$this->options) { if (!$container) { Loading
src/UnicaenAuth/Authentication/Adapter/Cas.php +0 −28 Original line number Diff line number Diff line Loading @@ -98,10 +98,6 @@ class Cas extends AbstractAdapter return true; } if (! $this->isEnabled()) { return false; } error_reporting($oldErrorReporting = error_reporting() & ~E_NOTICE); $this->getCasClient()->forceAuthentication(); Loading Loading @@ -131,23 +127,6 @@ class Cas extends AbstractAdapter return true; } /** * @return bool */ protected function isEnabled(): bool { $config = $this->moduleOptions->getCas(); if (! $config) { return false; } if (isset($config['enabled'])) { return (bool) $config['enabled']; } return true; } /** * @inheritDoc */ Loading @@ -155,10 +134,6 @@ class Cas extends AbstractAdapter { parent::logout($e); if (! $this->isEnabled()) { return; } $storage = $this->getStorage()->read(); if (! isset($storage['identity'])) { return; Loading Loading @@ -247,9 +222,6 @@ class Cas extends AbstractAdapter */ public function reconfigureRoutesForCasAuth(RouteInterface $router) { if (! $this->isEnabled()) { return; } if(!$router instanceof RouteStackInterface) { return; } Loading