From 9762c6ef74ee1790007f662576443af3a295c2f7 Mon Sep 17 00:00:00 2001 From: Sonia Zorba <zorba@oats.inaf.it> Date: Thu, 15 Jun 2017 12:54:11 +0200 Subject: [PATCH] Added user persistence to database and REST endpoint for retrieving user by id --- .gitignore | 2 +- classes/DAO.php | 77 +++++++--------- classes/Identity.php | 108 +++++++++++++++++++++++ classes/MySQLDAO.php | 186 +++++++++++++++++++++++++++++++++++++++ classes/SessionData.php | 44 +++++++++ classes/TokenHandler.php | 42 +++++++++ classes/User.php | 46 ++++++++++ classes/UserHandler.php | 55 ++++++++++++ config.ini | 8 ++ img/eduGain-200.png | Bin 0 -> 64295 bytes img/facebook-60.png | Bin 0 -> 14598 bytes img/google-60.png | Bin 0 -> 14639 bytes img/linkedin-60.png | Bin 0 -> 17288 bytes img/x509-200.png | Bin 0 -> 64295 bytes include/footer.php | 3 + include/header.php | 12 +++ include/init.php | 3 + index.php | 37 ++++++-- oauth2/google_token.php | 49 +++++++---- setup-database.sql | 14 --- sql/setup-database.sql | 43 +++++++++ views/index.php | 16 ++++ 22 files changed, 661 insertions(+), 84 deletions(-) create mode 100644 classes/Identity.php create mode 100644 classes/MySQLDAO.php create mode 100644 classes/SessionData.php create mode 100644 classes/TokenHandler.php create mode 100644 classes/User.php create mode 100644 classes/UserHandler.php create mode 100644 config.ini create mode 100755 img/eduGain-200.png create mode 100755 img/facebook-60.png create mode 100755 img/google-60.png create mode 100755 img/linkedin-60.png create mode 100755 img/x509-200.png create mode 100644 include/footer.php create mode 100644 include/header.php delete mode 100644 setup-database.sql create mode 100644 sql/setup-database.sql create mode 100644 views/index.php diff --git a/.gitignore b/.gitignore index ca46cde..5287a34 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ vendor composer.lock nbproject - +logs diff --git a/classes/DAO.php b/classes/DAO.php index 7225519..ccbabd8 100644 --- a/classes/DAO.php +++ b/classes/DAO.php @@ -24,61 +24,52 @@ namespace RAP; -use PDO; +abstract class DAO { -class DAO { + public abstract function getDBHandler(); - private static function getDBHandler() { - global $PDO; - return new PDO($PDO['connection_string'], $PDO['user'], $PDO['password']); - } - - public static function insertLogin($user) { - - global $log; - - $token = bin2hex(openssl_random_pseudo_bytes(16)); // http://stackoverflow.com/a/18890309/771431 - - $dbh = DAO::getDBHandler(); - - $stmt = $dbh->prepare("INSERT INTO token (token, data) VALUES(:token, :data)"); + public abstract function insertTokenData($token, $data); - $params = array( - ':token' => $token, - ':data' => json_encode($user) - ); + public abstract function findTokenData($token); - if ($stmt->execute($params)) { - return $token; - } else { - $log->error($stmt->errorInfo()[2]); - throw new \Exception("SQL error while storing user token"); - } - } + public abstract function deleteToken($token); - public static function getTokenData($token) { + /** + * Return the new identity ID. + */ + public abstract function insertIdentity(Identity $identity, $userId); - $dbh = DAO::getDBHandler(); + /** + * Return the new user ID. + */ + public abstract function createUser(); - $stmt = $dbh->prepare("SELECT data FROM token WHERE token = :token AND CURRENT_TIMESTAMP < TIMESTAMPADD(MINUTE,1,creation_time)"); - $stmt->bindParam(':token', $token); + public abstract function findUserById($userId); + + /** + * Return a User object, null if nothing was found. + * @param type $type Identity type (EDU_GAIN, X509, GOOGLE, ...) + * @param type $identifier value used to search the identity in the database + * @param type $dbIdentifier identifier of the database (used only for local identities) + */ + public abstract function findUserByIdentity($type, $identifier, $dbIdentifier); - $stmt->execute(); + public abstract function addEmailToUser($email, $userId); - foreach ($stmt->fetchAll() as $row) { - return $row['data']; - } + public $config; - return null; + public function __construct($config) { + $this->config = $config; } - public static function deleteToken($token) { - - $dbh = DAO::getDBHandler(); - - $stmt = $dbh->prepare("DELETE FROM token WHERE token = :token"); - $stmt->bindParam(':token', $token); - $stmt->execute(); + public static function get() { + $config = parse_ini_file(ROOT . '/config.ini', true); + switch ($config['dbtype']) { + case 'MySQL': + return new MySQLDAO($config); + default: + throw new \Exception($config['dbtype'] . ' not supported yet'); + } } } diff --git a/classes/Identity.php b/classes/Identity.php new file mode 100644 index 0000000..e792c17 --- /dev/null +++ b/classes/Identity.php @@ -0,0 +1,108 @@ +<?php + +/* ---------------------------------------------------------------------------- + * INAF - National Institute for Astrophysics + * IRA - Radioastronomical Institute - Bologna + * OATS - Astronomical Observatory - Trieste + * ---------------------------------------------------------------------------- + * + * Copyright (C) 2016 Istituto Nazionale di Astrofisica + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License Version 3 as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace RAP; + +class Identity { + + const EDU_GAIN = "eduGAIN"; + const X509 = "X.509"; + const GOOGLE = "Google"; + const FACEBOOK = "Facebook"; + const LINKEDIN = "LinkedIn"; + const LOCAL = "Local"; + + private static $ALLOWED_TYPES = [Identity::EDU_GAIN, Identity::X509, Identity::GOOGLE, Identity::FACEBOOK, Identity::LINKEDIN, Identity::LOCAL]; + + /** + * Identity id in the database. Mandatory field. + */ + public $id; + + /** + * One of the types specified above. Mandatory field. + */ + private $type; + + /** + * Data related to specific account type (shibboleth persistent id, facebook id, etc, ...). Mandatory field. + */ + public $typedId; + + /** + * Primary email related to this identity. Mandatory field. + * User can have additional email addresses. These are stored into User class. + */ + public $email; + + /** + * Unique identifier for local user database (specified in configuration file). + */ + public $localDBId; + + /** + * First name + */ + public $name; + + /** + * Last name / Family name + */ + public $surname; + + /** + * Institution / Organization. Not mandatory. + */ + public $institution; + + /** + * For local identities. + */ + public $username; + + /** + * For eduGAIN identities. + */ + public $eppn; + + public function __construct($userType) { + $isAllowedType = false; + foreach (Identity::$ALLOWED_TYPES as $type) { + if ($userType === $type) { + $isAllowedType = true; + break; + } + } + if (!$isAllowedType) { + throw new \Exception($userType . " is not a supported user type"); + } + + $this->type = $userType; + } + + public function getType() { + return $this->type; + } + +} diff --git a/classes/MySQLDAO.php b/classes/MySQLDAO.php new file mode 100644 index 0000000..e484c92 --- /dev/null +++ b/classes/MySQLDAO.php @@ -0,0 +1,186 @@ +<?php + +/* ---------------------------------------------------------------------------- + * INAF - National Institute for Astrophysics + * IRA - Radioastronomical Institute - Bologna + * OATS - Astronomical Observatory - Trieste + * ---------------------------------------------------------------------------- + * + * Copyright (C) 2016 Istituto Nazionale di Astrofisica + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License Version 3 as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace RAP; + +use PDO; + +class MySQLDAO extends DAO { + + public function getDBHandler() { + $connectionString = "mysql:host=" . $this->config['hostname'] . ";dbname=" . $this->config['dbname']; + return new PDO($connectionString, $this->config['username'], $this->config['password']); + } + + public function insertTokenData($token, $data) { + + global $log; + + $dbh = $this->getDBHandler(); + $stmt = $dbh->prepare("INSERT INTO token (token, data) VALUES(:token, :data)"); + + $params = array( + ':token' => $token, + ':data' => $data + ); + + if ($stmt->execute($params)) { + return $token; + } else { + $log->error($stmt->errorInfo()[2]); + throw new \Exception("SQL error while storing user token"); + } + } + + public function findTokenData($token) { + + $dbh = $this->getDBHandler(); + + $stmt = $dbh->prepare("SELECT data FROM token WHERE token = :token AND CURRENT_TIMESTAMP < TIMESTAMPADD(MINUTE,1,creation_time)"); + $stmt->bindParam(':token', $token); + + $stmt->execute(); + + foreach ($stmt->fetchAll() as $row) { + return $row['data']; + } + + return null; + } + + public function deleteToken($token) { + + $dbh = $this->getDBHandler(); + + $stmt = $dbh->prepare("DELETE FROM token WHERE token = :token"); + $stmt->bindParam(':token', $token); + $stmt->execute(); + } + + public function insertIdentity(Identity $identity, $userId) { + + $dbh = $this->getDBHandler(); + + $stmt = $dbh->prepare("INSERT INTO identity(`user_id`, `type`, `email`, `name`, `surname`, `institution`, `username`, `local_db_id`, `typed_id`, `eppn`)" + . " VALUES(:user_id, :type, :email, :name, :surname, :institution, :username, :local_db_id, :typed_id, :eppn)"); + + $stmt->bindParam(':user_id', $userId); + $stmt->bindParam(':type', $identity->getType()); + $stmt->bindParam(':email', $identity->email); + $stmt->bindParam(':name', $identity->name); + $stmt->bindParam(':surname', $identity->surname); + $stmt->bindParam(':institution', $identity->institution); + $stmt->bindParam(':username', $identity->username); + $stmt->bindParam(':local_db_id', $identity->localDBId); + $stmt->bindParam(':typed_id', $identity->typedId); + $stmt->bindParam(':eppn', $identity->eppn); + + $stmt->execute(); + + return $dbh->lastInsertId(); + } + + public function createUser() { + + $dbh = $this->getDBHandler(); + + $stmt = $dbh->prepare("INSERT INTO user() VALUES()"); + $stmt->execute(); + + return $dbh->lastInsertId(); + } + + public function findUserById($userId) { + + $dbh = $this->getDBHandler(); + + $stmt = $dbh->prepare("SELECT `id`, `type`, `typed_id`, `email`, `local_db_id`, `name`, `surname`, `institution`, `username`, `eppn`" + . " FROM identity WHERE user_id = :user_id"); + + $stmt->bindParam(':user_id', $userId); + $stmt->execute(); + + $user = new User(); + $user->id = $userId; + + foreach ($stmt->fetchAll() as $row) { + $identity = new Identity($row['type']); + $identity->id = $row['id']; + $identity->typedId = $row['typed_id']; + $identity->email = $row['email']; + $identity->localDBId = $row['local_db_id']; + $identity->name = $row['name']; + $identity->surname = $row['surname']; + $identity->institution = $row['institution']; + $identity->username = $row['username']; + $identity->eppn = $row['eppn']; + $user->addIdentity($identity); + } + + $stmtMail = $dbh->prepare("SELECT `email` FROM `additional_email` WHERE `user_id` = :user_id"); + $stmtMail->bindParam(':user_id', $userId); + $stmtMail->execute(); + foreach ($stmtMail->fetchAll() as $row) { + $user->addAdditionalEmail($row['email']); + } + + return $user; + } + + public function findUserByIdentity($type, $identifier, $dbIdentifier) { + + $dbh = $this->getDBHandler(); + + $stmt = $dbh->prepare("SELECT user_id FROM identity WHERE type = :type AND typed_id = :typed_id AND local_db_id = :local_db_id"); + $stmt->bindParam(':type', $type); + $stmt->bindParam(':typed_id', $identifier); + $stmt->bindParam(':local_db_id', $dbIdentifier); + + $stmt->execute(); + + $result = $stmt->fetchAll(); + + if (count($result) === 0) { + return null; + } + if (count($result) > 1) { + throw new Exception("Found multiple users associated to the same identity!"); + } + + $userId = $result[0]['user_id']; + return $this->findUserById($userId); + } + + public function addEmailToUser($email, $userId) { + + $dbh = $this->getDBHandler(); + + $stmt = $dbh->prepare("INSERT INTO `additional_email`(`user_id`, `email`) VALUES(:user_id, :email)"); + $stmt->bindParam(':user_id', $userId); + $stmt->bindParam(':email', $email); + + $stmt->execute(); + } + +} diff --git a/classes/SessionData.php b/classes/SessionData.php new file mode 100644 index 0000000..c6d8077 --- /dev/null +++ b/classes/SessionData.php @@ -0,0 +1,44 @@ +<?php + +/* ---------------------------------------------------------------------------- + * INAF - National Institute for Astrophysics + * IRA - Radioastronomical Institute - Bologna + * OATS - Astronomical Observatory - Trieste + * ---------------------------------------------------------------------------- + * + * Copyright (C) 2016 Istituto Nazionale di Astrofisica + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License Version 3 as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace RAP; + +class SessionData { + + public $callback; + public $user; + + public function save() { + $_SESSION['SessionData'] = $this; + } + + public static function get() { + + if (!isset($_SESSION['SessionData'])) { + $session = new SessionData(); + $session->save(); + } + return $_SESSION['SessionData']; + } +} diff --git a/classes/TokenHandler.php b/classes/TokenHandler.php new file mode 100644 index 0000000..c096a01 --- /dev/null +++ b/classes/TokenHandler.php @@ -0,0 +1,42 @@ +<?php + +/* ---------------------------------------------------------------------------- + * INAF - National Institute for Astrophysics + * IRA - Radioastronomical Institute - Bologna + * OATS - Astronomical Observatory - Trieste + * ---------------------------------------------------------------------------- + * + * Copyright (C) 2016 Istituto Nazionale di Astrofisica + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License Version 3 as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace RAP; + +class TokenHandler { + + public static function createNewToken($data) { + $token = bin2hex(openssl_random_pseudo_bytes(16)); // http://stackoverflow.com/a/18890309/771431 + DAO::get()->insertTokenData($token, $data); + } + + public static function deleteToken($token) { + DAO::get()->deleteToken($token); + } + + public static function getUserData($token) { + return DAO::get()->findTokenData($token); + } + +} diff --git a/classes/User.php b/classes/User.php new file mode 100644 index 0000000..220c6f0 --- /dev/null +++ b/classes/User.php @@ -0,0 +1,46 @@ +<?php + +/* ---------------------------------------------------------------------------- + * INAF - National Institute for Astrophysics + * IRA - Radioastronomical Institute - Bologna + * OATS - Astronomical Observatory - Trieste + * ---------------------------------------------------------------------------- + * + * Copyright (C) 2016 Istituto Nazionale di Astrofisica + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License Version 3 as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace RAP; + +class User { + + public $id; + public $identities; + public $additionalEmailAddresses; + + public function __construct() { + $this->identities = []; + $this->additionalEmailAddresses = []; + } + + public function addIdentity(Identity $identity) { + array_push($this->identities, $identity); + } + + public function addAdditionalEmail($email) { + array_push($this->additionalEmailAddresses, $email); + } + +} diff --git a/classes/UserHandler.php b/classes/UserHandler.php new file mode 100644 index 0000000..bb3bb3e --- /dev/null +++ b/classes/UserHandler.php @@ -0,0 +1,55 @@ +<?php + +/* ---------------------------------------------------------------------------- + * INAF - National Institute for Astrophysics + * IRA - Radioastronomical Institute - Bologna + * OATS - Astronomical Observatory - Trieste + * ---------------------------------------------------------------------------- + * + * Copyright (C) 2016 Istituto Nazionale di Astrofisica + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License Version 3 as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace RAP; + +class UserHandler { + + public static function saveUser(User $user) { + + $dao = DAO::get(); + + if ($user->id === null) { + $user->id = $dao->createUser(); + } + + foreach ($user->identities as $identity) { + if ($identity->id === null) { + $identity->id = $dao->insertIdentity($identity, $user->id); + } + } + + foreach ($user->additionalEmailAddresses as $email) { + if (!in_array($email, $user->additionalEmailAddresses)) { + + } + } + } + + public static function findUserByIdentity($type, $identifier, $dbIdentifier) { + + return DAO::get()->findUserByIdentity($type, $identifier, $dbIdentifier); + } + +} diff --git a/config.ini b/config.ini new file mode 100644 index 0000000..2065065 --- /dev/null +++ b/config.ini @@ -0,0 +1,8 @@ + +;connection_string = mysql:host=localhost;dbname=rap" +dbtype = MySQL +hostname = localhost +port = 3306 +username = rap +password = ***REMOVED*** +dbname = rap diff --git a/img/eduGain-200.png b/img/eduGain-200.png new file mode 100755 index 0000000000000000000000000000000000000000..5235aa8fcd435d54d4e4bec1374f6aa151a0c3d4 GIT binary patch literal 64295 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 YkmP;9?T)kQ9gq_}UHx3vIVCg!0IyQ{BLDyZ literal 0 HcmV?d00001 diff --git a/img/facebook-60.png b/img/facebook-60.png new file mode 100755 index 0000000000000000000000000000000000000000..a1badec4e1c2c5186f25c51590890bfa96cb183b GIT binary patch literal 14598 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 VObE0spQQ|PsHdx+%Q~loCIBBFve^It literal 0 HcmV?d00001 diff --git a/img/google-60.png b/img/google-60.png new file mode 100755 index 0000000000000000000000000000000000000000..fe63b937d17bc06b18ee1a3f401231dfd9989d32 GIT binary patch literal 14639 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 J);T3K0RUNC;I9Ax literal 0 HcmV?d00001 diff --git a/img/linkedin-60.png b/img/linkedin-60.png new file mode 100755 index 0000000000000000000000000000000000000000..0d8772a6e85a1e0d6c5eb314a1a07b0dc0283740 GIT binary patch literal 17288 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 U?<;>kI|bx^Pgg&ebxsLQ01!O(2><{9 literal 0 HcmV?d00001 diff --git a/img/x509-200.png b/img/x509-200.png new file mode 100755 index 0000000000000000000000000000000000000000..e1921f8ec165cb5cf527ef374e6f372466f69a2e GIT binary patch literal 64295 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 literal 0 HcmV?d00001 diff --git a/include/footer.php b/include/footer.php new file mode 100644 index 0000000..9943ff0 --- /dev/null +++ b/include/footer.php @@ -0,0 +1,3 @@ +</div> +</body> +</html> diff --git a/include/header.php b/include/header.php new file mode 100644 index 0000000..92c810d --- /dev/null +++ b/include/header.php @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html> + <head> + <title><?php echo $title; ?></title> + <!-- jQuery and Bootstrap --> + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous" /> + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous" /> + <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> + <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> + </head> + <body> + <div class="container"> diff --git a/include/init.php b/include/init.php index 1bfefed..e6124cf 100644 --- a/include/init.php +++ b/include/init.php @@ -39,3 +39,6 @@ include ROOT . '/config.php'; $log = new Monolog\Logger('mainLogger'); $log->pushHandler(new Monolog\Handler\StreamHandler($LOG_PATH, $LOG_LEVEL)); + +session_start(); +$session = RAP\SessionData::get(); diff --git a/index.php b/index.php index f152121..8241bc3 100644 --- a/index.php +++ b/index.php @@ -27,18 +27,28 @@ include './include/init.php'; /** * REST Web Service using http://flightphp.com/ */ -Flight::route('/', function() { +Flight::route('/demo', function() { $callback = (isset($_SERVER['HTTPS']) ? "https" : "http") . '://' . $_SERVER['HTTP_HOST'] . "/rap-service/user-info"; Flight::render('demo.php', array('callback' => $callback)); }); -Flight::route('POST /google', function() { +Flight::route('/', function() { + global $session; + Flight::render('index.php', array('title' => 'RAP', 'session' => $session)); +}); + +Flight::route('GET /logout', function() { + session_destroy(); + Flight::redirect('/'); +}); + +Flight::route('/google', function() { + global $session; + $callback = Flight::request()->data['callback']; - if (!isset($callback)) { - throw new Exception("Callback URL not set!"); - } - session_start(); - $_SESSION['rap_callback'] = $callback; + $session->callback = isset($callback) ? $callback : null; + $session->save(); + Flight::redirect('/oauth2/google_token.php'); }); @@ -68,4 +78,17 @@ Flight::route('GET /user-info', function() { echo $userData; }); +Flight::route('GET /user/@userId', function($userId) { + + $user = RAP\DAO::get()->findUserById($userId); + if ($user !== null) { + global $log; + $log->debug(count($user->identities)); + echo json_encode($user); + } else { + http_response_code(404); + die("User not found"); + } +}); + Flight::start(); diff --git a/oauth2/google_token.php b/oauth2/google_token.php index b0d5800..553bbb0 100644 --- a/oauth2/google_token.php +++ b/oauth2/google_token.php @@ -24,14 +24,6 @@ include '../include/init.php'; -session_start(); - -$callback = $_SESSION['rap_callback']; -if (!isset($callback)) { - http_response_code(422); - die("Callback URL not set!"); -} - $client = new Google_Client(array( 'client_id' => $Google['id'], 'client_secret' => $Google['secret'], @@ -77,17 +69,36 @@ if ($client->getAccessToken()) { array_push($emailAddresses, $addr->value); } - // Creating user object - $user = array( - "type" => "Google", - "name" => $name, - "surname" => $surname, - "emailAddresses" => $emailAddresses, - "typed_id" => explode('/', $res->getResourceName())[1] - ); - - $token = RAP\DAO::insertLogin($user); - header('Location: ' . $callback . '?token=' . $token); + $typedId = explode('/', $res->getResourceName())[1]; + + $user = RAP\UserHandler::findUserByIdentity(RAP\Identity::GOOGLE, $typedId, null); + + if ($user === null) { + $user = new RAP\User(); + + $identity = new RAP\Identity(RAP\Identity::GOOGLE); + $identity->email = $emailAddresses[0]; + $identity->name = $name; + $identity->surname = $surname; + $identity->typedId = $typedId; + + $user->addIdentity($identity); + + RAP\UserHandler::saveUser($user); + } + + if (isset($session->callback) && $session->callback !== null) { + // External login using token + $token = RAP\TokenHandler::createNewToken($user->id); + header('Location: ' . $session->callback . '?token=' . $token); + } else { + // Login in session + $session->user = $user; + $session->save(); + // Return to index + header('Location: ' . $BASE_PATH); + } + die(); } else { // Redirect to Google authorization URL for obtaining an access token diff --git a/setup-database.sql b/setup-database.sql deleted file mode 100644 index 9630ab4..0000000 --- a/setup-database.sql +++ /dev/null @@ -1,14 +0,0 @@ -CREATE TABLE `token` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `token` varchar(255) DEFAULT NULL, - `data` text, - `creation_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -CREATE EVENT tokens_cleanup - ON SCHEDULE - EVERY 1 MINUTE - COMMENT 'Remove expired tokens' - DO - DELETE FROM token WHERE CURRENT_TIMESTAMP > TIMESTAMPADD(MINUTE,1,creation_time); diff --git a/sql/setup-database.sql b/sql/setup-database.sql new file mode 100644 index 0000000..5ea15e1 --- /dev/null +++ b/sql/setup-database.sql @@ -0,0 +1,43 @@ +CREATE TABLE `user` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `identity` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `user_id` bigint(20) NOT NULL, + `type` varchar(50) NOT NULL, + `typed_id` varchar(255) NOT NULL, + `email` varchar(255) NOT NULL, + `name` varchar(255) DEFAULT NULL, + `surname` varchar(255) DEFAULT NULL, + `institution` varchar(255) DEFAULT NULL, + `username` varchar(255) DEFAULT NULL, + `local_db_id` varchar(255) DEFAULT NULL, + `eppn` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `additional_email` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `user_id` bigint(20) NOT NULL, + `email` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `token` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `token` varchar(255) NOT NULL, + `data` text, + `creation_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE EVENT tokens_cleanup + ON SCHEDULE + EVERY 1 MINUTE + COMMENT 'Remove expired tokens' + DO + DELETE FROM token WHERE CURRENT_TIMESTAMP > TIMESTAMPADD(MINUTE,1,creation_time); diff --git a/views/index.php b/views/index.php new file mode 100644 index 0000000..52a446c --- /dev/null +++ b/views/index.php @@ -0,0 +1,16 @@ +<?php +include 'include/header.php'; +?> + +<?php if ($session->user === null) { ?> + <a href="google"> + <img src="img/google-60.png" alt="Google Logo" /> + </a> +<?php } else { ?> + <?php echo json_encode($session->user); ?> + <a href="logout">Logout</a> +<?php } ?> + +<?php +include 'include/footer.php'; + -- GitLab