Files
api/services/nginx/app/routes/productOptionsRoute.php
T
OpenClaw 51a87655d6 feat(auth): add scope-based access control to all existing routes (TRU-149)
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
2026-08-17 11:43:13 +00:00

210 lines
9.4 KiB
PHP

<?php
namespace routes;
use classes\authentication;
use objects\logs_o;
use objects\product_options_o;
use traits\route_t;
use app\auth\Scope;
use app\auth\ScopeMiddleware;
class productOptionsRoute
{
use route_t;
public function run(): void
{
$this->get('/product/options', function () {
ScopeMiddleware::requireScope(Scope::CUSTOMER_READ, '/product/options');
// Require the user to be logged in
global $response;
$this->requirePermission('list_product_options');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Log the incident
(new logs_o())->add('product_options', 'global', 1, $user->id, 'LIST_PRODUCT_OPTIONS', 'User listed product options');
// Return the list of departments
$response->success(
(new product_options_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',
'product_id',
'option_id',
'name',
'min',
'max',
'created_at',
'updated_at'
])
->listObjectsWithPaginationIfSet(
function ($option) use ($user) {
// Return the object as an array
return [
'id' => (int)$option['id'],
'product_id' => (int)$option['product_id'],
'option_id' => (int)$option['option_id'],
'name' => (string)$option['name'],
'min' => $option['min'] === null ? null : (int)$option['min'],
'max' => $option['max'] === null ? null : (int)$option['max'],
'created_at' => (string)$option['created_at'],
'updated_at' => (string)$option['updated_at'],
];
}
)
);
} else {
// Log the incident
(new logs_o())->add('product_options', 'global', 1, 0, 'LIST_PRODUCT_OPTIONS', 'User tried to list product options without being logged in');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'list_product_options' => 'List all product options'
]
);
$this->post('/product/options', function () {
ScopeMiddleware::requireScope(Scope::CUSTOMER_WRITE, '/product/options');
// Require the user to be logged in
global $response;
$this->requirePermission('add_product_option');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Check if the required parameters are set
self::requireParameters(['product_id', 'option_id']);
// Check if the parameters are of the correct type
self::requireType(self::getParameter('product_id'), self::TYPE_INT());
self::requireType(self::getParameter('option_id'), self::TYPE_INT());
// Create the object
$product_options = new product_options_o();
// Add the object
$product_options->add(
self::getParameter('product_id'),
self::getParameter('option_id')
);
// Log the incident
(new logs_o())->add('product_options', 'global', 1, $user->id, 'ADD_PRODUCT_OPTION', 'User added a product option');
// Return the object
$response->success(
$product_options->__toString()
);
} else {
// Log the incident
(new logs_o())->add('product_options', 'global', 1, 0, 'ADD_PRODUCT_OPTION', 'User tried to add a product option without being logged in');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'add_product_option' => 'Add a product option'
]
);
$this->put('/product/options', function () {
ScopeMiddleware::requireScope(Scope::CUSTOMER_WRITE, '/product/options');
// Require the user to be logged in
global $response;
$this->requirePermission('edit_product_option');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Check if the required parameters are set
self::requireParameters(['id']);
// Check if the parameters are of the correct type
self::requireType(self::getParameter('id'), self::TYPE_INT());
// Create the object
$product_options = new product_options_o();
// Select the object
$product_options->select(self::getParameter('id'));
// Check what the user wants to edit
// Option name
if (self::isParametersSet(['name'])) {
// Check if the parameters are of the correct type
self::requireType(self::getParameter('name'), self::TYPE_STRING());
// Set the name
$product_options->set_name(self::getParameter('name'));
}
// Option min
if (self::isParametersSet(['min'])) {
// Check if the parameters are of the correct type (Or NULL)
self::requireTypeIn(self::getParameter('min'), [self::TYPE_INT(), self::TYPE_NULL()]);
// Set the min value
$product_options->set_min(self::getParameter('min'));
}
// Option max
if (self::isParametersSet(['max'])) {
// Check if the parameters are of the correct type
self::requireType(self::getParameter('max'), self::TYPE_INT());
// Set the max value
$product_options->set_max(self::getParameter('max'));
}
// Log the incident
(new logs_o())->add('product_options', 'global', 1, $user->id, 'EDIT_PRODUCT_OPTION', 'User edited a product option');
// Return the object
$response->success(
$product_options->__toString()
);
} else {
// Log the incident
(new logs_o())->add('product_options', 'global', 1, 0, 'EDIT_PRODUCT_OPTION', 'User tried to edit a product option without being logged in');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'edit_product_option' => 'Edit a product option'
]
);
$this->delete('/product/options', function () {
ScopeMiddleware::requireScope(Scope::CUSTOMER_WRITE, '/product/options');
// Require the user to be logged in
global $response;
$this->requirePermission('delete_product_option');
// Get the user object
$user = (new authentication())->get_user();
// Check if the request was successful
if ($user) {
// Check if the required parameters are set
self::requireParameters(['id']);
// Check if the parameters are of the correct type
self::requireType((int)self::getParameter('id'), self::TYPE_INT());
// Require the minimum value to be above 0
self::requireMinValue((int)self::getParameter('id'), 1);
// Create the object
$product_options = new product_options_o();
// Select the object
$product_options->select((int)self::getParameter('id'));
// Delete the object
$product_options->delete();
// Log the incident
(new logs_o())->add('product_options', 'global', 1, $user->id, 'DELETE_PRODUCT_OPTION', 'User deleted a product option');
// Return the object
$response->success(
'Product option deleted'
);
} else {
// Log the incident
(new logs_o())->add('product_options', 'global', 1, 0, 'DELETE_PRODUCT_OPTION', 'User tried to delete a product option without being logged in');
// Return an error
$response->error('Invalid session', 400);
}
},
[
'delete_product_option' => 'Delete a product option'
]
);
}
}