Skip to content
Snippets Groups Projects
Commit c81dccb5 authored by Bertrand Gauthier's avatar Bertrand Gauthier
Browse files

Annulation des commits resultant des erreurs de manip de Laurent.

Revert "Revert "Merge remote-tracking branch 'origin/master'""
This reverts commit 279a6e87.
parent 279a6e87
No related branches found
No related tags found
No related merge requests found
Showing
with 810 additions and 64 deletions
<?php
use UnicaenAuth\Authentication\Adapter\ShibSimulatorAdapter;
use UnicaenAuth\Authentication\Storage\ShibSimulatorStorage;
use UnicaenAuth\Controller\AuthControllerFactory;
use UnicaenAuth\Service\ShibService;
use UnicaenAuth\Service\ShibServiceFactory;
use UnicaenAuth\Service\UserContextFactory;
use UnicaenAuth\View\Helper\ShibConnectViewHelperFactory;
use UnicaenAuth\View\Helper\UserUsurpationHelperFactory;
$settings = [
/**
......@@ -373,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',
......@@ -432,6 +436,7 @@ return [
'userInfo' => 'UnicaenAuth\View\Helper\UserInfoFactory',
'userProfileSelect' => 'UnicaenAuth\View\Helper\UserProfileSelectFactory',
'userProfileSelectRadioItem' => 'UnicaenAuth\View\Helper\UserProfileSelectRadioItemFactory',
'userUsurpation' => UserUsurpationHelperFactory::class,
'shibConnect' => ShibConnectViewHelperFactory::class,
],
'invokables' => [
......
<?php
namespace UnicaenAuth\Authentication\Adapter;
use phpCAS;
use UnicaenApp\Exception;
use UnicaenApp\Mapper\Ldap\People as LdapPeopleMapper;
use UnicaenAuth\Options\ModuleOptions;
use UnicaenAuth\Service\User;
use Zend\Authentication\Exception\UnexpectedValueException;
use Zend\Authentication\Result as AuthenticationResult;
use Zend\EventManager\EventManager;
......@@ -48,6 +50,11 @@ class Cas extends AbstractAdapter implements ServiceManagerAwareInterface, Event
*/
protected $casClient;
/**
* @var LdapPeopleMapper
*/
protected $ldapPeopleMapper;
/**
* Réalise l'authentification.
*
......@@ -94,7 +101,12 @@ class Cas extends AbstractAdapter implements ServiceManagerAwareInterface, Event
$e->setCode(AuthenticationResult::SUCCESS)
->setMessages(['Authentication successful.']);
$this->getEventManager()->trigger('userAuthenticated', $e);
// recherche de l'individu dans l'annuaire LDAP (il existe forcément puisque l'auth CAS a réussi)
$ldapPeople = $this->getLdapPeopleMapper()->findOneByUsername($identity);
/* @var $userService User */
$userService = $this->getServiceManager()->get('unicaen-auth_user_service');
$userService->userAuthenticated($ldapPeople);
}
/**
......@@ -186,6 +198,31 @@ class Cas extends AbstractAdapter implements ServiceManagerAwareInterface, Event
return $this->options;
}
/**
* 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;
}
/**
* Get service manager
*
......
......@@ -12,11 +12,15 @@ use Zend\ServiceManager\ServiceLocatorInterface;
*/
class ChainServiceFactory implements FactoryInterface
{
protected $storages = [
private $mandatoryStorages = [
200 => 'UnicaenAuth\Authentication\Storage\Ldap',
100 => 'UnicaenAuth\Authentication\Storage\Db',
76 => 'UnicaenAuth\Authentication\Storage\ShibSimulatorStorage',
75 => 'UnicaenAuth\Authentication\Storage\Shib',
];
protected $storages = [];
/**
* Create service
*
......@@ -27,7 +31,10 @@ class ChainServiceFactory implements FactoryInterface
{
$chain = new Chain();
foreach ($this->storages as $priority => $name) {
$storages = $this->mandatoryStorages + $this->storages;
krsort($storages);
foreach ($storages as $priority => $name) {
$storage = $serviceLocator->get($name);
$chain->getEventManager()->attach('read', [$storage, 'read'], $priority);
$chain->getEventManager()->attach('write', [$storage, 'write'], $priority);
......
......@@ -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;
......@@ -25,11 +24,6 @@ class Shib implements ChainableStorage, ServiceLocatorAwareInterface
*/
protected $storage;
/**
* @var ModuleOptions
*/
protected $options;
/**
* @var ShibUser
*/
......@@ -51,15 +45,30 @@ class Shib implements ChainableStorage, ServiceLocatorAwareInterface
*/
public function read(ChainEvent $e)
{
/** @var ShibService $shib */
$shib = $this->getServiceLocator()->get(ShibService::class);
$shibUser = $shib->getAuthenticatedUser();
$shibUser = $this->getAuthenticatedUser();
$e->addContents('shib', $shibUser);
return $shibUser;
}
/**
* @return null|ShibUser
*/
private function getAuthenticatedUser()
{
if (null !== $this->resolvedIdentity) {
return $this->resolvedIdentity;
}
/** @var ShibService $shib */
$shib = $this->getServiceLocator()->get(ShibService::class);
$this->resolvedIdentity = $shib->getAuthenticatedUser();
return $this->resolvedIdentity;
}
/**
* Writes $contents to storage
*
......
<?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
......@@ -23,6 +23,18 @@ class AuthController extends AbstractActionController
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()
......@@ -30,44 +42,69 @@ class AuthController extends AbstractActionController
$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->isShibbolethEnable()) {
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
}
}
$shibUser = $this->shibService->getAuthenticatedUser();
if ($shibUser === null) {
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->getId());
$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);
}
public function shibboleth()
{
}
}
\ No newline at end of file
......@@ -2,16 +2,30 @@
namespace UnicaenAuth\Controller;
use UnicaenApp\Exception\RuntimeException;
use UnicaenApp\Mapper\Ldap\People as LdapPeopleMapper;
use UnicaenAuth\Entity\Db\AbstractUser;
use UnicaenAuth\Entity\Ldap\People;
use UnicaenAuth\Entity\Shibboleth\ShibUser;
use UnicaenAuth\Options\ModuleOptions;
use UnicaenAuth\Service\ShibService;
use UnicaenAuth\Service\UserContext;
use Zend\Authentication\AuthenticationService;
use Zend\Http\Request;
use Zend\Http\Response;
use Zend\Mvc\Controller\AbstractActionController;
use ZfcUser\Mapper\User as UserMapper;
/**
*
*
* @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr>
*/
class UtilisateurController extends AbstractActionController
{
/**
* @var LdapPeopleMapper
*/
protected $ldapPeopleMapper;
/**
* Traite les requêtes AJAX POST de sélection d'un profil utilisateur.
* La sélection est mémorisé en session par le service AuthUserContext.
......@@ -47,10 +61,125 @@ class UtilisateurController extends AbstractActionController
}
/**
* @return \UnicaenAuth\Service\UserContext
* Usurpe l'identité d'un autre utilisateur.
*
* @return Response
*/
public function usurperIdentiteAction()
{
$request = $this->getRequest();
if (! $request instanceof Request) {
exit(1);
}
$newIdentity = $request->getQuery('identity', $request->getPost('identity'));
if (! $newIdentity) {
return $this->redirect()->toRoute('home');
}
/** @var AuthenticationService $authenticationService */
$authenticationService = $this->getServiceLocator()->get(AuthenticationService::class);
$currentIdentity = $authenticationService->getIdentity();
if (! $currentIdentity || ! is_array($currentIdentity)) {
return $this->redirect()->toRoute('home');
}
if (isset($currentIdentity['ldap'])) {
// si l'identifiant demandé contient un @, on estime qu'il s'agit d'un eppn shibboleth : on autorise pas le mélange des genres!
// todo: faire mieux
if (strpos($newIdentity, '@') !== false) {
throw new RuntimeException("Usurpation Shibboleth interdite depuis une authentification LDAP");
}
/** @var People $currentIdentity */
$currentIdentity = $currentIdentity['ldap'];
// vérif existence de l'individu dans l'annuaire LDAP
$ldapPeople = $this->getLdapPeopleMapper()->findOneByUsername($newIdentity);
if (!$ldapPeople) {
throw new RuntimeException("Identifiant LDAP inconnu");
}
} elseif (isset($currentIdentity['shib'])) {
// si l'identifiant demandé ne contient pas @, on estime qu'il s'agit d'un identifiant LDAP : on autorise pas le mélange des genres!
// todo: faire mieux
if (strpos($newIdentity, '@') === false) {
throw new RuntimeException("Usurpation LDAP interdite depuis une authentification Shibboleth");
}
/** @var ShibUser $currentIdentity */
$currentIdentity = $currentIdentity['shib'];
}
else {
return $this->redirect()->toRoute('home');
}
// seuls les logins spécifiés dans la config sont habilités à usurper des identités
/** @var ModuleOptions $options */
$options = $this->getServiceLocator()->get('unicaen-auth_module_options');
if (! in_array($currentIdentity->getUsername(), $options->getUsurpationAllowedUsernames())) {
throw new RuntimeException("Usurpation non explicitement autorisée");
}
// cuisine spéciale pour Shibboleth
if ($currentIdentity instanceof ShibUser) {
$fromShibUser = $currentIdentity;
$toShibUser = $this->createShibUserFromUtilisateurUsername($newIdentity);
/** @var ShibService $shibService */
$shibService = $this->getServiceLocator()->get(ShibService::class);
$shibService->activateUsurpation($fromShibUser, $toShibUser);
}
$authenticationService->getStorage()->write($newIdentity);
return $this->redirect()->toRoute('home');
}
/**
* Recherche l'utilisateur dont le login est spécifié puis instancie un ShibUser à partir
* des attributs de cet utilisateur.
*
* @param string $username Ex tartempion@unicaen.fr
* @return ShibUser
*/
protected function createShibUserFromUtilisateurUsername($username)
{
/** @var AbstractUser $utilisateur */
$utilisateur = $this->getUserMapper()->findByUsername($username);
if ($utilisateur === null) {
throw new RuntimeException("L'utilisateur '$username' n'existe pas dans la table des utilisateurs");
}
$shibUser = new ShibUser();
$shibUser->setEppn($utilisateur->getUsername());
$shibUser->setId(uniqid()); // peut pas mieux faire pour l'instant
$shibUser->setDisplayName($utilisateur->getDisplayName());
$shibUser->setEmail($utilisateur->getEmail());
$shibUser->setNom('?'); // peut pas mieux faire pour l'instant
$shibUser->setPrenom('?'); // peut pas mieux faire pour l'instant
return $shibUser;
}
/**
* @return UserMapper
*/
public function getUserMapper()
{
return $this->getServiceLocator()->get('zfcuser_user_mapper');
}
/**
* @return UserContext
*/
protected function getAuthUserContextService()
{
return $this->getServiceLocator()->get('AuthUserContext');
}
/**
* @return LdapPeopleMapper
*/
public function getLdapPeopleMapper()
{
return $this->serviceLocator->get('ldap_people_mapper');
}
}
\ No newline at end of file
......@@ -55,6 +55,16 @@ class ShibUser implements UserInterface
return $this->getUsername();
}
/**
* Set eduPersoPrincipalName (EPPN).
*
* @param string $eppn eduPersoPrincipalName (EPPN), ex: 'gauthierb@unicaen.fr'
*/
public function setEppn($eppn)
{
$this->setUsername($eppn);
}
/**
* Get id.
*
......@@ -156,7 +166,7 @@ class ShibUser implements UserInterface
*/
public function getPrenom()
{
return $this->givenName;
return $this->prenom;
}
/**
......@@ -168,7 +178,7 @@ class ShibUser implements UserInterface
}
/**
* @return string
* @return string|null
*/
public function getCivilite()
{
......@@ -176,9 +186,9 @@ class ShibUser implements UserInterface
}
/**
* @param string $civilite
* @param string|null $civilite
*/
public function setCivilite($civilite)
public function setCivilite($civilite = null)
{
$this->civilite = $civilite;
}
......
......@@ -2,10 +2,14 @@
namespace UnicaenAuth\Service;
use Assert\Assertion;
use Assert\AssertionFailedException;
use UnicaenApp\Exception\LogicException;
use UnicaenApp\Exception\RuntimeException;
use UnicaenAuth\Entity\Shibboleth\ShibUser;
use UnicaenAuth\Options\ModuleOptions;
use Zend\Mvc\Router\Http\TreeRouteStack;
use Zend\Session\Container;
/**
* Shibboleth service
......@@ -44,35 +48,205 @@ EOS;
return $text;
}
/**
* @return ShibUser|null
*/
public function getAuthenticatedUser()
{
if (! $this->isShibbolethEnabled()) {
return null;
}
if ($this->authenticatedUser === null) {
// gestion de l'usurpation éventuelle
$this->handleUsurpation();
if (empty($_SERVER['REMOTE_USER'])) {
return null;
}
$this->authenticatedUser = $this->createShibUserFromServerArrayData();
}
return $this->authenticatedUser;
}
/**
* @return boolean
*/
public function isShibbolethEnable()
public function isShibbolethEnabled()
{
$options = $this->options->getShibboleth();
return array_key_exists('enable', $options) && (bool) $options['enable'];
}
/**
* @return array
*/
public function getShibbolethSimulate()
{
$options = $this->options->getShibboleth();
if (! array_key_exists('simulate', $options) || ! is_array($options['simulate'])) {
return [];
}
return $options['simulate'];
}
/**
* Retourne true si la simulation d'un utilisateur authentifié via Shibboleth est en cours.
*
* @return bool
*/
public function isSimulationActive()
{
$options = $this->options->getShibboleth();
if (array_key_exists('simulate', $options) && is_array($options['simulate']) && ! empty($options['simulate'])) {
return true;
}
return false;
}
/**
* @return ShibUser|null
*/
public function getAuthenticatedUser()
public function handleSimulation()
{
if ($this->authenticatedUser === null) {
if (empty($_SERVER['REMOTE_USER'])) {
if (! $this->isSimulationActive()) {
return null;
}
$this->authenticatedUser = $this->createShibUser();
// si nécessaire
$this->handleUsurpation();
$simulate = $this->getShibbolethSimulate();
try {
Assertion::keyIsset($simulate, 'eppn',
"Clé 'eppn' introuvable ou sans valeur dans l'option 'unicaen-auth.shibboleth.simulate'");
Assertion::eq(count(array_intersect($keys = ['supannEmpId', 'supannEtuId'], array_keys($simulate))), 1,
"L'une ou l'autre de ces clés doit être présente dans l'option 'unicaen-auth.shibboleth.simulate': " .
implode(', ', $keys));
} catch (AssertionFailedException $e) {
throw new LogicException("Configuration erronée", null, $e);
}
return $this->authenticatedUser;
$eppn = $simulate['eppn'];
$supannId = $simulate['supannEmpId'] ?: $simulate['supannEtuId'];
$email = isset($simulate['email']) ? $simulate['email'] : null;
$shibUser = new ShibUser();
$shibUser->setEppn($eppn);
$shibUser->setId($supannId);
$shibUser->setDisplayName("$eppn ($supannId)");
$shibUser->setEmail($email);
$shibUser->setNom('Shibboleth');
$shibUser->setPrenom('Simulation');
$this->simulateAuthenticatedUser($shibUser);
return $shibUser;
}
/**
* Retourne true si les données stockées en session indiquent qu'une usurpation d'identité Shibboleth est en cours.
*
* @return bool
*/
private function isUsurpationActive()
{
return $this->getSessionContainer()->offsetExists('fromShibUser');
}
/**
* @param ShibUser $fromShibUser
* @param ShibUser $toShibUser
* @return self
*/
public function activateUsurpation(ShibUser $fromShibUser, ShibUser $toShibUser)
{
// le login doit faire partie des usurpateurs autorisés
if (! in_array($fromShibUser->getUsername(), $this->options->getUsurpationAllowedUsernames())) {
throw new RuntimeException("Usurpation non autorisée");
}
$session = $this->getSessionContainer();
$session->offsetSet('fromShibUser', $fromShibUser);
$session->offsetSet('toShibUser', $toShibUser);
return $this;
}
/**
* Suppression des données stockées en session concernant l'usurpation d'identité Shibboleth.
*
* @return self
*/
public function deactivateUsurpation()
{
$session = $this->getSessionContainer();
$session->offsetUnset('fromShibUser');
$session->offsetUnset('toShibUser');
return $this;
}
/**
* @return $this
*/
public function handleUsurpation()
{
if (! $this->isUsurpationActive()) {
return $this;
}
$session = $this->getSessionContainer();
/** @var ShibUser|null $toShibUser */
$toShibUser = $session->offsetGet('toShibUser');
if ($toShibUser === null) {
throw new RuntimeException("Anomalie: 'toShibUser' introuvable");
}
$this->simulateAuthenticatedUser($toShibUser, 'supannEmpId');
return $this;
}
/**
* @return Container
*/
private function getSessionContainer()
{
return new Container(ShibService::class);
}
/**
* Inscrit dans le tableau $_SERVER le nécessaire pour usurper l'identité d'un utilisateur
* qui ce serait authentifié via Shibboleth.
*
* @param ShibUser $shibUser Utilisateur dont on veut usurper l'identité.
* @param string $keyForId Clé du tableau $_SERVER dans laquelle mettre l'id de l'utilsateur spécifié.
* Ex: 'supannEmpId', 'supannEtuId'.
*/
public function simulateAuthenticatedUser(ShibUser $shibUser, $keyForId = 'supannEmpId')
{
$_SERVER['REMOTE_USER'] = $shibUser->getEppn();
$_SERVER[$keyForId] = $shibUser->getId();
$_SERVER['displayName'] = $shibUser->getDisplayName();
$_SERVER['mail'] = $shibUser->getEmail();
$_SERVER['sn'] = $shibUser->getNom();
$_SERVER['givenName'] = $shibUser->getPrenom();
}
/**
* @return ShibUser
*/
private function createShibUser()
private function createShibUserFromServerArrayData()
{
$eppn = $_SERVER['REMOTE_USER'];
......@@ -81,7 +255,7 @@ EOS;
} elseif (isset($_SERVER['supannEmpId'])) {
$id = $_SERVER['supannEmpId'];
} else {
throw new RuntimeException('Un au moins des attributs suivants doivent exister dans $_SERVER : supannEtuId, supannEmpId.');
throw new RuntimeException('Un au moins des attributs Shibboleth suivants doit exister dans $_SERVER : supannEtuId, supannEmpId.');
}
$mail = null;
......@@ -94,11 +268,34 @@ EOS;
$displayName = $_SERVER['displayName'];
}
$surname = null;
if (isset($_SERVER['sn'])) {
$surname = $_SERVER['sn'];
} elseif (isset($_SERVER['surname'])) {
$surname = $_SERVER['surname'];
}
$givenName = null;
if (isset($_SERVER['givenName'])) {
$givenName = $_SERVER['givenName'];
}
$civilite = null;
if (isset($_SERVER['supannCivilite'])) {
$civilite = $_SERVER['supannCivilite'];
}
$shibUser = new ShibUser();
// propriétés de UserInterface
$shibUser->setId($id);
$shibUser->setUsername($eppn);
$shibUser->setDisplayName($displayName);
$shibUser->setEmail($mail);
$shibUser->setPassword(null);
// autres propriétés
$shibUser->setNom($surname);
$shibUser->setPrenom($givenName);
$shibUser->setCivilite($civilite);
return $shibUser;
}
......@@ -111,6 +308,10 @@ EOS;
*/
public function getLogoutUrl($returnAbsoluteUrl = null)
{
if ($this->getShibbolethSimulate()) {
return '/';
}
$logoutRelativeUrl = '/Shibboleth.sso/Logout?return='; // NB: '?return=' semble obligatoire!
if ($returnAbsoluteUrl) {
......
......@@ -5,6 +5,7 @@ namespace UnicaenAuth\Service;
use BjyAuthorize\Acl\Role;
use UnicaenApp\Exception\RuntimeException;
use UnicaenApp\Traits\SessionContainerTrait;
use UnicaenAuth\Entity\Shibboleth\ShibUser;
use UnicaenAuth\Event\UserRoleSelectedEvent;
use UnicaenAuth\Provider\Identity\Chain;
use Zend\EventManager\EventManagerAwareInterface;
......@@ -73,6 +74,24 @@ class UserContext extends AbstractService implements EventManagerAwareInterface
/**
* Retourne l'éventuel utilisateur Shibboleth courant.
*
* @return ShibUser|null
*/
public function getShibUser()
{
if (($identity = $this->getIdentity())) {
if (isset($identity['shib']) && $identity['shib'] instanceof ShibUser) {
return $identity['shib'];
}
}
return null;
}
/**
* Retourne les données d'identité correspondant à l'utilisateur courant.
*
......@@ -91,6 +110,27 @@ class UserContext extends AbstractService implements EventManagerAwareInterface
}
/**
* Retourne l'identifiant de connexion de l'utilisateur courant.
*
* @return string|null
*/
public function getIdentityUsername()
{
if ($user = $this->getShibUser()) {
return $user->getUsername();
}
if ($user = $this->getLdapUser()) {
return $user->getUsername();
}
if ($user = $this->getDbUser()) {
return $user->getUsername();
}
return null;
}
/**
* @param string $roleId
......
......@@ -34,7 +34,7 @@ class ShibConnectViewHelper extends AbstractHelper
*/
private function render()
{
if (! $this->shibService->isShibbolethEnable()) {
if (! $this->shibService->isShibbolethEnabled()) {
return '';
}
......
......@@ -103,6 +103,10 @@ class UserInfo extends UserAbstract
$out .= $aucuneAffDispo;
}
// formulaire d'usurpation d'identité
$userUsurpationHelper = $this->view->plugin('userUsurpation'); /* @var $userUsurpationHelper \UnicaenAuth\View\Helper\UserUsurpationHelper */
$out .= $userUsurpationHelper();
return $out;
}
......
<?php
namespace UnicaenAuth\View\Helper;
use UnicaenApp\Form\View\Helper\FormControlGroup;
use UnicaenAuth\Options\ModuleOptions;
use Zend\Form\Element\Submit;
use Zend\Form\Element\Text;
use Zend\Form\Form;
use Zend\Form\View\Helper\Form as FormHelper;
use Zend\View\Renderer\PhpRenderer;
/**
* Aide de vue permettant de saisir et valider le login de l'utilisateur dont on veut usurper l'identité.
*
* @author Bertrand GAUTHIER <bertrand.gauthier@unicaen.fr>
*/
class UserUsurpationHelper extends UserAbstract
{
/**
* @var PhpRenderer
*/
protected $view;
/**
* @var ModuleOptions
*/
protected $moduleOptions;
/**
* @var string
*/
private $url;
/**
* @var bool
*/
private $usurpationEnabled = false;
/**
* Point d'entrée.
*
* @return self
*/
public function __invoke()
{
return $this;
}
/**
* Retourne le code HTML généré par cette aide de vue.
*
* @return string
*/
public function __toString()
{
if (! $this->usurpationEnabled) {
return '';
}
/** @var FormHelper $formHelper */
$formHelper = $this->view->plugin('form');
/** @var FormControlGroup $formControlGroupHelper */
$formControlGroupHelper = $this->view->plugin('formControlGroup');
$form = new Form('user-usurpation-form');
$form->setAttributes([
'class' => 'user-usurpation-form',
'action' => $this->url,
]);
$input = new Text('identity');
$input->setAttributes([
'id' => 'user-usurpation-input',
'placeholder' => "Identifiant utilisateur",
]);
$submit = new Submit('submit');
$submit->setValue("Usurper");
$submit->setAttributes([
'class' => 'user-usurpation-submit btn btn-info',
]);
$html = '';
$html .= $formHelper->openTag($form);
$html .= "<div><strong>Usurpation d'identité :</strong></div>";
$html .= $formControlGroupHelper->__invoke($input);
$html .= $formControlGroupHelper->__invoke($submit);
$html .= $formHelper->closeTag();
$html .= <<<EOS
<script>
$(".user-usurpation-form").submit(function() {
$("body *").css('cursor', 'wait');
});
var input = $("#user-usurpation-input").on('input', function() {
updateUsurpationSubmit();
});
function updateUsurpationSubmit() {
$(".user-usurpation-submit").prop("disabled", input.val().length === 0);
}
updateUsurpationSubmit();
</script>
EOS;
return $html;
}
/**
* @param string $url
* @return $this
*/
public function setUrl($url)
{
$this->url = $url;
return $this;
}
/**
* @param ModuleOptions $moduleOptions
* @return self
*/
public function setModuleOptions(ModuleOptions $moduleOptions)
{
$this->moduleOptions = $moduleOptions;
return $this;
}
/**
* @param bool $usurpationEnabled
* @return self
*/
public function setUsurpationEnabled($usurpationEnabled = true)
{
$this->usurpationEnabled = $usurpationEnabled;
return $this;
}
}
\ No newline at end of file
<?php
namespace UnicaenAuth\View\Helper;
use UnicaenAuth\Options\ModuleOptions;
use UnicaenAuth\Service\UserContext;
use Zend\View\Helper\Url;
use Zend\View\HelperPluginManager;
class UserUsurpationHelperFactory
{
/**
* @param HelperPluginManager $hpm
* @return UserUsurpationHelper
*/
public function __invoke(HelperPluginManager $hpm)
{
/** @var Url $urlHelper */
$urlHelper = $hpm->get('url');
$url = $urlHelper->__invoke('utilisateur/default', ['action' => 'usurper-identite']);
/** @var UserContext $userContextService */
$userContextService = $hpm->getServiceLocator()->get('authUserContext');
/** @var ModuleOptions $moduleOptions */
$moduleOptions = $hpm->getServiceLocator()->get('unicaen-auth_module_options');
$usurpationAllowed = in_array(
$userContextService->getIdentityUsername(),
$moduleOptions->getUsurpationAllowedUsernames());
$helper = new UserUsurpationHelper($userContextService);
$helper->setUrl($url);
$helper->setUsurpationEnabled($usurpationAllowed);
return $helper;
}
}
\ No newline at end of file
<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>
<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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment