Refactor permission handling to leverage standardized "forbidden" responses and enhance unit test coverage.
This commit is contained in:
@@ -49,7 +49,7 @@ class bookingsRoute
|
||||
}
|
||||
// Check if the user has access to the booking
|
||||
if (!$user->hasAccessToBooking((int)self::getParameter('id'))) {
|
||||
$response->error('You are not allowed to access this booking', 403);
|
||||
$response->forbidden(['list_bookings']);
|
||||
}
|
||||
// Return the booking
|
||||
$response->success(
|
||||
@@ -158,7 +158,7 @@ class bookingsRoute
|
||||
}
|
||||
// Check if the user has access to the booking
|
||||
if (!$user->hasAccessToBooking((int)$this->getParameter('id'))) {
|
||||
$response->error('You are not allowed to update this booking', 403);
|
||||
$response->forbidden(['list_bookings']);
|
||||
}
|
||||
// Check if the optional parameters are set
|
||||
if (self::isParametersSet(['reference_number'])) {
|
||||
@@ -530,4 +530,4 @@ class bookingsRoute
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,7 @@ class cronRoute
|
||||
// Get the post data
|
||||
global $response;
|
||||
// Make sure the user has the SUPERUSER_RUN_CRON permission
|
||||
if (!$this->requirePermission('SUPERUSER_RUN_CRON')) {
|
||||
$response->error('Permission denied', 403);
|
||||
}
|
||||
$this->requirePermission('SUPERUSER_RUN_CRON');
|
||||
// Get the user object
|
||||
$user = (new authentication())->get_user();
|
||||
// Get the post data
|
||||
@@ -49,4 +47,4 @@ class cronRoute
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,11 @@ class departmentGoalsRoute
|
||||
}
|
||||
// Access control: must have all departments unless superuser
|
||||
if (!$isSuperuser && !$hasAllDepartments((array)$goal->departments->value())) {
|
||||
$response->error('You are not allowed to access this goal', 403);
|
||||
$missingDepartments = array_values(array_map('intval', array_diff(array_map('intval', (array)$goal->departments->value()), array_map('intval', $userDepartments))));
|
||||
$response->forbidden([
|
||||
...array_map(static fn($departmentId) => 'department_access_' . $departmentId, $missingDepartments),
|
||||
'superuser'
|
||||
]);
|
||||
}
|
||||
$response->success($goal->asArray());
|
||||
}
|
||||
@@ -115,7 +119,10 @@ class departmentGoalsRoute
|
||||
$goalDepartments = array_map(fn($d) => (int)$d->id, $departments);
|
||||
$missing = array_diff($goalDepartments, array_map('intval', $userDepartments));
|
||||
if (count($missing) > 0) {
|
||||
$response->error('You are not allowed to create goals for one or more selected departments', 403);
|
||||
$response->forbidden([
|
||||
...array_map(static fn($departmentId) => 'department_access_' . (int)$departmentId, $missing),
|
||||
'superuser'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +166,11 @@ class departmentGoalsRoute
|
||||
|
||||
// If not superuser or creator, require access to existing goal departments
|
||||
if (!$isSuperuser && !$isCreator && !$hasAllDepartments((array)$goal->departments->value(), $userDepartments)) {
|
||||
$response->error('You are not allowed to update this goal', 403);
|
||||
$missingDepartments = array_values(array_map('intval', array_diff(array_map('intval', (array)$goal->departments->value()), array_map('intval', $userDepartments))));
|
||||
$response->forbidden([
|
||||
...array_map(static fn($departmentId) => 'department_access_' . $departmentId, $missingDepartments),
|
||||
'superuser'
|
||||
]);
|
||||
}
|
||||
|
||||
$dataToUpdate = [];
|
||||
@@ -177,7 +188,10 @@ class departmentGoalsRoute
|
||||
if (!$isSuperuser && !$isCreator) {
|
||||
$missing = array_diff(array_map('intval', $departmentsInput), array_map('intval', $userDepartments));
|
||||
if (count($missing) > 0) {
|
||||
$response->error('You are not allowed to assign one or more selected departments to this goal', 403);
|
||||
$response->forbidden([
|
||||
...array_map(static fn($departmentId) => 'department_access_' . (int)$departmentId, $missing),
|
||||
'superuser'
|
||||
]);
|
||||
}
|
||||
}
|
||||
// store as ids array in column
|
||||
@@ -247,7 +261,11 @@ class departmentGoalsRoute
|
||||
$hasAll = count(array_diff(array_map('intval', $goalDepartments), array_map('intval', $userDepartments))) === 0;
|
||||
|
||||
if (!$isSuperuser && !$isCreator && !$hasAll) {
|
||||
$response->error('You are not allowed to delete this goal', 403);
|
||||
$missingDepartments = array_values(array_map('intval', array_diff(array_map('intval', $goalDepartments), array_map('intval', $userDepartments))));
|
||||
$response->forbidden([
|
||||
...array_map(static fn($departmentId) => 'department_access_' . $departmentId, $missingDepartments),
|
||||
'superuser'
|
||||
]);
|
||||
}
|
||||
$goal->delete();
|
||||
$response->success(['message' => 'Deleted']);
|
||||
@@ -282,7 +300,11 @@ class departmentGoalsRoute
|
||||
$goalDepartments = (array)$goal->departments->value();
|
||||
$hasAll = count(array_diff(array_map('intval', $goalDepartments), array_map('intval', $userDepartments))) === 0;
|
||||
if (!$isSuperuser && !$hasAll) {
|
||||
$response->error('You are not allowed to send progress alerts for this goal', 403);
|
||||
$missingDepartments = array_values(array_map('intval', array_diff(array_map('intval', $goalDepartments), array_map('intval', $userDepartments))));
|
||||
$response->forbidden([
|
||||
...array_map(static fn($departmentId) => 'department_access_' . $departmentId, $missingDepartments),
|
||||
'superuser'
|
||||
]);
|
||||
}
|
||||
|
||||
// Rebuild criteria from stored array
|
||||
|
||||
@@ -40,7 +40,7 @@ class departmentSelfserveConditionRulesRoute
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if ($condition_o->exists()) {
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, (int)$condition_o->department->value()) && !$this->hasPermission('view_all_department_selfserve_condition_rules')) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$condition_o->department->value(), ['view_all_department_selfserve_condition_rules']);
|
||||
}
|
||||
}
|
||||
$response->success($rules_o->asArray());
|
||||
@@ -59,7 +59,7 @@ class departmentSelfserveConditionRulesRoute
|
||||
if ($condition_o->exists()) {
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, (int)$condition_o->department->value()) && !$this->hasPermission('view_all_department_selfserve_condition_rules')) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$condition_o->department->value(), ['view_all_department_selfserve_condition_rules']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,7 @@ class departmentSelfserveConditionRulesRoute
|
||||
}
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, (int)$condition_o->department->value())) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$condition_o->department->value());
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -167,7 +167,7 @@ class departmentSelfserveConditionRulesRoute
|
||||
$condition_o->select((int)$rule_o->condition_id->value());
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if ($condition_o->exists() && !$this->canAccessDepartment($authorized_department_ids, (int)$condition_o->department->value())) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$condition_o->department->value());
|
||||
}
|
||||
|
||||
if ($response->isRequestParameterSet('condition_id')) {
|
||||
@@ -178,7 +178,7 @@ class departmentSelfserveConditionRulesRoute
|
||||
$response->error('Target condition not found', 404);
|
||||
}
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, (int)$new_condition_o->department->value())) {
|
||||
$response->error('You do not have access to the target department', 403);
|
||||
$this->forbidDepartmentAccess((int)$new_condition_o->department->value());
|
||||
}
|
||||
$rule_o->condition_id->update($new_condition_id);
|
||||
}
|
||||
@@ -231,7 +231,7 @@ class departmentSelfserveConditionRulesRoute
|
||||
$condition_o->select((int)$rule_o->condition_id->value());
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if ($condition_o->exists() && !$this->canAccessDepartment($authorized_department_ids, (int)$condition_o->department->value())) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$condition_o->department->value());
|
||||
}
|
||||
|
||||
$rule_o->delete();
|
||||
|
||||
@@ -37,7 +37,7 @@ class departmentSelfserveConditionsRoute
|
||||
if ($conditions_o->exists()) {
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, (int)$conditions_o->department->value())) {
|
||||
if (!$has_view_all_permission) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$conditions_o->department->value(), ['view_all_department_selfserve_conditions']);
|
||||
}
|
||||
}
|
||||
$response->success($conditions_o->asArray());
|
||||
@@ -50,7 +50,7 @@ class departmentSelfserveConditionsRoute
|
||||
if (self::isParametersSet(['department'])) {
|
||||
$requested_department = (int)self::getParameter('department');
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, $requested_department) && !$has_view_all_permission) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess($requested_department, ['view_all_department_selfserve_conditions']);
|
||||
}
|
||||
$filters['department'] = $requested_department;
|
||||
} else {
|
||||
@@ -125,7 +125,7 @@ class departmentSelfserveConditionsRoute
|
||||
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, $department)) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess($department);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -171,13 +171,13 @@ class departmentSelfserveConditionsRoute
|
||||
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, (int)$condition_o->department->value())) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$condition_o->department->value());
|
||||
}
|
||||
|
||||
if ($response->isRequestParameterSet('department')) {
|
||||
$new_department = (int)$response->getRequestParameter('department');
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, $new_department)) {
|
||||
$response->error('You do not have access to the target department', 403);
|
||||
$this->forbidDepartmentAccess($new_department);
|
||||
}
|
||||
$condition_o->department->update($new_department);
|
||||
}
|
||||
@@ -232,7 +232,7 @@ class departmentSelfserveConditionsRoute
|
||||
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, (int)$condition_o->department->value())) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$condition_o->department->value());
|
||||
}
|
||||
|
||||
$condition_o->delete();
|
||||
|
||||
@@ -37,7 +37,7 @@ class departmentSelfserveQuestionsRoute
|
||||
if ($questions_o->exists()) {
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, (int)$questions_o->department->value())) {
|
||||
if (!$has_view_all_permission) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$questions_o->department->value(), ['view_all_department_selfserve_questions']);
|
||||
}
|
||||
}
|
||||
$response->success($questions_o->asArray());
|
||||
@@ -50,7 +50,7 @@ class departmentSelfserveQuestionsRoute
|
||||
if (self::isParametersSet(['department'])) {
|
||||
$requested_department = (int)self::getParameter('department');
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, $requested_department) && !$has_view_all_permission) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess($requested_department, ['view_all_department_selfserve_questions']);
|
||||
}
|
||||
$filters['department'] = $requested_department;
|
||||
} else {
|
||||
@@ -116,7 +116,7 @@ class departmentSelfserveQuestionsRoute
|
||||
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, $department)) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess($department);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -160,13 +160,13 @@ class departmentSelfserveQuestionsRoute
|
||||
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, (int)$question_o->department->value())) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$question_o->department->value());
|
||||
}
|
||||
|
||||
if (self::isParametersSet(['department'])) {
|
||||
$new_department = (int)self::getParameter('department');
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, $new_department)) {
|
||||
$response->error('You do not have access to the new department', 403);
|
||||
$this->forbidDepartmentAccess($new_department);
|
||||
}
|
||||
$question_o->department->set($new_department);
|
||||
}
|
||||
@@ -218,7 +218,7 @@ class departmentSelfserveQuestionsRoute
|
||||
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, (int)$question_o->department->value())) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$question_o->department->value());
|
||||
}
|
||||
|
||||
$question_o->delete();
|
||||
|
||||
@@ -41,7 +41,7 @@ class departmentSelfserveTasksRoute
|
||||
if ($tasks_o->exists()) {
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, (int)$tasks_o->department->value())) {
|
||||
if (!$has_view_all_permission) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$tasks_o->department->value(), ['view_all_department_selfserve_tasks']);
|
||||
}
|
||||
}
|
||||
$response->success($tasks_o->asArray());
|
||||
@@ -54,7 +54,7 @@ class departmentSelfserveTasksRoute
|
||||
if (self::isParametersSet(['department'])) {
|
||||
$requested_department = (int)self::getParameter('department');
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, $requested_department) && !$has_view_all_permission) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess($requested_department, ['view_all_department_selfserve_tasks']);
|
||||
}
|
||||
$filters['department'] = $requested_department;
|
||||
} else {
|
||||
@@ -195,7 +195,7 @@ class departmentSelfserveTasksRoute
|
||||
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, $department)) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess($department);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -243,13 +243,13 @@ class departmentSelfserveTasksRoute
|
||||
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, (int)$task_o->department->value())) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$task_o->department->value());
|
||||
}
|
||||
|
||||
if (self::isParametersSet(['department'])) {
|
||||
$new_department = (int)self::getParameter('department');
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, $new_department)) {
|
||||
$response->error('You do not have access to the new department', 403);
|
||||
$this->forbidDepartmentAccess($new_department);
|
||||
}
|
||||
$task_o->department->set($new_department);
|
||||
}
|
||||
@@ -372,7 +372,7 @@ class departmentSelfserveTasksRoute
|
||||
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, (int)$task_o->department->value())) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$task_o->department->value());
|
||||
}
|
||||
|
||||
$task_o->delete();
|
||||
@@ -408,7 +408,7 @@ class departmentSelfserveTasksRoute
|
||||
$has_view_all_permission = $this->hasPermission('view_all_department_selfserve_tasks');
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, (int)$task_o->department->value())) {
|
||||
if (!$has_view_all_permission) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$task_o->department->value(), ['view_all_department_selfserve_tasks']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,7 +454,7 @@ class departmentSelfserveTasksRoute
|
||||
$has_view_all_permission = $this->hasPermission('view_all_department_selfserve_tasks');
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, (int)$task_o->department->value())) {
|
||||
if (!$has_view_all_permission) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$task_o->department->value(), ['view_all_department_selfserve_tasks']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,7 +491,7 @@ class departmentSelfserveTasksRoute
|
||||
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, (int)$task_o->department->value())) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$task_o->department->value());
|
||||
}
|
||||
|
||||
$attachment_store = new attachment_store();
|
||||
@@ -536,7 +536,7 @@ class departmentSelfserveTasksRoute
|
||||
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!$this->canAccessDepartment($authorized_department_ids, (int)$task_o->department->value())) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$task_o->department->value());
|
||||
}
|
||||
|
||||
$task_o->removeAttachment($attachment_id);
|
||||
|
||||
@@ -35,7 +35,7 @@ class departmentSelfserveVehicleConditionsRoute
|
||||
$has_own = $user->hasPermission('list_own_department_selfserve_vehicle_conditions');
|
||||
|
||||
if (!$has_global && !$has_own) {
|
||||
$response->error('Permission denied', 403);
|
||||
$response->forbidden(['list_department_selfserve_vehicle_conditions', 'list_own_department_selfserve_vehicle_conditions']);
|
||||
}
|
||||
|
||||
(new logs_o())->add('department_selfserve_vehicle_conditions', 'global', 1, $user->id, 'LIST_VEHICLE_CONDITIONS', 'User listed department self-serve vehicle conditions');
|
||||
@@ -49,11 +49,11 @@ class departmentSelfserveVehicleConditionsRoute
|
||||
if ($has_global) {
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!in_array((int)$conditions_o->department->value(), $authorized_department_ids, true)) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$conditions_o->department->value());
|
||||
}
|
||||
} else {
|
||||
if ((int)$conditions_o->customer_id->value() !== $customer_number) {
|
||||
$response->error('You do not have access to this condition', 403);
|
||||
$response->forbidden(['list_department_selfserve_vehicle_conditions']);
|
||||
}
|
||||
}
|
||||
$response->success($conditions_o->asArray());
|
||||
@@ -68,7 +68,7 @@ class departmentSelfserveVehicleConditionsRoute
|
||||
if (self::isParametersSet(['department'])) {
|
||||
$requested_department = (int)self::getParameter('department');
|
||||
if (!in_array($requested_department, $authorized_department_ids, true)) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess($requested_department);
|
||||
}
|
||||
$filters['department'] = $requested_department;
|
||||
} else {
|
||||
@@ -123,7 +123,7 @@ class departmentSelfserveVehicleConditionsRoute
|
||||
$has_global = $user->hasPermission('list_department_selfserve_vehicle_conditions');
|
||||
$has_own = $user->hasPermission('list_own_department_selfserve_vehicle_conditions');
|
||||
if (!$has_global && !$has_own) {
|
||||
$response->error('Permission denied', 403);
|
||||
$response->forbidden(['list_department_selfserve_vehicle_conditions', 'list_own_department_selfserve_vehicle_conditions']);
|
||||
}
|
||||
|
||||
self::requireParameters(['lane_id', 'reg']);
|
||||
@@ -133,7 +133,7 @@ class departmentSelfserveVehicleConditionsRoute
|
||||
$lane = $this->assertLaneAccess($user, $lane_id, $has_global);
|
||||
$customer_number = null;
|
||||
if (!$has_global && $has_own) {
|
||||
$vehicle = $this->assertOwnVehicle($user, $reg);
|
||||
$vehicle = $this->assertOwnVehicle($user, $reg, 'list_department_selfserve_vehicle_conditions');
|
||||
$customer_number = (int)$vehicle->customer_id->value();
|
||||
}
|
||||
|
||||
@@ -157,14 +157,14 @@ class departmentSelfserveVehicleConditionsRoute
|
||||
$has_global = $user->hasPermission('list_department_selfserve_vehicle_conditions');
|
||||
$has_own = $user->hasPermission('list_own_department_selfserve_vehicle_conditions');
|
||||
if (!$has_global && !$has_own) {
|
||||
$response->error('Permission denied', 403);
|
||||
$response->forbidden(['list_department_selfserve_vehicle_conditions', 'list_own_department_selfserve_vehicle_conditions']);
|
||||
}
|
||||
|
||||
$flow = $this->getWashFlow();
|
||||
try {
|
||||
if (self::isParametersSet(['session_id'])) {
|
||||
$summary = $flow->getSessionSummary((int)self::getParameter('session_id'));
|
||||
$this->assertSummaryAccess($user, $summary, $has_global);
|
||||
$this->assertSummaryAccess($user, $summary, $has_global, 'list_department_selfserve_vehicle_conditions');
|
||||
$response->success($summary);
|
||||
}
|
||||
|
||||
@@ -174,11 +174,11 @@ class departmentSelfserveVehicleConditionsRoute
|
||||
|
||||
$this->assertLaneAccess($user, $lane_id, $has_global);
|
||||
if (!$has_global && $has_own) {
|
||||
$this->assertOwnVehicle($user, $reg);
|
||||
$this->assertOwnVehicle($user, $reg, 'list_department_selfserve_vehicle_conditions');
|
||||
}
|
||||
|
||||
$summary = $flow->getLatestSessionSummary($lane_id, $reg);
|
||||
$this->assertSummaryAccess($user, $summary, $has_global);
|
||||
$this->assertSummaryAccess($user, $summary, $has_global, 'list_department_selfserve_vehicle_conditions');
|
||||
$response->success($summary);
|
||||
} catch (\RuntimeException $e) {
|
||||
$response->error($e->getMessage(), 404);
|
||||
@@ -202,7 +202,7 @@ class departmentSelfserveVehicleConditionsRoute
|
||||
$has_own = $user->hasPermission('add_own_department_selfserve_vehicle_conditions');
|
||||
|
||||
if (!$has_global && !$has_own) {
|
||||
$response->error('Permission denied', 403);
|
||||
$response->forbidden(['add_department_selfserve_vehicle_conditions', 'add_own_department_selfserve_vehicle_conditions']);
|
||||
}
|
||||
|
||||
$department = (int)$response->getRequestParameter('department');
|
||||
@@ -219,11 +219,11 @@ class departmentSelfserveVehicleConditionsRoute
|
||||
$customer_id = $response->isRequestParameterSet('customer_id') ? (int)$response->getRequestParameter('customer_id') : null;
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!in_array($department, $authorized_department_ids, true)) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess($department);
|
||||
}
|
||||
} else {
|
||||
$customer_id = (int)$user->customer_number->value();
|
||||
$vehicle_o = $this->assertOwnVehicle($user, $reg);
|
||||
$vehicle_o = $this->assertOwnVehicle($user, $reg, 'add_department_selfserve_vehicle_conditions');
|
||||
$customer_id = (int)$vehicle_o->customer_id->value();
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ class departmentSelfserveVehicleConditionsRoute
|
||||
$has_own = $user->hasPermission('update_own_department_selfserve_vehicle_conditions');
|
||||
|
||||
if (!$has_global && !$has_own) {
|
||||
$response->error('Permission denied', 403);
|
||||
$response->forbidden(['update_department_selfserve_vehicle_conditions', 'update_own_department_selfserve_vehicle_conditions']);
|
||||
}
|
||||
|
||||
$id = (int)$response->getRequestParameter('id');
|
||||
@@ -277,11 +277,11 @@ class departmentSelfserveVehicleConditionsRoute
|
||||
if ($has_global) {
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!in_array((int)$condition_o->department->value(), $authorized_department_ids, true)) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$condition_o->department->value());
|
||||
}
|
||||
} else {
|
||||
if ((int)$condition_o->customer_id->value() !== $customer_number) {
|
||||
$response->error('You do not have access to this condition', 403);
|
||||
$response->forbidden(['update_department_selfserve_vehicle_conditions']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,7 +290,7 @@ class departmentSelfserveVehicleConditionsRoute
|
||||
if ($has_global) {
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!in_array($new_department, $authorized_department_ids, true)) {
|
||||
$response->error('You do not have access to the target department', 403);
|
||||
$this->forbidDepartmentAccess($new_department);
|
||||
}
|
||||
}
|
||||
$condition_o->department->update($new_department);
|
||||
@@ -301,9 +301,9 @@ class departmentSelfserveVehicleConditionsRoute
|
||||
if ($response->isRequestParameterSet('reg')) {
|
||||
$new_reg = selfserve::standardize_registration((string)$response->getRequestParameter('reg'));
|
||||
if (!$has_global && $has_own) {
|
||||
$vehicle_o = $this->assertOwnVehicle($user, $new_reg);
|
||||
$vehicle_o = $this->assertOwnVehicle($user, $new_reg, 'update_department_selfserve_vehicle_conditions');
|
||||
if ((int)$vehicle_o->customer_id->value() !== $customer_number) {
|
||||
$response->error('You do not own this vehicle', 403);
|
||||
$response->forbidden(['update_department_selfserve_vehicle_conditions']);
|
||||
}
|
||||
}
|
||||
$condition_o->reg->update($new_reg);
|
||||
@@ -317,7 +317,7 @@ class departmentSelfserveVehicleConditionsRoute
|
||||
if ($response->isRequestParameterSet('customer_id')) {
|
||||
$new_customer_id = (int)$response->getRequestParameter('customer_id');
|
||||
if (!$has_global && $has_own && $new_customer_id !== $customer_number) {
|
||||
$response->error('You cannot change the customer ID to another customer', 403);
|
||||
$response->forbidden(['update_department_selfserve_vehicle_conditions']);
|
||||
}
|
||||
$condition_o->customer_id->update($new_customer_id);
|
||||
}
|
||||
@@ -355,7 +355,7 @@ class departmentSelfserveVehicleConditionsRoute
|
||||
$has_own = $user->hasPermission('delete_own_department_selfserve_vehicle_conditions');
|
||||
|
||||
if (!$has_global && !$has_own) {
|
||||
$response->error('Permission denied', 403);
|
||||
$response->forbidden(['delete_department_selfserve_vehicle_conditions', 'delete_own_department_selfserve_vehicle_conditions']);
|
||||
}
|
||||
|
||||
$id = (int)$response->getRequestParameter('id');
|
||||
@@ -372,11 +372,11 @@ class departmentSelfserveVehicleConditionsRoute
|
||||
if ($has_global) {
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!in_array((int)$condition_o->department->value(), $authorized_department_ids, true)) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$condition_o->department->value());
|
||||
}
|
||||
} else {
|
||||
if ((int)$condition_o->customer_id->value() !== (int)$user->customer_number->value()) {
|
||||
$response->error('You do not have access to this condition', 403);
|
||||
$response->forbidden(['delete_department_selfserve_vehicle_conditions']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,26 +420,26 @@ class departmentSelfserveVehicleConditionsRoute
|
||||
if ($hasGlobalPermission) {
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
if (!in_array((int)$lane->department->value(), $authorized_department_ids, true)) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess((int)$lane->department->value());
|
||||
}
|
||||
}
|
||||
|
||||
return $lane;
|
||||
}
|
||||
|
||||
private function assertOwnVehicle(object $user, string $reg): customer_vehicles_o
|
||||
private function assertOwnVehicle(object $user, string $reg, ?string $elevatedPermission = null): customer_vehicles_o
|
||||
{
|
||||
global $response;
|
||||
|
||||
$vehicle_o = (new customer_vehicles_o())->selectByPlate($reg);
|
||||
if (!$vehicle_o->exists() || (int)$vehicle_o->customer_id->value() !== (int)$user->customer_number->value()) {
|
||||
$response->error('You do not own this vehicle', 403);
|
||||
$response->forbidden([$elevatedPermission ?? 'list_department_selfserve_vehicle_conditions']);
|
||||
}
|
||||
|
||||
return $vehicle_o;
|
||||
}
|
||||
|
||||
private function assertSummaryAccess(object $user, array $summary, bool $hasGlobalPermission): void
|
||||
private function assertSummaryAccess(object $user, array $summary, bool $hasGlobalPermission, string $elevatedPermission): void
|
||||
{
|
||||
global $response;
|
||||
|
||||
@@ -447,7 +447,7 @@ class departmentSelfserveVehicleConditionsRoute
|
||||
$authorized_department_ids = $user->getGroup()->getDepartments();
|
||||
$lane_department = (int)($summary['lane']['department'] ?? 0);
|
||||
if (!in_array($lane_department, $authorized_department_ids, true)) {
|
||||
$response->error('You do not have access to this department', 403);
|
||||
$this->forbidDepartmentAccess($lane_department);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -463,6 +463,6 @@ class departmentSelfserveVehicleConditionsRoute
|
||||
return;
|
||||
}
|
||||
|
||||
$response->error('You do not have access to this wash summary', 403);
|
||||
$response->forbidden([$elevatedPermission]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,7 @@ class intimidateRoute
|
||||
// Get the post data
|
||||
global $response;
|
||||
// Make sure the user has the SUPERUSER_INTIMIDATE permission
|
||||
if (!$this->requirePermission('SUPERUSER_INTIMIDATE')) {
|
||||
$response->error('Permission denied', 403);
|
||||
}
|
||||
$this->requirePermission('SUPERUSER_INTIMIDATE');
|
||||
// Get the user object
|
||||
$user = (new authentication())->get_user();
|
||||
// Get the post data
|
||||
@@ -42,4 +40,4 @@ class intimidateRoute
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ class orderItemsRoute
|
||||
$hasPermission = $this->hasPermission('list_own_order_items');
|
||||
$hasAttribute = $user->showPricesOnBookingPage(); // Check if the user has the attribute to show prices on the booking page
|
||||
if (!$hasPermission && !$hasAttribute) {
|
||||
$response->error('You do not have permission to list order items, neither your own nor all orders', 403);
|
||||
$response->forbidden(['list_own_order_items', 'list_order_items']);
|
||||
}
|
||||
} else {
|
||||
$this->requirePermission('list_order_items');
|
||||
@@ -223,4 +223,4 @@ class orderItemsRoute
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@ class ordersRoute
|
||||
if (!$has_permission_other) {
|
||||
$effectiveCustomer = self::resolveEffectiveCustomerNumber();
|
||||
if ($effectiveCustomer === null || (int)$order->customer_id->value() !== (int)$effectiveCustomer) {
|
||||
$response->error('You do not have permission to download this order attachment', 403);
|
||||
$response->forbidden([$permission_other->permission]);
|
||||
}
|
||||
}
|
||||
// Get the attachment
|
||||
@@ -368,7 +368,7 @@ class ordersRoute
|
||||
self::requirePermission($permission_own);
|
||||
$effectiveCustomer = self::resolveEffectiveCustomerNumber();
|
||||
if ($effectiveCustomer === null || (int)$order->customer_id->value() !== (int)$effectiveCustomer) {
|
||||
$response->error('You do not have permission to view attachments for this order.', 403);
|
||||
$response->forbidden([$permission_other->permission]);
|
||||
}
|
||||
}
|
||||
// Get the attachments
|
||||
@@ -882,12 +882,12 @@ class ordersRoute
|
||||
if ($subuser_own_path) {
|
||||
$effectiveCustomer = self::resolveEffectiveCustomerNumber();
|
||||
if ($effectiveCustomer === null || (int)$order->customer_id->value() !== (int)$effectiveCustomer) {
|
||||
$response->error('You do not have permission to edit this order', 403);
|
||||
$response->forbidden([$permission_other->permission]);
|
||||
}
|
||||
} else {
|
||||
// Classic own path — compare against authenticated user customer number
|
||||
if ($order->customer_id->value() !== $user->customer_number->value()) {
|
||||
$response->error('You do not have permission to edit this order', 403);
|
||||
$response->forbidden([$permission_other->permission]);
|
||||
}
|
||||
// Ensure classic own-path requires user permission
|
||||
$this->requirePermission('user');
|
||||
@@ -1038,4 +1038,4 @@ class ordersRoute
|
||||
// Optional fields are not checked here, as they are optional and can be empty
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ class subusersRoute
|
||||
// If not admin/department permission, force restrict to effective customer
|
||||
if (!$has_permission_other) {
|
||||
if ($effectiveCustomer === null) {
|
||||
$response->error('Missing customer context. Subusers must provide X-Customer-Number header.', 403);
|
||||
$response->forbidden([$permission_other->permission]);
|
||||
}
|
||||
$filters['billing_customer_number'] = (int)$effectiveCustomer;
|
||||
$hasFilter = true; // ensure we don't fail below
|
||||
@@ -673,4 +673,4 @@ class subusersRoute
|
||||
$response->success(['cvr' => $cvr, 'customer_number' => $results[0]->customerNumber]);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ class systemSearchRoute
|
||||
|
||||
[$allowedTypes, $ownOnlyTypes] = $this->resolveAllowedTypes();
|
||||
if (empty($allowedTypes)) {
|
||||
$response->error('Permission denied. No searchable entity types available for this user.', 403);
|
||||
$response->forbidden($this->searchAccessPermissionCandidates());
|
||||
}
|
||||
|
||||
$permissionsCatalogAll = $this->flattenPermissionCatalog((array)$router->getPermissions());
|
||||
@@ -578,4 +578,27 @@ class systemSearchRoute
|
||||
{
|
||||
return array_keys($this->entityPermissionMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all permission keys that can unlock at least one searchable entity type.
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private function searchAccessPermissionCandidates(): array
|
||||
{
|
||||
$permissions = [];
|
||||
foreach ($this->entityPermissionMap() as $permissionSets) {
|
||||
foreach ((array)($permissionSets['all'] ?? []) as $permission) {
|
||||
if (is_string($permission) && $permission !== '') {
|
||||
$permissions[] = $permission;
|
||||
}
|
||||
}
|
||||
foreach ((array)($permissionSets['own'] ?? []) as $permission) {
|
||||
if (is_string($permission) && $permission !== '') {
|
||||
$permissions[] = $permission;
|
||||
}
|
||||
}
|
||||
}
|
||||
return array_values(array_unique($permissions));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
app_require('traits/route_t.php');
|
||||
|
||||
use traits\route_t;
|
||||
|
||||
class AllowOwnOrDepartmentAccessForbiddenHost
|
||||
{
|
||||
use route_t;
|
||||
|
||||
/** @var array<string, bool> */
|
||||
public array $permissionsByKey = [];
|
||||
public bool $ownContext = true;
|
||||
/** @var array<int, string>|null */
|
||||
public ?array $lastForbidden = null;
|
||||
public bool $departmentAccessChecked = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// no-op for unit tests
|
||||
}
|
||||
|
||||
public function hasPermission(string|\classes\permission_node $permission, int $customer_number = null): bool
|
||||
{
|
||||
$key = is_string($permission) ? $permission : (string)$permission->permission;
|
||||
return (bool)($this->permissionsByKey[$key] ?? false);
|
||||
}
|
||||
|
||||
public function isOwnCustomerContext(int $targetCustomerNumber): bool
|
||||
{
|
||||
return $this->ownContext;
|
||||
}
|
||||
|
||||
public function requireDepartmentAccess(string $department, string|null $permission = null): void
|
||||
{
|
||||
$this->departmentAccessChecked = true;
|
||||
}
|
||||
|
||||
protected function emitForbidden(array $permissions): void
|
||||
{
|
||||
$normalized = [];
|
||||
foreach ($permissions as $permission) {
|
||||
$key = is_string($permission) ? trim($permission) : trim((string)$permission->permission);
|
||||
if ($key !== '') {
|
||||
$normalized[] = $key;
|
||||
}
|
||||
}
|
||||
$this->lastForbidden = array_values(array_unique($normalized));
|
||||
throw new RuntimeException('forbidden');
|
||||
}
|
||||
}
|
||||
|
||||
it('returns both own and elevated permissions when both are missing', function (): void {
|
||||
$host = new AllowOwnOrDepartmentAccessForbiddenHost();
|
||||
$host->permissionsByKey = [
|
||||
'perm_own' => false,
|
||||
'perm_other' => false,
|
||||
];
|
||||
|
||||
expect(fn() => $host->allowOwnOrDepartmentAccess('perm_own', 'perm_other', 101, null))
|
||||
->toThrow(RuntimeException::class, 'forbidden');
|
||||
|
||||
expect($host->lastForbidden)->toBe(['perm_own', 'perm_other']);
|
||||
});
|
||||
|
||||
it('returns only elevated permission when own permission exists but own context fails', function (): void {
|
||||
$host = new AllowOwnOrDepartmentAccessForbiddenHost();
|
||||
$host->permissionsByKey = [
|
||||
'perm_own' => true,
|
||||
'perm_other' => false,
|
||||
];
|
||||
$host->ownContext = false;
|
||||
|
||||
expect(fn() => $host->allowOwnOrDepartmentAccess('perm_own', 'perm_other', 202, null))
|
||||
->toThrow(RuntimeException::class, 'forbidden');
|
||||
|
||||
expect($host->lastForbidden)->toBe(['perm_other']);
|
||||
});
|
||||
|
||||
it('allows elevated permission path without forbidden and validates department access', function (): void {
|
||||
$host = new AllowOwnOrDepartmentAccessForbiddenHost();
|
||||
$host->permissionsByKey = [
|
||||
'perm_own' => false,
|
||||
'perm_other' => true,
|
||||
];
|
||||
|
||||
$allowed = $host->allowOwnOrDepartmentAccess('perm_own', 'perm_other', 303, 77);
|
||||
|
||||
expect($allowed)->toBeTrue();
|
||||
expect($host->departmentAccessChecked)->toBeTrue();
|
||||
expect($host->lastForbidden)->toBeNull();
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
it('routes and trait use forbidden responses for permission and ownership denials', function (): void {
|
||||
$routeTrait = file_get_contents(app_path('traits/route_t.php'));
|
||||
expect($routeTrait)->not->toBeFalse();
|
||||
expect($routeTrait)->toContain('protected function emitForbidden(array $permissions): void');
|
||||
expect($routeTrait)->toContain('$response->forbidden($this->normalizePermissionKeys($permissions));');
|
||||
expect($routeTrait)->toContain('$this->emitForbidden($missingPermissions);');
|
||||
|
||||
$selfserveRoute = file_get_contents(app_path('routes/departmentSelfserveVehicleConditionsRoute.php'));
|
||||
expect($selfserveRoute)->not->toBeFalse();
|
||||
expect($selfserveRoute)->toContain("forbidden(['list_department_selfserve_vehicle_conditions', 'list_own_department_selfserve_vehicle_conditions'])");
|
||||
expect($selfserveRoute)->toContain('forbidDepartmentAccess(');
|
||||
|
||||
$ordersRoute = file_get_contents(app_path('routes/ordersRoute.php'));
|
||||
expect($ordersRoute)->not->toBeFalse();
|
||||
expect($ordersRoute)->toContain('$response->forbidden([$permission_other->permission]);');
|
||||
expect($ordersRoute)->not->toContain("\$response->error('You do not have permission to edit this order', 403);");
|
||||
});
|
||||
@@ -16,6 +16,7 @@ namespace classes {
|
||||
}
|
||||
class response {
|
||||
public function error($msg, $code) { throw new \Exception("Response Error ($code): $msg"); }
|
||||
public function forbidden(array $permissions) { throw new \Exception("Response Error (403): Missing permission(s): " . implode(', ', $permissions)); }
|
||||
public function add_meta($k, $v) {}
|
||||
}
|
||||
class permission_node {
|
||||
|
||||
@@ -321,6 +321,71 @@ trait route_t
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a permission definition to its canonical string key.
|
||||
*/
|
||||
private function permissionKey(string|permission_node $permission): string
|
||||
{
|
||||
return $permission instanceof permission_node
|
||||
? (string)$permission->permission
|
||||
: (string)$permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize and de-duplicate permission keys for forbidden responses.
|
||||
* Accepts string keys and permission_node definitions.
|
||||
*
|
||||
* @param array<int, string|permission_node> $permissions
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private function normalizePermissionKeys(array $permissions): array
|
||||
{
|
||||
$keys = [];
|
||||
foreach ($permissions as $permission) {
|
||||
if ($permission instanceof permission_node) {
|
||||
$key = trim((string)$permission->permission);
|
||||
} elseif (is_string($permission)) {
|
||||
$key = trim($permission);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if ($key !== '') {
|
||||
$keys[] = $key;
|
||||
}
|
||||
}
|
||||
return array_values(array_unique($keys));
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit standardized forbidden response payload with missing permission keys.
|
||||
* Extracted to allow focused unit tests by overriding this method.
|
||||
*
|
||||
* @param array<int, string|permission_node> $permissions
|
||||
*/
|
||||
protected function emitForbidden(array $permissions): void
|
||||
{
|
||||
global $response;
|
||||
$response->forbidden($this->normalizePermissionKeys($permissions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a forbidden response for missing department scope access.
|
||||
* Optionally include bypass permissions if they are relevant and missing.
|
||||
*
|
||||
* @param int $departmentId
|
||||
* @param array<int, string|permission_node> $optionalBypassPermissions
|
||||
*/
|
||||
public function forbidDepartmentAccess(int $departmentId, array $optionalBypassPermissions = []): void
|
||||
{
|
||||
$missing = ['department_access_' . $departmentId];
|
||||
foreach ($optionalBypassPermissions as $permission) {
|
||||
if (!$this->hasPermission($permission)) {
|
||||
$missing[] = $permission;
|
||||
}
|
||||
}
|
||||
$this->emitForbidden($missing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Centralized permission evaluation used by both requirePermission and hasPermission.
|
||||
* - Honors subusers permission nodes without falling back to classic user permissions when a node is defined.
|
||||
@@ -340,7 +405,7 @@ trait route_t
|
||||
if ($resolvedCustomer === null) {
|
||||
(new logs_o())->add('global', 'global', 1, $subuser->id ?? 0, 'PERMISSION_DENIED', 'Missing customer context for subuser permission evaluation: ' . $permission->permission);
|
||||
if ($throwOnDeny) {
|
||||
$response->error('Permission denied. Missing customer context for subuser.', 403);
|
||||
$this->emitForbidden([$permission]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -367,7 +432,7 @@ trait route_t
|
||||
|
||||
if (!$subuser_has_permission && $throwOnDeny) {
|
||||
(new logs_o())->add('global', 'global', 1, $subuser->id ?? 0, 'PERMISSION_DENIED', 'Permission denied via subuser node: ' . $permission->permission . ' (Node: ' . $permission->subusers_node_key->name . ', Customer: ' . $resolvedCustomer . ')');
|
||||
$response->error('Permission denied for subuser. Missing permission: ' . $permission->permission . ' (Customer context: ' . $resolvedCustomer . ')', 403);
|
||||
$this->emitForbidden([$permission]);
|
||||
}
|
||||
return $subuser_has_permission;
|
||||
}
|
||||
@@ -404,7 +469,7 @@ trait route_t
|
||||
|
||||
if (!$allowed && $throwOnDeny) {
|
||||
(new logs_o())->add('global', 'global', 1, $user->id, 'PERMISSION_DENIED', 'Permission denied. Missing permission: ' . $perm_string);
|
||||
$response->error('Permission denied. Missing permission: ' . $perm_string . ' for user: ' . $user->id . ' In group: ' . $user->group_id->value(), 403);
|
||||
$this->emitForbidden([$perm_string]);
|
||||
}
|
||||
return $allowed;
|
||||
} catch (Exception $e) {
|
||||
@@ -528,7 +593,21 @@ trait route_t
|
||||
}
|
||||
|
||||
if (!$allowed) {
|
||||
$response->error($denyMessage ?? 'Permission denied.', 403);
|
||||
$missingPermissions = [];
|
||||
if (!$hasOwn) {
|
||||
$missingPermissions[] = $permissionOwn;
|
||||
}
|
||||
if (!$hasOther) {
|
||||
$missingPermissions[] = $permissionOther;
|
||||
}
|
||||
// In own-scope failures (wrong customer/guard failure), report missing elevated permission only.
|
||||
if ($hasOwn && !$hasOther) {
|
||||
$missingPermissions[] = $permissionOther;
|
||||
}
|
||||
if (count($missingPermissions) === 0) {
|
||||
$missingPermissions[] = $permissionOther;
|
||||
}
|
||||
$this->emitForbidden($missingPermissions);
|
||||
}
|
||||
return $allowed;
|
||||
}
|
||||
@@ -816,4 +895,4 @@ trait route_t
|
||||
// Check if route is the same, or if it matches the regex pattern
|
||||
return $route === $this->route || preg_match($route, $this->route);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user