Files
api/services/nginx/app/traits/route_t.php
T
Jepp9350 2038547d1c Enhance department report handling with date support
Added date parameter support for department daily reports, including validation, new functions, and route updates. Improved functionality allows fetching or creating reports for specific dates and prevents duplicate entries for the same date. Introduced a standardized date format within the API.
2025-03-06 14:37:32 +01:00

381 lines
12 KiB
PHP

<?php
namespace traits;
use classes\authentication;
use classes\recaptcha;
use objects\logs_o;
trait route_t
{
protected array $permissions = [];
private string $route;
public function __construct()
{
$this->route = $_SERVER['REQUEST_URI'];
}
public function run(): void
{
// Add the routes here
}
/**
* Require type
* @param mixed $value
* @param string $type
* @return bool
*/
public function requireType(mixed $value, string $type): bool
{
// Get the type of the value
$value_type = gettype($value);
// Check if the value is of the required type
if ($value_type !== $type) {
global $response;
$response->error('Invalid type. Expected: ' . $type . ' Got: ' . $value_type . ' Value: ' . $value, 400);
}
return true;
}
/**
* The default date format (Y-m-d) Example: 2023-01-01
* @note This is used to format dates in the API
* @return string
*/
public function FORMAT_DATE(): string
{
return 'Y-m-d';
}
public function requireDateFormat(string $date, string $format): void
{
global $response;
$d = \DateTime::createFromFormat($format, $date);
if (!$d || $d->format($format) !== $date) {
$response->error('Invalid date format. Expected: ' . $format . ' Got: ' . $date, 400);
}
}
public function requireMinValue(int $value, int $min): void
{
global $response;
if ($value < $min) {
$response->error('Parameter must be at least ' . $min, 400);
}
}
public function type_int(): string
{
return 'integer';
}
public function type_string(): string
{
return 'string';
}
public function requireParameters(array $parameters): void
{
global $response;
$missing_parameters = [];
// Check if all required parameters are set
foreach ( $parameters as $parameter ) {
if (!$response->getRequestParameter($parameter) && !$response->isRequestParameterSet($parameter)) {
$missing_parameters[] = $parameter;
}
}
if (count($missing_parameters) > 0) {
$response->error('Missing required parameters: ' . implode(', ', $missing_parameters), 400);
}
}
/**
* Require department access
* @note This checks if the user has access to the department by checking if the user has the permission department_access_{department} (_{permission} if provided)
* @param string $department
* @param string|null $permission
*/
public function requireDepartmentAccess(string $department, string|null $permission = null): void
{
self::requirePermission('department_access_' . $department . ($permission ? '_' . $permission : ''));
}
/**
* Require permission
* @param string $permission
* @return bool
*/
public function requirePermission(string $permission): bool
{
global $response;
// Check if the users authorization token has the required permission
try {
$user = (new authentication())->get_user();
// If there is no user, return an error
if (!$user) {
(new logs_o())->add('global', 'global', 1, 0, 'AUTHENTICATION_FAILED', 'Authentication failed. Invalid, or missing token');
$response->error('Authentication failed. Invalid or missing token.', 401);
}
if (!$user->hasPermission($permission)) {
(new logs_o())->add('global', 'global', 1, $user->id, 'PERMISSION_DENIED', 'Permission denied. Missing permission: ' . $permission);
$response->error('Permission denied. Missing permission: ' . $permission . ' for user: ' . $user->id . ' In group: ' . $user->group_id->value(), 403);
}
} catch (\Exception $e) {
$response->error($e->getMessage(), 400);
}
return true;
}
/**
* Require parameter to be a positive integer
* @param int $value The value to check
* @param string|null $parameter The name of the parameter
* @return void
*/
public function requireParameterIntPositive(int $value, string $parameter = null): void
{
global $response;
if ($value <= 0) {
$response->error('Parameter ' . $parameter . ' must be a positive integer', 400);
}
}
public function requireMinLength(string $parameter, int $length): void
{
global $response;
$value = self::getParameter($parameter);
if (strlen($value) < $length) {
$response->error('Parameter ' . $parameter . ' must be at least ' . $length . ' characters long', 400);
}
}
/**
* Get the parameter from the request by name
* This is a shorthand for the response class method
* @param string $parameter
* @return mixed|null
*/
public function getParameter(string $parameter): mixed
{
global $response;
return $response->getRequestParameter($parameter);
}
public function requireMaxLength(string $parameter, int $length): void
{
global $response;
$value = self::getParameter($parameter);
if (strlen($value) > $length) {
$response->error('Parameter ' . $parameter . ' must be at most ' . $length . ' characters long', 400);
}
}
public function isParametersSet(array $parameters): bool
{
global $response;
// Check if all required parameters are set
foreach ( $parameters as $parameter ) {
if (!$response->isRequestParameterSet($parameter)) {
return false;
}
}
return true;
}
/**
* GET route
* @param string $route Example: /home, /home/{id}
* @param callable $callback
* @param array $permissions
* @return void
*/
public function get(string $route, callable $callback, array $permissions = []): void
{
$this->registerRoute($route, 'GET', $callback, $permissions);
}
private function registerRoute($route, $method, $callback, $permissions = []): void
{
global $router;
$router->add($route, $method, $callback, self::registerPermissions($permissions, $route, $method));
}
/**
* Register permissions for the route
* @param array $permissions The permissions to register
* @param null $endpoint
* @param null $method
* @return array
*/
public function registerPermissions(array $permissions, $endpoint = null, $method = null): array
{
foreach ( $permissions as $permission => $description ) {
$this->registerPermission($permission, $description, $endpoint, $method);
}
return $this->permissions;
}
/**
* Register a permission
* @param string $permission The permission to register (Example: 'modules_motorapi_lookup')
* @param string $description The description of the permission (Example: 'Lookup license plate information')
* @param null $endpoint The endpoint of the permission (Example: '/department/license-plate/lookup')
* @param null $method The method of the permission (Example: 'GET')
* @return void
*/
public function registerPermission(string $permission, string $description = 'No description provided.', $endpoint = null, $method = null): void
{
$this->permissions[$endpoint ?? $this->route][$method ?? 'UNKNOWN_METHOD'][$permission] = $description;
}
/**
* POST route
* @param string $route Example: /home, /home/{id}
*/
public function post(string $route, callable $callback, array $permissions = []): void
{
$this->registerRoute($route, 'POST', $callback, $permissions);
}
/**
* PUT route
* @param string $route Example: /home, /home/{id}
*/
public function put(string $route, callable $callback, array $permissions = []): void
{
$this->registerRoute($route, 'PUT', $callback, $permissions);
}
/**
* DELETE route
* @param string $route Example: /home, /home/{id}
*/
public function delete(string $route, callable $callback, array $permissions = []): void
{
$this->registerRoute($route, 'DELETE', $callback, $permissions);
}
/**
* OPTIONS route
* @param string $route Example: /home, /home/{id}
*/
public function options(string $route, callable $callback, array $permissions = []): void
{
$this->registerRoute($route, 'OPTIONS', $callback, $permissions);
}
/**
* PATCH route
* @param string $route Example: /home, /home/{id}
*/
public function patch(string $route, callable $callback, array $permissions = []): void
{
$this->registerRoute($route, 'PATCH', $callback, $permissions);
}
/**
* Get the parameter from the route URL by index
* @param string $index
* @return string|null
*/
public function fromRoute(string $index): ?string
{
$params = explode('/', $this->route);
$index = array_search($index, $params);
return $params[$index] ?? null;
}
/**
* Require plate scanner authentication
* @return bool
*/
public function requirePlateScannerAuth(): bool
{
global $response;
// Check if the plate scanner has a valid API key
try {
$plate_scanner = (new authentication())->get_plate_scanner();
if (!$plate_scanner) {
(new logs_o())->add('global', 'global', 1, 0, 'AUTHENTICATION_FAILED', 'Authentication failed. Invalid, or missing API key');
$response->error('Authentication failed. Invalid or missing API key.', 401);
}
} catch (\Exception $e) {
$response->error($e->getMessage(), 400);
}
return true;
}
/**
* Require order access
* @param int $order_id The order ID
* @param string|null $permission The permission to check
* @return bool
*/
public function requireOrderAccess(int $order_id, string|null $permission = null): bool
{
// TODO: Implement indirect order access, where the user has access to the department, and the department has access to the order
self::requirePermission('order_access_' . $order_id . ($permission ? '_' . $permission : ''));
return true;
}
/**
* Require reCAPTCHA for the request
* @return bool
*/
public function requireRecaptcha(): bool
{
global $response;
// Check if the reCAPTCHA is valid
try {
$recaptcha_response = $response->getRequestParameter('g_recaptcha_response');
$recaptcha = (new recaptcha())->validate($recaptcha_response);
if (!$recaptcha) {
(new logs_o())->add('global', 'global', 1, 0, 'AUTHENTICATION_FAILED', 'Authentication failed. Invalid, or missing reCAPTCHA');
$response->error('Authentication failed. Invalid or missing reCAPTCHA.', 401);
}
} catch (\Exception $e) {
$response->error($e->getMessage(), 400);
}
return true;
}
/**
* Get data from the request body or query string by name
* @param string $name
* @return string|null
*/
public function fromRequest(string $name): ?string
{
// If the $_POST variable is set, return the value from the POST variable, otherwise return the value from the query string
if (isset($_POST)) {
$data = json_decode(file_get_contents('php://input'), true);
if (isset($data[$name])) {
return $data[$name];
}
}
return (isset($_POST[$name])) ? $_POST[$name] : $this->fromQuery($name);
}
/**
* Get the parameter from the query string by name
* @param string $name
* @return string|null
*/
public function fromQuery(string $name): ?string
{
return (isset($_GET[$name])) ? $_GET[$name] : null;
}
/**
* Match route
* @param string $route Example: /home, /home/{id}
*/
private function match_route(string $route): bool
{
// Check if route is the same, or if it matches the regex pattern
return $route === $this->route || preg_match($route, $this->route);
}
}