Commit abb31635 authored by Bertrand Gauthier's avatar Bertrand Gauthier
Browse files

Possibilité de connaître la source de l'authentification (db, ldap, cas, shib).

Amélioration de l'usurpation d'identité, possibilité de stopper l'usurpation en cours.
parent ea6cd554
Pipeline #9329 passed with stage
in 1 minute and 13 seconds
......@@ -38,6 +38,8 @@ Première version officielle sous ZF3.
-----
- Aide de vue UserUsurpationHelper : ajout de la possibilité de dessiner un simple bouton.
3.1.3
3.2.0
-----
- [FIX] Usurpation d'un compte local en BDD
- Possibilité de connaître la source de l'authentification (db, ldap, cas, shib).
- Possibilité de stopper l'usurpation en cours pour revenir à l'identité d'origine.
- [FIX] Usurpation d'un compte local en BDD.
......@@ -75,15 +75,6 @@ class Module implements AutoloaderProviderInterface, ConfigProviderInterface, Se
},
//===========================================
// verrue pour forcer le label de l'identifiant qqsoit l'options 'auth_identity_fields'
'zfcuser_login_form' => function ($sm) {
$options = $sm->get('zfcuser_module_options');
$form = new Login(null, $options);
$form->setInputFilter(new LoginFilter($options));
$form->get('identity')->setLabel("Username");
return $form;
},
],
];
}
......
<?php
use UnicaenAuth\Authentication\Adapter\AdapterChainServiceFactory;
use UnicaenAuth\Authentication\Adapter\Cas;
use UnicaenAuth\Authentication\Adapter\CasAdapterFactory;
use UnicaenAuth\Authentication\Adapter\Db;
use UnicaenAuth\Authentication\Adapter\DbAdapterFactory;
use UnicaenAuth\Authentication\Adapter\Ldap;
use UnicaenAuth\Authentication\Adapter\LdapAdapterFactory;
use UnicaenAuth\Authentication\Adapter\LocalAdapter;
use UnicaenAuth\Authentication\Adapter\LocalAdapterFactory;
use UnicaenAuth\Authentication\Adapter\Shib;
use UnicaenAuth\Authentication\Adapter\ShibAdapterFactory;
use UnicaenAuth\Authentication\Storage\Auth;
use UnicaenAuth\Authentication\Storage\AuthFactory;
use UnicaenAuth\Authentication\Storage\DbFactory;
use UnicaenAuth\Authentication\Storage\LdapFactory;
use UnicaenAuth\Authentication\Storage\ShibFactory;
use UnicaenAuth\Authentication\Storage\Usurpation;
use UnicaenAuth\Authentication\Storage\UsurpationFactory;
use UnicaenAuth\Controller\AuthControllerFactory;
use UnicaenAuth\Controller\DroitsControllerFactory;
use UnicaenAuth\Controller\UtilisateurControllerFactory;
use UnicaenAuth\Form\CasLoginForm;
use UnicaenAuth\Form\CasLoginFormFactory;
use UnicaenAuth\Form\Droits\RoleFormFactory;
use UnicaenAuth\Form\LoginFormFactory;
use UnicaenAuth\Form\LoginForm;
use UnicaenAuth\Form\ShibLoginForm;
use UnicaenAuth\Form\ShibLoginFormFactory;
use UnicaenAuth\Guard\PrivilegeControllerFactory;
......@@ -59,9 +71,9 @@ use Zend\ServiceManager\Proxy\LazyServiceFactory;
$settings = [
/**
* Configuration de l'authentification via la fédération d'identité (Shibboleth).
* Configuration de l'authentification centralisée (CAS).
*/
'shib' => [
'cas' => [
/**
* Ordre d'affichage du formulaire de connexion.
*/
......@@ -70,12 +82,77 @@ $settings = [
/**
* Activation ou non de ce mode d'authentification.
*/
'enabled' => false,
'enabled' => true,
/**
* Description facultative de ce mode d'authentification qui apparaîtra sur la page de connexion.
*/
'description' => "Cliquez sur le bouton ci-dessous pour accéder à l'authentification centralisée.",
/**
* Adapter compétent pour réaliser l'authentification de l'utilisateur.
*/
'adapter' => Cas::class,
/**
* Service/formulaire d'authentification à utiliser.
*/
'form' => CasLoginForm::class,
/**
* Infos de connexion au serveur CAS.
*/
'connection' => [
'default' => [
'params' => [
'hostname' => 'host.domain.fr',
'port' => 443,
'version' => "2.0",
'uri' => "",
'debug' => false,
],
],
]
],
/**
* Configuration de l'authentification locale (compte LDAP établissement, ou compte BDD application).
*/
'local' => [
'order' => 2,
'enabled' => true,
'description' => "Utilisez ce formulaire si vous possédez un compte LDAP établissement ou un compte local dédié à l'application.",
'form' => LoginForm::class,
/**
* Mode d'authentification à l'aide d'un compte dans la BDD de l'application.
*/
'db' => [
'enabled' => true, // doit être activé pour que l'usurpation fonctionne (cf. Authentication/Storage/Db::read()) :-/ todo: faire mieux
'adapter' => Db::class,
'form' => LoginForm::class,
],
/**
* Description facultative de ce mode d'authentification qui apparaîtra sur le formulaire de connexion.
* Mode d'authentification à l'aide d'un compte LDAP.
*/
'ldap' => [
'enabled' => true,
'adapter' => Ldap::class,
'form' => LoginForm::class,
],
],
/**
* Configuration de l'authentification via la fédération d'identité (Shibboleth).
*/
'shib' => [
'order' => 3,
'enabled' => false,
'description' => "Cliquez sur le bouton ci-dessous pour accéder à l'authentification via la fédération d'identité.",
'adapter' => Shib::class,
'form' => ShibLoginForm::class,
/**
* URL de déconnexion.
......@@ -112,68 +189,6 @@ $settings = [
*/
],
/**
* Configuration de l'authentification LDAP (compte établissement).
*/
'ldap' => [
'order' => 2,
'enabled' => true,
'description' => "Utilisez ce formulaire pour vous connecter avec votre compte numérique établissement.",
/**
* Type de substitution.
* Permet de "fusionner" les types d'authentification locale (db) et établissement (ldap) et donc leurs
* formulaires de connexion respectifs.
*/
'type' => 'local',
],
/**
* Configuration de l'authentification locale (compte propre à l'appli).
*/
'db' => [
'order' => 3,
'enabled' => false,
/**
* Type de substitution.
* Permet de "grouper" les types d'authentification locale (db) et établissement (ldap) sous un même
* formulaire de connexion.
*/
'type' => 'local',
/**
* Description facultative de ce mode d'authentification qui apparaîtra sur le formulaire d'authentification.
* NB: si la valeur de 'order' pour le type 'db' est supérieure à celle pour le type 'ldap',
* c'est cette description qui sera visible.
*/
'description' => "Utilisez ce formulaire si vous possédez un compte local propre à l'application.",
],
/**
* Configuration de l'authentification centralisée (CAS).
*/
'cas' => [
'order' => 4,
'enabled' => false,
'description' => "Cliquez sur le bouton ci-dessous pour accéder à l'authentification centralisée.",
/**
* Infos de connexion au serveur CAS.
*/
'connection' => [
'default' => [
'params' => [
'hostname' => 'host.domain.fr',
'port' => 443,
'version' => "2.0",
'uri' => "",
'debug' => false,
],
],
]
],
/**
* Fournisseurs d'identité.
*/
......@@ -239,9 +254,8 @@ return [
* Default value: array containing 'ZfcUser\Authentication\Adapter\Db' with priority 100
* Accepted values: array containing services that implement 'ZfcUser\Authentication\Adapter\ChainableAdapter'
*/
'auth_adapters' => [
300 => 'UnicaenAuth\Authentication\Adapter\Ldap',
200 => 'UnicaenAuth\Authentication\Adapter\Db',
'auth_adapters' => [
400 => LocalAdapter::class, // délègue à Db et Ldap
100 => 'UnicaenAuth\Authentication\Adapter\Cas',
50 => 'UnicaenAuth\Authentication\Adapter\Shib',
],
......@@ -295,6 +309,8 @@ return [
['controller' => 'UnicaenApp\Controller\Application', 'action' => 'informatique-et-libertes', 'roles' => 'guest'],
['controller' => 'UnicaenApp\Controller\Application', 'action' => 'refresh-session', 'roles' => 'guest'],
['controller' => 'UnicaenAuth\Controller\Utilisateur', 'action' => 'selectionner-profil', 'roles' => 'guest'],
['controller' => 'UnicaenAuth\Controller\Utilisateur', 'action' => 'usurper-identite', 'roles' => 'guest'],
['controller' => 'UnicaenAuth\Controller\Utilisateur', 'action' => 'stopper-usurpation', 'roles' => 'guest'],
['controller' => 'UnicaenAuth\Controller\Auth', 'action' => 'login', 'roles' => 'guest'],
['controller' => 'UnicaenAuth\Controller\Auth', 'action' => 'authenticate', 'roles' => 'guest'],
......@@ -575,6 +591,7 @@ return [
// in /var/www/sygal/module/Application/src/Application/Controller/UtilisateurController.php on line 34
'service_manager' => [
'aliases' => [
'zfcuser_login_form' => LoginForm::class,
'Zend\Authentication\AuthenticationService' => 'zfcuser_auth_service',
'UnicaenAuth\Privilege\PrivilegeProvider' => 'UnicaenAuth\Service\Privilege',
'\UnicaenAuth\Guard\PrivilegeController' => 'UnicaenAuth\Guard\PrivilegeController',
......@@ -607,6 +624,7 @@ return [
'UnicaenAuth\Service\UserContext' => UserContextFactory::class,
'zfcuser_user_mapper' => UserMapperFactory::class,
'MouchardCompleterAuth' => 'UnicaenAuth\Mouchard\MouchardCompleterAuthFactory',
LocalAdapter::class => LocalAdapterFactory::class,
'UnicaenAuth\Authentication\Adapter\Ldap' => LdapAdapterFactory::class,
'UnicaenAuth\Authentication\Adapter\Db' => DbAdapterFactory::class,
'UnicaenAuth\Authentication\Adapter\Cas' => CasAdapterFactory::class,
......@@ -614,11 +632,16 @@ return [
'UnicaenAuth\Authentication\Storage\Db' => DbFactory::class,
'UnicaenAuth\Authentication\Storage\Ldap' => LdapFactory::class,
'UnicaenAuth\Authentication\Storage\Shib' => ShibFactory::class,
Usurpation::class => UsurpationFactory::class,
Auth::class => AuthFactory::class,
'UnicaenAuth\Service\User' => UserFactory::class,
'UnicaenAuth\Guard\PrivilegeController' => PrivilegeControllerFactory::class,
'UnicaenAuth\Guard\PrivilegeRoute' => PrivilegeRouteFactory::class,
'UnicaenAuth\Provider\Rule\PrivilegeRuleProvider' => PrivilegeRuleProviderFactory::class,
// verrue pour forcer le label de l'identifiant qqsoit l'options 'auth_identity_fields'
LoginForm::class => LoginFormFactory::class,
CasLoginForm::class => CasLoginFormFactory::class,
ShibLoginForm::class => ShibLoginFormFactory::class,
'ZfcUser\Authentication\Adapter\AdapterChain' => AdapterChainServiceFactory::class,
......
<?php
use UnicaenAuth\Authentication\Adapter\Shib;
use UnicaenAuth\Authentication\Adapter\Cas;
use UnicaenAuth\Authentication\Adapter\Ldap;
use UnicaenAuth\Authentication\Adapter\Db;
return [
'unicaen-auth' => [
/**
* Authentification LDAP (compte établissement).
* Configuration de l'authentification centralisée (CAS).
*/
'ldap' => [
'cas' => [
/**
* Ordre d'affichage du formulaire de connexion.
*/
......@@ -23,39 +18,9 @@ return [
'enabled' => true,
/**
* Type de substitution.
* Permet de "fusionner" les types d'authentification applicative (db) et établissement (ldap) et donc leurs
* formulaires de connexion respectifs.
*/
'type' => 'local',
/**
* Description facultative de ce mode d'authentification qui apparaîtra sur le formulaire de connexion.
* Description facultative de ce mode d'authentification qui apparaîtra sur la page de connexion.
*/
'description' => "Utilisez ce formulaire si vous possédez un compte établissement.",
],
/**
* Authentification BDD (compte dédié à l'appli).
*/
'db' => [
'order' => 2,
'enabled' => true,
'type' => 'local',
/**
* Description facultative de ce mode d'authentification qui apparaîtra sur le formulaire d'authentification.
* (NB: Si l'authentification LDAP est également activée, c'est cette description qui sera utilisée)
*/
'description' => "Utilisez ce formulaire si vous possédez un compte local dédié à cette application.",
],
/**
* Authentification centralisée (CAS).
*/
'cas' => [
'order' => 3,
'enabled' => false,
'description' => "Cliquez sur le bouton ci-dessous pour accéder à l'authentification centralisée.",
/**
* Infos de connexion au serveur CAS.
......@@ -70,6 +35,28 @@ return [
'debug' => false,
],
],
]
],
/**
* Configuration de l'authentification locale (compte LDAP établissement, ou compte BDD application).
*/
'local' => [
'order' => 2,
'enabled' => true,
'description' => "Utilisez ce formulaire si vous possédez un compte LDAP établissement ou un compte local dédié à l'application.",
/**
* Mode d'authentification à l'aide d'un compte dans la BDD de l'application.
*/
'db' => [
'enabled' => true, // doit être activé pour que l'usurpation fonctionne (cf. Authentication/Storage/Db::read()) :-/
],
/**
* Mode d'authentification à l'aide d'un compte LDAP.
*/
'ldap' => [
'enabled' => true,
],
],
......@@ -77,7 +64,7 @@ return [
* Authentification via la fédération d'identité (Shibboleth).
*/
'shib' => [
'order' => 4,
'order' => 3,
'enabled' => false,
'description' =>
"Cliquez sur le bouton ci-dessous pour accéder à l'authentification via la fédération d'identité. " .
......
......@@ -2,18 +2,25 @@
namespace UnicaenAuth\Authentication\Adapter;
use Zend\Authentication\Storage;
use UnicaenAuth\Authentication\SessionIdentity;
use Zend\Authentication\Storage\StorageInterface;
use Zend\EventManager\EventInterface;
use Zend\EventManager\EventManagerInterface;
use Zend\EventManager\ListenerAggregateInterface;
use Zend\EventManager\ListenerAggregateTrait;
use ZfcUser\Authentication\Adapter\ChainableAdapter;
abstract class AbstractAdapter implements ChainableAdapter
abstract class AbstractAdapter implements ChainableAdapter, ListenerAggregateInterface
{
use ListenerAggregateTrait;
/**
* @var string
*/
protected $type;
/**
* @var Storage\StorageInterface
* @var StorageInterface
*/
protected $storage;
......@@ -27,29 +34,31 @@ abstract class AbstractAdapter implements ChainableAdapter
return $this;
}
/**
* @return string
*/
public function getType(): string
{
return $this->type;
}
/**
* Returns the persistent storage handler
*
* Session storage is used by default unless a different storage adapter has been set.
*
* @return Storage\StorageInterface
* @return StorageInterface
*/
public function getStorage()
public function getStorage(): StorageInterface
{
if (null === $this->storage) {
$this->setStorage(new Storage\Session(get_class($this)));
}
return $this->storage;
}
/**
* Sets the persistent storage handler
*
* @param Storage\StorageInterface $storage
* @return AbstractAdapter Provides a fluent interface
* @param StorageInterface $storage
* @return self Provides a fluent interface
*/
public function setStorage(Storage\StorageInterface $storage)
public function setStorage(StorageInterface $storage): self
{
$this->storage = $storage;
return $this;
......@@ -60,7 +69,7 @@ abstract class AbstractAdapter implements ChainableAdapter
*
* @return bool
*/
public function isSatisfied()
public function isSatisfied(): bool
{
$storage = $this->getStorage()->read();
return (isset($storage['is_satisfied']) && true === $storage['is_satisfied']);
......@@ -70,13 +79,41 @@ abstract class AbstractAdapter implements ChainableAdapter
* Set if this adapter is satisfied or not
*
* @param bool $bool
* @return AbstractAdapter
* @return self
*/
public function setSatisfied($bool = true)
public function setSatisfied($bool = true): self
{
$storage = $this->getStorage()->read() ?: array();
$storage['is_satisfied'] = $bool;
$this->getStorage()->write($storage);
return $this;
}
/**
* @param string $username
* @return SessionIdentity
*/
protected function createSessionIdentity(string $username): SessionIdentity
{
return SessionIdentity::newInstance($username, $this->type);
}
/**
* Called when user id logged out
*
* @param EventInterface $e
*/
public function logout(EventInterface $e)
{
$this->getStorage()->clear();
}
/**
* @inheritDoc
*/
public function attach(EventManagerInterface $events, $priority = 1)
{
$events->attach('authenticate', [$this, 'authenticate'], $priority);
$events->attach('logout', [$this, 'logout'], $priority);
}
}
......@@ -22,21 +22,21 @@ class AdapterChain extends \ZfcUser\Authentication\Adapter\AdapterChain
$this->getEventManager()->trigger('authenticate.pre', $e);
$result = $this->getEventManager()->triggerUntil(function ($test) {
return ($test instanceof Response);
$result = $this->getEventManager()->triggerUntil(function ($result) {
return $result === true || $result instanceof Response;
}, 'authenticate', $e);
if ($result->stopped()) {
if ($result->last() instanceof Response) {
return $result->last();
$last = $result->last();
if ($last === true || $last instanceof Response) {
return $last;
}
throw new Exception\AuthenticationEventException(
sprintf(
'Auth event was stopped without a response. Got "%s" instead',
is_object($result->last()) ? get_class($result->last()) : gettype($result->last())
)
);
// throw new Exception\AuthenticationEventException(
// sprintf(
// 'Auth event was stopped without a response. Got "%s" instead',
// is_object($result->last()) ? get_class($result->last()) : gettype($result->last())
// )
// );
}
if ($e->getIdentity()) {
......@@ -54,7 +54,7 @@ class AdapterChain extends \ZfcUser\Authentication\Adapter\AdapterChain
*
* @return Response|null
*/
public function logoutAdapters()
public function logoutAdapters(): ?Response
{
//Adapters might need to perform additional cleanup after logout
$responseCollection = $this->getEventManager()->triggerUntil(function ($test) {
......
......@@ -12,17 +12,14 @@ class AdapterChainServiceFactory
$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
foreach ($options->getAuthAdapters() as $priority => $adapterName) {
/** @var AbstractAdapter $adapter */
$adapter = $container->get($adapterName);
if (is_callable(array($adapter, 'authenticate'))) {
$chain->getEventManager()->attach('authenticate', array($adapter, 'authenticate'), $priority);
}
if (is_callable(array($adapter, 'logout'))) {
$chain->getEventManager()->attach('logout', array($adapter, 'logout'), $priority);
if (in_array($adapter->getType(), $enabledTypes)) {
$adapter->attach($chain->getEventManager());
}
}
......@@ -62,7 +59,7 @@ class AdapterChainServiceFactory
);
}
$this->setOptions($container->get('zfcuser_module_options'));
$this->setOptions($container->get('unicaen-auth_module_options'));
}
return $this->options;
......
......@@ -7,13 +7,11 @@ use phpCAS;
use UnicaenApp\Mapper\Ldap\People as LdapPeopleMapper;
use UnicaenAuth\Options\Traits\ModuleOptionsAwareTrait;
use UnicaenAuth\Service\User;
use Zend\Authentication\Exception\UnexpectedValueException;
use Zend\Authentication\Result as AuthenticationResult;
use Zend\EventManager\Event;
use Zend\EventManager\EventInterface;
use Zend\Router\RouteInterface;
use Zend\Router\RouteStackInterface;
use ZfcUser\Authentication\Adapter\ChainableAdapter;
use ZfcUser\Authentication\Adapter\AdapterChainEvent;
/**
* CAS authentication adpater
......@@ -73,22 +71,18 @@ class Cas extends AbstractAdapter
}
/**
* Réalise l'authentification.
*
* @param EventInterface $e
* @throws UnexpectedValueException
* @see ChainableAdapter
* @inheritDoc
*/
public function authenticate(EventInterface $e)
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().
$e = $e->getTarget();
$event = $e->getTarget(); /* @var $event AdapterChainEvent */
$type = $e->getRequest()->getPost()->get('type');
$type = $event->getRequest()->getPost()->get('type');
if ($type !== $this->type) {
return;
return false;
}
// if ($e->getIdentity()) {
......@@ -97,14 +91,15 @@ class Cas extends AbstractAdapter
/* DS : modification liée à une boucle infinie lors de l'authentification CAS */
if ($this->isSatisfied()) {
$storage = $this->getStorage()->read();
$e->setIdentity($storage['identity'])