Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
lib
unicaen
auth
Commits
30760bf8
Commit
30760bf8
authored
Apr 24, 2018
by
Bertrand Gauthier
Browse files
Merge branch 'usurpation'
parents
058ae8f8
f90a09b9
Changes
11
Hide whitespace changes
Inline
Side-by-side
config/module.config.php
View file @
30760bf8
...
...
@@ -5,6 +5,7 @@ use UnicaenAuth\Service\ShibService;
use
UnicaenAuth\Service\ShibServiceFactory
;
use
UnicaenAuth\Service\UserContextFactory
;
use
UnicaenAuth\View\Helper\ShibConnectViewHelperFactory
;
use
UnicaenAuth\View\Helper\UserUsurpationHelperFactory
;
$settings
=
[
/**
...
...
@@ -432,6 +433,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/Storage/Shib.php
View file @
30760bf8
...
...
@@ -51,15 +51,57 @@ 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
(
!
$this
->
isShibbolethEnabled
())
{
return
null
;
}
if
(
null
!==
$this
->
resolvedIdentity
)
{
return
$this
->
resolvedIdentity
;
}
/** @var ShibService $shib */
$shib
=
$this
->
getServiceLocator
()
->
get
(
ShibService
::
class
);
$this
->
resolvedIdentity
=
$shib
->
getAuthenticatedUser
();
return
$this
->
resolvedIdentity
;
}
/**
* @return bool
*/
private
function
isShibbolethEnabled
()
{
$options
=
$this
->
getModuleOptions
();
$shibboleth
=
$options
->
getShibboleth
();
return
isset
(
$shibboleth
[
'enable'
])
&&
(
bool
)
$shibboleth
[
'enable'
];
}
/**
* @return ModuleOptions
*/
private
function
getModuleOptions
()
{
if
(
null
===
$this
->
options
)
{
$this
->
options
=
$this
->
getServiceLocator
()
->
get
(
'unicaen-auth_module_options'
);
}
return
$this
->
options
;
}
/**
* Writes $contents to storage
*
...
...
src/UnicaenAuth/Controller/AuthController.php
View file @
30760bf8
...
...
@@ -3,6 +3,7 @@
namespace
UnicaenAuth\Controller
;
use
UnicaenApp\Exception\RuntimeException
;
use
UnicaenAuth\Service\ShibService
;
use
UnicaenAuth\Service\Traits\ShibServiceAwareTrait
;
use
UnicaenAuth\Service\Traits\UserServiceAwareTrait
;
use
Zend\Authentication\AuthenticationService
;
...
...
@@ -23,6 +24,16 @@ class AuthController extends AbstractActionController
use
UserServiceAwareTrait
;
/**
* Cette action n'est exécutée qu'une fois l'authentification Shibboleth réalisée avec succès.
*
* Lorsque l'authentification Shibboleth est activée (unicaen-auth.shibboleth.enable === true),
* et que la config Apache est correcte, une requête à l'adresse correspondant à cette action
* (suite au clic sur le bouton "Authentification Shibboleth, typiquement)
* est détournée pour réaliser l'authentification.
* Ce n'est qu'une fois l'authentification réalisée avec succès que cette action entre en jeu.
*
* @see ShibService::apacheConfigSnippet()
*
* @return Response|array
*/
public
function
shibbolethAction
()
...
...
@@ -36,9 +47,14 @@ class AuthController extends AbstractActionController
$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
...
...
@@ -65,9 +81,4 @@ class AuthController extends AbstractActionController
return
$this
->
redirect
()
->
toUrl
(
$redirectUrl
);
}
public
function
shibboleth
()
{
}
}
\ No newline at end of file
src/UnicaenAuth/Controller/UtilisateurController.php
View file @
30760bf8
...
...
@@ -6,8 +6,6 @@ use Zend\Http\Request;
use
Zend\Mvc\Controller\AbstractActionController
;
/**
*
*
* @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr>
*/
class
UtilisateurController
extends
AbstractActionController
...
...
src/UnicaenAuth/Entity/Shibboleth/ShibUser.php
View file @
30760bf8
...
...
@@ -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.
*
...
...
src/UnicaenAuth/Service/ShibService.php
View file @
30760bf8
...
...
@@ -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,10 +48,47 @@ EOS;
return
$text
;
}
/**
* @return ShibUser|null
*/
public
function
getAuthenticatedUser
()
{
if
(
!
$this
->
isShibbolethEnabled
())
{
return
null
;
}
if
(
$this
->
authenticatedUser
===
null
)
{
if
(
!
$this
->
getShibbolethSimulate
()
&&
!
isset
(
$_SERVER
[
'REMOTE_USER'
]))
{
try
{
Assertion
::
keyIsset
(
$_SERVER
,
'REMOTE_USER'
);
}
catch
(
AssertionFailedException
$e
)
{
throw
new
RuntimeException
(
"La clé suivante est introuvable ou sans valeur dans
\$
_SERVER : 'REMOTE_USER'."
);
}
}
// gestion de l'usurpation éventuelle
if
(
$this
->
isUsurpationActive
())
{
$this
->
handleUsurpation
();
}
if
(
empty
(
$_SERVER
[
'REMOTE_USER'
]))
{
if
(
$this
->
isSimulationActive
())
{
$this
->
handleSimulation
();
}
else
{
return
null
;
}
}
$this
->
authenticatedUser
=
$this
->
createShibUserFromServerArrayData
();
}
return
$this
->
authenticatedUser
;
}
/**
* @return boolean
*/
public
function
isShibbolethEnable
()
public
function
isShibbolethEnable
d
()
{
$options
=
$this
->
options
->
getShibboleth
();
...
...
@@ -55,24 +96,160 @@ EOS;
}
/**
* @return
ShibUser|null
* @return
array
*/
public
function
get
AuthenticatedUser
()
public
function
get
ShibbolethSimulate
()
{
if
(
$this
->
authenticatedUser
===
null
)
{
if
(
empty
(
$_SERVER
[
'REMOTE_USER'
]))
{
return
null
;
}
$this
->
authenticatedUser
=
$this
->
createShibUser
();
$options
=
$this
->
options
->
getShibboleth
();
if
(
!
array_key_exists
(
'simulate'
,
$options
)
||
!
is_array
(
$options
[
'simulate'
]))
{
return
[];
}
return
$this
->
authenticatedUser
;
return
$options
[
'simulate'
];
}
/**
* Retourne true si la simulation d'un utilisateur authentifié via Shibboleth est en cours.
*
* @return bool
*/
private
function
isSimulationActive
()
{
$options
=
$this
->
options
->
getShibboleth
();
if
(
array_key_exists
(
'simulate'
,
$options
)
&&
is_array
(
$options
[
'simulate'
])
&&
!
empty
(
$options
[
'simulate'
]))
{
return
true
;
}
return
false
;
}
/**
* @return $this
*/
private
function
handleSimulation
()
{
$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
);
}
$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'
);
ShibService
::
simulateAuthenticatedUser
(
$shibUser
);
return
$this
;
}
/**
* 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
*/
private
function
handleUsurpation
()
{
$session
=
$this
->
getSessionContainer
();
/** @var ShibUser|null $toShibUser */
$toShibUser
=
$session
->
offsetGet
(
'toShibUser'
);
if
(
$toShibUser
===
null
)
{
throw
new
RuntimeException
(
"Anomalie: 'toShibUser' introuvable"
);
}
static
::
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
static
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
createShibUser
FromServerArrayData
()
{
$eppn
=
$_SERVER
[
'REMOTE_USER'
];
...
...
@@ -81,7 +258,7 @@ EOS;
}
elseif
(
isset
(
$_SERVER
[
'supannEmpId'
]))
{
$id
=
$_SERVER
[
'supannEmpId'
];
}
else
{
throw
new
RuntimeException
(
'Un au moins des attributs suivants doit exister dans $_SERVER : supannEtuId, supannEmpId.'
);
throw
new
RuntimeException
(
'Un au moins des attributs
Shibboleth
suivants doit exister dans $_SERVER : supannEtuId, supannEmpId.'
);
}
$mail
=
null
;
...
...
@@ -134,6 +311,10 @@ EOS;
*/
public
function
getLogoutUrl
(
$returnAbsoluteUrl
=
null
)
{
if
(
$this
->
getShibbolethSimulate
())
{
return
'/'
;
}
$logoutRelativeUrl
=
'/Shibboleth.sso/Logout?return='
;
// NB: '?return=' semble obligatoire!
if
(
$returnAbsoluteUrl
)
{
...
...
src/UnicaenAuth/Service/UserContext.php
View file @
30760bf8
...
...
@@ -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
...
...
src/UnicaenAuth/View/Helper/ShibConnectViewHelper.php
View file @
30760bf8
...
...
@@ -34,7 +34,7 @@ class ShibConnectViewHelper extends AbstractHelper
*/
private
function
render
()
{
if
(
!
$this
->
shibService
->
isShibbolethEnable
())
{
if
(
!
$this
->
shibService
->
isShibbolethEnable
d
())
{
return
''
;
}
...
...
src/UnicaenAuth/View/Helper/UserInfo.php
View file @
30760bf8
...
...
@@ -102,7 +102,11 @@ 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
;
}
...
...
src/UnicaenAuth/View/Helper/UserUsurpationHelper.php
0 → 100644
View file @
30760bf8
<?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
;
}
/**