]> BookStack Code Mirror - bookstack/blob - tests/Auth/MfaConfigurationTest.php
870850a73bc292fbb059fbe7ab5fe6f95ebfa3ed
[bookstack] / tests / Auth / MfaConfigurationTest.php
1 <?php
2
3 namespace Tests\Auth;
4
5 use BookStack\Auth\Access\Mfa\MfaValue;
6 use PragmaRX\Google2FA\Google2FA;
7 use Tests\TestCase;
8
9 class MfaConfigurationTest extends TestCase
10 {
11
12     public function test_totp_setup()
13     {
14         $editor = $this->getEditor();
15         $this->assertDatabaseMissing('mfa_values', ['user_id' => $editor->id]);
16
17         // Setup page state
18         $resp = $this->actingAs($editor)->get('/mfa/setup');
19         $resp->assertElementContains('a[href$="/mfa/totp-generate"]', 'Setup');
20
21         // Generate page access
22         $resp = $this->get('/mfa/totp-generate');
23         $resp->assertSee('Mobile App Setup');
24         $resp->assertSee('Verify Setup');
25         $resp->assertElementExists('form[action$="/mfa/totp-confirm"] button');
26         $this->assertSessionHas('mfa-setup-totp-secret');
27         $svg = $resp->getElementHtml('#main-content .card svg');
28
29         // Validation error, code should remain the same
30         $resp = $this->post('/mfa/totp-confirm', [
31             'code' => 'abc123',
32         ]);
33         $resp->assertRedirect('/mfa/totp-generate');
34         $resp = $this->followRedirects($resp);
35         $resp->assertSee('The provided code is not valid or has expired.');
36         $revisitSvg = $resp->getElementHtml('#main-content .card svg');
37         $this->assertTrue($svg === $revisitSvg);
38
39         // Successful confirmation
40         $google2fa = new Google2FA();
41         $secret = decrypt(session()->get('mfa-setup-totp-secret'));
42         $otp = $google2fa->getCurrentOtp($secret);
43         $resp = $this->post('/mfa/totp-confirm', [
44             'code' => $otp,
45         ]);
46         $resp->assertRedirect('/mfa/setup');
47
48         // Confirmation of setup
49         $resp = $this->followRedirects($resp);
50         $resp->assertSee('Multi-factor method successfully configured');
51         $resp->assertElementContains('a[href$="/mfa/totp-generate"]', 'Reconfigure');
52
53         $this->assertDatabaseHas('mfa_values', [
54             'user_id' => $editor->id,
55             'method' => 'totp',
56         ]);
57         $this->assertFalse(session()->has('mfa-setup-totp-secret'));
58         $value = MfaValue::query()->where('user_id', '=', $editor->id)
59             ->where('method', '=', 'totp')->first();
60         $this->assertEquals($secret, decrypt($value->value));
61     }
62
63     public function test_backup_codes_setup()
64     {
65         $editor = $this->getEditor();
66         $this->assertDatabaseMissing('mfa_values', ['user_id' => $editor->id]);
67
68         // Setup page state
69         $resp = $this->actingAs($editor)->get('/mfa/setup');
70         $resp->assertElementContains('a[href$="/mfa/backup-codes-generate"]', 'Setup');
71
72         // Generate page access
73         $resp = $this->get('/mfa/backup-codes-generate');
74         $resp->assertSee('Backup Codes');
75         $resp->assertElementContains('form[action$="/mfa/backup-codes-confirm"]', 'Confirm and Enable');
76         $this->assertSessionHas('mfa-setup-backup-codes');
77         $codes = decrypt(session()->get('mfa-setup-backup-codes'));
78         // Check code format
79         $this->assertCount(16, $codes);
80         $this->assertEquals(16*11, strlen(implode('', $codes)));
81         // Check download link
82         $resp->assertSee(base64_encode(implode("\n\n", $codes)));
83
84         // Confirm submit
85         $resp = $this->post('/mfa/backup-codes-confirm');
86         $resp->assertRedirect('/mfa/setup');
87
88         // Confirmation of setup
89         $resp = $this->followRedirects($resp);
90         $resp->assertSee('Multi-factor method successfully configured');
91         $resp->assertElementContains('a[href$="/mfa/backup-codes-generate"]', 'Reconfigure');
92
93         $this->assertDatabaseHas('mfa_values', [
94             'user_id' => $editor->id,
95             'method' => 'backup_codes',
96         ]);
97         $this->assertFalse(session()->has('mfa-setup-backup-codes'));
98         $value = MfaValue::query()->where('user_id', '=', $editor->id)
99             ->where('method', '=', 'backup_codes')->first();
100         $this->assertEquals($codes, json_decode(decrypt($value->value)));
101     }
102
103     public function test_backup_codes_cannot_be_confirmed_if_not_previously_generated()
104     {
105         $resp = $this->asEditor()->post('/mfa/backup-codes-confirm');
106         $resp->assertStatus(500);
107     }
108
109 }