Refactor route_t and orderBookingRoute to simplify permission handling

- Add reusable helpers: `isOwnCustomerContext`, `resolveEffectiveCustomerNumber`, and `allowOwnOrDepartmentAccess` in `route_t` for streamlined permission checks.
- Replace inlined permission logic in `orderBookingRoute` with common helpers for creating, viewing, editing, and deleting bookings.
- Localize permission names, descriptions, and labels to Danish across relevant modules.
- Improve error handling and simplify context resolution for subuser and department-level permissions.
This commit is contained in:
Jeppe Bundgaard
2026-02-12 15:43:02 +01:00
parent cd894b4e3b
commit f265a83034
6 changed files with 173 additions and 159 deletions
+100
View File
@@ -395,6 +395,106 @@ trait route_t
return $this->evaluatePermission($permission, $customer_number, false);
}
/**
* Determine if the current principal (classic user or subuser) acts on their own customer context.
* - For classic users: compares against the authenticated user's customer_number
* - For subusers: compares against X-Customer-Number target header
*/
public function isOwnCustomerContext(int $targetCustomerNumber): bool
{
try {
$auth = new authentication();
$sub = $auth->get_subuser();
if ($sub !== false) {
$tgt = $auth->get_subuser_customer_number_target();
return ((int)$tgt === (int)$targetCustomerNumber);
}
$user = $auth->get_user();
if ($user !== false) {
return ((int)$user->customer_number->value() === (int)$targetCustomerNumber);
}
} catch (Exception) {
// fall through
}
return false;
}
/**
* Resolve the most relevant customer number for the current request context.
* Priority: classic user -> X-Customer-Number header -> query/body customer_number
*/
public function resolveEffectiveCustomerNumber(): ?int
{
try {
$auth = new authentication();
$user = $auth->get_user();
if ($user !== false && isset($user->customer_number)) {
return (int)$user->customer_number->value();
}
$sub = $auth->get_subuser();
if ($sub !== false) {
$tgt = $auth->get_subuser_customer_number_target();
if ($tgt !== false && $tgt !== null) return (int)$tgt;
}
// Fallbacks
if (isset($_GET['customer_number'])) return (int)$_GET['customer_number'];
if (isset($_POST['customer_number'])) return (int)$_POST['customer_number'];
} catch (Exception) {
// ignore
}
return null;
}
/**
* Common pattern helper: allow via own-permission (with optional guard) or via department/admin permission.
* If neither path allows, respond with 403 and provided message.
*
* @param string|permission_node $permissionOwn Own-scope permission (often linked to a subuser node)
* @param string|permission_node $permissionOther Department/admin permission
* @param int|null $targetCustomerNumber Customer number to validate "own" scope against (null means list context)
* @param int|null $departmentId Department id for admin path (will be validated when provided)
* @param callable|null $ownGuard Optional additional guard for own path. Return true to allow, false to deny own-path.
* @param string|null $denyMessage Message to return on deny (defaults to generic)
* @return bool True if access is allowed (also throws on deny)
*/
public function allowOwnOrDepartmentAccess(
string|permission_node $permissionOwn,
string|permission_node $permissionOther,
?int $targetCustomerNumber,
?int $departmentId,
?callable $ownGuard = null,
?string $denyMessage = null
): bool {
global $response;
$allowed = false;
$hasOwn = $this->hasPermission($permissionOwn);
$hasOther = $this->hasPermission($permissionOther);
// Try own path first
if ($hasOwn) {
$isOwnContext = ($targetCustomerNumber === null) ? true : $this->isOwnCustomerContext((int)$targetCustomerNumber);
if ($isOwnContext) {
$guardOk = $ownGuard ? (bool)call_user_func($ownGuard) : true;
if ($guardOk) {
$allowed = true;
}
}
}
// Fallback to department/admin path
if (!$allowed && $hasOther) {
if ($departmentId !== null) {
$this->requireDepartmentAccess((int)$departmentId);
}
$allowed = true;
}
if (!$allowed) {
$response->error($denyMessage ?? 'Permission denied.', 403);
}
return $allowed;
}
/**
* Require parameter to be a positive integer
* @param int $value The value to check