forked from pterodactyl/panel
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTwoFactorControllerTest.php
More file actions
167 lines (130 loc) · 5.84 KB
/
TwoFactorControllerTest.php
File metadata and controls
167 lines (130 loc) · 5.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
<?php
namespace Pterodactyl\Tests\Integration\Api\Client;
use Carbon\Carbon;
use Pterodactyl\Models\User;
use Illuminate\Http\Response;
use PragmaRX\Google2FA\Google2FA;
use Pterodactyl\Models\RecoveryToken;
use PHPUnit\Framework\ExpectationFailedException;
class TwoFactorControllerTest extends ClientApiIntegrationTestCase
{
/**
* Test that image data for enabling 2FA is returned by the endpoint and that the user
* record in the database is updated as expected.
*/
public function testTwoFactorImageDataIsReturned()
{
/** @var \Pterodactyl\Models\User $user */
$user = factory(User::class)->create(['use_totp' => false]);
$this->assertFalse($user->use_totp);
$this->assertEmpty($user->totp_secret);
$this->assertEmpty($user->totp_authenticated_at);
$response = $this->actingAs($user)->getJson('/api/client/account/two-factor');
$response->assertOk();
$response->assertJsonStructure(['data' => ['image_url_data']]);
$user = $user->refresh();
$this->assertFalse($user->use_totp);
$this->assertNotEmpty($user->totp_secret);
$this->assertEmpty($user->totp_authenticated_at);
}
/**
* Test that an error is returned if the user's account already has 2FA enabled on it.
*/
public function testErrorIsReturnedWhenTwoFactorIsAlreadyEnabled()
{
/** @var \Pterodactyl\Models\User $user */
$user = factory(User::class)->create(['use_totp' => true]);
$response = $this->actingAs($user)->getJson('/api/client/account/two-factor');
$response->assertStatus(Response::HTTP_BAD_REQUEST);
$response->assertJsonPath('errors.0.code', 'BadRequestHttpException');
$response->assertJsonPath('errors.0.detail', 'Two-factor authentication is already enabled on this account.');
}
/**
* Test that a validation error is thrown if invalid data is passed to the 2FA endpoint.
*/
public function testValidationErrorIsReturnedIfInvalidDataIsPassedToEnabled2FA()
{
/** @var \Pterodactyl\Models\User $user */
$user = factory(User::class)->create(['use_totp' => false]);
$response = $this->actingAs($user)->postJson('/api/client/account/two-factor', [
'code' => '',
]);
$response->assertStatus(Response::HTTP_UNPROCESSABLE_ENTITY);
$response->assertJsonPath('errors.0.code', 'required');
}
/**
* Tests that 2FA can be enabled on an account for the user.
*/
public function testTwoFactorCanBeEnabledOnAccount()
{
/** @var \Pterodactyl\Models\User $user */
$user = factory(User::class)->create(['use_totp' => false]);
// Make the initial call to get the account setup for 2FA.
$this->actingAs($user)->getJson('/api/client/account/two-factor')->assertOk();
$user = $user->refresh();
$this->assertNotNull($user->totp_secret);
/** @var \PragmaRX\Google2FA\Google2FA $service */
$service = $this->app->make(Google2FA::class);
$secret = decrypt($user->totp_secret);
$token = $service->getCurrentOtp($secret);
$response = $this->actingAs($user)->postJson('/api/client/account/two-factor', [
'code' => $token,
]);
$response->assertOk();
$response->assertJsonPath('object', 'recovery_tokens');
$user = $user->refresh();
$this->assertTrue($user->use_totp);
$tokens = RecoveryToken::query()->where('user_id', $user->id)->get();
$this->assertCount(10, $tokens);
$this->assertStringStartsWith('$2y$10$', $tokens[0]->token);
$tokens = $tokens->pluck('token')->toArray();
foreach ($response->json('attributes.tokens') as $raw) {
foreach ($tokens as $hashed) {
if (password_verify($raw, $hashed)) {
continue 2;
}
}
throw new ExpectationFailedException(
sprintf('Failed asserting that token [%s] exists as a hashed value in recovery_tokens table.', $raw)
);
}
}
/**
* Test that two factor authentication can be disabled on an account as long as the password
* provided is valid for the account.
*/
public function testTwoFactorCanBeDisabledOnAccount()
{
Carbon::setTestNow(Carbon::now());
/** @var \Pterodactyl\Models\User $user */
$user = factory(User::class)->create(['use_totp' => true]);
$response = $this->actingAs($user)->deleteJson('/api/client/account/two-factor', [
'password' => 'invalid',
]);
$response->assertStatus(Response::HTTP_BAD_REQUEST);
$response->assertJsonPath('errors.0.code', 'BadRequestHttpException');
$response->assertJsonPath('errors.0.detail', 'The password provided was not valid.');
$response = $this->actingAs($user)->deleteJson('/api/client/account/two-factor', [
'password' => 'password',
]);
$response->assertStatus(Response::HTTP_NO_CONTENT);
$user = $user->refresh();
$this->assertFalse($user->use_totp);
$this->assertNotNull($user->totp_authenticated_at);
$this->assertSame(Carbon::now()->toIso8601String(), $user->totp_authenticated_at->toIso8601String());
}
/**
* Test that no error is returned when trying to disabled two factor on an account where it
* was not enabled in the first place.
*/
public function testNoErrorIsReturnedIfTwoFactorIsNotEnabled()
{
Carbon::setTestNow(Carbon::now());
/** @var \Pterodactyl\Models\User $user */
$user = factory(User::class)->create(['use_totp' => false]);
$response = $this->actingAs($user)->deleteJson('/api/client/account/two-factor', [
'password' => 'password',
]);
$response->assertStatus(Response::HTTP_NO_CONTENT);
}
}