X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/a274406038e13cf678e14d65dfa70d04ead67206..refs/pull/5280/head:/tests/Auth/MfaVerificationTest.php diff --git a/tests/Auth/MfaVerificationTest.php b/tests/Auth/MfaVerificationTest.php index ee6f3ecc8..76c59bc74 100644 --- a/tests/Auth/MfaVerificationTest.php +++ b/tests/Auth/MfaVerificationTest.php @@ -2,12 +2,12 @@ namespace Tests\Auth; -use BookStack\Auth\Access\LoginService; -use BookStack\Auth\Access\Mfa\MfaValue; -use BookStack\Auth\Access\Mfa\TotpService; -use BookStack\Auth\Role; -use BookStack\Auth\User; +use BookStack\Access\LoginService; +use BookStack\Access\Mfa\MfaValue; +use BookStack\Access\Mfa\TotpService; use BookStack\Exceptions\StoppedAuthenticationException; +use BookStack\Users\Models\Role; +use BookStack\Users\Models\User; use Illuminate\Support\Facades\Hash; use PragmaRX\Google2FA\Google2FA; use Tests\TestCase; @@ -23,7 +23,7 @@ class MfaVerificationTest extends TestCase $resp = $this->get('/mfa/verify'); $resp->assertSee('Verify Access'); $resp->assertSee('Enter the code, generated using your mobile app, below:'); - $resp->assertElementExists('form[action$="/mfa/totp/verify"] input[name="code"]'); + $this->withHtml($resp)->assertElementExists('form[action$="/mfa/totp/verify"] input[name="code"][autofocus]'); $google2fa = new Google2FA(); $resp = $this->post('/mfa/totp/verify', [ @@ -57,6 +57,15 @@ class MfaVerificationTest extends TestCase $this->assertNull(auth()->user()); } + public function test_totp_form_has_autofill_configured() + { + [$user, $secret, $loginResp] = $this->startTotpLogin(); + $html = $this->withHtml($this->get('/mfa/verify')); + + $html->assertElementExists('form[autocomplete="off"][action$="/verify"]'); + $html->assertElementExists('input[autocomplete="one-time-code"][name="code"]'); + } + public function test_backup_code_verification() { [$user, $codes, $loginResp] = $this->startBackupCodeLogin(); @@ -66,7 +75,7 @@ class MfaVerificationTest extends TestCase $resp->assertSee('Verify Access'); $resp->assertSee('Backup Code'); $resp->assertSee('Enter one of your remaining backup codes below:'); - $resp->assertElementExists('form[action$="/mfa/backup_codes/verify"] input[name="code"]'); + $this->withHtml($resp)->assertElementExists('form[action$="/mfa/backup_codes/verify"] input[name="code"]'); $resp = $this->post('/mfa/backup_codes/verify', [ 'code' => $codes[1], @@ -138,9 +147,18 @@ class MfaVerificationTest extends TestCase $resp->assertSeeText('You have less than 5 backup codes remaining, Please generate and store a new set before you run out of codes to prevent being locked out of your account.'); } + public function test_backup_code_form_has_autofill_configured() + { + [$user, $codes, $loginResp] = $this->startBackupCodeLogin(); + $html = $this->withHtml($this->get('/mfa/verify')); + + $html->assertElementExists('form[autocomplete="off"][action$="/verify"]'); + $html->assertElementExists('input[autocomplete="one-time-code"][name="code"]'); + } + public function test_both_mfa_options_available_if_set_on_profile() { - $user = $this->getEditor(); + $user = $this->users->editor(); $user->password = Hash::make('password'); $user->save(); @@ -154,18 +172,18 @@ class MfaVerificationTest extends TestCase ]); // Totp shown by default - $mfaView->assertElementExists('form[action$="/mfa/totp/verify"] input[name="code"]'); - $mfaView->assertElementContains('a[href$="/mfa/verify?method=backup_codes"]', 'Verify using a backup code'); + $this->withHtml($mfaView)->assertElementExists('form[action$="/mfa/totp/verify"] input[name="code"]'); + $this->withHtml($mfaView)->assertElementContains('a[href$="/mfa/verify?method=backup_codes"]', 'Verify using a backup code'); // Ensure can view backup_codes view $resp = $this->get('/mfa/verify?method=backup_codes'); - $resp->assertElementExists('form[action$="/mfa/backup_codes/verify"] input[name="code"]'); - $resp->assertElementContains('a[href$="/mfa/verify?method=totp"]', 'Verify using a mobile app'); + $this->withHtml($resp)->assertElementExists('form[action$="/mfa/backup_codes/verify"] input[name="code"]'); + $this->withHtml($resp)->assertElementContains('a[href$="/mfa/verify?method=totp"]', 'Verify using a mobile app'); } public function test_mfa_required_with_no_methods_leads_to_setup() { - $user = $this->getEditor(); + $user = $this->users->editor(); $user->password = Hash::make('password'); $user->save(); /** @var Role $role */ @@ -184,7 +202,7 @@ class MfaVerificationTest extends TestCase ]); $resp->assertSeeText('No Methods Configured'); - $resp->assertElementContains('a[href$="/mfa/setup"]', 'Configure'); + $this->withHtml($resp)->assertElementContains('a[href$="/mfa/setup"]', 'Configure'); $this->get('/mfa/backup_codes/generate'); $resp = $this->post('/mfa/backup_codes/confirm'); @@ -222,7 +240,7 @@ class MfaVerificationTest extends TestCase // Attempted login user, who has configured mfa, access // Sets up user that has MFA required after attempted login. $loginService = $this->app->make(LoginService::class); - $user = $this->getEditor(); + $user = $this->users->editor(); /** @var Role $role */ $role = $user->roles->first(); $role->mfa_enforced = true; @@ -241,13 +259,23 @@ class MfaVerificationTest extends TestCase } } + public function test_login_mfa_interception_does_not_log_error() + { + $logHandler = $this->withTestLogger(); + + [$user, $secret, $loginResp] = $this->startTotpLogin(); + + $loginResp->assertRedirect('/mfa/verify'); + $this->assertFalse($logHandler->hasErrorRecords()); + } + /** - * @return Array + * @return array */ protected function startTotpLogin(): array { $secret = $this->app->make(TotpService::class)->generateSecret(); - $user = $this->getEditor(); + $user = $this->users->editor(); $user->password = Hash::make('password'); $user->save(); MfaValue::upsertWithValue($user, MfaValue::METHOD_TOTP, $secret); @@ -260,11 +288,11 @@ class MfaVerificationTest extends TestCase } /** - * @return Array + * @return array */ protected function startBackupCodeLogin($codes = ['kzzu6-1pgll', 'bzxnf-plygd', 'bwdsp-ysl51', '1vo93-ioy7n', 'lf7nw-wdyka', 'xmtrd-oplac']): array { - $user = $this->getEditor(); + $user = $this->users->editor(); $user->password = Hash::make('password'); $user->save(); MfaValue::upsertWithValue($user, MfaValue::METHOD_BACKUP_CODES, json_encode($codes));