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
148 lines
5.9 KiB
PHP
148 lines
5.9 KiB
PHP
<?php
|
|
|
|
namespace routes;
|
|
|
|
use classes\authentication;
|
|
use objects\customer_vehicles_addons_o;
|
|
use objects\customer_vehicles_o;
|
|
use objects\product_options_o;
|
|
use traits\route_t;
|
|
|
|
use app\auth\Scope;
|
|
use app\auth\ScopeMiddleware;
|
|
use modules\subusers\helpers\subusers_permission_node_key;
|
|
|
|
class vehicleAddonRoute
|
|
{
|
|
use route_t;
|
|
|
|
public function run(): void
|
|
{
|
|
$this->get('/vehicles/addons/available', function () {
|
|
ScopeMiddleware::requireScope(Scope::CUSTOMER_READ, '/vehicles/addons/available');
|
|
global $response;
|
|
$auth = new authentication();
|
|
$user = $auth->get_user();
|
|
$permission_own = self::definePermission('list_vehicle_addon_own', subusers_permission_node_key::VEHICLES_LIST);
|
|
$permission_other = self::definePermission('list_vehicles_addon_other');
|
|
// Require the parameter vehicle_id
|
|
self::requireParameters(['id']);
|
|
$vehicle_id = (int)self::getParameter('id');
|
|
self::requireMinValue($vehicle_id, 1);
|
|
// Check if the vehicle exists
|
|
$vehicle = (new customer_vehicles_o())->select($vehicle_id);
|
|
if (!$vehicle->exists()) {
|
|
$response->error('Vehicle not found.', 404);
|
|
}
|
|
// Enforce access (own vs broader)
|
|
self::allowOwnOrDepartmentAccess(
|
|
$permission_own,
|
|
$permission_other,
|
|
(int)$vehicle->customer_id->value(),
|
|
null,
|
|
null,
|
|
'You are not allowed to view addons for vehicles from other users'
|
|
);
|
|
// Get the addons for the vehicle
|
|
$available_addons = (new product_options_o())->getProductOptions((int)$vehicle->type->value());
|
|
// Filter the addons, to only show the ones that has "subscription_allowed" set to true
|
|
$available_addons = array_filter($available_addons, function ($addon) {
|
|
return (bool)$addon['product']['subscription_allowed'];
|
|
});
|
|
// Get the addons for the vehicle (Currently selected)
|
|
|
|
$enabled_addons = (new customer_vehicles_addons_o())->getFieldsWhere([
|
|
'vehicle_id' => $vehicle_id,
|
|
], [
|
|
'id',
|
|
'addon_id',
|
|
'amount',
|
|
]);
|
|
|
|
// Create a list of available addons, with the amount of each addon
|
|
$available_addons = array_map(function ($addon) use ($enabled_addons) {
|
|
$addon['amount'] = 0;
|
|
foreach ( $enabled_addons as $enabled_addon ) {
|
|
if ((int)$enabled_addon['addon_id'] === (int)$addon['id']) {
|
|
$addon['amount'] = (int)$enabled_addon['amount'];
|
|
}
|
|
}
|
|
return $addon;
|
|
}, $available_addons);
|
|
// Sort the addons by name
|
|
usort($available_addons, function ($a, $b) {
|
|
return strcmp($a['name'], $b['name']);
|
|
});
|
|
// Return the addons
|
|
$response->success(
|
|
$available_addons,
|
|
);
|
|
},
|
|
[
|
|
'list_vehicle_addon_own' => 'List own vehicles addons',
|
|
'list_vehicles_addon_other' => 'List other vehicles addons',
|
|
]
|
|
);
|
|
|
|
$this->post('/vehicles/addons/toggle', function () {
|
|
ScopeMiddleware::requireScope(Scope::CUSTOMER_WRITE, '/vehicles/addons/toggle');
|
|
global $response;
|
|
$auth = new authentication();
|
|
$user = $auth->get_user();
|
|
$permission_own = self::definePermission('toggle_vehicle_addon_own', subusers_permission_node_key::VEHICLES_EDIT);
|
|
$permission_other = self::definePermission('toggle_vehicle_addon_other');
|
|
// Require the parameter vehicle_id
|
|
self::requireParameters(['vehicle_id', 'addon_id']);
|
|
$vehicle_id = (int)self::getParameter('vehicle_id');
|
|
self::requireMinValue($vehicle_id, 1);
|
|
// Check if the vehicle exists
|
|
$vehicle = (new customer_vehicles_o())->select($vehicle_id);
|
|
if (!$vehicle->exists()) {
|
|
$response->error('Vehicle not found.', 404);
|
|
}
|
|
// Enforce access (own vs broader)
|
|
self::allowOwnOrDepartmentAccess(
|
|
$permission_own,
|
|
$permission_other,
|
|
(int)$vehicle->customer_id->value(),
|
|
null,
|
|
null,
|
|
'You are not allowed to edit addons for vehicles from other users'
|
|
);
|
|
// Get the addon id
|
|
$addon_id = (int)self::getParameter('addon_id');
|
|
self::requireMinValue($addon_id, 1);
|
|
|
|
// Check if the addon exists
|
|
$addon = (new product_options_o())->select($addon_id);
|
|
if (!$addon->exists()) {
|
|
$response->error('Addon not found.', 404);
|
|
}
|
|
|
|
// Check if the addon is already added to the vehicle
|
|
$existing_addon = (new customer_vehicles_addons_o())->getFieldsWhere([
|
|
'vehicle_id' => $vehicle_id,
|
|
'addon_id' => $addon_id,
|
|
], ['id']);
|
|
|
|
if (count($existing_addon) > 0) {
|
|
// If it exists, remove it
|
|
$tmp_addon = (new customer_vehicles_addons_o())->select($existing_addon[0]['id']);
|
|
$tmp_addon->delete();
|
|
$response->success([
|
|
'message' => 'Addon removed from vehicle.',
|
|
]);
|
|
}
|
|
// If it doesn't exist, add it
|
|
(new customer_vehicles_addons_o())->add($vehicle_id, $addon_id);
|
|
$response->success([
|
|
'message' => 'Addon added to vehicle.',
|
|
]);
|
|
},
|
|
[
|
|
'toggle_vehicle_addon_own' => 'Toggle own vehicles addons',
|
|
'toggle_vehicle_addon_other' => 'Toggle other vehicles addons',
|
|
]
|
|
);
|
|
}
|
|
} |