Loading CHANGELOG.md +2 −0 Original line number Diff line number Diff line Loading @@ -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. config/module.config.php +27 −0 Original line number Diff line number Diff line Loading @@ -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', ], ], ], ], /** Loading config/unicaen-auth.local.php.dist +27 −0 Original line number Diff line number Diff line Loading @@ -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', ], ], ], ], /** Loading src/UnicaenAuth/Entity/Shibboleth/ShibUser.php +12 −0 Original line number Diff line number Diff line Loading @@ -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 Loading src/UnicaenAuth/Service/ShibService.php +72 −43 Original line number Diff line number Diff line Loading @@ -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'; Loading Loading @@ -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); } /** Loading Loading @@ -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]; } /** * */ Loading Loading @@ -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); Loading @@ -234,10 +257,15 @@ 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); Loading Loading @@ -332,7 +360,7 @@ EOS; throw new RuntimeException("Anomalie: clé '$key' introuvable"); } $this->simulateAuthenticatedUser($toShibUser, 'supannEmpId'); $this->simulateAuthenticatedUser($toShibUser); return $this; } Loading Loading @@ -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()); Loading @@ -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); Loading @@ -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. * Loading Loading @@ -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 Loading
CHANGELOG.md +2 −0 Original line number Diff line number Diff line Loading @@ -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.
config/module.config.php +27 −0 Original line number Diff line number Diff line Loading @@ -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', ], ], ], ], /** Loading
config/unicaen-auth.local.php.dist +27 −0 Original line number Diff line number Diff line Loading @@ -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', ], ], ], ], /** Loading
src/UnicaenAuth/Entity/Shibboleth/ShibUser.php +12 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
src/UnicaenAuth/Service/ShibService.php +72 −43 Original line number Diff line number Diff line Loading @@ -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'; Loading Loading @@ -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); } /** Loading Loading @@ -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]; } /** * */ Loading Loading @@ -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); Loading @@ -234,10 +257,15 @@ 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); Loading Loading @@ -332,7 +360,7 @@ EOS; throw new RuntimeException("Anomalie: clé '$key' introuvable"); } $this->simulateAuthenticatedUser($toShibUser, 'supannEmpId'); $this->simulateAuthenticatedUser($toShibUser); return $this; } Loading Loading @@ -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()); Loading @@ -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); Loading @@ -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. * Loading Loading @@ -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