create(['is_active' => true]); foreach (['view active sessions', 'manage active sessions'] as $name) { $perm = Permission::firstOrCreate(['name' => $name, 'guard_name' => 'web']); $user->givePermissionTo($perm); } return $user; } function makeSessionViewer(): User { $user = User::factory()->create(['is_active' => true]); $perm = Permission::firstOrCreate(['name' => 'view active sessions', 'guard_name' => 'web']); $user->givePermissionTo($perm); return $user; } // ── Access control ──────────────────────────────────────────────────────────── test('guest cannot access session manager', function () { $this->get('/session-manager')->assertRedirect('/login'); }); test('user without permission is forbidden from session manager', function () { $user = User::factory()->create(); $this->actingAs($user)->get('/session-manager')->assertForbidden(); }); test('user with view permission can access session manager', function () { $user = makeSessionViewer(); $this->actingAs($user)->get('/session-manager')->assertOk(); }); test('guest cannot access session stats endpoint', function () { // JSON requests return 401 Unauthorized (not a redirect) for unauthenticated requests $this->getJson('/session-manager/stats')->assertUnauthorized(); }); test('user with view permission can access session stats', function () { $user = makeSessionViewer(); $this->actingAs($user)->getJson('/session-manager/stats') ->assertOk() ->assertJsonStructure(['total', 'active']); }); // ── Stats structure ──────────────────────────────────────────────────────────── test('stats endpoint returns valid json with total key', function () { $user = makeSessionViewer(); $this->actingAs($user) ->getJson('/session-manager/stats') ->assertOk() ->assertJsonStructure(['total', 'active']); }); // ── Terminate session ───────────────────────────────────────────────────────── test('user without manage permission cannot terminate a session', function () { $viewer = makeSessionViewer(); $this->actingAs($viewer) ->deleteJson('/session-manager/some-session-id') ->assertForbidden(); }); test('manager cannot terminate their own current session', function () { // The controller's destroy() checks: if ($id === session()->getId()) → 403. // We invoke it directly, bypassing HTTP, so the session ID is stable. $manager = makeSessionManager(); $this->actingAs($manager); $controller = app(\App\Http\Controllers\SystemSettings\SessionManagerController::class); $currentId = session()->getId(); $response = $controller->destroy(request(), $currentId); expect($response->getStatusCode())->toBe(403); expect(json_decode($response->getContent(), true)['success'])->toBeFalse(); }); test('manager can terminate a different session id', function () { // With array/redis driver the controller returns success when no row is found to delete. $manager = makeSessionManager(); $this->actingAs($manager) ->deleteJson('/session-manager/some-other-session-id') ->assertOk() ->assertJsonPath('success', true); }); test('guest cannot terminate sessions', function () { // JSON requests return 401 (not a redirect) for unauthenticated requests $this->deleteJson('/session-manager/any-id')->assertUnauthorized(); });