Add subuser permission evaluation system and extend subuser-related route handling

- Introduce `hasPermission` method in `subusers_o` for permission checks tied to customer context.
- Update `/subusers/me` route to return subuser grants with normalized permissions and metadata.
- Add `get_subuser_customer_number_target` in `authentication` to resolve customer context from request headers.
- Refactor route-level permission checks to handle subuser grants dynamically.
- Introduce CLI test scripts for subuser grants and permission node mappings.
- Add test coverage for subuser grants and permission nodes in new test classes.
This commit is contained in:
Jeppe Bundgaard
2026-02-12 15:29:43 +01:00
parent 6375bc7f12
commit 497ef1496b
9 changed files with 297 additions and 113 deletions
+26 -32
View File
@@ -472,43 +472,37 @@ class subusersRoute
$response->success($objects);
}, []);
$this->get('/subusers/{id}', function () {
$this->get('/subusers/me', function () {
global $response;
$user = (new authentication())->get_user();
if ($user === false) {
$subuser = (new authentication())->get_subuser();
if ($subuser === false) {
$response->error('Unauthorized', 401);
}
$customerNumber = (int)$user->customer_number->value();
if ($customerNumber === 0) {
$response->error('Unauthorized', 401);
}
$id = (int)self::fromRoute('id');
self::requireType($id, self::type_int());
$subuser = (new subusers_o())->select($id);
if (!$subuser->exists()) {
$response->error('Subuser not found', 404);
}
$grants = (new subuser_grants_o())->getFieldsWhere([
'subuser' => $subuser->id,
'enabled' => 1,
'deleted_at' => null,
], ['permissions', 'billing_customer_number']);
// Check visibility via grants
$permissions = (new subuser_grants_o())->getGrantsForSubuserAndCustomer($id, $customerNumber);
if (empty($permissions)) {
// Hide existence if not visible
$response->error('Subuser not found', 404);
}
$response->success([
'id' => (int)$subuser->id,
'username' => $subuser->username->value(),
'name' => $subuser->name->value(),
'email' => $subuser->email->value(),
'phone_country_code' => (int)$subuser->phone_country_code->value(),
'phone' => (int)$subuser->phone->value(),
'created_at' => $subuser->created_at->value(),
'updated_at' => $subuser->updated_at->value(),
'suspended_at' => $subuser->suspended_at->value(),
'permissions' => $permissions,
]);
$result = [
"id" => (int)$subuser->id,
"username" => $subuser->username->value(),
"name" => $subuser->name->value(),
"email" => $subuser->email->value(),
"phone_country_code" => $subuser->phone_country_code->value() !== null ? (int)$subuser->phone_country_code->value() : null,
"phone" => $subuser->phone->value() !== null ? (int)$subuser->phone->value() : null,
"grants" => array_map(function ($grant) {
return [
'billing_customer_number' => (int)$grant['billing_customer_number'],
'permissions' => json_decode($grant['permissions'], true) ?: [],
];
}, $grants),
"created_at" => $subuser->created_at->value() ?? null,
"updated_at" => $subuser->updated_at->value() ?? null,
"suspended_at" => $subuser->suspended_at->value() ?? null,
];
$response->success($result);
}, []);
}
}