Rev 2095 | Rev 2143 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
<?php// declare(encoding='UTF-8');/*** Contient les méthodes permettant d'identifier l'utilisateur de l'application DEL.** @category DEL* @package Services* @package Bibliotheque* @version 0.1* @author Mathias CHOUET <mathias@tela-botanica.org>* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>* @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-2014 Tela Botanica <accueil@tela-botanica.org>*/class GestionUtilisateur {protected $conteneur;protected $contexte;protected $bdd;protected $utilisateur = array();public function __construct(Conteneur $conteneur) {$this->conteneur = $conteneur;$this->bdd = $this->conteneur->getBdd();$this->contexte = $this->conteneur->getContexte();$this->chargerUtilisateur();}/*** Charge des données utilisateur : un vrai profil si l'utilisateur est identifié,* un profil "anonyme" sinon*/private function chargerUtilisateur() {$this->demarrerSession();$infos = $this->getUtilisateurIdentifie();$this->utilisateur = ($infos == null) ? $this->getUtilisateurAnonyme() : $infos;}private function demarrerSession() {if (session_id() == '') {// TODO : modifier ce test lors du passage en php 5.4// TODO : expliquer pourquoi SVP :)session_start();}}// rétrocompat avec un vieux machinpublic function getIdAnonymeTemporaire() {return $this->utilisateur['session_id'];}/*** Retourne l'utilisateur en cours, chargé dans le constructeur* par getutilisateurIdentifié()*/public function getUtilisateur() {return $this->utilisateur;}/*** 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é** @return Array un profil utilisateur ou null*/public function getUtilisateurIdentifie() {$utilisateur = null;// lecture du jeton$jeton = $this->lireJetonEntete();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"];$utilisateur = $this->recupererUtilisateurEnBdd($courriel);}}}if ($utilisateur != null) {$utilisateur = $this->completerInfosUtilisateur($utilisateur);}return $utilisateur;}/*** 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->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);}/*** 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;}/*** Retourne un profil d'utilisateur non connecté (anonyme), avec un identifiant de session* PHP qui permet de suivre son activité*/public function getUtilisateurAnonyme() {return array('connecte' => false,'id_utilisateur' => session_id(),'courriel' => '','mot_de_passe' => '','nom' => '','prenom' => '','admin' => '0','session_id' => session_id());}/*** Récupère les données d'un utilisateur dans la BDD depuis son login; considère que* l'utilisateur est légitime car il fournit un jeton SSO vérifié par l'annuaire** @param String $login le courriel de l'utilisateur* @return array les infos de l'utilisateur*/private function recupererUtilisateurEnBdd($login) {$loginP = $this->bdd->proteger($login);$requete = 'SELECT du.id_utilisateur, nom, prenom, courriel, dui.admin '.'FROM del_utilisateur AS du '.' LEFT JOIN del_utilisateur_infos AS dui ON (du.id_utilisateur = dui.id_utilisateur) '."WHERE courriel = $loginP ".' -- '.__FILE__.' : '.__LINE__;return $this->bdd->recuperer($requete);}protected function completerInfosUtilisateur($utilisateur) {$utilisateur['session_id'] = session_id();$utilisateur['connecte'] = true;return $utilisateur;}protected function getEvenements($id_utilisateur) {$sql = $this->conteneur->getSql();$date = $this->getDerniereDateConsultationEvenements($id_utilisateur);$requete_activite = $sql->getRequeteNbEvenementsDepuisDate($id_utilisateur, $date);$resultats = $this->bdd->recupererTous($requete_activite);$nb_evenements = $resultats[0]['nb_evenements'];$evenements['nb_evenements'] = $nb_evenements;$evenements['date_derniere_consultation_evenements'] = $date;return $evenements;}public function getDerniereDateConsultationEvenements($id_utilisateur) {$requete = "SELECT date_derniere_consultation_evenements FROM del_utilisateur_infos "."WHERE id_utilisateur = ".$this->bdd->proteger($id_utilisateur);$date = $this->bdd->recuperer($requete);$date = !empty($date['date_derniere_consultation_evenements']) ? $date['date_derniere_consultation_evenements'] : "0";return $date;}public function setDerniereDateConsultationEvenements($id_utilisateur, $date) {// Vérification que la ligne correspondant à l'utilisateur dans la table// infos existe bien (sinon on la crée)$infos_utilisateur = $this->obtenirInfosUtilisateur($id_utilisateur);if (empty($infos_utilisateur)) {$this->initialiserInfosUtilisateur($id_utilisateur);}$requete = "UPDATE del_utilisateur_infos SET date_derniere_consultation_evenements = ".$this->bdd->proteger($date)." "."WHERE id_utilisateur = ".$this->bdd->proteger($id_utilisateur);$this->bdd->executer($requete);}protected function ajouterEvenements(&$utilisateur) {$evenements = $this->getEvenements($utilisateur['id_utilisateur']);$utilisateur = array_merge($utilisateur, $evenements);}public function obtenirPreferencesUtilisateur($id_utilisateur) {$prefs_utilisateur = $this->obtenirInfosUtilisateur($id_utilisateur);if (empty($prefs_utilisateur)) {$this->initialiserInfosUtilisateur($id_utilisateur);$prefs_utilisateur = $this->renvoyerInfosUtilisateurDefaut($id_utilisateur);} else {if (empty($prefs_utilisateur['preferences'])) {$prefs_utilisateur['preferences'] = $this->obtenirTableauPreferenceDefaut();} else {$prefs_utilisateur['preferences'] = json_decode($prefs_utilisateur['preferences']);}$prefs_utilisateur['admin'] = $prefs_utilisateur['admin'];}return $prefs_utilisateur;}private function obtenirTableauPreferenceDefaut() {return array('mail_notification_mes_obs' => '1', 'mail_notification_toutes_obs' => '0');}private function renvoyerInfosUtilisateurDefaut($id_utilisateur) {return array('id_utilisateur' => $id_utilisateur,'admin' => '0','preferences' => $this->obtenirTableauPreferenceDefaut(),'date_premiere_utilisation' => date('Y-m-d H:i:s'),'date_derniere_consultation_evenements' => '0000-00-00 00:00:00');}public function obtenirInfosUtilisateur($id_utilisateur) {$requete = 'SELECT * '.'FROM del_utilisateur_infos '.'WHERE id_utilisateur = '.$this->bdd->proteger($id_utilisateur).' '.' -- '.__FILE__.' : '.__LINE__;$prefs_utilisateur = $this->bdd->recuperer($requete);return $prefs_utilisateur;}public function initialiserInfosUtilisateur($id_utilisateur) {$preferences_defaut = $this->obtenirTableauPreferenceDefaut();$prefsEncodeesP = $this->bdd->proteger(json_encode($preferences_defaut));$idUtilisateurP = $this->bdd->proteger($id_utilisateur);$requete = 'INSERT INTO del_utilisateur_infos '.'(id_utilisateur, admin, preferences, date_premiere_utilisation )'."VALUES ($idUtilisateurP, 0, $prefsEncodeesP, NOW()) ".'ON DUPLICATE KEY UPDATE date_premiere_utilisation = NOW() '.' -- '.__FILE__.' : '.__LINE__;return $this->bdd->executer($requete);}/*** Vérifie qu'un utilisateur connu est identifié (mode non anonyme) et* que son identifiant numérique est égal à $id_utilisateur; si non,* retourne une erreur HTTP 401 et quitte le programme** @param integer $id_utilisateur l'utilisateur attendu*/public function controleUtilisateurIdentifie($id_utilisateur) {$ok = ($this->utilisateur['connecte'] === true && $this->utilisateur['id_utilisateur'] == $id_utilisateur);if (! $ok) {$message = "";if ($this->utilisateur['connecte'] === true) {$message = "Vous n'êtes pas propriétaire de la ressource demandée";} else {$message = "Vous n'êtes pas identifié";}$code = RestServeur::HTTP_CODE_ACCES_NON_AUTORISE;throw new Exception($message, $code);}}// rétrocompat'public function etreAdmin() {return ($this->utilisateur['admin'] >= 1);}// rétrocompat'public function etreUtilisateurAvecDroitAdmin() {$etreAdmin = $this->etreAdmin();if (! $etreAdmin) {$message = "Vous ne pouvez pas accéder à ce service car vous n'avez pas les droits d'administrateur !\n";$code = RestServeur::HTTP_CODE_ACCES_NON_AUTORISE;throw new Exception($message, $code);}return $etreAdmin;}}/*** 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;}}