forked from BookStackApp/BookStack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathApiAuthTest.php
More file actions
194 lines (157 loc) · 6.84 KB
/
ApiAuthTest.php
File metadata and controls
194 lines (157 loc) · 6.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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
<?php
namespace Tests\Api;
use BookStack\Permissions\Models\RolePermission;
use BookStack\Users\Models\Role;
use BookStack\Users\Models\User;
use Carbon\Carbon;
use Tests\TestCase;
class ApiAuthTest extends TestCase
{
use TestsApi;
protected string $endpoint = '/api/books';
public function test_requests_succeed_with_default_auth()
{
$viewer = $this->users->viewer();
$this->permissions->grantUserRolePermissions($viewer, ['access-api']);
$resp = $this->get($this->endpoint);
$resp->assertStatus(401);
$this->actingAs($viewer, 'standard');
$this->startSession();
$resp = $this->withCredentials()->get($this->endpoint);
$resp->assertStatus(200);
}
public function test_no_token_throws_error()
{
$resp = $this->get($this->endpoint);
$resp->assertStatus(401);
$resp->assertJson($this->errorResponse('No authorization token found on the request', 401));
}
public function test_bad_token_format_throws_error()
{
$resp = $this->get($this->endpoint, ['Authorization' => 'Token abc123']);
$resp->assertStatus(401);
$resp->assertJson($this->errorResponse('An authorization token was found on the request but the format appeared incorrect', 401));
}
public function test_token_with_non_existing_id_throws_error()
{
$resp = $this->get($this->endpoint, ['Authorization' => 'Token abc:123']);
$resp->assertStatus(401);
$resp->assertJson($this->errorResponse('No matching API token was found for the provided authorization token', 401));
}
public function test_token_with_bad_secret_value_throws_error()
{
$resp = $this->get($this->endpoint, ['Authorization' => "Token {$this->apiTokenId}:123"]);
$resp->assertStatus(401);
$resp->assertJson($this->errorResponse('The secret provided for the given used API token is incorrect', 401));
}
public function test_api_access_permission_required_to_access_api()
{
$resp = $this->get($this->endpoint, $this->apiAuthHeader());
$resp->assertStatus(200);
auth()->logout();
$accessApiPermission = RolePermission::getByName('access-api');
$editorRole = $this->users->editor()->roles()->first();
$editorRole->detachPermission($accessApiPermission);
$resp = $this->get($this->endpoint, $this->apiAuthHeader());
$resp->assertStatus(403);
$resp->assertJson($this->errorResponse('The owner of the used API token does not have permission to make API calls', 403));
}
public function test_api_access_permission_required_to_access_api_with_session_auth()
{
$editor = $this->users->editor();
$this->actingAs($editor, 'standard');
$this->startSession();
$resp = $this->get($this->endpoint);
$resp->assertStatus(200);
auth('standard')->logout();
$accessApiPermission = RolePermission::getByName('access-api');
$editorRole = $this->users->editor()->roles()->first();
$editorRole->detachPermission($accessApiPermission);
$editor = User::query()->where('id', '=', $editor->id)->first();
$this->actingAs($editor, 'standard');
$resp = $this->get($this->endpoint);
$resp->assertStatus(403);
$resp->assertJson($this->errorResponse('The owner of the used API token does not have permission to make API calls', 403));
}
public function test_access_prevented_for_guest_users_with_api_permission_while_public_access_disabled()
{
$this->disableCookieEncryption();
$publicRole = Role::getSystemRole('public');
$accessApiPermission = RolePermission::getByName('access-api');
$publicRole->attachPermission($accessApiPermission);
$this->withCookie('bookstack_session', 'abc123');
// Test API access when not public
setting()->put('app-public', false);
$resp = $this->get($this->endpoint);
$resp->assertStatus(403);
// Test API access when public
setting()->put('app-public', true);
$resp = $this->get($this->endpoint);
$resp->assertStatus(200);
}
public function test_only_get_requests_are_supported_with_session_auth()
{
$user = $this->users->admin();
$this->actingAs($user, 'standard');
$this->startSession();
$uriByMethods = [
'POST' => '/books',
'PUT' => '/books/1',
'DELETE' => '/books/1',
'HEAD' => '/books',
];
foreach ($uriByMethods as $method => $uri) {
$resp = $this->withCredentials()->json($method, "/api{$uri}");
$resp->assertStatus(403);
if ($method !== 'HEAD') {
$resp->assertJson($this->errorResponse('Only GET requests are allowed when using the API with cookie-based authentication', 403));
}
}
}
public function test_token_expiry_checked()
{
$editor = $this->users->editor();
$token = $editor->apiTokens()->first();
$resp = $this->get($this->endpoint, $this->apiAuthHeader());
$resp->assertStatus(200);
auth()->logout();
$token->expires_at = Carbon::now()->subDay()->format('Y-m-d');
$token->save();
$resp = $this->get($this->endpoint, $this->apiAuthHeader());
$resp->assertJson($this->errorResponse('The authorization token used has expired', 403));
}
public function test_email_confirmation_checked_using_api_auth()
{
$editor = $this->users->editor();
$editor->email_confirmed = false;
$editor->save();
// Set settings and get user instance
$this->setSettings(['registration-enabled' => 'true', 'registration-confirmation' => 'true']);
$resp = $this->get($this->endpoint, $this->apiAuthHeader());
$resp->assertStatus(401);
$resp->assertJson($this->errorResponse('The email address for the account in use needs to be confirmed', 401));
}
public function test_rate_limit_headers_active_on_requests()
{
$resp = $this->actingAsApiEditor()->get($this->endpoint);
$resp->assertHeader('x-ratelimit-limit', 180);
$resp->assertHeader('x-ratelimit-remaining', 179);
$resp = $this->actingAsApiEditor()->get($this->endpoint);
$resp->assertHeader('x-ratelimit-remaining', 178);
}
public function test_rate_limit_hit_gives_json_error()
{
config()->set(['api.requests_per_minute' => 1]);
$resp = $this->actingAsApiEditor()->get($this->endpoint);
$resp->assertStatus(200);
$resp = $this->actingAsApiEditor()->get($this->endpoint);
$resp->assertStatus(429);
$resp->assertHeader('x-ratelimit-remaining', 0);
$resp->assertHeader('retry-after');
$resp->assertJson([
'error' => [
'code' => 429,
],
]);
}
}