From c42956bcafc7c43275457887c119476af8f72b36 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Mon, 13 Mar 2023 13:18:33 +0000 Subject: [PATCH 1/3] Started build of content-permissions API endpoints --- app/Auth/Permissions/EntityPermission.php | 17 ++-- app/Entities/EntityProvider.php | 38 +++----- app/Entities/Tools/PermissionsUpdater.php | 74 +++++++++++++++- .../Api/ContentPermissionsController.php | 87 +++++++++++++++++++ routes/api.php | 4 + 5 files changed, 181 insertions(+), 39 deletions(-) create mode 100644 app/Http/Controllers/Api/ContentPermissionsController.php diff --git a/app/Auth/Permissions/EntityPermission.php b/app/Auth/Permissions/EntityPermission.php index 32ebc440d..603cf61ad 100644 --- a/app/Auth/Permissions/EntityPermission.php +++ b/app/Auth/Permissions/EntityPermission.php @@ -5,7 +5,6 @@ namespace BookStack\Auth\Permissions; use BookStack\Auth\Role; use BookStack\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\MorphTo; /** * @property int $id @@ -23,14 +22,14 @@ class EntityPermission extends Model protected $fillable = ['role_id', 'view', 'create', 'update', 'delete']; public $timestamps = false; - - /** - * Get this restriction's attached entity. - */ - public function restrictable(): MorphTo - { - return $this->morphTo('restrictable'); - } + protected $hidden = ['entity_id', 'entity_type', 'id']; + protected $casts = [ + 'view' => 'boolean', + 'create' => 'boolean', + 'read' => 'boolean', + 'update' => 'boolean', + 'delete' => 'boolean', + ]; /** * Get the role assigned to this entity permission. diff --git a/app/Entities/EntityProvider.php b/app/Entities/EntityProvider.php index aaf392c7b..365daf7eb 100644 --- a/app/Entities/EntityProvider.php +++ b/app/Entities/EntityProvider.php @@ -18,30 +18,11 @@ use BookStack\Entities\Models\PageRevision; */ class EntityProvider { - /** - * @var Bookshelf - */ - public $bookshelf; - - /** - * @var Book - */ - public $book; - - /** - * @var Chapter - */ - public $chapter; - - /** - * @var Page - */ - public $page; - - /** - * @var PageRevision - */ - public $pageRevision; + public Bookshelf $bookshelf; + public Book $book; + public Chapter $chapter; + public Page $page; + public PageRevision $pageRevision; public function __construct() { @@ -69,13 +50,18 @@ class EntityProvider } /** - * Get an entity instance by it's basic name. + * Get an entity instance by its basic name. */ public function get(string $type): Entity { $type = strtolower($type); + $instance = $this->all()[$type] ?? null; - return $this->all()[$type]; + if (is_null($instance)) { + throw new \InvalidArgumentException("Provided type \"{$type}\" is not a valid entity type"); + } + + return $instance; } /** diff --git a/app/Entities/Tools/PermissionsUpdater.php b/app/Entities/Tools/PermissionsUpdater.php index eb4eb6b48..0d1d307af 100644 --- a/app/Entities/Tools/PermissionsUpdater.php +++ b/app/Entities/Tools/PermissionsUpdater.php @@ -4,20 +4,20 @@ namespace BookStack\Entities\Tools; use BookStack\Actions\ActivityType; use BookStack\Auth\Permissions\EntityPermission; +use BookStack\Auth\Role; use BookStack\Auth\User; use BookStack\Entities\Models\Book; use BookStack\Entities\Models\Bookshelf; use BookStack\Entities\Models\Entity; use BookStack\Facades\Activity; use Illuminate\Http\Request; -use Illuminate\Support\Collection; class PermissionsUpdater { /** * Update an entities permissions from a permission form submit request. */ - public function updateFromPermissionsForm(Entity $entity, Request $request) + public function updateFromPermissionsForm(Entity $entity, Request $request): void { $permissions = $request->get('permissions', null); $ownerId = $request->get('owned_by', null); @@ -39,12 +39,44 @@ class PermissionsUpdater Activity::add(ActivityType::PERMISSIONS_UPDATE, $entity); } + /** + * Update permissions from API request data. + */ + public function updateFromApiRequestData(Entity $entity, array $data): void + { + if (isset($data['override_role_permissions'])) { + $entity->permissions()->where('role_id', '!=', 0)->delete(); + $rolePermissionData = $this->formatPermissionsFromApiRequestToEntityPermissions($data['override_role_permissions'] ?? [], false); + $entity->permissions()->createMany($rolePermissionData); + } + + if (array_key_exists('override_fallback_permissions', $data)) { + $entity->permissions()->where('role_id', '=', 0)->delete(); + } + + if (isset($data['override_fallback_permissions'])) { + $data = $data['override_fallback_permissions']; + $data['role_id'] = 0; + $rolePermissionData = $this->formatPermissionsFromApiRequestToEntityPermissions([$data], true); + $entity->permissions()->createMany($rolePermissionData); + } + + if (isset($data['owner_id'])) { + $this->updateOwnerFromId($entity, intval($data['owner_id'])); + } + + $entity->save(); + $entity->rebuildPermissions(); + + Activity::add(ActivityType::PERMISSIONS_UPDATE, $entity); + } + /** * Update the owner of the given entity. * Checks the user exists in the system first. * Does not save the model, just updates it. */ - protected function updateOwnerFromId(Entity $entity, int $newOwnerId) + protected function updateOwnerFromId(Entity $entity, int $newOwnerId): void { $newOwner = User::query()->find($newOwnerId); if (!is_null($newOwner)) { @@ -67,7 +99,41 @@ class PermissionsUpdater $formatted[] = $entityPermissionData; } - return $formatted; + return $this->filterEntityPermissionDataUponRole($formatted, true); + } + + protected function formatPermissionsFromApiRequestToEntityPermissions(array $permissions, bool $allowFallback): array + { + $formatted = []; + + foreach ($permissions as $requestPermissionData) { + $entityPermissionData = ['role_id' => $requestPermissionData['role_id']]; + foreach (EntityPermission::PERMISSIONS as $permission) { + $entityPermissionData[$permission] = boolval($requestPermissionData[$permission] ?? false); + } + $formatted[] = $entityPermissionData; + } + + return $this->filterEntityPermissionDataUponRole($formatted, $allowFallback); + } + + protected function filterEntityPermissionDataUponRole(array $entityPermissionData, bool $allowFallback): array + { + $roleIds = []; + foreach ($entityPermissionData as $permissionEntry) { + $roleIds[] = intval($permissionEntry['role_id']); + } + + $actualRoleIds = array_unique(array_values(array_filter($roleIds))); + $rolesById = Role::query()->whereIn('id', $actualRoleIds)->get('id')->keyBy('id'); + + return array_values(array_filter($entityPermissionData, function ($data) use ($rolesById, $allowFallback) { + if (intval($data['role_id']) === 0) { + return $allowFallback; + } + + return $rolesById->has($data['role_id']); + })); } /** diff --git a/app/Http/Controllers/Api/ContentPermissionsController.php b/app/Http/Controllers/Api/ContentPermissionsController.php new file mode 100644 index 000000000..16fb68a0f --- /dev/null +++ b/app/Http/Controllers/Api/ContentPermissionsController.php @@ -0,0 +1,87 @@ + [ + 'owner_id' => ['int'], + + 'override_role_permissions' => ['array'], + 'override_role_permissions.*.role_id' => ['required', 'int'], + 'override_role_permissions.*.view' => ['required', 'boolean'], + 'override_role_permissions.*.create' => ['required', 'boolean'], + 'override_role_permissions.*.update' => ['required', 'boolean'], + 'override_role_permissions.*.delete' => ['required', 'boolean'], + + 'override_fallback_permissions' => ['nullable'], + 'override_fallback_permissions.view' => ['required', 'boolean'], + 'override_fallback_permissions.create' => ['required', 'boolean'], + 'override_fallback_permissions.update' => ['required', 'boolean'], + 'override_fallback_permissions.delete' => ['required', 'boolean'], + ] + ]; + + /** + * Read the configured content-level permissions for the item of the given type and ID. + * 'contentType' should be one of: page, book, chapter, bookshelf. + * 'contentId' should be the relevant ID of that item type you'd like to handle permissions for. + */ + public function read(string $contentType, string $contentId) + { + $entity = $this->entities->get($contentType) + ->newQuery()->scopes(['visible'])->findOrFail($contentId); + + $this->checkOwnablePermission('restrictions-manage', $entity); + + return response()->json($this->formattedPermissionDataForEntity($entity)); + } + + /** + * Update the configured content-level permissions for the item of the given type and ID. + * 'contentType' should be one of: page, book, chapter, bookshelf. + * 'contentId' should be the relevant ID of that item type you'd like to handle permissions for. + */ + public function update(Request $request, string $contentType, string $contentId) + { + $entity = $this->entities->get($contentType) + ->newQuery()->scopes(['visible'])->findOrFail($contentId); + + $this->checkOwnablePermission('restrictions-manage', $entity); + + $data = $this->validate($request, $this->rules()['update']); + $this->permissionsUpdater->updateFromApiRequestData($entity, $data); + + return response()->json($this->formattedPermissionDataForEntity($entity)); + } + + protected function formattedPermissionDataForEntity(Entity $entity): array + { + $rolePermissions = $entity->permissions() + ->where('role_id', '!=', 0) + ->with(['role:id,display_name']) + ->get(); + + $fallback = $entity->permissions()->where('role_id', '=', 0)->first(); + $fallback?->makeHidden('role_id'); + + return [ + 'owner' => $entity->ownedBy()->first(), + 'override_role_permissions' => $rolePermissions, + 'override_fallback_permissions' => $fallback, + 'inheriting' => is_null($fallback), + ]; + } +} diff --git a/routes/api.php b/routes/api.php index d1b64d455..1b852fed7 100644 --- a/routes/api.php +++ b/routes/api.php @@ -13,6 +13,7 @@ use BookStack\Http\Controllers\Api\BookExportApiController; use BookStack\Http\Controllers\Api\BookshelfApiController; use BookStack\Http\Controllers\Api\ChapterApiController; use BookStack\Http\Controllers\Api\ChapterExportApiController; +use BookStack\Http\Controllers\Api\ContentPermissionsController; use BookStack\Http\Controllers\Api\PageApiController; use BookStack\Http\Controllers\Api\PageExportApiController; use BookStack\Http\Controllers\Api\RecycleBinApiController; @@ -85,3 +86,6 @@ Route::delete('roles/{id}', [RoleApiController::class, 'delete']); Route::get('recycle-bin', [RecycleBinApiController::class, 'list']); Route::put('recycle-bin/{deletionId}', [RecycleBinApiController::class, 'restore']); Route::delete('recycle-bin/{deletionId}', [RecycleBinApiController::class, 'destroy']); + +Route::get('content-permissions/{contentType}/{contentId}', [ContentPermissionsController::class, 'read']); +Route::put('content-permissions/{contentType}/{contentId}', [ContentPermissionsController::class, 'update']); From 0de75300591873ece8af60152fdb51172e41f3a5 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Mon, 13 Mar 2023 20:06:52 +0000 Subject: [PATCH 2/3] Tweaked content permission endpoints, covered with tests --- app/Entities/Tools/PermissionsUpdater.php | 10 +- .../Api/ContentPermissionsController.php | 43 ++- tests/Api/ContentPermissionsApiTest.php | 262 ++++++++++++++++++ 3 files changed, 295 insertions(+), 20 deletions(-) create mode 100644 tests/Api/ContentPermissionsApiTest.php diff --git a/app/Entities/Tools/PermissionsUpdater.php b/app/Entities/Tools/PermissionsUpdater.php index 0d1d307af..36ed7ccde 100644 --- a/app/Entities/Tools/PermissionsUpdater.php +++ b/app/Entities/Tools/PermissionsUpdater.php @@ -44,18 +44,18 @@ class PermissionsUpdater */ public function updateFromApiRequestData(Entity $entity, array $data): void { - if (isset($data['override_role_permissions'])) { + if (isset($data['role_permissions'])) { $entity->permissions()->where('role_id', '!=', 0)->delete(); - $rolePermissionData = $this->formatPermissionsFromApiRequestToEntityPermissions($data['override_role_permissions'] ?? [], false); + $rolePermissionData = $this->formatPermissionsFromApiRequestToEntityPermissions($data['role_permissions'] ?? [], false); $entity->permissions()->createMany($rolePermissionData); } - if (array_key_exists('override_fallback_permissions', $data)) { + if (array_key_exists('fallback_permissions', $data)) { $entity->permissions()->where('role_id', '=', 0)->delete(); } - if (isset($data['override_fallback_permissions'])) { - $data = $data['override_fallback_permissions']; + if (isset($data['fallback_permissions']['inheriting']) && $data['fallback_permissions']['inheriting'] !== true) { + $data = $data['fallback_permissions']; $data['role_id'] = 0; $rolePermissionData = $this->formatPermissionsFromApiRequestToEntityPermissions([$data], true); $entity->permissions()->createMany($rolePermissionData); diff --git a/app/Http/Controllers/Api/ContentPermissionsController.php b/app/Http/Controllers/Api/ContentPermissionsController.php index 16fb68a0f..1db90f97a 100644 --- a/app/Http/Controllers/Api/ContentPermissionsController.php +++ b/app/Http/Controllers/Api/ContentPermissionsController.php @@ -19,18 +19,19 @@ class ContentPermissionsController extends ApiController 'update' => [ 'owner_id' => ['int'], - 'override_role_permissions' => ['array'], - 'override_role_permissions.*.role_id' => ['required', 'int'], - 'override_role_permissions.*.view' => ['required', 'boolean'], - 'override_role_permissions.*.create' => ['required', 'boolean'], - 'override_role_permissions.*.update' => ['required', 'boolean'], - 'override_role_permissions.*.delete' => ['required', 'boolean'], + 'role_permissions' => ['array'], + 'role_permissions.*.role_id' => ['required', 'int', 'exists:roles,id'], + 'role_permissions.*.view' => ['required', 'boolean'], + 'role_permissions.*.create' => ['required', 'boolean'], + 'role_permissions.*.update' => ['required', 'boolean'], + 'role_permissions.*.delete' => ['required', 'boolean'], - 'override_fallback_permissions' => ['nullable'], - 'override_fallback_permissions.view' => ['required', 'boolean'], - 'override_fallback_permissions.create' => ['required', 'boolean'], - 'override_fallback_permissions.update' => ['required', 'boolean'], - 'override_fallback_permissions.delete' => ['required', 'boolean'], + 'fallback_permissions' => ['nullable'], + 'fallback_permissions.inheriting' => ['required_with:fallback_permissions', 'boolean'], + 'fallback_permissions.view' => ['required_if:fallback_permissions.inheriting,false', 'boolean'], + 'fallback_permissions.create' => ['required_if:fallback_permissions.inheriting,false', 'boolean'], + 'fallback_permissions.update' => ['required_if:fallback_permissions.inheriting,false', 'boolean'], + 'fallback_permissions.delete' => ['required_if:fallback_permissions.inheriting,false', 'boolean'], ] ]; @@ -38,6 +39,9 @@ class ContentPermissionsController extends ApiController * Read the configured content-level permissions for the item of the given type and ID. * 'contentType' should be one of: page, book, chapter, bookshelf. * 'contentId' should be the relevant ID of that item type you'd like to handle permissions for. + * The permissions shown are those that override the default for just the specified item, they do not show the + * full evaluated permission for a role, nor do they reflect permissions inherited from other items in the hierarchy. + * Fallback permission values may be `null` when inheriting is active. */ public function read(string $contentType, string $contentId) { @@ -53,6 +57,10 @@ class ContentPermissionsController extends ApiController * Update the configured content-level permissions for the item of the given type and ID. * 'contentType' should be one of: page, book, chapter, bookshelf. * 'contentId' should be the relevant ID of that item type you'd like to handle permissions for. + * Providing an empty `role_permissions` array will remove any existing configured role permissions, + * so you may want to fetch existing permissions beforehand if just adding/removing a single item. + * You should completely omit the `owner_id`, `role_permissions` and/or the `fallback_permissions` properties + * if you don't wish to update details within those categories. */ public function update(Request $request, string $contentType, string $contentId) { @@ -75,13 +83,18 @@ class ContentPermissionsController extends ApiController ->get(); $fallback = $entity->permissions()->where('role_id', '=', 0)->first(); - $fallback?->makeHidden('role_id'); + $fallbackData = [ + 'inheriting' => is_null($fallback), + 'view' => $fallback->view ?? null, + 'create' => $fallback->create ?? null, + 'update' => $fallback->update ?? null, + 'delete' => $fallback->delete ?? null, + ]; return [ 'owner' => $entity->ownedBy()->first(), - 'override_role_permissions' => $rolePermissions, - 'override_fallback_permissions' => $fallback, - 'inheriting' => is_null($fallback), + 'role_permissions' => $rolePermissions, + 'fallback_permissions' => $fallbackData, ]; } } diff --git a/tests/Api/ContentPermissionsApiTest.php b/tests/Api/ContentPermissionsApiTest.php new file mode 100644 index 000000000..50b82e5c4 --- /dev/null +++ b/tests/Api/ContentPermissionsApiTest.php @@ -0,0 +1,262 @@ +entities->page(); + $endpointMap = [ + ['get', "/api/content-permissions/page/{$page->id}"], + ['put', "/api/content-permissions/page/{$page->id}"], + ]; + $editor = $this->users->editor(); + + $this->actingAs($editor, 'api'); + foreach ($endpointMap as [$method, $uri]) { + $resp = $this->json($method, $uri); + $resp->assertStatus(403); + $resp->assertJson($this->permissionErrorResponse()); + } + + $this->permissions->grantUserRolePermissions($editor, ['restrictions-manage-all']); + + foreach ($endpointMap as [$method, $uri]) { + $resp = $this->json($method, $uri); + $this->assertNotEquals(403, $resp->getStatusCode()); + } + } + + public function test_read_endpoint_shows_expected_detail() + { + $page = $this->entities->page(); + $owner = $this->users->newUser(); + $role = $this->users->createRole(); + $this->permissions->addEntityPermission($page, ['view', 'delete'], $role); + $this->permissions->changeEntityOwner($page, $owner); + $this->permissions->setFallbackPermissions($page, ['update', 'create']); + + $this->actingAsApiAdmin(); + $resp = $this->getJson($this->baseEndpoint . "/page/{$page->id}"); + + $resp->assertOk(); + $resp->assertExactJson([ + 'owner' => [ + 'id' => $owner->id, 'name' => $owner->name, 'slug' => $owner->slug, + ], + 'role_permissions' => [ + [ + 'role_id' => $role->id, + 'view' => true, + 'create' => false, + 'update' => false, + 'delete' => true, + 'role' => [ + 'id' => $role->id, + 'display_name' => $role->display_name, + ] + ] + ], + 'fallback_permissions' => [ + 'inheriting' => false, + 'view' => false, + 'create' => true, + 'update' => true, + 'delete' => false, + ], + ]); + } + + public function test_read_endpoint_shows_expected_detail_when_items_are_empty() + { + $page = $this->entities->page(); + $page->permissions()->delete(); + $page->owned_by = null; + $page->save(); + + $this->actingAsApiAdmin(); + $resp = $this->getJson($this->baseEndpoint . "/page/{$page->id}"); + + $resp->assertOk(); + $resp->assertExactJson([ + 'owner' => null, + 'role_permissions' => [], + 'fallback_permissions' => [ + 'inheriting' => true, + 'view' => null, + 'create' => null, + 'update' => null, + 'delete' => null, + ], + ]); + } + + public function test_update_endpoint_can_change_owner() + { + $page = $this->entities->page(); + $newOwner = $this->users->newUser(); + + $this->actingAsApiAdmin(); + $resp = $this->putJson($this->baseEndpoint . "/page/{$page->id}", [ + 'owner_id' => $newOwner->id, + ]); + + $resp->assertOk(); + $resp->assertExactJson([ + 'owner' => ['id' => $newOwner->id, 'name' => $newOwner->name, 'slug' => $newOwner->slug], + 'role_permissions' => [], + 'fallback_permissions' => [ + 'inheriting' => true, + 'view' => null, + 'create' => null, + 'update' => null, + 'delete' => null, + ], + ]); + } + + public function test_update_can_set_role_permissions() + { + $page = $this->entities->page(); + $page->owned_by = null; + $page->save(); + $newRoleA = $this->users->createRole(); + $newRoleB = $this->users->createRole(); + + $this->actingAsApiAdmin(); + $resp = $this->putJson($this->baseEndpoint . "/page/{$page->id}", [ + 'role_permissions' => [ + ['role_id' => $newRoleA->id, 'view' => true, 'create' => false, 'update' => false, 'delete' => false], + ['role_id' => $newRoleB->id, 'view' => true, 'create' => false, 'update' => true, 'delete' => true], + ], + ]); + + $resp->assertOk(); + $resp->assertExactJson([ + 'owner' => null, + 'role_permissions' => [ + [ + 'role_id' => $newRoleA->id, + 'view' => true, + 'create' => false, + 'update' => false, + 'delete' => false, + 'role' => [ + 'id' => $newRoleA->id, + 'display_name' => $newRoleA->display_name, + ] + ], + [ + 'role_id' => $newRoleB->id, + 'view' => true, + 'create' => false, + 'update' => true, + 'delete' => true, + 'role' => [ + 'id' => $newRoleB->id, + 'display_name' => $newRoleB->display_name, + ] + ] + ], + 'fallback_permissions' => [ + 'inheriting' => true, + 'view' => null, + 'create' => null, + 'update' => null, + 'delete' => null, + ], + ]); + } + + public function test_update_can_set_fallback_permissions() + { + $page = $this->entities->page(); + $page->owned_by = null; + $page->save(); + + $this->actingAsApiAdmin(); + $resp = $this->putJson($this->baseEndpoint . "/page/{$page->id}", [ + 'fallback_permissions' => [ + 'inheriting' => false, + 'view' => true, + 'create' => true, + 'update' => true, + 'delete' => false, + ], + ]); + + $resp->assertOk(); + $resp->assertExactJson([ + 'owner' => null, + 'role_permissions' => [], + 'fallback_permissions' => [ + 'inheriting' => false, + 'view' => true, + 'create' => true, + 'update' => true, + 'delete' => false, + ], + ]); + } + + public function test_update_can_clear_roles_permissions() + { + $page = $this->entities->page(); + $this->permissions->addEntityPermission($page, ['view'], $this->users->createRole()); + $page->owned_by = null; + $page->save(); + + $this->actingAsApiAdmin(); + $resp = $this->putJson($this->baseEndpoint . "/page/{$page->id}", [ + 'role_permissions' => [], + ]); + + $resp->assertOk(); + $resp->assertExactJson([ + 'owner' => null, + 'role_permissions' => [], + 'fallback_permissions' => [ + 'inheriting' => true, + 'view' => null, + 'create' => null, + 'update' => null, + 'delete' => null, + ], + ]); + } + + public function test_update_can_clear_fallback_permissions() + { + $page = $this->entities->page(); + $this->permissions->setFallbackPermissions($page, ['view', 'update']); + $page->owned_by = null; + $page->save(); + + $this->actingAsApiAdmin(); + $resp = $this->putJson($this->baseEndpoint . "/page/{$page->id}", [ + 'fallback_permissions' => [ + 'inheriting' => true, + ], + ]); + + $resp->assertOk(); + $resp->assertExactJson([ + 'owner' => null, + 'role_permissions' => [], + 'fallback_permissions' => [ + 'inheriting' => true, + 'view' => null, + 'create' => null, + 'update' => null, + 'delete' => null, + ], + ]); + } +} From 190392482992801e4bed18828ad328b07f7e572a Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Mon, 13 Mar 2023 20:41:32 +0000 Subject: [PATCH 3/3] Added content-perms API examples and docs tweaks --- .../Api/ContentPermissionsController.php | 4 +- .../requests/content-permissions-update.json | 26 +++++++++++++ .../responses/content-permissions-read.json | 38 +++++++++++++++++++ .../responses/content-permissions-update.json | 38 +++++++++++++++++++ 4 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 dev/api/requests/content-permissions-update.json create mode 100644 dev/api/responses/content-permissions-read.json create mode 100644 dev/api/responses/content-permissions-update.json diff --git a/app/Http/Controllers/Api/ContentPermissionsController.php b/app/Http/Controllers/Api/ContentPermissionsController.php index 1db90f97a..ef17af8ad 100644 --- a/app/Http/Controllers/Api/ContentPermissionsController.php +++ b/app/Http/Controllers/Api/ContentPermissionsController.php @@ -54,13 +54,13 @@ class ContentPermissionsController extends ApiController } /** - * Update the configured content-level permissions for the item of the given type and ID. + * Update the configured content-level permission overrides for the item of the given type and ID. * 'contentType' should be one of: page, book, chapter, bookshelf. * 'contentId' should be the relevant ID of that item type you'd like to handle permissions for. * Providing an empty `role_permissions` array will remove any existing configured role permissions, * so you may want to fetch existing permissions beforehand if just adding/removing a single item. * You should completely omit the `owner_id`, `role_permissions` and/or the `fallback_permissions` properties - * if you don't wish to update details within those categories. + * from your request data if you don't wish to update details within those categories. */ public function update(Request $request, string $contentType, string $contentId) { diff --git a/dev/api/requests/content-permissions-update.json b/dev/api/requests/content-permissions-update.json new file mode 100644 index 000000000..124bb8bff --- /dev/null +++ b/dev/api/requests/content-permissions-update.json @@ -0,0 +1,26 @@ +{ + "owner_id": 1, + "role_permissions": [ + { + "role_id": 2, + "view": true, + "create": true, + "update": true, + "delete": false + }, + { + "role_id": 3, + "view": false, + "create": false, + "update": false, + "delete": false + } + ], + "fallback_permissions": { + "inheriting": false, + "view": true, + "create": true, + "update": false, + "delete": false + } +} \ No newline at end of file diff --git a/dev/api/responses/content-permissions-read.json b/dev/api/responses/content-permissions-read.json new file mode 100644 index 000000000..591fc5c86 --- /dev/null +++ b/dev/api/responses/content-permissions-read.json @@ -0,0 +1,38 @@ +{ + "owner": { + "id": 1, + "name": "Admin", + "slug": "admin" + }, + "role_permissions": [ + { + "role_id": 2, + "view": true, + "create": false, + "update": true, + "delete": false, + "role": { + "id": 2, + "display_name": "Editor" + } + }, + { + "role_id": 10, + "view": true, + "create": true, + "update": false, + "delete": false, + "role": { + "id": 10, + "display_name": "Wizards of the west" + } + } + ], + "fallback_permissions": { + "inheriting": false, + "view": true, + "create": false, + "update": false, + "delete": false + } +} \ No newline at end of file diff --git a/dev/api/responses/content-permissions-update.json b/dev/api/responses/content-permissions-update.json new file mode 100644 index 000000000..67fa40bf0 --- /dev/null +++ b/dev/api/responses/content-permissions-update.json @@ -0,0 +1,38 @@ +{ + "owner": { + "id": 1, + "name": "Admin", + "slug": "admin" + }, + "role_permissions": [ + { + "role_id": 2, + "view": true, + "create": true, + "update": true, + "delete": false, + "role": { + "id": 2, + "display_name": "Editor" + } + }, + { + "role_id": 3, + "view": false, + "create": false, + "update": false, + "delete": false, + "role": { + "id": 3, + "display_name": "Viewer" + } + } + ], + "fallback_permissions": { + "inheriting": false, + "view": true, + "create": true, + "update": false, + "delete": false + } +} \ No newline at end of file