diff --git a/app/Enums/UserFlags.php b/app/Enums/UserFlags.php
index f85902b..3d23ff5 100644
--- a/app/Enums/UserFlags.php
+++ b/app/Enums/UserFlags.php
@@ -6,4 +6,5 @@ enum UserFlags: string
{
case HEAD_DIRECTOR = 'head_director';
case MONITOR = 'monitor';
+ case CAN_IMPERSONATE = 'can_impersonate';
}
diff --git a/app/Http/Controllers/Admin/ImpersonationController.php b/app/Http/Controllers/Admin/ImpersonationController.php
new file mode 100644
index 0000000..756d982
--- /dev/null
+++ b/app/Http/Controllers/Admin/ImpersonationController.php
@@ -0,0 +1,78 @@
+user_id);
+ $admin = $request->user();
+
+ if (! $admin->can('impersonate', $user)) {
+ abort(403);
+ }
+
+ // Prevent impersonating yourself or impersonating if already impersonating
+ if ($admin->id === $user->id || session()->has('impersonator_id')) {
+ return back()->with('error', 'Cannot impersonate.');
+ }
+
+ // Save the original admin id and optionally guard
+ session()->put('impersonator_id', $admin->id);
+ session()->put('impersonator_started_at', now()->toDateTimeString());
+
+ auditionLog('Started impersonating '.$user->full_name().' - '.$user->email, ['users' => [$user->id]]);
+
+ // Switch user
+ Auth::loginUsingId($user->getAuthIdentifier());
+
+ // Regenerate session to mitigate fixation
+ $request->session()->regenerate();
+
+ return redirect(route('dashboard'))->with('success', 'Now impersonating '.$user->email);
+ }
+
+ public function stop(Request $request)
+ {
+ $impersonatedUser = Auth::user();
+ $impersonatorId = session('impersonator_id');
+ if (! $impersonatorId) {
+ return back()->with('error', 'Not impersonating.');
+ }
+
+ // Restore original admin
+ $admin = User::find($impersonatorId);
+ if ($admin) {
+ Auth::loginUsingId($admin->getAuthIdentifier());
+ } else {
+ // If admin was deleted, just log out
+ Auth::logout();
+ }
+
+ auditionLog('Stopped impersonating '.$impersonatedUser->full_name().' - '.$impersonatedUser->email, ['users' => [$impersonatedUser->id]]);
+
+ // Clear impersonation data
+ session()->forget(['impersonator_id', 'impersonator_started_at']);
+
+ // Regenerate session
+ $request->session()->regenerate();
+
+ return redirect(route('dashboard'))->with('success', 'Stopped impersonation.');
+ }
+
+ public function index()
+ {
+
+ $users = User::where('id', '!=', auth()->id())->get();
+
+ return view('admin.impersonation.index', compact('users'));
+ }
+}
diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index c7288e0..7b408f5 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -38,6 +38,7 @@ use App\Services\EntryService;
use App\Services\ScoreService;
use App\Services\UserService;
use Illuminate\Database\Eloquent\Model;
+use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
@@ -78,6 +79,9 @@ class AppServiceProvider extends ServiceProvider
Student::observe(StudentObserver::class);
User::observe(UserObserver::class);
+ Gate::define('impersonate', function ($admin, $target) {
+ return $admin->hasFlag('can_impersonate') && $admin->id !== $target->id;
+ });
// Model::preventLazyLoading(! app()->isProduction());
}
}
diff --git a/app/Providers/FortifyServiceProvider.php b/app/Providers/FortifyServiceProvider.php
index 2d54ccf..b929be9 100644
--- a/app/Providers/FortifyServiceProvider.php
+++ b/app/Providers/FortifyServiceProvider.php
@@ -65,5 +65,6 @@ class FortifyServiceProvider extends ServiceProvider
Fortify::verifyEmailView(function () {
return view('auth.verify-email');
});
+
}
}
diff --git a/resources/views/admin/impersonation/index.blade.php b/resources/views/admin/impersonation/index.blade.php
new file mode 100644
index 0000000..bed32eb
--- /dev/null
+++ b/resources/views/admin/impersonation/index.blade.php
@@ -0,0 +1,15 @@
+