From ea8e620d6b83990a698cbb9345a95eedd444cefa Mon Sep 17 00:00:00 2001 From: Lucas Mascaro Date: Wed, 2 Dec 2015 12:32:57 +0100 Subject: [PATCH] mise en place de l'authentification --- autoloader.php | 27 ++++-- login.php | 43 ++++----- src/Authentification.php | 87 +++++++++++++++++ src/Response.php | 202 +++++++++++++++++++++++++++++++++++++++ src/config/users.json | 6 ++ 5 files changed, 331 insertions(+), 34 deletions(-) create mode 100755 src/Authentification.php create mode 100644 src/Response.php create mode 100755 src/config/users.json diff --git a/autoloader.php b/autoloader.php index dbfecc3..da6f80f 100755 --- a/autoloader.php +++ b/autoloader.php @@ -4,18 +4,27 @@ * fonction d'autoloading : prend en paramètre le nom de la classe et s'occupe d'inclure les fichiers correspondant aux classes */ +//pour l'inclusion dans le dossier src +$GLOBALS['managers_dir'] = dirname(__FILE__).DIRECTORY_SEPARATOR.'src'; + function autoloader($class) { - //si on charge le StaticRepo - if(strpos($class, 'StaticRepo') !== FALSE){ - require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'repositories'.DIRECTORY_SEPARATOR.$class . '.php'; - } - //si on charge un Repo - elseif(strpos($class, 'Repo') !== FALSE){ - require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'repositories'.DIRECTORY_SEPARATOR.'repos'.DIRECTORY_SEPARATOR.$class . '.php'; + //si on charge le StaticRepo + if(strpos($class, 'StaticRepo') !== FALSE){ + require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'repositories'.DIRECTORY_SEPARATOR.$class . '.php'; + } + //si on charge un Repo + elseif(strpos($class, 'Repo') !== FALSE){ + require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'repositories'.DIRECTORY_SEPARATOR.'repos'.DIRECTORY_SEPARATOR.$class . '.php'; - //cas particuliers pas identifiable par nom de classe - } + //cas particuliers pas identifiable par nom de classe + }else{ + //si on charge un manager + if(is_file(dirname(__FILE__).DIRECTORY_SEPARATOR.'src'.DIRECTORY_SEPARATOR.$class . '.php')){ + require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'src'.DIRECTORY_SEPARATOR.$class . '.php'; + + } + } } //enregistrememnt de la fonction tout en bas de la pile pour ne pas casser l'autoloader de phpUnit diff --git a/login.php b/login.php index 84e8407..c07c582 100755 --- a/login.php +++ b/login.php @@ -1,29 +1,23 @@ - 1 && strlen($_POST['mail']) > 1 && strlen($_POST['password']) > 1 && strlen($_POST['co']) > 1; // si au moins 1 caractère - $usernameCheck = $postVariablesNEmpty && preg_match("/^[\w -]{3,10}$/i", $_POST['username']); // utilisateur -> "alphanum_- " -> 3 à 10 caractères - $mailCheck = $usernameCheck && preg_match("/^[\w\.-]+@[\w\.-]+\.[a-z]{2,4}$/i", $_POST['mail']); // mail -> bon format - $passwordCheck = $mailCheck && preg_match("/^[\w -]{8,50}$/i", $_POST['password']); // password -> "alphanum_- " -> 8 à 50 caractères - $coCheck = $passwordCheck && $_POST['co'] == 'Me connecter'; - -if( $coCheck ){ // si toutes les valeurs sont correctes - - $user = array(); // on définit l'utilisateur - $user['name'] = $_POST['username']; - $user['mail'] = $_POST['mail']; - $user['password'] = $_POST['password']; - $user['hash'] = sha1($_POST['password']); +authentification($_POST['username'],$_POST['password']); } - -// retourne VRAI si l'utilisateur est connecté -function connected($user){ return ($user != null); } - +if(Authentification::checkUser(0)){ + header("Location: http://".$_SERVER['HTTP_HOST']."/Dashboard.php"); + die(); +}; ?> @@ -59,14 +53,13 @@ function connected($user){ return ($user != null); } echo 'Vous êtes connectés.'; } - echo ""; - echo ""; - echo ""; + echo ""; + echo ""; echo ""; echo ""; ?> - + \ No newline at end of file diff --git a/src/Authentification.php b/src/Authentification.php new file mode 100755 index 0000000..a81fd19 --- /dev/null +++ b/src/Authentification.php @@ -0,0 +1,87 @@ +users = json_decode(file_get_contents($GLOBALS['managers_dir'].DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'users.json'),true); + } + + /** + * méthode d'authentification, utilise param['identifiant'] et param['mdp'] et les comparent à + * nos utilisateurs enregistrés puis créer une session securisée par token + * @param array $param contiens les infomations de connection + * @return json json contenant le résultat de l'authentification (true si authentification correcte, sinon non) + */ + public function authentification($user,$mdp){ + foreach($this->users as $utilisateur=>$infos){ + if($utilisateur == $user and $infos['password'] == $mdp){ + $this->createSecureSession($user,$infos['role']); + return true; + } + } + return false; + } + + /** + * déconnecte l'utilisateur en détruisant la session et le cookie + * @return json renvoie true, il n'y aucune raison que ça foire + */ + public function deconnection(){ + $this->destroySecureSession(); + Response::quickResponse(200,json_encode(['result' => true])); + } + + /** + * créer une session sécurisé , protégé du vol de session par identification de l'utilisateur par navigateur/ip/cookie + * @param String $user nom d'utilisateur + * @param String $role role de l'utilisateur (0=administrateur, 1= prof, 2=scolarité,3=élève) + * @return void + */ + private function createSecureSession($user,$role){ + $id = uniqid(); + $_SESSION['id'] = $id; + $_SESSION['token'] = sha1($_SERVER['HTTP_USER_AGENT'].$_SERVER['REMOTE_ADDR'].$id); + setcookie('UserId',$id,time()+10*60,'/'); + + $_SESSION['user'] = $user; + $_SESSION['role'] = $role; + + } + + /** + * Détruit une session + * @return void + */ + private function destroySecureSession(){ + session_destroy(); + setcookie('token',time()-1); + } + + /** + * Vérifie qu'un utilisateur donné a les droits demandés (passés en paramètres) + * @param int $role role minimum + * @param boolean $strict si strict vaut true, seul les utilisateurs avec le role précis seront acceptés, sinon tout les utilisateurs + * avec un role superieur le seront + * @return boolean + */ + public static function checkUser($role, $strict=false){ + if(isset($_SESSION['token'])){ + foreach($_SESSION['role'] as $roleUser){ + if(($strict and $roleUser == $role) or (!$strict and $roleUser<= $role)){ + if($_SESSION['token'] == sha1($_SERVER['HTTP_USER_AGENT'].$_SERVER['REMOTE_ADDR'].$_SESSION['id'])){ + setcookie('UserId',$_COOKIE['UserId'],time()+10*60,'/'); + return true; + }; + } + } + } + return false; + } + + public static function getCurrentUser(){ + return $_SESSION['user']; + } +} +?> diff --git a/src/Response.php b/src/Response.php new file mode 100644 index 0000000..23e3971 --- /dev/null +++ b/src/Response.php @@ -0,0 +1,202 @@ + '100 Continue', + 101 => '101 Switching Protocols', + //Successful 2xx + 200 => '200 OK', + 201 => '201 Created', + 202 => '202 Accepted', + 203 => '203 Non-Authoritative Information', + 204 => '204 No Content', + 205 => '205 Reset Content', + 206 => '206 Partial Content', + 226 => '226 IM Used', + //Redirection 3xx + 300 => '300 Multiple Choices', + 301 => '301 Moved Permanently', + 302 => '302 Found', + 303 => '303 See Other', + 304 => '304 Not Modified', + 305 => '305 Use Proxy', + 306 => '306 (Unused)', + 307 => '307 Temporary Redirect', + //Client Error 4xx + 400 => '400 Bad Request', + 401 => '401 Unauthorized', + 402 => '402 Payment Required', + 403 => '403 Forbidden', + 404 => '404 Not Found', + 405 => '405 Method Not Allowed', + 406 => '406 Not Acceptable', + 407 => '407 Proxy Authentication Required', + 408 => '408 Request Timeout', + 409 => '409 Conflict', + 410 => '410 Gone', + 411 => '411 Length Required', + 412 => '412 Precondition Failed', + 413 => '413 Request Entity Too Large', + 414 => '414 Request-URI Too Long', + 415 => '415 Unsupported Media Type', + 416 => '416 Requested Range Not Satisfiable', + 417 => '417 Expectation Failed', + 418 => '418 I\'m a teapot', + 422 => '422 Unprocessable Entity', + 423 => '423 Locked', + 426 => '426 Upgrade Required', + 428 => '428 Precondition Required', + 429 => '429 Too Many Requests', + 431 => '431 Request Header Fields Too Large', + //Server Error 5xx + 500 => '500 Internal Server Error', + 501 => '501 Not Implemented', + 502 => '502 Bad Gateway', + 503 => '503 Service Unavailable', + 504 => '504 Gateway Timeout', + 505 => '505 HTTP Version Not Supported', + 506 => '506 Variant Also Negotiates', + 510 => '510 Not Extended', + 511 => '511 Network Authentication Required' + ); + + /** + * Constructeur de la Response + * @param int $status status HTTP de la réponse (404,200,500, etc) + * @param bool|false $stream Si la réponse est un stream (avtive/désactive les méthodes send/stream() + * @param string $type type HTTP des données de retour + * @param bool|true $clearBuffer si activé, vide le buffer avant chaque envoi de donnée (a pour effet de ne pas afficher les echo/printf) + */ + public function __construct($status = 200,$stream = false,$type = 'application/json', $clearBuffer = false) + { + $this->status = $status; + array_push($this->headers,['Content-Type',$type]); + + $this->config['clearBuffer'] = $clearBuffer; + $this->config['stream'] = $stream; + } + + /** Ajoute du contenu a la réponse qui sera envoyé (par stream() ou par send() ) + * @param $content contenu a ajouter a la réponse + */ + public function write($content){ + $this->response .= $content; + } + + /** Envoie une partie de réponse au client (doit être récupéré en ajax, aucun intéret sinon), chaque bloc de donéne envoyé est séparé par + * un délimiteur ("//Block//" par défaut).ATTENTION: stream() vide la réponse (si on write() puis stream(), la réponse qu'il restera dans l'objet sera vide) + * @param string $content contenu a envoyer (optionnel car on peut utiliser la méthode write pour le faire) + * @throws Exception si la réponse n'est pas un stream + */ + public function stream($content="",$delimiter = "//Block//"){ + //vérification que la réponse est un stream + if(!$this->config['stream']){ + throw new Exception("Stream d'une réponse synchrone"); + } + //si les headers ne sont pas encore envoyés, on le fait + if(!headers_sent()){ + $this->sendHeader(); + } + //si demandé, on clear le buffer avant d'envoyer + if($this->config['clearBuffer']){ + ob_end_clean(); + if($GLOBALS['compression']){ + ob_start("ob_gzhandler"); + }else{ + ob_start(); + } + } + //on envoi le contenu de response et la variable content + if($this->response!=""){ + echo $delimiter.$this->response; + }if($content != ""){ + echo $delimiter.$content; + } + ob_flush();flush(); + $this->response = ''; + } + + /** + * Envoi les headers de la réponse (status et ceux potentiellement défnini par l'utilisateur) + */ + public function sendHeader(){ + //envoie le status de la requete (petit trick suivant l'architecture de PHP) + if (strpos(PHP_SAPI, 'cgi') === 0) { + header(sprintf('Status: %s', $this->Messages[$this->status])); + } else { + header(sprintf('HTTP/1.1 %s', $this->Messages[$this->status])); + } + //les autres headers + foreach($this->headers as $header){ + header(sprintf('%s: %s',$header[0],$header[1])); + } + } + + /** + * Défini un header qui sera envoyé + * @param $header Nom du header + * @param $value Valeur du header + */ + public function setHeader($header,$value){ + array_push($this->headers,[$header,$value]); + } + + /** Envoi la réponse et ferme la communication + * @throws Exception si la réponse est un stream + */ + public function send(){ + //vérification que la réponse n'est pas un stream + if($this->config['stream']){ + throw new Exception("Envoi synchrone d'une réponse stream"); + } + //si les headers ne sont pas encore envoyés, on le fait + if(!headers_sent()){ + $this->sendHeader(); + } + //si demandé, on clear le buffer avant d'envoyer + if($this->config['clearBuffer']){ + ob_end_clean(); + if($GLOBALS['compression']){ + ob_start("ob_gzhandler"); + }else{ + ob_start(); + } + } + //envoi de la réponse + echo $this->response; + //fermeture de la communication + header('Connection: close'); + header('Content-Length: '.ob_get_length()); + ob_end_flush(); + ob_flush(); + flush(); + //permet au reste du script de s'executer même si la réponse a été envoyé et que l'utilisateur interromp le script (changement de page, etc...) + ignore_user_abort(true); + } + + /** + * @param int $status status HTTP de la réponse (404,200,500, etc) + * @param $content + * @param string $type + */ + public static function quickResponse($status,$content,$type = 'application/json'){ + $response = new Response($status,false,$type); + $response->write($content); + $response->send(); + } +} diff --git a/src/config/users.json b/src/config/users.json new file mode 100755 index 0000000..310a83a --- /dev/null +++ b/src/config/users.json @@ -0,0 +1,6 @@ +{ + "secretaire": { + "password":"thecakeisalie", + "role":[0] + } +}