Rev 3068 | Blame | Compare with Previous | Last modification | View Log | RSS feed
<?php// declare(encoding='UTF-8');/*** Classe de gestion des utilisateurs*** @internal Mininum PHP version : 5.2* @category CEL* @package Services* @subpackage Bibliothèques* @version 0.1* @author Aurelien PERONNET <aurelien@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>* @copyright 1999-2015 Tela Botanica <accueil@tela-botanica.org>*/class GestionUtilisateur extends Cel {/** contient le jeton SSO décodé, si une authentification a eu lieu avec succès */protected $token_decode;public function obtenirIdentiteConnectee() {$login_utilisateur = $this->getLoginUtilisateurVerifie();if ($login_utilisateur) {$utilisateur = $this->chargerInfosUtilisateur($login_utilisateur);$utilisateur['connecte'] = true;} else {$utilisateur = $this->getUtilisateurAnonyme();}return $utilisateur;}/*** Retourne l'utilisateur demandé, seulement s'il s'il est connecté au SSO* (le nom de la méthode est trompeur @TODO renommer); retourne false si* aucun jeton SSO n'est disponible*/public function obtenirUtilisateurSiExiste($login_utilisateur) {$utilisateur = $this->getUtilisateurAnonyme();if ($utilisateur_existe = $this->chargerInfosUtilisateur($login_utilisateur)) {$utilisateur = $utilisateur_existe;$utilisateur['connecte'] = true;}return $utilisateur;}/*** Retourne les informations contenues dans la table cel_utilisateurs_infos,* pour un login (adresse courriel) donné* @TODO un jour, se baser sur l'ID pour éviter les difficultés de migration de courriel*/public function lireCelUtilisateursInfos($login) {$requete = 'SELECT * '.'FROM cel_utilisateurs_infos AS cu '.'WHERE courriel = '.Cel::db()->proteger($login).' '.' -- '.__FILE__.':'.__LINE__;$resultats = Cel::db()->requeter($requete);if (is_array($resultats) && count($resultats) > 0) {return $resultats[0];} else {return false;}}/*** Retourne true si le profil local stocké dans cel_utilisateurs_infos* n'est plus à jour par rapport aux informations du jeton SSO; si le* jeton est vide, retourne false pour éviter de tout casser*/protected function profilAChange($infosCUI) {$aChange = false;if ($this->token_decode != null) {$aChange = ($this->token_decode['nom'] != $infosCUI['nom'])|| ($this->token_decode['intitule'] != $infosCUI['intitule'])|| ($this->token_decode['prenom'] != $infosCUI['prenom']);}//var_dump($aChange);return $aChange;}/*** Met à jour cel_utilisateurs_infos en fonction des informations* contenues par le jeton SSO; si ce dernier est vide, ne fait* rien (boulette-proof)*/protected function mettreAJourProfilLocal() {//echo "Mise à jour profil local !!";if ($this->token_decode != null && $this->token_decode['id'] != '') {$requete = 'UPDATE cel_utilisateurs_infos SET'. ' nom = ' . Cel::db()->proteger($this->token_decode['nom']) . ', '. ' intitule = ' . Cel::db()->proteger($this->token_decode['intitule']) . ', '. ' prenom = ' . Cel::db()->proteger($this->token_decode['prenom']). ' WHERE id_utilisateur = ' . Cel::db()->proteger($this->token_decode['id']). ' -- '.__FILE__.':'.__LINE__;//var_dump($requete);Cel::db()->executer($requete);}}/*** Répercute le nom et le prénom contenus dans le jeton SSO (si au* moins un des deux n'est pas vide) dans toutes les observations* et images de l'auteur; si le jeton SSO est vide, ne fait rien* (boulette-proof)** @TODO gérer l'intitulé un jour*/protected function mettreAJourCoordonneesDansObsEtImages() {//echo "Mise à jour obs et images !!";if ($this->token_decode != null && $this->token_decode['id'] != '' && ($this->token_decode['nom'] != '' || $this->token_decode['prenom'] != '')) {$requete = 'UPDATE cel_obs SET'. ' nom_utilisateur = ' . Cel::db()->proteger($this->token_decode['nom']) . ', '. ' prenom_utilisateur = ' . Cel::db()->proteger($this->token_decode['prenom']). ' WHERE ce_utilisateur = ' . Cel::db()->proteger($this->token_decode['id']) // s'assurer qu'il y a des ' autour de l'ID sans quoi les hash MD5 matcheront !. ' -- '.__FILE__.':'.__LINE__;//var_dump($requete);Cel::db()->executer($requete);$requete = 'UPDATE cel_images SET'. ' nom_utilisateur = ' . Cel::db()->proteger($this->token_decode['nom']) . ', '. ' prenom_utilisateur = ' . Cel::db()->proteger($this->token_decode['prenom']). ' WHERE ce_utilisateur = ' . Cel::db()->proteger($this->token_decode['id']) // s'assurer qu'il y a des ' autour de l'ID sans quoi les hash MD5 matcheront !. ' -- '.__FILE__.':'.__LINE__;//var_dump($requete);Cel::db()->executer($requete);}}/*** Charge l'état de l'utilisateur en fonction du jeton SSO avec lequel celui-ci* est identifié; si c'est la première fois que l'utilisateur se connecte au CeL,* crée un profil local dans cel_utilisateurs_infos; si le profil a changé depuis* la dernière connexion, le met à jour ainsi que les coordonnées dans les images* et observations** Si l'utilisateur n'est pas identifié, retourne false (pour le widget de saisie par ex.)* @TODO changer ce flux qui est sous-optimal, et renommer les méthodes dont* les noms ne collent pas** @param string $login le login (adresse courriel) de l'utilisateur*/private function chargerInfosUtilisateur($login) {//var_dump($this->token_decode);// si l'utilisateur n'est pas connecté (pas porteur d'un jeton), au revoirif ($this->token_decode == null) {return false;}// sinon, si on a affaire à quelqu'un de connecté : on récupère son profil local// Attention, si l'utilisateur a changé d'email depuis la dernière connection,// son profil local ne sera pas retrouvé; le cas est traité plus bas$infosCUI = $this->lireCelUtilisateursInfos($login);$retour = false;if ($infosCUI !== false) {// Les données du profil ont-elles changé depuis la dernière connexion ?if ($this->profilAChange($infosCUI)) {// mise à jour du profil local$this->mettreAJourProfilLocal();// mise à jour des obs et images$this->mettreAJourCoordonneesDansObsEtImages();// re-lecture du nouveau profil local (permet de détecter si la mise à jour s'est mal passée)$retour = $this->lireCelUtilisateursInfos($login);} else {$retour = $infosCUI;}} else {// première connexion au CeL ou changement d'email ! Crée le compte ou met à jour l'email$this->initialiserInfosUtilisateur();// re-lecture du nouveau profil local (si l'email a changé)$retour = $this->lireCelUtilisateursInfos($login);$this->affecterDonneesWidgetSaisie($login, $retour);}// booleanisation des valeurs$retour['admin'] = ($retour['admin'] == 1);$retour['licence_acceptee'] = (isset($retour['licence_acceptee']) && ($retour['licence_acceptee'] == 1));return $retour;}// @TODO a l'air inutilisée (2017-01-02) - vérifierprivate function utilisateurEstAutorise($id_utilisateur) {$autorise = false;$token = $this->getToken();// TODO: tester si le jeton contient réelement quelque chose ?if($token) {// On demande à l'annuaire si le jeton est bien valide$valide = file_get_contents($this->config['identification']['sso_url'].'/verifierjeton?token='.$token);$token_decode = $this->decoderToken($token);// Si l'utilisateur du token est bien le même que celui sur lequel on veut agir : OKif($token_decode['id'] == $id_utilisateur) {$autorise = true;} else {// Sinon on vérifie que l'utilisateur est admin$requete = "SELECT admin FROM cel_utilisateurs_infos WHERE id_utilisateur = ".Cel::db()->proteger($token_decode['id']);$resultat = Cel::db()->requeter($requete);$admin = false;if ($resultat && count($resultat) > 0) {$autorise = ($resultat[0]['admin'] == 1);}}} else {// pas de token, on vérifie bien qu'il s'agit d'une session temporaire$autorise = ($id_utilisateur == session_id());}return $autorise;}// renvoie false ou bien le login utilisateur actuelprivate function getLoginUtilisateurVerifie() {$token = $this->getToken();$login = false;if($token) {// On demande à l'annuaire si le jeton est bien valide// curl avec les options suivantes ignore le pb de certificat (pour tester en local)// @TODO CHANGER !$ch = curl_init();$timeout = 5;$url = $this->config['identification']['sso_url'].'/verifierjeton?token='.$token;curl_setopt($ch, CURLOPT_URL, $url);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);$valide = curl_exec($ch);curl_close($ch);//$valide = file_get_contents($this->config['identification']['sso_url'].'/verifierjeton?token='.$token);$login = ($valide === "true") ? $this->obtenirLoginParToken($token) : false;}return $login;}// @WTF doublon avec la méthode du dessus ? Qu'est-ce que c'est que ce bins ?protected function verifierJeton($jeton) {$urlServiceVerification = $this->conteneur->getParametre("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);}private function decoderToken($token) {$token_parts = explode('.', $token);return json_decode($this->urlsafeB64Decode($token_parts[1]), true);}/*** Decode a string with URL-safe Base64.* copié depuis firebase/jwt** @param string $input A Base64 encoded string* @return string A decoded string*/protected function urlsafeB64Decode($input) {$remainder = strlen($input) % 4;if ($remainder) {$padlen = 4 - $remainder;$input .= str_repeat('=', $padlen);}return base64_decode(strtr($input, '-_', '+/'));}private function obtenirLoginParToken($token) {$this->token_decode = $this->decoderToken($token);return $this->token_decode['sub'];}private function getToken() {// Premier essai, dans le header$headers = apache_request_headers();$token = !empty($headers['Authorization']) ? $headers['Authorization'] : null;// Sinon dans $_REQUEST ?if($token == null) {$token = !empty($_REQUEST['Authorization']) ? $_REQUEST['Authorization'] : null;}return $token;}private function getUtilisateurAnonyme() {return array('connecte' => false,'id_utilisateur' => session_id(),'courriel' => '','mot_de_passe' => '','nom' => '','prenom' => '','licence_acceptee' => false,'preferences_utilisateur' => '','admin' => false);}/*** Lors de la première connexion au CeL, remplis la table cel_utilisateurs_infos avec* les données du jeton SSO*/private function initialiserInfosUtilisateur() {$requete = 'INSERT INTO cel_utilisateurs_infos'. ' (id_utilisateur, intitule, prenom, nom, courriel, admin, licence_acceptee, preferences, date_premiere_utilisation )'. ' VALUES ('. Cel::db()->proteger($this->token_decode['id']) . ', '. Cel::db()->proteger($this->token_decode['intitule']) . ', '. Cel::db()->proteger($this->token_decode['prenom']) . ', '. Cel::db()->proteger($this->token_decode['nom']) . ', '. Cel::db()->proteger($this->token_decode['sub']) . ', '. "'0', '0', NULL, NOW()". ' ) ON DUPLICATE KEY UPDATE'. ' date_premiere_utilisation = NOW(),'. ' intitule = ' . Cel::db()->proteger($this->token_decode['intitule']) . ','. ' prenom = ' . Cel::db()->proteger($this->token_decode['prenom']) . ','. ' nom = ' . Cel::db()->proteger($this->token_decode['nom']) . ','. ' courriel = ' . Cel::db()->proteger($this->token_decode['sub']). ' -- '.__FILE__.':'.__LINE__;Cel::db()->executer($requete);}/*** Lors de la première connection au cel d'un utilisateur, affecte à son compte ses observations saisies* dans les widgets de saisie, où seul son mail avait été conservé en attendant* Enter description here ...* @param string $mail_utilisateur* @param array $infos_utilisateur*/private function affecterDonneesWidgetSaisie($mail_utilisateur, $infos_utilisateur) {//TODO tout ceci pourrait être simplifié sans avoir besoin d'instancier quoi que ce soit$gestion_obs = new GestionObservation($this->config);$gestion_img = new GestionImage($this->config);$gestion_obs->migrerObservationsMailVersId($mail_utilisateur, $infos_utilisateur);$gestion_img->migrerImagesMailVersId($mail_utilisateur, $infos_utilisateur);GestionMotsClesChemin::migrerMotsClesMailVersId($mail_utilisateur, $infos_utilisateur);}}/*** 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;}}?>