76 lines
4.9 KiB
PHP
76 lines
4.9 KiB
PHP
<?php
|
|
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Http\Middleware\PreventRequestForgery;
|
|
use Inertia\Testing\AssertableInertia as Assert;
|
|
use PragmaRX\Google2FA\Google2FA;
|
|
|
|
require_once __DIR__.'/../../Support/FortifyMfa.php';
|
|
|
|
test('mfa management requires recent password confirmation without mutating enrollment on consultation', function () {
|
|
foundationMfaRoutes();
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user)->get('/settings/security')->assertRedirect('/user/confirm-password');
|
|
$this->postJson('/user/two-factor-authentication')->assertStatus(423);
|
|
expect($user->fresh()->two_factor_secret)->toBeNull();
|
|
$this->withSession(['auth.password_confirmed_at' => now()->timestamp])->get('/settings/security')->assertOk()
|
|
->assertHeader('Cache-Control', 'max-age=0, no-store, private')->assertInertia(fn (Assert $p) => $p
|
|
->where('canManageTwoFactor', true)->where('twoFactorEnabled', false)->where('twoFactorPending', false)->missing('secretKey')->missing('recoveryCodes'));
|
|
expect($user->fresh()->two_factor_secret)->toBeNull();
|
|
});
|
|
|
|
test('mfa management completes native enrollment renewal and disable only for the current account', function () {
|
|
foundationMfaRoutes();
|
|
$user = User::factory()->create();
|
|
$other = User::factory()->create();
|
|
$this->actingAs($user)->withSession(['auth.password_confirmed_at' => now()->timestamp]);
|
|
$this->postJson('/user/two-factor-authentication', ['user_id' => $other->id])->assertSuccessful();
|
|
$secret = $this->getJson('/user/two-factor-secret-key')->assertOk()->assertHeader('Cache-Control', 'max-age=0, no-store, private')->json('secretKey');
|
|
expect($user->fresh()->hasEnabledTwoFactorAuthentication())->toBeFalse();
|
|
$before = $user->fresh()->getAttributes();
|
|
$this->get('/settings/security')->assertInertia(fn (Assert $p) => $p->where('twoFactorPending', true)->where('twoFactorEnabled', false)->missing('secretKey')->missing('recoveryCodes'));
|
|
expect($user->fresh()->getAttributes())->toBe($before);
|
|
$code = (new Google2FA)->getCurrentOtp($secret);
|
|
$this->postJson('/user/confirmed-two-factor-authentication', ['code' => $code])->assertSuccessful();
|
|
expect($user->fresh()->hasEnabledTwoFactorAuthentication())->toBeTrue();
|
|
$oldCodes = $this->getJson('/user/two-factor-recovery-codes')->assertOk()->assertHeader('Cache-Control', 'max-age=0, no-store, private')->json();
|
|
$this->postJson('/user/two-factor-recovery-codes')->assertSuccessful();
|
|
$newCodes = $this->getJson('/user/two-factor-recovery-codes')->assertOk()->json();
|
|
expect($oldCodes)->toHaveCount(8)->and($newCodes)->toHaveCount(8)->and(array_intersect($oldCodes, $newCodes))->toBe([]);
|
|
$this->withSession(['auth.password_confirmed_at' => 0])->deleteJson('/user/two-factor-authentication')->assertStatus(423);
|
|
expect($user->fresh()->hasEnabledTwoFactorAuthentication())->toBeTrue();
|
|
$this->withSession(['auth.password_confirmed_at' => now()->timestamp])->deleteJson('/user/two-factor-authentication')->assertSuccessful();
|
|
expect($user->fresh()->two_factor_secret)->toBeNull()->and($user->fresh()->two_factor_recovery_codes)->toBeNull()->and($user->fresh()->two_factor_confirmed_at)->toBeNull();
|
|
expect($other->fresh()->two_factor_secret)->toBeNull();
|
|
});
|
|
|
|
test('mfa management invalid confirmation does not flash authentication codes or activate the account', function () {
|
|
foundationMfaRoutes();
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user)->withSession(['auth.password_confirmed_at' => now()->timestamp]);
|
|
$this->postJson('/user/two-factor-authentication')->assertSuccessful();
|
|
$this->from('/settings/security')->post('/user/confirmed-two-factor-authentication', ['code' => 'invalid'])->assertSessionHasErrorsIn('confirmTwoFactorAuthentication', ['code'])
|
|
->assertSessionMissing('_old_input.code');
|
|
expect($user->fresh()->hasEnabledTwoFactorAuthentication())->toBeFalse();
|
|
});
|
|
|
|
test('mfa management rejects missing csrf and accepts the encrypted xsrf cookie header used by the interface', function () {
|
|
foundationMfaRoutes();
|
|
$this->instance(PreventRequestForgery::class,
|
|
new class(app(), app('encrypter')) extends PreventRequestForgery
|
|
{
|
|
protected function runningUnitTests(): bool
|
|
{
|
|
return false;
|
|
}
|
|
});
|
|
$user = User::factory()->create();
|
|
$response = $this->actingAs($user)->withSession(['auth.password_confirmed_at' => now()->timestamp])->get('/settings/security')->assertOk();
|
|
$cookie = $response->getCookie('XSRF-TOKEN', false);
|
|
expect($cookie)->not->toBeNull();
|
|
$this->postJson('/user/two-factor-authentication')->assertStatus(419);
|
|
expect($user->fresh()->two_factor_secret)->toBeNull();
|
|
$this->withHeader('X-XSRF-TOKEN', $cookie->getValue())->postJson('/user/two-factor-authentication')->assertSuccessful();
|
|
expect($user->fresh()->two_factor_secret)->not->toBeNull();
|
|
});
|