]> BookStack Code Mirror - bookstack/commitdiff
Copied over work from user_permissions branch
authorDan Brown <redacted>
Sat, 21 Jan 2023 11:08:34 +0000 (11:08 +0000)
committerDan Brown <redacted>
Sat, 21 Jan 2023 11:08:34 +0000 (11:08 +0000)
Only that relevant to the additional testing work.

72 files changed:
dev/docs/permission-scenario-testing.md [new file with mode: 0644]
tests/Actions/AuditLogTest.php
tests/Actions/WebhookCallTest.php
tests/Actions/WebhookFormatTesting.php
tests/Actions/WebhookManagementTest.php
tests/Api/ApiAuthTest.php
tests/Api/AttachmentsApiTest.php
tests/Api/BooksApiTest.php
tests/Api/ChaptersApiTest.php
tests/Api/PagesApiTest.php
tests/Api/RecycleBinApiTest.php
tests/Api/TestsApi.php
tests/Api/UsersApiTest.php
tests/Auth/AuthTest.php
tests/Auth/GroupSyncServiceTest.php
tests/Auth/LdapTest.php
tests/Auth/LoginAutoInitiateTest.php
tests/Auth/MfaConfigurationTest.php
tests/Auth/MfaVerificationTest.php
tests/Auth/OidcTest.php
tests/Auth/ResetPasswordTest.php
tests/Auth/Saml2Test.php
tests/Auth/SocialAuthTest.php
tests/Auth/UserInviteTest.php
tests/Commands/ClearActivityCommandTest.php
tests/Commands/ClearViewsCommandTest.php
tests/Commands/CopyShelfPermissionsCommandTest.php
tests/Commands/RegeneratePermissionsCommandTest.php
tests/Entity/BookShelfTest.php
tests/Entity/BookTest.php
tests/Entity/ChapterTest.php
tests/Entity/ConvertTest.php
tests/Entity/EntityAccessTest.php
tests/Entity/EntitySearchTest.php
tests/Entity/ExportTest.php
tests/Entity/PageContentTest.php
tests/Entity/PageDraftTest.php
tests/Entity/PageRevisionTest.php
tests/Entity/PageTemplateTest.php
tests/Entity/PageTest.php
tests/Entity/SortTest.php
tests/Entity/TagTest.php
tests/ErrorTest.php
tests/FavouriteTest.php
tests/Helpers/EntityProvider.php
tests/Helpers/PermissionsProvider.php [new file with mode: 0644]
tests/Helpers/UserRoleProvider.php [new file with mode: 0644]
tests/HomepageTest.php
tests/LanguageTest.php
tests/Permissions/EntityPermissionsTest.php
tests/Permissions/ExportPermissionsTest.php
tests/Permissions/RolesTest.php
tests/Permissions/Scenarios/EntityRolePermissionsTest.php [new file with mode: 0644]
tests/Permissions/Scenarios/EntityUserPermissionsTest.php [new file with mode: 0644]
tests/Permissions/Scenarios/PermissionScenarioTestCase.php [new file with mode: 0644]
tests/Permissions/Scenarios/RoleContentPermissionsTest.php [new file with mode: 0644]
tests/PublicActionTest.php
tests/References/ReferencesTest.php
tests/Settings/RecycleBinTest.php
tests/Settings/RegenerateReferencesTest.php
tests/Settings/TestEmailTest.php
tests/TestCase.php
tests/ThemeTest.php
tests/Unit/FrameworkAssumptionTest.php
tests/Uploads/AttachmentTest.php
tests/Uploads/DrawioTest.php
tests/Uploads/ImageTest.php
tests/User/UserApiTokenTest.php
tests/User/UserManagementTest.php
tests/User/UserPreferencesTest.php
tests/User/UserProfileTest.php
tests/User/UserSearchTest.php

diff --git a/dev/docs/permission-scenario-testing.md b/dev/docs/permission-scenario-testing.md
new file mode 100644 (file)
index 0000000..6d0935f
--- /dev/null
@@ -0,0 +1,421 @@
+# Permission Scenario Testing
+
+Due to complexity that can arise in the various combinations of permissions, this document details scenarios and their expected results.
+
+Test cases are written ability abstract, since all abilities should act the same in theory. Functional test cases may test abilities separate due to implementation differences.
+
+Tests are categorised by the most specific element involved in the scenario, where the below list is most specific to least:
+
+- User entity permissions.
+- Role entity permissions.
+- Fallback entity permissions.
+- Role permissions.
+
+- TODO - Test fallback in the context of the above.
+
+## General Permission Logical Rules
+
+The below are some general rules we follow to standardise the behaviour of permissions in the platform:
+
+- Most specific permission application (as above) take priority and can deny less specific permissions.
+- Parent user/role entity permissions that may be inherited, are considered to essentially be applied on the item they are inherited to unless a lower level has its own permission rule for an already specific role/user.
+- Where both grant and deny exist at the same specificity, we side towards grant.
+
+## Cases
+
+### Content Role Permissions
+
+These are tests related to item/entity permissions that are set only at a role level.
+
+#### test_01_allow
+
+- Role A has role all-page permission.
+- User has Role A.
+
+User granted page permission.
+
+#### test_02_deny
+
+- Role A has no page permission.
+- User has Role A.
+
+User denied page permission.
+
+#### test_10_allow_on_own_with_own
+
+- Role A has role own-page permission.
+- User has Role A.
+- User is owner of page.
+
+User granted page permission.
+
+#### test_11_deny_on_other_with_own
+
+- Role A has role own-page permission.
+- User has Role A.
+- User is not owner of page.
+
+User denied page permission.
+
+#### test_20_multiple_role_conflicting_all
+
+- Role A has role all-page permission.
+- Role B has no page permission.
+- User has Role A & B.
+
+User granted page permission.
+
+#### test_21_multiple_role_conflicting_own
+
+- Role A has role own-page permission.
+- Role B has no page permission.
+- User has Role A & B.
+- User is owner of page.
+
+User granted page permission.
+
+---
+
+### Entity Role Permissions
+
+These are tests related to entity-level role-specific permission overrides.
+
+#### test_01_explicit_allow
+
+- Page permissions have inherit disabled.
+- Role A has entity allow page permission.
+- User has Role A.
+
+User granted page permission.
+
+#### test_02_explicit_deny
+
+- Page permissions have inherit disabled.
+- Role A has entity deny page permission.
+- User has Role A.
+
+User denied page permission.
+
+#### test_03_same_level_conflicting
+
+- Page permissions have inherit disabled.
+- Role A has entity allow page permission.
+- Role B has entity deny page permission.
+- User has both Role A & B.
+
+User granted page permission. 
+Explicit grant overrides entity deny at same level.
+
+#### test_20_inherit_allow
+
+- Page permissions have inherit enabled.
+- Chapter permissions has inherit disabled.
+- Role A has entity allow chapter permission.
+- User has Role A.
+
+User granted page permission.
+
+#### test_21_inherit_deny
+
+- Page permissions have inherit enabled.
+- Chapter permissions has inherit disabled.
+- Role A has entity deny chapter permission.
+- User has Role A.
+
+User denied page permission.
+
+#### test_22_same_level_conflict_inherit 
+
+- Page permissions have inherit enabled.
+- Chapter permissions has inherit disabled.
+- Role A has entity deny chapter permission.
+- Role B has entity allow chapter permission.
+- User has both Role A & B.
+
+User granted page permission.
+
+#### test_30_child_inherit_override_allow
+
+- Page permissions have inherit enabled.
+- Chapter permissions has inherit disabled.
+- Role A has entity deny chapter permission.
+- Role A has entity allow page permission.
+- User has Role A.
+
+User granted page permission.
+
+#### test_31_child_inherit_override_deny
+
+- Page permissions have inherit enabled.
+- Chapter permissions has inherit disabled.
+- Role A has entity allow chapter permission.
+- Role A has entity deny page permission.
+- User has Role A.
+
+User denied page permission.
+
+#### test_40_multi_role_inherit_conflict_override_deny
+
+- Page permissions have inherit enabled.
+- Chapter permissions has inherit disabled.
+- Role A has entity deny page permission.
+- Role B has entity allow chapter permission.
+- User has Role A & B.
+
+User granted page permission.
+
+#### test_41_multi_role_inherit_conflict_retain_allow
+
+- Page permissions have inherit enabled.
+- Chapter permissions has inherit disabled.
+- Role A has entity allow page permission.
+- Role B has entity deny chapter permission.
+- User has Role A & B.
+
+User granted page permission.
+
+#### test_50_role_override_allow
+
+- Page permissions have inherit enabled.
+- Role A has no page role permission.
+- Role A has entity allow page permission.
+- User has Role A.
+
+User granted page permission.
+
+#### test_51_role_override_deny
+
+- Page permissions have inherit enabled.
+- Role A has no page-view-all role permission.
+- Role A has entity deny page permission.
+- User has Role A.
+
+User denied page permission.
+
+#### test_60_inherited_role_override_allow
+
+- Page permissions have inherit enabled.
+- Chapter permissions have inherit enabled.
+- Role A has no page role permission.
+- Role A has entity allow chapter permission.
+- User has Role A.
+
+User granted page permission.
+
+#### test_61_inherited_role_override_deny
+
+- Page permissions have inherit enabled.
+- Chapter permissions have inherit enabled.
+- Role A has page role permission.
+- Role A has entity denied chapter permission.
+- User has Role A.
+
+User denied page permission.
+
+#### test_62_inherited_role_override_deny_on_own
+
+- Page permissions have inherit enabled.
+- Chapter permissions have inherit enabled.
+- Role A has own-page role permission.
+- Role A has entity denied chapter permission.
+- User has Role A.
+- User owns Page.
+
+User denied page permission.
+
+#### test_70_multi_role_inheriting_deny
+
+- Page permissions have inherit enabled.
+- Role A has all page role permission.
+- Role B has entity denied page permission.
+- User has Role A and B.
+
+User denied page permission.
+
+#### test_80_multi_role_inherited_deny_via_parent
+
+- Page permissions have inherit enabled.
+- Chapter permissions have inherit enabled.
+- Role A has all-pages role permission.
+- Role B has entity denied chapter permission.
+- User has Role A & B.
+
+User denied page permission.
+
+---
+
+### Entity User Permissions
+
+These are tests related to entity-level user-specific permission overrides.
+
+#### test_01_explicit_allow
+
+- Page permissions have inherit disabled.
+- User has entity allow page permission.
+
+User granted page permission.
+
+#### test_02_explicit_deny
+
+- Page permissions have inherit disabled.
+- User has entity deny page permission.
+
+User denied page permission.
+
+#### test_10_allow_inherit
+
+- Page permissions have inherit enabled.
+- Chapter permissions have inherit disabled.
+- User has entity allow chapter permission.
+
+User granted page permission.
+
+#### test_11_deny_inherit
+
+- Page permissions have inherit enabled.
+- Chapter permissions have inherit disabled.
+- User has entity deny chapter permission.
+
+User denied page permission.
+
+#### test_12_allow_inherit_override
+
+- Page permissions have inherit enabled.
+- Chapter permissions have inherit disabled.
+- User has entity deny chapter permission.
+- User has entity allow page permission.
+
+User granted page permission.
+
+#### test_13_deny_inherit_override
+
+- Page permissions have inherit enabled.
+- Chapter permissions have inherit disabled.
+- User has entity allow chapter permission.
+- User has entity deny page permission.
+
+User denied page permission.
+
+#### test_40_entity_role_override_allow
+
+- Page permissions have inherit disabled.
+- User has entity allow page permission.
+- Role A has entity deny page permission.
+- User has role A.
+
+User granted page permission.
+
+#### test_41_entity_role_override_deny
+
+- Page permissions have inherit disabled.
+- User has entity deny page permission.
+- Role A has entity allow page permission.
+- User has role A.
+
+User denied page permission.
+
+#### test_42_entity_role_override_allow_via_inherit
+
+- Page permissions have inherit enabled.
+- Chapter permissions have inherit disabled.
+- User has entity allow chapter permission.
+- Role A has entity deny page permission.
+- User has role A.
+
+User granted page permission.
+
+#### test_43_entity_role_override_deny_via_inherit
+
+- Page permissions have inherit enabled.
+- Chapter permissions have inherit disabled.
+- User has entity deny chapter permission.
+- Role A has entity allow page permission.
+- User has role A.
+
+User denied page permission.
+
+#### test_50_role_override_allow
+
+- Page permissions have inherit enabled.
+- Role A has no page role permission.
+- User has entity allow page permission.
+- User has Role A.
+
+User granted page permission.
+
+#### test_51_role_override_deny
+
+- Page permissions have inherit enabled.
+- Role A has all-page role permission.
+- User has entity deny page permission.
+- User has Role A.
+
+User denied page permission.
+
+#### test_60_inherited_role_override_allow
+
+- Page permissions have inherit enabled.
+- Role A has no page role permission.
+- User has entity allow chapter permission.
+- User has Role A.
+
+User granted page permission.
+
+#### test_61_inherited_role_override_deny
+
+- Page permissions have inherit enabled.
+- Role A has view-all page role permission.
+- User has entity deny chapter permission.
+- User has Role A.
+
+User denied page permission.
+
+#### test_61_inherited_role_override_deny_on_own
+
+- Page permissions have inherit enabled.
+- Role A has view-own page role permission.
+- User has entity deny chapter permission.
+- User has Role A.
+- User owns Page.
+
+User denied page permission.
+
+#### test_70_all_override_allow
+
+- Page permissions have inherit enabled.
+- Role A has no page role permission.
+- Role A has entity deny page permission.
+- User has entity allow page permission.
+- User has Role A.
+
+User granted page permission.
+
+#### test_71_all_override_deny
+
+- Page permissions have inherit enabled.
+- Role A has page-all role permission.
+- Role A has entity allow page permission.
+- User has entity deny page permission.
+- User has Role A.
+
+User denied page permission.
+
+#### test_80_inherited_all_override_allow
+
+- Page permissions have inherit enabled.
+- Role A has no page role permission.
+- Role A has entity deny chapter permission.
+- User has entity allow chapter permission.
+- User has Role A.
+
+User granted page permission.
+
+#### test_81_inherited_all_override_deny
+
+- Page permissions have inherit enabled.
+- Role A has view-all page role permission.
+- Role A has entity allow chapter permission.
+- User has entity deny chapter permission.
+- User has Role A.
+
+User denied page permission.
\ No newline at end of file
index 25fa2b7963893a5cbb3689a1b8e7698c817f731a..52b45e71212f9d90d4bf2f4de1cba6373b56394c 100644 (file)
@@ -23,17 +23,17 @@ class AuditLogTest extends TestCase
 
     public function test_only_accessible_with_right_permissions()
     {
-        $viewer = $this->getViewer();
+        $viewer = $this->users->viewer();
         $this->actingAs($viewer);
 
         $resp = $this->get('/settings/audit');
         $this->assertPermissionError($resp);
 
-        $this->giveUserPermissions($viewer, ['settings-manage']);
+        $this->permissions->grantUserRolePermissions($viewer, ['settings-manage']);
         $resp = $this->get('/settings/audit');
         $this->assertPermissionError($resp);
 
-        $this->giveUserPermissions($viewer, ['users-manage']);
+        $this->permissions->grantUserRolePermissions($viewer, ['users-manage']);
         $resp = $this->get('/settings/audit');
         $resp->assertStatus(200);
         $resp->assertSeeText('Audit Log');
@@ -41,7 +41,7 @@ class AuditLogTest extends TestCase
 
     public function test_shows_activity()
     {
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
         $this->actingAs($admin);
         $page = $this->entities->page();
         $this->activityService->add(ActivityType::PAGE_CREATE, $page);
@@ -56,7 +56,7 @@ class AuditLogTest extends TestCase
 
     public function test_shows_name_for_deleted_items()
     {
-        $this->actingAs($this->getAdmin());
+        $this->actingAs($this->users->admin());
         $page = $this->entities->page();
         $pageName = $page->name;
         $this->activityService->add(ActivityType::PAGE_CREATE, $page);
@@ -71,12 +71,12 @@ class AuditLogTest extends TestCase
 
     public function test_shows_activity_for_deleted_users()
     {
-        $viewer = $this->getViewer();
+        $viewer = $this->users->viewer();
         $this->actingAs($viewer);
         $page = $this->entities->page();
         $this->activityService->add(ActivityType::PAGE_CREATE, $page);
 
-        $this->actingAs($this->getAdmin());
+        $this->actingAs($this->users->admin());
         app(UserRepo::class)->destroy($viewer);
 
         $resp = $this->get('settings/audit');
@@ -85,7 +85,7 @@ class AuditLogTest extends TestCase
 
     public function test_filters_by_key()
     {
-        $this->actingAs($this->getAdmin());
+        $this->actingAs($this->users->admin());
         $page = $this->entities->page();
         $this->activityService->add(ActivityType::PAGE_CREATE, $page);
 
@@ -98,7 +98,7 @@ class AuditLogTest extends TestCase
 
     public function test_date_filters()
     {
-        $this->actingAs($this->getAdmin());
+        $this->actingAs($this->users->admin());
         $page = $this->entities->page();
         $this->activityService->add(ActivityType::PAGE_CREATE, $page);
 
@@ -120,8 +120,8 @@ class AuditLogTest extends TestCase
 
     public function test_user_filter()
     {
-        $admin = $this->getAdmin();
-        $editor = $this->getEditor();
+        $admin = $this->users->admin();
+        $editor = $this->users->editor();
         $this->actingAs($admin);
         $page = $this->entities->page();
         $this->activityService->add(ActivityType::PAGE_CREATE, $page);
@@ -142,7 +142,7 @@ class AuditLogTest extends TestCase
     public function test_ip_address_logged_and_visible()
     {
         config()->set('app.proxies', '*');
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $page = $this->entities->page();
 
         $this->actingAs($editor)->put($page->getUrl(), [
@@ -166,7 +166,7 @@ class AuditLogTest extends TestCase
     public function test_ip_address_is_searchable()
     {
         config()->set('app.proxies', '*');
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $page = $this->entities->page();
 
         $this->actingAs($editor)->put($page->getUrl(), [
@@ -192,7 +192,7 @@ class AuditLogTest extends TestCase
     {
         config()->set('app.proxies', '*');
         config()->set('app.env', 'demo');
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $page = $this->entities->page();
 
         $this->actingAs($editor)->put($page->getUrl(), [
@@ -215,7 +215,7 @@ class AuditLogTest extends TestCase
     {
         config()->set('app.proxies', '*');
         config()->set('app.ip_address_precision', 2);
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $page = $this->entities->page();
 
         $this->actingAs($editor)->put($page->getUrl(), [
index 7ca190200eb9286aad40fd34d2ecc2f77ada71cb..078b8bdf4beda6a6134e5ff7d673fa8a9de8cd0e 100644 (file)
@@ -88,7 +88,7 @@ class WebhookCallTest extends TestCase
         ]);
         $webhook = $this->newWebhook(['active' => true, 'endpoint' => 'https://wh.example.com'], ['all']);
         $page = $this->entities->page();
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
 
         $this->runEvent(ActivityType::PAGE_UPDATE, $page, $editor);
 
@@ -111,7 +111,7 @@ class WebhookCallTest extends TestCase
     protected function runEvent(string $event, $detail = '', ?User $user = null)
     {
         if (is_null($user)) {
-            $user = $this->getEditor();
+            $user = $this->users->editor();
         }
 
         $this->actingAs($user);
index 07341c75b2431a8c2d4fe7a33439b652d92ff6ba..be67d4d522f63184d779808a5b532e94609fec13 100644 (file)
@@ -41,7 +41,7 @@ class WebhookFormatTesting extends TestCase
     protected function getWebhookData(string $event, $detail): array
     {
         $webhook = Webhook::factory()->make();
-        $user = $this->getEditor();
+        $user = $this->users->editor();
         $formatter = WebhookFormatter::getDefault($event, $webhook, $detail, $user, time());
 
         return $formatter->format();
index f106f303ace395cb39f75db7b75e59026dc5f616..52838becab883fe2c24b0fecf9ce7383539b8433 100644 (file)
@@ -135,7 +135,7 @@ class WebhookManagementTest extends TestCase
 
     public function test_settings_manage_permission_required_for_webhook_routes()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->actingAs($editor);
 
         $routes = [
@@ -153,7 +153,7 @@ class WebhookManagementTest extends TestCase
             $this->assertPermissionError($resp);
         }
 
-        $this->giveUserPermissions($editor, ['settings-manage']);
+        $this->permissions->grantUserRolePermissions($editor, ['settings-manage']);
 
         foreach ($routes as [$method, $endpoint]) {
             $resp = $this->call($method, $endpoint);
index cc6818e27aba701ce25f912e3a18ede29e08f2c8..038c4e06717564e59c6212a62b1cc6e85d744e00 100644 (file)
@@ -16,8 +16,8 @@ class ApiAuthTest extends TestCase
 
     public function test_requests_succeed_with_default_auth()
     {
-        $viewer = $this->getViewer();
-        $this->giveUserPermissions($viewer, ['access-api']);
+        $viewer = $this->users->viewer();
+        $this->permissions->grantUserRolePermissions($viewer, ['access-api']);
 
         $resp = $this->get($this->endpoint);
         $resp->assertStatus(401);
@@ -63,7 +63,7 @@ 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());
@@ -73,7 +73,7 @@ class ApiAuthTest extends TestCase
 
     public function test_api_access_permission_required_to_access_api_with_session_auth()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->actingAs($editor, 'standard');
 
         $resp = $this->get($this->endpoint);
@@ -81,7 +81,7 @@ class ApiAuthTest extends TestCase
         auth('standard')->logout();
 
         $accessApiPermission = RolePermission::getByName('access-api');
-        $editorRole = $this->getEditor()->roles()->first();
+        $editorRole = $this->users->editor()->roles()->first();
         $editorRole->detachPermission($accessApiPermission);
 
         $editor = User::query()->where('id', '=', $editor->id)->first();
@@ -114,7 +114,7 @@ class ApiAuthTest extends TestCase
 
     public function test_token_expiry_checked()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $token = $editor->apiTokens()->first();
 
         $resp = $this->get($this->endpoint, $this->apiAuthHeader());
@@ -130,7 +130,7 @@ class ApiAuthTest extends TestCase
 
     public function test_email_confirmation_checked_using_api_auth()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $editor->email_confirmed = false;
         $editor->save();
 
index 4d1d3b340b153c3c588ebdcb7103453d5f98d4be..b03f280ac6784074ab692df20b3ded892d6c8f32 100644 (file)
@@ -50,7 +50,7 @@ class AttachmentsApiTest extends TestCase
             ],
         ]]);
 
-        $this->entities->setPermissions($page, [], []);
+        $this->permissions->setEntityPermissions($page, [], []);
 
         $resp = $this->getJson($this->baseEndpoint . '?count=1&sort=+id');
         $resp->assertJsonMissing(['data' => [
@@ -246,13 +246,13 @@ class AttachmentsApiTest extends TestCase
     public function test_attachment_not_visible_on_other_users_draft()
     {
         $this->actingAsApiAdmin();
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
 
         $page = $this->entities->page();
         $page->draft = true;
         $page->owned_by = $editor->id;
         $page->save();
-        $this->entities->regenPermissions($page);
+        $this->permissions->regenerateForEntity($page);
 
         $attachment = $this->createAttachmentForPage($page, [
             'name'  => 'my attachment',
@@ -342,7 +342,7 @@ class AttachmentsApiTest extends TestCase
 
     protected function createAttachmentForPage(Page $page, $attributes = []): Attachment
     {
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
         /** @var Attachment $attachment */
         $attachment = $page->attachments()->forceCreate(array_merge([
             'uploaded_to' => $page->id,
index 614185c93b00b755ea3a3b40cf335c5b70dc3943..dd187672eaa2445e9956baa4496414ae235375be 100644 (file)
@@ -246,7 +246,7 @@ class BooksApiTest extends TestCase
     {
         $types = ['html', 'plaintext', 'pdf', 'markdown'];
         $this->actingAsApiEditor();
-        $this->removePermissionFromUser($this->getEditor(), 'content-export');
+        $this->permissions->removeUserRolePermissions($this->users->editor(), ['content-export']);
 
         $book = $this->entities->book();
         foreach ($types as $type) {
index d2db0313fa44180ea6e8e92aa0d3fbf3a220160c..a48e3b02626f5a80a47031cb71acbf942f707696 100644 (file)
@@ -221,7 +221,7 @@ class ChaptersApiTest extends TestCase
     {
         $types = ['html', 'plaintext', 'pdf', 'markdown'];
         $this->actingAsApiEditor();
-        $this->removePermissionFromUser($this->getEditor(), 'content-export');
+        $this->permissions->removeUserRolePermissions($this->users->editor(), ['content-export']);
 
         $chapter = Chapter::visible()->has('pages')->first();
         foreach ($types as $type) {
index 8c533680fbb76d75716fc4a465c0b70882a62883..12b38bc076aa8a61d3df534dacdec62f555b809a 100644 (file)
@@ -209,7 +209,7 @@ class PagesApiTest extends TestCase
         $this->actingAsApiEditor();
         $page = $this->entities->page();
         $chapter = Chapter::visible()->where('book_id', '!=', $page->book_id)->first();
-        $this->entities->setPermissions($chapter, ['view'], [$this->getEditor()->roles()->first()]);
+        $this->permissions->setEntityPermissions($chapter, ['view'], [$this->users->editor()->roles()->first()]);
         $details = [
             'name'       => 'My updated API page',
             'chapter_id' => $chapter->id,
@@ -315,7 +315,7 @@ class PagesApiTest extends TestCase
     {
         $types = ['html', 'plaintext', 'pdf', 'markdown'];
         $this->actingAsApiEditor();
-        $this->removePermissionFromUser($this->getEditor(), 'content-export');
+        $this->permissions->removeUserRolePermissions($this->users->editor(), ['content-export']);
 
         $page = $this->entities->page();
         foreach ($types as $type) {
index bc7249987dd0da3e01a9d7ee777d336ffb8b98af..d174838c27d0db38d5346cae9478ba1c0b3d09bd 100644 (file)
@@ -21,8 +21,8 @@ class RecycleBinApiTest extends TestCase
 
     public function test_settings_manage_permission_needed_for_all_endpoints()
     {
-        $editor = $this->getEditor();
-        $this->giveUserPermissions($editor, ['settings-manage']);
+        $editor = $this->users->editor();
+        $this->permissions->grantUserRolePermissions($editor, ['settings-manage']);
         $this->actingAs($editor);
 
         foreach ($this->endpointMap as [$method, $uri]) {
@@ -34,8 +34,8 @@ class RecycleBinApiTest extends TestCase
 
     public function test_restrictions_manage_all_permission_needed_for_all_endpoints()
     {
-        $editor = $this->getEditor();
-        $this->giveUserPermissions($editor, ['restrictions-manage-all']);
+        $editor = $this->users->editor();
+        $this->permissions->grantUserRolePermissions($editor, ['restrictions-manage-all']);
         $this->actingAs($editor);
 
         foreach ($this->endpointMap as [$method, $uri]) {
@@ -47,7 +47,7 @@ class RecycleBinApiTest extends TestCase
 
     public function test_index_endpoint_returns_expected_page()
     {
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
 
         $page = $this->entities->page();
         $book = $this->entities->book();
@@ -82,7 +82,7 @@ class RecycleBinApiTest extends TestCase
 
     public function test_index_endpoint_returns_children_count()
     {
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
 
         $book = Book::query()->whereHas('pages')->whereHas('chapters')->withCount(['pages', 'chapters'])->first();
         $this->actingAs($admin)->delete($book->getUrl());
@@ -109,7 +109,7 @@ class RecycleBinApiTest extends TestCase
 
     public function test_index_endpoint_returns_parent()
     {
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
         $page = $this->entities->pageWithinChapter();
 
         $this->actingAs($admin)->delete($page->getUrl());
index 0cdd93741272c608b38233e8f75365ab5bc740cb..501f2875458616da28bf33906ae49ac267c0465d 100644 (file)
@@ -12,7 +12,7 @@ trait TestsApi
      */
     protected function actingAsApiEditor()
     {
-        $this->actingAs($this->getEditor(), 'api');
+        $this->actingAs($this->users->editor(), 'api');
 
         return $this;
     }
@@ -22,7 +22,7 @@ trait TestsApi
      */
     protected function actingAsApiAdmin()
     {
-        $this->actingAs($this->getAdmin(), 'api');
+        $this->actingAs($this->users->admin(), 'api');
 
         return $this;
     }
index 739981f24b17d1cd883398715d4973fcc7395a1e..c89f9e6e30b83e26154f99174c59d86519393daa 100644 (file)
@@ -175,7 +175,7 @@ class UsersApiTest extends TestCase
     {
         $this->actingAsApiAdmin();
         /** @var User $user */
-        $user = $this->getAdmin();
+        $user = $this->users->admin();
         $roles = Role::query()->pluck('id');
         $resp = $this->putJson($this->baseEndpoint . "/{$user->id}", [
             'name'             => 'My updated user',
@@ -204,7 +204,7 @@ class UsersApiTest extends TestCase
     {
         $this->actingAsApiAdmin();
         /** @var User $user */
-        $user = $this->getAdmin();
+        $user = $this->users->admin();
         $roleCount = $user->roles()->count();
         $resp = $this->putJson($this->baseEndpoint . "/{$user->id}", []);
 
@@ -222,7 +222,7 @@ class UsersApiTest extends TestCase
     {
         $this->actingAsApiAdmin();
         /** @var User $user */
-        $user = User::query()->where('id', '!=', $this->getAdmin()->id)
+        $user = User::query()->where('id', '!=', $this->users->admin()->id)
             ->whereNull('system_name')
             ->first();
 
@@ -236,7 +236,7 @@ class UsersApiTest extends TestCase
     {
         $this->actingAsApiAdmin();
         /** @var User $user */
-        $user = User::query()->where('id', '!=', $this->getAdmin()->id)
+        $user = User::query()->where('id', '!=', $this->users->admin()->id)
             ->whereNull('system_name')
             ->first();
         $entityChain = $this->entities->createChainBelongingToUser($user);
index 3220b2aac72138c00a965093cd5d59e54d4c6ff5..fe7e62568cf8d8124b67913e6110e1089d7ecdfe 100644 (file)
@@ -44,7 +44,7 @@ class AuthTest extends TestCase
 
     public function test_mfa_session_cleared_on_logout()
     {
-        $user = $this->getEditor();
+        $user = $this->users->editor();
         $mfaSession = $this->app->make(MfaSession::class);
 
         $mfaSession->markVerifiedForUser($user);
@@ -94,7 +94,7 @@ class AuthTest extends TestCase
 
     public function test_login_authenticates_nonadmins_on_default_guard_only()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $editor->password = bcrypt('password');
         $editor->save();
 
@@ -120,7 +120,7 @@ class AuthTest extends TestCase
     public function test_logged_in_user_with_unconfirmed_email_is_logged_out()
     {
         $this->setSettings(['registration-confirmation' => 'true']);
-        $user = $this->getEditor();
+        $user = $this->users->editor();
         $user->email_confirmed = false;
         $user->save();
 
index 2fad53b261943fc2fb6b8544ae6ab53170700a52..dbf4110d8719ce918b4a2f7cb049dfe8ffe8ab8b 100644 (file)
@@ -11,7 +11,7 @@ class GroupSyncServiceTest extends TestCase
 {
     public function test_user_is_assigned_to_matching_roles()
     {
-        $user = $this->getViewer();
+        $user = $this->users->viewer();
 
         $roleA = Role::factory()->create(['display_name' => 'Wizards']);
         $roleB = Role::factory()->create(['display_name' => 'Gremlins']);
@@ -33,7 +33,7 @@ class GroupSyncServiceTest extends TestCase
 
     public function test_multiple_values_in_role_external_auth_id_handled()
     {
-        $user = $this->getViewer();
+        $user = $this->users->viewer();
         $role = Role::factory()->create(['display_name' => 'ABC123', 'external_auth_id' => 'sales, engineering, developers, marketers']);
         $this->assertFalse($user->hasRole($role->id));
 
@@ -45,7 +45,7 @@ class GroupSyncServiceTest extends TestCase
 
     public function test_commas_can_be_used_in_external_auth_id_if_escaped()
     {
-        $user = $this->getViewer();
+        $user = $this->users->viewer();
         $role = Role::factory()->create(['display_name' => 'ABC123', 'external_auth_id' => 'sales\,-developers, marketers']);
         $this->assertFalse($user->hasRole($role->id));
 
@@ -57,7 +57,7 @@ class GroupSyncServiceTest extends TestCase
 
     public function test_external_auth_id_matches_ignoring_case()
     {
-        $user = $this->getViewer();
+        $user = $this->users->viewer();
         $role = Role::factory()->create(['display_name' => 'ABC123', 'external_auth_id' => 'WaRRioRs']);
         $this->assertFalse($user->hasRole($role->id));
 
index 978420f869ede41d480cbe7258f1c6600773e3d5..cac2ea5e10f1f11a07fb764f7c1fc89be074319d 100644 (file)
@@ -235,7 +235,7 @@ class LdapTest extends TestCase
 
     public function test_user_edit_form()
     {
-        $editUser = $this->getNormalUser();
+        $editUser = $this->users->viewer();
         $editPage = $this->asAdmin()->get("/settings/users/{$editUser->id}");
         $editPage->assertSee('Edit User');
         $editPage->assertDontSee('Password');
@@ -257,7 +257,7 @@ class LdapTest extends TestCase
 
     public function test_non_admins_cannot_change_auth_id()
     {
-        $testUser = $this->getNormalUser();
+        $testUser = $this->users->viewer();
         $this->actingAs($testUser)
             ->get('/settings/users/' . $testUser->id)
             ->assertDontSee('External Authentication');
index 2d03844356eb62b68c67d9a2333b2ed2a0e54126..fcb4431af9c14ff8ecb068acf22527178b020c03 100644 (file)
@@ -70,7 +70,7 @@ class LoginAutoInitiateTest extends TestCase
         config()->set([
             'auth.method' => 'oidc',
         ]);
-        $this->actingAs($this->getEditor());
+        $this->actingAs($this->users->editor());
 
         $req = $this->post('/logout');
         $req->assertRedirect('/login?prevent_auto_init=true');
index 3416263f3f44bac46579ec364007a5ea30444d2d..fb941f00b7d384f1c3ebae8075a8d877bf7774de 100644 (file)
@@ -13,7 +13,7 @@ class MfaConfigurationTest extends TestCase
 {
     public function test_totp_setup()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->assertDatabaseMissing('mfa_values', ['user_id' => $editor->id]);
 
         // Setup page state
@@ -66,7 +66,7 @@ class MfaConfigurationTest extends TestCase
 
     public function test_backup_codes_setup()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->assertDatabaseMissing('mfa_values', ['user_id' => $editor->id]);
 
         // Setup page state
@@ -112,8 +112,8 @@ class MfaConfigurationTest extends TestCase
 
     public function test_mfa_method_count_is_visible_on_user_edit_page()
     {
-        $user = $this->getEditor();
-        $resp = $this->actingAs($this->getAdmin())->get($user->getEditUrl());
+        $user = $this->users->editor();
+        $resp = $this->actingAs($this->users->admin())->get($user->getEditUrl());
         $resp->assertSee('0 methods configured');
 
         MfaValue::upsertWithValue($user, MfaValue::METHOD_TOTP, 'test');
@@ -127,17 +127,17 @@ class MfaConfigurationTest extends TestCase
 
     public function test_mfa_setup_link_only_shown_when_viewing_own_user_edit_page()
     {
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
         $resp = $this->actingAs($admin)->get($admin->getEditUrl());
         $this->withHtml($resp)->assertElementExists('a[href$="/mfa/setup"]');
 
-        $resp = $this->actingAs($admin)->get($this->getEditor()->getEditUrl());
+        $resp = $this->actingAs($admin)->get($this->users->editor()->getEditUrl());
         $this->withHtml($resp)->assertElementNotExists('a[href$="/mfa/setup"]');
     }
 
     public function test_mfa_indicator_shows_in_user_list()
     {
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
         User::query()->where('id', '!=', $admin->id)->delete();
 
         $resp = $this->actingAs($admin)->get('/settings/users');
@@ -150,7 +150,7 @@ class MfaConfigurationTest extends TestCase
 
     public function test_remove_mfa_method()
     {
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
 
         MfaValue::upsertWithValue($admin, MfaValue::METHOD_TOTP, 'test');
         $this->assertEquals(1, $admin->mfaValues()->count());
@@ -168,7 +168,7 @@ class MfaConfigurationTest extends TestCase
 
     public function test_totp_setup_url_shows_correct_user_when_setup_forced_upon_login()
     {
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
         /** @var Role $role */
         $role = $admin->roles()->first();
         $role->mfa_enforced = true;
index ba4c9b983a3feebce56e44c34cb5ca8904030d20..e23250314907a1fbd81706ab91823888a1439c49 100644 (file)
@@ -140,7 +140,7 @@ class MfaVerificationTest extends TestCase
 
     public function test_both_mfa_options_available_if_set_on_profile()
     {
-        $user = $this->getEditor();
+        $user = $this->users->editor();
         $user->password = Hash::make('password');
         $user->save();
 
@@ -165,7 +165,7 @@ class MfaVerificationTest extends TestCase
 
     public function test_mfa_required_with_no_methods_leads_to_setup()
     {
-        $user = $this->getEditor();
+        $user = $this->users->editor();
         $user->password = Hash::make('password');
         $user->save();
         /** @var Role $role */
@@ -222,7 +222,7 @@ class MfaVerificationTest extends TestCase
         // Attempted login user, who has configured mfa, access
         // Sets up user that has MFA required after attempted login.
         $loginService = $this->app->make(LoginService::class);
-        $user = $this->getEditor();
+        $user = $this->users->editor();
         /** @var Role $role */
         $role = $user->roles->first();
         $role->mfa_enforced = true;
@@ -257,7 +257,7 @@ class MfaVerificationTest extends TestCase
     protected function startTotpLogin(): array
     {
         $secret = $this->app->make(TotpService::class)->generateSecret();
-        $user = $this->getEditor();
+        $user = $this->users->editor();
         $user->password = Hash::make('password');
         $user->save();
         MfaValue::upsertWithValue($user, MfaValue::METHOD_TOTP, $secret);
@@ -274,7 +274,7 @@ class MfaVerificationTest extends TestCase
      */
     protected function startBackupCodeLogin($codes = ['kzzu6-1pgll', 'bzxnf-plygd', 'bwdsp-ysl51', '1vo93-ioy7n', 'lf7nw-wdyka', 'xmtrd-oplac']): array
     {
-        $user = $this->getEditor();
+        $user = $this->users->editor();
         $user->password = Hash::make('password');
         $user->save();
         MfaValue::upsertWithValue($user, MfaValue::METHOD_BACKUP_CODES, json_encode($codes));
index db1f87bd5676a585ddb9867a7636cbc841211657..32c2d4ae2e5f3225280c6e0cf7a6f03325724920 100644 (file)
@@ -93,7 +93,7 @@ class OidcTest extends TestCase
 
     public function test_logout_route_functions()
     {
-        $this->actingAs($this->getEditor());
+        $this->actingAs($this->users->editor());
         $this->post('/logout');
         $this->assertFalse(auth()->check());
     }
@@ -228,7 +228,7 @@ class OidcTest extends TestCase
 
     public function test_auth_login_as_existing_user()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $editor->external_auth_id = 'benny505';
         $editor->save();
 
@@ -245,7 +245,7 @@ class OidcTest extends TestCase
 
     public function test_auth_login_as_existing_user_email_with_different_auth_id_fails()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $editor->external_auth_id = 'editor101';
         $editor->save();
 
index 7b2d2e72b1cf910f42032543ecad09d24212901f..72e26f10ceb54269b53fb3b2ff410fb29be1bc4e 100644 (file)
@@ -85,7 +85,7 @@ class ResetPasswordTest extends TestCase
 
     public function test_reset_request_is_throttled()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         Notification::fake();
         $this->get('/password/email');
         $this->followingRedirects()->post('/password/email', [
index 4c8d14dd5c81ab8491b0e4edecde62e48dc321ea..0ee419610ca330e959cb492812e75e0e714cf219 100644 (file)
@@ -170,7 +170,7 @@ class Saml2Test extends TestCase
             'saml2.onelogin.strict' => false,
         ]);
 
-        $resp = $this->actingAs($this->getEditor())->get('/');
+        $resp = $this->actingAs($this->users->editor())->get('/');
         $this->withHtml($resp)->assertElementContains('form[action$="/saml2/logout"] button', 'Logout');
     }
 
index 67da771a5176f439200e95a9fbd27d257f79539f..24deedd5f349cc38234b1ce9d105bb2326b8b2c3 100644 (file)
@@ -77,18 +77,18 @@ class SocialAuthTest extends TestCase
 
         // Test social callback with matching social account
         DB::table('social_accounts')->insert([
-            'user_id'   => $this->getAdmin()->id,
+            'user_id'   => $this->users->admin()->id,
             'driver'    => 'github',
             'driver_id' => 'logintest123',
         ]);
         $resp = $this->followingRedirects()->get('/login/service/github/callback');
         $resp->assertDontSee('login-form');
-        $this->assertActivityExists(ActivityType::AUTH_LOGIN, null, 'github; (' . $this->getAdmin()->id . ') ' . $this->getAdmin()->name);
+        $this->assertActivityExists(ActivityType::AUTH_LOGIN, null, 'github; (' . $this->users->admin()->id . ') ' . $this->users->admin()->name);
     }
 
     public function test_social_account_detach()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         config([
             'GITHUB_APP_ID' => 'abc123', 'GITHUB_APP_SECRET' => '123abc',
             'APP_URL'       => 'http://localhost',
index ccbb538a659e96516f782c7d8aba935c4d00019d..e82ce46387661c5f5098bc9ab9d1069e8ffd4edd 100644 (file)
@@ -17,7 +17,7 @@ class UserInviteTest extends TestCase
     public function test_user_creation_creates_invite()
     {
         Notification::fake();
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
 
         $email = Str::random(16) . '@example.com';
         $resp = $this->actingAs($admin)->post('/settings/users/create', [
@@ -38,7 +38,7 @@ class UserInviteTest extends TestCase
     public function test_user_invite_sent_in_selected_language()
     {
         Notification::fake();
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
 
         $email = Str::random(16) . '@example.com';
         $resp = $this->actingAs($admin)->post('/settings/users/create', [
@@ -62,7 +62,7 @@ class UserInviteTest extends TestCase
     public function test_invite_set_password()
     {
         Notification::fake();
-        $user = $this->getViewer();
+        $user = $this->users->viewer();
         $inviteService = app(UserInviteService::class);
 
         $inviteService->sendInvitation($user);
@@ -91,7 +91,7 @@ class UserInviteTest extends TestCase
     public function test_invite_set_has_password_validation()
     {
         Notification::fake();
-        $user = $this->getViewer();
+        $user = $this->users->viewer();
         $inviteService = app(UserInviteService::class);
 
         $inviteService->sendInvitation($user);
@@ -126,7 +126,7 @@ class UserInviteTest extends TestCase
     public function test_token_expires_after_two_weeks()
     {
         Notification::fake();
-        $user = $this->getViewer();
+        $user = $this->users->viewer();
         $inviteService = app(UserInviteService::class);
 
         $inviteService->sendInvitation($user);
index cf2fba0d670e8a5e8a8f6a7ce4515a8bd83590dd..b2624e23df285ca133ed72c6abee140ca4a93ca8 100644 (file)
@@ -19,7 +19,7 @@ class ClearActivityCommandTest extends TestCase
         $this->assertDatabaseHas('activities', [
             'type'      => 'page_update',
             'entity_id' => $page->id,
-            'user_id'   => $this->getEditor()->id,
+            'user_id'   => $this->users->editor()->id,
         ]);
 
         DB::rollBack();
index bbd06fa01086aeeb9fa3134669975ff9af83e2c7..c9179089b20a7a9e73d7f2c55399157aaf3834b8 100644 (file)
@@ -16,7 +16,7 @@ class ClearViewsCommandTest extends TestCase
         $this->get($page->getUrl());
 
         $this->assertDatabaseHas('views', [
-            'user_id'     => $this->getEditor()->id,
+            'user_id'     => $this->users->editor()->id,
             'viewable_id' => $page->id,
             'views'       => 1,
         ]);
@@ -27,7 +27,7 @@ class ClearViewsCommandTest extends TestCase
         $this->assertTrue($exitCode === 0, 'Command executed successfully');
 
         $this->assertDatabaseMissing('views', [
-            'user_id' => $this->getEditor()->id,
+            'user_id' => $this->users->editor()->id,
         ]);
     }
 }
index cb9a845fda300487b28ecb694c3976cb5d88041e..c4b9fe6f305142cad1795f27b34b2145c67b0fb2 100644 (file)
@@ -18,11 +18,11 @@ class CopyShelfPermissionsCommandTest extends TestCase
     {
         $shelf = $this->entities->shelf();
         $child = $shelf->books()->first();
-        $editorRole = $this->getEditor()->roles()->first();
+        $editorRole = $this->users->editor()->roles()->first();
         $this->assertFalse($child->hasPermissions(), 'Child book should not be restricted by default');
         $this->assertTrue($child->permissions()->count() === 0, 'Child book should have no permissions by default');
 
-        $this->entities->setPermissions($shelf, ['view', 'update'], [$editorRole]);
+        $this->permissions->setEntityPermissions($shelf, ['view', 'update'], [$editorRole]);
         $this->artisan('bookstack:copy-shelf-permissions', [
             '--slug' => $shelf->slug,
         ]);
@@ -43,11 +43,11 @@ class CopyShelfPermissionsCommandTest extends TestCase
         $shelf = $this->entities->shelf();
         Bookshelf::query()->where('id', '!=', $shelf->id)->delete();
         $child = $shelf->books()->first();
-        $editorRole = $this->getEditor()->roles()->first();
+        $editorRole = $this->users->editor()->roles()->first();
         $this->assertFalse($child->hasPermissions(), 'Child book should not be restricted by default');
         $this->assertTrue($child->permissions()->count() === 0, 'Child book should have no permissions by default');
 
-        $this->entities->setPermissions($shelf, ['view', 'update'], [$editorRole]);
+        $this->permissions->setEntityPermissions($shelf, ['view', 'update'], [$editorRole]);
         $this->artisan('bookstack:copy-shelf-permissions --all')
             ->expectsQuestion('Permission settings for all shelves will be cascaded. Books assigned to multiple shelves will receive only the permissions of it\'s last processed shelf. Are you sure you want to proceed?', 'y');
         $child = $shelf->books()->first();
index d514e5f9d90262c9ece2d2ad73ab425ab8f192bd..cc53b460d890a2f7826d29bdd86e2bf3b313717a 100644 (file)
@@ -2,8 +2,7 @@
 
 namespace Tests\Commands;
 
-use BookStack\Auth\Permissions\JointPermission;
-use BookStack\Entities\Models\Page;
+use BookStack\Auth\Permissions\CollapsedPermission;
 use Illuminate\Support\Facades\Artisan;
 use Illuminate\Support\Facades\DB;
 use Tests\TestCase;
@@ -13,15 +12,23 @@ class RegeneratePermissionsCommandTest extends TestCase
     public function test_regen_permissions_command()
     {
         DB::rollBack();
-        JointPermission::query()->truncate();
-        $page = Page::first();
+        $page = $this->entities->page();
+        $editor = $this->users->editor();
+        $this->permissions->addEntityPermission($page, ['view'], null, $editor);
+        CollapsedPermission::query()->truncate();
 
-        $this->assertDatabaseMissing('joint_permissions', ['entity_id' => $page->id]);
+        $this->assertDatabaseMissing('entity_permissions_collapsed', ['entity_id' => $page->id]);
 
         $exitCode = Artisan::call('bookstack:regenerate-permissions');
         $this->assertTrue($exitCode === 0, 'Command executed successfully');
-        DB::beginTransaction();
 
-        $this->assertDatabaseHas('joint_permissions', ['entity_id' => $page->id]);
+        $this->assertDatabaseHas('entity_permissions_collapsed', [
+            'entity_id' => $page->id,
+            'user_id' => $editor->id,
+            'view' => 1,
+        ]);
+
+        CollapsedPermission::query()->truncate();
+        DB::beginTransaction();
     }
 }
index 5d919f12bc2ed92775e4ed147b75c4c194877c91..5c6489281720711c56c3ada7d1458eb634876488 100644 (file)
@@ -16,21 +16,20 @@ class BookShelfTest extends TestCase
 
     public function test_shelves_shows_in_header_if_have_view_permissions()
     {
-        $viewer = $this->getViewer();
+        $viewer = $this->users->viewer();
         $resp = $this->actingAs($viewer)->get('/');
         $this->withHtml($resp)->assertElementContains('header', 'Shelves');
 
         $viewer->roles()->delete();
-        $this->giveUserPermissions($viewer);
         $resp = $this->actingAs($viewer)->get('/');
         $this->withHtml($resp)->assertElementNotContains('header', 'Shelves');
 
-        $this->giveUserPermissions($viewer, ['bookshelf-view-all']);
+        $this->permissions->grantUserRolePermissions($viewer, ['bookshelf-view-all']);
         $resp = $this->actingAs($viewer)->get('/');
         $this->withHtml($resp)->assertElementContains('header', 'Shelves');
 
         $viewer->roles()->delete();
-        $this->giveUserPermissions($viewer, ['bookshelf-view-own']);
+        $this->permissions->grantUserRolePermissions($viewer, ['bookshelf-view-own']);
         $resp = $this->actingAs($viewer)->get('/');
         $this->withHtml($resp)->assertElementContains('header', 'Shelves');
     }
@@ -38,14 +37,14 @@ class BookShelfTest extends TestCase
     public function test_shelves_shows_in_header_if_have_any_shelve_view_permission()
     {
         $user = User::factory()->create();
-        $this->giveUserPermissions($user, ['image-create-all']);
+        $this->permissions->grantUserRolePermissions($user, ['image-create-all']);
         $shelf = $this->entities->shelf();
         $userRole = $user->roles()->first();
 
         $resp = $this->actingAs($user)->get('/');
         $this->withHtml($resp)->assertElementNotContains('header', 'Shelves');
 
-        $this->entities->setPermissions($shelf, ['view'], [$userRole]);
+        $this->permissions->setEntityPermissions($shelf, ['view'], [$userRole]);
 
         $resp = $this->get('/');
         $this->withHtml($resp)->assertElementContains('header', 'Shelves');
@@ -69,7 +68,7 @@ class BookShelfTest extends TestCase
         $resp->assertSee($book->name);
         $resp->assertSee($book->getUrl());
 
-        $this->entities->setPermissions($book, []);
+        $this->permissions->setEntityPermissions($book, []);
 
         $resp = $this->asEditor()->get('/shelves');
         $resp->assertDontSee($book->name);
@@ -93,7 +92,7 @@ class BookShelfTest extends TestCase
             ],
         ]));
         $resp->assertRedirect();
-        $editorId = $this->getEditor()->id;
+        $editorId = $this->users->editor()->id;
         $this->assertDatabaseHas('bookshelves', array_merge($shelfInfo, ['created_by' => $editorId, 'updated_by' => $editorId]));
 
         $shelf = Bookshelf::where('name', '=', $shelfInfo['name'])->first();
@@ -186,13 +185,13 @@ class BookShelfTest extends TestCase
         $this->withHtml($resp)->assertElementContains('.book-content a.grid-card:nth-child(1)', $books[0]->name);
         $this->withHtml($resp)->assertElementNotContains('.book-content a.grid-card:nth-child(3)', $books[0]->name);
 
-        setting()->putUser($this->getEditor(), 'shelf_books_sort_order', 'desc');
+        setting()->putUser($this->users->editor(), 'shelf_books_sort_order', 'desc');
         $resp = $this->asEditor()->get($shelf->getUrl());
         $this->withHtml($resp)->assertElementNotContains('.book-content a.grid-card:nth-child(1)', $books[0]->name);
         $this->withHtml($resp)->assertElementContains('.book-content a.grid-card:nth-child(3)', $books[0]->name);
 
-        setting()->putUser($this->getEditor(), 'shelf_books_sort_order', 'desc');
-        setting()->putUser($this->getEditor(), 'shelf_books_sort', 'name');
+        setting()->putUser($this->users->editor(), 'shelf_books_sort_order', 'desc');
+        setting()->putUser($this->users->editor(), 'shelf_books_sort', 'name');
         $resp = $this->asEditor()->get($shelf->getUrl());
         $this->withHtml($resp)->assertElementContains('.book-content a.grid-card:nth-child(1)', 'hdgfgdfg');
         $this->withHtml($resp)->assertElementContains('.book-content a.grid-card:nth-child(2)', 'bsfsdfsdfsd');
@@ -224,7 +223,7 @@ class BookShelfTest extends TestCase
         $resp->assertRedirect($shelf->getUrl());
         $this->assertSessionHas('success');
 
-        $editorId = $this->getEditor()->id;
+        $editorId = $this->users->editor()->id;
         $this->assertDatabaseHas('bookshelves', array_merge($shelfInfo, ['id' => $shelf->id, 'created_by' => $editorId, 'updated_by' => $editorId]));
 
         $shelfPage = $this->get($shelf->getUrl());
@@ -294,11 +293,11 @@ class BookShelfTest extends TestCase
         $resp->assertSee("action=\"{$shelf->getUrl('/copy-permissions')}\"", false);
 
         $child = $shelf->books()->first();
-        $editorRole = $this->getEditor()->roles()->first();
+        $editorRole = $this->users->editor()->roles()->first();
         $this->assertFalse($child->hasPermissions(), 'Child book should not be restricted by default');
         $this->assertTrue($child->permissions()->count() === 0, 'Child book should have no permissions by default');
 
-        $this->entities->setPermissions($shelf, ['view', 'update'], [$editorRole]);
+        $this->permissions->setEntityPermissions($shelf, ['view', 'update'], [$editorRole]);
         $resp = $this->post($shelf->getUrl('/copy-permissions'));
         $child = $shelf->books()->first();
 
index 9e2750fd05e647cdb9fa1a350b05930e9c80b81d..8435c534f5f76e46847921a635845bfa43f37e78 100644 (file)
@@ -221,7 +221,7 @@ class BookTest extends TestCase
     public function test_books_view_shows_view_toggle_option()
     {
         /** @var Book $book */
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         setting()->putUser($editor, 'books_view_type', 'list');
 
         $resp = $this->actingAs($editor)->get('/books');
@@ -304,7 +304,7 @@ class BookTest extends TestCase
         // Hide child content
         /** @var BookChild $page */
         foreach ($book->getDirectChildren() as $child) {
-            $this->entities->setPermissions($child, [], []);
+            $this->permissions->setEntityPermissions($child, [], []);
         }
 
         $this->asEditor()->post($book->getUrl('/copy'), ['name' => 'My copy book']);
@@ -318,8 +318,8 @@ class BookTest extends TestCase
     {
         /** @var Book $book */
         $book = Book::query()->whereHas('chapters')->whereHas('directPages')->whereHas('chapters')->first();
-        $viewer = $this->getViewer();
-        $this->giveUserPermissions($viewer, ['book-create-all']);
+        $viewer = $this->users->viewer();
+        $this->permissions->grantUserRolePermissions($viewer, ['book-create-all']);
 
         $this->actingAs($viewer)->post($book->getUrl('/copy'), ['name' => 'My copy book']);
         /** @var Book $copy */
@@ -354,9 +354,9 @@ class BookTest extends TestCase
         $shelfA->appendBook($book);
         $shelfB->appendBook($book);
 
-        $viewer = $this->getViewer();
-        $this->giveUserPermissions($viewer, ['book-update-all', 'book-create-all', 'bookshelf-update-all']);
-        $this->entities->setPermissions($shelfB);
+        $viewer = $this->users->viewer();
+        $this->permissions->grantUserRolePermissions($viewer, ['book-update-all', 'book-create-all', 'bookshelf-update-all']);
+        $this->permissions->setEntityPermissions($shelfB);
 
 
         $this->asEditor()->post($book->getUrl('/copy'), ['name' => 'My copy book']);
index b726280c9184afe8ded72ca442d8b53c58d67bb9..7fa32c252675a655748369ee3fab793a57850aa8 100644 (file)
@@ -101,7 +101,7 @@ class ChapterTest extends TestCase
         // Hide pages to all non-admin roles
         /** @var Page $page */
         foreach ($chapter->pages as $page) {
-            $this->entities->setPermissions($page, [], []);
+            $this->permissions->setEntityPermissions($page, [], []);
         }
 
         $this->asEditor()->post($chapter->getUrl('/copy'), [
@@ -116,8 +116,8 @@ class ChapterTest extends TestCase
     public function test_copy_does_not_copy_pages_if_user_cant_page_create()
     {
         $chapter = $this->entities->chapterHasPages();
-        $viewer = $this->getViewer();
-        $this->giveUserPermissions($viewer, ['chapter-create-all']);
+        $viewer = $this->users->viewer();
+        $this->permissions->grantUserRolePermissions($viewer, ['chapter-create-all']);
 
         // Lacking permission results in no copied pages
         $this->actingAs($viewer)->post($chapter->getUrl('/copy'), [
@@ -128,7 +128,7 @@ class ChapterTest extends TestCase
         $newChapter = Chapter::query()->where('name', '=', 'My copied chapter')->first();
         $this->assertEquals(0, $newChapter->pages()->count());
 
-        $this->giveUserPermissions($viewer, ['page-create-all']);
+        $this->permissions->grantUserRolePermissions($viewer, ['page-create-all']);
 
         // Having permission rules in copied pages
         $this->actingAs($viewer)->post($chapter->getUrl('/copy'), [
@@ -144,7 +144,7 @@ class ChapterTest extends TestCase
     {
         $chapter = $this->entities->chapter();
 
-        $resp = $this->actingAs($this->getViewer())->get($chapter->getUrl());
+        $resp = $this->actingAs($this->users->viewer())->get($chapter->getUrl());
         $this->withHtml($resp)->assertLinkNotExists($chapter->book->getUrl('sort'));
 
         $resp = $this->asEditor()->get($chapter->getUrl());
index 16dd890683640499ed0f5ba33c3b669b478a7c26..4beec7fa61dfbfa92d7391499f385cf6b15c10b8 100644 (file)
@@ -49,16 +49,16 @@ class ConvertTest extends TestCase
     public function test_convert_chapter_to_book_requires_permissions()
     {
         $chapter = $this->entities->chapter();
-        $user = $this->getViewer();
+        $user = $this->users->viewer();
 
         $permissions = ['chapter-delete-all', 'book-create-all', 'chapter-update-all'];
-        $this->giveUserPermissions($user, $permissions);
+        $this->permissions->grantUserRolePermissions($user, $permissions);
 
         foreach ($permissions as $permission) {
-            $this->removePermissionFromUser($user, $permission);
+            $this->permissions->removeUserRolePermissions($user, [$permission]);
             $resp = $this->actingAs($user)->post($chapter->getUrl('/convert-to-book'));
             $this->assertPermissionError($resp);
-            $this->giveUserPermissions($user, [$permission]);
+            $this->permissions->grantUserRolePermissions($user, [$permission]);
         }
 
         $resp = $this->actingAs($user)->post($chapter->getUrl('/convert-to-book'));
@@ -122,16 +122,16 @@ class ConvertTest extends TestCase
     public function test_book_convert_to_shelf_requires_permissions()
     {
         $book = $this->entities->book();
-        $user = $this->getViewer();
+        $user = $this->users->viewer();
 
         $permissions = ['book-delete-all', 'bookshelf-create-all', 'book-update-all', 'book-create-all'];
-        $this->giveUserPermissions($user, $permissions);
+        $this->permissions->grantUserRolePermissions($user, $permissions);
 
         foreach ($permissions as $permission) {
-            $this->removePermissionFromUser($user, $permission);
+            $this->permissions->removeUserRolePermissions($user, [$permission]);
             $resp = $this->actingAs($user)->post($book->getUrl('/convert-to-shelf'));
             $this->assertPermissionError($resp);
-            $this->giveUserPermissions($user, [$permission]);
+            $this->permissions->grantUserRolePermissions($user, [$permission]);
         }
 
         $resp = $this->actingAs($user)->post($book->getUrl('/convert-to-shelf'));
index 2bb32fde8d61781aa51aeabfeaeac7b4ae54af69..ab7587a3b273e3bdd06c7f0e4e4567fa6c9cddc5 100644 (file)
@@ -11,8 +11,8 @@ class EntityAccessTest extends TestCase
     public function test_entities_viewable_after_creator_deletion()
     {
         // Create required assets and revisions
-        $creator = $this->getEditor();
-        $updater = $this->getViewer();
+        $creator = $this->users->editor();
+        $updater = $this->users->viewer();
         $entities = $this->entities->createChainBelongingToUser($creator, $updater);
         app()->make(UserRepo::class)->destroy($creator);
         $this->entities->updatePage($entities['page'], ['html' => '<p>hello!</p>>']);
@@ -23,8 +23,8 @@ class EntityAccessTest extends TestCase
     public function test_entities_viewable_after_updater_deletion()
     {
         // Create required assets and revisions
-        $creator = $this->getViewer();
-        $updater = $this->getEditor();
+        $creator = $this->users->viewer();
+        $updater = $this->users->editor();
         $entities = $this->entities->createChainBelongingToUser($creator, $updater);
         app()->make(UserRepo::class)->destroy($updater);
         $this->entities->updatePage($entities['page'], ['html' => '<p>Hello there!</p>']);
index 2650b6743cd015f5c85c027fdb6cffcf12de6670..4563fb651ead8e1348f6f2aff205b2dbce88f7b5 100644 (file)
@@ -132,7 +132,7 @@ class EntitySearchTest extends TestCase
     public function test_search_filters()
     {
         $page = $this->entities->newPage(['name' => 'My new test quaffleachits', 'html' => 'this is about an orange donkey danzorbhsing']);
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->actingAs($editor);
 
         // Viewed filter searches
@@ -171,7 +171,7 @@ class EntitySearchTest extends TestCase
 
         // Restricted filter
         $this->get('/search?term=' . urlencode('danzorbhsing {is_restricted}'))->assertDontSee($page->name);
-        $this->entities->setPermissions($page, ['view'], [$editor->roles->first()]);
+        $this->permissions->setEntityPermissions($page, ['view'], [$editor->roles->first()]);
         $this->get('/search?term=' . urlencode('danzorbhsing {is_restricted}'))->assertSee($page->name);
 
         // Date filters
@@ -235,7 +235,7 @@ class EntitySearchTest extends TestCase
         $this->withHtml($resp)->assertElementContains($baseSelector, $page->name);
         $this->withHtml($resp)->assertElementNotContains($baseSelector, "You don't have the required permissions to select this item");
 
-        $resp = $this->actingAs($this->getViewer())->get($searchUrl);
+        $resp = $this->actingAs($this->users->viewer())->get($searchUrl);
         $this->withHtml($resp)->assertElementContains($baseSelector, $page->name);
         $this->withHtml($resp)->assertElementContains($baseSelector, "You don't have the required permissions to select this item");
     }
@@ -246,7 +246,7 @@ class EntitySearchTest extends TestCase
         $this->assertGreaterThan(2, count($chapter->pages), 'Ensure we\'re testing with at least 1 sibling');
         $page = $chapter->pages->first();
 
-        $search = $this->actingAs($this->getViewer())->get("/search/entity/siblings?entity_id={$page->id}&entity_type=page");
+        $search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$page->id}&entity_type=page");
         $search->assertSuccessful();
         foreach ($chapter->pages as $page) {
             $search->assertSee($page->name);
@@ -261,7 +261,7 @@ class EntitySearchTest extends TestCase
         $bookChildren = $page->book->getDirectChildren();
         $this->assertGreaterThan(2, count($bookChildren), 'Ensure we\'re testing with at least 1 sibling');
 
-        $search = $this->actingAs($this->getViewer())->get("/search/entity/siblings?entity_id={$page->id}&entity_type=page");
+        $search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$page->id}&entity_type=page");
         $search->assertSuccessful();
         foreach ($bookChildren as $child) {
             $search->assertSee($child->name);
@@ -276,7 +276,7 @@ class EntitySearchTest extends TestCase
         $bookChildren = $chapter->book->getDirectChildren();
         $this->assertGreaterThan(2, count($bookChildren), 'Ensure we\'re testing with at least 1 sibling');
 
-        $search = $this->actingAs($this->getViewer())->get("/search/entity/siblings?entity_id={$chapter->id}&entity_type=chapter");
+        $search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$chapter->id}&entity_type=chapter");
         $search->assertSuccessful();
         foreach ($bookChildren as $child) {
             $search->assertSee($child->name);
@@ -291,7 +291,7 @@ class EntitySearchTest extends TestCase
         $book = $books->first();
         $this->assertGreaterThan(2, count($books), 'Ensure we\'re testing with at least 1 sibling');
 
-        $search = $this->actingAs($this->getViewer())->get("/search/entity/siblings?entity_id={$book->id}&entity_type=book");
+        $search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$book->id}&entity_type=book");
         $search->assertSuccessful();
         foreach ($books as $expectedBook) {
             $search->assertSee($expectedBook->name);
@@ -304,7 +304,7 @@ class EntitySearchTest extends TestCase
         $shelf = $shelves->first();
         $this->assertGreaterThan(2, count($shelves), 'Ensure we\'re testing with at least 1 sibling');
 
-        $search = $this->actingAs($this->getViewer())->get("/search/entity/siblings?entity_id={$shelf->id}&entity_type=bookshelf");
+        $search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$shelf->id}&entity_type=bookshelf");
         $search->assertSuccessful();
         foreach ($shelves as $expectedShelf) {
             $search->assertSee($expectedShelf->name);
index 0f80bdd49283134cd40045a5d5c328503f27a105..0f8d0f48cfb329c524406590d666541331bcd5ef 100644 (file)
@@ -275,7 +275,7 @@ class ExportTest extends TestCase
 
     public function test_page_export_with_deleted_creator_and_updater()
     {
-        $user = $this->getViewer(['name' => 'ExportWizardTheFifth']);
+        $user = $this->users->viewer(['name' => 'ExportWizardTheFifth']);
         $page = $this->entities->page();
         $page->created_by = $user->id;
         $page->updated_by = $user->id;
@@ -409,7 +409,7 @@ class ExportTest extends TestCase
         $chapter = $book->chapters()->first();
         $page = $chapter->pages()->first();
         $entities = [$book, $chapter, $page];
-        $user = $this->getViewer();
+        $user = $this->users->viewer();
         $this->actingAs($user);
 
         foreach ($entities as $entity) {
@@ -417,8 +417,7 @@ class ExportTest extends TestCase
             $resp->assertSee('/export/pdf');
         }
 
-        /** @var Role $role */
-        $this->removePermissionFromUser($user, 'content-export');
+        $this->permissions->removeUserRolePermissions($user, ['content-export']);
 
         foreach ($entities as $entity) {
             $resp = $this->get($entity->getUrl());
index 0c9854206d12be7c9124c5057c24a2a203b8e822..e24ee4fb5fa9cdd057ecb011680b6fe6c6dad311 100644 (file)
@@ -483,7 +483,7 @@ class PageContentTest extends TestCase
     {
         $page = $this->entities->page();
 
-        $this->actingAs($this->getAdmin())
+        $this->actingAs($this->users->admin())
             ->put($page->getUrl(''), [
                 'name' => 'Testing',
                 'html' => '<p>&quot;Hello &amp; welcome&quot;</p>',
index 010173852958c32c39074cf5aa94ecd2d6cd027f..75b1933ea0e10d46fcd2e648081183d13b8abebf 100644 (file)
@@ -39,7 +39,7 @@ class PageDraftTest extends TestCase
         $this->withHtml($resp)->assertElementNotContains('[name="html"]', $addedContent);
 
         $newContent = $this->page->html . $addedContent;
-        $newUser = $this->getEditor();
+        $newUser = $this->users->editor();
         $this->pageRepo->updatePageDraft($this->page, ['html' => $newContent]);
 
         $resp = $this->actingAs($newUser)->get($this->page->getUrl('/edit'));
@@ -62,7 +62,7 @@ class PageDraftTest extends TestCase
         $this->withHtml($resp)->assertElementNotContains('[name="html"]', $addedContent);
 
         $newContent = $this->page->html . $addedContent;
-        $newUser = $this->getEditor();
+        $newUser = $this->users->editor();
         $this->pageRepo->updatePageDraft($this->page, ['html' => $newContent]);
 
         $this->actingAs($newUser)
@@ -75,8 +75,8 @@ class PageDraftTest extends TestCase
 
     public function test_draft_save_shows_alert_if_draft_older_than_last_page_update()
     {
-        $admin = $this->getAdmin();
-        $editor = $this->getEditor();
+        $admin = $this->users->admin();
+        $editor = $this->users->editor();
         $page = $this->entities->page();
 
         $this->actingAs($editor)->put('/ajax/page/' . $page->id . '/save-draft', [
@@ -109,8 +109,8 @@ class PageDraftTest extends TestCase
 
     public function test_draft_save_shows_alert_if_draft_edit_started_by_someone_else()
     {
-        $admin = $this->getAdmin();
-        $editor = $this->getEditor();
+        $admin = $this->users->admin();
+        $editor = $this->users->editor();
         $page = $this->entities->page();
 
         $this->actingAs($admin)->put('/ajax/page/' . $page->id . '/save-draft', [
@@ -143,7 +143,7 @@ class PageDraftTest extends TestCase
     {
         $book = $this->entities->book();
         $chapter = $book->chapters->first();
-        $newUser = $this->getEditor();
+        $newUser = $this->users->editor();
 
         $this->actingAs($newUser)->get($book->getUrl('/create-page'));
         $this->get($chapter->getUrl('/create-page'));
index 0749888c833d698ec7e3efe11ac8fb9c05007f32..0df37728e19de8ac220b889a9ea437b12fb09d4c 100644 (file)
@@ -208,13 +208,13 @@ class PageRevisionTest extends TestCase
         $page = $this->entities->page();
         $this->createRevisions($page, 2);
 
-        $viewer = $this->getViewer();
+        $viewer = $this->users->viewer();
         $this->actingAs($viewer);
         $respHtml = $this->withHtml($this->get($page->getUrl('/revisions')));
         $respHtml->assertElementNotContains('.actions a', 'Restore');
         $respHtml->assertElementNotExists('form[action$="/restore"]');
 
-        $this->giveUserPermissions($viewer, ['page-update-all']);
+        $this->permissions->grantUserRolePermissions($viewer, ['page-update-all']);
 
         $respHtml = $this->withHtml($this->get($page->getUrl('/revisions')));
         $respHtml->assertElementContains('.actions a', 'Restore');
@@ -226,13 +226,13 @@ class PageRevisionTest extends TestCase
         $page = $this->entities->page();
         $this->createRevisions($page, 2);
 
-        $viewer = $this->getViewer();
+        $viewer = $this->users->viewer();
         $this->actingAs($viewer);
         $respHtml = $this->withHtml($this->get($page->getUrl('/revisions')));
         $respHtml->assertElementNotContains('.actions a', 'Delete');
         $respHtml->assertElementNotExists('form[action$="/delete"]');
 
-        $this->giveUserPermissions($viewer, ['page-delete-all']);
+        $this->permissions->grantUserRolePermissions($viewer, ['page-delete-all']);
 
         $respHtml = $this->withHtml($this->get($page->getUrl('/revisions')));
         $respHtml->assertElementContains('.actions a', 'Delete');
index dc45fcfb8a224d79acb26adb84bd4a5f53e4bb13..6a68c3ab18ff4d80852e632cc4f036dc8ba0d075 100644 (file)
@@ -25,7 +25,7 @@ class PageTemplateTest extends TestCase
     public function test_manage_templates_permission_required_to_change_page_template_status()
     {
         $page = $this->entities->page();
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->actingAs($editor);
 
         $pageUpdateData = [
@@ -40,7 +40,7 @@ class PageTemplateTest extends TestCase
             'template' => false,
         ]);
 
-        $this->giveUserPermissions($editor, ['templates-manage']);
+        $this->permissions->grantUserRolePermissions($editor, ['templates-manage']);
 
         $this->put($page->getUrl(), $pageUpdateData);
         $this->assertDatabaseHas('pages', [
@@ -53,7 +53,7 @@ class PageTemplateTest extends TestCase
     {
         $content = '<div>my_custom_template_content</div>';
         $page = $this->entities->page();
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->actingAs($editor);
 
         $templateFetch = $this->get('/templates/' . $page->id);
@@ -73,7 +73,7 @@ class PageTemplateTest extends TestCase
 
     public function test_template_endpoint_returns_paginated_list_of_templates()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->actingAs($editor);
 
         $toBeTemplates = Page::query()->orderBy('name', 'asc')->take(12)->get();
index f481ffb6171751369164d18495d8d20d0de2fe4a..370c4381c63c8e22c9cc815aab6f2a10ea245c3e 100644 (file)
@@ -38,8 +38,8 @@ class PageTest extends TestCase
     public function test_page_view_when_creator_is_deleted_but_owner_exists()
     {
         $page = $this->entities->page();
-        $user = $this->getViewer();
-        $owner = $this->getEditor();
+        $user = $this->users->viewer();
+        $owner = $this->users->editor();
         $page->created_by = $user->id;
         $page->owned_by = $owner->id;
         $page->save();
@@ -190,15 +190,15 @@ class PageTest extends TestCase
         $page = $this->entities->page();
         $currentBook = $page->book;
         $newBook = Book::where('id', '!=', $currentBook->id)->first();
-        $viewer = $this->getViewer();
+        $viewer = $this->users->viewer();
 
         $resp = $this->actingAs($viewer)->get($page->getUrl());
         $resp->assertDontSee($page->getUrl('/copy'));
 
         $newBook->owned_by = $viewer->id;
         $newBook->save();
-        $this->giveUserPermissions($viewer, ['page-create-own']);
-        $this->entities->regenPermissions($newBook);
+        $this->permissions->grantUserRolePermissions($viewer, ['page-create-own']);
+        $this->permissions->regenerateForEntity($newBook);
 
         $resp = $this->actingAs($viewer)->get($page->getUrl());
         $resp->assertSee($page->getUrl('/copy'));
@@ -249,7 +249,7 @@ class PageTest extends TestCase
 
     public function test_recently_updated_pages_view()
     {
-        $user = $this->getEditor();
+        $user = $this->users->editor();
         $content = $this->entities->createChainBelongingToUser($user);
 
         $resp = $this->asAdmin()->get('/pages/recently-updated');
@@ -258,7 +258,7 @@ class PageTest extends TestCase
 
     public function test_recently_updated_pages_view_shows_updated_by_details()
     {
-        $user = $this->getEditor();
+        $user = $this->users->editor();
         $page = $this->entities->page();
 
         $this->actingAs($user)->put($page->getUrl(), [
@@ -272,7 +272,7 @@ class PageTest extends TestCase
 
     public function test_recently_updated_pages_view_shows_parent_chain()
     {
-        $user = $this->getEditor();
+        $user = $this->users->editor();
         $page = $this->entities->pageWithinChapter();
 
         $this->actingAs($user)->put($page->getUrl(), [
@@ -287,7 +287,7 @@ class PageTest extends TestCase
 
     public function test_recently_updated_pages_view_does_not_show_parent_if_not_visible()
     {
-        $user = $this->getEditor();
+        $user = $this->users->editor();
         $page = $this->entities->pageWithinChapter();
 
         $this->actingAs($user)->put($page->getUrl(), [
@@ -295,8 +295,8 @@ class PageTest extends TestCase
             'html' => '<p>Updated content</p>',
         ]);
 
-        $this->entities->setPermissions($page->book);
-        $this->entities->setPermissions($page, ['view'], [$user->roles->first()]);
+        $this->permissions->setEntityPermissions($page->book);
+        $this->permissions->setEntityPermissions($page, ['view'], [$user->roles->first()]);
 
         $resp = $this->get('/pages/recently-updated');
         $resp->assertDontSee($page->book->getShortName(42));
index f02e15d21a78657c8f0313a5beb1663678fdc7dd..9a5a2fe175a43ed0a2bbdd614f60b106ebdd9bb4 100644 (file)
@@ -53,7 +53,7 @@ class SortTest extends TestCase
         $newBook = Book::query()->where('id', '!=', $currentBook->id)->first();
         $newChapter = $newBook->chapters()->first();
 
-        $movePageResp = $this->actingAs($this->getEditor())->put($page->getUrl('/move'), [
+        $movePageResp = $this->actingAs($this->users->editor())->put($page->getUrl('/move'), [
             'entity_selection' => 'chapter:' . $newChapter->id,
         ]);
         $page->refresh();
@@ -71,7 +71,7 @@ class SortTest extends TestCase
         $page = $oldChapter->pages()->first();
         $newBook = Book::query()->where('id', '!=', $oldChapter->book_id)->first();
 
-        $movePageResp = $this->actingAs($this->getEditor())->put($page->getUrl('/move'), [
+        $movePageResp = $this->actingAs($this->users->editor())->put($page->getUrl('/move'), [
             'entity_selection' => 'book:' . $newBook->id,
         ]);
         $page->refresh();
@@ -89,16 +89,16 @@ class SortTest extends TestCase
         $page = $this->entities->page();
         $currentBook = $page->book;
         $newBook = Book::query()->where('id', '!=', $currentBook->id)->first();
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
 
-        $this->entities->setPermissions($newBook, ['view', 'update', 'delete'], $editor->roles->all());
+        $this->permissions->setEntityPermissions($newBook, ['view', 'update', 'delete'], $editor->roles->all());
 
         $movePageResp = $this->actingAs($editor)->put($page->getUrl('/move'), [
             'entity_selection' => 'book:' . $newBook->id,
         ]);
         $this->assertPermissionError($movePageResp);
 
-        $this->entities->setPermissions($newBook, ['view', 'update', 'delete', 'create'], $editor->roles->all());
+        $this->permissions->setEntityPermissions($newBook, ['view', 'update', 'delete', 'create'], $editor->roles->all());
         $movePageResp = $this->put($page->getUrl('/move'), [
             'entity_selection' => 'book:' . $newBook->id,
         ]);
@@ -114,10 +114,10 @@ class SortTest extends TestCase
         $page = $this->entities->page();
         $currentBook = $page->book;
         $newBook = Book::query()->where('id', '!=', $currentBook->id)->first();
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
 
-        $this->entities->setPermissions($newBook, ['view', 'update', 'create', 'delete'], $editor->roles->all());
-        $this->entities->setPermissions($page, ['view', 'update', 'create'], $editor->roles->all());
+        $this->permissions->setEntityPermissions($newBook, ['view', 'update', 'create', 'delete'], $editor->roles->all());
+        $this->permissions->setEntityPermissions($page, ['view', 'update', 'create'], $editor->roles->all());
 
         $movePageResp = $this->actingAs($editor)->put($page->getUrl('/move'), [
             'entity_selection' => 'book:' . $newBook->id,
@@ -126,7 +126,7 @@ class SortTest extends TestCase
         $pageView = $this->get($page->getUrl());
         $pageView->assertDontSee($page->getUrl('/move'));
 
-        $this->entities->setPermissions($page, ['view', 'update', 'create', 'delete'], $editor->roles->all());
+        $this->permissions->setEntityPermissions($page, ['view', 'update', 'create', 'delete'], $editor->roles->all());
         $movePageResp = $this->put($page->getUrl('/move'), [
             'entity_selection' => 'book:' . $newBook->id,
         ]);
@@ -169,10 +169,10 @@ class SortTest extends TestCase
         $chapter = $this->entities->chapter();
         $currentBook = $chapter->book;
         $newBook = Book::query()->where('id', '!=', $currentBook->id)->first();
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
 
-        $this->entities->setPermissions($newBook, ['view', 'update', 'create', 'delete'], $editor->roles->all());
-        $this->entities->setPermissions($chapter, ['view', 'update', 'create'], $editor->roles->all());
+        $this->permissions->setEntityPermissions($newBook, ['view', 'update', 'create', 'delete'], $editor->roles->all());
+        $this->permissions->setEntityPermissions($chapter, ['view', 'update', 'create'], $editor->roles->all());
 
         $moveChapterResp = $this->actingAs($editor)->put($chapter->getUrl('/move'), [
             'entity_selection' => 'book:' . $newBook->id,
@@ -181,7 +181,7 @@ class SortTest extends TestCase
         $pageView = $this->get($chapter->getUrl());
         $pageView->assertDontSee($chapter->getUrl('/move'));
 
-        $this->entities->setPermissions($chapter, ['view', 'update', 'create', 'delete'], $editor->roles->all());
+        $this->permissions->setEntityPermissions($chapter, ['view', 'update', 'create', 'delete'], $editor->roles->all());
         $moveChapterResp = $this->put($chapter->getUrl('/move'), [
             'entity_selection' => 'book:' . $newBook->id,
         ]);
@@ -196,17 +196,17 @@ class SortTest extends TestCase
         $chapter = $this->entities->chapter();
         $currentBook = $chapter->book;
         $newBook = Book::query()->where('id', '!=', $currentBook->id)->first();
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
 
-        $this->entities->setPermissions($newBook, ['view', 'update', 'delete'], [$editor->roles->first()]);
-        $this->entities->setPermissions($chapter, ['view', 'update', 'create', 'delete'], [$editor->roles->first()]);
+        $this->permissions->setEntityPermissions($newBook, ['view', 'update', 'delete'], [$editor->roles->first()]);
+        $this->permissions->setEntityPermissions($chapter, ['view', 'update', 'create', 'delete'], [$editor->roles->first()]);
 
         $moveChapterResp = $this->actingAs($editor)->put($chapter->getUrl('/move'), [
             'entity_selection' => 'book:' . $newBook->id,
         ]);
         $this->assertPermissionError($moveChapterResp);
 
-        $this->entities->setPermissions($newBook, ['view', 'update', 'create', 'delete'], [$editor->roles->first()]);
+        $this->permissions->setEntityPermissions($newBook, ['view', 'update', 'create', 'delete'], [$editor->roles->first()]);
         $moveChapterResp = $this->put($chapter->getUrl('/move'), [
             'entity_selection' => 'book:' . $newBook->id,
         ]);
@@ -313,7 +313,7 @@ class SortTest extends TestCase
         $page = $this->entities->pageWithinChapter();
         /** @var Chapter $otherChapter */
         $otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
-        $this->entities->setPermissions($otherChapter);
+        $this->permissions->setEntityPermissions($otherChapter);
 
         $sortData = [
             'id'            => $page->id,
@@ -334,8 +334,8 @@ class SortTest extends TestCase
         $page = $this->entities->pageWithinChapter();
         /** @var Chapter $otherChapter */
         $otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
-        $editor = $this->getEditor();
-        $this->entities->setPermissions($otherChapter->book, ['update', 'delete'], [$editor->roles()->first()]);
+        $editor = $this->users->editor();
+        $this->permissions->setEntityPermissions($otherChapter->book, ['update', 'delete'], [$editor->roles()->first()]);
 
         $sortData = [
             'id'            => $page->id,
@@ -356,8 +356,8 @@ class SortTest extends TestCase
         $page = $this->entities->pageWithinChapter();
         /** @var Chapter $otherChapter */
         $otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
-        $editor = $this->getEditor();
-        $this->entities->setPermissions($otherChapter, ['view', 'delete'], [$editor->roles()->first()]);
+        $editor = $this->users->editor();
+        $this->permissions->setEntityPermissions($otherChapter, ['view', 'delete'], [$editor->roles()->first()]);
 
         $sortData = [
             'id'            => $page->id,
@@ -378,8 +378,8 @@ class SortTest extends TestCase
         $page = $this->entities->pageWithinChapter();
         /** @var Chapter $otherChapter */
         $otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
-        $editor = $this->getEditor();
-        $this->entities->setPermissions($page, ['view', 'delete'], [$editor->roles()->first()]);
+        $editor = $this->users->editor();
+        $this->permissions->setEntityPermissions($page, ['view', 'delete'], [$editor->roles()->first()]);
 
         $sortData = [
             'id'            => $page->id,
@@ -400,8 +400,8 @@ class SortTest extends TestCase
         $page = $this->entities->pageWithinChapter();
         /** @var Chapter $otherChapter */
         $otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
-        $editor = $this->getEditor();
-        $this->entities->setPermissions($page, ['view', 'update'], [$editor->roles()->first()]);
+        $editor = $this->users->editor();
+        $this->permissions->setEntityPermissions($page, ['view', 'update'], [$editor->roles()->first()]);
 
         $sortData = [
             'id'            => $page->id,
index ab06686e0e6f3d5abb7718fe79efd614842667e8..7e667495908d2dbf62e862a77952533841c5d056 100644 (file)
@@ -75,7 +75,7 @@ class TagTest extends TestCase
         $this->asEditor()->get('/ajax/tags/suggest/names?search=co')->assertSimilarJson(['color', 'country']);
 
         // Set restricted permission the page
-        $this->entities->setPermissions($page, [], []);
+        $this->permissions->setEntityPermissions($page, [], []);
 
         $this->asAdmin()->get('/ajax/tags/suggest/names?search=co')->assertSimilarJson(['color', 'country']);
         $this->asEditor()->get('/ajax/tags/suggest/names?search=co')->assertSimilarJson([]);
@@ -178,7 +178,7 @@ class TagTest extends TestCase
         $resp = $this->get('/tags?name=SuperCategory');
         $resp->assertSee('GreatTestContent');
 
-        $this->entities->setPermissions($page, [], []);
+        $this->permissions->setEntityPermissions($page, [], []);
 
         $resp = $this->asEditor()->get('/tags');
         $resp->assertDontSee('SuperCategory');
index ebd9874d32134afb6b0ac9f31d9724595584991d..6ba01dd88e6ed8dfb094e637f3cbf6ed1ea6cfe1 100644 (file)
@@ -11,7 +11,7 @@ class ErrorTest extends TestCase
         // Due to middleware being handled differently this will not fail
         // if our custom, middleware-loaded handler fails but this is here
         // as a reminder and as a general check in the event of other issues.
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $editor->name = 'tester';
         $editor->save();
 
@@ -24,7 +24,7 @@ class ErrorTest extends TestCase
 
     public function test_item_not_found_does_not_get_logged_to_file()
     {
-        $this->actingAs($this->getViewer());
+        $this->actingAs($this->users->viewer());
         $handler = $this->withTestLogger();
         $book = $this->entities->book();
 
@@ -41,7 +41,7 @@ class ErrorTest extends TestCase
 
     public function test_access_to_non_existing_image_location_provides_404_response()
     {
-        $resp = $this->actingAs($this->getViewer())->get('/uploads/images/gallery/2021-05/anonexistingimage.png');
+        $resp = $this->actingAs($this->users->viewer())->get('/uploads/images/gallery/2021-05/anonexistingimage.png');
         $resp->assertStatus(404);
         $resp->assertSeeText('Image Not Found');
     }
index 456f2213cd464e18643718263bd65c93209f190c..7778aa8e93584ad5e09dab15b6a0c2d86eac15fc 100644 (file)
@@ -10,7 +10,7 @@ class FavouriteTest extends TestCase
     public function test_page_add_favourite_flow()
     {
         $page = $this->entities->page();
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
 
         $resp = $this->actingAs($editor)->get($page->getUrl());
         $this->withHtml($resp)->assertElementContains('button', 'Favourite');
@@ -33,7 +33,7 @@ class FavouriteTest extends TestCase
     public function test_page_remove_favourite_flow()
     {
         $page = $this->entities->page();
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         Favourite::query()->forceCreate([
             'user_id'           => $editor->id,
             'favouritable_id'   => $page->id,
@@ -63,7 +63,7 @@ class FavouriteTest extends TestCase
         $book->owned_by = $user->id;
         $book->save();
 
-        $this->giveUserPermissions($user, ['book-view-own']);
+        $this->permissions->grantUserRolePermissions($user, ['book-view-own']);
 
         $this->actingAs($user)->get($book->getUrl());
         $resp = $this->post('/favourites/add', [
@@ -81,7 +81,7 @@ class FavouriteTest extends TestCase
 
     public function test_each_entity_type_shows_favourite_button()
     {
-        $this->actingAs($this->getEditor());
+        $this->actingAs($this->users->editor());
 
         foreach ($this->entities->all() as $entity) {
             $resp = $this->get($entity->getUrl());
@@ -94,13 +94,13 @@ class FavouriteTest extends TestCase
         $this->setSettings(['app-public' => 'true']);
         $resp = $this->get('/');
         $this->withHtml($resp)->assertElementNotContains('header', 'My Favourites');
-        $resp = $this->actingAs($this->getViewer())->get('/');
+        $resp = $this->actingAs($this->users->viewer())->get('/');
         $this->withHtml($resp)->assertElementContains('header a', 'My Favourites');
     }
 
     public function test_favourites_shown_on_homepage()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
 
         $resp = $this->actingAs($editor)->get('/');
         $this->withHtml($resp)->assertElementNotExists('#top-favourites');
@@ -116,7 +116,7 @@ class FavouriteTest extends TestCase
     public function test_favourites_list_page_shows_favourites_and_has_working_pagination()
     {
         $page = $this->entities->page();
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
 
         $resp = $this->actingAs($editor)->get('/favourites');
         $resp->assertDontSee($page->name);
index 9e8cf0b73ba28c979ab80806134324ce891a404b..d79015f75537deade2443f57a7018d6972139f46 100644 (file)
@@ -2,8 +2,6 @@
 
 namespace Tests\Helpers;
 
-use BookStack\Auth\Permissions\EntityPermission;
-use BookStack\Auth\Role;
 use BookStack\Auth\User;
 use BookStack\Entities\Models\Book;
 use BookStack\Entities\Models\Bookshelf;
@@ -186,44 +184,6 @@ class EntityProvider
         return $pageRepo->publishDraft($draftPage, $input);
     }
 
-    /**
-     * Regenerate the permission for an entity.
-     * Centralised to manage clearing of cached elements between requests.
-     */
-    public function regenPermissions(Entity $entity): void
-    {
-        $entity->rebuildPermissions();
-        $entity->load('jointPermissions');
-    }
-
-    /**
-     * Set the given entity as having restricted permissions, and apply the given
-     * permissions for the given roles.
-     * @param string[] $actions
-     * @param Role[] $roles
-     */
-    public function setPermissions(Entity $entity, array $actions = [], array $roles = []): void
-    {
-        $entity->permissions()->delete();
-
-        $permissions = [
-            // Set default permissions to not allow actions so that only the provided role permissions are at play.
-            ['role_id' => 0, 'view' => false, 'create' => false, 'update' => false, 'delete' => false],
-        ];
-
-        foreach ($roles as $role) {
-            $permission = ['role_id' => $role->id];
-            foreach (EntityPermission::PERMISSIONS as $possibleAction) {
-                $permission[$possibleAction] = in_array($possibleAction, $actions);
-            }
-            $permissions[] = $permission;
-        }
-
-        $entity->permissions()->createMany($permissions);
-        $entity->load('permissions');
-        $this->regenPermissions($entity);
-    }
-
     /**
      * @param Entity|Entity[] $entities
      */
diff --git a/tests/Helpers/PermissionsProvider.php b/tests/Helpers/PermissionsProvider.php
new file mode 100644 (file)
index 0000000..ac9a2a6
--- /dev/null
@@ -0,0 +1,136 @@
+<?php
+
+namespace Tests\Helpers;
+
+use BookStack\Auth\Permissions\EntityPermission;
+use BookStack\Auth\Permissions\RolePermission;
+use BookStack\Auth\Role;
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Entity;
+
+class PermissionsProvider
+{
+    protected UserRoleProvider $userRoleProvider;
+
+    public function __construct(UserRoleProvider $userRoleProvider)
+    {
+        $this->userRoleProvider = $userRoleProvider;
+    }
+
+    /**
+     * Grant role permissions to the provided user.
+     */
+    public function grantUserRolePermissions(User $user, array $permissions): void
+    {
+        $newRole = $this->userRoleProvider->createRole($permissions);
+        $user->attachRole($newRole);
+        $user->load('roles');
+        $user->clearPermissionCache();
+    }
+
+    /**
+     * Completely remove specific role permissions from the provided user.
+     */
+    public function removeUserRolePermissions(User $user, array $permissions): void
+    {
+        foreach ($permissions as $permissionName) {
+            /** @var RolePermission $permission */
+            $permission = RolePermission::query()
+                ->where('name', '=', $permissionName)
+                ->firstOrFail();
+
+            $roles = $user->roles()->whereHas('permissions', function ($query) use ($permission) {
+                $query->where('id', '=', $permission->id);
+            })->get();
+
+            /** @var Role $role */
+            foreach ($roles as $role) {
+                $role->detachPermission($permission);
+            }
+
+            $user->clearPermissionCache();
+        }
+    }
+
+    /**
+     * Change the owner of the given entity to the given user.
+     */
+    public function changeEntityOwner(Entity $entity, User $newOwner): void
+    {
+        $entity->owned_by = $newOwner->id;
+        $entity->save();
+        $entity->rebuildPermissions();
+    }
+
+    /**
+     * Regenerate the permission for an entity.
+     * Centralised to manage clearing of cached elements between requests.
+     */
+    public function regenerateForEntity(Entity $entity): void
+    {
+        $entity->rebuildPermissions();
+    }
+
+    /**
+     * Set the given entity as having restricted permissions, and apply the given
+     * permissions for the given roles.
+     * @param string[] $actions
+     * @param Role[] $roles
+     */
+    public function setEntityPermissions(Entity $entity, array $actions = [], array $roles = [], $inherit = false): void
+    {
+        $entity->permissions()->delete();
+
+        $permissions = [];
+
+        if (!$inherit) {
+            // Set default permissions to not allow actions so that only the provided role permissions are at play.
+            $permissions[] = ['role_id' => null, 'user_id' => null, 'view' => false, 'create' => false, 'update' => false, 'delete' => false];
+        }
+
+        foreach ($roles as $role) {
+            $permissions[] = $this->actionListToEntityPermissionData($actions, $role->id);
+        }
+
+        $this->addEntityPermissionEntries($entity, $permissions);
+    }
+
+    public function addEntityPermission(Entity $entity, array $actionList, ?Role $role = null, ?User $user = null)
+    {
+        $permissionData = $this->actionListToEntityPermissionData($actionList, $role->id ?? null, $user->id ?? null);
+        $this->addEntityPermissionEntries($entity, [$permissionData]);
+    }
+
+    /**
+     * Disable inherited permissions on the given entity.
+     * Effectively sets the "Other Users" UI permission option to not inherit, with no permissions.
+     */
+    public function disableEntityInheritedPermissions(Entity $entity): void
+    {
+        $entity->permissions()->whereNull(['user_id', 'role_id'])->delete();
+        $fallback = $this->actionListToEntityPermissionData([]);
+        $this->addEntityPermissionEntries($entity, [$fallback]);
+    }
+
+    protected function addEntityPermissionEntries(Entity $entity, array $entityPermissionData): void
+    {
+        $entity->permissions()->createMany($entityPermissionData);
+        $entity->load('permissions');
+        $this->regenerateForEntity($entity);
+    }
+
+    /**
+     * For the given simple array of string actions (view, create, update, delete), convert
+     * the format to entity permission data, where permission is granted if the action is in the
+     * given actionList array.
+     */
+    protected function actionListToEntityPermissionData(array $actionList, int $roleId = null, int $userId = null): array
+    {
+        $permissionData = ['role_id' => $roleId, 'user_id' => $userId];
+        foreach (EntityPermission::PERMISSIONS as $possibleAction) {
+            $permissionData[$possibleAction] = in_array($possibleAction, $actionList);
+        }
+
+        return $permissionData;
+    }
+}
diff --git a/tests/Helpers/UserRoleProvider.php b/tests/Helpers/UserRoleProvider.php
new file mode 100644 (file)
index 0000000..355c168
--- /dev/null
@@ -0,0 +1,97 @@
+<?php
+
+namespace Tests\Helpers;
+
+use BookStack\Auth\Permissions\PermissionsRepo;
+use BookStack\Auth\Role;
+use BookStack\Auth\User;
+
+class UserRoleProvider
+{
+    protected ?User $admin = null;
+    protected ?User $editor = null;
+
+    /**
+     * Get a typical "Admin" user.
+     */
+    public function admin(): User
+    {
+        if (is_null($this->admin)) {
+            $adminRole = Role::getSystemRole('admin');
+            $this->admin = $adminRole->users->first();
+        }
+
+        return $this->admin;
+    }
+
+    /**
+     * Get a typical "Editor" user.
+     */
+    public function editor(): User
+    {
+        if ($this->editor === null) {
+            $editorRole = Role::getRole('editor');
+            $this->editor = $editorRole->users->first();
+        }
+
+        return $this->editor;
+    }
+
+    /**
+     * Get a typical "Viewer" user.
+     */
+    public function viewer(array $attributes = []): User
+    {
+        $user = Role::getRole('viewer')->users()->first();
+        if (!empty($attributes)) {
+            $user->forceFill($attributes)->save();
+        }
+
+        return $user;
+    }
+
+    /**
+     * Create a new fresh user without any relations.
+     */
+    public function newUser(array $attrs = []): User
+    {
+        return User::factory()->create($attrs);
+    }
+
+    /**
+     * Create a new fresh user, with the given attrs, that has assigned a fresh role
+     * that has the given role permissions.
+     * Intended as a helper to create a blank slate baseline user and role.
+     * @return array{0: User, 1: Role}
+     */
+    public function newUserWithRole(array $userAttrs = [], array $rolePermissions = []): array
+    {
+        $user = $this->newUser($userAttrs);
+        $role = $this->attachNewRole($user, $rolePermissions);
+
+        return [$user, $role];
+    }
+
+    /**
+     * Attach a new role, with the given role permissions, to the given user
+     * and return that role.
+     */
+    public function attachNewRole(User $user, array $rolePermissions = []): Role
+    {
+        $role = $this->createRole($rolePermissions);
+        $user->attachRole($role);
+        return $role;
+    }
+
+    /**
+     * Create a new basic role with the given role permissions.
+     */
+    public function createRole(array $rolePermissions = []): Role
+    {
+        $permissionRepo = app(PermissionsRepo::class);
+        $roleData = Role::factory()->make()->toArray();
+        $roleData['permissions'] = array_flip($rolePermissions);
+
+        return $permissionRepo->saveNewRole($roleData);
+    }
+}
index cf69425fb932aa62f450ecbbe94b7ec4483e95fc..c7e8b69bb2c8df3e83fba52381e09994e410d6dd 100644 (file)
@@ -114,7 +114,7 @@ class HomepageTest extends TestCase
 
     public function test_set_book_homepage()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         setting()->putUser($editor, 'books_view_type', 'grid');
 
         $this->setSettings(['app-homepage-type' => 'books']);
@@ -133,7 +133,7 @@ class HomepageTest extends TestCase
 
     public function test_set_bookshelves_homepage()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         setting()->putUser($editor, 'bookshelves_view_type', 'grid');
         $shelf = $this->entities->shelf();
 
@@ -152,7 +152,7 @@ class HomepageTest extends TestCase
 
     public function test_shelves_list_homepage_adheres_to_book_visibility_permissions()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         setting()->putUser($editor, 'bookshelves_view_type', 'list');
         $this->setSettings(['app-homepage-type' => 'bookshelves']);
         $this->asEditor();
@@ -167,13 +167,13 @@ class HomepageTest extends TestCase
 
         // Ensure book no longer visible without view permission
         $editor->roles()->detach();
-        $this->giveUserPermissions($editor, ['bookshelf-view-all']);
+        $this->permissions->grantUserRolePermissions($editor, ['bookshelf-view-all']);
         $homeVisit = $this->get('/');
         $this->withHtml($homeVisit)->assertElementContains('.content-wrap', $shelf->name);
         $this->withHtml($homeVisit)->assertElementNotContains('.content-wrap', $book->name);
 
         // Ensure is visible again with entity-level view permission
-        $this->entities->setPermissions($book, ['view'], [$editor->roles()->first()]);
+        $this->permissions->setEntityPermissions($book, ['view'], [$editor->roles()->first()]);
         $homeVisit = $this->get('/');
         $this->withHtml($homeVisit)->assertElementContains('.content-wrap', $shelf->name);
         $this->withHtml($homeVisit)->assertElementContains('.content-wrap', $book->name);
index 27de5f875b552074820afb730e4f89dcfee03c26..ba522a74e380c5de371899011500e703e83189d9 100644 (file)
@@ -4,7 +4,7 @@ namespace Tests;
 
 class LanguageTest extends TestCase
 {
-    protected array $langs;
+    protected $langs;
 
     /**
      * LanguageTest constructor.
@@ -77,20 +77,8 @@ class LanguageTest extends TestCase
     {
         $this->asEditor();
         $this->assertFalse(config('app.rtl'), 'App RTL config should be false by default');
-        setting()->putUser($this->getEditor(), 'language', 'ar');
+        setting()->putUser($this->users->editor(), 'language', 'ar');
         $this->get('/');
         $this->assertTrue(config('app.rtl'), 'App RTL config should have been set to true by middleware');
     }
-
-    public function test_pluralisation_for_non_standard_locales()
-    {
-        $text = trans_choice('entities.x_pages', 1, [], 'de_informal');
-        $this->assertEquals('1 Seite', $text);
-
-        $text = trans_choice('entities.x_pages', 2, [], 'de_informal');
-        $this->assertEquals('2 Seiten', $text);
-
-        $text = trans_choice('entities.x_pages', 0, [], 'de_informal');
-        $this->assertEquals('0 Seiten', $text);
-    }
 }
index 4b613b49ce07b6313daf3fc1d1eb67fb8d37713a..68a4ed2441c3a49e5e496a57a56184c202090cb8 100644 (file)
@@ -2,6 +2,7 @@
 
 namespace Tests\Permissions;
 
+use BookStack\Auth\Role;
 use BookStack\Auth\User;
 use BookStack\Entities\Models\Book;
 use BookStack\Entities\Models\Bookshelf;
@@ -20,8 +21,8 @@ class EntityPermissionsTest extends TestCase
     protected function setUp(): void
     {
         parent::setUp();
-        $this->user = $this->getEditor();
-        $this->viewer = $this->getViewer();
+        $this->user = $this->users->editor();
+        $this->viewer = $this->users->viewer();
     }
 
     protected function setRestrictionsForTestRoles(Entity $entity, array $actions = [])
@@ -30,7 +31,7 @@ class EntityPermissionsTest extends TestCase
             $this->user->roles->first(),
             $this->viewer->roles->first(),
         ];
-        $this->entities->setPermissions($entity, $actions, $roles);
+        $this->permissions->setEntityPermissions($entity, $actions, $roles);
     }
 
     public function test_bookshelf_view_restriction()
@@ -378,8 +379,10 @@ class EntityPermissionsTest extends TestCase
 
         $this->put($modelInstance->getUrl('/permissions'), [
             'permissions' => [
-                $roleId => [
-                    $permission => 'true',
+                'role' => [
+                    $roleId => [
+                        $permission => 'true',
+                    ],
                 ],
             ],
         ]);
@@ -655,6 +658,34 @@ class EntityPermissionsTest extends TestCase
         $resp->assertRedirect($book->getUrl('/page/test-page'));
     }
 
+    public function test_access_to_item_prevented_if_inheritance_active_but_permission_prevented_via_role()
+    {
+        $user = $this->users->viewer();
+        $viewerRole = $user->roles->first();
+        $chapter = $this->entities->chapter();
+        $book = $chapter->book;
+
+        $this->permissions->setEntityPermissions($book, ['edit'], [$viewerRole], false);
+        $this->permissions->setEntityPermissions($chapter, [], [$viewerRole], true);
+
+        $this->assertFalse(userCan('chapter-update', $chapter));
+    }
+
+    public function test_access_to_item_allowed_if_inheritance_active_and_permission_prevented_via_role_but_allowed_via_parent()
+    {
+        $user = $this->users->viewer();
+        $viewerRole = $user->roles->first();
+        $editorRole = Role::getRole('Editor');
+        $user->attachRole($editorRole);
+        $chapter = $this->entities->chapter();
+        $book = $chapter->book;
+
+        $this->permissions->setEntityPermissions($book, ['edit'], [$editorRole], false);
+        $this->permissions->setEntityPermissions($chapter, [], [$viewerRole], true);
+
+        $this->assertTrue(userCan('chapter-update', $chapter));
+    }
+
     public function test_book_permissions_can_be_generated_without_error_if_child_chapter_is_in_recycle_bin()
     {
         $book = $this->entities->bookHasChaptersAndPages();
@@ -665,7 +696,7 @@ class EntityPermissionsTest extends TestCase
 
         $error = null;
         try {
-            $this->entities->setPermissions($book, ['view'], []);
+            $this->permissions->setEntityPermissions($book, ['view'], []);
         } catch (Exception $e) {
             $error = $e;
         }
index 642cf1beba14dba92b2b14e456483009066cc45b..8072221e5f7ff4ad674d753b67eb310ca25b2685 100644 (file)
@@ -14,7 +14,7 @@ class ExportPermissionsTest extends TestCase
         $pageContent = Str::random(48);
         $page->html = '<p>' . $pageContent . '</p>';
         $page->save();
-        $viewer = $this->getViewer();
+        $viewer = $this->users->viewer();
         $this->actingAs($viewer);
         $formats = ['html', 'plaintext'];
 
@@ -25,7 +25,7 @@ class ExportPermissionsTest extends TestCase
             $resp->assertSee($pageContent);
         }
 
-        $this->entities->setPermissions($page, []);
+        $this->permissions->setEntityPermissions($page, []);
 
         foreach ($formats as $format) {
             $resp = $this->get($chapter->getUrl("export/{$format}"));
@@ -42,7 +42,7 @@ class ExportPermissionsTest extends TestCase
         $pageContent = Str::random(48);
         $page->html = '<p>' . $pageContent . '</p>';
         $page->save();
-        $viewer = $this->getViewer();
+        $viewer = $this->users->viewer();
         $this->actingAs($viewer);
         $formats = ['html', 'plaintext'];
 
@@ -53,7 +53,7 @@ class ExportPermissionsTest extends TestCase
             $resp->assertSee($pageContent);
         }
 
-        $this->entities->setPermissions($page, []);
+        $this->permissions->setEntityPermissions($page, []);
 
         foreach ($formats as $format) {
             $resp = $this->get($book->getUrl("export/{$format}"));
index 88d400259e0e683a8c4d3a21906b0842aa6e6e7e..8bf700c071180d5ef5ac5a4c769df2bcee343b50 100644 (file)
@@ -22,7 +22,7 @@ class RolesTest extends TestCase
     protected function setUp(): void
     {
         parent::setUp();
-        $this->user = $this->getViewer();
+        $this->user = $this->users->viewer();
     }
 
     public function test_admin_can_see_settings()
@@ -42,7 +42,7 @@ class RolesTest extends TestCase
 
     public function test_role_cannot_be_deleted_if_default()
     {
-        $newRole = $this->createNewRole();
+        $newRole = $this->users->createRole();
         $this->setSettings(['registration-role' => $newRole->id]);
 
         $deletePageUrl = '/settings/roles/delete/' . $newRole->id;
@@ -121,11 +121,11 @@ class RolesTest extends TestCase
     {
         /** @var Role $adminRole */
         $adminRole = Role::query()->where('system_name', '=', 'admin')->first();
-        $adminUser = $this->getAdmin();
+        $adminUser = $this->users->admin();
         $adminRole->users()->where('id', '!=', $adminUser->id)->delete();
         $this->assertEquals(1, $adminRole->users()->count());
 
-        $viewerRole = $this->getViewer()->roles()->first();
+        $viewerRole = $this->users->viewer()->roles()->first();
 
         $editUrl = '/settings/users/' . $adminUser->id;
         $resp = $this->actingAs($adminUser)->put($editUrl, [
@@ -169,7 +169,7 @@ class RolesTest extends TestCase
         $roleA = Role::query()->create(['display_name' => 'Entity Permissions Delete Test']);
         $page = $this->entities->page();
 
-        $this->entities->setPermissions($page, ['view'], [$roleA]);
+        $this->permissions->setEntityPermissions($page, ['view'], [$roleA]);
 
         $this->assertDatabaseHas('entity_permissions', [
             'role_id' => $roleA->id,
@@ -214,7 +214,7 @@ class RolesTest extends TestCase
     public function test_manage_user_permission()
     {
         $this->actingAs($this->user)->get('/settings/users')->assertRedirect('/');
-        $this->giveUserPermissions($this->user, ['users-manage']);
+        $this->permissions->grantUserRolePermissions($this->user, ['users-manage']);
         $this->actingAs($this->user)->get('/settings/users')->assertOk();
     }
 
@@ -222,9 +222,9 @@ class RolesTest extends TestCase
     {
         $usersLink = 'href="' . url('/settings/users') . '"';
         $this->actingAs($this->user)->get('/')->assertDontSee($usersLink, false);
-        $this->giveUserPermissions($this->user, ['users-manage']);
+        $this->permissions->grantUserRolePermissions($this->user, ['users-manage']);
         $this->actingAs($this->user)->get('/')->assertSee($usersLink, false);
-        $this->giveUserPermissions($this->user, ['settings-manage', 'users-manage']);
+        $this->permissions->grantUserRolePermissions($this->user, ['settings-manage', 'users-manage']);
         $this->actingAs($this->user)->get('/')->assertDontSee($usersLink, false);
     }
 
@@ -247,7 +247,7 @@ class RolesTest extends TestCase
             'name'  => 'my_new_name',
         ]);
 
-        $this->giveUserPermissions($this->user, ['users-manage']);
+        $this->permissions->grantUserRolePermissions($this->user, ['users-manage']);
 
         $resp = $this->get($userProfileUrl)
             ->assertOk();
@@ -269,7 +269,7 @@ class RolesTest extends TestCase
     {
         $this->actingAs($this->user)->get('/settings/roles')->assertRedirect('/');
         $this->get('/settings/roles/1')->assertRedirect('/');
-        $this->giveUserPermissions($this->user, ['user-roles-manage']);
+        $this->permissions->grantUserRolePermissions($this->user, ['user-roles-manage']);
         $this->actingAs($this->user)->get('/settings/roles')->assertOk();
         $this->get('/settings/roles/1')
             ->assertOk()
@@ -279,7 +279,7 @@ class RolesTest extends TestCase
     public function test_settings_manage_permission()
     {
         $this->actingAs($this->user)->get('/settings/features')->assertRedirect('/');
-        $this->giveUserPermissions($this->user, ['settings-manage']);
+        $this->permissions->grantUserRolePermissions($this->user, ['settings-manage']);
         $this->get('/settings/features')->assertOk();
 
         $resp = $this->post('/settings/features', []);
@@ -295,7 +295,7 @@ class RolesTest extends TestCase
         $this->actingAs($this->user)->get($page->getUrl())->assertDontSee('Permissions');
         $this->get($page->getUrl('/permissions'))->assertRedirect('/');
 
-        $this->giveUserPermissions($this->user, ['restrictions-manage-all']);
+        $this->permissions->grantUserRolePermissions($this->user, ['restrictions-manage-all']);
 
         $this->actingAs($this->user)->get($page->getUrl())->assertSee('Permissions');
 
@@ -325,7 +325,7 @@ class RolesTest extends TestCase
         $this->actingAs($this->user)->get($page->getUrl())->assertDontSee('Permissions');
         $this->get($page->getUrl('/permissions'))->assertRedirect('/');
 
-        $this->giveUserPermissions($this->user, ['restrictions-manage-own']);
+        $this->permissions->grantUserRolePermissions($this->user, ['restrictions-manage-own']);
 
         // Check can't restrict other's content
         $this->actingAs($this->user)->get($otherUsersPage->getUrl())->assertDontSee('Permissions');
@@ -350,7 +350,7 @@ class RolesTest extends TestCase
             $this->withHtml($resp)->assertElementNotContains('.action-buttons', $text);
         }
 
-        $this->giveUserPermissions($this->user, [$permission]);
+        $this->permissions->grantUserRolePermissions($this->user, [$permission]);
 
         foreach ($accessUrls as $url) {
             $this->actingAs($this->user)->get($url)->assertOk();
@@ -380,7 +380,7 @@ class RolesTest extends TestCase
         $otherShelf = Bookshelf::query()->first();
         $ownShelf = $this->entities->newShelf(['name' => 'test-shelf', 'slug' => 'test-shelf']);
         $ownShelf->forceFill(['owned_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
-        $this->entities->regenPermissions($ownShelf);
+        $this->permissions->regenerateForEntity($ownShelf);
 
         $this->checkAccessPermission('bookshelf-update-own', [
             $ownShelf->getUrl('/edit'),
@@ -406,12 +406,12 @@ class RolesTest extends TestCase
 
     public function test_bookshelves_delete_own_permission()
     {
-        $this->giveUserPermissions($this->user, ['bookshelf-update-all']);
+        $this->permissions->grantUserRolePermissions($this->user, ['bookshelf-update-all']);
         /** @var Bookshelf $otherShelf */
         $otherShelf = Bookshelf::query()->first();
         $ownShelf = $this->entities->newShelf(['name' => 'test-shelf', 'slug' => 'test-shelf']);
         $ownShelf->forceFill(['owned_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
-        $this->entities->regenPermissions($ownShelf);
+        $this->permissions->regenerateForEntity($ownShelf);
 
         $this->checkAccessPermission('bookshelf-delete-own', [
             $ownShelf->getUrl('/delete'),
@@ -430,7 +430,7 @@ class RolesTest extends TestCase
 
     public function test_bookshelves_delete_all_permission()
     {
-        $this->giveUserPermissions($this->user, ['bookshelf-update-all']);
+        $this->permissions->grantUserRolePermissions($this->user, ['bookshelf-update-all']);
         /** @var Bookshelf $otherShelf */
         $otherShelf = Bookshelf::query()->first();
         $this->checkAccessPermission('bookshelf-delete-all', [
@@ -486,7 +486,7 @@ class RolesTest extends TestCase
 
     public function test_books_delete_own_permission()
     {
-        $this->giveUserPermissions($this->user, ['book-update-all']);
+        $this->permissions->grantUserRolePermissions($this->user, ['book-update-all']);
         /** @var Book $otherBook */
         $otherBook = Book::query()->take(1)->get()->first();
         $ownBook = $this->entities->createChainBelongingToUser($this->user)['book'];
@@ -506,7 +506,7 @@ class RolesTest extends TestCase
 
     public function test_books_delete_all_permission()
     {
-        $this->giveUserPermissions($this->user, ['book-update-all']);
+        $this->permissions->grantUserRolePermissions($this->user, ['book-update-all']);
         /** @var Book $otherBook */
         $otherBook = Book::query()->take(1)->get()->first();
         $this->checkAccessPermission('book-delete-all', [
@@ -585,7 +585,7 @@ class RolesTest extends TestCase
 
     public function test_chapter_delete_own_permission()
     {
-        $this->giveUserPermissions($this->user, ['chapter-update-all']);
+        $this->permissions->grantUserRolePermissions($this->user, ['chapter-update-all']);
         /** @var Chapter $otherChapter */
         $otherChapter = Chapter::query()->first();
         $ownChapter = $this->entities->createChainBelongingToUser($this->user)['chapter'];
@@ -607,7 +607,7 @@ class RolesTest extends TestCase
 
     public function test_chapter_delete_all_permission()
     {
-        $this->giveUserPermissions($this->user, ['chapter-update-all']);
+        $this->permissions->grantUserRolePermissions($this->user, ['chapter-update-all']);
         /** @var Chapter $otherChapter */
         $otherChapter = Chapter::query()->first();
         $this->checkAccessPermission('chapter-delete-all', [
@@ -645,7 +645,7 @@ class RolesTest extends TestCase
             $ownChapter->getUrl() => 'New Page',
         ]);
 
-        $this->giveUserPermissions($this->user, ['page-create-own']);
+        $this->permissions->grantUserRolePermissions($this->user, ['page-create-own']);
 
         foreach ($accessUrls as $index => $url) {
             $resp = $this->actingAs($this->user)->get($url);
@@ -688,7 +688,7 @@ class RolesTest extends TestCase
             $chapter->getUrl() => 'New Page',
         ]);
 
-        $this->giveUserPermissions($this->user, ['page-create-all']);
+        $this->permissions->grantUserRolePermissions($this->user, ['page-create-all']);
 
         foreach ($accessUrls as $index => $url) {
             $resp = $this->actingAs($this->user)->get($url);
@@ -742,7 +742,7 @@ class RolesTest extends TestCase
 
     public function test_page_delete_own_permission()
     {
-        $this->giveUserPermissions($this->user, ['page-update-all']);
+        $this->permissions->grantUserRolePermissions($this->user, ['page-update-all']);
         /** @var Page $otherPage */
         $otherPage = Page::query()->first();
         $ownPage = $this->entities->createChainBelongingToUser($this->user)['page'];
@@ -764,7 +764,7 @@ class RolesTest extends TestCase
 
     public function test_page_delete_all_permission()
     {
-        $this->giveUserPermissions($this->user, ['page-update-all']);
+        $this->permissions->grantUserRolePermissions($this->user, ['page-update-all']);
         /** @var Page $otherPage */
         $otherPage = Page::query()->first();
 
@@ -823,7 +823,7 @@ class RolesTest extends TestCase
 
     public function test_image_delete_own_permission()
     {
-        $this->giveUserPermissions($this->user, ['image-update-all']);
+        $this->permissions->grantUserRolePermissions($this->user, ['image-update-all']);
         $page = $this->entities->page();
         $image = Image::factory()->create([
             'uploaded_to' => $page->id,
@@ -833,7 +833,7 @@ class RolesTest extends TestCase
 
         $this->actingAs($this->user)->json('delete', '/images/' . $image->id)->assertStatus(403);
 
-        $this->giveUserPermissions($this->user, ['image-delete-own']);
+        $this->permissions->grantUserRolePermissions($this->user, ['image-delete-own']);
 
         $this->actingAs($this->user)->json('delete', '/images/' . $image->id)->assertOk();
         $this->assertDatabaseMissing('images', ['id' => $image->id]);
@@ -841,18 +841,18 @@ class RolesTest extends TestCase
 
     public function test_image_delete_all_permission()
     {
-        $this->giveUserPermissions($this->user, ['image-update-all']);
-        $admin = $this->getAdmin();
+        $this->permissions->grantUserRolePermissions($this->user, ['image-update-all']);
+        $admin = $this->users->admin();
         $page = $this->entities->page();
         $image = Image::factory()->create(['uploaded_to' => $page->id, 'created_by' => $admin->id, 'updated_by' => $admin->id]);
 
         $this->actingAs($this->user)->json('delete', '/images/' . $image->id)->assertStatus(403);
 
-        $this->giveUserPermissions($this->user, ['image-delete-own']);
+        $this->permissions->grantUserRolePermissions($this->user, ['image-delete-own']);
 
         $this->actingAs($this->user)->json('delete', '/images/' . $image->id)->assertStatus(403);
 
-        $this->giveUserPermissions($this->user, ['image-delete-all']);
+        $this->permissions->grantUserRolePermissions($this->user, ['image-delete-all']);
 
         $this->actingAs($this->user)->json('delete', '/images/' . $image->id)->assertOk();
         $this->assertDatabaseMissing('images', ['id' => $image->id]);
@@ -863,7 +863,7 @@ class RolesTest extends TestCase
         // To cover issue fixed in f99c8ff99aee9beb8c692f36d4b84dc6e651e50a.
         $page = $this->entities->page();
         $viewerRole = Role::getRole('viewer');
-        $viewer = $this->getViewer();
+        $viewer = $this->users->viewer();
         $this->actingAs($viewer)->get($page->getUrl())->assertOk();
 
         $this->asAdmin()->put('/settings/roles/' . $viewerRole->id, [
@@ -877,18 +877,18 @@ class RolesTest extends TestCase
 
     public function test_empty_state_actions_not_visible_without_permission()
     {
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
         // Book links
         $book = Book::factory()->create(['created_by' => $admin->id, 'updated_by' => $admin->id]);
-        $this->entities->regenPermissions($book);
-        $this->actingAs($this->getViewer())->get($book->getUrl())
+        $this->permissions->regenerateForEntity($book);
+        $this->actingAs($this->users->viewer())->get($book->getUrl())
             ->assertDontSee('Create a new page')
             ->assertDontSee('Add a chapter');
 
         // Chapter links
         $chapter = Chapter::factory()->create(['created_by' => $admin->id, 'updated_by' => $admin->id, 'book_id' => $book->id]);
-        $this->entities->regenPermissions($chapter);
-        $this->actingAs($this->getViewer())->get($chapter->getUrl())
+        $this->permissions->regenerateForEntity($chapter);
+        $this->actingAs($this->users->viewer())->get($chapter->getUrl())
             ->assertDontSee('Create a new page')
             ->assertDontSee('Sort the current book');
     }
@@ -901,7 +901,7 @@ class RolesTest extends TestCase
             ->addComment($ownPage)
             ->assertStatus(403);
 
-        $this->giveUserPermissions($this->user, ['comment-create-all']);
+        $this->permissions->grantUserRolePermissions($this->user, ['comment-create-all']);
 
         $this->actingAs($this->user)
             ->addComment($ownPage)
@@ -911,7 +911,7 @@ class RolesTest extends TestCase
     public function test_comment_update_own_permission()
     {
         $ownPage = $this->entities->createChainBelongingToUser($this->user)['page'];
-        $this->giveUserPermissions($this->user, ['comment-create-all']);
+        $this->permissions->grantUserRolePermissions($this->user, ['comment-create-all']);
         $this->actingAs($this->user)->addComment($ownPage);
         /** @var Comment $comment */
         $comment = $ownPage->comments()->latest()->first();
@@ -919,7 +919,7 @@ class RolesTest extends TestCase
         // no comment-update-own
         $this->actingAs($this->user)->updateComment($comment)->assertStatus(403);
 
-        $this->giveUserPermissions($this->user, ['comment-update-own']);
+        $this->permissions->grantUserRolePermissions($this->user, ['comment-update-own']);
 
         // now has comment-update-own
         $this->actingAs($this->user)->updateComment($comment)->assertOk();
@@ -936,7 +936,7 @@ class RolesTest extends TestCase
         // no comment-update-all
         $this->actingAs($this->user)->updateComment($comment)->assertStatus(403);
 
-        $this->giveUserPermissions($this->user, ['comment-update-all']);
+        $this->permissions->grantUserRolePermissions($this->user, ['comment-update-all']);
 
         // now has comment-update-all
         $this->actingAs($this->user)->updateComment($comment)->assertOk();
@@ -946,7 +946,7 @@ class RolesTest extends TestCase
     {
         /** @var Page $ownPage */
         $ownPage = $this->entities->createChainBelongingToUser($this->user)['page'];
-        $this->giveUserPermissions($this->user, ['comment-create-all']);
+        $this->permissions->grantUserRolePermissions($this->user, ['comment-create-all']);
         $this->actingAs($this->user)->addComment($ownPage);
 
         /** @var Comment $comment */
@@ -955,7 +955,7 @@ class RolesTest extends TestCase
         // no comment-delete-own
         $this->actingAs($this->user)->deleteComment($comment)->assertStatus(403);
 
-        $this->giveUserPermissions($this->user, ['comment-delete-own']);
+        $this->permissions->grantUserRolePermissions($this->user, ['comment-delete-own']);
 
         // now has comment-update-own
         $this->actingAs($this->user)->deleteComment($comment)->assertOk();
@@ -972,7 +972,7 @@ class RolesTest extends TestCase
         // no comment-delete-all
         $this->actingAs($this->user)->deleteComment($comment)->assertStatus(403);
 
-        $this->giveUserPermissions($this->user, ['comment-delete-all']);
+        $this->permissions->grantUserRolePermissions($this->user, ['comment-delete-all']);
 
         // now has comment-delete-all
         $this->actingAs($this->user)->deleteComment($comment)->assertOk();
diff --git a/tests/Permissions/Scenarios/EntityRolePermissionsTest.php b/tests/Permissions/Scenarios/EntityRolePermissionsTest.php
new file mode 100644 (file)
index 0000000..b92ce62
--- /dev/null
@@ -0,0 +1,201 @@
+<?php
+
+namespace Tests\Permissions\Scenarios;
+
+class EntityRolePermissionsTest extends PermissionScenarioTestCase
+{
+    public function test_01_explicit_allow()
+    {
+        [$user, $role] = $this->users->newUserWithRole();
+        $page = $this->entities->page();
+        $this->permissions->setEntityPermissions($page, ['view'], [$role], false);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_02_explicit_deny()
+    {
+        [$user, $role] = $this->users->newUserWithRole();
+        $page = $this->entities->page();
+        $this->permissions->setEntityPermissions($page, [], [$role], false);
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+
+    public function test_03_same_level_conflicting()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole();
+        $roleB = $this->users->attachNewRole($user);
+        $page = $this->entities->page();
+
+        $this->permissions->disableEntityInheritedPermissions($page);
+        $this->permissions->addEntityPermission($page, [], $roleA);
+        $this->permissions->addEntityPermission($page, ['view'], $roleB);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_20_inherit_allow()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole();
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+
+        $this->permissions->disableEntityInheritedPermissions($chapter);
+        $this->permissions->addEntityPermission($chapter, ['view'], $roleA);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_21_inherit_deny()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole();
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+
+        $this->permissions->disableEntityInheritedPermissions($chapter);
+        $this->permissions->addEntityPermission($chapter, [], $roleA);
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+
+    public function test_22_same_level_conflict_inherit()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole();
+        $roleB = $this->users->attachNewRole($user);
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+
+        $this->permissions->disableEntityInheritedPermissions($chapter);
+        $this->permissions->addEntityPermission($chapter, [], $roleA);
+        $this->permissions->addEntityPermission($chapter, ['view'], $roleB);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_30_child_inherit_override_allow()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole();
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+
+        $this->permissions->disableEntityInheritedPermissions($chapter);
+        $this->permissions->addEntityPermission($chapter, [], $roleA);
+        $this->permissions->addEntityPermission($page, ['view'], $roleA);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_31_child_inherit_override_deny()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole();
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+
+        $this->permissions->disableEntityInheritedPermissions($chapter);
+        $this->permissions->addEntityPermission($chapter, ['view'], $roleA);
+        $this->permissions->addEntityPermission($page, [], $roleA);
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+
+    public function test_40_multi_role_inherit_conflict_override_deny()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole();
+        $roleB = $this->users->attachNewRole($user);
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+
+        $this->permissions->disableEntityInheritedPermissions($chapter);
+        $this->permissions->addEntityPermission($page, [], $roleA);
+        $this->permissions->addEntityPermission($chapter, ['view'], $roleB);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_41_multi_role_inherit_conflict_retain_allow()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole();
+        $roleB = $this->users->attachNewRole($user);
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+
+        $this->permissions->disableEntityInheritedPermissions($chapter);
+        $this->permissions->addEntityPermission($page, ['view'], $roleA);
+        $this->permissions->addEntityPermission($chapter, [], $roleB);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_50_role_override_allow()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole();
+        $page = $this->entities->page();
+        $this->permissions->addEntityPermission($page, ['view'], $roleA);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_51_role_override_deny()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole([], ['page-view-all']);
+        $page = $this->entities->page();
+        $this->permissions->addEntityPermission($page, [], $roleA);
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+
+    public function test_60_inherited_role_override_allow()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole([], []);
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+        $this->permissions->addEntityPermission($chapter, ['view'], $roleA);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_61_inherited_role_override_deny()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole([], ['page-view-all']);
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+        $this->permissions->addEntityPermission($chapter, [], $roleA);
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+
+    public function test_62_inherited_role_override_deny_on_own()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole([], ['page-view-own']);
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+        $this->permissions->addEntityPermission($chapter, [], $roleA);
+        $this->permissions->changeEntityOwner($page, $user);
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+
+    public function test_70_multi_role_inheriting_deny()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole([], ['page-view-all']);
+        $roleB = $this->users->attachNewRole($user);
+        $page = $this->entities->page();
+
+        $this->permissions->addEntityPermission($page, [], $roleB);
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+
+    public function test_80_multi_role_inherited_deny_via_parent()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole([], ['page-view-all']);
+        $roleB = $this->users->attachNewRole($user);
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+
+        $this->permissions->addEntityPermission($chapter, [], $roleB);
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+}
diff --git a/tests/Permissions/Scenarios/EntityUserPermissionsTest.php b/tests/Permissions/Scenarios/EntityUserPermissionsTest.php
new file mode 100644 (file)
index 0000000..4fa8058
--- /dev/null
@@ -0,0 +1,209 @@
+<?php
+
+namespace Tests\Permissions\Scenarios;
+
+class EntityUserPermissionsTest extends PermissionScenarioTestCase
+{
+    public function test_01_explicit_allow()
+    {
+        $user = $this->users->newUser();
+        $page = $this->entities->page();
+        $this->permissions->disableEntityInheritedPermissions($page);
+        $this->permissions->addEntityPermission($page, ['view'], null, $user);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_02_explicit_deny()
+    {
+        $user = $this->users->newUser();
+        $page = $this->entities->page();
+        $this->permissions->disableEntityInheritedPermissions($page);
+        $this->permissions->addEntityPermission($page, [], null, $user);
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+
+    public function test_10_allow_inherit()
+    {
+        $user = $this->users->newUser();
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+        $this->permissions->disableEntityInheritedPermissions($chapter);
+        $this->permissions->addEntityPermission($chapter, ['view'], null, $user);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_11_deny_inherit()
+    {
+        $user = $this->users->newUser();
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+        $this->permissions->disableEntityInheritedPermissions($chapter);
+        $this->permissions->addEntityPermission($chapter, [], null, $user);
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+
+    public function test_12_allow_inherit_override()
+    {
+        $user = $this->users->newUser();
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+        $this->permissions->disableEntityInheritedPermissions($chapter);
+        $this->permissions->addEntityPermission($chapter, [], null, $user);
+        $this->permissions->addEntityPermission($page, ['view'], null, $user);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_13_deny_inherit_override()
+    {
+        $user = $this->users->newUser();
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+        $this->permissions->disableEntityInheritedPermissions($chapter);
+        $this->permissions->addEntityPermission($chapter, ['view'], null, $user);
+        $this->permissions->addEntityPermission($page, ['deny'], null, $user);
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+
+    public function test_40_entity_role_override_allow()
+    {
+        [$user, $role] = $this->users->newUserWithRole();
+        $page = $this->entities->page();
+        $this->permissions->disableEntityInheritedPermissions($page);
+        $this->permissions->addEntityPermission($page, ['view'], null, $user);
+        $this->permissions->addEntityPermission($page, [], $role);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_41_entity_role_override_deny()
+    {
+        [$user, $role] = $this->users->newUserWithRole();
+        $page = $this->entities->page();
+        $this->permissions->disableEntityInheritedPermissions($page);
+        $this->permissions->addEntityPermission($page, [], null, $user);
+        $this->permissions->addEntityPermission($page, ['view'], $role);
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+
+    public function test_42_entity_role_override_allow_via_inherit()
+    {
+        [$user, $role] = $this->users->newUserWithRole();
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+        $this->permissions->disableEntityInheritedPermissions($chapter);
+        $this->permissions->addEntityPermission($chapter, ['view'], null, $user);
+        $this->permissions->addEntityPermission($page, [], $role);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_43_entity_role_override_deny_via_inherit()
+    {
+        [$user, $role] = $this->users->newUserWithRole();
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+        $this->permissions->disableEntityInheritedPermissions($chapter);
+        $this->permissions->addEntityPermission($chapter, [], null, $user);
+        $this->permissions->addEntityPermission($page, ['view'], $role);
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+
+    public function test_50_role_override_allow()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole();
+        $page = $this->entities->page();
+        $this->permissions->addEntityPermission($page, ['view'], null, $user);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_51_role_override_deny()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole([], ['page-view-all']);
+        $page = $this->entities->page();
+        $this->permissions->addEntityPermission($page, [], null, $user);
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+
+    public function test_60_inherited_role_override_allow()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole([], []);
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+        $this->permissions->addEntityPermission($chapter, ['view'], null, $user);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_61_inherited_role_override_deny()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole([], ['page-view-all']);
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+        $this->permissions->addEntityPermission($chapter, [], null, $user);
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+
+    public function test_61_inherited_role_override_deny_on_own()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole([], ['page-view-own']);
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+        $this->permissions->addEntityPermission($chapter, [], null, $user);
+        $this->permissions->changeEntityOwner($page, $user);
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+
+    public function test_70_all_override_allow()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole([], []);
+        $page = $this->entities->page();
+        $this->permissions->addEntityPermission($page, [], $roleA, null);
+        $this->permissions->addEntityPermission($page, ['view'], null, $user);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_71_all_override_deny()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole([], ['page-view-all']);
+        $page = $this->entities->page();
+        $this->permissions->addEntityPermission($page, ['view'], $roleA, null);
+        $this->permissions->addEntityPermission($page, [], null, $user);
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+
+    public function test_80_inherited_all_override_allow()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole([], []);
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+        $this->permissions->addEntityPermission($chapter, [], $roleA, null);
+        $this->permissions->addEntityPermission($chapter, ['view'], null, $user);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_81_inherited_all_override_deny()
+    {
+        [$user, $roleA] = $this->users->newUserWithRole([], ['page-view-all']);
+        $page = $this->entities->pageWithinChapter();
+        $chapter = $page->chapter;
+        $this->permissions->addEntityPermission($chapter, ['view'], $roleA, null);
+        $this->permissions->addEntityPermission($chapter, [], null, $user);
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+}
diff --git a/tests/Permissions/Scenarios/PermissionScenarioTestCase.php b/tests/Permissions/Scenarios/PermissionScenarioTestCase.php
new file mode 100644 (file)
index 0000000..5352f46
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+namespace Tests\Permissions\Scenarios;
+
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Entity;
+use Tests\TestCase;
+
+// Cases defined in dev/docs/permission-scenario-testing.md
+
+class PermissionScenarioTestCase extends TestCase
+{
+    protected function assertVisibleToUser(Entity $entity, User $user)
+    {
+        $this->actingAs($user);
+        $funcView = userCan($entity->getMorphClass() . '-view', $entity);
+        $queryView = $entity->newQuery()->scopes(['visible'])->find($entity->id) !== null;
+
+        $id = $entity->getMorphClass() . ':' . $entity->id;
+        $msg = "Item [{$id}] should be visible but was not found via ";
+        $msg .= implode(' and ', array_filter([!$funcView ? 'userCan' : '', !$queryView ? 'query' : '']));
+
+        static::assertTrue($funcView && $queryView, $msg);
+    }
+
+    protected function assertNotVisibleToUser(Entity $entity, User $user)
+    {
+        $this->actingAs($user);
+        $funcView = userCan($entity->getMorphClass() . '-view', $entity);
+        $queryView = $entity->newQuery()->scopes(['visible'])->find($entity->id) !== null;
+
+        $id = $entity->getMorphClass() . ':' . $entity->id;
+        $msg = "Item [{$id}] should not be visible but was found via ";
+        $msg .= implode(' and ', array_filter([$funcView ? 'userCan' : '', $queryView ? 'query' : '']));
+
+        static::assertTrue(!$funcView && !$queryView, $msg);
+    }
+}
diff --git a/tests/Permissions/Scenarios/RoleContentPermissionsTest.php b/tests/Permissions/Scenarios/RoleContentPermissionsTest.php
new file mode 100644 (file)
index 0000000..8b8c903
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+
+namespace Tests\Permissions\Scenarios;
+
+class RoleContentPermissionsTest extends PermissionScenarioTestCase
+{
+    public function test_01_allow()
+    {
+        [$user] = $this->users->newUserWithRole([], ['page-view-all']);
+        $page = $this->entities->page();
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_02_deny()
+    {
+        [$user] = $this->users->newUserWithRole([], []);
+        $page = $this->entities->page();
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+
+    public function test_10_allow_on_own_with_own()
+    {
+        [$user] = $this->users->newUserWithRole([], ['page-view-own']);
+        $page = $this->entities->page();
+        $this->permissions->changeEntityOwner($page, $user);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_11_deny_on_other_with_own()
+    {
+        [$user] = $this->users->newUserWithRole([], ['page-view-own']);
+        $page = $this->entities->page();
+        $this->permissions->changeEntityOwner($page, $this->users->editor());
+
+        $this->assertNotVisibleToUser($page, $user);
+    }
+
+    public function test_20_multiple_role_conflicting_all()
+    {
+        [$user] = $this->users->newUserWithRole([], ['page-view-all']);
+        $this->users->attachNewRole($user, []);
+        $page = $this->entities->page();
+
+        $this->assertVisibleToUser($page, $user);
+    }
+
+    public function test_21_multiple_role_conflicting_own()
+    {
+        [$user] = $this->users->newUserWithRole([], ['page-view-own']);
+        $this->users->attachNewRole($user, []);
+        $page = $this->entities->page();
+        $this->permissions->changeEntityOwner($page, $user);
+
+        $this->assertVisibleToUser($page, $user);
+    }
+}
index 7e3f7be0032fae9b8ab5030adc6175b46be8aad5..afc7fcef36aafb4e2fd6fd0c92c8aee9c4102e69 100644 (file)
@@ -2,7 +2,6 @@
 
 namespace Tests;
 
-use BookStack\Auth\Permissions\JointPermissionBuilder;
 use BookStack\Auth\Permissions\RolePermission;
 use BookStack\Auth\Role;
 use BookStack\Auth\User;
@@ -89,7 +88,6 @@ class PublicActionTest extends TestCase
         foreach (RolePermission::all() as $perm) {
             $publicRole->attachPermission($perm);
         }
-        $this->app->make(JointPermissionBuilder::class)->rebuildForRole($publicRole);
         user()->clearPermissionCache();
 
         $chapter = $this->entities->chapter();
@@ -173,7 +171,7 @@ class PublicActionTest extends TestCase
     {
         $this->setSettings(['app-public' => 'true']);
         $book = $this->entities->book();
-        $this->entities->setPermissions($book);
+        $this->permissions->setEntityPermissions($book);
 
         $resp = $this->get($book->getUrl());
         $resp->assertSee('Book not found');
index 148b2197ceca4a89823e13d54d62e743793eb26b..4330598baf6378a54ea7cab7b41759b00b955482 100644 (file)
@@ -91,7 +91,7 @@ class ReferencesTest extends TestCase
         $pageB = $this->entities->page();
         $this->createReference($pageB, $page);
 
-        $this->entities->setPermissions($pageB);
+        $this->permissions->setEntityPermissions($pageB);
 
         $this->asEditor()->get($page->getUrl('/references'))->assertDontSee($pageB->name);
         $this->asAdmin()->get($page->getUrl('/references'))->assertSee($pageB->name);
index 990df607e1c14b9f5c37cb6c1f2a488ef77e950b..8adc92f253853e75805dc6e4c52100cb9343bb04 100644 (file)
@@ -14,7 +14,7 @@ class RecycleBinTest extends TestCase
     public function test_recycle_bin_routes_permissions()
     {
         $page = $this->entities->page();
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->actingAs($editor)->delete($page->getUrl());
         $deletion = Deletion::query()->firstOrFail();
 
@@ -33,7 +33,7 @@ class RecycleBinTest extends TestCase
             $this->assertPermissionError($resp);
         }
 
-        $this->giveUserPermissions($editor, ['restrictions-manage-all']);
+        $this->permissions->grantUserRolePermissions($editor, ['restrictions-manage-all']);
 
         foreach ($routes as $route) {
             [$method, $url] = explode(':', $route);
@@ -41,7 +41,7 @@ class RecycleBinTest extends TestCase
             $this->assertPermissionError($resp);
         }
 
-        $this->giveUserPermissions($editor, ['settings-manage']);
+        $this->permissions->grantUserRolePermissions($editor, ['settings-manage']);
 
         foreach ($routes as $route) {
             DB::beginTransaction();
@@ -56,7 +56,7 @@ class RecycleBinTest extends TestCase
     {
         $page = $this->entities->page();
         $book = Book::query()->whereHas('pages')->whereHas('chapters')->withCount(['pages', 'chapters'])->first();
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->actingAs($editor)->delete($page->getUrl());
         $this->actingAs($editor)->delete($book->getUrl());
 
@@ -73,7 +73,7 @@ class RecycleBinTest extends TestCase
     {
         $page = $this->entities->page();
         $book = Book::query()->where('id', '!=', $page->book_id)->whereHas('pages')->whereHas('chapters')->with(['pages', 'chapters'])->firstOrFail();
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->actingAs($editor)->delete($page->getUrl());
         $this->actingAs($editor)->delete($book->getUrl());
 
index 0f3122074d7f452b647ddcbb2cfbef8a1b9de536..239f50e765f6d4974d7f8d5540749a8dc9067e1e 100644 (file)
@@ -32,11 +32,11 @@ class RegenerateReferencesTest extends TestCase
 
     public function test_settings_manage_permission_required()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $resp = $this->actingAs($editor)->post('/settings/maintenance/regenerate-references');
         $this->assertPermissionError($resp);
 
-        $this->giveUserPermissions($editor, ['settings-manage']);
+        $this->permissions->grantUserRolePermissions($editor, ['settings-manage']);
 
         $resp = $this->actingAs($editor)->post('/settings/maintenance/regenerate-references');
         $this->assertNotPermissionError($resp);
index 31c51158f8b87645f8be30c2e6328f65048b5cce..322f90107ddb67d8e3a29c4d2075f5f71269423e 100644 (file)
@@ -20,7 +20,7 @@ class TestEmailTest extends TestCase
     public function test_send_test_email_endpoint_sends_email_and_redirects_user_and_shows_notification()
     {
         Notification::fake();
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
 
         $sendReq = $this->actingAs($admin)->post('/settings/maintenance/send-test-email');
         $sendReq->assertRedirect('/settings/maintenance#image-cleanup');
@@ -37,7 +37,7 @@ class TestEmailTest extends TestCase
         $exception = new \Exception('A random error occurred when testing an email');
         $mockDispatcher->shouldReceive('sendNow')->andThrow($exception);
 
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
         $sendReq = $this->actingAs($admin)->post('/settings/maintenance/send-test-email');
         $sendReq->assertRedirect('/settings/maintenance#image-cleanup');
         $this->assertSessionHas('error');
@@ -50,12 +50,12 @@ class TestEmailTest extends TestCase
     public function test_send_test_email_requires_settings_manage_permission()
     {
         Notification::fake();
-        $user = $this->getViewer();
+        $user = $this->users->viewer();
 
         $sendReq = $this->actingAs($user)->post('/settings/maintenance/send-test-email');
         Notification::assertNothingSent();
 
-        $this->giveUserPermissions($user, ['settings-manage']);
+        $this->permissions->grantUserRolePermissions($user, ['settings-manage']);
         $sendReq = $this->actingAs($user)->post('/settings/maintenance/send-test-email');
         Notification::assertSentTo($user, TestEmail::class);
     }
index d0dd7d772084f9a6042e2902022854dfd0a7b21b..70fd0da1d2719e90ead4389a51cde9d2db3794e9 100644 (file)
@@ -2,11 +2,6 @@
 
 namespace Tests;
 
-use BookStack\Auth\Permissions\JointPermissionBuilder;
-use BookStack\Auth\Permissions\PermissionsRepo;
-use BookStack\Auth\Permissions\RolePermission;
-use BookStack\Auth\Role;
-use BookStack\Auth\User;
 use BookStack\Entities\Models\Entity;
 use BookStack\Settings\SettingService;
 use BookStack\Uploads\HttpFetcher;
@@ -22,12 +17,15 @@ use Illuminate\Support\Env;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\Log;
 use Illuminate\Testing\Assert as PHPUnit;
+use Mockery;
 use Monolog\Handler\TestHandler;
 use Monolog\Logger;
 use Psr\Http\Client\ClientInterface;
 use Ssddanbrown\AssertHtml\TestsHtml;
 use Tests\Helpers\EntityProvider;
+use Tests\Helpers\PermissionsProvider;
 use Tests\Helpers\TestServiceProvider;
+use Tests\Helpers\UserRoleProvider;
 
 abstract class TestCase extends BaseTestCase
 {
@@ -35,13 +33,16 @@ abstract class TestCase extends BaseTestCase
     use DatabaseTransactions;
     use TestsHtml;
 
-    protected ?User $admin = null;
-    protected ?User $editor = null;
     protected EntityProvider $entities;
+    protected UserRoleProvider $users;
+    protected PermissionsProvider $permissions;
 
     protected function setUp(): void
     {
         $this->entities = new EntityProvider();
+        $this->users = new UserRoleProvider();
+        $this->permissions = new PermissionsProvider($this->users);
+
         parent::setUp();
     }
 
@@ -70,20 +71,7 @@ abstract class TestCase extends BaseTestCase
      */
     public function asAdmin()
     {
-        return $this->actingAs($this->getAdmin());
-    }
-
-    /**
-     * Get the current admin user.
-     */
-    public function getAdmin(): User
-    {
-        if (is_null($this->admin)) {
-            $adminRole = Role::getSystemRole('admin');
-            $this->admin = $adminRole->users->first();
-        }
-
-        return $this->admin;
+        return $this->actingAs($this->users->admin());
     }
 
     /**
@@ -91,20 +79,7 @@ abstract class TestCase extends BaseTestCase
      */
     public function asEditor()
     {
-        return $this->actingAs($this->getEditor());
-    }
-
-    /**
-     * Get a editor user.
-     */
-    protected function getEditor(): User
-    {
-        if ($this->editor === null) {
-            $editorRole = Role::getRole('editor');
-            $this->editor = $editorRole->users->first();
-        }
-
-        return $this->editor;
+        return $this->actingAs($this->users->editor());
     }
 
     /**
@@ -112,28 +87,7 @@ abstract class TestCase extends BaseTestCase
      */
     public function asViewer()
     {
-        return $this->actingAs($this->getViewer());
-    }
-
-    /**
-     * Get an instance of a user with 'viewer' permissions.
-     */
-    protected function getViewer(array $attributes = []): User
-    {
-        $user = Role::getRole('viewer')->users()->first();
-        if (!empty($attributes)) {
-            $user->forceFill($attributes)->save();
-        }
-
-        return $user;
-    }
-
-    /**
-     * Get a user that's not a system user such as the guest user.
-     */
-    public function getNormalUser(): User
-    {
-        return User::query()->where('system_name', '=', null)->get()->last();
+        return $this->actingAs($this->users->viewer());
     }
 
     /**
@@ -147,52 +101,6 @@ abstract class TestCase extends BaseTestCase
         }
     }
 
-    /**
-     * Give the given user some permissions.
-     */
-    protected function giveUserPermissions(User $user, array $permissions = []): void
-    {
-        $newRole = $this->createNewRole($permissions);
-        $user->attachRole($newRole);
-        $user->load('roles');
-        $user->clearPermissionCache();
-    }
-
-    /**
-     * Completely remove the given permission name from the given user.
-     */
-    protected function removePermissionFromUser(User $user, string $permissionName)
-    {
-        $permissionBuilder = app()->make(JointPermissionBuilder::class);
-
-        /** @var RolePermission $permission */
-        $permission = RolePermission::query()->where('name', '=', $permissionName)->firstOrFail();
-
-        $roles = $user->roles()->whereHas('permissions', function ($query) use ($permission) {
-            $query->where('id', '=', $permission->id);
-        })->get();
-
-        /** @var Role $role */
-        foreach ($roles as $role) {
-            $role->detachPermission($permission);
-            $permissionBuilder->rebuildForRole($role);
-        }
-
-        $user->clearPermissionCache();
-    }
-
-    /**
-     * Create a new basic role for testing purposes.
-     */
-    protected function createNewRole(array $permissions = []): Role
-    {
-        $permissionRepo = app(PermissionsRepo::class);
-        $roleData = Role::factory()->make()->toArray();
-        $roleData['permissions'] = array_flip($permissions);
-
-        return $permissionRepo->saveNewRole($roleData);
-    }
-
     /**
      * Mock the HttpFetcher service and return the given data on fetch.
      */
index efab53379f3d72a83d5af45d7fe4aa47de51b2d1..ee4f20f3084ce3ece8f34c98d2535b28051da954 100644 (file)
@@ -36,7 +36,7 @@ class ThemeTest extends TestCase
         ';
             file_put_contents($translationPath . '/entities.php', $customTranslations);
 
-            $homeRequest = $this->actingAs($this->getViewer())->get('/');
+            $homeRequest = $this->actingAs($this->users->viewer())->get('/');
             $this->withHtml($homeRequest)->assertElementContains('header nav', 'Sandwiches');
         });
     }
index 54d315de94054c2765fa559b3e258a7adb4442c9..d4feff60c9da66973019dec0596cd80a316f2845 100644 (file)
@@ -25,7 +25,7 @@ class FrameworkAssumptionTest extends TestCase
         // Page has SoftDeletes trait by default, so we apply our custom scope and ensure
         // it stacks on the global scope to filter out deleted items.
         $query = Page::query()->scopes('visible')->toSql();
-        $this->assertStringContainsString('joint_permissions', $query);
+        $this->assertStringContainsString('entity_permissions_collapsed', $query);
         $this->assertStringContainsString('`deleted_at` is null', $query);
     }
 }
index b6fcb8f69995d3767d5dc1a8f3f140b5f469d66a..f2f30ff2e8addeb36883e08b1afa07513429311d 100644 (file)
@@ -75,7 +75,7 @@ class AttachmentTest extends TestCase
     {
         $page = $this->entities->page();
         $this->asAdmin();
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
         $fileName = 'upload_test_file.txt';
 
         $expectedResp = [
@@ -137,7 +137,7 @@ class AttachmentTest extends TestCase
     public function test_attaching_link_to_page()
     {
         $page = $this->entities->page();
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
         $this->asAdmin();
 
         $linkReq = $this->call('POST', 'attachments/link', [
@@ -245,15 +245,15 @@ class AttachmentTest extends TestCase
 
     public function test_attachment_access_without_permission_shows_404()
     {
-        $admin = $this->getAdmin();
-        $viewer = $this->getViewer();
+        $admin = $this->users->admin();
+        $viewer = $this->users->viewer();
         $page = $this->entities->page(); /** @var Page $page */
         $this->actingAs($admin);
         $fileName = 'permission_test.txt';
         $this->uploadFile($fileName, $page->id);
         $attachment = Attachment::orderBy('id', 'desc')->take(1)->first();
 
-        $this->entities->setPermissions($page, [], []);
+        $this->permissions->setEntityPermissions($page, [], []);
 
         $this->actingAs($viewer);
         $attachmentGet = $this->get($attachment->getUrl());
index 2ed4da7cadc6a9ff65525e6afb5b623c737709a6..080f05d7402f8971db9eff17d0d86459b03124bb 100644 (file)
@@ -30,7 +30,7 @@ class DrawioTest extends TestCase
     public function test_drawing_base64_upload()
     {
         $page = Page::first();
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->actingAs($editor);
 
         $upload = $this->postJson('images/drawio', [
@@ -58,7 +58,7 @@ class DrawioTest extends TestCase
     {
         config()->set('services.drawio', 'http://cats.com?dog=tree');
         $page = Page::first();
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
 
         $resp = $this->actingAs($editor)->get($page->getUrl('/edit'));
         $resp->assertSee('drawio-url="http://cats.com?dog=tree"', false);
@@ -68,7 +68,7 @@ class DrawioTest extends TestCase
     {
         config()->set('services.drawio', true);
         $page = Page::first();
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
 
         $resp = $this->actingAs($editor)->get($page->getUrl('/edit'));
         $resp->assertSee('drawio-url="https://embed.diagrams.net/?embed=1&amp;proto=json&amp;spin=1&amp;configure=1"', false);
index 0e4065a827453c743188b16342f00d3bf5ddcdd2..c6e678ff2f73ecbf94141958dc7a015ea3ac4cac 100644 (file)
@@ -16,7 +16,7 @@ class ImageTest extends TestCase
     public function test_image_upload()
     {
         $page = $this->entities->page();
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
         $this->actingAs($admin);
 
         $imgDetails = $this->uploadGalleryImage($page);
@@ -40,7 +40,7 @@ class ImageTest extends TestCase
     public function test_image_display_thumbnail_generation_does_not_increase_image_size()
     {
         $page = $this->entities->page();
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
         $this->actingAs($admin);
 
         $originalFile = $this->getTestImageFilePath('compressed.png');
@@ -64,7 +64,7 @@ class ImageTest extends TestCase
     public function test_image_display_thumbnail_generation_for_apng_images_uses_original_file()
     {
         $page = $this->entities->page();
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
         $this->actingAs($admin);
 
         $imgDetails = $this->uploadGalleryImage($page, 'animated.png');
@@ -76,7 +76,7 @@ class ImageTest extends TestCase
 
     public function test_image_edit()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->actingAs($editor);
 
         $imgDetails = $this->uploadGalleryImage();
@@ -126,7 +126,7 @@ class ImageTest extends TestCase
     public function test_image_usage()
     {
         $page = $this->entities->page();
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->actingAs($editor);
 
         $imgDetails = $this->uploadGalleryImage($page);
@@ -146,7 +146,7 @@ class ImageTest extends TestCase
     public function test_php_files_cannot_be_uploaded()
     {
         $page = $this->entities->page();
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
         $this->actingAs($admin);
 
         $fileName = 'bad.php';
@@ -168,7 +168,7 @@ class ImageTest extends TestCase
     public function test_php_like_files_cannot_be_uploaded()
     {
         $page = $this->entities->page();
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
         $this->actingAs($admin);
 
         $fileName = 'bad.phtml';
@@ -185,7 +185,7 @@ class ImageTest extends TestCase
     public function test_files_with_double_extensions_will_get_sanitized()
     {
         $page = $this->entities->page();
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
         $this->actingAs($admin);
 
         $fileName = 'bad.phtml.png';
@@ -358,7 +358,7 @@ class ImageTest extends TestCase
 
         $this->get($expectedUrl)->assertOk();
 
-        $this->entities->setPermissions($page, [], []);
+        $this->permissions->setEntityPermissions($page, [], []);
 
         $resp = $this->get($expectedUrl);
         $resp->assertNotFound();
@@ -382,7 +382,7 @@ class ImageTest extends TestCase
 
         $this->get($expectedUrl)->assertOk();
 
-        $this->entities->setPermissions($page, [], []);
+        $this->permissions->setEntityPermissions($page, [], []);
 
         $resp = $this->get($expectedUrl);
         $resp->assertNotFound();
@@ -415,7 +415,7 @@ class ImageTest extends TestCase
         $export = $this->get($pageB->getUrl('/export/html'));
         $this->assertStringContainsString($encodedImageContent, $export->getContent());
 
-        $this->entities->setPermissions($pageA, [], []);
+        $this->permissions->setEntityPermissions($pageA, [], []);
 
         $export = $this->get($pageB->getUrl('/export/html'));
         $this->assertStringNotContainsString($encodedImageContent, $export->getContent());
@@ -479,7 +479,7 @@ class ImageTest extends TestCase
         $imageName = 'first-image.png';
         $relPath = $this->getTestImagePath('gallery', $imageName);
         $this->deleteImage($relPath);
-        $viewer = $this->getViewer();
+        $viewer = $this->users->viewer();
 
         $this->uploadImage($imageName, $page->id);
         $image = Image::first();
@@ -490,7 +490,7 @@ class ImageTest extends TestCase
         $resp = $this->actingAs($viewer)->get("/images/edit/{$image->id}");
         $this->withHtml($resp)->assertElementNotExists('button#image-manager-delete[title="Delete"]');
 
-        $this->giveUserPermissions($viewer, ['image-delete-all']);
+        $this->permissions->grantUserRolePermissions($viewer, ['image-delete-all']);
 
         $resp = $this->actingAs($viewer)->get("/images/edit/{$image->id}");
         $this->withHtml($resp)->assertElementExists('button#image-manager-delete[title="Delete"]');
@@ -509,8 +509,8 @@ class ImageTest extends TestCase
 
     public function test_user_image_upload()
     {
-        $editor = $this->getEditor();
-        $admin = $this->getAdmin();
+        $editor = $this->users->editor();
+        $admin = $this->users->admin();
         $this->actingAs($admin);
 
         $file = $this->getTestProfileImage();
@@ -525,7 +525,7 @@ class ImageTest extends TestCase
 
     public function test_user_images_deleted_on_user_deletion()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->actingAs($editor);
 
         $file = $this->getTestProfileImage();
@@ -555,7 +555,7 @@ class ImageTest extends TestCase
     public function test_deleted_unused_images()
     {
         $page = $this->entities->page();
-        $admin = $this->getAdmin();
+        $admin = $this->users->admin();
         $this->actingAs($admin);
 
         $imageName = 'unused-image.png';
index 716f3614cb214f52a50fadb51e8468268d50c7c7..93070b712188e325f0ddc94a3653d600394702c5 100644 (file)
@@ -16,12 +16,12 @@ class UserApiTokenTest extends TestCase
 
     public function test_tokens_section_not_visible_without_access_api_permission()
     {
-        $user = $this->getViewer();
+        $user = $this->users->viewer();
 
         $resp = $this->actingAs($user)->get($user->getEditUrl());
         $resp->assertDontSeeText('API Tokens');
 
-        $this->giveUserPermissions($user, ['access-api']);
+        $this->permissions->grantUserRolePermissions($user, ['access-api']);
 
         $resp = $this->actingAs($user)->get($user->getEditUrl());
         $resp->assertSeeText('API Tokens');
@@ -30,9 +30,9 @@ class UserApiTokenTest extends TestCase
 
     public function test_those_with_manage_users_can_view_other_user_tokens_but_not_create()
     {
-        $viewer = $this->getViewer();
-        $editor = $this->getEditor();
-        $this->giveUserPermissions($viewer, ['users-manage']);
+        $viewer = $this->users->viewer();
+        $editor = $this->users->editor();
+        $this->permissions->grantUserRolePermissions($viewer, ['users-manage']);
 
         $resp = $this->actingAs($viewer)->get($editor->getEditUrl());
         $resp->assertSeeText('API Tokens');
@@ -41,7 +41,7 @@ class UserApiTokenTest extends TestCase
 
     public function test_create_api_token()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
 
         $resp = $this->asAdmin()->get($editor->getEditUrl('/create-api-token'));
         $resp->assertStatus(200);
@@ -74,7 +74,7 @@ class UserApiTokenTest extends TestCase
 
     public function test_create_with_no_expiry_sets_expiry_hundred_years_away()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->asAdmin()->post($editor->getEditUrl('/create-api-token'), ['name' => 'No expiry token', 'expires_at' => '']);
         $token = ApiToken::query()->latest()->first();
 
@@ -88,7 +88,7 @@ class UserApiTokenTest extends TestCase
 
     public function test_created_token_displays_on_profile_page()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->asAdmin()->post($editor->getEditUrl('/create-api-token'), $this->testTokenData);
         $token = ApiToken::query()->latest()->first();
 
@@ -101,7 +101,7 @@ class UserApiTokenTest extends TestCase
 
     public function test_secret_shown_once_after_creation()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $resp = $this->asAdmin()->followingRedirects()->post($editor->getEditUrl('/create-api-token'), $this->testTokenData);
         $resp->assertSeeText('Token Secret');
 
@@ -114,7 +114,7 @@ class UserApiTokenTest extends TestCase
 
     public function test_token_update()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->asAdmin()->post($editor->getEditUrl('/create-api-token'), $this->testTokenData);
         $token = ApiToken::query()->latest()->first();
         $updateData = [
@@ -132,7 +132,7 @@ class UserApiTokenTest extends TestCase
 
     public function test_token_update_with_blank_expiry_sets_to_hundred_years_away()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->asAdmin()->post($editor->getEditUrl('/create-api-token'), $this->testTokenData);
         $token = ApiToken::query()->latest()->first();
 
@@ -152,7 +152,7 @@ class UserApiTokenTest extends TestCase
 
     public function test_token_delete()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->asAdmin()->post($editor->getEditUrl('/create-api-token'), $this->testTokenData);
         $token = ApiToken::query()->latest()->first();
 
@@ -171,9 +171,9 @@ class UserApiTokenTest extends TestCase
 
     public function test_user_manage_can_delete_token_without_api_permission_themselves()
     {
-        $viewer = $this->getViewer();
-        $editor = $this->getEditor();
-        $this->giveUserPermissions($editor, ['users-manage']);
+        $viewer = $this->users->viewer();
+        $editor = $this->users->editor();
+        $this->permissions->grantUserRolePermissions($editor, ['users-manage']);
 
         $this->asAdmin()->post($viewer->getEditUrl('/create-api-token'), $this->testTokenData);
         $token = ApiToken::query()->latest()->first();
index b5cd764da13f4ac2a69cb112cbca42ee11baed3b..1c5c040da726bf16cc84f0180a33cc8475daa880 100644 (file)
@@ -46,7 +46,7 @@ class UserManagementTest extends TestCase
 
     public function test_user_updating()
     {
-        $user = $this->getNormalUser();
+        $user = $this->users->viewer();
         $password = $user->password;
 
         $resp = $this->asAdmin()->get('/settings/users/' . $user->id);
@@ -65,7 +65,7 @@ class UserManagementTest extends TestCase
 
     public function test_user_password_update()
     {
-        $user = $this->getNormalUser();
+        $user = $this->users->viewer();
         $userProfilePage = '/settings/users/' . $user->id;
 
         $this->asAdmin()->get($userProfilePage);
@@ -113,7 +113,7 @@ class UserManagementTest extends TestCase
 
     public function test_delete()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $resp = $this->asAdmin()->delete("settings/users/{$editor->id}");
         $resp->assertRedirect('/settings/users');
         $resp = $this->followRedirects($resp);
@@ -126,7 +126,7 @@ class UserManagementTest extends TestCase
 
     public function test_delete_offers_migrate_option()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $resp = $this->asAdmin()->get("settings/users/{$editor->id}/delete");
         $resp->assertSee('Migrate Ownership');
         $resp->assertSee('new_owner_id');
@@ -134,13 +134,13 @@ class UserManagementTest extends TestCase
 
     public function test_migrate_option_hidden_if_user_cannot_manage_users()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
 
         $resp = $this->asEditor()->get("settings/users/{$editor->id}/delete");
         $resp->assertDontSee('Migrate Ownership');
         $resp->assertDontSee('new_owner_id');
 
-        $this->giveUserPermissions($editor, ['users-manage']);
+        $this->permissions->grantUserRolePermissions($editor, ['users-manage']);
 
         $resp = $this->asEditor()->get("settings/users/{$editor->id}/delete");
         $resp->assertSee('Migrate Ownership');
@@ -162,7 +162,7 @@ class UserManagementTest extends TestCase
 
     public function test_delete_removes_user_preferences()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         setting()->putUser($editor, 'dark-mode-enabled', 'true');
 
         $this->assertDatabaseHas('settings', [
@@ -253,7 +253,7 @@ class UserManagementTest extends TestCase
 
     public function test_user_create_update_fails_if_locale_is_invalid()
     {
-        $user = $this->getEditor();
+        $user = $this->users->editor();
 
         // Too long
         $resp = $this->asAdmin()->put($user->getEditUrl(), ['language' => 'this_is_too_long']);
@@ -274,34 +274,4 @@ class UserManagementTest extends TestCase
         $resp->assertSessionHasErrors(['language' => 'The language may not be greater than 15 characters.']);
         $resp->assertSessionHasErrors(['language' => 'The language may only contain letters, numbers, dashes and underscores.']);
     }
-
-    public function test_role_removal_on_user_edit_removes_all_role_assignments()
-    {
-        $user = $this->getEditor();
-
-        $this->assertEquals(1, $user->roles()->count());
-
-        // A roles[0] hidden fields is used to indicate the existence of role selection in the submission
-        // of the user edit form. We check that field is used and emulate its submission.
-        $resp = $this->asAdmin()->get("/settings/users/{$user->id}");
-        $this->withHtml($resp)->assertElementExists('input[type="hidden"][name="roles[0]"][value="0"]');
-
-        $resp = $this->asAdmin()->put("/settings/users/{$user->id}", [
-            'name'  => $user->name,
-            'email' => $user->email,
-            'roles' => ['0' => '0'],
-        ]);
-        $resp->assertRedirect("/settings/users");
-
-        $this->assertEquals(0, $user->roles()->count());
-    }
-
-    public function test_role_form_hidden_indicator_field_does_not_exist_where_roles_cannot_be_managed()
-    {
-        $user = $this->getEditor();
-        $resp = $this->actingAs($user)->get("/settings/users/{$user->id}");
-        $html = $this->withHtml($resp);
-        $html->assertElementExists('input[name="email"]');
-        $html->assertElementNotExists('input[type="hidden"][name="roles[0]"]');
-    }
 }
index 03dad7990464f7228acf389818a8d2f6018a5462..e47a259a5bad6279b1d2942c0025ec53b90a9ecc 100644 (file)
@@ -36,7 +36,7 @@ class UserPreferencesTest extends TestCase
 
     public function test_body_has_shortcuts_component_when_active()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->actingAs($editor);
 
         $this->withHtml($this->get('/'))->assertElementNotExists('body[component="shortcuts"]');
@@ -47,7 +47,7 @@ class UserPreferencesTest extends TestCase
 
     public function test_update_sort_preference()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->actingAs($editor);
 
         $updateRequest = $this->patch('/preferences/change-sort/books', [
@@ -70,7 +70,7 @@ class UserPreferencesTest extends TestCase
 
     public function test_update_sort_bad_entity_type_handled()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->actingAs($editor);
 
         $updateRequest = $this->patch('/preferences/change-sort/dogs', [
@@ -85,7 +85,7 @@ class UserPreferencesTest extends TestCase
 
     public function test_update_expansion_preference()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $this->actingAs($editor);
 
         $updateRequest = $this->patch('/preferences/change-expansion/home-details', ['expand' => 'true']);
@@ -103,7 +103,7 @@ class UserPreferencesTest extends TestCase
 
     public function test_toggle_dark_mode()
     {
-        $home = $this->actingAs($this->getEditor())->get('/');
+        $home = $this->actingAs($this->users->editor())->get('/');
         $home->assertSee('Dark Mode');
         $this->withHtml($home)->assertElementNotExists('.dark-mode');
 
@@ -112,7 +112,7 @@ class UserPreferencesTest extends TestCase
         $prefChange->assertRedirect();
         $this->assertEquals(true, setting()->getForCurrentUser('dark-mode-enabled'));
 
-        $home = $this->actingAs($this->getEditor())->get('/');
+        $home = $this->actingAs($this->users->editor())->get('/');
         $this->withHtml($home)->assertElementExists('.dark-mode');
         $home->assertDontSee('Dark Mode');
         $home->assertSee('Light Mode');
@@ -133,7 +133,7 @@ class UserPreferencesTest extends TestCase
 
     public function test_books_view_type_preferences_when_list()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         setting()->putUser($editor, 'books_view_type', 'list');
 
         $resp = $this->actingAs($editor)->get('/books');
@@ -144,7 +144,7 @@ class UserPreferencesTest extends TestCase
 
     public function test_books_view_type_preferences_when_grid()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         setting()->putUser($editor, 'books_view_type', 'grid');
 
         $resp = $this->actingAs($editor)->get('/books');
@@ -153,7 +153,7 @@ class UserPreferencesTest extends TestCase
 
     public function test_shelf_view_type_change()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $shelf = $this->entities->shelf();
         setting()->putUser($editor, 'bookshelf_view_type', 'list');
 
@@ -175,7 +175,7 @@ class UserPreferencesTest extends TestCase
 
     public function test_update_code_language_favourite()
     {
-        $editor = $this->getEditor();
+        $editor = $this->users->editor();
         $page = $this->entities->page();
         $this->actingAs($editor);
 
index 77f1644a5e184335419e5ee121b1e9f816e2abbf..c507e8fa63a98d96329e1fad0d435b5021ec32b0 100644 (file)
@@ -88,8 +88,8 @@ class UserProfileTest extends TestCase
 
     public function test_profile_has_search_links_in_created_entity_lists()
     {
-        $user = $this->getEditor();
-        $resp = $this->actingAs($this->getAdmin())->get('/user/' . $user->slug);
+        $user = $this->users->editor();
+        $resp = $this->actingAs($this->users->admin())->get('/user/' . $user->slug);
 
         $expectedLinks = [
             '/search?term=%7Bcreated_by%3A' . $user->slug . '%7D+%7Btype%3Apage%7D',
index 243af1186a2be9e2f5b1e7aceffbc66a475c918a..1b3ca8a35148e8bd22e3631857663fa4fc5298e6 100644 (file)
@@ -9,8 +9,8 @@ class UserSearchTest extends TestCase
 {
     public function test_select_search_matches_by_name()
     {
-        $viewer = $this->getViewer();
-        $admin = $this->getAdmin();
+        $viewer = $this->users->viewer();
+        $admin = $this->users->admin();
         $resp = $this->actingAs($admin)->get('/search/users/select?search=' . urlencode($viewer->name));
 
         $resp->assertOk();
@@ -30,8 +30,8 @@ class UserSearchTest extends TestCase
 
     public function test_select_search_does_not_match_by_email()
     {
-        $viewer = $this->getViewer();
-        $editor = $this->getEditor();
+        $viewer = $this->users->viewer();
+        $editor = $this->users->editor();
         $resp = $this->actingAs($editor)->get('/search/users/select?search=' . urlencode($viewer->email));
 
         $resp->assertDontSee($viewer->name);
@@ -40,13 +40,13 @@ class UserSearchTest extends TestCase
     public function test_select_requires_right_permission()
     {
         $permissions = ['users-manage', 'restrictions-manage-own', 'restrictions-manage-all'];
-        $user = $this->getViewer();
+        $user = $this->users->viewer();
 
         foreach ($permissions as $permission) {
             $resp = $this->actingAs($user)->get('/search/users/select?search=a');
             $this->assertPermissionError($resp);
 
-            $this->giveUserPermissions($user, [$permission]);
+            $this->permissions->grantUserRolePermissions($user, [$permission]);
             $resp = $this->actingAs($user)->get('/search/users/select?search=a');
             $resp->assertOk();
             $user->roles()->delete();
@@ -58,7 +58,7 @@ class UserSearchTest extends TestCase
     {
         $this->setSettings(['app-public' => true]);
         $defaultUser = User::getDefault();
-        $this->giveUserPermissions($defaultUser, ['users-manage']);
+        $this->permissions->grantUserRolePermissions($defaultUser, ['users-manage']);
 
         $resp = $this->get('/search/users/select?search=a');
         $this->assertPermissionError($resp);
Morty Proxy This is a proxified and sanitized view of the page, visit original site.