diff --git a/api_handler/Methods/v1/AuthenticationTestMethod.php b/api_handler/Methods/v1/AuthenticationTestMethod.php new file mode 100644 index 0000000..d60c641 --- /dev/null +++ b/api_handler/Methods/v1/AuthenticationTestMethod.php @@ -0,0 +1,21 @@ +ResultData = KimchiAPI::getAuthenticationToken(); + return $response; + } + } \ No newline at end of file diff --git a/api_handler/Methods/v1/UserAuthenticationTestMethod.php b/api_handler/Methods/v1/UserAuthenticationTestMethod.php new file mode 100644 index 0000000..c3e136c --- /dev/null +++ b/api_handler/Methods/v1/UserAuthenticationTestMethod.php @@ -0,0 +1,23 @@ +ResultData = KimchiAPI::getUserAuthentication()->toArray(); + return $response; + } + } \ No newline at end of file diff --git a/api_handler/configuration.json b/api_handler/configuration.json index 8be54dd..fb1d35d 100644 --- a/api_handler/configuration.json +++ b/api_handler/configuration.json @@ -16,7 +16,9 @@ "enabled": true, "methods": [ {"methods": ["GET", "POST"], "path": "ping", "class": "\\Methods\\v1\\PingMethod"}, - {"methods": ["GET", "POST"], "path": "parameter_test", "class": "\\Methods\\v1\\ParameterTestMethod"} + {"methods": ["GET", "POST"], "path": "parameter_test", "class": "\\Methods\\v1\\ParameterTestMethod"}, + {"methods": ["GET", "POST"], "path": "auth/simple", "class": "\\Methods\\v1\\AuthenticationTestMethod"}, + {"methods": ["GET", "POST"], "path": "auth/user", "class": "\\Methods\\v1\\UserAuthenticationTestMethod"} ] } ] diff --git a/api_handler/package.json b/api_handler/package.json index c196925..4e74802 100644 --- a/api_handler/package.json +++ b/api_handler/package.json @@ -27,9 +27,17 @@ "required": true, "file": "Methods/v1/ParameterTestMethod.php" }, + { + "required": true, + "file": "Methods/v1/UserAuthenticationTestMethod.php" + }, { "required": true, "file": "Methods/v1/PingMethod.php" + }, + { + "required": true, + "file": "Methods/v1/AuthenticationTestMethod.php" } ], "files": [ diff --git a/src/KimchiAPI/Classes/Request.php b/src/KimchiAPI/Classes/Request.php index c2ebfdd..8dc4d67 100644 --- a/src/KimchiAPI/Classes/Request.php +++ b/src/KimchiAPI/Classes/Request.php @@ -74,6 +74,75 @@ return $_GET; } + /** + * Returns a specified header otherwise null if not set + * + * @param string $value + * @return string|null + */ + public function getHeaderParameter(string $value): ?string + { + $headers = self::getHeaderParameters(); + if(isset($headers[$value])) + return $headers[$value]; + return null; + } + + /** + * Returns an array of header parameters + * + * @return array + */ + public static function getHeaderParameters(): array + { + if(function_exists('getallheaders')) + return getallheaders(); + + $headers = []; + + $copy_server = [ + 'CONTENT_TYPE' => 'Content-Type', + 'CONTENT_LENGTH' => 'Content-Length', + 'CONTENT_MD5' => 'Content-Md5', + ]; + + foreach ($_SERVER as $key => $value) + { + if (substr($key, 0, 5) === 'HTTP_') + { + $key = substr($key, 5); + if (!isset($copy_server[$key]) || !isset($_SERVER[$key])) + { + $key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $key)))); + $headers[$key] = $value; + } + } + elseif (isset($copy_server[$key])) + { + $headers[$copy_server[$key]] = $value; + } + } + + if (!isset($headers['Authorization'])) + { + if (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) + { + $headers['Authorization'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION']; + } + elseif (isset($_SERVER['PHP_AUTH_USER'])) + { + $basic_pass = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : ''; + $headers['Authorization'] = 'Basic ' . base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . $basic_pass); + } + elseif (isset($_SERVER['PHP_AUTH_DIGEST'])) + { + $headers['Authorization'] = $_SERVER['PHP_AUTH_DIGEST']; + } + } + + return $headers; + } + /** * Returns a POST/GET Parameter * @@ -104,6 +173,7 @@ public static function getParameters(): array { return array_merge( + self::getHeaderParameters(), self::getGetParameters(), self::getPostParameters(), self::getDefinedDynamicParameters(), diff --git a/src/KimchiAPI/Exceptions/AccessKeyNotProvidedException.php b/src/KimchiAPI/Exceptions/AccessKeyNotProvidedException.php new file mode 100644 index 0000000..300e206 --- /dev/null +++ b/src/KimchiAPI/Exceptions/AccessKeyNotProvidedException.php @@ -0,0 +1,21 @@ +message = $message; + $this->code = $code; + } + } \ No newline at end of file diff --git a/src/KimchiAPI/Exceptions/AuthenticationNotProvidedException.php b/src/KimchiAPI/Exceptions/AuthenticationNotProvidedException.php new file mode 100644 index 0000000..00cc485 --- /dev/null +++ b/src/KimchiAPI/Exceptions/AuthenticationNotProvidedException.php @@ -0,0 +1,21 @@ +message = $message; + $this->code = $code; + } + } \ No newline at end of file diff --git a/src/KimchiAPI/KimchiAPI.php b/src/KimchiAPI/KimchiAPI.php index 922e6a9..5ca19cc 100644 --- a/src/KimchiAPI/KimchiAPI.php +++ b/src/KimchiAPI/KimchiAPI.php @@ -9,8 +9,11 @@ use KimchiAPI\Abstracts\ResponseStandard; use KimchiAPI\Abstracts\ResponseType; use KimchiAPI\Classes\API; + use KimchiAPI\Classes\Request; + use KimchiAPI\Exceptions\AccessKeyNotProvidedException; use KimchiAPI\Exceptions\ApiException; use KimchiAPI\Exceptions\ApiMethodNotFoundException; + use KimchiAPI\Exceptions\AuthenticationNotProvidedException; use KimchiAPI\Exceptions\IOException; use KimchiAPI\Exceptions\MissingComponentsException; use KimchiAPI\Exceptions\UnsupportedResponseStandardException; @@ -18,6 +21,7 @@ use KimchiAPI\Objects\ResponseStandards\GoogleAPI; use KimchiAPI\Objects\ResponseStandards\IntellivoidAPI; use KimchiAPI\Objects\ResponseStandards\JsonApiOrg; + use KimchiAPI\Objects\UserAuthentication; use KimchiAPI\Utilities\Converter; use ppm\Exceptions\AutoloaderException; use ppm\Exceptions\InvalidComponentException; @@ -26,7 +30,6 @@ use ppm\Exceptions\VersionNotFoundException; use ppm\ppm; use RuntimeException; - use Symfony\Component\Uid\Uuid; use VerboseAdventure\Abstracts\EventType; use VerboseAdventure\VerboseAdventure; @@ -153,10 +156,14 @@ } catch(ApiMethodNotFoundException $e) { - unset($e); self::handle404(); } + catch(AccessKeyNotProvidedException|AuthenticationNotProvidedException $e) + { + unset($e); + self::requireAuthentication(KIMCHI_API_NAME); + } catch(Exception $e) { self::getVerboseAdventure()->logException($e, KIMCHI_API_REQUEST_ID); @@ -215,6 +222,31 @@ self::handleResponse($response); } + /** + * Returns an authentication required header + * + * @param string $realm + * @param string $response_standard + * @param string $response_type + * @return void + * @throws ApiException + * @throws Exceptions\UnsupportedResponseTypeExceptions + * @throws UnsupportedResponseStandardException + */ + public static function requireAuthentication(string $realm, string $response_standard = ResponseStandard::KimchiAPI, string $response_type = ResponseType::Json) + { + $response = new Response(); + $response->ResponseCode = 401; + $response->Success = false; + $response->ErrorCode = 401; + $response->ErrorMessage = 'Unauthorized'; + $response->ResponseStandard = $response_standard; + $response->ResponseType = $response_type; + $response->Headers['WWW-Authenticate'] = 'Basic realm="' . $realm . '"'; + + self::handleResponse($response); + } + /** * Returns the headers used for framework * @@ -240,7 +272,6 @@ ]; } - /** * Handles the response handler and returns the response data to the client * @@ -302,4 +333,56 @@ print($return_results); exit(); } + + /** + * Returns a username and password authentication + * + * @return UserAuthentication + * @throws AuthenticationNotProvidedException + */ + public static function getUserAuthentication(): UserAuthentication + { + if(isset($_SERVER['PHP_AUTH_USER']) == false) + { + $parameters = Request::getParameters(); + + if(isset($parameters['username']) && isset($parameters['password'])) + { + $authentication_results = new UserAuthentication(); + $authentication_results->Username = $parameters['username']; + $authentication_results->Password = $parameters['password']; + return $authentication_results; + } + + throw new AuthenticationNotProvidedException('Authentication required, 401 unauthorized'); + } + + $authentication_results = new UserAuthentication(); + $authentication_results->Username = $_SERVER['PHP_AUTH_USER']; + $authentication_results->Password = $_SERVER['PHP_AUTH_PW']; + + return $authentication_results; + } + + /** + * Attempts to fetch the authentication token + * + * @param string $parameter_name + * @return string + * @throws AccessKeyNotProvidedException + */ + public static function getAuthenticationToken(string $parameter_name='access_key'): string + { + if(isset($_SERVER['PHP_AUTH_USER']) == false) + { + $parameters = Request::getParameters(); + + if(isset($parameters[$parameter_name]) && is_string($parameters[$parameter_name])) + return (string)$parameters[$parameter_name]; + + throw new AccessKeyNotProvidedException('Authentication required, 401 unauthorized'); + } + + return $_SERVER['PHP_AUTH_PW']; + } } \ No newline at end of file diff --git a/src/KimchiAPI/Objects/UserAuthentication.php b/src/KimchiAPI/Objects/UserAuthentication.php new file mode 100644 index 0000000..7ecf992 --- /dev/null +++ b/src/KimchiAPI/Objects/UserAuthentication.php @@ -0,0 +1,52 @@ + $this->Username, + 'password' => $this->Password + ]; + } + + /** + * Constructs object from an array representation + * + * @param array $data + * @return UserAuthentication + */ + public static function fromArray(array $data): UserAuthentication + { + $UserAuthenticationObject = new UserAuthentication(); + + if(isset($data['username'])) + $UserAuthenticationObject->Username = $data['username']; + + if(isset($data['password'])) + $UserAuthenticationObject->Password = $data['password']; + + return $UserAuthenticationObject; + } + } \ No newline at end of file diff --git a/src/KimchiAPI/package.json b/src/KimchiAPI/package.json index 9da8ac5..4ea1ee2 100644 --- a/src/KimchiAPI/package.json +++ b/src/KimchiAPI/package.json @@ -87,6 +87,10 @@ "required": true, "file": "Exceptions/BadEnvironmentException.php" }, + { + "required": true, + "file": "Exceptions/AuthenticationNotProvidedException.php" + }, { "required": true, "file": "Exceptions/MissingComponentsException.php" @@ -99,6 +103,10 @@ "required": true, "file": "Exceptions/InternalServerException.php" }, + { + "required": true, + "file": "Exceptions/AccessKeyNotProvidedException.php" + }, { "required": true, "file": "Exceptions/ConnectionBlockedException.php" @@ -143,6 +151,10 @@ "required": true, "file": "Interfaces/ResponseStandardInterface.php" }, + { + "required": true, + "file": "Objects/UserAuthentication.php" + }, { "required": true, "file": "Objects/Configuration/MethodConfiguration.php"