* @author David DELON * @author Mathias CHOUET * @license GPL v3 * @license CECILL v2 * @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($login, $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; } } ?>