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

Merge de la branche 1.2.0.x-dev

parents e491c9ce fc06f5fd
vendor/
.idea
\ No newline at end of file
.idea
......@@ -2,9 +2,18 @@
namespace UnicaenAuth;
use UnicaenAuth\Authentication\Adapter\Cas as CasAdapter;
use UnicaenAuth\Options\ModuleOptions;
use UnicaenAuth\Service\ShibService;
use Zend\EventManager\EventInterface;
use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
use Zend\ModuleManager\Feature\ConfigProviderInterface;
use Zend\ModuleManager\Feature\ServiceProviderInterface;
use Zend\ServiceManager\Exception\ServiceNotFoundException;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\View\Helper\Navigation;
use ZfcUser\Form\Login;
use ZfcUser\Form\LoginFilter;
/**
* Point d'entrée du module d'authentification Unicaen.
......@@ -14,7 +23,11 @@ use Zend\ModuleManager\Feature\ServiceProviderInterface;
class Module implements AutoloaderProviderInterface, ConfigProviderInterface, ServiceProviderInterface
{
/**
*
* @var ModuleOptions
*/
private $options;
/**
* @return array
* @see ConfigProviderInterface
*/
......@@ -23,10 +36,7 @@ class Module implements AutoloaderProviderInterface, ConfigProviderInterface, Se
return include __DIR__ . '/config/module.config.php';
}
/**
*
* @return array
* @see AutoloaderProviderInterface
*/
......@@ -44,8 +54,6 @@ class Module implements AutoloaderProviderInterface, ConfigProviderInterface, Se
];
}
/**
* This method is called once the MVC bootstrapping is complete,
* after the "loadModule.post" event, once $application->bootstrap() is called.
......@@ -54,9 +62,9 @@ class Module implements AutoloaderProviderInterface, ConfigProviderInterface, Se
*
* @see BootstrapListenerInterface
*/
public function onBootstrap(\Zend\EventManager\EventInterface $e)
/* @var \Zend\Mvc\MvcEvent $e */
public function onBootstrap(EventInterface $e)
{
/* @var \Zend\Mvc\MvcEvent $e */
$application = $e->getApplication();
/* @var $services \Zend\ServiceManager\ServiceManager */
$services = $application->getServiceManager();
......@@ -65,63 +73,45 @@ class Module implements AutoloaderProviderInterface, ConfigProviderInterface, Se
try {
$authorizeService = $services->get('BjyAuthorize\Service\Authorize');
/* @var $authorizeService \BjyAuthorize\Service\Authorize */
\Zend\View\Helper\Navigation::setDefaultAcl($authorizeService->getAcl());
\Zend\View\Helper\Navigation::setDefaultRole($authorizeService->getIdentity());
} catch (\Zend\ServiceManager\Exception\ServiceNotFoundException $snfe) {
Navigation::setDefaultAcl($authorizeService->getAcl());
Navigation::setDefaultRole($authorizeService->getIdentity());
} catch (ServiceNotFoundException $snfe) {
// pas de module BjyAuthorize : pas d'ACL
}
/* @var $options Options\ModuleOptions */
$options = $services->get('unicaen-auth_module_options');
/* @var $options ModuleOptions */
$this->options = $services->get('unicaen-auth_module_options');
// si l'auth CAS est demandée, modif de la route de connexion pour zapper le formulaire
if ($options->getCas() && php_sapi_name() !== 'cli') {
/* @var $router \Zend\Mvc\Router\Http\TreeRouteStack */
$router = $services->get('router');
$router->addRoutes([
// remplace les routes existantes (cf. config du module)
'zfcuser' => [
'type' => 'Literal',
'priority' => 1000,
'options' => [
'route' => '/auth',
'defaults' => [
'controller' => 'zfcuser',
'action' => 'index',
],
],
'may_terminate' => true,
'child_routes' => [
'login' => [
'type' => 'Literal',
'options' => [
'route' => '/connexion',
'defaults' => [
'controller' => 'zfcuser',
'action' => 'authenticate', // zappe l'action 'login'
],
],
],
'logout' => [
'type' => 'Literal',
'options' => [
'route' => '/deconnexion',
'defaults' => [
'controller' => 'zfcuser',
'action' => 'logout',
],
],
],
],
],
]);
}
$this->reconfigureRoutesForAuth($services);
}
/**
* @param ServiceLocatorInterface $sl
*/
private function reconfigureRoutesForAuth(ServiceLocatorInterface $sl)
{
/* @var $router \Zend\Mvc\Router\Http\TreeRouteStack */
$router = $sl->get('router');
// si l'auth CAS est activée, modif de la route de connexion pour zapper le formulaire d'auth maison.
$isCasEnable = (bool) $this->options->getCas();
if ($isCasEnable && php_sapi_name() !== 'cli') {
/** @var CasAdapter $casAdapter */
$casAdapter = $sl->get('UnicaenAuth\Authentication\Adapter\Cas');
$casAdapter->reconfigureRoutesForCasAuth($router);
}
// si l'auth Shibboleth est activée, modif de la route de déconnexion pour réaliser la déconnexion Shibboleth.
$shibOptions = $this->options->getShibboleth();
$isShibEnable = array_key_exists('enable', $shibOptions) && (bool) $shibOptions['enable'];
if ($isShibEnable && php_sapi_name() !== 'cli') {
/** @var ShibService $shibService */
$shibService = $sl->get(ShibService::class);
$shibService->reconfigureRoutesForShibAuth($router);
}
}
/**
*
* @return array
* @see ServiceProviderInterface
*/
......@@ -132,8 +122,8 @@ 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 \ZfcUser\Form\Login(null, $options);
$form->setInputFilter(new \ZfcUser\Form\LoginFilter($options));
$form = new Login(null, $options);
$form->setInputFilter(new LoginFilter($options));
$form->get('identity')->setLabel("Username");
return $form;
......
......@@ -4,7 +4,7 @@
"repositories": [
{
"type": "composer",
"url": "https://dev.unicaen.fr/packagist"
"url": "https://gest.unicaen.fr/packagist"
}
],
"require": {
......
<?php
use UnicaenAuth\Provider\Privilege\Privileges;
use UnicaenAuth\Controller\AuthControllerFactory;
use UnicaenAuth\Service\ShibService;
use UnicaenAuth\Service\ShibServiceFactory;
use UnicaenAuth\View\Helper\ShibConnectViewHelperFactory;
$settings = [
/**
* Fournisseurs d'identité.
*/
......@@ -119,6 +121,8 @@ return [
['controller' => 'UnicaenApp\Controller\Application', 'action' => 'informatique-et-libertes', 'roles' => []],
['controller' => 'UnicaenApp\Controller\Application', 'action' => 'refresh-session', 'roles' => []],
['controller' => 'UnicaenAuth\Controller\Utilisateur', 'action' => 'selectionner-profil', 'roles' => []],
['controller' => 'UnicaenAuth\Controller\Auth', 'action' => 'shibboleth', 'roles' => []],
],
],
],
......@@ -167,6 +171,27 @@ return [
],
'router' => [
'routes' => [
'auth' => [
'type' => 'Literal',
'options' => [
'route' => '/auth',
'defaults' => [
'controller' => 'UnicaenAuth\Controller\Auth',
],
],
'may_terminate' => false,
'child_routes' => [
'shibboleth' => [
'type' => 'Literal',
'options' => [
'route' => '/shibboleth',
'defaults' => [
'action' => 'shibboleth',
],
],
],
],
],
'zfcuser' => [
'type' => 'Literal',
'priority' => 1000,
......@@ -346,12 +371,11 @@ return [
'invokables' => [
'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\View\RedirectionStrategy' => 'UnicaenAuth\View\RedirectionStrategy',
'UnicaenAuth\Service\UserContext' => 'UnicaenAuth\Service\UserContext',
'UnicaenAuth\Service\User' => 'UnicaenAuth\Service\User',
'UnicaenAuth\Service\Privilege' => 'UnicaenAuth\Service\PrivilegeService',
'UnicaenAuth\Service\CategoriePrivilege' => 'UnicaenAuth\Service\CategoriePrivilegeService',
'UnicaenAuth\Service\Role' => 'UnicaenAuth\Service\RoleService',
],
'abstract_factories' => [
'UnicaenAuth\Authentication\Adapter\AbstractFactory',
......@@ -367,8 +391,11 @@ return [
'UnicaenAuth\Provider\Role\Config' => 'UnicaenAuth\Provider\Role\ConfigServiceFactory',
'UnicaenAuth\Provider\Role\DbRole' => 'UnicaenAuth\Provider\Role\DbRoleServiceFactory',
'UnicaenAuth\Provider\Role\Username' => 'UnicaenAuth\Provider\Role\UsernameServiceFactory',
'UnicaenAuth\Service\Role' => 'UnicaenAuth\Service\RoleServiceFactory',
'UnicaenAuth\Service\Privilege' => 'UnicaenAuth\Service\PrivilegeServiceFactory',
'BjyAuthorize\Service\Authorize' => 'UnicaenAuth\Service\AuthorizeServiceFactory', // substituion
'zfcuser_redirect_callback' => 'UnicaenAuth\Authentication\RedirectCallbackFactory', // substituion
ShibService::class => ShibServiceFactory::class,
'MouchardCompleterAuth' => 'UnicaenAuth\Mouchard\MouchardCompleterAuthFactory',
],
'shared' => [
......@@ -384,6 +411,9 @@ return [
'UnicaenAuth\Controller\Utilisateur' => 'UnicaenAuth\Controller\UtilisateurController',
'UnicaenAuth\Controller\Droits' => 'UnicaenAuth\Controller\DroitsController',
],
'factories' => [
'UnicaenAuth\Controller\Auth' => AuthControllerFactory::class,
],
],
'form_elements' => [
......@@ -401,6 +431,7 @@ return [
'userInfo' => 'UnicaenAuth\View\Helper\UserInfoFactory',
'userProfileSelect' => 'UnicaenAuth\View\Helper\UserProfileSelectFactory',
'userProfileSelectRadioItem' => 'UnicaenAuth\View\Helper\UserProfileSelectRadioItemFactory',
'shibConnect' => ShibConnectViewHelperFactory::class,
],
'invokables' => [
'appConnection' => 'UnicaenAuth\View\Helper\AppConnection',
......
......@@ -67,6 +67,20 @@ $config = [
if ($settings['enable_privileges']) {
$privileges = [
'unicaen-auth' => [
/**
* Classes représentant les entités rôle et privilège.
* - Entité rôle : héritant de \UnicaenAuth\Entity\Db\AbstractRole ou implémentant \UnicaenAuth\Entity\Db\RoleInterface.
* - Entité privilège : héritant de \UnicaenAuth\Entity\Db\AbstractPrivilege ou implémentant \UnicaenAuth\Entity\Db\PrivilegeInterface.
*
* Valeurs par défaut :
* - 'role_entity_class' : 'UnicaenAuth\Entity\Db\Role'
* - 'privilege_entity_class' : 'UnicaenAuth\Entity\Db\Privilege'
*/
'role_entity_class' => 'UnicaenAuth\Entity\Db\Role',
'privilege_entity_class' => 'UnicaenAuth\Entity\Db\Privilege',
],
'bjyauthorize' => [
'resource_providers' => [
......
......@@ -6,6 +6,12 @@
* drop this config file in it and change the values as you wish.
*/
$settings = [
/**
* Activation ou non de l'authentification Shibboleth.
*/
'shibboleth' => [
'enable' => false,
],
/**
* Paramètres de connexion au serveur CAS :
* - pour désactiver l'authentification CAS, le tableau 'cas' doit être vide.
......
<?php
namespace UnicaenAuth\Authentication\Adapter;
use UnicaenApp\Exception;
use UnicaenAuth\Authentication\Adapter\Cas;
use UnicaenAuth\Authentication\Adapter\Db;
use UnicaenAuth\Authentication\Adapter\Ldap;
use UnicaenApp\Exception\LogicException;
use Zend\EventManager\EventManager;
use Zend\EventManager\EventManagerAwareInterface;
use Zend\ServiceManager\AbstractFactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
......@@ -34,7 +34,7 @@ class AbstractFactory implements AbstractFactoryInterface
* @param ServiceLocatorInterface $serviceLocator
* @param $name
* @param $requestedName
* @return mixed
* @return \ZfcUser\Authentication\Adapter\AbstractAdapter
*/
public function createServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName)
{
......@@ -48,12 +48,19 @@ class AbstractFactory implements AbstractFactoryInterface
case __NAMESPACE__ . '\Cas':
$adapter = new Cas();
break;
//
// NB: pour faire simple, la stratégie de créer un adapter pour l'auth Shibboleth n'a pas été retenue.
//
// case __NAMESPACE__ . '\Shib':
// $adapter = new Shib();
// break;
default:
throw new Exception("Service demandé inattendu : '$requestedName'!");
throw new LogicException("Service demandé inattendu : '$requestedName'!");
break;
}
if ($adapter instanceof \Zend\EventManager\EventManagerAwareInterface) {
if ($adapter instanceof EventManagerAwareInterface) {
/** @var EventManager $eventManager */
$eventManager = $serviceLocator->get('event_manager');
$adapter->setEventManager($eventManager);
$userService = $serviceLocator->get('unicaen-auth_user_service'); /* @var $userService \UnicaenAuth\Service\User */
......
......@@ -9,6 +9,7 @@ use Zend\Authentication\Result as AuthenticationResult;
use Zend\EventManager\EventManager;
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerInterface;
use Zend\Mvc\Router\Http\TreeRouteStack;
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\ServiceManagerAwareInterface;
use ZfcUser\Authentication\Adapter\AbstractAdapter;
......@@ -230,4 +231,48 @@ class Cas extends AbstractAdapter implements ServiceManagerAwareInterface, Event
$this->eventManager = $eventManager;
return $this;
}
/**
* @param TreeRouteStack $router
*/
public function reconfigureRoutesForCasAuth(TreeRouteStack $router)
{
$router->addRoutes([
// remplace les routes existantes (cf. config du module)
'zfcuser' => [
'type' => 'Literal',
'priority' => 1000,
'options' => [
'route' => '/auth',
'defaults' => [
'controller' => 'zfcuser',
'action' => 'index',
],
],
'may_terminate' => true,
'child_routes' => [
'login' => [
'type' => 'Literal',
'options' => [
'route' => '/connexion',
'defaults' => [
'controller' => 'zfcuser',
'action' => 'authenticate', // zappe l'action 'login'
],
],
],
'logout' => [
'type' => 'Literal',
'options' => [
'route' => '/deconnexion',
'defaults' => [
'controller' => 'zfcuser',
'action' => 'logout',
],
],
],
],
],
]);
}
}
\ No newline at end of file
<?php
namespace UnicaenAuth\Authentication\Adapter;
use UnicaenApp\Exception\RuntimeException;
use UnicaenApp\Mapper\Ldap\People as LdapPeopleMapper;
use UnicaenAuth\Options\ModuleOptions;
use Zend\Authentication\Exception\UnexpectedValueException;
use Zend\Authentication\Result as AuthenticationResult;
use UnicaenAuth\Service\User;
use Zend\Authentication\Adapter\Ldap as LdapAuthAdapter;
use Zend\Authentication\Result;
use Zend\EventManager\Event;
use Zend\Authentication\Exception\ExceptionInterface;
use Zend\Authentication\Result as AuthenticationResult;
use Zend\EventManager\EventManager;
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerInterface;
use Zend\Ldap\Exception\LdapException;
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\ServiceManagerAwareInterface;
use ZfcUser\Authentication\Adapter\AbstractAdapter;
......@@ -41,6 +42,11 @@ class Ldap extends AbstractAdapter implements ServiceManagerAwareInterface, Even
*/
protected $ldapAuthAdapter;
/**
* @var LdapPeopleMapper
*/
protected $ldapPeopleMapper;
/**
* @var ModuleOptions
*/
......@@ -55,16 +61,21 @@ class Ldap extends AbstractAdapter implements ServiceManagerAwareInterface, Even
*
* @param AuthEvent $e
* @return boolean
* @throws UnexpectedValueException
* @throws \Zend\Authentication\Adapter\Exception\ExceptionInterface
* @throws \Zend\Ldap\Exception\LdapException
* @see ChainableAdapter
*/
public function authenticate(AuthEvent $e)
{
if ($this->isSatisfied()) {
$storage = $this->getStorage()->read();
try {
$storage = $this->getStorage()->read();
} catch (ExceptionInterface $e) {
throw new RuntimeException("Erreur de lecture du storage");
}
$e->setIdentity($storage['identity'])
->setCode(AuthenticationResult::SUCCESS)
->setMessages(['Authentication successful.']);
->setCode(AuthenticationResult::SUCCESS)
->setMessages(['Authentication successful.']);
return;
}
......@@ -82,28 +93,69 @@ class Ldap extends AbstractAdapter implements ServiceManagerAwareInterface, Even
// Failure!
if (! $success) {
$e->setCode(AuthenticationResult::FAILURE)
->setMessages(['LDAP bind failed.']);
->setMessages(['LDAP bind failed.']);
$this->setSatisfied(false);
return false;
}
// recherche de l'individu dans l'annuaire LDAP
$ldapPeople = $this->getLdapPeopleMapper()->findOneByUsername($username);
if (!$ldapPeople) {
$e
->setCode(AuthenticationResult::FAILURE)
->setMessages(['Authentication failed.']);
$this->setSatisfied(false);
return false;
}
$e->setIdentity($this->usernameUsurpe ?: $username);
$this->setSatisfied(true);
$storage = $this->getStorage()->read();
$storage['identity'] = $e->getIdentity();
$this->getStorage()->write($storage);
try {
$storage = $this->getStorage()->read();
$storage['identity'] = $e->getIdentity();
$this->getStorage()->write($storage);
} catch (ExceptionInterface $e) {
throw new RuntimeException("Erreur de concernant le storage");
}
$e->setCode(AuthenticationResult::SUCCESS)
->setMessages(['Authentication successful.']);
->setMessages(['Authentication successful.']);
/* @var $userService User */
$userService = $this->getServiceManager()->get('unicaen-auth_user_service');
$userService->userAuthenticated($ldapPeople);
}
/**
* Extrait le loginUsurpateur et le loginUsurpé si l'identifiant spécifé est de la forme
* "loginUsurpateur=loginUsurpé".
*
* @param string $identifiant Identifiant, éventuellement de la forme "loginUsurpateur=loginUsurpé"
* @return array
* [loginUsurpateur, loginUsurpé] si l'identifiant est de la forme "loginUsurpateur=loginUsurpé" ;
* [] sinon.
*/
static public function extractUsernamesUsurpation($identifiant)
{
if (strpos($identifiant, self::USURPATION_USERNAMES_SEP) > 0) {
list($identifiant, $usernameUsurpe) = explode(self::USURPATION_USERNAMES_SEP, $identifiant, 2);
return [
$identifiant,
$usernameUsurpe
];
}
$this->getEventManager()->trigger('userAuthenticated', $e);
return [];
}
/**
* Authentifie l'identifiant et le mot de passe spécifiés.
*
* @param string $username Identifiant de connexion
* @param string $username Identifiant de connexion
* @param string $credential Mot de passe
* @return boolean
* @throws \Zend\Authentication\Adapter\Exception\ExceptionInterface
* @throws \Zend\Ldap\Exception\LdapException
*/
public function authenticateUsername($username, $credential)
{
......@@ -111,20 +163,17 @@ class Ldap extends AbstractAdapter implements ServiceManagerAwareInterface, Even
// - le format attendu est "loginUsurpateur=loginUsurpé"
// - le mot de passe attendu est celui du compte usurpateur (loginUsurpateur)
$this->usernameUsurpe = null;
if (strpos($username, self::USURPATION_USERNAMES_SEP) > 0) {
list($username, $this->usernameUsurpe) = explode(self::USURPATION_USERNAMES_SEP, $username, 2);
$usernames = self::extractUsernamesUsurpation($username);
if (count($usernames) === 2) {
list ($username, $this->usernameUsurpe) = $usernames;
if (!in_array($username, $this->getOptions()->getUsurpationAllowedUsernames())) {
$this->usernameUsurpe = null;
}
}
// LDAP auth
/** @var Result $result */
$result = $this->getLdapAuthAdapter()->setUsername($username)->setPassword($credential)->authenticate();
if ($result && count($result->getMessages())) {
// Obtenir le message LDAP
// $msg = preg_replace('/\[0x\d* \((.*)\):/','$1', $event->getParam('result')->getMessages()[1]);
......@@ -149,6 +198,31 @@ class Ldap extends AbstractAdapter implements ServiceManagerAwareInterface, Even
return $success;
}
/**
* get ldap people mapper
*
* @return LdapPeopleMapper
*/
public function getLdapPeopleMapper()
{
if (null === $this->ldapPeopleMapper) {
$this->ldapPeopleMapper = $this->getServiceManager()->get('ldap_people_mapper');
}
return $this->ldapPeopleMapper;
}
/**
* set ldap people mapper
*
* @param LdapPeopleMapper $mapper
* @return self
*/
public function setLdapPeopleMapper(LdapPeopleMapper $mapper)
{
$this->ldapPeopleMapper = $mapper;
return $this;
}
/**
* @param ModuleOptions $options
*/
......@@ -164,8 +238,8 @@ class Ldap extends AbstractAdapter implements ServiceManagerAwareInterface, Even
{
if (!$this->options instanceof ModuleOptions) {
$options = array_merge(
$this->getServiceManager()->get('zfcuser_module_options')->toArray(),
$this->getServiceManager()->get('unicaen-auth_module_options')->toArray());
$this->getServiceManager()->get('zfcuser_module_options')->toArray(),
$this->getServiceManager()->get('unicaen-auth_module_options')->toArray());
$this->setOptions(new ModuleOptions($options));
}
return $this->options;
......@@ -250,6 +324,10 @@ class Ldap extends AbstractAdapter implements ServiceManagerAwareInterface, Even
*/
public function setEventManager(EventManagerInterface $eventManager)
{