Adds a scope-based access control layer to all 81 existing API routes. Sits alongside existing session-cookie auth (does not replace it). What this PR does: - Audits every existing route and documents required scope per route (see documentation/auth/route-scope-audit.md) - Adds classes/auth/scope.php with 10 scope constants and role→scope defaults - Adds classes/auth/scope_middleware.php with requireScope/requireAnyScope/requireRole - Applies require*() calls to all 81 existing routes - Adds ScopeMiddlewareTest (unit, 178 lines) and RouteScopeTest (integration, 212 lines) Coexistence note: This branch's classes/auth/scope.php is a stub that will be replaced by classes/auth/scope_registry.php (from TRU-145 / PR #396) when that PR merges first. The two have compatible APIs. Refs: TRU-149
242 lines
11 KiB
PHP
242 lines
11 KiB
PHP
<?php
|
|
|
|
namespace routes;
|
|
|
|
use classes\authentication;
|
|
use objects\departments_o;
|
|
use objects\logs_o;
|
|
use objects\plate_scanners_o;
|
|
use traits\route_t;
|
|
|
|
use app\auth\Scope;
|
|
use app\auth\ScopeMiddleware;
|
|
|
|
class plateScannersRoute
|
|
{
|
|
use route_t;
|
|
|
|
public function run(): void
|
|
{
|
|
$this->get('/numberplatescanners', function () {
|
|
ScopeMiddleware::requireScope(Scope::CUSTOMER_READ, '/numberplatescanners');
|
|
// Require the user to be logged in
|
|
global $response;
|
|
$this->requirePermission('list_number_plate_scanners');
|
|
// Get the user object
|
|
$user = (new authentication())->get_user();
|
|
// Check if the request was successful
|
|
if ($user) {
|
|
// Log the incident
|
|
(new logs_o())->add('numberplatescanners', 'global', 1, $user->id, 'LIST_NUMBER_PLATE_SCANNERS', 'Successfully listed number plate scanners');
|
|
// Return the list of plate scanners
|
|
$response->success(
|
|
(new plate_scanners_o())
|
|
->setSearchableFields([
|
|
// The fields that can be searched. This would otherwise make it possible to get secret information from the database, simply by searching for it and getting the result count back
|
|
'id',
|
|
'department_id',
|
|
'name',
|
|
'notes'
|
|
])
|
|
->listObjectsWithPaginationIfSet(
|
|
static function (array $scanner): array {
|
|
return (new plate_scanners_o())->select((int)$scanner['id'])->asArray();
|
|
}
|
|
)
|
|
);
|
|
} else {
|
|
// Log the incident
|
|
(new logs_o())->add('numberplatescanners', 'global', 1, 0, 'LIST_NUMBER_PLATE_SCANNERS', 'No user found, or invalid session');
|
|
// Return an error
|
|
$response->error('Invalid session', 400);
|
|
}
|
|
},
|
|
[
|
|
'list_number_plate_scanners' => 'List all number plate scanners'
|
|
]
|
|
);
|
|
|
|
$this->post('/numberplatescanners', function () {
|
|
ScopeMiddleware::requireScope(Scope::CUSTOMER_WRITE, '/numberplatescanners');
|
|
// Require the user to be logged in
|
|
global $response;
|
|
$this->requirePermission('add_number_plate_scanner');
|
|
// Get the user object
|
|
$user = (new authentication())->get_user();
|
|
// Check if the request was successful
|
|
if ($user) {
|
|
// Get the post data
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
// Check if the required fields are set
|
|
if (!isset($data['department_id'])) {
|
|
$response->error('Department ID is required', 400);
|
|
}
|
|
if (!isset($data['name'])) {
|
|
$response->error('Name is required', 400);
|
|
}
|
|
if (!isset($data['notes'])) {
|
|
$response->error('Notes is required', 400);
|
|
}
|
|
$laneId = array_key_exists('lane_id', $data) && $data['lane_id'] !== null
|
|
? (int)$data['lane_id']
|
|
: null;
|
|
// Add the number plate scanner
|
|
$scanner = new plate_scanners_o();
|
|
$scanner->add((int)$data['department_id'], (string)$data['name'], (string)$data['notes'], $laneId);
|
|
// Log the incident
|
|
(new logs_o())->add('numberplatescanners', 'global', 1, $user->id, 'ADD_NUMBER_PLATE_SCANNER', 'Successfully added a number plate scanner');
|
|
// Return a success message
|
|
$response->success([
|
|
'message' => 'Number plate scanner added',
|
|
'scanner' => $scanner->asArray(),
|
|
]);
|
|
} else {
|
|
// Log the incident
|
|
(new logs_o())->add('numberplatescanners', 'global', 1, 0, 'ADD_NUMBER_PLATE_SCANNER', 'No user found, or invalid session');
|
|
// Return an error
|
|
$response->error('Invalid session', 400);
|
|
}
|
|
},
|
|
[
|
|
'add_number_plate_scanner' => 'Add a number plate scanner'
|
|
]
|
|
);
|
|
|
|
$this->put('/numberplatescanners', function () {
|
|
ScopeMiddleware::requireScope(Scope::CUSTOMER_WRITE, '/numberplatescanners');
|
|
// Require the user to be logged in
|
|
global $response;
|
|
$this->requirePermission('edit_number_plate_scanner');
|
|
// Get the user object
|
|
$user = (new authentication())->get_user();
|
|
// Check if the request was successful
|
|
if ($user) {
|
|
// Get the post data
|
|
$data = json_decode(file_get_contents('php://input'), true);
|
|
// Check if the required fields are set
|
|
if (!isset($data['id'])) {
|
|
$response->error('ID is required', 400);
|
|
}
|
|
if (!isset($data['department_id'])) {
|
|
$response->error('Department ID is required', 400);
|
|
}
|
|
if (!isset($data['name'])) {
|
|
$response->error('Name is required', 400);
|
|
}
|
|
if (!isset($data['notes'])) {
|
|
$response->error('Notes is required', 400);
|
|
}
|
|
$laneIdProvided = array_key_exists('lane_id', $data);
|
|
$laneId = $laneIdProvided && $data['lane_id'] !== null ? (int)$data['lane_id'] : null;
|
|
// Edit the number plate scanner
|
|
$scanner = new plate_scanners_o();
|
|
$scanner->edit(
|
|
(int)$data['id'],
|
|
(int)$data['department_id'],
|
|
(string)$data['name'],
|
|
(string)$data['notes'],
|
|
$laneId,
|
|
$laneIdProvided
|
|
);
|
|
// Log the incident
|
|
(new logs_o())->add('numberplatescanners', 'global', 1, $user->id, 'EDIT_NUMBER_PLATE_SCANNER', 'Successfully edited a number plate scanner');
|
|
// Return a success message
|
|
$response->success([
|
|
'message' => 'Number plate scanner edited',
|
|
'scanner' => $scanner->asArray(),
|
|
]);
|
|
} else {
|
|
// Log the incident
|
|
(new logs_o())->add('numberplatescanners', 'global', 1, 0, 'EDIT_NUMBER_PLATE_SCANNER', 'No user found, or invalid session');
|
|
// Return an error
|
|
$response->error('Invalid session', 400);
|
|
}
|
|
},
|
|
[
|
|
'edit_number_plate_scanner' => 'Edit a number plate scanner'
|
|
]
|
|
);
|
|
|
|
$this->post('/numberplatescanners/{id}/rotate-key', function () {
|
|
ScopeMiddleware::requireScope(Scope::CUSTOMER_WRITE, '/numberplatescanners/{id}/rotate-key');
|
|
global $response;
|
|
$this->requirePermission('edit_number_plate_scanner');
|
|
$user = (new authentication())->get_user();
|
|
|
|
if (!$user) {
|
|
(new logs_o())->add('numberplatescanners', 'global', 1, 0, 'ROTATE_NUMBER_PLATE_SCANNER_KEY', 'No user found, or invalid session');
|
|
$response->error('Invalid session', 400);
|
|
}
|
|
|
|
$scannerId = (int)$this->fromRoute('id');
|
|
self::requireParameterIntPositive($scannerId, 'id');
|
|
$scanner = (new plate_scanners_o())->select($scannerId);
|
|
if (!$scanner->exists()) {
|
|
$response->error('Number plate scanner not found', 404);
|
|
}
|
|
|
|
self::requireDepartmentAccess((int)$scanner->department_id->value());
|
|
$rotatedScanner = (new plate_scanners_o())->rotateApiKey($scannerId);
|
|
(new logs_o())->add('numberplatescanners', 'global', 1, $user->id, 'ROTATE_NUMBER_PLATE_SCANNER_KEY', 'Successfully rotated a number plate scanner API key');
|
|
$response->success([
|
|
'message' => 'Number plate scanner API key rotated',
|
|
'scanner' => $rotatedScanner,
|
|
'api_key' => (string)$rotatedScanner['api_key'],
|
|
]);
|
|
}, [
|
|
'edit_number_plate_scanner' => 'Rotate a number plate scanner API key',
|
|
]);
|
|
|
|
self::get('/department/numberplatescanners', function () {
|
|
// Require the user to be logged in
|
|
global $response;
|
|
$this->requirePermission('list_department_number_plate_scanners');
|
|
// Get the user object
|
|
$user = (new authentication())->get_user();
|
|
// Check if the request was successful
|
|
if ($user) {
|
|
// Get the department ID
|
|
self::requireParameters(['id']);
|
|
self::requireType((int)self::getParameter('id'), self::type_int());
|
|
self::requireParameterIntPositive((int)self::getParameter('id'), 'id');
|
|
// Check if the user has access to the department
|
|
self::requireDepartmentAccess((int)self::getParameter('id'));
|
|
// Check if the department exists
|
|
$department = (new departments_o())->select((int)self::getParameter('id'));
|
|
$department->requireSelected();
|
|
// Log the incident
|
|
(new logs_o())->add('numberplatescanners', 'global', 1, $user->id, 'LIST_DEPARTMENT_NUMBER_PLATE_SCANNERS', 'Successfully listed department number plate scanners');
|
|
// Get the department scanners.
|
|
$result = (new plate_scanners_o())
|
|
->getFieldsWhere([
|
|
'department_id' => (int)self::getParameter('id')
|
|
], [
|
|
'id',
|
|
'lane_id',
|
|
'name',
|
|
'notes'
|
|
]);
|
|
// Parse the result
|
|
foreach ( $result as $key => $value ) {
|
|
$result[$key]['id'] = (int)$value['id'];
|
|
$result[$key]['lane_id'] = $value['lane_id'] === null ? null : (int)$value['lane_id'];
|
|
}
|
|
// Return the list of plate scanners
|
|
$response->success(
|
|
$result
|
|
);
|
|
} else {
|
|
// Log the incident
|
|
(new logs_o())->add('numberplatescanners', 'global', 1, 0, 'LIST_DEPARTMENT_NUMBER_PLATE_SCANNERS', 'No user found, or invalid session');
|
|
// Return an error
|
|
$response->error('Invalid session', 400);
|
|
}
|
|
},
|
|
[
|
|
'list_department_number_plate_scanners' => 'List all number plate scanners in a department',
|
|
'department_access_:id' => 'Access department :id'
|
|
]
|
|
);
|
|
}
|
|
}
|