From 658238a232d9ab55a04527b3d09f38936de6635d Mon Sep 17 00:00:00 2001 From: Sonia Zorba Date: Tue, 27 Jun 2017 13:03:25 +0200 Subject: [PATCH] removed globals from classes, added AJAX waiting animations, removed additional email address, little fixes --- auth/oauth2/facebook_token.php | 6 +-- auth/oauth2/google_token.php | 6 +-- auth/saml2/aai.php | 7 +-- auth/x509/certlogin.php | 8 ++-- classes/CallbackHandler.php | 22 +++++---- classes/DAO.php | 46 +++++++----------- classes/GrouperClient.php | 8 ++-- classes/Identity.php | 7 +++ classes/MySQLDAO.php | 68 +++++++++++++-------------- classes/SessionData.php | 21 +++++++-- classes/User.php | 14 +++--- classes/UserHandler.php | 42 ++++++++++------- classes/X509Data.php | 7 +-- css/animation.css | 85 ++++++++++++++++++++++++++++++++++ css/style.css | 34 ++++++++++++++ include/footer.php | 5 ++ include/front-controller.php | 14 +++--- include/gui-backend.php | 20 +++++++- include/header.php | 2 + include/init.php | 15 +++++- include/rest-web-service.php | 14 ++++-- include/user-data.php | 19 ++++++-- js/index.js | 16 +++++++ js/script.js | 14 ++++++ sql/setup-database.sql | 11 ++--- views/confirm-join.php | 2 +- views/index.php | 2 +- 27 files changed, 360 insertions(+), 155 deletions(-) create mode 100644 css/animation.css create mode 100644 js/script.js diff --git a/auth/oauth2/facebook_token.php b/auth/oauth2/facebook_token.php index 1daca05..b914131 100755 --- a/auth/oauth2/facebook_token.php +++ b/auth/oauth2/facebook_token.php @@ -80,7 +80,7 @@ $fbUser = $response->getGraphUser(); $typedId = $fbUser["id"]; -$user = RAP\UserHandler::findUserByIdentity(RAP\Identity::FACEBOOK, $typedId); +$user = $userHandler->findUserByIdentity(RAP\Identity::FACEBOOK, $typedId); if ($user === null) { $user = new RAP\User(); @@ -93,8 +93,8 @@ if ($user === null) { $user->addIdentity($identity); - RAP\UserHandler::saveUser($user); + $userHandler->saveUser($user); } -RAP\CallbackHandler::manageLoginRedirect($user); +$callbackHandler->manageLoginRedirect($user, $session); ?> diff --git a/auth/oauth2/google_token.php b/auth/oauth2/google_token.php index 59e9c01..a44c19a 100644 --- a/auth/oauth2/google_token.php +++ b/auth/oauth2/google_token.php @@ -74,7 +74,7 @@ if ($client->getAccessToken()) { $typedId = explode('/', $res->getResourceName())[1]; - $user = RAP\UserHandler::findUserByIdentity(RAP\Identity::GOOGLE, $typedId); + $user = $userHandler->findUserByIdentity(RAP\Identity::GOOGLE, $typedId); if ($user === null) { $user = new RAP\User(); @@ -87,10 +87,10 @@ if ($client->getAccessToken()) { $user->addIdentity($identity); - RAP\UserHandler::saveUser($user); + $userHandler->saveUser($user); } - RAP\CallbackHandler::manageLoginRedirect($user); + $callbackHandler->manageLoginRedirect($user, $session); die(); } else { diff --git a/auth/saml2/aai.php b/auth/saml2/aai.php index 02a728d..cb93949 100644 --- a/auth/saml2/aai.php +++ b/auth/saml2/aai.php @@ -29,7 +29,7 @@ if (isset($_SERVER['Shib-Session-ID'])) { $eppn = $_SERVER['eppn']; - $user = RAP\UserHandler::findUserByIdentity(RAP\Identity::EDU_GAIN, $eppn); + $user = $userHandler->findUserByIdentity(RAP\Identity::EDU_GAIN, $eppn); if ($user === null) { $user = new RAP\User(); @@ -39,14 +39,15 @@ if (isset($_SERVER['Shib-Session-ID'])) { $identity->name = $_SERVER['givenName']; $identity->surname = $_SERVER['sn']; $identity->typedId = $eppn; + $identity->eppn = $eppn; //$_SERVER['Shib-Identity-Provider'] $user->addIdentity($identity); - RAP\UserHandler::saveUser($user); + $userHandler->saveUser($user); } - RAP\CallbackHandler::manageLoginRedirect($user); + $callbackHandler->manageLoginRedirect($user, $session); } else { http_response_code(500); die("Shib-Session-ID not found!"); diff --git a/auth/x509/certlogin.php b/auth/x509/certlogin.php index e2319ea..e0ce9ea 100644 --- a/auth/x509/certlogin.php +++ b/auth/x509/certlogin.php @@ -28,7 +28,7 @@ startSession(); function saveUserFromX509Data($x509Data) { - global $session; + global $session, $userHandler; $user = new RAP\User(); @@ -41,7 +41,7 @@ function saveUserFromX509Data($x509Data) { $user->addIdentity($identity); - RAP\UserHandler::saveUser($user); + $userHandler->saveUser($user); $session->x509DataToRegister = null; @@ -58,7 +58,7 @@ if ($session->x509DataToRegister !== null && $session->x509DataToRegister->name $x509Data = RAP\X509Data::parse($_SERVER); - $user = RAP\UserHandler::findUserByIdentity(RAP\Identity::X509, $x509Data->serialNumber); + $user = $userHandler->findUserByIdentity(RAP\Identity::X509, $x509Data->serialNumber); if ($user === null) { @@ -77,4 +77,4 @@ if ($session->x509DataToRegister !== null && $session->x509DataToRegister->name } } -RAP\CallbackHandler::manageLoginRedirect($user); +$callbackHandler->manageLoginRedirect($user, $session); diff --git a/classes/CallbackHandler.php b/classes/CallbackHandler.php index 85d33e9..a569844 100644 --- a/classes/CallbackHandler.php +++ b/classes/CallbackHandler.php @@ -26,6 +26,16 @@ namespace RAP; class CallbackHandler { + private $dao; + private $basePath; + private $callbacks; + + public function __construct(DAO $dao, $basePath, $callbacks) { + $this->dao = $dao; + $this->basePath = $basePath; + $this->callbacks = $callbacks; + } + /** * returns null if the callback URL is not listed in configuration file. */ @@ -35,9 +45,7 @@ class CallbackHandler { return "Account Management"; } - global $CALLBACKS; - - foreach ($CALLBACKS as $callback) { + foreach ($this->callbacks as $callback) { if ($callback['url'] === $callbackURL) { return $callback['title']; } @@ -46,21 +54,19 @@ class CallbackHandler { throw new \Exception("Unauthorized callback URL"); } - public static function manageLoginRedirect($user) { - - global $BASE_PATH, $session, $log; + public function manageLoginRedirect($user, SessionData $session) { if ($session->getCallbackURL() !== null) { // External login using token $token = Util::createNewToken(); - DAO::get()->createLoginToken($token, $user->id); + $this->dao->createLoginToken($token, $user->id); header('Location: ' . $session->getCallbackURL() . '?token=' . $token); } else { // Login in session $session->user = $user; $session->save(); // Return to index - header('Location: ' . $BASE_PATH); + header('Location: ' . $this->basePath); } } diff --git a/classes/DAO.php b/classes/DAO.php index 888f66c..9a28d92 100644 --- a/classes/DAO.php +++ b/classes/DAO.php @@ -24,58 +24,46 @@ namespace RAP; -abstract class DAO { +interface DAO { - public abstract function getDBHandler(); + function getDBHandler(); - public abstract function createLoginToken($token, $userId); + function createLoginToken($token, $userId); - public abstract function findLoginToken($token); + function findLoginToken($token); - public abstract function deleteLoginToken($token); + function deleteLoginToken($token); /** * Return the new identity ID. */ - public abstract function insertIdentity(Identity $identity, $userId); + function insertIdentity(Identity $identity, $userId); /** * Return the new user ID. */ - public abstract function createUser(); + function createUser(); - public abstract function findUserById($userId); + function findUserById($userId); + + function setPrimaryIdentity($userId, $identityId); /** * 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 */ - public abstract function findUserByIdentity($type, $identifier); - - public abstract function searchUser($searchText); - - public abstract function addEmailToUser($email, $userId); - - public abstract function createJoinRequest($token, $applicantUserId, $targetUserId); + function findUserByIdentity($type, $identifier); - public abstract function findJoinRequest($token); + function searchUser($searchText); - public abstract function deleteUser($userId); + function createJoinRequest($token, $applicantUserId, $targetUserId); - public abstract function joinUsers($userId1, $userId2); - - public abstract function deleteJoinRequest($token); + function findJoinRequest($token); - public static function get() { - global $DATABASE; + function deleteUser($userId); - switch ($DATABASE['dbtype']) { - case 'MySQL': - return new MySQLDAO($DATABASE); - default: - throw new \Exception($DATABASE['dbtype'] . ' not supported yet'); - } - } + function joinUsers($userId1, $userId2); + function deleteJoinRequest($token); } diff --git a/classes/GrouperClient.php b/classes/GrouperClient.php index 2bef66b..fb8927b 100644 --- a/classes/GrouperClient.php +++ b/classes/GrouperClient.php @@ -71,9 +71,11 @@ class GrouperClient { if ($this->isSuccess($response)) { if (count($response->return->results) === 1) { $groups = []; - foreach ($response->return->results[0]->wsGroups as $group) { - if (!$this->startsWith($group->name, 'etc:')) { - array_push($groups, $group->name); + if ($response->return->results[0]->wsGroups !== null) { + foreach ($response->return->results[0]->wsGroups as $group) { + if (!$this->startsWith($group->name, 'etc:')) { + array_push($groups, $group->name); + } } } return $groups; diff --git a/classes/Identity.php b/classes/Identity.php index e731aab..fc82a1d 100644 --- a/classes/Identity.php +++ b/classes/Identity.php @@ -76,6 +76,11 @@ class Identity { */ public $eppn; + /** + * True if this has been selected as the primary identity. + */ + public $primary; + public function __construct($userType) { $isAllowedType = false; foreach (Identity::$ALLOWED_TYPES as $type) { @@ -89,6 +94,8 @@ class Identity { } $this->type = $userType; + + $this->primary = false; } } diff --git a/classes/MySQLDAO.php b/classes/MySQLDAO.php index d6343b2..536882d 100644 --- a/classes/MySQLDAO.php +++ b/classes/MySQLDAO.php @@ -26,12 +26,17 @@ namespace RAP; use PDO; -class MySQLDAO extends DAO { +class MySQLDAO implements DAO { + + private $config; + + public function __construct($config) { + $this->config = $config; + } public function getDBHandler() { - global $DATABASE; - $connectionString = "mysql:host=" . $DATABASE['hostname'] . ";dbname=" . $DATABASE['dbname']; - $dbh = new PDO($connectionString, $DATABASE['username'], $DATABASE['password']); + $connectionString = "mysql:host=" . $this->config['hostname'] . ";dbname=" . $this->config['dbname']; + $dbh = new PDO($connectionString, $this->config['username'], $this->config['password']); // For transaction errors (see https://stackoverflow.com/a/9659366/771431) $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); return $dbh; @@ -39,8 +44,6 @@ class MySQLDAO extends DAO { public function createLoginToken($token, $userId) { - global $log; - $dbh = $this->getDBHandler(); $stmt = $dbh->prepare("INSERT INTO login_token (token, user_id) VALUES(:token, :user_id)"); @@ -52,7 +55,7 @@ class MySQLDAO extends DAO { if ($stmt->execute($params)) { return $token; } else { - $log->error($stmt->errorInfo()[2]); + error_log($stmt->errorInfo()[2]); throw new \Exception("SQL error while storing user token"); } } @@ -118,6 +121,7 @@ class MySQLDAO extends DAO { $identity = new Identity($row['type']); $identity->id = $row['id']; + $identity->primary = $row['primary']; $identity->typedId = $row['typed_id']; $identity->email = $row['email']; $identity->name = $row['name']; @@ -136,8 +140,11 @@ class MySQLDAO extends DAO { $dbh = $this->getDBHandler(); - $stmt = $dbh->prepare("SELECT `id`, `type`, `typed_id`, `email`, `name`, `surname`, `institution`, `eppn`" - . " FROM identity WHERE user_id = :user_id"); + $stmt = $dbh->prepare("SELECT (u.`primary_identity` = i.`id`) AS `primary`," + . " i.`id`, `type`, `typed_id`, `email`, `name`, `surname`, `institution`, `eppn`" + . " FROM identity i" + . " JOIN `user` u on u.id = i.user_id" + . " WHERE i.user_id = :user_id"); $stmt->bindParam(':user_id', $userId); $stmt->execute(); @@ -155,16 +162,19 @@ class MySQLDAO extends DAO { $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 setPrimaryIdentity($userId, $identityId) { + + $dbh = $this->getDBHandler(); + + $stmt = $dbh->prepare("UPDATE `user` SET `primary_identity` = :identity_id WHERE `id` = :user_id"); + $stmt->bindParam(':identity_id', $identityId); + $stmt->bindParam(':user_id', $userId); + $stmt->execute(); + } + public function findUserByIdentity($type, $identifier) { $dbh = $this->getDBHandler(); @@ -194,10 +204,11 @@ class MySQLDAO extends DAO { $dbh = $this->getDBHandler(); - // TODO: Add additional email search... - - $query = "SELECT `user_id`, `id`, `type`, `typed_id`, `email`, `name`, `surname`, `institution`, `eppn`" - . " FROM identity WHERE `email` LIKE :email OR `name` LIKE :name OR `surname` LIKE :surname"; + $query = "SELECT `user_id`, (u.`primary_identity` = i.`id`) AS `primary`," + . " i.`id`, `type`, `typed_id`, `email`, `name`, `surname`, `institution`, `eppn`" + . " FROM identity i" + . " JOIN `user` u on u.id = i.user_id" + . " WHERE `email` LIKE :email OR `name` LIKE :name OR `surname` LIKE :surname"; $stmt = $dbh->prepare($query); @@ -233,17 +244,6 @@ class MySQLDAO extends DAO { return $users; } - 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(); - } - public function createJoinRequest($token, $applicantUserId, $targetUserId) { $dbh = $this->getDBHandler(); @@ -298,12 +298,6 @@ class MySQLDAO extends DAO { $stmt1->bindParam(':id2', $userId2); $stmt1->execute(); - // Moving additional email addresses from user2 to user1 - $stmt2 = $dbh->prepare("UPDATE `additional_email` SET `user_id` = :id1 WHERE `user_id` = :id2"); - $stmt2->bindParam(':id1', $userId1); - $stmt2->bindParam(':id2', $userId2); - $stmt2->execute(); - // Deleting user2 join requests $stmt3 = $dbh->prepare("DELETE FROM `join_request` WHERE `target_user_id` = :id2"); $stmt3->bindParam(':id2', $userId2); diff --git a/classes/SessionData.php b/classes/SessionData.php index cc578f2..8dd9f52 100644 --- a/classes/SessionData.php +++ b/classes/SessionData.php @@ -26,28 +26,33 @@ namespace RAP; class SessionData { + private $dao; private $callbackURL; private $callbackTitle; public $user; public $userSearchResults; public $x509DataToRegister; + public function __construct(DAO $dao) { + $this->dao = $dao; + } + public function save() { $_SESSION['SessionData'] = $this; } - public static function get() { + public static function get(DAO $dao) { if (!isset($_SESSION['SessionData'])) { - $session = new SessionData(); + $session = new SessionData($dao); $session->save(); } return $_SESSION['SessionData']; } - public function setCallbackURL($callbackURL) { + public function setCallbackURL(CallbackHandler $callbackHandler, $callbackURL) { $this->callbackURL = $callbackURL; - $this->callbackTitle = CallbackHandler::getCallbackTitle($callbackURL); + $this->callbackTitle = $callbackHandler->getCallbackTitle($callbackURL); $this->save(); } @@ -60,7 +65,7 @@ class SessionData { } public function searchUser($searchText) { - $users = DAO::get()->searchUser($searchText); + $users = $this->dao->searchUser($searchText); $this->userSearchResults = []; foreach ($users as $user) { @@ -74,4 +79,10 @@ class SessionData { $this->save(); } + public function updatePrimaryIdentity($identityId) { + foreach ($this->user->identities as $identity) { + $identity->primary = ($identity->id === $identityId); + } + } + } diff --git a/classes/User.php b/classes/User.php index 1d7a6cb..e82db49 100644 --- a/classes/User.php +++ b/classes/User.php @@ -28,22 +28,22 @@ 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); - } - public function getPrimaryEmail() { - return $this->identities[0]->email; + foreach ($this->identities as $identity) { + if ($identity->primary) { + return $identity->email; + } + } + throw new \Exception("No primary identity defined for user " . $this->id); } + } diff --git a/classes/UserHandler.php b/classes/UserHandler.php index 58c0462..47824bb 100644 --- a/classes/UserHandler.php +++ b/classes/UserHandler.php @@ -26,45 +26,51 @@ namespace RAP; class UserHandler { - public static function saveUser(User $user) { + private $dao; + private $grouperConfig; - $dao = DAO::get(); + public function __construct(DAO $dao, $grouperConfig) { + $this->dao = $dao; + $this->grouperConfig = $grouperConfig; + } + + public function saveUser(User $user) { + $primarySpecified = true; + + // If new user if ($user->id === null) { - $user->id = $dao->createUser(); + $primarySpecified = false; + $user->id = $this->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)) { - + $identity->id = $this->dao->insertIdentity($identity, $user->id); + if (!$primarySpecified) { + $this->dao->setPrimaryIdentity($user->id, $identity->id); + $identity->primary = true; + } } } } - public static function findUserByIdentity($type, $identifier) { + public function findUserByIdentity($type, $identifier) { - return DAO::get()->findUserByIdentity($type, $identifier); + return $this->dao->findUserByIdentity($type, $identifier); } - public static function joinUsers($userId1, $userId2) { - - global $GROUPER; + public function joinUsers($userId1, $userId2) { - if (isset($GROUPER)) { - $gc = new GrouperClient($GROUPER); + if ($this->grouperConfig !== null) { + $gc = new GrouperClient($this->grouperConfig); $groupsToMove = $gc->getSubjectGroups('RAP:' . $userId2); $gc->addMemberships('RAP:' . $userId1, $groupsToMove); $gc->removeMemberships('RAP:' . $userId2, $groupsToMove); } - DAO::get()->joinUsers($userId1, $userId2); + $this->dao->joinUsers($userId1, $userId2); } } diff --git a/classes/X509Data.php b/classes/X509Data.php index c1bb523..3c77411 100644 --- a/classes/X509Data.php +++ b/classes/X509Data.php @@ -128,11 +128,7 @@ class X509Data { $parsedData = new X509Data(); if (isset($server['SSL_CLIENT_CERT'])) { - $parsedData->parseUsingOpenSSL(['SSL_CLIENT_CERT']); - // If all mandatory information has been parsed return the object - if ($parsedData->email !== null && $parsedData->fullName !== null && $parsedData->serialNumber !== null) { - return $parsedData; - } + $parsedData->parseUsingOpenSSL($server['SSL_CLIENT_CERT']); } if ($parsedData->fullName === null) { @@ -158,7 +154,6 @@ class X509Data { // Here a hex->dec conversion is performed, in order to store the // serial number in a consistent format: $hexSerial = $server['SSL_CLIENT_M_SERIAL']; - // TODO $parsedData->serialNumber = X509Data::bchexdec($hexSerial); } else { throw new Exception("Unable to retrieve serial number from certificate"); diff --git a/css/animation.css b/css/animation.css new file mode 100644 index 0000000..ac5a956 --- /dev/null +++ b/css/animation.css @@ -0,0 +1,85 @@ +/* + Animation example, for spinners +*/ +.animate-spin { + -moz-animation: spin 2s infinite linear; + -o-animation: spin 2s infinite linear; + -webkit-animation: spin 2s infinite linear; + animation: spin 2s infinite linear; + display: inline-block; +} +@-moz-keyframes spin { + 0% { + -moz-transform: rotate(0deg); + -o-transform: rotate(0deg); + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -moz-transform: rotate(359deg); + -o-transform: rotate(359deg); + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@-webkit-keyframes spin { + 0% { + -moz-transform: rotate(0deg); + -o-transform: rotate(0deg); + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -moz-transform: rotate(359deg); + -o-transform: rotate(359deg); + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@-o-keyframes spin { + 0% { + -moz-transform: rotate(0deg); + -o-transform: rotate(0deg); + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -moz-transform: rotate(359deg); + -o-transform: rotate(359deg); + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@-ms-keyframes spin { + 0% { + -moz-transform: rotate(0deg); + -o-transform: rotate(0deg); + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -moz-transform: rotate(359deg); + -o-transform: rotate(359deg); + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@keyframes spin { + 0% { + -moz-transform: rotate(0deg); + -o-transform: rotate(0deg); + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -moz-transform: rotate(359deg); + -o-transform: rotate(359deg); + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} diff --git a/css/style.css b/css/style.css index 9affc9a..cc7f30a 100644 --- a/css/style.css +++ b/css/style.css @@ -3,6 +3,30 @@ body { padding-bottom: 150px; } +.waiting { + position: fixed; + top: 0; + bottom: 0; + right: 0; + left: 0; + background-color: rgba(255, 255, 255, 0.5); + z-index: 100000; + font-size: 40px; + text-align: center; +} + +.waiting .icon-wrapper { + position: absolute; + width: 100%; + height: 100%; + display: table; +} + +.waiting .glyphicon { + display: table-cell; + vertical-align: middle; +} + @keyframes pulse { from { transform: scale(1, 1); @@ -141,3 +165,13 @@ body { font-size: 30px; } +.primary-identity-icon { + font-size: 17px; + color: #4ea64e; +} +.primary-identity-icon a { + color: #ddd; +} +.primary-identity-icon a:hover { + color: #888; +} \ No newline at end of file diff --git a/include/footer.php b/include/footer.php index 0de7f71..5d0b79f 100644 --- a/include/footer.php +++ b/include/footer.php @@ -1,4 +1,9 @@ +
+ + + +