feat(auth): enforce strict branch scoping for branch-assigned HR and staff accounts
- Introduce authBranchId() helper to resolve assigned branch while allowing company admin/superadmin cross-branch oversight - Restrict branch, department, and employee listings and mutations to assigned branch for branch-scoped accounts - Scope attendance records, biometric logs, leave applications/balances, payslips, shifts, and announcements by assigned branch - Add comprehensive automated feature tests in BranchScopedAccessTest to prevent branch cross-visibility regression
This commit is contained in:
@@ -25,9 +25,12 @@ class UserController extends BaseController
|
||||
abort(403, 'Unauthorized Access Prevented');
|
||||
}
|
||||
|
||||
// $userQuery = User::withPermissionCheck()->with(['roles', 'creator'])->latest();
|
||||
$userQuery = User::with(['roles', 'creator'])->where(function ($q) {
|
||||
if (Auth::user()->can('manage-any-users')) {
|
||||
$scopedBranchId = authBranchId();
|
||||
$userQuery = User::with(['roles', 'creator'])->where(function ($q) use ($scopedBranchId) {
|
||||
if ($scopedBranchId) {
|
||||
$q->whereIn('created_by', getCompanyAndUsersId())
|
||||
->where('branch_id', $scopedBranchId);
|
||||
} elseif (Auth::user()->can('manage-any-users')) {
|
||||
$q->whereIn('created_by', getCompanyAndUsersId());
|
||||
} elseif (Auth::user()->can('manage-own-users')) {
|
||||
$q->where('created_by', Auth::id());
|
||||
@@ -121,7 +124,11 @@ class UserController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
$branches = \App\Models\Branch::whereIn('created_by', getCompanyAndUsersId())->get(['id', 'name']);
|
||||
$branchesQuery = \App\Models\Branch::whereIn('created_by', getCompanyAndUsersId());
|
||||
if ($scopedBranchId) {
|
||||
$branchesQuery->where('id', $scopedBranchId);
|
||||
}
|
||||
$branches = $branchesQuery->get(['id', 'name']);
|
||||
|
||||
return Inertia::render('users/index', [
|
||||
'users' => $users,
|
||||
@@ -179,13 +186,16 @@ class UserController extends BaseController
|
||||
$created_by = auth()->id();
|
||||
}
|
||||
|
||||
$scopedBranchId = authBranchId();
|
||||
$assignedBranchId = $scopedBranchId ?: $request->branch_id;
|
||||
|
||||
$user = User::create([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'password' => Hash::make($request->password),
|
||||
'created_by' => creatorId(),
|
||||
'lang' => $userLang,
|
||||
'branch_id' => $request->branch_id,
|
||||
'branch_id' => $assignedBranchId,
|
||||
]);
|
||||
|
||||
if ($user && $request->roles) {
|
||||
@@ -222,10 +232,17 @@ class UserController extends BaseController
|
||||
public function update(UserRequest $request, User $user)
|
||||
{
|
||||
if (Auth::user()->can('edit-users')) {
|
||||
$scopedBranchId = authBranchId();
|
||||
if ($scopedBranchId && (int)$user->branch_id !== (int)$scopedBranchId) {
|
||||
return redirect()->back()->with('error', __('Permission Denied. You cannot edit users outside your assigned branch.'));
|
||||
}
|
||||
|
||||
if ($user) {
|
||||
$user->name = $request->name;
|
||||
$user->email = $request->email;
|
||||
if ($request->has('branch_id')) {
|
||||
if ($scopedBranchId) {
|
||||
$user->branch_id = $scopedBranchId;
|
||||
} elseif ($request->has('branch_id')) {
|
||||
$user->branch_id = $request->branch_id;
|
||||
}
|
||||
|
||||
@@ -261,6 +278,11 @@ class UserController extends BaseController
|
||||
public function destroy(User $user)
|
||||
{
|
||||
if (Auth::user()->can('delete-users')) {
|
||||
$scopedBranchId = authBranchId();
|
||||
if ($scopedBranchId && (int)$user->branch_id !== (int)$scopedBranchId) {
|
||||
return redirect()->back()->with('error', __('Permission Denied. You cannot delete users outside your assigned branch.'));
|
||||
}
|
||||
|
||||
if ($user) {
|
||||
$user->delete();
|
||||
return redirect()->route('users.index')->with('success', __('User deleted with roles'));
|
||||
|
||||
Reference in New Issue
Block a user