New file |
0,0 → 1,507 |
<?php |
/** |
* Service d'itentification d'un utilisateur. |
* Modifié pour fonctionner avec le SSO. |
* |
* Encodage en entrée : utf8 |
* Encodage en sortie : utf8 |
* |
* Cas d'utilisation : |
* Le service reçoit un jeton SSO (ou pas) dans le header "Authorization", le fait |
* vérifier par l'annuaire; s'il est valide, le décode puis retourne le profil utilisateur |
* associé; sinon retourne un profil anonyme. |
* |
* 1: Aucun jeton ou jeton invalide transmis |
* 1: L'application retourne l'identifiant de session en cours (mode anonyme) |
* |
* 2: Jeton valide transmis |
* 1 : Passe ou reste dans l'état "connecté"; le profil actif est retourné |
* |
* En résumé, utilisation des URLs : |
* /CoelUtilisateur/identite : retour identifiant de session si jamais connecté, sinon retour de l'id (+ login et mot de passe) |
* |
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org> |
* @author David DELON <david.delon@clapas.net> |
* @author Mathias CHOUET <mathias@tela-botanica.org> |
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt> |
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt> |
* @version $Id$ |
* @copyright 2009 |
*/ |
class CoelUtilisateur extends Coel { |
|
protected $licence_acceptee = false; |
|
/** |
* Point d'entrée GET sans paramètres |
*/ |
public function getRessource() { |
// par défaut, mode anonyme |
$infos = $this->getUtilisateurAnonyme(); |
// recherche d'un utilisateur identifié en fonction du jeton SSO |
$infosIdentifie = $this->getUtilisateurSSO(); |
if ($infosIdentifie != null) { |
$infos = $infosIdentifie; |
} |
// et vlan, passe-moi l'éponge |
$this->envoyer($infos); |
} |
|
/** |
* Point d'entrée GET avec paramètres |
*/ |
public function getElement($ressources) { |
switch($ressources[0]) { |
case "setLicence" : |
$this->setLicence($ressources[1]); |
break; |
default: |
echo "action [" . $ressources[0] . "] inconnue"; |
} |
} |
|
/** |
* Renvoie un profil utilisateur CoeL anonyme |
* (licence vide, id de session, non connecté) |
*/ |
protected function getUtilisateurAnonyme() { |
$info = array("", session_id(), false); |
return $info; |
} |
|
/** |
* Recherche un jeton SSO dans l'entête HTTP "Authorization", vérifie ce |
* jeton auprès de l'annuaire et en cas de succès charge les informations |
* de l'utilisateur associé; si besoin, copie l'utilisateur dans la table |
* Personnes de CoeL |
* |
* @return Array un profil utilisateur ou null |
*/ |
protected function getUtilisateurSSO() { |
$utilisateur = null; |
// lecture du jeton |
$jeton = $this->lireJetonEntete(); |
//echo "Jeton : "; var_dump($jeton); |
if ($jeton != null) { |
// validation par l'annuaire |
$valide = $this->verifierJeton($jeton); |
if ($valide === true) { |
// décodage du courriel utilisateur depuis le jeton |
$donneesJeton = $this->decoderJeton($jeton); |
if ($donneesJeton != null && $donneesJeton["sub"] != "") { |
// récupération de l'utilisateur |
$courriel = $donneesJeton["sub"]; |
|
// lecture des infos dans l'annuaire (relou mais nécessaire pour la copie dans la table Personnes de CoeL) |
$infosAnnuaire = $this->obtenirInfosAnnuaire($courriel); |
$this->setInfosAnnuaire($infosAnnuaire); |
//echo "Infos Annu: "; var_dump($infosAnnuaire); |
|
// lecture de l'utilisateur connu (ou pas) par CoeL (table Personnes) |
$utilisateur_existant = $this->chargerUtilisateur($courriel); |
//echo "UTIL EXIST: "; var_dump($utilisateur_existant); |
|
// Vérification de la nécessité de mettre à jour l'utilisateur COEL vis à vis de l'annuaire de Tela Botanica |
if (!is_null($infosAnnuaire) && $this->avoirBesoinMiseAJour($utilisateur_existant)) { |
$this->debug[] = "Besoin d'une mise à jour"; |
|
$presence_dans_coel = ($utilisateur_existant != false); // si on l'a trouvé juste au dessus |
$this->debug[] = "Presence: $presence_dans_coel"; |
$mot_de_passe_sha1 = $infosAnnuaire['pass']; |
//echo "MDP: " .$mot_de_passe_sha1; |
|
if ($presence_dans_coel) { |
// Nécessite de faire une mise à jour |
$this->debug[] = "Mise à jour de l'utilisateur {$infosAnnuaire['id']}"; |
$this->mettreAJourUtilisateur($courriel, $mot_de_passe_sha1, $infosAnnuaire); |
// ci-dessous : ne pas caster en int car GWT attend une String (le con !) |
$utilisateur_existant['licence'] = $this->recupererLicenceUtilisateur($infosAnnuaire['id']); |
} else { |
// Nécessite d'ajouter le nouvel utilisateur |
$this->debug[] = "Ajout d'une nouvel utilisateur"; |
$this->ajouterUtilisateurACoel($infosAnnuaire, $mot_de_passe_sha1); |
// rechargement après l'avoir ajouté (un peu nul) |
$utilisateur_existant = $this->chargerUtilisateur($courriel); |
} |
} |
|
// stockage de l'utilisateur en session |
$this->setUtilisateur($utilisateur_existant); |
|
// renvoi des infos |
$utilisateur = array($utilisateur_existant['licence'], $infosAnnuaire['id'], true, $this->getUtilisateurNomComplet(), $this->getUtilisateurPrenom(), $this->getUtilisateurNom(), $this->getParametre()); |
} |
} |
} |
|
return $utilisateur; |
} |
|
protected function deconnecterUtilisateur() { |
$_SESSION['coel_utilisateur'] = ''; |
$_SESSION['coel_infosAnnuaire'] = ''; |
} |
|
/** |
* Retourne true si le compte utilisateur a été modifié dans l'annuaire, et que les |
* modifications ont besoin d'être répercutées dans la table Personnes de Coel |
*/ |
protected function avoirBesoinMiseAJour($info_annuaire_coel) { |
$necessite_maj = false; |
if ($info_annuaire_coel == false) { |
// Le login et/ou le mot de passe a pu changer |
$necessite_maj = true; |
} else { |
$info_annuaire_distant = $this->getInfosAnnuaire(); |
if ($this->comparerInfosAnnuairesDistantEtCoel($info_annuaire_distant, $info_annuaire_coel) == false) { |
$necessite_maj = true; |
} |
} |
return $necessite_maj; |
} |
|
protected function comparerInfosAnnuairesDistantEtCoel($annuaire_distant, $annuaire_coel) { |
$identique = true; |
$tableau_annuaire_distant = array('nom' => $annuaire_distant['nom'], |
'prenom' => $annuaire_distant['prenom'], |
'ville' => $annuaire_distant['ville'], |
'code_postal' => $annuaire_distant['code_postal']); |
$tableau_annuaire_coel = array('nom' => $annuaire_coel['nom'], |
'prenom' => $annuaire_coel['prenom'], |
'ville' => $annuaire_coel['ville'], |
'code_postal' => $annuaire_coel['code_postal']); |
foreach ($tableau_annuaire_distant as $cle => $valeur) { |
if ($tableau_annuaire_coel[$cle] != $valeur) { |
$identique = false; |
break; |
} |
} |
return $identique; |
} |
|
/** |
* Vérifie si un utilisateur est présent dans la table Personnes de CoeL |
*/ |
protected function verifierPresenceUtilisateur($id) { |
$present = false; |
$requete = 'SELECT COUNT(cp_id_personne) AS nbre '. |
'FROM coel_personne '. |
"WHERE cp_ce_annuaire = {$this->bdd->quote($id)} ". |
" AND cp_ce_annuaire = cp_id_personne "; |
try { |
$nbre = $this->bdd->query($requete)->fetchColumn(); |
if (0 == $nbre) { |
$this->debug[] = "Utilisateur NON présent dans l'annuaire de COEL."; |
} else if (1 == $nbre) { |
$this->debug[] = "Utilisateur présent dans l'annuaire de COEL."; |
$present = true; |
} else if (false === $nbre) { |
$this->debug[] = "Erreur dans la requête de vérification de présence dans l'annuaire de COEL."; |
} |
} catch (PDOException $e) { |
$this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage()); |
} |
|
return $present; |
} |
|
/** |
* Renvoie l'état d'acceptation de la licence pour un utilisateur donné : |
* 0 (non acceptée) ou 1 (acceptée) |
*/ |
protected function recupererLicenceUtilisateur($id) { |
$requete = 'SELECT cp_mark_licence '. |
'FROM coel_personne '. |
"WHERE cp_ce_annuaire = {$this->bdd->quote($id)} ". |
" AND cp_ce_annuaire = cp_id_personne "; |
try { |
$licence = $this->bdd->query($requete)->fetchColumn(); |
if ($licence === false) { |
$this->debug[] = "La licence n'a pas pu être récupérée."; |
return 0; |
} else { |
return $licence; |
} |
} catch (PDOException $e) { |
$this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage()); |
} |
} |
|
/** |
* Met à jour les données de l'utilisateur dans la table CoeL Personnes |
*/ |
protected function mettreAJourUtilisateur($login, $mot_de_passe_sha1, $infos) { |
try { |
$cp_fmt_nom_complet = $infos['prenom'].' '.$infos['nom']; |
$requete = '' |
. 'UPDATE coel_personne' |
. " SET cp_id_personne = '{$infos['id']}', " |
. " cp_fmt_nom_complet = '$cp_fmt_nom_complet', cp_prenom = '{$infos['prenom']}', cp_nom = '{$infos['nom']}', " |
. " cp_code_postal = '{$infos['code_postal']}', cp_ville = '{$infos['ville']}', cp_truk_courriel = '{$infos['courriel']}', " |
. " cp_login = '{$infos['courriel']}', cp_mot_de_passe = '$mot_de_passe_sha1', cp_ce_annuaire = '{$infos['id']}' " |
. " WHERE cp_login = '$login' " |
//. " AND cp_mot_de_passe = '{$infos['mot_de_passe']}' " // WTF ? |
; |
|
// Ajout des données |
$resultat = $this->bdd->exec($requete); |
if ($resultat === false) { |
$this->messages[] = "L'utilisateur n'a pas été mis à jour car la requête a échouée."; |
} |
} catch (PDOException $e) { |
$messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete); |
} |
} |
|
/** |
* Met à jour l'utilisateur d'id $idUtilisateur, et passe son acceptation de |
* la licence à "1" |
*/ |
protected function setLicence($idUtilisateur = null) { |
if ($idUtilisateur == null) { |
$messages[] = "Id utilisateur null"; |
$this->envoyer(); |
exit; |
} |
// Mise à jour de l'utilisateur |
try { |
$requete = 'UPDATE coel_personne '. |
"SET cp_mark_licence = 1 ". |
"WHERE cp_ce_annuaire = '$idUtilisateur' "; |
// Ajout des données |
$resultat = $this->bdd->exec($requete); |
if ($resultat === false) { |
$this->messages[] = "L'utilisateur n'a pas été mis à jour car la requête a échouée."; |
} else { |
// Confirmation attendue par l'interface |
$this->envoyer(array("1")); |
exit; |
} |
} catch (PDOException $e) { |
$messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete); |
$this->envoyer(); |
} |
} |
|
/** |
* Ajoute une copie de l'utilisateur dans la table CoeL Personnes |
*/ |
protected function ajouterUtilisateurACoel($infos, $mot_de_passe_sha1) { |
try { |
// Construction de la requête d'ajout |
// Notes : pour rester compatibles avec l'annuaire de Tela, les utilisateurs sont ajoutés directement avec l'id |
// de l'annuaire Tela. Dans CoelPersonne, les personnes qui ne sont pas utilisateur sont ajoutés avec un id supérieur à 100 000 |
$cp_fmt_nom_complet = $infos['prenom'].' '.$infos['nom']; |
$cp_mark_licence = '0'; |
// si un utilisateur a changé son email dans un compte TB existant, une ligne avec son ID existera déjà et /i |
// => on fait un REPLACE (attention à la compatibilité !) |
$requete = 'REPLACE INTO coel_personne '. |
' (cp_id_personne, cp_fmt_nom_complet, cp_prenom, cp_nom, cp_code_postal, '. |
' cp_ville, cp_truk_courriel, cp_login, cp_mot_de_passe, cp_ce_annuaire, cp_mark_licence) '. |
"VALUES ('{$infos['id']}', '$cp_fmt_nom_complet', '{$infos['prenom']}', '{$infos['nom']}', ". |
"'{$infos['code_postal']}', '{$infos['ville']}', '{$infos['courriel']}', '{$infos['courriel']}', '".$mot_de_passe_sha1."', ". |
"'{$infos['id']}', $cp_mark_licence) "; |
//$this->debug[] = $requete; |
// Ajout des données |
$resultat = $this->bdd->exec($requete); |
if ($resultat === false) { |
$this->debug[] = "Utilisateur NON ajouté dans coel_personne car la requête a échouée."; |
} else { |
$this->debug[] = "Utilisateur ajouté à coel_personne."; |
} |
} catch (PDOException $e) { |
//$this->debug[] = "EREEUR SQL: " . sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete); |
$messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete); |
} |
} |
|
/** |
* Appelle l'annuaire pour connaître tous les détails de l'utilisateur de courriel $login |
*/ |
protected function obtenirInfosAnnuaire($login) { |
$url_annuaire = $this->config['coel']['urlAnnuaire']; |
$login_annuaire = $this->config['coel']['loginAnnuaire']; |
$mdp_annuaire = $this->config['coel']['mdpAnnuaire']; |
|
$posds = strpos($url_annuaire, "//"); |
$protocole = substr($url_annuaire, 0, $posds+2); |
$reste_adresse = substr($url_annuaire, $posds+2); |
// auth HTTP basic cracra |
$url_annuaire = $protocole . $login_annuaire . ':' . $mdp_annuaire . "@" . $reste_adresse; |
$url_annuaire .= '/' . $login; |
$url_annuaire .= '/xml'; // @TODO un jour, faire en sorte que ça lise du JSON |
|
$resultat_annuaire = file_get_contents($url_annuaire); |
$tableau_annuaire = null; |
if ($xml_utilisateur = simplexml_load_string($resultat_annuaire)) { |
// La fonction a retourné un objet |
foreach ($xml_utilisateur->children() as $key => $val) { |
if ((string) $val != '') { |
$tableau_annuaire[$key] = (String) $val; |
} |
} |
} |
return $tableau_annuaire; |
} |
|
/** |
* Enregistre le fait que la personne de login $login a accepté la licence de CoeL |
*/ |
protected function accepterLicence($login) { |
$sortie = false; |
try { |
$requete = 'UPDATE coel_personne '. |
'SET cp_mark_licence = 1 '. |
"WHERE cp_login = {$this->bdd->quote($login)} "; |
$resultat = $this->bdd->exec($requete); |
if ($resultat === false) { |
$this->debug[] = "La table Personne n'a pas été mise à jour car la requête a échoué"; |
} else { |
$this->debug[] = "Création du cookie licence."; |
$_SESSION['coel_utilisateur']['licence'] = '1'; |
// @TODO CHANGER |
//$this->setCookiePersistant('coel_licence', '1'); |
$sortie = true; |
} |
} catch (PDOException $e) { |
$messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete); |
} |
return $sortie; |
} |
|
/** |
* Essaye de trouver un jeton JWT non vide dans l'entête HTTP "Authorization" |
* |
* @return String un jeton JWT ou null |
*/ |
protected function lireJetonEntete() { |
$jwt = null; |
$headers = apache_request_headers(); |
if (isset($headers["Authorization"]) && ($headers["Authorization"] != "")) { |
$jwt = $headers["Authorization"]; |
} |
return $jwt; |
} |
|
/** |
* Vérifie un jeton auprès de l'annuaire |
* |
* @param String $jeton un jeton JWT |
* @return true si le jeton est vérifié, false sinon |
*/ |
protected function verifierJeton($jeton) { |
$urlServiceVerification =$this->config['coel']['urlServiceBaseAuth'] . "verifierjeton"; |
$urlServiceVerification .= "?token=" . $jeton; |
|
// file_get_contents râle si le certificat HTTPS est auto-signé |
//$retour = file_get_contents($urlServiceVerification); |
|
// curl avec les options suivantes ignore le pb de certificat (pour tester en local) |
$ch = curl_init(); |
$timeout = 5; |
curl_setopt($ch, CURLOPT_URL, $urlServiceVerification); |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); |
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); |
// équivalent de "-k" |
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); |
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); |
$data = curl_exec($ch); |
curl_close($ch); |
$retour = $data; |
|
$retour = json_decode($retour, true); |
|
return ($retour === true); |
} |
|
/** |
* Décode un jeton JWT (SSO) précédemment validé et retourne les infos |
* qu'il contient (payload / claims) |
* @param String $jeton un jeton JWT précédemment validé |
*/ |
protected function decoderJeton($jeton) { |
$parts = explode('.', $jeton); |
$payload = $parts[1]; |
$payload = base64_decode($payload); |
$payload = json_decode($payload, true); |
|
return $payload; |
} |
|
// accesseurs à deux ronds |
protected function getUtilisateurId() { |
if ($utilisateur = $this->getUtilisateur()) { |
return $utilisateur['id']; |
} else { |
return ''; |
} |
} |
protected function getUtilisateurLogin() { |
if ($utilisateur = $this->getUtilisateur()) { |
return $utilisateur['login']; |
} else { |
return ''; |
} |
} |
protected function getUtilisateurNomComplet() { |
if ($utilisateur = $this->getUtilisateur()) { |
return $utilisateur['nom_complet']; |
} else { |
return ''; |
} |
} |
protected function getUtilisateurPrenom() { |
if ($utilisateur = $this->getUtilisateur()) { |
return $utilisateur['prenom']; |
} else { |
return ''; |
} |
} |
protected function getUtilisateurNom() { |
if ($utilisateur = $this->getUtilisateur()) { |
return $utilisateur['nom']; |
} else { |
return ''; |
} |
} |
protected function getParametre() { |
if ($utilisateur = $this->getUtilisateur()) { |
return $utilisateur['parametre']; |
} else { |
return ''; |
} |
} |
protected function getLicence() { |
if (!empty($_SESSION['coel_utilisateur'])) { |
return (string) $_SESSION['coel_utilisateur']['licence']; |
} else { |
return ''; |
} |
} |
protected function getInfosAnnuaire() { |
if (!empty($_SESSION['coel_infosAnnuaire'])) { |
return $_SESSION['coel_infosAnnuaire']; |
} else { |
return ''; |
} |
} |
protected function setInfosAnnuaire($infosAnnuaire) { |
$_SESSION['coel_infosAnnuaire'] = $infosAnnuaire; |
} |
} |
|
/** |
* Compatibilité avec nginx - merci http://php.net/manual/fr/function.getallheaders.php |
*/ |
if (! function_exists('apache_request_headers')) { |
function apache_request_headers() { |
$headers = ''; |
foreach ($_SERVER as $name => $value) { |
if (substr($name, 0, 5) == 'HTTP_') { |
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value; |
} |
} |
return $headers; |
} |
} |
?> |