3 namespace BookStack\Http\Controllers\Api;
5 use BookStack\Entities\EntityProvider;
6 use BookStack\Entities\Models\Entity;
7 use BookStack\Entities\Tools\PermissionsUpdater;
8 use Illuminate\Http\Request;
10 class ContentPermissionApiController extends ApiController
12 public function __construct(
13 protected PermissionsUpdater $permissionsUpdater,
14 protected EntityProvider $entities
20 'owner_id' => ['int'],
22 'role_permissions' => ['array'],
23 'role_permissions.*.role_id' => ['required', 'int', 'exists:roles,id'],
24 'role_permissions.*.view' => ['required', 'boolean'],
25 'role_permissions.*.create' => ['required', 'boolean'],
26 'role_permissions.*.update' => ['required', 'boolean'],
27 'role_permissions.*.delete' => ['required', 'boolean'],
29 'fallback_permissions' => ['nullable'],
30 'fallback_permissions.inheriting' => ['required_with:fallback_permissions', 'boolean'],
31 'fallback_permissions.view' => ['required_if:fallback_permissions.inheriting,false', 'boolean'],
32 'fallback_permissions.create' => ['required_if:fallback_permissions.inheriting,false', 'boolean'],
33 'fallback_permissions.update' => ['required_if:fallback_permissions.inheriting,false', 'boolean'],
34 'fallback_permissions.delete' => ['required_if:fallback_permissions.inheriting,false', 'boolean'],
39 * Read the configured content-level permissions for the item of the given type and ID.
40 * 'contentType' should be one of: page, book, chapter, bookshelf.
41 * 'contentId' should be the relevant ID of that item type you'd like to handle permissions for.
42 * The permissions shown are those that override the default for just the specified item, they do not show the
43 * full evaluated permission for a role, nor do they reflect permissions inherited from other items in the hierarchy.
44 * Fallback permission values may be `null` when inheriting is active.
46 public function read(string $contentType, string $contentId)
48 $entity = $this->entities->get($contentType)
49 ->newQuery()->scopes(['visible'])->findOrFail($contentId);
51 $this->checkOwnablePermission('restrictions-manage', $entity);
53 return response()->json($this->formattedPermissionDataForEntity($entity));
57 * Update the configured content-level permission overrides for the item of the given type and ID.
58 * 'contentType' should be one of: page, book, chapter, bookshelf.
59 * 'contentId' should be the relevant ID of that item type you'd like to handle permissions for.
60 * Providing an empty `role_permissions` array will remove any existing configured role permissions,
61 * so you may want to fetch existing permissions beforehand if just adding/removing a single item.
62 * You should completely omit the `owner_id`, `role_permissions` and/or the `fallback_permissions` properties
63 * from your request data if you don't wish to update details within those categories.
65 public function update(Request $request, string $contentType, string $contentId)
67 $entity = $this->entities->get($contentType)
68 ->newQuery()->scopes(['visible'])->findOrFail($contentId);
70 $this->checkOwnablePermission('restrictions-manage', $entity);
72 $data = $this->validate($request, $this->rules()['update']);
73 $this->permissionsUpdater->updateFromApiRequestData($entity, $data);
75 return response()->json($this->formattedPermissionDataForEntity($entity));
78 protected function formattedPermissionDataForEntity(Entity $entity): array
80 $rolePermissions = $entity->permissions()
81 ->where('role_id', '!=', 0)
82 ->with(['role:id,display_name'])
85 $fallback = $entity->permissions()->where('role_id', '=', 0)->first();
87 'inheriting' => is_null($fallback),
88 'view' => $fallback->view ?? null,
89 'create' => $fallback->create ?? null,
90 'update' => $fallback->update ?? null,
91 'delete' => $fallback->delete ?? null,
95 'owner' => $entity->ownedBy()->first(),
96 'role_permissions' => $rolePermissions,
97 'fallback_permissions' => $fallbackData,