Commit 23c62cc7 authored by Bertrand Gauthier's avatar Bertrand Gauthier
Browse files

Configuration de la stratégie d'extraction d'un identifiant utile parmi les...

Configuration de la stratégie d'extraction d'un identifiant utile parmi les données d'authentification shibboleth (config 'shib_user_id_extractor').
parent abb31635
Pipeline #9577 passed with stage
in 28 seconds
......@@ -40,6 +40,8 @@ Première version officielle sous ZF3.
3.2.0
-----
- Configuration de la stratégie d'extraction d'un identifiant utile parmi les données d'authentification shibboleth
(config 'shib_user_id_extractor').
- 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.
......@@ -187,6 +187,33 @@ $settings = [
'supannEtuId|supannEmpId',
],
*/
/**
* Configuration de la stratégie d'extraction d'un identifiant utile parmi les données d'authentification
* shibboleth.
* Ex: identifiant de l'usager au sein du référentiel établissement, transmis par l'IDP via le supannRefId.
*/
'shib_user_id_extractor' => [
// domaine (ex: 'unicaen.fr') de l'EPPN (ex: hochonp@unicaen.fr')
'unicaen.fr' => [
[
// nom du 1er attribut recherché
'name' => 'supannRefId', // ex: '{REFERENTIEL}1234;{ISO15693}044D1AZE7A5P80'
// pattern éventuel pour extraire la partie intéressante
'preg_match_pattern' => '|\{REFERENTIEL\}(\d+)|', // ex: permet d'extraire '1234'
],
[
// nom du 2e attribut recherché
'name' => 'supannEmpId',
// pas de pattern donc valeur directement utilisable
'preg_match_pattern' => null,
],
[
// nom du 3e attribut recherché
'name' => 'supannEtuId',
],
],
],
],
/**
......
......@@ -115,6 +115,33 @@ return [
// 'givenName',
// 'supannEtuId|supannEmpId',
//],
/**
* Configuration de la stratégie d'extraction d'un identifiant utile parmi les données d'authentification
* shibboleth.
* Ex: identifiant de l'usager au sein du référentiel établissement, transmis par l'IDP via le supannRefId.
*/
'shib_user_id_extractor' => [
// domaine (ex: 'unicaen.fr') de l'EPPN (ex: hochonp@unicaen.fr')
'unicaen.fr' => [
[
// nom du 1er attribut recherché
'name' => 'supannRefId', // ex: '{REFERENTIEL}1234;{ISO15693}044D1AZE7A5P80'
// pattern éventuel pour extraire la partie intéressante
'preg_match_pattern' => '|\{REFERENTIEL\}(\d+)|', // ex: permet d'extraire '1234'
],
[
// nom du 2e attribut recherché
'name' => 'supannEmpId',
// pas de pattern donc valeur directement utilisable
'preg_match_pattern' => null,
],
[
// nom du 3e attribut recherché
'name' => 'supannEtuId',
],
],
],
],
/**
......
......@@ -47,6 +47,18 @@ class ShibUser implements UserInterface
*/
protected $state = 1;
/**
* Retourne la partie domaine DNS de l'EPPN.
* Retourne par exemple "unicaen.fr" lorsque l'EPPN est "tartempion@unicaen.fr"
*
* @return string
*/
public function getEppnDomain()
{
$parts = explode('@', $this->getEppn());
return $parts[1];
}
/**
* @return string
......
......@@ -19,6 +19,8 @@ use Zend\Session\Container;
*/
class ShibService
{
const SHIB_USER_ID_EXTRACTOR = 'shib_user_id_extractor';
const KEY_fromShibUser = 'fromShibUser';
const KEY_toShibUser = 'toShibUser';
......@@ -105,9 +107,9 @@ EOS;
private function isAuthenticated(): bool
{
return
$this->getServerArrayVariable('REMOTE_USER') ||
$this->getServerArrayVariable('Shib-Session-ID') ||
$this->getServerArrayVariable('HTTP_SHIB_SESSION_ID');
$this->getValueFromShibData('REMOTE_USER', $_SERVER) ||
$this->getValueFromShibData('Shib-Session-ID', $_SERVER) ||
$this->getValueFromShibData('HTTP_SHIB_SESSION_ID', $_SERVER);
}
/**
......@@ -195,6 +197,28 @@ EOS;
return (array)$this->shibbolethConfig['required_attributes'];
}
/**
* Retourne la config permettant d'extraire l'id à partir des attributs.
*
* @param string $domain
* @return array
*/
private function getShibUserIdExtractorForDomain(string $domain): array
{
$key = self::SHIB_USER_ID_EXTRACTOR;
if (! array_key_exists($key, $this->shibbolethConfig)) {
throw new RuntimeException("Aucune config '$key' trouvée.");
}
$config = $this->shibbolethConfig[$key];
if (! array_key_exists($domain, $config)) {
throw new RuntimeException("Aucune config '$key' trouvée pour le domaine '$domain'.");
}
return $config[$domain];
}
/**
*
*/
......@@ -224,7 +248,6 @@ EOS;
$this->assertRequiredAttributesExistInData($data);
$eppn = $this->getValueFromShibData('eppn', $data);
$supannId = $this->getValueFromShibData('supannEmpId', $data) ?: $this->getValueFromShibData('supannEtuId', $data);
$email = $this->getValueFromShibData('mail', $data);
$displayName = $this->getValueFromShibData('displayName', $data);
$givenName = $this->getValueFromShibData('givenName', $data);
......@@ -234,17 +257,22 @@ EOS;
Assertion::contains($eppn, '@', "L'eppn '" . $eppn . "' n'est pas de la forme 'id@domaine' attendue (ex: 'tartempion@unicaen.fr')");
$shibUser = new ShibUser();
// propriétés de UserInterface
$shibUser->setEppn($eppn);
$shibUser->setId($supannId);
$shibUser->setUsername($eppn);
$domain = $shibUser->getEppnDomain(); // possible uniquement après $shibUser->setEppn($eppn)
$id = $this->extractShibUserIdValueForDomainFromShibData($domain, $data);
$shibUser->setId($id);
$shibUser->setDisplayName($displayName);
$shibUser->setEmail($email);
// autres propriétés
$shibUser->setNom($surname);
$shibUser->setPrenom($givenName);
$shibUser->setCivilite($civilite);
return $shibUser;
}
/**
* Retourne true si les données stockées en session indiquent qu'une usurpation d'identité Shibboleth est en cours.
*
......@@ -332,7 +360,7 @@ EOS;
throw new RuntimeException("Anomalie: clé '$key' introuvable");
}
$this->simulateAuthenticatedUser($toShibUser, 'supannEmpId');
$this->simulateAuthenticatedUser($toShibUser);
return $this;
}
......@@ -394,26 +422,15 @@ EOS;
* qui se 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')
public function simulateAuthenticatedUser(ShibUser $shibUser)
{
// 'REMOTE_USER' (notamment) est utilisé pour savoir si un utilisateur est authentifié ou non
$this->setServerArrayVariable('REMOTE_USER', $shibUser->getEppn());
// // on s'assure que tous les attributs obligatoires ont une valeur
// foreach ($this->getShibbolethRequiredAttributes() as $requiredAttribute) {
// // un pipe permet d'exprimer un OU logique, ex: 'supannEmpId|supannEtuId'
// $attributes = array_map('trim', explode('|', $requiredAttribute));
// foreach ($attributes as $attribute) {
// $this->setServerArrayVariable($attribute, 'qqchose');
// }
// }
// pour certains attributs, on veut une valeur sensée!
$this->setServerArrayVariable('eppn', $shibUser->getEppn());
$this->setServerArrayVariable($keyForId, $shibUser->getId());
$this->setServerArrayVariable('supannEmpId', $shibUser->getId()); // ou bien 'supannEtuId', peu importe.
$this->setServerArrayVariable('displayName', $shibUser->getDisplayName());
$this->setServerArrayVariable('mail', $shibUser->getEppn());
$this->setServerArrayVariable('sn', $shibUser->getNom());
......@@ -432,18 +449,20 @@ EOS;
throw new RuntimeException('Des attributs Shibboleth obligatoires font défaut dans $_SERVER.', null, $e);
}
$eppn = $this->getServerArrayVariable('eppn');
$id = $this->getServerArrayVariable('supannEtuId') ?: $this->getServerArrayVariable('supannEmpId');
$mail = $this->getServerArrayVariable('mail');
$displayName = $this->getServerArrayVariable('displayName');
$surname = $this->getServerArrayVariable('sn') ?: $this->getServerArrayVariable('surname');
$givenName = $this->getServerArrayVariable('givenName');
$civilite = $this->getServerArrayVariable('supannCivilite');
$eppn = $this->getValueFromShibData('eppn', $_SERVER);
$mail = $this->getValueFromShibData('mail', $_SERVER);
$displayName = $this->getValueFromShibData('displayName', $_SERVER);
$surname = $this->getValueFromShibData('sn', $_SERVER) ?: $this->getValueFromShibData('surname', $_SERVER);
$givenName = $this->getValueFromShibData('givenName', $_SERVER);
$civilite = $this->getValueFromShibData('supannCivilite', $_SERVER);
$shibUser = new ShibUser();
// propriétés de UserInterface
$shibUser->setId($id);
$shibUser->setEppn($eppn);
$shibUser->setUsername($eppn);
$domain = $shibUser->getEppnDomain(); // possible uniquement après $shibUser->setEppn($eppn)
$id = $this->extractShibUserIdValueForDomainFromShibData($domain, $_SERVER);
$shibUser->setId($id);
$shibUser->setDisplayName($displayName);
$shibUser->setEmail($mail);
$shibUser->setPassword(null);
......@@ -455,6 +474,31 @@ EOS;
return $shibUser;
}
/**
* @param string $domain
* @param array $data
* @return string|null
*/
private function extractShibUserIdValueForDomainFromShibData(string $domain, array $data): ?string
{
$config = $this->getShibUserIdExtractorForDomain($domain);
foreach ($config as $array) {
$name = $array['name'];
$value = $this->getValueFromShibData($name, $data);
if ($value !== null) {
$pregMatchPattern = $array['preg_match_pattern'] ?? null;
if ($pregMatchPattern !== null && preg_match($pregMatchPattern, $value, $matches)) {
$value = $matches[1];
}
return $value;
}
}
return null;
}
/**
* Retourne l'URL de déconnexion Shibboleth.
*
......@@ -554,19 +598,4 @@ EOS;
$_SERVER[$key] = $value;
}
/**
* @param $name
* @return string|null
*/
private function getServerArrayVariable($name): ?string
{
$key = $this->getShibbolethAliasFor($name) ?: $name;
if (! array_key_exists($key, $_SERVER)) {
return null;
}
return $_SERVER[$key];
}
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment