3 namespace BookStack\Entities\Tools;
5 use BookStack\Activity\ActivityType;
6 use BookStack\Entities\Models\Book;
7 use BookStack\Entities\Models\Bookshelf;
8 use BookStack\Entities\Models\Entity;
9 use BookStack\Facades\Activity;
10 use BookStack\Permissions\Models\EntityPermission;
11 use BookStack\Permissions\Permission;
12 use BookStack\Users\Models\Role;
13 use BookStack\Users\Models\User;
14 use Illuminate\Http\Request;
16 class PermissionsUpdater
19 * Update an entities permissions from a permission form submit request.
21 public function updateFromPermissionsForm(Entity $entity, Request $request): void
23 $permissions = $request->get('permissions', null);
24 $ownerId = $request->get('owned_by', null);
26 $entity->permissions()->delete();
28 if (!is_null($permissions)) {
29 $entityPermissionData = $this->formatPermissionsFromRequestToEntityPermissions($permissions);
30 $entity->permissions()->createMany($entityPermissionData);
33 if (!is_null($ownerId)) {
34 $this->updateOwnerFromId($entity, intval($ownerId));
38 $entity->rebuildPermissions();
40 Activity::add(ActivityType::PERMISSIONS_UPDATE, $entity);
44 * Update permissions from API request data.
46 public function updateFromApiRequestData(Entity $entity, array $data): void
48 if (isset($data['role_permissions'])) {
49 $entity->permissions()->where('role_id', '!=', 0)->delete();
50 $rolePermissionData = $this->formatPermissionsFromApiRequestToEntityPermissions($data['role_permissions'] ?? [], false);
51 $entity->permissions()->createMany($rolePermissionData);
54 if (array_key_exists('fallback_permissions', $data)) {
55 $entity->permissions()->where('role_id', '=', 0)->delete();
58 if (isset($data['fallback_permissions']['inheriting']) && $data['fallback_permissions']['inheriting'] !== true) {
59 $fallbackData = $data['fallback_permissions'];
60 $fallbackData['role_id'] = 0;
61 $rolePermissionData = $this->formatPermissionsFromApiRequestToEntityPermissions([$fallbackData], true);
62 $entity->permissions()->createMany($rolePermissionData);
65 if (isset($data['owner_id'])) {
66 $this->updateOwnerFromId($entity, intval($data['owner_id']));
70 $entity->rebuildPermissions();
72 Activity::add(ActivityType::PERMISSIONS_UPDATE, $entity);
76 * Update the owner of the given entity.
77 * Checks the user exists in the system first.
78 * Does not save the model, just updates it.
80 protected function updateOwnerFromId(Entity $entity, int $newOwnerId): void
82 $newOwner = User::query()->find($newOwnerId);
83 if (!is_null($newOwner)) {
84 $entity->owned_by = $newOwner->id;
89 * Format permissions provided from a permission form to be EntityPermission data.
91 protected function formatPermissionsFromRequestToEntityPermissions(array $permissions): array
95 foreach ($permissions as $roleId => $info) {
96 $entityPermissionData = ['role_id' => $roleId];
97 foreach (Permission::genericForEntity() as $permission) {
98 $permName = $permission->value;
99 $entityPermissionData[$permName] = (($info[$permName] ?? false) === "true");
101 $formatted[] = $entityPermissionData;
104 return $this->filterEntityPermissionDataUponRole($formatted, true);
107 protected function formatPermissionsFromApiRequestToEntityPermissions(array $permissions, bool $allowFallback): array
111 foreach ($permissions as $requestPermissionData) {
112 $entityPermissionData = ['role_id' => $requestPermissionData['role_id']];
113 foreach (Permission::genericForEntity() as $permission) {
114 $permName = $permission->value;
115 $entityPermissionData[$permName] = boolval($requestPermissionData[$permName] ?? false);
117 $formatted[] = $entityPermissionData;
120 return $this->filterEntityPermissionDataUponRole($formatted, $allowFallback);
123 protected function filterEntityPermissionDataUponRole(array $entityPermissionData, bool $allowFallback): array
126 foreach ($entityPermissionData as $permissionEntry) {
127 $roleIds[] = intval($permissionEntry['role_id']);
130 $actualRoleIds = array_unique(array_values(array_filter($roleIds)));
131 $rolesById = Role::query()->whereIn('id', $actualRoleIds)->get('id')->keyBy('id');
133 return array_values(array_filter($entityPermissionData, function ($data) use ($rolesById, $allowFallback) {
134 if (intval($data['role_id']) === 0) {
135 return $allowFallback;
138 return $rolesById->has($data['role_id']);
143 * Copy down the permissions of the given shelf to all child books.
145 public function updateBookPermissionsFromShelf(Bookshelf $shelf, $checkUserPermissions = true): int
147 $shelfPermissions = $shelf->permissions()->get(['role_id', 'view', 'create', 'update', 'delete'])->toArray();
148 $shelfBooks = $shelf->books()->get(['id', 'owned_by']);
149 $updatedBookCount = 0;
151 /** @var Book $book */
152 foreach ($shelfBooks as $book) {
153 if ($checkUserPermissions && !userCan(Permission::RestrictionsManage, $book)) {
156 $book->permissions()->delete();
157 $book->permissions()->createMany($shelfPermissions);
158 $book->rebuildPermissions();
162 return $updatedBookCount;