Refactor /subusers/grants update logic and require explicit autoloading for subuser module

- Replace route method for updating grants (`PATCH` to `PUT`) and streamline grant updates by directly modifying object properties instead of using manual data arrays.
- Introduce parameter validation for consistency and permission checks for cross-customer grant management.
- Require autoloading for all subuser module components (interfaces, traits, helpers, classes, and permissions) to improve modularity and maintainability.
This commit is contained in:
Jeppe Bundgaard
2026-02-12 13:17:10 +01:00
parent e9e0b3e19e
commit dcf4252218
2 changed files with 43 additions and 40 deletions
+17 -2
View File
@@ -112,8 +112,23 @@ require_once 'modules/goals/classes/goals_criteria_products.php';
require_once 'modules/goals/classes/goals_criteria.php';
require_once 'modules/goals/classes/goals.php';
// Subusers module (helpers used by objects)
require_once 'modules/subusers/helpers/subusers_permission_node_key.php';
// Subusers module
// Load interfaces, traits, classes, helpers and permissions to ensure autoloading for route usage
foreach (glob(WD . '/modules/subusers/interfaces/*.php') as $interface) {
require_once $interface;
}
foreach (glob(WD . '/modules/subusers/traits/*.php') as $trait) {
require_once $trait;
}
foreach (glob(WD . '/modules/subusers/classes/*.php') as $classFile) {
require_once $classFile;
}
foreach (glob(WD . '/modules/subusers/helpers/*.php') as $helper) {
require_once $helper;
}
foreach (glob(WD . '/modules/subusers/permissions/*.php') as $permissionFile) {
require_once $permissionFile;
}
// Dynamic Images module
foreach (glob(WD . '/modules/dynamicimages/interfaces/*.php') as $interface) {
+26 -38
View File
@@ -128,29 +128,34 @@ class subusersRoute
}
}, []);
$this->patch('/subusers/grants/{id}', function () {
$this->put('/subusers/grants', function () {
global $response;
$id = (int)self::fromRoute('id');
self::requireType($id, self::type_int());
$grant = (new subuser_grants_o())->select($id);
$grant->requireSelected();
$data = [];
self::requireParameters(['id']);
self::requireType(self::getParameter('id'), self::type_int());
$grant = (new subuser_grants_o())->select((int)self::getParameter('id'));
if (!$grant->exists()) {
$response->error('Grant not found', 404);
}
$user = (new authentication())->get_user();
// Validate permissions
if ((int)$grant->billing_customer_number->value() !== (int)$user->customer_number->value()) {
self::requirePermission('manage_subuser_grants_other_customer');
}
// Update fields provided in the request
if (self::isParametersSet(['enabled'])) {
$enabled = (bool)filter_var(self::getParameter('enabled'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
if ($enabled === null) {
$response->error('Invalid enabled flag', 400);
}
$data['enabled'] = (bool)$enabled;
if ($enabled === null) { $enabled = false; }
$grant->enabled->set($enabled);
}
if (self::isParametersSet(['note'])) {
$note = (string)self::getParameter('note');
self::requireType($note, self::type_string());
self::requireMaxLength('note', 65535);
$data['note'] = $note;
$grant->note->set($note);
}
if (self::isParametersSet(['permissions'])) {
$raw = self::getParameter('permissions');
$permissions = null;
// Expect array (already parsed) or JSON string
if (is_string($raw)) {
$decoded = json_decode($raw, true);
if (!is_array($decoded)) {
@@ -162,37 +167,20 @@ class subusersRoute
} else {
$response->error('Invalid permissions type', 400);
}
// Validate each permission is a known key
foreach ($permissions as $perm) {
if (!is_string($perm) || subusers_permission_node_key::tryFrom($perm) === null) {
$response->error('Unknown permission key: ' . (string)$perm, 400);
}
}
$data['permissions'] = $permissions;
$grant->permissions->set($permissions);
}
if (empty($data)) {
$response->error('No updatable fields provided', 400);
}
try {
$grant->update($data);
$response->success(['grant' => $grant->asArray()]);
} catch (Exception $e) {
$response->error('Failed to update grant', 500);
}
}, []);
$this->delete('/subusers/grants/{id}', function () {
global $response;
$id = (int)self::fromRoute('id');
self::requireType($id, self::type_int());
$grant = (new subuser_grants_o())->select($id);
$grant->requireSelected();
try {
$grant->delete();
$response->success(['message' => 'Grant deleted']);
} catch (Exception $e) {
$response->error('Failed to delete grant', 500);
}
}, []);
$response->success($grant->asArray());
},
[
"manage_subuser_grants_other_customer" => "Allow managing subuser grants for customers other than the one associated with the current session's user (use with caution, as this allows modifying permissions for subusers of other companies)"
]
);
$this->get('/subusers/permission-nodes', function () {
global $response;
@@ -222,7 +210,7 @@ class subusersRoute
'nodes' => $nodes,
];
}
$response->success(['permission_nodes' => $out]);
$response->success($out);
}, []);
$this->post('/subusers', function () {