Harden Sæby demo registration and department scope (#335)
Complete and secure public customer/driver registration, authoritative limited-backoffice department scope, one-time employee QR login, and pricing concurrency for the Sæby demo.
This commit is contained in:
@@ -267,6 +267,18 @@ trait route_t
|
||||
*/
|
||||
public function requireDepartmentAccess(string $department, string|null $permission = null): void
|
||||
{
|
||||
global $response;
|
||||
|
||||
$departmentId = (int)$department;
|
||||
$authenticatedUser = (new authentication())->get_user();
|
||||
if ($authenticatedUser instanceof \objects\users_o) {
|
||||
$managedScope = (new \classes\limited_backoffice_service())
|
||||
->managedEmployeeDepartmentIds($authenticatedUser);
|
||||
if ($managedScope !== null && !in_array($departmentId, $managedScope, true)) {
|
||||
$response->error('Department resource not found', 404);
|
||||
}
|
||||
}
|
||||
|
||||
self::requirePermission('department_access_' . $department . ($permission ? '_' . $permission : ''));
|
||||
}
|
||||
|
||||
@@ -279,9 +291,80 @@ trait route_t
|
||||
*/
|
||||
public function hasDepartmentAccess(string $department, string|null $permission = null): bool
|
||||
{
|
||||
$departmentId = (int)$department;
|
||||
$authenticatedUser = (new authentication())->get_user();
|
||||
if ($authenticatedUser instanceof \objects\users_o) {
|
||||
$managedScope = (new \classes\limited_backoffice_service())
|
||||
->managedEmployeeDepartmentIds($authenticatedUser);
|
||||
if ($managedScope !== null && !in_array($departmentId, $managedScope, true)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return self::hasPermission('department_access_' . $department . ($permission ? '_' . $permission : ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies department scoping only to users managed by limited backoffice.
|
||||
* Other user types retain the route's existing permission contract.
|
||||
*
|
||||
* @return array<int, int>|null
|
||||
*/
|
||||
public function limitedBackofficeDepartmentScope(object $user): ?array
|
||||
{
|
||||
if (!$user instanceof \objects\users_o) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (new \classes\limited_backoffice_service())->managedEmployeeDepartmentIds($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authoritative managed assignment for limited-backoffice
|
||||
* employees and preserves the legacy group-derived scope for every other
|
||||
* account type.
|
||||
*
|
||||
* @return array<int, int>
|
||||
*/
|
||||
public function effectiveDepartmentIds(object $user): array
|
||||
{
|
||||
$managedScope = $this->limitedBackofficeDepartmentScope($user);
|
||||
if ($managedScope !== null) {
|
||||
$departmentIds = array_values(array_unique(array_filter(
|
||||
array_map('intval', $managedScope),
|
||||
static fn (int $departmentId): bool => $departmentId > 0
|
||||
)));
|
||||
|
||||
// Empty filter arrays are treated as "no filter" by legacy query
|
||||
// helpers. A corrupt or missing managed assignment must deny all,
|
||||
// never widen a limited employee to global department access.
|
||||
return $departmentIds === [] ? [0] : $departmentIds;
|
||||
}
|
||||
|
||||
if (!method_exists($user, 'getGroup')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_values(array_unique(array_map(
|
||||
'intval',
|
||||
(array)$user->getGroup()->getDepartments()
|
||||
)));
|
||||
}
|
||||
|
||||
public function requireLimitedBackofficeDepartmentAccess(object $user, int $departmentId): void
|
||||
{
|
||||
global $response;
|
||||
|
||||
$departmentScope = $this->limitedBackofficeDepartmentScope($user);
|
||||
if ($departmentScope === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!in_array($departmentId, $departmentScope, true)) {
|
||||
$response->error('Department resource not found', 404);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parameters as an array from the request
|
||||
* @return array
|
||||
|
||||
Reference in New Issue
Block a user