]> BookStack Code Mirror - bookstack/blob - tests/Api/ApiAuthTest.php
Merge pull request #2820 from BookStackApp/analysis-6470L9
[bookstack] / tests / Api / ApiAuthTest.php
1 <?php
2
3 namespace Tests\Api;
4
5 use BookStack\Auth\Permissions\RolePermission;
6 use BookStack\Auth\User;
7 use Carbon\Carbon;
8 use Tests\TestCase;
9
10 class ApiAuthTest extends TestCase
11 {
12     use TestsApi;
13
14     protected $endpoint = '/api/books';
15
16     public function test_requests_succeed_with_default_auth()
17     {
18         $viewer = $this->getViewer();
19         $this->giveUserPermissions($viewer, ['access-api']);
20
21         $resp = $this->get($this->endpoint);
22         $resp->assertStatus(401);
23
24         $this->actingAs($viewer, 'standard');
25
26         $resp = $this->get($this->endpoint);
27         $resp->assertStatus(200);
28     }
29
30     public function test_no_token_throws_error()
31     {
32         $resp = $this->get($this->endpoint);
33         $resp->assertStatus(401);
34         $resp->assertJson($this->errorResponse('No authorization token found on the request', 401));
35     }
36
37     public function test_bad_token_format_throws_error()
38     {
39         $resp = $this->get($this->endpoint, ['Authorization' => 'Token abc123']);
40         $resp->assertStatus(401);
41         $resp->assertJson($this->errorResponse('An authorization token was found on the request but the format appeared incorrect', 401));
42     }
43
44     public function test_token_with_non_existing_id_throws_error()
45     {
46         $resp = $this->get($this->endpoint, ['Authorization' => 'Token abc:123']);
47         $resp->assertStatus(401);
48         $resp->assertJson($this->errorResponse('No matching API token was found for the provided authorization token', 401));
49     }
50
51     public function test_token_with_bad_secret_value_throws_error()
52     {
53         $resp = $this->get($this->endpoint, ['Authorization' => "Token {$this->apiTokenId}:123"]);
54         $resp->assertStatus(401);
55         $resp->assertJson($this->errorResponse('The secret provided for the given used API token is incorrect', 401));
56     }
57
58     public function test_api_access_permission_required_to_access_api()
59     {
60         $resp = $this->get($this->endpoint, $this->apiAuthHeader());
61         $resp->assertStatus(200);
62         auth()->logout();
63
64         $accessApiPermission = RolePermission::getByName('access-api');
65         $editorRole = $this->getEditor()->roles()->first();
66         $editorRole->detachPermission($accessApiPermission);
67
68         $resp = $this->get($this->endpoint, $this->apiAuthHeader());
69         $resp->assertStatus(403);
70         $resp->assertJson($this->errorResponse('The owner of the used API token does not have permission to make API calls', 403));
71     }
72
73     public function test_api_access_permission_required_to_access_api_with_session_auth()
74     {
75         $editor = $this->getEditor();
76         $this->actingAs($editor, 'standard');
77
78         $resp = $this->get($this->endpoint);
79         $resp->assertStatus(200);
80         auth('standard')->logout();
81
82         $accessApiPermission = RolePermission::getByName('access-api');
83         $editorRole = $this->getEditor()->roles()->first();
84         $editorRole->detachPermission($accessApiPermission);
85
86         $editor = User::query()->where('id', '=', $editor->id)->first();
87
88         $this->actingAs($editor, 'standard');
89         $resp = $this->get($this->endpoint);
90         $resp->assertStatus(403);
91         $resp->assertJson($this->errorResponse('The owner of the used API token does not have permission to make API calls', 403));
92     }
93
94     public function test_token_expiry_checked()
95     {
96         $editor = $this->getEditor();
97         $token = $editor->apiTokens()->first();
98
99         $resp = $this->get($this->endpoint, $this->apiAuthHeader());
100         $resp->assertStatus(200);
101         auth()->logout();
102
103         $token->expires_at = Carbon::now()->subDay()->format('Y-m-d');
104         $token->save();
105
106         $resp = $this->get($this->endpoint, $this->apiAuthHeader());
107         $resp->assertJson($this->errorResponse('The authorization token used has expired', 403));
108     }
109
110     public function test_email_confirmation_checked_using_api_auth()
111     {
112         $editor = $this->getEditor();
113         $editor->email_confirmed = false;
114         $editor->save();
115
116         // Set settings and get user instance
117         $this->setSettings(['registration-enabled' => 'true', 'registration-confirmation' => 'true']);
118
119         $resp = $this->get($this->endpoint, $this->apiAuthHeader());
120         $resp->assertStatus(401);
121         $resp->assertJson($this->errorResponse('The email address for the account in use needs to be confirmed', 401));
122     }
123
124     public function test_rate_limit_headers_active_on_requests()
125     {
126         $resp = $this->actingAsApiEditor()->get($this->endpoint);
127         $resp->assertHeader('x-ratelimit-limit', 180);
128         $resp->assertHeader('x-ratelimit-remaining', 179);
129         $resp = $this->actingAsApiEditor()->get($this->endpoint);
130         $resp->assertHeader('x-ratelimit-remaining', 178);
131     }
132
133     public function test_rate_limit_hit_gives_json_error()
134     {
135         config()->set(['api.requests_per_minute' => 1]);
136         $resp = $this->actingAsApiEditor()->get($this->endpoint);
137         $resp->assertStatus(200);
138
139         $resp = $this->actingAsApiEditor()->get($this->endpoint);
140         $resp->assertStatus(429);
141         $resp->assertHeader('x-ratelimit-remaining', 0);
142         $resp->assertHeader('retry-after');
143         $resp->assertJson([
144             'error' => [
145                 'code' => 429,
146             ],
147         ]);
148     }
149 }