diff --git a/services/nginx/app/routes/departmentGoalsRoute.php b/services/nginx/app/routes/departmentGoalsRoute.php index c184e2ab..cce22a7f 100644 --- a/services/nginx/app/routes/departmentGoalsRoute.php +++ b/services/nginx/app/routes/departmentGoalsRoute.php @@ -20,6 +20,21 @@ class departmentGoalsRoute global /** @var response $response */ $response; $this->requirePermission('goals_department_list'); + // Current user and access helpers + $user = (new authentication())->get_user(); + if (!$user) { + $response->error('Unauthorized', 401); + } + $isSuperuser = method_exists($user, 'hasPermission') && $user->hasPermission('superuser'); + $userDepartments = method_exists($user, 'getGroup') ? (array)$user->getGroup()->getDepartments() : []; + + $hasAllDepartments = function (array $goalDepartments) use ($userDepartments): bool { + // Ensure ints + $goalDepartments = array_map('intval', $goalDepartments); + $userDepartments = array_map('intval', $userDepartments); + return count(array_diff($goalDepartments, $userDepartments)) === 0; + }; + $goals = new department_goals_o(); // Single by id @@ -32,17 +47,27 @@ class departmentGoalsRoute if (!$goal->exists()) { $response->error('Goal not found', 404); } + // 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); + } $response->success($goal->asArray()); } // List with pagination if set (page, limit, search, filters, order supported by db_object_t) - $response->success( - $goals->listObjectsWithPaginationIfSet( - function ($row) { - return (new department_goals_o())->select((int)$row['id'])->asArray(); - } - ) + $list = $goals->listObjectsWithPaginationIfSet( + function ($row) { + return (new department_goals_o())->select((int)$row['id'])->asArray(); + } ); + // Filter unauthorized results unless superuser + if (!$isSuperuser && is_array($list)) { + $list = array_values(array_filter($list, function ($item) use ($hasAllDepartments) { + $deps = isset($item['departments']) && is_array($item['departments']) ? $item['departments'] : []; + return $hasAllDepartments($deps); + })); + } + $response->success($list); }, [ 'goals_department_list' => 'List department goals or retrieve a single goal when id is provided' ]); @@ -57,6 +82,8 @@ class departmentGoalsRoute if (!$user) { $response->error('Unauthorized', 401); } + $isSuperuser = method_exists($user, 'hasPermission') && $user->hasPermission('superuser'); + $userDepartments = method_exists($user, 'getGroup') ? (array)$user->getGroup()->getDepartments() : []; // Validate input $this->requireParameters(['departments', 'criteria']); @@ -78,6 +105,15 @@ class departmentGoalsRoute $departments[] = $dept; } + // Access control: creator must have all departments unless superuser + if (!$isSuperuser) { + $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); + } + } + // Parse criteria $criteria = goals_criteria::fromJson(json_encode($criteriaInput)); @@ -104,6 +140,25 @@ class departmentGoalsRoute $response->error('Goal not found', 404); } + // Current user and access helpers + $user = (new authentication())->get_user(); + if (!$user) { + $response->error('Unauthorized', 401); + } + $isSuperuser = method_exists($user, 'hasPermission') && $user->hasPermission('superuser'); + $isCreator = ((int)$goal->created_by->value()) === (int)$user->id; + $userDepartments = method_exists($user, 'getGroup') ? (array)$user->getGroup()->getDepartments() : []; + $hasAllDepartments = function (array $goalDepartments, array $userDepartments) { + $goalDepartments = array_map('intval', $goalDepartments); + $userDepartments = array_map('intval', $userDepartments); + return count(array_diff($goalDepartments, $userDepartments)) === 0; + }; + + // 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); + } + $dataToUpdate = []; // Optional: departments @@ -115,6 +170,13 @@ class departmentGoalsRoute $response->error('All department ids must be numeric', 400); } } + // If not superuser or creator, ensure new departments are within user departments + 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); + } + } // store as ids array in column $dataToUpdate['departments'] = array_map('intval', $departmentsInput); } @@ -152,6 +214,21 @@ class departmentGoalsRoute if (!$goal->exists()) { $response->error('Goal not found', 404); } + + // Current user and access helpers + $user = (new authentication())->get_user(); + if (!$user) { + $response->error('Unauthorized', 401); + } + $isSuperuser = method_exists($user, 'hasPermission') && $user->hasPermission('superuser'); + $isCreator = ((int)$goal->created_by->value()) === (int)$user->id; + $userDepartments = method_exists($user, 'getGroup') ? (array)$user->getGroup()->getDepartments() : []; + $goalDepartments = (array)$goal->departments->value(); + $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); + } $goal->delete(); $response->success(['message' => 'Deleted']); }, [