Add access control for department goal operations in departmentGoalsRoute
- Restrict goal listing and retrieval based on user permissions and department access. - Enforce department-level access control for goal creation, updates, and deletions. - Validate user actions against `superuser` status and department memberships.
This commit is contained in:
@@ -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']);
|
||||
}, [
|
||||
|
||||
Reference in New Issue
Block a user