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.
+- Parent 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.
- Where both grant and deny exist at the same specificity, we side towards grant.
## Cases
- 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
namespace Tests\Commands;
use BookStack\Auth\Permissions\CollapsedPermission;
+use BookStack\Auth\Permissions\EntityPermission;
+use BookStack\Auth\Permissions\JointPermission;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
DB::rollBack();
$page = $this->entities->page();
$editor = $this->users->editor();
- $this->permissions->addEntityPermission($page, ['view'], null, $editor);
- CollapsedPermission::query()->truncate();
+ $role = $editor->roles()->first();
+ $this->permissions->addEntityPermission($page, ['view'], $role);
+ JointPermission::query()->truncate();
- $this->assertDatabaseMissing('entity_permissions_collapsed', ['entity_id' => $page->id]);
+ $this->assertDatabaseMissing('joint_permissions', ['entity_id' => $page->id]);
$exitCode = Artisan::call('bookstack:regenerate-permissions');
$this->assertTrue($exitCode === 0, 'Command executed successfully');
- $this->assertDatabaseHas('entity_permissions_collapsed', [
+ $this->assertDatabaseHas('joint_permissions', [
'entity_id' => $page->id,
- 'user_id' => $editor->id,
- 'view' => 1,
+ 'entity_type' => 'page',
+ 'role_id' => $role->id,
+ 'has_permission' => 1,
]);
- CollapsedPermission::query()->truncate();
+ $page->permissions()->delete();
+ $page->rebuildPermissions();
+
DB::beginTransaction();
}
}
$this->withHtml($resp)->assertElementContains('header', 'Shelves');
$viewer->roles()->delete();
+ $this->permissions->grantUserRolePermissions($viewer, []);
$resp = $this->actingAs($viewer)->get('/');
$this->withHtml($resp)->assertElementNotContains('header', 'Shelves');
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];
+ $permissions[] = ['role_id' => 0, 'view' => false, 'create' => false, 'update' => false, 'delete' => false];
}
foreach ($roles as $role) {
$this->addEntityPermissionEntries($entity, $permissions);
}
- public function addEntityPermission(Entity $entity, array $actionList, ?Role $role = null, ?User $user = null)
+ public function addEntityPermission(Entity $entity, array $actionList, Role $role)
{
- $permissionData = $this->actionListToEntityPermissionData($actionList, $role->id ?? null, $user->id ?? null);
+ $permissionData = $this->actionListToEntityPermissionData($actionList, $role->id);
$this->addEntityPermissionEntries($entity, [$permissionData]);
}
*/
public function disableEntityInheritedPermissions(Entity $entity): void
{
- $entity->permissions()->whereNull(['user_id', 'role_id'])->delete();
+ $entity->permissions()->where('role_id', '=', 0)->delete();
$fallback = $this->actionListToEntityPermissionData([]);
$this->addEntityPermissionEntries($entity, [$fallback]);
}
* 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
+ protected function actionListToEntityPermissionData(array $actionList, int $roleId = 0): array
{
- $permissionData = ['role_id' => $roleId, 'user_id' => $userId];
+ $permissionData = ['role_id' => $roleId];
foreach (EntityPermission::PERMISSIONS as $possibleAction) {
$permissionData[$possibleAction] = in_array($possibleAction, $actionList);
}
$this->put($modelInstance->getUrl('/permissions'), [
'permissions' => [
- 'role' => [
- $roleId => [
- $permission => 'true',
- ],
+ $roleId => [
+ $permission => 'true',
],
],
]);
$this->assertDatabaseHas('entity_permissions', [
- 'entity_id' => $modelInstance->id,
- 'entity_type' => $modelInstance->getMorphClass(),
- 'role_id' => $roleId,
- $permission => true,
+ 'entity_id' => $modelInstance->id,
+ 'entity_type' => $modelInstance->getMorphClass(),
+ 'role_id' => $roleId,
+ $permission => true,
]);
}
+++ /dev/null
-<?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);
- }
-}
DB::purge();
config()->set('database.connections.mysql_testing.database', $database);
+ DB::beginTransaction();
$callback();
+ DB::rollBack();
+
if (is_null($originalVal)) {
unset($_SERVER[$name]);
} else {
// 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('entity_permissions_collapsed', $query);
+ $this->assertStringContainsString('joint_permissions', $query);
$this->assertStringContainsString('`deleted_at` is null', $query);
}
}