From 92a77b86a6f8e0a0ceeb0e30b0f3476f5f6a1449 Mon Sep 17 00:00:00 2001 From: Sonia Zorba <sonia.zorba@inaf.it> Date: Wed, 10 Jul 2019 13:51:54 +0200 Subject: [PATCH] RAP web service changes --- classes/OAuth2RequestHandler.php | 20 ++++++++ .../datalayer/mysql/MySQLAccessTokenDAO.php | 24 ++++++---- classes/exceptions/UnauthorizedException.php | 13 +++++ classes/model/AccessToken.php | 1 + include/rest-web-service.php | 47 ++++++++++++++----- index.php | 4 +- 6 files changed, 87 insertions(+), 22 deletions(-) create mode 100644 classes/exceptions/UnauthorizedException.php diff --git a/classes/OAuth2RequestHandler.php b/classes/OAuth2RequestHandler.php index aa95981..c481f98 100644 --- a/classes/OAuth2RequestHandler.php +++ b/classes/OAuth2RequestHandler.php @@ -151,4 +151,24 @@ class OAuth2RequestHandler { return $expTime - $now; } + public function validateToken(): void { + $headers = apache_request_headers(); + + if (!isset($headers['Authorization'])) { + throw new BadRequestException("Missing Authorization header"); + } + + $authorizationHeader = explode(" ", $headers['Authorization']); + if ($authorizationHeader[0] === "Bearer") { + $bearer_token = $authorizationHeader[1]; + } else { + throw new BadRequestException("Invalid token type"); + } + + $accessToken = $this->locator->getAccessTokenDAO()->getAccessToken($bearer_token); + if ($accessToken->expired) { + throw new UnauthorizedException("Access token is expired"); + } + } + } diff --git a/classes/datalayer/mysql/MySQLAccessTokenDAO.php b/classes/datalayer/mysql/MySQLAccessTokenDAO.php index 3c757c7..ee55315 100644 --- a/classes/datalayer/mysql/MySQLAccessTokenDAO.php +++ b/classes/datalayer/mysql/MySQLAccessTokenDAO.php @@ -30,6 +30,7 @@ class MySQLAccessTokenDAO extends BaseMySQLDAO implements AccessTokenDAO { ); if ($stmt->execute($params)) { + $accessToken->expired = false; return $accessToken; } else { error_log($stmt->errorInfo()[2]); @@ -42,13 +43,18 @@ class MySQLAccessTokenDAO extends BaseMySQLDAO implements AccessTokenDAO { $dbh = $this->getDBHandler(); // Access token can be retrieved from code in 1 minute from the creation - $stmt = $dbh->prepare("SELECT token, code, user_id, redirect_uri, client_id, creation_time, expiration_time, scope" - . " FROM access_token WHERE code = :code"); // AND CURRENT_TIMESTAMP < TIMESTAMPADD(MINUTE, 1, creation_time) + $stmt = $dbh->prepare("SELECT token, code, user_id, redirect_uri, client_id, creation_time, expiration_time, scope," + . " (expiration_time < CURRENT_TIMESTAMP) AS expired " + . " FROM access_token WHERE code = :code AND CURRENT_TIMESTAMP < TIMESTAMPADD(MINUTE, 1, creation_time)"); $stmt->bindParam(':code', $code); $stmt->execute(); $row = $stmt->fetch(); + if (!$row) { + return null; + } + return $this->getAccessTokenFromRow($row); } @@ -56,21 +62,22 @@ class MySQLAccessTokenDAO extends BaseMySQLDAO implements AccessTokenDAO { $dbh = $this->getDBHandler(); - $stmt = $dbh->prepare("SELECT token, code, user_id, redirect_uri, client_id, creation_time, expiration_time, scope" + $stmt = $dbh->prepare("SELECT token, code, user_id, redirect_uri, client_id, creation_time, expiration_time, scope," + . " (expiration_time < CURRENT_TIMESTAMP) AS expired " . " FROM access_token WHERE token = :token"); $stmt->bindParam(':token', $token); $stmt->execute(); $row = $stmt->fetch(); + if (!$row) { + return null; + } + return $this->getAccessTokenFromRow($row); } - private function getAccessTokenFromRow(?array $row): ?AccessToken { - - if ($row === null) { - return null; - } + private function getAccessTokenFromRow(array $row): ?AccessToken { $token = new AccessToken(); $token->token = $row['token']; @@ -80,6 +87,7 @@ class MySQLAccessTokenDAO extends BaseMySQLDAO implements AccessTokenDAO { $token->clientId = $row['client_id']; $token->creationTime = $row['creation_time']; $token->expirationTime = $row['expiration_time']; + $token->expired = $row['expired'] === "1"; $scope = null; if (isset($row['scope'])) { diff --git a/classes/exceptions/UnauthorizedException.php b/classes/exceptions/UnauthorizedException.php new file mode 100644 index 0000000..27cd752 --- /dev/null +++ b/classes/exceptions/UnauthorizedException.php @@ -0,0 +1,13 @@ +<?php + +namespace RAP; + +class UnauthorizedException extends \Exception { + + public $message; + + public function __construct($message) { + $this->message = $message; + } + +} diff --git a/classes/model/AccessToken.php b/classes/model/AccessToken.php index e7fc0f1..746848b 100644 --- a/classes/model/AccessToken.php +++ b/classes/model/AccessToken.php @@ -9,6 +9,7 @@ class AccessToken { public $userId; public $creationTime; public $expirationTime; + public $expired; public $redirectUri; public $clientId; public $scope; diff --git a/include/rest-web-service.php b/include/rest-web-service.php index e1590e8..a09c66e 100644 --- a/include/rest-web-service.php +++ b/include/rest-web-service.php @@ -42,9 +42,11 @@ Flight::route('GET ' . $WS_PREFIX . '/user-info', function() { */ Flight::route('GET ' . $WS_PREFIX . '/user/@userId', function($userId) { - global $dao; + global $locator; + + $locator->getOAuth2RequestHandler()->validateToken(); - $user = $dao->findUserById($userId); + $user = $locator->getUserDAO()->findUserById($userId); if ($user !== null) { Flight::json($user); } else { @@ -58,19 +60,26 @@ Flight::route('GET ' . $WS_PREFIX . '/user/@userId', function($userId) { */ Flight::route('GET ' . $WS_PREFIX . '/user', function() { - global $dao; + global $locator; + + $locator->getOAuth2RequestHandler()->validateToken(); $searchText = Flight::request()->query['search']; - $users = $dao->searchUser($searchText); + + $users = $locator->getUserDAO()->searchUser($searchText); Flight::json($users); }); /** * Create new user from identity data. Return the new user encoded in JSON. + * This can be used to automatically import users without they explicitly + * register (this is done for INAF eduGAIN users readling directly from LDAP). */ Flight::route('POST ' . $WS_PREFIX . '/user', function() { - global $userHandler; + global $locator; + + $locator->getOAuth2RequestHandler()->validateToken(); $postData = Flight::request()->data; @@ -95,7 +104,7 @@ Flight::route('POST ' . $WS_PREFIX . '/user', function() { $user->addIdentity($identity); - $userHandler->saveUser($user); + $locator->getUserHandler()->saveUser($user); Flight::json($user); }); @@ -105,15 +114,27 @@ Flight::route('POST ' . $WS_PREFIX . '/user', function() { */ Flight::route('POST ' . $WS_PREFIX . '/join', function() { - global $userHandler; + global $locator; + + $locator->getOAuth2RequestHandler()->validateToken(); + $postData = Flight::request()->data; - $userHandler->joinUsers($postData['user1'], $postData['user2']); + $userId1 = $postData['user1']; + $userId2 = $postData['user2']; - // if the join has success, returns the remaining user id - echo $postData['user1']; -}); + $user1 = $locator->getUserDAO()->findUserById($userId1); + if ($user1 === null) { + throw new BadRequestException("User " . $userId1 . " doesn't exists"); + } -Flight::route('GET ' . $WS_PREFIX . '/test', function() { - + $user2 = $locator->getUserDAO()->findUserById($userId2); + if ($user2 === null) { + throw new BadRequestException("User " . $userId2 . " doesn't exists"); + } + + $locator->getUserHandler()->joinUsers($user1, $user2); + + // if the join has success, returns the remaining user id + echo $userId1; }); diff --git a/index.php b/index.php index 7984b1c..76b64a8 100644 --- a/index.php +++ b/index.php @@ -33,6 +33,9 @@ Flight::map('error', function($ex) { if ($ex instanceof \RAP\BadRequestException) { http_response_code(400); echo "Bad request: " . $ex->message; + } else if ($ex instanceof \RAP\UnauthorizedException) { + http_response_code(401); + echo "Unauthorized: " . $ex->message; } else if ($ex instanceof \Exception) { if ($ex->getMessage() !== null) { echo $ex->getMessage(); @@ -40,7 +43,6 @@ Flight::map('error', function($ex) { echo $ex->getTraceAsString(); } } else { - error_log('Error'); throw $ex; } }); -- GitLab