]> BookStack Code Mirror - bookstack/blobdiff - tests/Api/ApiAuthTest.php
respective book and chapter structure added.
[bookstack] / tests / Api / ApiAuthTest.php
index 30d7f4ead4e855478ba2209b443d919d5070b53c..93e4b02e423178a514a2ab9fe9b2c52a6eaa474f 100644 (file)
@@ -1,9 +1,12 @@
 <?php
 
-namespace Tests;
+namespace Tests\Api;
 
-use BookStack\Auth\Permissions\RolePermission;
+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
 {
@@ -13,11 +16,13 @@ class ApiAuthTest extends TestCase
 
     public function test_requests_succeed_with_default_auth()
     {
-        $viewer = $this->getViewer();
+        $viewer = $this->users->viewer();
+        $this->permissions->grantUserRolePermissions($viewer, ['access-api']);
+
         $resp = $this->get($this->endpoint);
         $resp->assertStatus(401);
 
-        $this->actingAs($viewer, 'web');
+        $this->actingAs($viewer, 'standard');
 
         $resp = $this->get($this->endpoint);
         $resp->assertStatus(200);
@@ -27,28 +32,28 @@ class ApiAuthTest extends TestCase
     {
         $resp = $this->get($this->endpoint);
         $resp->assertStatus(401);
-        $resp->assertJson($this->errorResponse("No authorization token found on the request", 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 = $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));
+        $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 = $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));
+        $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));
+        $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()
@@ -58,16 +63,58 @@ class ApiAuthTest extends TestCase
         auth()->logout();
 
         $accessApiPermission = RolePermission::getByName('access-api');
-        $editorRole = $this->getEditor()->roles()->first();
+        $editorRole = $this->users->editor()->roles()->first();
         $editorRole->detachPermission($accessApiPermission);
 
         $resp = $this->get($this->endpoint, $this->apiAuthHeader());
-        $resp->assertJson($this->errorResponse("The owner of the used API token does not have permission to make API calls", 403));
+        $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');
+
+        $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_token_expiry_checked()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $token = $editor->apiTokens()->first();
 
         $resp = $this->get($this->endpoint, $this->apiAuthHeader());
@@ -78,12 +125,12 @@ class ApiAuthTest extends TestCase
         $token->save();
 
         $resp = $this->get($this->endpoint, $this->apiAuthHeader());
-        $resp->assertJson($this->errorResponse("The authorization token used has expired", 403));
+        $resp->assertJson($this->errorResponse('The authorization token used has expired', 403));
     }
 
     public function test_email_confirmation_checked_using_api_auth()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $editor->email_confirmed = false;
         $editor->save();
 
@@ -92,7 +139,32 @@ class ApiAuthTest extends TestCase
 
         $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));
+        $resp->assertJson($this->errorResponse('The email address for the account in use needs to be confirmed', 401));
     }
 
-}
\ No newline at end of file
+    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,
+            ],
+        ]);
+    }
+}