Subversion Repositories Applications.papyrus

Rev

Rev 2156 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

<?php

// Remplacement de apache_request_headers pour les vieux serveurs
// http://stackoverflow.com/questions/2916232/call-to-undefined-function-apache-request-headers
if(!function_exists('apache_request_headers')) {
        function apache_request_headers() {
                // Based on: http://www.iana.org/assignments/message-headers/message-headers.xml#perm-headers
                $arrCasedHeaders = array(
                                // HTTP
                                'Dasl'             => 'DASL',
                                'Dav'              => 'DAV',
                                'Etag'             => 'ETag',
                                'Mime-Version'     => 'MIME-Version',
                                'Slug'             => 'SLUG',
                                'Te'               => 'TE',
                                'Www-Authenticate' => 'WWW-Authenticate',
                                // MIME
                                'Content-Md5'      => 'Content-MD5',
                                'Content-Id'       => 'Content-ID',
                                'Content-Features' => 'Content-features',
                );
                $arrHttpHeaders = array();

                foreach($_SERVER as $strKey => $mixValue) {
                        if('HTTP_' !== substr($strKey, 0, 5)) {
                                continue;
                        }

                        $strHeaderKey = strtolower(substr($strKey, 5));

                        if(0 < substr_count($strHeaderKey, '_')) {
                                $arrHeaderKey = explode('_', $strHeaderKey);
                                $arrHeaderKey = array_map('ucfirst', $arrHeaderKey);
                                $strHeaderKey = implode('-', $arrHeaderKey);
                        }
                        else {
                                $strHeaderKey = ucfirst($strHeaderKey);
                        }

                        if(array_key_exists($strHeaderKey, $arrCasedHeaders)) {
                                $strHeaderKey = $arrCasedHeaders[$strHeaderKey];
                        }

                        $arrHttpHeaders[$strHeaderKey] = $mixValue;
                }
                return $arrHttpHeaders;
        }
}

// Attention bien v�rifier la pr�sence des variables suivantes :
// IDEN_UTILISE_SSO, IDEN_URL_SSO, IDEN_COOKIE_SSO, IDEN_SSO_SYNC
// dans le fichier iden_config.inc.php
class identificationSso {
        /**
         * si la valeur est vraiment vide, le cookie n'est pas posé, alors on met
         * une valeur vide pas vide :)
         */
        public static $VALEUR_JETON_VIDE = "jeton-vide";

        protected $cookie_mandataire = "";
        /** le cookie qui dure jusqu'à Vladivostok, si synchro partielle seulement */
        protected $duree_cookie_mandataire;

        /**
         * lorsqu'appelé par un client non-navigateur (qui n'envoie et ne reçoit pas
         * de cookies), permet de ne pas rediriger en boucle, en fournissant le
         * paramètre GET "non_interactif"
         */
        protected $mode_non_interactif = false;

        /** voir IDEN_SSO_SYNC dans iden_config.inc.php  */
        protected $synchro_complete;

        protected $annuaire_url = '';
        protected $bdd_annuaire = '';
        protected $table_annuaire = '';
        protected $champ_login = '';
        protected $champ_mdp = '';
        
        protected $communs_papyrus = null;

        public function __construct() {
                $this->communs_papyrus = $GLOBALS['_GEN_commun'];
                
                $this->cookie_mandataire = IDEN_COOKIE_SSO;
                $this->auth_header = IDEN_HEADER_SSO;   
                $this->annuaire_url = IDEN_URL_SSO;
                $this->synchro_complete = IDEN_SSO_SYNC;

                // gestion des clients qui ne gèrent pas les cookies (file_get_contents...)
                if (isset($_GET['non_interactif'])) {
                        $this->mode_non_interactif = true;
                }

                // si on est en mode synchro complète, on vérifie périodiquement l'état
                // du SSO, en utilisant un cookie qui ne dure pas jusqu'à Vladivostok
                $this->duree_cookie_mandataire = 3600 * 24 * 365;
                if ($this->synchro_complete === true) {
                        $this->duree_cookie_mandataire = 60; // une fois par minute (ça ou autre chose)
                }
                
                // c'est moche mais je n'ai pas trouv� plus simple pour r�cuperer la table annuaire en cours 
                // d'utilisation pour le site actuel (la bdd annuaire est la derni�re partie du dsn)
                $dsn_annuaire = $this->communs_papyrus['info_auth_bdd']->gsab_dsn;
                $dsn_annuaire = explode('/', $dsn_annuaire);
                $this->bdd_annuaire = end($dsn_annuaire);
                
                $this->table_annuaire = $this->communs_papyrus['info_auth_bdd']->gsab_nom_table;
                $this->champ_login = $this->communs_papyrus['info_auth_bdd']->gsab_nom_champ_login;
                $this->champ_mdp = $this->communs_papyrus['info_auth_bdd']->gsab_nom_champ_mdp;
        }

        // http://stackoverflow.com/questions/1251582/beautiful-way-to-remove-get-variables-with-php?lq=1
        protected function supprimerUrlVar($url, $var) {
                 return rtrim(preg_replace('/([?&])'.$var.'=[^&]*(&|$)/','$1',$url), '&?');
        }

        /**
         * Se connecte au SSO : redirige vers le service d'authentification, qui pose
         * le cookie tb_auth, puis re-redirige vers Papyrus avec le jeton en GET, puis
         * Papyrus stocke ce jeton dans son cookie mandataire, puis se re-redirige
         * vers lui-même pour éliminer le paramètre GET de l'URL (ouf !)
         */
        public function connecterEtRediriger() {
                //echo "JE CONNECTE<br/>";
                // sauvegarde de l'URL courante
                $url_redirect = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
                $params = 'login='.$_POST['username'].'&password='.$_POST['password'].'&redirect_url='.urlencode($url_redirect);
                $connexion_url = $this->annuaire_url."connexion?".$params;
                // redirection vers l'annuaire, qui va nous connecter au SSO et nous
                // renvoyer un jeton en GET
                header('Location: '.$connexion_url);
                exit;
        }

        /**
         * Se déconnecte du SSO : redirige vers le service d'authentification, qui
         * supprime le cookie tb_auth, puis re-redirige vers Papyrus avec un jeton
         * vide en GET; Papyrus stocke ce jeton vide dans son cookie mandataire,
         * puis se re-redirige vers lui-même pour éliminer le paramètre GET de l'URL
         */
        public function deconnecterEtRediriger() {
                //echo "JE DECONNECTE<br/>";
                // sauvegarde de l'URL courante
                $url_redirect = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
                $url_redirect = $this->supprimerUrlVar($url_redirect, 'logout');
                $deconnexion_url = $this->annuaire_url."deconnexion?".'redirect_url='.urlencode($url_redirect);
                // redirection vers l'annuaire, qui va nous déconnecter du SSO et nous
                // renvoyer un jeton vide en GET
                header('Location: '.$deconnexion_url);
                exit;
        }
        
        protected function synchroniserDepuisEtatSso() {
                //echo "JE SYNCHRONISE<br/>";
                // sauvegarde de l'URL courante
                $url_redirect = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
                $params = 'redirect_url='.urlencode($url_redirect);
                $identite_url = $this->annuaire_url."identite?".$params;
                // redirection vers l'annuaire, qui va nous donner l'état du SSO et nous
                // renvoyer un jeton en GET
                header('Location: '.$identite_url);
                exit;
        }

        /**
         * Appelée à chaque chargement de page
         */
        public function verifierIdentiteEtRediriger() {
                // si on fait autre chose qu'un GET, on ne vérifie pas l'identité, car
                // cela conduirait à une redirection en GET (avec le jeton), qui
                // supprimerait les données du corps de la requête
                if ($_SERVER['REQUEST_METHOD'] == "GET") {

                        // lecture jeton en GET si on vient de l'annuaire
                        if (isset($_GET['Authorization'])) {
                                //echo "Re-Redirection pour éliminer le GET<br/>";

                                // création / mise à jour du cookie mandataire (le jeton peut être
                                // vide en cas de déconnexion)
                                $jetonRecu = $_GET['Authorization'];
                                if ($jetonRecu == '') {
                                        $jetonRecu = self::$VALEUR_JETON_VIDE;
                                }
                                $this->setCookieMandataire($jetonRecu);
                                //echo "JETON REÇU: " . $_GET['Authorization'];
                                //echo "JETON POSÉ: "; var_dump($jetonRecu);
                                //exit;

                                // redirection pour éliminer le paramètre GET
                                $url_redirect = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
                                $url_redirect = $this->supprimerUrlVar($url_redirect, 'Authorization');
                                //echo "URL Redirect: $url_redirect"; exit;
                                header('Location: ' . $url_redirect);
                                exit;
                        }

                        // une fois qu'on a fait une connexion ou une déconnexion, on se base
                        // sur le cookie mandataire pour gérer l'identité dans Papyrus
                        //var_dump($_COOKIE); echo "<br/>";
                        // lecture cookie mandataire de Papyrus
                        $jeton = null;
                        $cookiePresent = false;
                        if (isset($_COOKIE[$this->cookie_mandataire])) {
                                $cookiePresent = true;
                                $jeton = $_COOKIE[$this->cookie_mandataire];
                        }
                        //echo "Cookie Présent: "; var_dump($cookiePresent);
                        //echo "JETON: "; var_dump($jeton); echo "<br/>";
                        //exit;

                        // s'il y a un cookie, qu'il contienne un jeton valide ou vide
                        if ($cookiePresent) {
                                // s'il contient un jeton non-vide
                                if ($jeton != self::$VALEUR_JETON_VIDE) {
                                        // reconnexion PEAR (l'utilisateur peut avoir changé)
                                        //echo "RECONNEXION!<br/>"; exit;
                                        $this->deconnexionPear();
                                        $this->connexionPear($jeton);

                                        // prolonge le cookie de 30 secondes si on est connecté, pour
                                        // minimiser les synchros avec le SSO
                                        $this->setCookieMandataire($jeton, 30);

                                        // si mode synchro partielle, le cookie mandataire dure logntemps,
                                        // on rafraîchit donc le jeton pour... euh... on sait jamais :-/
                                        if (! $this->synchro_complete) {
                                                // appel annuaire / identité avec jeton en GET
                                                $infosJson = $this->rafraichirJeton($jeton);
                                                $infos = json_decode($infosJson, true);
                                                //echo "INFOS: "; var_dump($infos); echo "<br/>";
                                                $jetonRafraichi = null;
                                                if (isset($infos['token'])) {
                                                        $jetonRafraichi = $infos['token'];
                                                }
                                                //echo "JETON Rafraîchi: "; var_dump($jetonRafraichi); echo "<br/>";
                                                //exit;
                                                // si jeton rafraîchi reçu
                                                if ($jetonRafraichi != null) {
                                                        // màj cookie avec jeton rafraîchi
                                                        $this->setCookieMandataire($jetonRafraichi);
                                                        // reconnexion PEAR (l'utilisateur peut avoir changé)
                                                        $this->deconnexionPear();
                                                        $this->connexionPear($jetonRafraichi);
                                                        //exit;
                                                } else {
                                                // sinon (non connecté ou problème annuaire / jeton)
                                                        // écriture cookie avec jeton vide
                                                        $this->setCookieMandataire(self::$VALEUR_JETON_VIDE);
                                                        // déconnexion PEAR
                                                        $this->deconnexionPear();
                                                }
                                        }
                                } else {
                                        // déconnexion PEAR ?
                                        $this->deconnexionPear();       
                                }
                        } else { // sinon (aucun cookie mandataire)
                                // en mode synchro complète, on vérifie chaque minute (lorsque le
                                // cookie mandataire n'est plus présent) si on n'est pas connecté
                                // sur le SSO, sauf si on est en mode non interactif
                                if ($this->synchro_complete && (! $this->mode_non_interactif)) {
                                        $this->synchroniserDepuisEtatSso();
                                }
                        }
                }
        }

        protected function setCookieMandataire($valeur, $duree=null) {
                if ($duree === null) {
                        $duree = $this->duree_cookie_mandataire;
                }
                setcookie($this->cookie_mandataire, $valeur, time() + $duree);
        }

        /**
         * Connecte l'utilisateur à Papyrus avec le système traditionnel fourni par PEAR
         */
        protected function connexionPear($jeton) {
                $jeton_decode = $this->decoderToken($jeton);

                // R�cup�ration du mot de passe pour remplir les infos de l'objet PEAR Auth
                $requete =  'SELECT '.$this->champ_mdp.' '.
                                'FROM '.$this->bdd_annuaire.'.'.$this->table_annuaire.' '.
                                'WHERE '.$this->champ_login.' = "'.$jeton_decode['sub'].'" ';

                // TODO: normalement �a n'est jamais le cas mais que fait t'on si l'utilisateur n'existe pas
                // dans notre base de donn�es ? (au pire il ne sera pas connect�)
                $this->communs_papyrus['pear_auth']->username = $jeton_decode['sub'];
                $this->communs_papyrus['pear_auth']->password = $this->communs_papyrus['pear_db']->getOne($requete);

                // Le mot de passe est d�j� crypt� dans la bdd donc il faut indiquer � pear de ne pas le re crytper
                if (isset($this->communs_papyrus['pear_auth']->storage_options)) {
                        $this->communs_papyrus['pear_auth']->storage_options['cryptType'] = 'none';
                }
                if (isset($this->communs_papyrus['pear_auth']->storage->options)) {
                        $this->communs_papyrus['pear_auth']->storage->options['cryptType'] = 'none';
                }

                // si on fait pas ça, ça marche pas (régénération de session apparemment)
                //$this->communs_papyrus['pear_auth']->setAuth($jeton_decode['sub']);
        }

        /**
         * Supprime tout ce qui a trait à PEAR afin de déconnecter l'utilisateur du
         * système d'authentification traditionnel de Papyrus
         */
        protected function deconnexionPear() {
                $this->communs_papyrus['pear_auth']->logout();

                $cookie_persistant_nom = session_name().'-memo';
                $cookie_utilisateur_nom = session_name().'-utilisateur';
                // Destruction du cookie de session de Papyrus : est ce utile?
                setcookie(session_name(), session_id(), time()-3600, '/');
                // Destruction du cookie de permanence de l'identitification de Papyrus
                setcookie($cookie_persistant_nom, '', time()-3600, '/');
                setcookie($cookie_utilisateur_nom, '', time()-3600, '/');
        }

        /**
         * Fournit un jeton à l'annuaire (jeton provenant du cookie mandataire
         * de Papyrus), le fait rafraîchir puis met à jour le cookie mandataire avec
         * le jeton rafraîchi
         */
        protected function rafraichirJeton($jeton) {
                $identiteServiceURL = $this->annuaire_url . "identite";
                $identiteServiceURL .= "?token=" . $jeton;
                //echo "URL: $identiteServiceURL<br/>";
                $ch = curl_init($identiteServiceURL);
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
                curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
                $jetonRafraichi = curl_exec($ch);
                //var_dump($jetonRafraichi);
                //var_dump(curl_error($ch)); exit;

                return $jetonRafraichi;
        }

        protected function decoderToken($token) {
                $token_parts = explode('.', $token);
                return json_decode(base64_decode($token_parts[1]), true);
        }
}
?>