Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
lib
unicaen
auth
Commits
cb5d0bbf
Commit
cb5d0bbf
authored
May 28, 2018
by
Laurent Lécluse
Browse files
Merge remote-tracking branch 'origin/master'
parents
345436e2
7ffe2396
Changes
15
Hide whitespace changes
Inline
Side-by-side
config/module.config.php
View file @
cb5d0bbf
<?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'
=>
[
...
...
src/UnicaenAuth/Authentication/Adapter/Cas.php
View file @
cb5d0bbf
<?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
*
...
...
src/UnicaenAuth/Authentication/Storage/ChainServiceFactory.php
View file @
cb5d0bbf
...
...
@@ -12,11 +12,15 @@ use Zend\ServiceManager\ServiceLocatorInterface;
*/
class
ChainServiceFactory
implements
FactoryInterface
{
pr
otec
te
d
$
s
torages
=
[
pr
iva
te
$
mandatoryS
torages
=
[
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
);
...
...
src/UnicaenAuth/Authentication/Storage/Shib.php
View file @
cb5d0bbf
...
...
@@ -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
...
...
@@ -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
*
...
...
src/UnicaenAuth/Authentication/Storage/ShibSimulatorStorage.php
0 → 100644
View file @
cb5d0bbf
<?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
src/UnicaenAuth/Controller/AuthController.php
View file @
cb5d0bbf
...
...
@@ -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'
)
{
// 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
())
{
$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'
,
'/'
);
if
(
$shibUser
===
null
)
{
return
[];
// une page d'aide s'affichera
// 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
);
}
/** @var AuthenticationService $authService */
$authService
=
$this
->
getServiceLocator
()
->
get
(
'zfcuser_auth_service'
);
try
{
$authService
->
getStorage
()
->
write
(
$shibUser
->
getId
());
}
catch
(
ExceptionInterface
$e
)
{
throw
new
RuntimeException
(
"Impossible d'écrire dans le storage"
);
$shibUser
=
$this
->
shibService
->
getAuthenticatedUser
();
if
(
$shibUser
===
null
)
{
return
[];
// une page d'aide s'affichera si les données issues de Shibboleth attendues sont absentes
}
$this
->
userService
->
userA
uthenticat
ed
(
$shibUser
);
// arrivé ici, l'a
uthenti
fi
cat
ion shibboleth a été faite en bonne et due forme et a réussie.
$redirectUrl
=
$this
->
params
()
->
fromQuery
(
'redirect'
,
'/'
);
$this
->
setStoredAuthenticatedUsername
(
$shibUser
->
getUsername
());
$this
->
userService
->
userAuthenticated
(
$shibUser
);
return
$this
->
redirect
()
->
toUrl
(
$redirectUrl
);
}
public
function
shibboleth
()
/**
* 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
(
$username
);
}
catch
(
ExceptionInterface
$e
)
{
throw
new
RuntimeException
(
"Impossible d'écrire dans le storage"
);
}
}
}
\ No newline at end of file
src/UnicaenAuth/Controller/UtilisateurController.php
View file @
cb5d0bbf
...
...
@@ -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.
...
...
@@ -45,12 +59,127 @@ class UtilisateurController extends AbstractActionController
return
false
;
}
/**
* @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
src/UnicaenAuth/Entity/Shibboleth/ShibUser.php
View file @
cb5d0bbf
...
...
@@ -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
;
}
...
...
src/UnicaenAuth/Service/ShibService.php
View file @
cb5d0bbf
...
...
@@ -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
)
{
<