feat: add routes, lang, tests, stubs, docs, and docker configurations

This commit is contained in:
2026-05-21 16:05:16 +07:00
parent fad70d096b
commit 28a06315b8
3385 changed files with 177070 additions and 0 deletions
+131
View File
@@ -0,0 +1,131 @@
<?php
/**
* Session fixation prevention tests.
*
* Verifies that all authentication flows regenerate the session ID
* after a successful login, preventing session fixation attacks.
*/
use App\Models\Role;
use App\Models\User;
use App\Services\SystemConfig\SystemConfigService;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Session;
use Laravel\Socialite\Facades\Socialite;
beforeEach(function () {
$ref = new ReflectionClass(SystemConfigService::class);
$prop = $ref->getProperty('resolvedSettings');
$prop->setAccessible(true);
$prop->setValue(null, null);
Cache::flush();
});
// ── Web login ─────────────────────────────────────────────────────────────────
test('web login regenerates the session id', function () {
$user = User::factory()->create();
$before = session()->getId();
$this->post('/login', [
'email' => $user->email,
'password' => 'password',
]);
// After login the session must have a different ID
expect(session()->getId())->not->toBe($before);
});
// ── 2FA verify ────────────────────────────────────────────────────────────────
test('2fa verify regenerates the session id', function () {
$user = User::factory()->create();
Session::put('auth.2fa_user_id', $user->id);
Session::put('auth.2fa_code', '123456');
Session::put('auth.2fa_expires_at', now()->addMinutes(10)->timestamp);
$before = session()->getId();
$this->post('/2fa', ['code' => '123456']);
expect(session()->getId())->not->toBe($before);
});
// ── OAuth / Socialite callback ────────────────────────────────────────────────
test('oauth callback regenerates the session id after successful login', function () {
Role::firstOrCreate(['name' => 'User', 'guard_name' => 'web']);
app(SystemConfigService::class)->update(['feature_google_oauth' => true]);
session(['social_auth_provider' => 'google']);
$socialUser = new Laravel\Socialite\Two\User;
$socialUser->id = 'google-fixation-id';
$socialUser->name = 'Fixation User';
$socialUser->email = 'fixation@example.com';
$socialUser->avatar = '';
$socialUser->user = [];
Socialite::shouldReceive('driver->user')->andReturn($socialUser);
$before = session()->getId();
$this->get('/auth/callback')->assertRedirect('/dashboard');
expect(session()->getId())->not->toBe($before);
});
// ── Password reset ────────────────────────────────────────────────────────────
test('password reset regenerates the session id', function () {
Notification::fake();
$user = User::factory()->create();
// Request a reset token
$this->post('/forgot-password', ['email' => $user->email]);
$token = Password::broker()->createToken($user);
$before = session()->getId();
$this->post('/reset-password', [
'token' => $token,
'email' => $user->email,
'password' => 'NewSecurePass1!',
'password_confirmation' => 'NewSecurePass1!',
])->assertRedirect(route('login'));
expect(session()->getId())->not->toBe($before);
});
// ── Impersonation ─────────────────────────────────────────────────────────────
test('starting impersonation regenerates the session id', function () {
$perm = \App\Models\Permission::firstOrCreate(['name' => 'impersonate users', 'guard_name' => 'web']);
$admin = User::factory()->create(['is_active' => true]);
$admin->givePermissionTo($perm);
$target = User::factory()->create(['is_active' => true]);
$before = session()->getId();
$this->actingAs($admin)->post("/impersonate/{$target->id}");
expect(session()->getId())->not->toBe($before);
});
test('stopping impersonation regenerates the session id', function () {
$perm = \App\Models\Permission::firstOrCreate(['name' => 'impersonate users', 'guard_name' => 'web']);
$admin = User::factory()->create(['is_active' => true]);
$admin->givePermissionTo($perm);
$target = User::factory()->create(['is_active' => true]);
// Start impersonation first
$this->actingAs($admin)->post("/impersonate/{$target->id}");
$before = session()->getId();
$this->post('/impersonate/stop');
expect(session()->getId())->not->toBe($before);
});