From 2abe52963f85de02ecf1c92fb052f8679fe4c56a Mon Sep 17 00:00:00 2001 From: Alan Cell Date: Fri, 6 Nov 2020 20:00:53 +0100 Subject: [PATCH] Imitate the REST api using url parameter based implementation Reason for this implementation is some clients having trouble configuring the rest api either due to not having proper access to the webserver in a shared hosting environment or security restrictions. But still from icehrm frontend we need to consume the backend rest api. --- app/rest.php | 6 +- core/api.php | 41 +++++++ core/src/Classes/IceRoute.php | 177 ++++++++++++++++++++++++++++++ core/src/Classes/Macaw.php | 2 + core/src/Classes/RestEndPoint.php | 2 + web/api/AdapterBase.js | 2 +- web/api/IceApiClient.js | 9 +- 7 files changed, 236 insertions(+), 3 deletions(-) create mode 100644 core/api.php create mode 100755 core/src/Classes/IceRoute.php diff --git a/app/rest.php b/app/rest.php index aea46162..34442742 100755 --- a/app/rest.php +++ b/app/rest.php @@ -1,3 +1,7 @@ getSetting('Api: REST Api Enabled') == '1') { + + \Utils\LogManager::getInstance()->info("Request: " . $_REQUEST); + + if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { + http_response_code(200); + exit(); + } + + define('REST_API_PATH', '/'); + + $echoRoute = \Classes\Macaw::get(REST_API_PATH . 'echo', function () { + echo "Echo " . rand(); + }); + + \Utils\LogManager::getInstance()->debug('Api registered URI: '.$echoRoute); + + $moduleManagers = \Classes\BaseService::getInstance()->getModuleManagers(); + + foreach ($moduleManagers as $moduleManagerObj) { + + $moduleManagerObj->setupRestEndPoints(); + } + $method = $_SERVER['REQUEST_METHOD']; + if (strtoupper($method) === 'GET') { + \Classes\IceRoute::dispatch($_GET['url'], $method); + } else { + $method = strtoupper($_REQUEST['method']); + \Classes\IceRoute::dispatch($_REQUEST['url'], $method); + } + + +}else{ + echo "REST Api is not enabled. Please set 'Api: REST Api Enabled' setting to true"; +} \ No newline at end of file diff --git a/core/src/Classes/IceRoute.php b/core/src/Classes/IceRoute.php new file mode 100755 index 00000000..3ffc2912 --- /dev/null +++ b/core/src/Classes/IceRoute.php @@ -0,0 +1,177 @@ + '[^/]+', + ':num' => '[0-9]+', + ':all' => '.*' + ); + + public static $error_callback; + + /** + * Defines a route w/ callback and method + */ + public static function __callstatic($method, $params) + { + + $uri = $params[0][0]; + $callback = $params[0][1]; + + array_push(self::$routes, $uri); + array_push(self::$methods, strtoupper($method)); + array_push(self::$callbacks, $callback); + + return $uri; + } + + /** + * Defines callback if route is not found + */ + public static function error($callback) + { + self::$error_callback = $callback; + } + + public static function haltOnMatch($flag = true) + { + self::$halts = $flag; + } + + /** + * Runs the callback for the given request + */ + public static function dispatch($uri, $method) + { + + $searches = array_keys(static::$patterns); + $replaces = array_values(static::$patterns); + + $found_route = false; + + self::$routes = str_replace('//', '/', self::$routes); + + // check if route is defined without regex + if (in_array($uri, self::$routes)) { + $route_pos = array_keys(self::$routes, $uri); + foreach ($route_pos as $route) { + //using an ANY option to match both GET and POST requests + if (self::$methods[$route] == $method || self::$methods[$route] == 'ANY') { + $found_route = true; + + //if route is not an object + if (!is_object(self::$callbacks[$route])) { + //grab all parts based on a / separator + $parts = explode('/', self::$callbacks[$route]); + + //collect the last index of the array + $last = end($parts); + + //grab the controller name and method call + $segments = explode('@', $last); + + //instanitate controller + $controller = new $segments[0](); + + //call method + $controller->$segments[1](); + + if (self::$halts) { + return; + } + } else { + //call closure + call_user_func(self::$callbacks[$route]); + + if (self::$halts) { + return; + } + } + } + } + } else { + // check if defined with regex + $pos = 0; + foreach (self::$routes as $route) { + if (strpos($route, ':') !== false) { + $route = str_replace($searches, $replaces, $route); + } + + if (preg_match('#^' . $route . '$#', $uri, $matched)) { + if (self::$methods[$pos] == $method) { + $found_route = true; + + array_shift($matched); //remove $matched[0] as [1] is the first parameter. + + + if (!is_object(self::$callbacks[$pos])) { + //grab all parts based on a / separator + $parts = explode('/', self::$callbacks[$pos]); + + //collect the last index of the array + $last = end($parts); + + //grab the controller name and method call + $segments = explode('@', $last); + + //instanitate controller + $controller = new $segments[0](); + + //fix multi parameters + if (!method_exists($controller, $segments[1])) { + echo "controller and action not found"; + } else { + call_user_func_array(array($controller, $segments[1]), $matched); + } + + //call method and pass any extra parameters to the method + // $controller->$segments[1](implode(",", $matched)); + + if (self::$halts) { + return; + } + } else { + call_user_func_array(self::$callbacks[$pos], $matched); + + if (self::$halts) { + return; + } + } + } + } + $pos++; + } + } + + // run the error callback if the route was not found + if ($found_route == false) { + if (!self::$error_callback) { + self::$error_callback = function () { + header($_SERVER['SERVER_PROTOCOL']." 404 Not Found"); + echo '404'; + }; + } + call_user_func(self::$error_callback); + } + } +} diff --git a/core/src/Classes/Macaw.php b/core/src/Classes/Macaw.php index 78995f5b..fab8ee3e 100755 --- a/core/src/Classes/Macaw.php +++ b/core/src/Classes/Macaw.php @@ -42,6 +42,8 @@ class Macaw array_push(self::$methods, strtoupper($method)); array_push(self::$callbacks, $callback); + call_user_func('\Classes\IceRoute::'.$method, $params); + return $uri; } diff --git a/core/src/Classes/RestEndPoint.php b/core/src/Classes/RestEndPoint.php index beb3daeb..340af0ea 100644 --- a/core/src/Classes/RestEndPoint.php +++ b/core/src/Classes/RestEndPoint.php @@ -420,6 +420,8 @@ class RestEndPoint if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) { $token = $matches[1]; } + } else { + $token = $_GET['token']; } if (strlen($token) > 32) { diff --git a/web/api/AdapterBase.js b/web/api/AdapterBase.js index 97faad2b..8a293e56 100644 --- a/web/api/AdapterBase.js +++ b/web/api/AdapterBase.js @@ -55,7 +55,7 @@ class AdapterBase extends ModuleBase { } setupApiClient(token) { - this.apiClient = new IceApiClient(this.apiUrl, token); + this.apiClient = new IceApiClient(this.apiUrl, token, window.CLIENT_BASE_URL, true); } setApiUrl(apiUrl) { diff --git a/web/api/IceApiClient.js b/web/api/IceApiClient.js index b18a19dd..a620877a 100644 --- a/web/api/IceApiClient.js +++ b/web/api/IceApiClient.js @@ -1,12 +1,19 @@ const axios = require('axios'); class IceApiClient { - constructor(baseUrl, token) { + constructor(baseUrl, token, clientBaseUrl, legacyApiWrapper = true) { this.baseUrl = baseUrl; this.token = token; + this.clientBaseUrl = clientBaseUrl; + this.legacyApiWrapper = legacyApiWrapper; } get(endpoint) { + if (this.legacyApiWrapper) { + const url = `${this.clientBaseUrl}rest.php?token=${this.token}&method=get&url=/${endpoint}`; + return axios.get(url); + } + return axios.get(this.baseUrl + endpoint, { headers: { Authorization: `Bearer ${this.token}`,