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