104 lines
3.8 KiB
PHP
104 lines
3.8 KiB
PHP
<?php
|
|
|
|
use App\Livewire\Admin\Settings;
|
|
use App\Models\Operator;
|
|
use App\Support\Settings as AppSettings;
|
|
use Livewire\Livewire;
|
|
use PragmaRX\Google2FA\Google2FA;
|
|
|
|
it('is voluntary by default', function () {
|
|
expect(AppSettings::bool('console.require_2fa', false))->toBeFalse();
|
|
|
|
$operator = Operator::factory()->role('Owner')->create();
|
|
|
|
$this->actingAs($operator, 'operator')->get(route('admin.overview'))->assertOk();
|
|
});
|
|
|
|
it('sends an operator without two-factor to the setup page once the switch is on', function () {
|
|
AppSettings::set('console.require_2fa', true);
|
|
|
|
$operator = Operator::factory()->role('Owner')->create();
|
|
|
|
$this->actingAs($operator, 'operator')
|
|
->get(route('admin.overview'))
|
|
->assertRedirect(route('admin.settings'));
|
|
});
|
|
|
|
it('lets an operator with confirmed two-factor through', function () {
|
|
AppSettings::set('console.require_2fa', true);
|
|
|
|
$operator = Operator::factory()->role('Owner')->create([
|
|
'two_factor_secret' => encrypt('JBSWY3DPEHPK3PXP'),
|
|
'two_factor_confirmed_at' => now(),
|
|
]);
|
|
|
|
$this->actingAs($operator, 'operator')->get(route('admin.overview'))->assertOk();
|
|
});
|
|
|
|
it('refuses to switch it on while your own account has no two-factor', function () {
|
|
// The lock-out this exists to prevent: the page that would turn it back off
|
|
// sits behind the switch. Same shape as console.allowed_ips.
|
|
$operator = Operator::factory()->role('Owner')->create();
|
|
|
|
Livewire::actingAs($operator, 'operator')->test(Settings::class)
|
|
->set('requireTwoFactor', true)
|
|
->call('saveTwoFactorPolicy')
|
|
->assertHasErrors('requireTwoFactor');
|
|
|
|
expect(AppSettings::bool('console.require_2fa', false))->toBeFalse();
|
|
});
|
|
|
|
it('lets an operator without two-factor genuinely enrol while the switch is on, then reach the console', function () {
|
|
// The trap this guards against: the exemption alone is not enough if
|
|
// there is nowhere to actually enrol behind it. Uses a non-Owner role —
|
|
// the requirement applies to every operator, not only the one who can
|
|
// flip the switch.
|
|
AppSettings::set('console.require_2fa', true);
|
|
|
|
$operator = Operator::factory()->role('Support')->create(['password' => 'password']);
|
|
|
|
// Bounced off an ordinary page…
|
|
$this->actingAs($operator, 'operator')
|
|
->get(route('admin.overview'))
|
|
->assertRedirect(route('admin.settings'));
|
|
|
|
// …but the settings page itself is genuinely reachable, not another
|
|
// bounce in disguise.
|
|
$this->actingAs($operator, 'operator')
|
|
->get(route('admin.settings'))
|
|
->assertOk();
|
|
|
|
// Enrol for real: password confirmation, then a real TOTP code.
|
|
$page = Livewire::actingAs($operator, 'operator')
|
|
->test(Settings::class)
|
|
->set('confirmablePassword', 'password')
|
|
->call('confirmPassword')
|
|
->assertHasNoErrors()
|
|
->call('enableTwoFactor');
|
|
|
|
$code = app(Google2FA::class)->getCurrentOtp(decrypt($operator->refresh()->two_factor_secret));
|
|
|
|
$page->set('twoFactorCode', $code)->call('confirmTwoFactor')->assertHasNoErrors();
|
|
|
|
expect($operator->refresh()->two_factor_confirmed_at)->not->toBeNull();
|
|
|
|
// And now the rest of the console actually opens.
|
|
$this->actingAs($operator->fresh(), 'operator')
|
|
->get(route('admin.overview'))
|
|
->assertOk();
|
|
});
|
|
|
|
it('allows it once your own two-factor is confirmed', function () {
|
|
$operator = Operator::factory()->role('Owner')->create([
|
|
'two_factor_secret' => encrypt('JBSWY3DPEHPK3PXP'),
|
|
'two_factor_confirmed_at' => now(),
|
|
]);
|
|
|
|
Livewire::actingAs($operator, 'operator')->test(Settings::class)
|
|
->set('requireTwoFactor', true)
|
|
->call('saveTwoFactorPolicy')
|
|
->assertHasNoErrors();
|
|
|
|
expect(AppSettings::bool('console.require_2fa', false))->toBeTrue();
|
|
});
|