From dc1f95d35ace2b8e4b43ca63407a528b7afb090e Mon Sep 17 00:00:00 2001 From: Thomas Pathier <tpxp@live.fr> Date: Wed, 26 Dec 2018 20:52:42 +0100 Subject: [PATCH] Add a first revision of the project --- .gitignore | 1 + LICENCE | 47 +++++++++++++ README.md | 14 +++- html/auth.php | 9 +++ html/index.php | 33 +++++++++ html/logout.php | 10 +++ lib/config.template.php | 37 ++++++++++ lib/init.php | 31 +++++++++ lib/oauth.php | 148 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 327 insertions(+), 3 deletions(-) create mode 100644 .gitignore create mode 100644 LICENCE create mode 100644 html/auth.php create mode 100644 html/index.php create mode 100644 html/logout.php create mode 100644 lib/config.template.php create mode 100644 lib/init.php create mode 100644 lib/oauth.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4867f1f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +example/lib/config.php diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..164a6d4 --- /dev/null +++ b/LICENCE @@ -0,0 +1,47 @@ + Licence "Tout le monde sauf ViaRézo" (TLMSVR) + +D'après la licence "Anyone but" (AB) et la licence MIT + +Copyright (c) 2018 Thomas Pathier + +Il est gratuitement permis à toute personne obtenant une copie de ce logiciel +et de la documentation asssociée (le "Logiciel") ou à qui est fourni ce Logiciel +d'en bénéficier sans restriction, et ce sans limitation de droits, copie, +fusion, publication, distribution, sous-licenciement et/ou vente de copies du +Logiciel, à condition de respecter les conditions suivantes : + +La licence ci-dessus et ces informations de permissions doivent être inclues +dans toutes les copies du Logiciel. + +La mention du logiciel ci-dessous doit apparaître sur tous les produits +utilisant le Logiciel (par exemple dans le footer) : +""" +Connexion gentiment fournie par Thomas Pathier (https://tpxp.ddns.net). <3 +""" +Si la technologie le permet un lien hypertexte vers "https://tpxp.ddns.net" peut +être créé sur le texte "Thomas Pathier", la mention "(https://tpxp.ddns.net)" +peut alors être enlevée. + +Toutes les altérations du Logiciel mentionnées ci-dessus (notamment copie, +modification, fusion, publication, distribution, sous-licenciement,...) doivent +être placées sous licence TLMSVR. + +LE LOGICIEL EST FOURNI "EN L'ETAT", SANS AUCUNE GARANTIE EXPRESSE OU IMPLICITE, +NOTAMMENT MAIS NON LIMITE AUX GARANTIES DE VENDICITE, ADAPTABILITE A UN BESOIN +PARTICULIER ET RESPECT DES LOIS. + +EN AUCUN CAS LE(S) AUTEUR(S) OU PROPRIETAIRE(S) DU COPYRIGHT NE SAURAI(EN)T ETRE +RESPONSABLE(S) POUR UNE QUELCONQUE PLAINTE, DOMMAGE OU AUTRE RESPONSABILITE, +QU'ELLE SOIT DANS L'ACTION D'UN CONTRAT, UN TORT OU AUTRE, QU'ELLE RESULTE OU +NON, OU SOIT EN LIEN AVEC LE LOGICIEL, SON USAGE OU D'AUTRES TRANSACTIONS DANS +LE LOGICIEL. + +EXCEPTIONS + +Le Logiciel ne doit pas être utilisé dans un logiciel de l'Association des +Réseaux de CentraleSupélec (ARCS), également connue de son nom d'usage ViaRézo +sans accord explicite de l'ayant-droit du Logiciel. Les membres de +l'association susmentionnée, qu'ils soient actifs, anciennement actifs ou +futurs membres actifs (également nommés GPAs ou n1As) ne sont pas autorisés à +utiliser le Logiciel ni à y contribuer sauf autorisation expression de l'ayant +droit du Logiciel. diff --git a/README.md b/README.md index 6e8ec8d..78ae6c9 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,17 @@ en `/auth.php`, sans quoi le serveur oAuth refusera la redirection ! Une fois votre client validé par une personne ayant les droits sur le serveur oAuth (ne me demandez pas, je ne les ai plus !), vous pouvez continuer. -... A suivre ... +### Exemple +Un exemple d'utilisation de la librairie est dans le dossier `html`. Si vous +êtes pressé, vous pouvez vous baser dessus ! ## Contributions Vos contributions sont les bienvenues ! Il y a notamment un peu de travail -sur la récupération de données avec des services externes et sur le -rafraîchissement du token (refresh token). +sur : +* la récupération de données avec des services externes (LinkCS) +* le rafraîchissement du token (refresh token - chemin /refresh) +* des tests unitaires si ça vous chante +* PHPDoc pour documenter les fonctions de la librairie + +Notez que toutes les contributions sont placées de manière irréversible sous licence TLMSVR +(voir le fichier `LICENCE`). \ No newline at end of file diff --git a/html/auth.php b/html/auth.php new file mode 100644 index 0000000..43637fb --- /dev/null +++ b/html/auth.php @@ -0,0 +1,9 @@ +<?php +require('../lib/init.php'); + +// Perform authentication +if(!$oauth->checkLogin()) + $oauth->forceLogin(); + +// Go back to index or another page if set +header('Location: /'); diff --git a/html/index.php b/html/index.php new file mode 100644 index 0000000..5d185a8 --- /dev/null +++ b/html/index.php @@ -0,0 +1,33 @@ +<?php +// Toutes vos pages utilisant la connexion doivent inclure la librairie de connexion +// oAuth en premier (avant d'afficher quoi que ce soit) + +// Ici, init.php s'occupe de la charger et de l'initialiser avec la configuration +// CONFIG: Consultez init.php pour plus d'infos sur la configuration de la librarie +require('../lib/init.php'); + +// On n'impose la connexion que si le paramètre ?login est défini +if(isset($_GET['login'])) + // Si votre page nécessite une connexion, ne mettez pas la condition ci-dessus + // checkLogin permet de savoir si l'utilisateur est connecté + if(!$oauth->checkLogin()) + // Sur n'importe quelle page, vous pouvez imposer la connexion avec forceLogin() + $oauth->forceLogin(); + +?><!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <title>Bonjour!</title> +</head> +<body> + <h1>Bonjour !</h1> + <?php if(!$oauth->checkLogin()) { ?> + <p>Vous n'avez pas l'air connecté, vous voulez <a href="index.php?login">vous connecter</a> ?</p> + <?php } else { ?> + <p>Bonjour, <?php echo $oauth->getUserData('firstName'); ?> + <?php echo $oauth->getUserData('lastName'); ?>. Vous voulez <a href="logout.php">vous déconnecter</a> ? + </p> + <?php } ?> +</body> +</html> diff --git a/html/logout.php b/html/logout.php new file mode 100644 index 0000000..c11d981 --- /dev/null +++ b/html/logout.php @@ -0,0 +1,10 @@ +<?php +session_start(); +// Pour déconnecter un utilisateur, il suffit de supprimer sa session PHP +session_destroy(); +// Si vous voulez également déconnecter l'utilisateur de l'oAuth ViaRézo, vous pouvez utiliser le chemin /logout +// Le paramètre logout_redirect permet de revenir sur votre site après la déconnexion +require('../lib/config.php'); +header('Location:' . $config_oauth['host'] . '/logout?redirect_logout=' . $config_general['my_url']); + +// Sinon, un simple header('Location: /'); suffit pour ramener l'utlisateur à l'accueil diff --git a/lib/config.template.php b/lib/config.template.php new file mode 100644 index 0000000..ce35cf7 --- /dev/null +++ b/lib/config.template.php @@ -0,0 +1,37 @@ +<?php +// Configuration générale du site +// Commencez par copier ce ficher et renommez la copie en config.php puis modifiez-le + +/*** Configuration générale ***/ +$config_general = array( + 'my_url' => 'http://monsite.test', // CONFIG: Mettez l'adresse de votre site (à changer entre votre machine et le site de production) +); + +/*** oAuth ***/ +$config_oauth = array( + 'clientId' => 'test', // CONFIG: Mettez votre client id à la place de "test" + 'clientSecret' => 'test', // CONFIG: Mettez votre client secret à la place de "test" + 'scope' => 'default', // Si vous voulez lire des données sur LinkCS, vous pouvez utiliser "linkcs:read linkcs:write"... + // Mais assurez-vous que ces scopes sont autorisés pour votre client! + + // Si vous voulez modifier l'adresse à laquelle le serveur oAuth doit renvoyer les utilisateurs connectés, faites-le ici + // Notez que vous devrez également changer les URLs de redirection + 'redirect_uri' => '/auth.php', + + // Normalement, le reste ne bouge pas + 'host' => 'https://auth.viarezo.fr', + 'authURL' => '/oauth/authorize', + 'tokenURL' => '/oauth/token', + 'userDataURL' => '/api/user/show/me', +); + +/*** Base de données ***/ +// Généralement, vous mettrez aussi votre configuration de Base de données dans ce fichier... Mais vous faites comme vous voulez ! +/* +$config_bdd = array( + 'host' => 'localhost', + 'name' => 'mon_site', + 'user' => 'root', + 'password' => 'toor' +);*/ + diff --git a/lib/init.php b/lib/init.php new file mode 100644 index 0000000..2c6a89e --- /dev/null +++ b/lib/init.php @@ -0,0 +1,31 @@ +<?php +// Chargement de la configuration +// CONFIG: Voir ../lib/config.template.php pour plus d'informations sur la configuration du site +require(dirname(__FILE__) . '/config.php'); + +// Chargement de la librairie oAuth +require(dirname(__FILE__) . '/oauth.php'); + +// Initialisation de la librarie +$oauth = new oAuth2( + $config_oauth['host'], + $config_oauth['clientId'], + $config_oauth['clientSecret'], + $config_oauth['scope'], + $config_oauth['authURL'], + $config_oauth['tokenURL'], + $config_oauth['userDataURL'], + $config_general['my_url'], + $config_oauth['redirect_url'] +); + +/* Si vous avez une base de données, c'est le moment de l'initialiser! +$bdd_dsn = 'mysql:dbname=' . $config_bdd['name'] . ';charset=utf8;host=' . $config_bdd['host']; +$bdd_user = $config_bdd['user']; +$bdd_password = $config_bdd['password']; +try { + $bdd = new PDO($bdd_dsn, $bdd_user, $bdd_password); +} catch (PDOException $e) { + die('Erreur de connexion à la BDD: ' . $e->getMessage()); +} +*/ \ No newline at end of file diff --git a/lib/oauth.php b/lib/oauth.php new file mode 100644 index 0000000..1d99797 --- /dev/null +++ b/lib/oauth.php @@ -0,0 +1,148 @@ +<?php +/* oAuth2 lib + * Copyright (c) 2018 Thomas Pathier + * Licence TLMSVR + * Une copie de la licence TLMSVR doit être inclue avec ce fichier. + * Si vous n'avez pas reçu de copie de la licence TLMSVR, consultez + * https://gitlab.viarezo.fr/2016pathiert/libphpoauth + */ + +// Start the PHP Session if needed (used for persistency) +if(session_status() !== PHP_SESSION_ACTIVE) + session_start(); + +class oAuth2{ + private $userData = false; + private $config = array(); + + function __construct($host, $clientId, $clientSecret, $scope, $authURL, $tokenURL, $userDataURL, $myURL, $redirectURL){ + $this->config['host'] = $host; + $this->config['clientId'] = $clientId; + $this->config['clientSecret'] = $clientSecret; + $this->config['scope'] = $scope; + $this->config['authURL'] = $authURL; + $this->config['tokenURL'] = $tokenURL; + $this->config['userDataURL'] = $userDataURL; + $this->config['myURL'] = $myURL; + $this->config['redirectURL'] = $redirectURL; + } + + function checkLogin(){ + if(!isset($_SESSION['oauth_userData'], $_SESSION['oauth_access_token'], $_SESSION['oauth_expiration'], $_SESSION['oauth_refresh_token'], $_SESSION['oauth_user_roles'])) + return false; + + // Make sure the token has not expired yet + if($_SESSION['oauth_expiration'] < time()) + return false; + + $this->userData = $_SESSION['oauth_userData']; + // OK, the user is logged in ! + return true; + } + + function forceLogin(){ + /* Make sure the user is logged in. + * If this function returns, then the user is connected and you can call + * oAuth2::get('<something>') to get the value of the JWT + */ + if(substr($_SERVER['REQUEST_URI'], 0, strlen($this->config['redirectURL'])) == $this->config['redirectURL'] && !isset($_GET['perform'])){ + // This is an oauth callback + // Check the state + if(!isset($_GET['state']) || $_GET['state'] !== $_SESSION['oauth_state']) + exit('Invalid state'); + // Fetch the oauth token + if(!isset($_GET['code'])) + exit('Missing authorization code'); + try{ + $options = array( + 'http' => array( + 'header' => 'Content-type: application/x-www-form-urlencoded', + 'method' => 'POST', + 'content' => http_build_query(array( + 'grant_type' => 'authorization_code', + 'code' => $_GET['code'], + 'redirect_uri' => $this->config['myURL'] . $this->config['redirectURL'], + 'client_id' => $this->config['clientId'], + 'client_secret' => $this->config['clientSecret'] + )) + ) + ); + + // Make the POST request to the oAuth Server... + $context = stream_context_create($options); + + $result = file_get_contents($this->config['host'] . $this->config['tokenURL'], false, $context); + + if(!$result) + die('You are not allowed to use this service. If you think you should, contact ViaRézo.'); + + // Parse the response + $result = json_decode($result, true); + // Save the refresh token + $_SESSION['oauth_refresh_token'] = $result['refresh_token']; + // ... And the expiration date + $_SESSION['oauth_expiration'] = $result['expires_at']; + + // Extract the access token + $result = $result['access_token']; + $_SESSION['oauth_access_token'] = $result; + + // Extract the roles of the token + $result = explode('.', $result)[1]; + $result = base64_decode($result); + $result = json_decode($result, true); + $_SESSION['oauth_user_roles'] = explode(' ', $result['user']['roles']); + + // Fetch some data about the user ! + $options = array( + 'http' => array( + 'header' => 'Authorization: Bearer ' . $_SESSION['oauth_access_token'] + ) + ); + + $context = stream_context_create($options); + + $result = file_get_contents($this->config['host'] . $this->config['userDataURL'], false, $context); + + $_SESSION['oauth_userData'] = json_decode($result, true); + + // Parse the data we got, make sure it is correct + if(!$this->checkLogin()) + throw new Exception('Invalid token'); + } catch(Exception $e){ + exit('Internal error'); + } + + // OK, the user logged in ! + header('Location: ' . (isset($_SESSION['oauth_redir_url'])?$_SESSION['oauth_redir_url']:'/')); + unset($_SESSION['oauth_redir_url']); + exit; + } + + // Else, initialize the oauth flow + $_SESSION['oauth_redir_url'] = $_SERVER['REQUEST_URI']; + $state = sha1(rand() . time() . rand()); + $_SESSION['oauth_state'] = $state; + header('Location: ' . $this->config['host'] . $this->config['authURL'] . + '?redirect_uri=' . urlencode($this->config['myURL'] . $this->config['redirectURL']) . + '&client_id=' . $this->config['clientId'] . + '&response_type=code' . + '&state=' . $state . + '&scope=' . $this->config['scope']); // FIXME: Ajouter des scopes ? + // Stop the script right here + exit; + } + + function getUserData($value){ + if(isset($this->userData[$value])) + return $this->userData[$value]; + return false; + } + + function hasRole($role){ + if(!isset($_SESSION['oauth_user_roles'])) + return false; + + return in_array($role, $_SESSION['oauth_user_roles']); + } +} -- GitLab