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