Files
api/services/nginx/app/traits/route_t.php
T
Jepp9350 d6ed51f9ba Add endpoint to fetch the latest department daily report
Introduced a new method in `department_daily_reports_o` to select the latest daily report for a department. Added a corresponding API route to fetch the latest report details for a given department ID, with appropriate validations and access control measures. Adjusted parameter handling logic to enhance request validation.
2025-03-04 15:19:40 +01:00

371 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;
}
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);
}
}