diff --git a/app/Access/LoginService.php b/app/Access/LoginService.php index 76ae1938d..c81e95572 100644 --- a/app/Access/LoginService.php +++ b/app/Access/LoginService.php @@ -9,6 +9,7 @@ use BookStack\Exceptions\LoginAttemptInvalidUserException; use BookStack\Exceptions\StoppedAuthenticationException; use BookStack\Facades\Activity; use BookStack\Facades\Theme; +use BookStack\Permissions\Permission; use BookStack\Theming\ThemeEvents; use BookStack\Users\Models\User; use Exception; @@ -50,7 +51,7 @@ class LoginService Theme::dispatch(ThemeEvents::AUTH_LOGIN, $method, $user); // Authenticate on all session guards if a likely admin - if ($user->can('users-manage') && $user->can('user-roles-manage')) { + if ($user->can(Permission::UsersManage) && $user->can(Permission::UserRolesManage)) { $guards = ['standard', 'ldap', 'saml2', 'oidc']; foreach ($guards as $guard) { auth($guard)->login($user); diff --git a/app/Activity/Controllers/AuditLogApiController.php b/app/Activity/Controllers/AuditLogApiController.php index 650d17446..0cb4d9cb6 100644 --- a/app/Activity/Controllers/AuditLogApiController.php +++ b/app/Activity/Controllers/AuditLogApiController.php @@ -4,6 +4,7 @@ namespace BookStack\Activity\Controllers; use BookStack\Activity\Models\Activity; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; class AuditLogApiController extends ApiController { @@ -16,8 +17,8 @@ class AuditLogApiController extends ApiController */ public function list() { - $this->checkPermission('settings-manage'); - $this->checkPermission('users-manage'); + $this->checkPermission(Permission::SettingsManage); + $this->checkPermission(Permission::UsersManage); $query = Activity::query()->with(['user']); diff --git a/app/Activity/Controllers/AuditLogController.php b/app/Activity/Controllers/AuditLogController.php index 66ca30197..c4f9b91ed 100644 --- a/app/Activity/Controllers/AuditLogController.php +++ b/app/Activity/Controllers/AuditLogController.php @@ -5,6 +5,7 @@ namespace BookStack\Activity\Controllers; use BookStack\Activity\ActivityType; use BookStack\Activity\Models\Activity; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Sorting\SortUrl; use BookStack\Util\SimpleListOptions; use Illuminate\Http\Request; @@ -13,8 +14,8 @@ class AuditLogController extends Controller { public function index(Request $request) { - $this->checkPermission('settings-manage'); - $this->checkPermission('users-manage'); + $this->checkPermission(Permission::SettingsManage); + $this->checkPermission(Permission::UsersManage); $sort = $request->get('sort', 'activity_date'); $order = $request->get('order', 'desc'); diff --git a/app/Activity/Controllers/CommentController.php b/app/Activity/Controllers/CommentController.php index 479d57c4d..fd5463dff 100644 --- a/app/Activity/Controllers/CommentController.php +++ b/app/Activity/Controllers/CommentController.php @@ -7,6 +7,7 @@ use BookStack\Activity\Tools\CommentTree; use BookStack\Activity\Tools\CommentTreeNode; use BookStack\Entities\Queries\PageQueries; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use Illuminate\Http\Request; use Illuminate\Validation\ValidationException; @@ -42,7 +43,7 @@ class CommentController extends Controller } // Create a new comment. - $this->checkPermission('comment-create-all'); + $this->checkPermission(Permission::CommentCreateAll); $contentRef = $input['content_ref'] ?? ''; $comment = $this->commentRepo->create($page, $input['html'], $input['parent_id'] ?? null, $contentRef); @@ -64,8 +65,8 @@ class CommentController extends Controller ]); $comment = $this->commentRepo->getById($commentId); - $this->checkOwnablePermission('page-view', $comment->entity); - $this->checkOwnablePermission('comment-update', $comment); + $this->checkOwnablePermission(Permission::PageView, $comment->entity); + $this->checkOwnablePermission(Permission::CommentUpdate, $comment); $comment = $this->commentRepo->update($comment, $input['html']); @@ -81,8 +82,8 @@ class CommentController extends Controller public function archive(int $id) { $comment = $this->commentRepo->getById($id); - $this->checkOwnablePermission('page-view', $comment->entity); - if (!userCan('comment-update', $comment) && !userCan('comment-delete', $comment)) { + $this->checkOwnablePermission(Permission::PageView, $comment->entity); + if (!userCan(Permission::CommentUpdate, $comment) && !userCan(Permission::CommentDelete, $comment)) { $this->showPermissionError(); } @@ -101,8 +102,8 @@ class CommentController extends Controller public function unarchive(int $id) { $comment = $this->commentRepo->getById($id); - $this->checkOwnablePermission('page-view', $comment->entity); - if (!userCan('comment-update', $comment) && !userCan('comment-delete', $comment)) { + $this->checkOwnablePermission(Permission::PageView, $comment->entity); + if (!userCan(Permission::CommentUpdate, $comment) && !userCan(Permission::CommentDelete, $comment)) { $this->showPermissionError(); } @@ -121,7 +122,7 @@ class CommentController extends Controller public function destroy(int $id) { $comment = $this->commentRepo->getById($id); - $this->checkOwnablePermission('comment-delete', $comment); + $this->checkOwnablePermission(Permission::CommentDelete, $comment); $this->commentRepo->delete($comment); diff --git a/app/Activity/Controllers/WatchController.php b/app/Activity/Controllers/WatchController.php index 5df75da39..b77a893ea 100644 --- a/app/Activity/Controllers/WatchController.php +++ b/app/Activity/Controllers/WatchController.php @@ -5,13 +5,14 @@ namespace BookStack\Activity\Controllers; use BookStack\Activity\Tools\UserEntityWatchOptions; use BookStack\Entities\Tools\MixedEntityRequestHelper; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use Illuminate\Http\Request; class WatchController extends Controller { public function update(Request $request, MixedEntityRequestHelper $entityHelper) { - $this->checkPermission('receive-notifications'); + $this->checkPermission(Permission::ReceiveNotifications); $this->preventGuestAccess(); $requestData = $this->validate($request, array_merge([ diff --git a/app/Activity/Controllers/WebhookController.php b/app/Activity/Controllers/WebhookController.php index dcca1dc41..6a65b8363 100644 --- a/app/Activity/Controllers/WebhookController.php +++ b/app/Activity/Controllers/WebhookController.php @@ -6,6 +6,7 @@ use BookStack\Activity\ActivityType; use BookStack\Activity\Models\Webhook; use BookStack\Activity\Queries\WebhooksAllPaginatedAndSorted; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Util\SimpleListOptions; use Illuminate\Http\Request; @@ -14,7 +15,7 @@ class WebhookController extends Controller public function __construct() { $this->middleware([ - 'can:settings-manage', + Permission::SettingsManage->middleware() ]); } diff --git a/app/Activity/Notifications/Handlers/BaseNotificationHandler.php b/app/Activity/Notifications/Handlers/BaseNotificationHandler.php index 3a9b0c1dc..bc6f2e22f 100644 --- a/app/Activity/Notifications/Handlers/BaseNotificationHandler.php +++ b/app/Activity/Notifications/Handlers/BaseNotificationHandler.php @@ -5,6 +5,7 @@ namespace BookStack\Activity\Notifications\Handlers; use BookStack\Activity\Models\Loggable; use BookStack\Activity\Notifications\Messages\BaseActivityNotification; use BookStack\Entities\Models\Entity; +use BookStack\Permissions\Permission; use BookStack\Permissions\PermissionApplicator; use BookStack\Users\Models\User; use Illuminate\Support\Facades\Log; @@ -26,7 +27,7 @@ abstract class BaseNotificationHandler implements NotificationHandler } // Prevent sending of the user does not have notification permissions - if (!$user->can('receive-notifications')) { + if (!$user->can(Permission::ReceiveNotifications)) { continue; } diff --git a/app/Activity/Tools/CommentTree.php b/app/Activity/Tools/CommentTree.php index a05a9d247..66df29430 100644 --- a/app/Activity/Tools/CommentTree.php +++ b/app/Activity/Tools/CommentTree.php @@ -4,6 +4,7 @@ namespace BookStack\Activity\Tools; use BookStack\Activity\Models\Comment; use BookStack\Entities\Models\Page; +use BookStack\Permissions\Permission; class CommentTree { @@ -70,7 +71,7 @@ class CommentTree public function canUpdateAny(): bool { foreach ($this->comments as $comment) { - if (userCan('comment-update', $comment)) { + if (userCan(Permission::CommentUpdate, $comment)) { return true; } } diff --git a/app/Activity/Tools/TagClassGenerator.php b/app/Activity/Tools/TagClassGenerator.php index 5bcb44113..0f7aa1fe0 100644 --- a/app/Activity/Tools/TagClassGenerator.php +++ b/app/Activity/Tools/TagClassGenerator.php @@ -6,6 +6,7 @@ use BookStack\Activity\Models\Tag; use BookStack\Entities\Models\BookChild; use BookStack\Entities\Models\Entity; use BookStack\Entities\Models\Page; +use BookStack\Permissions\Permission; class TagClassGenerator { @@ -26,14 +27,14 @@ class TagClassGenerator array_push($classes, ...$this->generateClassesForTag($tag)); } - if ($this->entity instanceof BookChild && userCan('view', $this->entity->book)) { + if ($this->entity instanceof BookChild && userCan(Permission::BookView, $this->entity->book)) { $bookTags = $this->entity->book->tags; foreach ($bookTags as $bookTag) { array_push($classes, ...$this->generateClassesForTag($bookTag, 'book-')); } } - if ($this->entity instanceof Page && $this->entity->chapter && userCan('view', $this->entity->chapter)) { + if ($this->entity instanceof Page && $this->entity->chapter && userCan(Permission::ChapterView, $this->entity->chapter)) { $chapterTags = $this->entity->chapter->tags; foreach ($chapterTags as $chapterTag) { array_push($classes, ...$this->generateClassesForTag($chapterTag, 'chapter-')); diff --git a/app/Activity/Tools/UserEntityWatchOptions.php b/app/Activity/Tools/UserEntityWatchOptions.php index 559d7903d..8e5f70758 100644 --- a/app/Activity/Tools/UserEntityWatchOptions.php +++ b/app/Activity/Tools/UserEntityWatchOptions.php @@ -7,6 +7,7 @@ use BookStack\Activity\WatchLevels; use BookStack\Entities\Models\BookChild; use BookStack\Entities\Models\Entity; use BookStack\Entities\Models\Page; +use BookStack\Permissions\Permission; use BookStack\Users\Models\User; use Illuminate\Database\Eloquent\Builder; @@ -22,7 +23,7 @@ class UserEntityWatchOptions public function canWatch(): bool { - return $this->user->can('receive-notifications') && !$this->user->isGuest(); + return $this->user->can(Permission::ReceiveNotifications) && !$this->user->isGuest(); } public function getWatchLevel(): string diff --git a/app/Api/ApiTokenGuard.php b/app/Api/ApiTokenGuard.php index 6302884a9..9f4537b29 100644 --- a/app/Api/ApiTokenGuard.php +++ b/app/Api/ApiTokenGuard.php @@ -4,6 +4,7 @@ namespace BookStack\Api; use BookStack\Access\LoginService; use BookStack\Exceptions\ApiAuthException; +use BookStack\Permissions\Permission; use Illuminate\Auth\GuardHelpers; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\Guard; @@ -146,7 +147,7 @@ class ApiTokenGuard implements Guard throw new ApiAuthException(trans('errors.api_user_token_expired'), 403); } - if (!$token->user->can('access-api')) { + if (!$token->user->can(Permission::AccessApi)) { throw new ApiAuthException(trans('errors.api_user_no_api_permission'), 403); } } diff --git a/app/Api/UserApiTokenController.php b/app/Api/UserApiTokenController.php index 3606e8260..2ca9e2235 100644 --- a/app/Api/UserApiTokenController.php +++ b/app/Api/UserApiTokenController.php @@ -4,6 +4,7 @@ namespace BookStack\Api; use BookStack\Activity\ActivityType; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Users\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; @@ -16,8 +17,8 @@ class UserApiTokenController extends Controller */ public function create(Request $request, int $userId) { - $this->checkPermission('access-api'); - $this->checkPermissionOrCurrentUser('users-manage', $userId); + $this->checkPermission(Permission::AccessApi); + $this->checkPermissionOrCurrentUser(Permission::UsersManage, $userId); $this->updateContext($request); $user = User::query()->findOrFail($userId); @@ -35,8 +36,8 @@ class UserApiTokenController extends Controller */ public function store(Request $request, int $userId) { - $this->checkPermission('access-api'); - $this->checkPermissionOrCurrentUser('users-manage', $userId); + $this->checkPermission(Permission::AccessApi); + $this->checkPermissionOrCurrentUser(Permission::UsersManage, $userId); $this->validate($request, [ 'name' => ['required', 'max:250'], @@ -143,8 +144,8 @@ class UserApiTokenController extends Controller */ protected function checkPermissionAndFetchUserToken(int $userId, int $tokenId): array { - $this->checkPermissionOr('users-manage', function () use ($userId) { - return $userId === user()->id && userCan('access-api'); + $this->checkPermissionOr(Permission::UsersManage, function () use ($userId) { + return $userId === user()->id && userCan(Permission::AccessApi); }); $user = User::query()->findOrFail($userId); diff --git a/app/App/helpers.php b/app/App/helpers.php index 2305c2d72..0e357e36a 100644 --- a/app/App/helpers.php +++ b/app/App/helpers.php @@ -3,6 +3,7 @@ use BookStack\App\AppVersion; use BookStack\App\Model; use BookStack\Facades\Theme; +use BookStack\Permissions\Permission; use BookStack\Permissions\PermissionApplicator; use BookStack\Settings\SettingService; use BookStack\Users\Models\User; @@ -39,7 +40,7 @@ function user(): User * Check if the current user has a permission. If an ownable element * is passed in the jointPermissions are checked against that particular item. */ -function userCan(string $permission, ?Model $ownable = null): bool +function userCan(string|Permission $permission, ?Model $ownable = null): bool { if (is_null($ownable)) { return user()->can($permission); @@ -55,7 +56,7 @@ function userCan(string $permission, ?Model $ownable = null): bool * Check if the current user can perform the given action on any items in the system. * Can be provided the class name of an entity to filter ability to that specific entity type. */ -function userCanOnAny(string $action, string $entityClass = ''): bool +function userCanOnAny(string|Permission $action, string $entityClass = ''): bool { $permissions = app()->make(PermissionApplicator::class); diff --git a/app/Entities/Controllers/BookApiController.php b/app/Entities/Controllers/BookApiController.php index a617ee2da..5baea163f 100644 --- a/app/Entities/Controllers/BookApiController.php +++ b/app/Entities/Controllers/BookApiController.php @@ -11,6 +11,7 @@ use BookStack\Entities\Queries\PageQueries; use BookStack\Entities\Repos\BookRepo; use BookStack\Entities\Tools\BookContents; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use Illuminate\Http\Request; use Illuminate\Validation\ValidationException; @@ -47,7 +48,7 @@ class BookApiController extends ApiController */ public function create(Request $request) { - $this->checkPermission('book-create-all'); + $this->checkPermission(Permission::BookCreateAll); $requestData = $this->validate($request, $this->rules()['create']); $book = $this->bookRepo->create($requestData); @@ -92,7 +93,7 @@ class BookApiController extends ApiController public function update(Request $request, string $id) { $book = $this->queries->findVisibleByIdOrFail(intval($id)); - $this->checkOwnablePermission('book-update', $book); + $this->checkOwnablePermission(Permission::BookUpdate, $book); $requestData = $this->validate($request, $this->rules()['update']); $book = $this->bookRepo->update($book, $requestData); @@ -109,7 +110,7 @@ class BookApiController extends ApiController public function delete(string $id) { $book = $this->queries->findVisibleByIdOrFail(intval($id)); - $this->checkOwnablePermission('book-delete', $book); + $this->checkOwnablePermission(Permission::BookDelete, $book); $this->bookRepo->destroy($book); diff --git a/app/Entities/Controllers/BookController.php b/app/Entities/Controllers/BookController.php index 5d3d67f64..cbf7ffb79 100644 --- a/app/Entities/Controllers/BookController.php +++ b/app/Entities/Controllers/BookController.php @@ -17,6 +17,7 @@ use BookStack\Exceptions\ImageUploadException; use BookStack\Exceptions\NotFoundException; use BookStack\Facades\Activity; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\References\ReferenceFetcher; use BookStack\Util\DatabaseTransaction; use BookStack\Util\SimpleListOptions; @@ -73,12 +74,12 @@ class BookController extends Controller */ public function create(?string $shelfSlug = null) { - $this->checkPermission('book-create-all'); + $this->checkPermission(Permission::BookCreateAll); $bookshelf = null; if ($shelfSlug !== null) { $bookshelf = $this->shelfQueries->findVisibleBySlugOrFail($shelfSlug); - $this->checkOwnablePermission('bookshelf-update', $bookshelf); + $this->checkOwnablePermission(Permission::BookshelfUpdate, $bookshelf); } $this->setPageTitle(trans('entities.books_create')); @@ -96,7 +97,7 @@ class BookController extends Controller */ public function store(Request $request, ?string $shelfSlug = null) { - $this->checkPermission('book-create-all'); + $this->checkPermission(Permission::BookCreateAll); $validated = $this->validate($request, [ 'name' => ['required', 'string', 'max:255'], 'description_html' => ['string', 'max:2000'], @@ -108,7 +109,7 @@ class BookController extends Controller $bookshelf = null; if ($shelfSlug !== null) { $bookshelf = $this->shelfQueries->findVisibleBySlugOrFail($shelfSlug); - $this->checkOwnablePermission('bookshelf-update', $bookshelf); + $this->checkOwnablePermission(Permission::BookshelfUpdate, $bookshelf); } $book = $this->bookRepo->create($validated); @@ -154,7 +155,7 @@ class BookController extends Controller public function edit(string $slug) { $book = $this->queries->findVisibleBySlugOrFail($slug); - $this->checkOwnablePermission('book-update', $book); + $this->checkOwnablePermission(Permission::BookUpdate, $book); $this->setPageTitle(trans('entities.books_edit_named', ['bookName' => $book->getShortName()])); return view('books.edit', ['book' => $book, 'current' => $book]); @@ -170,7 +171,7 @@ class BookController extends Controller public function update(Request $request, string $slug) { $book = $this->queries->findVisibleBySlugOrFail($slug); - $this->checkOwnablePermission('book-update', $book); + $this->checkOwnablePermission(Permission::BookUpdate, $book); $validated = $this->validate($request, [ 'name' => ['required', 'string', 'max:255'], @@ -197,7 +198,7 @@ class BookController extends Controller public function showDelete(string $bookSlug) { $book = $this->queries->findVisibleBySlugOrFail($bookSlug); - $this->checkOwnablePermission('book-delete', $book); + $this->checkOwnablePermission(Permission::BookDelete, $book); $this->setPageTitle(trans('entities.books_delete_named', ['bookName' => $book->getShortName()])); return view('books.delete', ['book' => $book, 'current' => $book]); @@ -211,7 +212,7 @@ class BookController extends Controller public function destroy(string $bookSlug) { $book = $this->queries->findVisibleBySlugOrFail($bookSlug); - $this->checkOwnablePermission('book-delete', $book); + $this->checkOwnablePermission(Permission::BookDelete, $book); $this->bookRepo->destroy($book); @@ -226,7 +227,7 @@ class BookController extends Controller public function showCopy(string $bookSlug) { $book = $this->queries->findVisibleBySlugOrFail($bookSlug); - $this->checkOwnablePermission('book-view', $book); + $this->checkOwnablePermission(Permission::BookView, $book); session()->flashInput(['name' => $book->name]); @@ -243,8 +244,8 @@ class BookController extends Controller public function copy(Request $request, Cloner $cloner, string $bookSlug) { $book = $this->queries->findVisibleBySlugOrFail($bookSlug); - $this->checkOwnablePermission('book-view', $book); - $this->checkPermission('book-create-all'); + $this->checkOwnablePermission(Permission::BookView, $book); + $this->checkPermission(Permission::BookCreateAll); $newName = $request->get('name') ?: $book->name; $bookCopy = $cloner->cloneBook($book, $newName); @@ -259,10 +260,10 @@ class BookController extends Controller public function convertToShelf(HierarchyTransformer $transformer, string $bookSlug) { $book = $this->queries->findVisibleBySlugOrFail($bookSlug); - $this->checkOwnablePermission('book-update', $book); - $this->checkOwnablePermission('book-delete', $book); - $this->checkPermission('bookshelf-create-all'); - $this->checkPermission('book-create-all'); + $this->checkOwnablePermission(Permission::BookUpdate, $book); + $this->checkOwnablePermission(Permission::BookDelete, $book); + $this->checkPermission(Permission::BookshelfCreateAll); + $this->checkPermission(Permission::BookCreateAll); $shelf = (new DatabaseTransaction(function () use ($book, $transformer) { return $transformer->transformBookToShelf($book); diff --git a/app/Entities/Controllers/BookshelfApiController.php b/app/Entities/Controllers/BookshelfApiController.php index b512f2d05..f4bd394a9 100644 --- a/app/Entities/Controllers/BookshelfApiController.php +++ b/app/Entities/Controllers/BookshelfApiController.php @@ -6,6 +6,7 @@ use BookStack\Entities\Models\Bookshelf; use BookStack\Entities\Queries\BookshelfQueries; use BookStack\Entities\Repos\BookshelfRepo; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use Exception; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Http\Request; @@ -45,7 +46,7 @@ class BookshelfApiController extends ApiController */ public function create(Request $request) { - $this->checkPermission('bookshelf-create-all'); + $this->checkPermission(Permission::BookshelfCreateAll); $requestData = $this->validate($request, $this->rules()['create']); $bookIds = $request->get('books', []); @@ -84,7 +85,7 @@ class BookshelfApiController extends ApiController public function update(Request $request, string $id) { $shelf = $this->queries->findVisibleByIdOrFail(intval($id)); - $this->checkOwnablePermission('bookshelf-update', $shelf); + $this->checkOwnablePermission(Permission::BookshelfUpdate, $shelf); $requestData = $this->validate($request, $this->rules()['update']); $bookIds = $request->get('books', null); @@ -103,7 +104,7 @@ class BookshelfApiController extends ApiController public function delete(string $id) { $shelf = $this->queries->findVisibleByIdOrFail(intval($id)); - $this->checkOwnablePermission('bookshelf-delete', $shelf); + $this->checkOwnablePermission(Permission::BookshelfDelete, $shelf); $this->bookshelfRepo->destroy($shelf); diff --git a/app/Entities/Controllers/BookshelfController.php b/app/Entities/Controllers/BookshelfController.php index 6cedd23e7..f47742ffa 100644 --- a/app/Entities/Controllers/BookshelfController.php +++ b/app/Entities/Controllers/BookshelfController.php @@ -11,6 +11,7 @@ use BookStack\Entities\Tools\ShelfContext; use BookStack\Exceptions\ImageUploadException; use BookStack\Exceptions\NotFoundException; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\References\ReferenceFetcher; use BookStack\Util\SimpleListOptions; use Exception; @@ -68,7 +69,7 @@ class BookshelfController extends Controller */ public function create() { - $this->checkPermission('bookshelf-create-all'); + $this->checkPermission(Permission::BookshelfCreateAll); $books = $this->bookQueries->visibleForList()->orderBy('name')->get(['name', 'id', 'slug', 'created_at', 'updated_at']); $this->setPageTitle(trans('entities.shelves_create')); @@ -83,7 +84,7 @@ class BookshelfController extends Controller */ public function store(Request $request) { - $this->checkPermission('bookshelf-create-all'); + $this->checkPermission(Permission::BookshelfCreateAll); $validated = $this->validate($request, [ 'name' => ['required', 'string', 'max:255'], 'description_html' => ['string', 'max:2000'], @@ -105,7 +106,7 @@ class BookshelfController extends Controller public function show(Request $request, ActivityQueries $activities, string $slug) { $shelf = $this->queries->findVisibleBySlugOrFail($slug); - $this->checkOwnablePermission('bookshelf-view', $shelf); + $this->checkOwnablePermission(Permission::BookshelfView, $shelf); $listOptions = SimpleListOptions::fromRequest($request, 'shelf_books')->withSortOptions([ 'default' => trans('common.sort_default'), @@ -143,7 +144,7 @@ class BookshelfController extends Controller public function edit(string $slug) { $shelf = $this->queries->findVisibleBySlugOrFail($slug); - $this->checkOwnablePermission('bookshelf-update', $shelf); + $this->checkOwnablePermission(Permission::BookshelfUpdate, $shelf); $shelfBookIds = $shelf->books()->get(['id'])->pluck('id'); $books = $this->bookQueries->visibleForList() @@ -169,7 +170,7 @@ class BookshelfController extends Controller public function update(Request $request, string $slug) { $shelf = $this->queries->findVisibleBySlugOrFail($slug); - $this->checkOwnablePermission('bookshelf-update', $shelf); + $this->checkOwnablePermission(Permission::BookshelfUpdate, $shelf); $validated = $this->validate($request, [ 'name' => ['required', 'string', 'max:255'], 'description_html' => ['string', 'max:2000'], @@ -195,7 +196,7 @@ class BookshelfController extends Controller public function showDelete(string $slug) { $shelf = $this->queries->findVisibleBySlugOrFail($slug); - $this->checkOwnablePermission('bookshelf-delete', $shelf); + $this->checkOwnablePermission(Permission::BookshelfDelete, $shelf); $this->setPageTitle(trans('entities.shelves_delete_named', ['name' => $shelf->getShortName()])); @@ -210,7 +211,7 @@ class BookshelfController extends Controller public function destroy(string $slug) { $shelf = $this->queries->findVisibleBySlugOrFail($slug); - $this->checkOwnablePermission('bookshelf-delete', $shelf); + $this->checkOwnablePermission(Permission::BookshelfDelete, $shelf); $this->shelfRepo->destroy($shelf); diff --git a/app/Entities/Controllers/ChapterApiController.php b/app/Entities/Controllers/ChapterApiController.php index 6ba2e9fd2..80eab7bb8 100644 --- a/app/Entities/Controllers/ChapterApiController.php +++ b/app/Entities/Controllers/ChapterApiController.php @@ -9,6 +9,7 @@ use BookStack\Entities\Queries\EntityQueries; use BookStack\Entities\Repos\ChapterRepo; use BookStack\Exceptions\PermissionsException; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use Exception; use Illuminate\Http\Request; @@ -65,7 +66,7 @@ class ChapterApiController extends ApiController $bookId = $request->get('book_id'); $book = $this->entityQueries->books->findVisibleByIdOrFail(intval($bookId)); - $this->checkOwnablePermission('chapter-create', $book); + $this->checkOwnablePermission(Permission::ChapterCreate, $book); $chapter = $this->chapterRepo->create($requestData, $book); @@ -101,10 +102,10 @@ class ChapterApiController extends ApiController { $requestData = $this->validate($request, $this->rules()['update']); $chapter = $this->queries->findVisibleByIdOrFail(intval($id)); - $this->checkOwnablePermission('chapter-update', $chapter); + $this->checkOwnablePermission(Permission::ChapterUpdate, $chapter); if ($request->has('book_id') && $chapter->book_id !== intval($requestData['book_id'])) { - $this->checkOwnablePermission('chapter-delete', $chapter); + $this->checkOwnablePermission(Permission::ChapterDelete, $chapter); try { $this->chapterRepo->move($chapter, "book:{$requestData['book_id']}"); @@ -129,7 +130,7 @@ class ChapterApiController extends ApiController public function delete(string $id) { $chapter = $this->queries->findVisibleByIdOrFail(intval($id)); - $this->checkOwnablePermission('chapter-delete', $chapter); + $this->checkOwnablePermission(Permission::ChapterDelete, $chapter); $this->chapterRepo->destroy($chapter); diff --git a/app/Entities/Controllers/ChapterController.php b/app/Entities/Controllers/ChapterController.php index 677745500..9335e0a70 100644 --- a/app/Entities/Controllers/ChapterController.php +++ b/app/Entities/Controllers/ChapterController.php @@ -17,6 +17,7 @@ use BookStack\Exceptions\NotFoundException; use BookStack\Exceptions\NotifyException; use BookStack\Exceptions\PermissionsException; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\References\ReferenceFetcher; use BookStack\Util\DatabaseTransaction; use Illuminate\Http\Request; @@ -39,7 +40,7 @@ class ChapterController extends Controller public function create(string $bookSlug) { $book = $this->entityQueries->books->findVisibleBySlugOrFail($bookSlug); - $this->checkOwnablePermission('chapter-create', $book); + $this->checkOwnablePermission(Permission::ChapterCreate, $book); $this->setPageTitle(trans('entities.chapters_create')); @@ -64,7 +65,7 @@ class ChapterController extends Controller ]); $book = $this->entityQueries->books->findVisibleBySlugOrFail($bookSlug); - $this->checkOwnablePermission('chapter-create', $book); + $this->checkOwnablePermission(Permission::ChapterCreate, $book); $chapter = $this->chapterRepo->create($validated, $book); @@ -77,7 +78,6 @@ class ChapterController extends Controller public function show(string $bookSlug, string $chapterSlug) { $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); - $this->checkOwnablePermission('chapter-view', $chapter); $sidebarTree = (new BookContents($chapter->book))->getTree(); $pages = $this->entityQueries->pages->visibleForChapterList($chapter->id)->get(); @@ -106,7 +106,7 @@ class ChapterController extends Controller public function edit(string $bookSlug, string $chapterSlug) { $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); - $this->checkOwnablePermission('chapter-update', $chapter); + $this->checkOwnablePermission(Permission::ChapterUpdate, $chapter); $this->setPageTitle(trans('entities.chapters_edit_named', ['chapterName' => $chapter->getShortName()])); @@ -128,7 +128,7 @@ class ChapterController extends Controller ]); $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); - $this->checkOwnablePermission('chapter-update', $chapter); + $this->checkOwnablePermission(Permission::ChapterUpdate, $chapter); $this->chapterRepo->update($chapter, $validated); @@ -143,7 +143,7 @@ class ChapterController extends Controller public function showDelete(string $bookSlug, string $chapterSlug) { $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); - $this->checkOwnablePermission('chapter-delete', $chapter); + $this->checkOwnablePermission(Permission::ChapterDelete, $chapter); $this->setPageTitle(trans('entities.chapters_delete_named', ['chapterName' => $chapter->getShortName()])); @@ -159,7 +159,7 @@ class ChapterController extends Controller public function destroy(string $bookSlug, string $chapterSlug) { $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); - $this->checkOwnablePermission('chapter-delete', $chapter); + $this->checkOwnablePermission(Permission::ChapterDelete, $chapter); $this->chapterRepo->destroy($chapter); @@ -175,8 +175,8 @@ class ChapterController extends Controller { $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $this->setPageTitle(trans('entities.chapters_move_named', ['chapterName' => $chapter->getShortName()])); - $this->checkOwnablePermission('chapter-update', $chapter); - $this->checkOwnablePermission('chapter-delete', $chapter); + $this->checkOwnablePermission(Permission::ChapterUpdate, $chapter); + $this->checkOwnablePermission(Permission::ChapterDelete, $chapter); return view('chapters.move', [ 'chapter' => $chapter, @@ -192,8 +192,8 @@ class ChapterController extends Controller public function move(Request $request, string $bookSlug, string $chapterSlug) { $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); - $this->checkOwnablePermission('chapter-update', $chapter); - $this->checkOwnablePermission('chapter-delete', $chapter); + $this->checkOwnablePermission(Permission::ChapterUpdate, $chapter); + $this->checkOwnablePermission(Permission::ChapterDelete, $chapter); $entitySelection = $request->get('entity_selection', null); if ($entitySelection === null || $entitySelection === '') { @@ -221,7 +221,6 @@ class ChapterController extends Controller public function showCopy(string $bookSlug, string $chapterSlug) { $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); - $this->checkOwnablePermission('chapter-view', $chapter); session()->flashInput(['name' => $chapter->name]); @@ -240,7 +239,6 @@ class ChapterController extends Controller public function copy(Request $request, Cloner $cloner, string $bookSlug, string $chapterSlug) { $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); - $this->checkOwnablePermission('chapter-view', $chapter); $entitySelection = $request->get('entity_selection') ?: null; $newParentBook = $entitySelection ? $this->entityQueries->findVisibleByStringIdentifier($entitySelection) : $chapter->getParent(); @@ -251,7 +249,7 @@ class ChapterController extends Controller return redirect($chapter->getUrl('/copy')); } - $this->checkOwnablePermission('chapter-create', $newParentBook); + $this->checkOwnablePermission(Permission::ChapterCreate, $newParentBook); $newName = $request->get('name') ?: $chapter->name; $chapterCopy = $cloner->cloneChapter($chapter, $newParentBook, $newName); @@ -266,9 +264,9 @@ class ChapterController extends Controller public function convertToBook(HierarchyTransformer $transformer, string $bookSlug, string $chapterSlug) { $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); - $this->checkOwnablePermission('chapter-update', $chapter); - $this->checkOwnablePermission('chapter-delete', $chapter); - $this->checkPermission('book-create-all'); + $this->checkOwnablePermission(Permission::ChapterUpdate, $chapter); + $this->checkOwnablePermission(Permission::ChapterDelete, $chapter); + $this->checkPermission(Permission::BookCreateAll); $book = (new DatabaseTransaction(function () use ($chapter, $transformer) { return $transformer->transformChapterToBook($chapter); diff --git a/app/Entities/Controllers/PageApiController.php b/app/Entities/Controllers/PageApiController.php index 8fcba3dc6..033c19a7a 100644 --- a/app/Entities/Controllers/PageApiController.php +++ b/app/Entities/Controllers/PageApiController.php @@ -7,6 +7,7 @@ use BookStack\Entities\Queries\PageQueries; use BookStack\Entities\Repos\PageRepo; use BookStack\Exceptions\PermissionsException; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use Exception; use Illuminate\Http\Request; @@ -76,7 +77,7 @@ class PageApiController extends ApiController } else { $parent = $this->entityQueries->books->findVisibleByIdOrFail(intval($request->get('book_id'))); } - $this->checkOwnablePermission('page-create', $parent); + $this->checkOwnablePermission(Permission::PageCreate, $parent); $draft = $this->pageRepo->getNewDraftPage($parent); $this->pageRepo->publishDraft($draft, $request->only(array_keys($this->rules['create']))); @@ -116,7 +117,7 @@ class PageApiController extends ApiController $requestData = $this->validate($request, $this->rules['update']); $page = $this->queries->findVisibleByIdOrFail($id); - $this->checkOwnablePermission('page-update', $page); + $this->checkOwnablePermission(Permission::PageUpdate, $page); $parent = null; if ($request->has('chapter_id')) { @@ -126,7 +127,7 @@ class PageApiController extends ApiController } if ($parent && !$parent->matches($page->getParent())) { - $this->checkOwnablePermission('page-delete', $page); + $this->checkOwnablePermission(Permission::PageDelete, $page); try { $this->pageRepo->move($page, $parent->getType() . ':' . $parent->id); @@ -151,7 +152,7 @@ class PageApiController extends ApiController public function delete(string $id) { $page = $this->queries->findVisibleByIdOrFail($id); - $this->checkOwnablePermission('page-delete', $page); + $this->checkOwnablePermission(Permission::PageDelete, $page); $this->pageRepo->destroy($page); diff --git a/app/Entities/Controllers/PageController.php b/app/Entities/Controllers/PageController.php index de3aed7d9..67ecb0bb3 100644 --- a/app/Entities/Controllers/PageController.php +++ b/app/Entities/Controllers/PageController.php @@ -20,6 +20,7 @@ use BookStack\Exceptions\NotFoundException; use BookStack\Exceptions\NotifyException; use BookStack\Exceptions\PermissionsException; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\References\ReferenceFetcher; use Exception; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -50,7 +51,7 @@ class PageController extends Controller $parent = $this->entityQueries->books->findVisibleBySlugOrFail($bookSlug); } - $this->checkOwnablePermission('page-create', $parent); + $this->checkOwnablePermission(Permission::PageCreate, $parent); // Redirect to draft edit screen if signed in if ($this->isSignedIn()) { @@ -82,7 +83,7 @@ class PageController extends Controller $parent = $this->entityQueries->books->findVisibleBySlugOrFail($bookSlug); } - $this->checkOwnablePermission('page-create', $parent); + $this->checkOwnablePermission(Permission::PageCreate, $parent); $page = $this->pageRepo->getNewDraftPage($parent); $this->pageRepo->publishDraft($page, [ @@ -100,7 +101,7 @@ class PageController extends Controller public function editDraft(Request $request, string $bookSlug, int $pageId) { $draft = $this->queries->findVisibleByIdOrFail($pageId); - $this->checkOwnablePermission('page-create', $draft->getParent()); + $this->checkOwnablePermission(Permission::PageCreate, $draft->getParent()); $editorData = new PageEditorData($draft, $this->entityQueries, $request->query('editor', '')); $this->setPageTitle(trans('entities.pages_edit_draft')); @@ -120,7 +121,7 @@ class PageController extends Controller 'name' => ['required', 'string', 'max:255'], ]); $draftPage = $this->queries->findVisibleByIdOrFail($pageId); - $this->checkOwnablePermission('page-create', $draftPage->getParent()); + $this->checkOwnablePermission(Permission::PageCreate, $draftPage->getParent()); $page = $this->pageRepo->publishDraft($draftPage, $request->all()); @@ -148,8 +149,6 @@ class PageController extends Controller return redirect($page->getUrl()); } - $this->checkOwnablePermission('page-view', $page); - $pageContent = (new PageContent($page)); $page->html = $pageContent->render(); $pageNav = $pageContent->getNavigation($page->html); @@ -197,7 +196,7 @@ class PageController extends Controller public function edit(Request $request, string $bookSlug, string $pageSlug) { $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-update', $page, $page->getUrl()); + $this->checkOwnablePermission(Permission::PageUpdate, $page, $page->getUrl()); $editorData = new PageEditorData($page, $this->entityQueries, $request->query('editor', '')); if ($editorData->getWarnings()) { @@ -221,7 +220,7 @@ class PageController extends Controller 'name' => ['required', 'string', 'max:255'], ]); $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-update', $page); + $this->checkOwnablePermission(Permission::PageUpdate, $page); $this->pageRepo->update($page, $request->all()); @@ -236,7 +235,7 @@ class PageController extends Controller public function saveDraft(Request $request, int $pageId) { $page = $this->queries->findVisibleByIdOrFail($pageId); - $this->checkOwnablePermission('page-update', $page); + $this->checkOwnablePermission(Permission::PageUpdate, $page); if (!$this->isSignedIn()) { return $this->jsonError(trans('errors.guests_cannot_save_drafts'), 500); @@ -273,7 +272,7 @@ class PageController extends Controller public function showDelete(string $bookSlug, string $pageSlug) { $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-delete', $page); + $this->checkOwnablePermission(Permission::PageDelete, $page); $this->setPageTitle(trans('entities.pages_delete_named', ['pageName' => $page->getShortName()])); $usedAsTemplate = $this->entityQueries->books->start()->where('default_template_id', '=', $page->id)->count() > 0 || @@ -295,7 +294,7 @@ class PageController extends Controller public function showDeleteDraft(string $bookSlug, int $pageId) { $page = $this->queries->findVisibleByIdOrFail($pageId); - $this->checkOwnablePermission('page-update', $page); + $this->checkOwnablePermission(Permission::PageUpdate, $page); $this->setPageTitle(trans('entities.pages_delete_draft_named', ['pageName' => $page->getShortName()])); $usedAsTemplate = $this->entityQueries->books->start()->where('default_template_id', '=', $page->id)->count() > 0 || @@ -318,7 +317,7 @@ class PageController extends Controller public function destroy(string $bookSlug, string $pageSlug) { $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-delete', $page); + $this->checkOwnablePermission(Permission::PageDelete, $page); $parent = $page->getParent(); $this->pageRepo->destroy($page); @@ -337,13 +336,13 @@ class PageController extends Controller $page = $this->queries->findVisibleByIdOrFail($pageId); $book = $page->book; $chapter = $page->chapter; - $this->checkOwnablePermission('page-update', $page); + $this->checkOwnablePermission(Permission::PageUpdate, $page); $this->pageRepo->destroy($page); $this->showSuccessNotification(trans('entities.pages_delete_draft_success')); - if ($chapter && userCan('view', $chapter)) { + if ($chapter && userCan(Permission::ChapterView, $chapter)) { return redirect($chapter->getUrl()); } @@ -384,8 +383,8 @@ class PageController extends Controller public function showMove(string $bookSlug, string $pageSlug) { $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-update', $page); - $this->checkOwnablePermission('page-delete', $page); + $this->checkOwnablePermission(Permission::PageUpdate, $page); + $this->checkOwnablePermission(Permission::PageDelete, $page); return view('pages.move', [ 'book' => $page->book, @@ -402,8 +401,8 @@ class PageController extends Controller public function move(Request $request, string $bookSlug, string $pageSlug) { $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-update', $page); - $this->checkOwnablePermission('page-delete', $page); + $this->checkOwnablePermission(Permission::PageUpdate, $page); + $this->checkOwnablePermission(Permission::PageDelete, $page); $entitySelection = $request->get('entity_selection', null); if ($entitySelection === null || $entitySelection === '') { @@ -431,7 +430,6 @@ class PageController extends Controller public function showCopy(string $bookSlug, string $pageSlug) { $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-view', $page); session()->flashInput(['name' => $page->name]); return view('pages.copy', [ @@ -449,7 +447,7 @@ class PageController extends Controller public function copy(Request $request, Cloner $cloner, string $bookSlug, string $pageSlug) { $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-view', $page); + $this->checkOwnablePermission(Permission::PageView, $page); $entitySelection = $request->get('entity_selection') ?: null; $newParent = $entitySelection ? $this->entityQueries->findVisibleByStringIdentifier($entitySelection) : $page->getParent(); @@ -460,7 +458,7 @@ class PageController extends Controller return redirect($page->getUrl('/copy')); } - $this->checkOwnablePermission('page-create', $newParent); + $this->checkOwnablePermission(Permission::PageCreate, $newParent); $newName = $request->get('name') ?: $page->name; $pageCopy = $cloner->clonePage($page, $newParent, $newName); diff --git a/app/Entities/Controllers/PageRevisionController.php b/app/Entities/Controllers/PageRevisionController.php index c43eea08b..35f1e8daf 100644 --- a/app/Entities/Controllers/PageRevisionController.php +++ b/app/Entities/Controllers/PageRevisionController.php @@ -11,6 +11,7 @@ use BookStack\Entities\Tools\PageContent; use BookStack\Exceptions\NotFoundException; use BookStack\Facades\Activity; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Util\SimpleListOptions; use Illuminate\Http\Request; use Ssddanbrown\HtmlDiff\Diff; @@ -124,7 +125,7 @@ class PageRevisionController extends Controller public function restore(string $bookSlug, string $pageSlug, int $revisionId) { $page = $this->pageQueries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-update', $page); + $this->checkOwnablePermission(Permission::PageUpdate, $page); $page = $this->pageRepo->restoreRevision($page, $revisionId); @@ -139,7 +140,7 @@ class PageRevisionController extends Controller public function destroy(string $bookSlug, string $pageSlug, int $revId) { $page = $this->pageQueries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-delete', $page); + $this->checkOwnablePermission(Permission::PageDelete, $page); $revision = $page->revisions()->where('id', '=', $revId)->first(); if ($revision === null) { diff --git a/app/Entities/Controllers/RecycleBinApiController.php b/app/Entities/Controllers/RecycleBinApiController.php index 89bd3f41a..614685136 100644 --- a/app/Entities/Controllers/RecycleBinApiController.php +++ b/app/Entities/Controllers/RecycleBinApiController.php @@ -9,6 +9,7 @@ use BookStack\Entities\Models\Deletion; use BookStack\Entities\Models\Page; use BookStack\Entities\Repos\DeletionRepo; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -17,8 +18,8 @@ class RecycleBinApiController extends ApiController public function __construct() { $this->middleware(function ($request, $next) { - $this->checkPermission('settings-manage'); - $this->checkPermission('restrictions-manage-all'); + $this->checkPermission(Permission::SettingsManage); + $this->checkPermission(Permission::RestrictionsManageAll); return $next($request); }); diff --git a/app/Entities/Controllers/RecycleBinController.php b/app/Entities/Controllers/RecycleBinController.php index d11dde4dd..f3c2b6a01 100644 --- a/app/Entities/Controllers/RecycleBinController.php +++ b/app/Entities/Controllers/RecycleBinController.php @@ -8,6 +8,7 @@ use BookStack\Entities\Models\Entity; use BookStack\Entities\Repos\DeletionRepo; use BookStack\Entities\Tools\TrashCan; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; class RecycleBinController extends Controller { @@ -20,8 +21,8 @@ class RecycleBinController extends Controller public function __construct() { $this->middleware(function ($request, $next) { - $this->checkPermission('settings-manage'); - $this->checkPermission('restrictions-manage-all'); + $this->checkPermission(Permission::SettingsManage); + $this->checkPermission(Permission::RestrictionsManageAll); return $next($request); }); diff --git a/app/Entities/Repos/ChapterRepo.php b/app/Entities/Repos/ChapterRepo.php index 6503e63cf..5d4b52978 100644 --- a/app/Entities/Repos/ChapterRepo.php +++ b/app/Entities/Repos/ChapterRepo.php @@ -11,6 +11,7 @@ use BookStack\Entities\Tools\TrashCan; use BookStack\Exceptions\MoveOperationException; use BookStack\Exceptions\PermissionsException; use BookStack\Facades\Activity; +use BookStack\Permissions\Permission; use BookStack\Util\DatabaseTransaction; use Exception; @@ -87,7 +88,7 @@ class ChapterRepo throw new MoveOperationException('Book to move chapter into not found'); } - if (!userCan('chapter-create', $parent)) { + if (!userCan(Permission::ChapterCreate, $parent)) { throw new PermissionsException('User does not have permission to create a chapter within the chosen book'); } diff --git a/app/Entities/Repos/PageRepo.php b/app/Entities/Repos/PageRepo.php index 63e8b8370..76377f9a6 100644 --- a/app/Entities/Repos/PageRepo.php +++ b/app/Entities/Repos/PageRepo.php @@ -16,6 +16,7 @@ use BookStack\Entities\Tools\TrashCan; use BookStack\Exceptions\MoveOperationException; use BookStack\Exceptions\PermissionsException; use BookStack\Facades\Activity; +use BookStack\Permissions\Permission; use BookStack\References\ReferenceStore; use BookStack\References\ReferenceUpdater; use BookStack\Util\DatabaseTransaction; @@ -55,7 +56,7 @@ class PageRepo } $defaultTemplate = $page->chapter->defaultTemplate ?? $page->book->defaultTemplate; - if ($defaultTemplate && userCan('view', $defaultTemplate)) { + if ($defaultTemplate && userCan(Permission::PageView, $defaultTemplate)) { $page->forceFill([ 'html' => $defaultTemplate->html, 'markdown' => $defaultTemplate->markdown, @@ -142,7 +143,7 @@ class PageRepo protected function updateTemplateStatusAndContentFromInput(Page $page, array $input): void { - if (isset($input['template']) && userCan('templates-manage')) { + if (isset($input['template']) && userCan(Permission::TemplatesManage)) { $page->template = ($input['template'] === 'true'); } @@ -165,7 +166,7 @@ class PageRepo $pageContent->setNewHTML($input['html'], user()); } - if (($newEditor !== $currentEditor || empty($page->editor)) && userCan('editor-change')) { + if (($newEditor !== $currentEditor || empty($page->editor)) && userCan(Permission::EditorChange)) { $page->editor = $newEditor->value; } elseif (empty($page->editor)) { $page->editor = $defaultEditor->value; @@ -271,7 +272,7 @@ class PageRepo throw new MoveOperationException('Book or chapter to move page into not found'); } - if (!userCan('page-create', $parent)) { + if (!userCan(Permission::PageCreate, $parent)) { throw new PermissionsException('User does not have permission to create a page within the new parent'); } diff --git a/app/Entities/Tools/Cloner.php b/app/Entities/Tools/Cloner.php index 87aa770c0..05618fef4 100644 --- a/app/Entities/Tools/Cloner.php +++ b/app/Entities/Tools/Cloner.php @@ -12,6 +12,7 @@ use BookStack\Entities\Models\Page; use BookStack\Entities\Repos\BookRepo; use BookStack\Entities\Repos\ChapterRepo; use BookStack\Entities\Repos\PageRepo; +use BookStack\Permissions\Permission; use BookStack\Uploads\Image; use BookStack\Uploads\ImageService; use Illuminate\Http\UploadedFile; @@ -49,7 +50,7 @@ class Cloner $copyChapter = $this->chapterRepo->create($chapterDetails, $parent); - if (userCan('page-create', $copyChapter)) { + if (userCan(Permission::PageCreate, $copyChapter)) { /** @var Page $page */ foreach ($original->getVisiblePages() as $page) { $this->clonePage($page, $copyChapter, $page->name); @@ -61,7 +62,7 @@ class Cloner /** * Clone the given book. - * Clones all child chapters & pages. + * Clones all child chapters and pages. */ public function cloneBook(Book $original, string $newName): Book { @@ -74,11 +75,11 @@ class Cloner // Clone contents $directChildren = $original->getDirectVisibleChildren(); foreach ($directChildren as $child) { - if ($child instanceof Chapter && userCan('chapter-create', $copyBook)) { + if ($child instanceof Chapter && userCan(Permission::ChapterCreate, $copyBook)) { $this->cloneChapter($child, $copyBook, $child->name); } - if ($child instanceof Page && !$child->draft && userCan('page-create', $copyBook)) { + if ($child instanceof Page && !$child->draft && userCan(Permission::PageCreate, $copyBook)) { $this->clonePage($child, $copyBook, $child->name); } } @@ -86,7 +87,7 @@ class Cloner // Clone bookshelf relationships /** @var Bookshelf $shelf */ foreach ($original->shelves as $shelf) { - if (userCan('bookshelf-update', $shelf)) { + if (userCan(Permission::BookshelfUpdate, $shelf)) { $shelf->appendBook($copyBook); } } diff --git a/app/Entities/Tools/PageContent.php b/app/Entities/Tools/PageContent.php index d2f5de65c..4b1d77db7 100644 --- a/app/Entities/Tools/PageContent.php +++ b/app/Entities/Tools/PageContent.php @@ -7,6 +7,7 @@ use BookStack\Entities\Queries\PageQueries; use BookStack\Entities\Tools\Markdown\MarkdownToHtml; use BookStack\Exceptions\ImageUploadException; use BookStack\Facades\Theme; +use BookStack\Permissions\Permission; use BookStack\Theming\ThemeEvents; use BookStack\Uploads\ImageRepo; use BookStack\Uploads\ImageService; @@ -122,7 +123,7 @@ class PageContent $imageInfo = $this->parseBase64ImageUri($uri); // Validate user has permission to create images - if (!$updater->can('image-create-all')) { + if (!$updater->can(Permission::ImageCreateAll)) { return ''; } diff --git a/app/Entities/Tools/PageEditorData.php b/app/Entities/Tools/PageEditorData.php index e4fe2fd25..b41b31909 100644 --- a/app/Entities/Tools/PageEditorData.php +++ b/app/Entities/Tools/PageEditorData.php @@ -7,6 +7,7 @@ use BookStack\Entities\Models\Page; use BookStack\Entities\Queries\EntityQueries; use BookStack\Entities\Tools\Markdown\HtmlToMarkdown; use BookStack\Entities\Tools\Markdown\MarkdownToHtml; +use BookStack\Permissions\Permission; class PageEditorData { @@ -98,9 +99,9 @@ class PageEditorData { $editorType = PageEditorType::forPage($page) ?: PageEditorType::getSystemDefault(); - // Use requested editor if valid and if we have permission + // Use the requested editor if valid and if we have permission $requestedType = PageEditorType::fromRequestValue($this->requestedEditor); - if ($requestedType && userCan('editor-change')) { + if ($requestedType && userCan(Permission::EditorChange)) { $editorType = $requestedType; } diff --git a/app/Entities/Tools/PermissionsUpdater.php b/app/Entities/Tools/PermissionsUpdater.php index 9f3b8f952..fa9ae753c 100644 --- a/app/Entities/Tools/PermissionsUpdater.php +++ b/app/Entities/Tools/PermissionsUpdater.php @@ -8,6 +8,7 @@ use BookStack\Entities\Models\Bookshelf; use BookStack\Entities\Models\Entity; use BookStack\Facades\Activity; use BookStack\Permissions\Models\EntityPermission; +use BookStack\Permissions\Permission; use BookStack\Users\Models\Role; use BookStack\Users\Models\User; use Illuminate\Http\Request; @@ -93,8 +94,9 @@ class PermissionsUpdater foreach ($permissions as $roleId => $info) { $entityPermissionData = ['role_id' => $roleId]; - foreach (EntityPermission::PERMISSIONS as $permission) { - $entityPermissionData[$permission] = (($info[$permission] ?? false) === "true"); + foreach (Permission::genericForEntity() as $permission) { + $permName = $permission->value; + $entityPermissionData[$permName] = (($info[$permName] ?? false) === "true"); } $formatted[] = $entityPermissionData; } @@ -108,8 +110,9 @@ class PermissionsUpdater foreach ($permissions as $requestPermissionData) { $entityPermissionData = ['role_id' => $requestPermissionData['role_id']]; - foreach (EntityPermission::PERMISSIONS as $permission) { - $entityPermissionData[$permission] = boolval($requestPermissionData[$permission] ?? false); + foreach (Permission::genericForEntity() as $permission) { + $permName = $permission->value; + $entityPermissionData[$permName] = boolval($requestPermissionData[$permName] ?? false); } $formatted[] = $entityPermissionData; } @@ -147,7 +150,7 @@ class PermissionsUpdater /** @var Book $book */ foreach ($shelfBooks as $book) { - if ($checkUserPermissions && !userCan('restrictions-manage', $book)) { + if ($checkUserPermissions && !userCan(Permission::RestrictionsManage, $book)) { continue; } $book->permissions()->delete(); diff --git a/app/Exports/Controllers/BookExportApiController.php b/app/Exports/Controllers/BookExportApiController.php index 87f1d7eef..21f276f8a 100644 --- a/app/Exports/Controllers/BookExportApiController.php +++ b/app/Exports/Controllers/BookExportApiController.php @@ -6,6 +6,7 @@ use BookStack\Entities\Queries\BookQueries; use BookStack\Exports\ExportFormatter; use BookStack\Exports\ZipExports\ZipExportBuilder; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use Throwable; class BookExportApiController extends ApiController @@ -14,7 +15,7 @@ class BookExportApiController extends ApiController protected ExportFormatter $exportFormatter, protected BookQueries $queries, ) { - $this->middleware('can:content-export'); + $this->middleware(Permission::ContentExport->middleware()); } /** diff --git a/app/Exports/Controllers/BookExportController.php b/app/Exports/Controllers/BookExportController.php index 67247598c..f6bb66666 100644 --- a/app/Exports/Controllers/BookExportController.php +++ b/app/Exports/Controllers/BookExportController.php @@ -7,6 +7,7 @@ use BookStack\Exceptions\NotFoundException; use BookStack\Exports\ExportFormatter; use BookStack\Exports\ZipExports\ZipExportBuilder; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use Throwable; class BookExportController extends Controller @@ -15,7 +16,7 @@ class BookExportController extends Controller protected BookQueries $queries, protected ExportFormatter $exportFormatter, ) { - $this->middleware('can:content-export'); + $this->middleware(Permission::ContentExport->middleware()); $this->middleware('throttle:exports'); } diff --git a/app/Exports/Controllers/ChapterExportApiController.php b/app/Exports/Controllers/ChapterExportApiController.php index bccd414af..7e5a23c70 100644 --- a/app/Exports/Controllers/ChapterExportApiController.php +++ b/app/Exports/Controllers/ChapterExportApiController.php @@ -6,6 +6,7 @@ use BookStack\Entities\Queries\ChapterQueries; use BookStack\Exports\ExportFormatter; use BookStack\Exports\ZipExports\ZipExportBuilder; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use Throwable; class ChapterExportApiController extends ApiController @@ -14,7 +15,7 @@ class ChapterExportApiController extends ApiController protected ExportFormatter $exportFormatter, protected ChapterQueries $queries, ) { - $this->middleware('can:content-export'); + $this->middleware(Permission::ContentExport->middleware()); } /** diff --git a/app/Exports/Controllers/ChapterExportController.php b/app/Exports/Controllers/ChapterExportController.php index 849024343..fdb2bba94 100644 --- a/app/Exports/Controllers/ChapterExportController.php +++ b/app/Exports/Controllers/ChapterExportController.php @@ -7,6 +7,7 @@ use BookStack\Exceptions\NotFoundException; use BookStack\Exports\ExportFormatter; use BookStack\Exports\ZipExports\ZipExportBuilder; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use Throwable; class ChapterExportController extends Controller @@ -15,7 +16,7 @@ class ChapterExportController extends Controller protected ChapterQueries $queries, protected ExportFormatter $exportFormatter, ) { - $this->middleware('can:content-export'); + $this->middleware(Permission::ContentExport->middleware()); $this->middleware('throttle:exports'); } diff --git a/app/Exports/Controllers/ImportApiController.php b/app/Exports/Controllers/ImportApiController.php index cac155c7c..f8eaea5a1 100644 --- a/app/Exports/Controllers/ImportApiController.php +++ b/app/Exports/Controllers/ImportApiController.php @@ -8,6 +8,7 @@ use BookStack\Exceptions\ZipImportException; use BookStack\Exceptions\ZipValidationException; use BookStack\Exports\ImportRepo; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use BookStack\Uploads\AttachmentService; use Illuminate\Http\Request; use Illuminate\Http\JsonResponse; @@ -18,7 +19,7 @@ class ImportApiController extends ApiController public function __construct( protected ImportRepo $imports, ) { - $this->middleware('can:content-import'); + $this->middleware(Permission::ContentImport->middleware()); } /** diff --git a/app/Exports/Controllers/ImportController.php b/app/Exports/Controllers/ImportController.php index 0d3e2414b..7ecc09a41 100644 --- a/app/Exports/Controllers/ImportController.php +++ b/app/Exports/Controllers/ImportController.php @@ -8,6 +8,7 @@ use BookStack\Exceptions\ZipImportException; use BookStack\Exceptions\ZipValidationException; use BookStack\Exports\ImportRepo; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Uploads\AttachmentService; use Illuminate\Http\Request; @@ -16,7 +17,7 @@ class ImportController extends Controller public function __construct( protected ImportRepo $imports, ) { - $this->middleware('can:content-import'); + $this->middleware(Permission::ContentImport->middleware()); } /** diff --git a/app/Exports/Controllers/PageExportApiController.php b/app/Exports/Controllers/PageExportApiController.php index 73af01afa..c5b186289 100644 --- a/app/Exports/Controllers/PageExportApiController.php +++ b/app/Exports/Controllers/PageExportApiController.php @@ -6,6 +6,7 @@ use BookStack\Entities\Queries\PageQueries; use BookStack\Exports\ExportFormatter; use BookStack\Exports\ZipExports\ZipExportBuilder; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use Throwable; class PageExportApiController extends ApiController @@ -14,7 +15,7 @@ class PageExportApiController extends ApiController protected ExportFormatter $exportFormatter, protected PageQueries $queries, ) { - $this->middleware('can:content-export'); + $this->middleware(Permission::ContentExport->middleware()); } /** diff --git a/app/Exports/Controllers/PageExportController.php b/app/Exports/Controllers/PageExportController.php index 145dce9dd..9bc79f247 100644 --- a/app/Exports/Controllers/PageExportController.php +++ b/app/Exports/Controllers/PageExportController.php @@ -8,6 +8,7 @@ use BookStack\Exceptions\NotFoundException; use BookStack\Exports\ExportFormatter; use BookStack\Exports\ZipExports\ZipExportBuilder; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use Throwable; class PageExportController extends Controller @@ -16,7 +17,7 @@ class PageExportController extends Controller protected PageQueries $queries, protected ExportFormatter $exportFormatter, ) { - $this->middleware('can:content-export'); + $this->middleware(Permission::ContentExport->middleware()); $this->middleware('throttle:exports'); } diff --git a/app/Exports/ImportRepo.php b/app/Exports/ImportRepo.php index 896af903a..79db69fca 100644 --- a/app/Exports/ImportRepo.php +++ b/app/Exports/ImportRepo.php @@ -16,6 +16,7 @@ use BookStack\Exports\ZipExports\ZipExportReader; use BookStack\Exports\ZipExports\ZipExportValidator; use BookStack\Exports\ZipExports\ZipImportRunner; use BookStack\Facades\Activity; +use BookStack\Permissions\Permission; use BookStack\Uploads\FileStorage; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; @@ -46,7 +47,7 @@ class ImportRepo { $query = Import::query(); - if (!userCan('settings-manage')) { + if (!userCan(Permission::SettingsManage)) { $query->where('created_by', user()->id); } @@ -57,7 +58,7 @@ class ImportRepo { $query = Import::query(); - if (!userCan('settings-manage')) { + if (!userCan(Permission::SettingsManage)) { $query->where('created_by', user()->id); } diff --git a/app/Exports/ZipExports/ZipExportReferences.php b/app/Exports/ZipExports/ZipExportReferences.php index b21248ffd..64107cf21 100644 --- a/app/Exports/ZipExports/ZipExportReferences.php +++ b/app/Exports/ZipExports/ZipExportReferences.php @@ -12,6 +12,7 @@ use BookStack\Exports\ZipExports\Models\ZipExportChapter; use BookStack\Exports\ZipExports\Models\ZipExportImage; use BookStack\Exports\ZipExports\Models\ZipExportModel; use BookStack\Exports\ZipExports\Models\ZipExportPage; +use BookStack\Permissions\Permission; use BookStack\Uploads\Attachment; use BookStack\Uploads\Image; @@ -135,7 +136,7 @@ class ZipExportReferences // Find and include images if in visibility $page = $model->getPage(); $pageExportModel = $this->pages[$page->id] ?? ($exportModel instanceof ZipExportPage ? $exportModel : null); - if (isset($this->images[$model->id]) || ($page && $pageExportModel && userCan('view', $page))) { + if (isset($this->images[$model->id]) || ($page && $pageExportModel && userCan(Permission::PageView, $page))) { if (!isset($this->images[$model->id])) { $exportImage = ZipExportImage::fromModel($model, $files); $this->images[$model->id] = $exportImage; diff --git a/app/Exports/ZipExports/ZipImportRunner.php b/app/Exports/ZipExports/ZipImportRunner.php index d25a1621f..eafb527e8 100644 --- a/app/Exports/ZipExports/ZipImportRunner.php +++ b/app/Exports/ZipExports/ZipImportRunner.php @@ -18,6 +18,7 @@ use BookStack\Exports\ZipExports\Models\ZipExportChapter; use BookStack\Exports\ZipExports\Models\ZipExportImage; use BookStack\Exports\ZipExports\Models\ZipExportPage; use BookStack\Exports\ZipExports\Models\ZipExportTag; +use BookStack\Permissions\Permission; use BookStack\Uploads\Attachment; use BookStack\Uploads\AttachmentService; use BookStack\Uploads\FileStorage; @@ -288,7 +289,7 @@ class ZipImportRunner $attachments = []; if ($exportModel instanceof ZipExportBook) { - if (!userCan('book-create-all')) { + if (!userCan(Permission::BookCreateAll)) { $errors[] = trans('errors.import_perms_books'); } array_push($pages, ...$exportModel->pages); @@ -317,11 +318,11 @@ class ZipImportRunner if (count($pages) > 0) { if ($parent) { - if (!userCan('page-create', $parent)) { + if (!userCan(Permission::PageCreate, $parent)) { $errors[] = trans('errors.import_perms_pages'); } } else { - $hasPermission = userCan('page-create-all') || userCan('page-create-own'); + $hasPermission = userCan(Permission::PageCreateAll) || userCan(Permission::PageCreateOwn); if (!$hasPermission) { $errors[] = trans('errors.import_perms_pages'); } @@ -329,13 +330,13 @@ class ZipImportRunner } if (count($images) > 0) { - if (!userCan('image-create-all')) { + if (!userCan(Permission::ImageCreateAll)) { $errors[] = trans('errors.import_perms_images'); } } if (count($attachments) > 0) { - if (!userCan('attachment-create-all')) { + if (!userCan(Permission::AttachmentCreateAll)) { $errors[] = trans('errors.import_perms_attachments'); } } diff --git a/app/Http/Controller.php b/app/Http/Controller.php index 7f2134dd8..5d3be4951 100644 --- a/app/Http/Controller.php +++ b/app/Http/Controller.php @@ -6,6 +6,7 @@ use BookStack\Activity\Models\Loggable; use BookStack\App\Model; use BookStack\Exceptions\NotifyException; use BookStack\Facades\Activity; +use BookStack\Permissions\Permission; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Http\JsonResponse; @@ -27,10 +28,9 @@ abstract class Controller extends BaseController } /** - * Stops the application and shows a permission error if - * the application is in demo mode. + * Stops the application and shows a permission error if the application is in demo mode. */ - protected function preventAccessInDemoMode() + protected function preventAccessInDemoMode(): void { if (config('app.env') === 'demo') { $this->showPermissionError(); @@ -40,14 +40,13 @@ abstract class Controller extends BaseController /** * Adds the page title into the view. */ - public function setPageTitle(string $title) + public function setPageTitle(string $title): void { view()->share('pageTitle', $title); } /** - * On a permission error redirect to home and display. - * the error as a notification. + * On a permission error redirect to home and display the error as a notification. * * @throws NotifyException */ @@ -61,7 +60,7 @@ abstract class Controller extends BaseController /** * Checks that the current user has the given permission otherwise throw an exception. */ - protected function checkPermission(string $permission): void + protected function checkPermission(string|Permission $permission): void { if (!user() || !user()->can($permission)) { $this->showPermissionError(); @@ -81,7 +80,7 @@ abstract class Controller extends BaseController /** * Check the current user's permissions against an ownable item otherwise throw an exception. */ - protected function checkOwnablePermission(string $permission, Model $ownable, string $redirectLocation = '/'): void + protected function checkOwnablePermission(string|Permission $permission, Model $ownable, string $redirectLocation = '/'): void { if (!userCan($permission, $ownable)) { $this->showPermissionError($redirectLocation); @@ -92,7 +91,7 @@ abstract class Controller extends BaseController * Check if a user has a permission or bypass the permission * check if the given callback resolves true. */ - protected function checkPermissionOr(string $permission, callable $callback): void + protected function checkPermissionOr(string|Permission $permission, callable $callback): void { if ($callback() !== true) { $this->checkPermission($permission); @@ -103,7 +102,7 @@ abstract class Controller extends BaseController * Check if the current user has a permission or bypass if the provided user * id matches the current user. */ - protected function checkPermissionOrCurrentUser(string $permission, int $userId): void + protected function checkPermissionOrCurrentUser(string|Permission $permission, int $userId): void { $this->checkPermissionOr($permission, function () use ($userId) { return $userId === user()->id; @@ -111,7 +110,7 @@ abstract class Controller extends BaseController } /** - * Send back a json error message. + * Send back a JSON error message. */ protected function jsonError(string $messageText = '', int $statusCode = 500): JsonResponse { @@ -127,7 +126,7 @@ abstract class Controller extends BaseController } /** - * Show a positive, successful notification to the user on next view load. + * Show a positive, successful notification to the user on the next view load. */ protected function showSuccessNotification(string $message): void { @@ -135,7 +134,7 @@ abstract class Controller extends BaseController } /** - * Show a warning notification to the user on next view load. + * Show a warning notification to the user on the next view load. */ protected function showWarningNotification(string $message): void { @@ -143,7 +142,7 @@ abstract class Controller extends BaseController } /** - * Show an error notification to the user on next view load. + * Show an error notification to the user on the next view load. */ protected function showErrorNotification(string $message): void { diff --git a/app/Http/Middleware/ApiAuthenticate.php b/app/Http/Middleware/ApiAuthenticate.php index 5f3ad3168..15b5a325a 100644 --- a/app/Http/Middleware/ApiAuthenticate.php +++ b/app/Http/Middleware/ApiAuthenticate.php @@ -3,6 +3,7 @@ namespace BookStack\Http\Middleware; use BookStack\Exceptions\ApiAuthException; +use BookStack\Permissions\Permission; use Closure; use Illuminate\Http\Request; @@ -51,7 +52,7 @@ class ApiAuthenticate */ protected function sessionUserHasApiAccess(): bool { - $hasApiPermission = user()->can('access-api'); + $hasApiPermission = user()->can(Permission::AccessApi); return $hasApiPermission && user()->hasAppAccess(); } diff --git a/app/Http/Middleware/CheckUserHasPermission.php b/app/Http/Middleware/CheckUserHasPermission.php index b5678e734..ea4ff652a 100644 --- a/app/Http/Middleware/CheckUserHasPermission.php +++ b/app/Http/Middleware/CheckUserHasPermission.php @@ -2,6 +2,7 @@ namespace BookStack\Http\Middleware; +use BookStack\Permissions\Permission; use Closure; use Illuminate\Http\Request; @@ -10,13 +11,9 @@ class CheckUserHasPermission /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @param string $permission - * * @return mixed */ - public function handle($request, Closure $next, $permission) + public function handle(Request $request, Closure $next, string|Permission $permission) { if (!user()->can($permission)) { return $this->errorResponse($request); diff --git a/app/Permissions/ContentPermissionApiController.php b/app/Permissions/ContentPermissionApiController.php index bddbc2c7d..0720cc478 100644 --- a/app/Permissions/ContentPermissionApiController.php +++ b/app/Permissions/ContentPermissionApiController.php @@ -51,7 +51,7 @@ class ContentPermissionApiController extends ApiController $entity = $this->entities->get($contentType) ->newQuery()->scopes(['visible'])->findOrFail($contentId); - $this->checkOwnablePermission('restrictions-manage', $entity); + $this->checkOwnablePermission(Permission::RestrictionsManage, $entity); return response()->json($this->formattedPermissionDataForEntity($entity)); } @@ -71,7 +71,7 @@ class ContentPermissionApiController extends ApiController $entity = $this->entities->get($contentType) ->newQuery()->scopes(['visible'])->findOrFail($contentId); - $this->checkOwnablePermission('restrictions-manage', $entity); + $this->checkOwnablePermission(Permission::RestrictionsManage, $entity); $data = $this->validate($request, $this->rules()['update']); $this->permissionsUpdater->updateFromApiRequestData($entity, $data); diff --git a/app/Permissions/Models/EntityPermission.php b/app/Permissions/Models/EntityPermission.php index 1ef9c2886..efad2ba39 100644 --- a/app/Permissions/Models/EntityPermission.php +++ b/app/Permissions/Models/EntityPermission.php @@ -18,8 +18,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; */ class EntityPermission extends Model { - public const PERMISSIONS = ['view', 'create', 'update', 'delete']; - protected $fillable = ['role_id', 'view', 'create', 'update', 'delete']; public $timestamps = false; protected $hidden = ['entity_id', 'entity_type', 'id']; diff --git a/app/Permissions/Models/RolePermission.php b/app/Permissions/Models/RolePermission.php index d43313ea0..67b1c55f5 100644 --- a/app/Permissions/Models/RolePermission.php +++ b/app/Permissions/Models/RolePermission.php @@ -9,7 +9,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany; /** * @property int $id * @property string $name - * @property string $display_name */ class RolePermission extends Model { diff --git a/app/Permissions/Permission.php b/app/Permissions/Permission.php new file mode 100644 index 000000000..a434e54fd --- /dev/null +++ b/app/Permissions/Permission.php @@ -0,0 +1,144 @@ +value; + } +} diff --git a/app/Permissions/PermissionApplicator.php b/app/Permissions/PermissionApplicator.php index e59b8ab67..23fdcfda9 100644 --- a/app/Permissions/PermissionApplicator.php +++ b/app/Permissions/PermissionApplicator.php @@ -24,11 +24,12 @@ class PermissionApplicator /** * Checks if an entity has a restriction set upon it. */ - public function checkOwnableUserAccess(Model&OwnableInterface $ownable, string $permission): bool + public function checkOwnableUserAccess(Model&OwnableInterface $ownable, string|Permission $permission): bool { - $explodedPermission = explode('-', $permission); + $permissionName = is_string($permission) ? $permission : $permission->value; + $explodedPermission = explode('-', $permissionName); $action = $explodedPermission[1] ?? $explodedPermission[0]; - $fullPermission = count($explodedPermission) > 1 ? $permission : $ownable->getMorphClass() . '-' . $permission; + $fullPermission = count($explodedPermission) > 1 ? $permissionName : $ownable->getMorphClass() . '-' . $permissionName; $user = $this->currentUser(); $userRoleIds = $this->getCurrentUserRoleIds(); @@ -75,12 +76,13 @@ class PermissionApplicator * Checks if a user has the given permission for any items in the system. * Can be passed an entity instance to filter on a specific type. */ - public function checkUserHasEntityPermissionOnAny(string $action, string $entityClass = ''): bool + public function checkUserHasEntityPermissionOnAny(string|Permission $action, string $entityClass = ''): bool { - $this->ensureValidEntityAction($action); + $permissionName = is_string($action) ? $action : $action->value; + $this->ensureValidEntityAction($permissionName); $permissionQuery = EntityPermission::query() - ->where($action, '=', true) + ->where($permissionName, '=', true) ->whereIn('role_id', $this->getCurrentUserRoleIds()); if (!empty($entityClass)) { @@ -235,8 +237,13 @@ class PermissionApplicator */ protected function ensureValidEntityAction(string $action): void { - if (!in_array($action, EntityPermission::PERMISSIONS)) { - throw new InvalidArgumentException('Action should be a simple entity permission action, not a role permission'); + $allowed = Permission::genericForEntity(); + foreach ($allowed as $permission) { + if ($permission->value === $action) { + return; + } } + + throw new InvalidArgumentException('Action should be a simple entity permission action, not a role permission'); } } diff --git a/app/Permissions/PermissionsController.php b/app/Permissions/PermissionsController.php index 9dcfe242e..d93d9e718 100644 --- a/app/Permissions/PermissionsController.php +++ b/app/Permissions/PermissionsController.php @@ -24,7 +24,7 @@ class PermissionsController extends Controller public function showForPage(string $bookSlug, string $pageSlug) { $page = $this->queries->pages->findVisibleBySlugsOrFail($bookSlug, $pageSlug); - $this->checkOwnablePermission('restrictions-manage', $page); + $this->checkOwnablePermission(Permission::RestrictionsManage, $page); $this->setPageTitle(trans('entities.pages_permissions')); return view('pages.permissions', [ @@ -39,7 +39,7 @@ class PermissionsController extends Controller public function updateForPage(Request $request, string $bookSlug, string $pageSlug) { $page = $this->queries->pages->findVisibleBySlugsOrFail($bookSlug, $pageSlug); - $this->checkOwnablePermission('restrictions-manage', $page); + $this->checkOwnablePermission(Permission::RestrictionsManage, $page); (new DatabaseTransaction(function () use ($page, $request) { $this->permissionsUpdater->updateFromPermissionsForm($page, $request); @@ -56,7 +56,7 @@ class PermissionsController extends Controller public function showForChapter(string $bookSlug, string $chapterSlug) { $chapter = $this->queries->chapters->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); - $this->checkOwnablePermission('restrictions-manage', $chapter); + $this->checkOwnablePermission(Permission::RestrictionsManage, $chapter); $this->setPageTitle(trans('entities.chapters_permissions')); return view('chapters.permissions', [ @@ -71,7 +71,7 @@ class PermissionsController extends Controller public function updateForChapter(Request $request, string $bookSlug, string $chapterSlug) { $chapter = $this->queries->chapters->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); - $this->checkOwnablePermission('restrictions-manage', $chapter); + $this->checkOwnablePermission(Permission::RestrictionsManage, $chapter); (new DatabaseTransaction(function () use ($chapter, $request) { $this->permissionsUpdater->updateFromPermissionsForm($chapter, $request); @@ -88,7 +88,7 @@ class PermissionsController extends Controller public function showForBook(string $slug) { $book = $this->queries->books->findVisibleBySlugOrFail($slug); - $this->checkOwnablePermission('restrictions-manage', $book); + $this->checkOwnablePermission(Permission::RestrictionsManage, $book); $this->setPageTitle(trans('entities.books_permissions')); return view('books.permissions', [ @@ -103,7 +103,7 @@ class PermissionsController extends Controller public function updateForBook(Request $request, string $slug) { $book = $this->queries->books->findVisibleBySlugOrFail($slug); - $this->checkOwnablePermission('restrictions-manage', $book); + $this->checkOwnablePermission(Permission::RestrictionsManage, $book); (new DatabaseTransaction(function () use ($book, $request) { $this->permissionsUpdater->updateFromPermissionsForm($book, $request); @@ -120,7 +120,7 @@ class PermissionsController extends Controller public function showForShelf(string $slug) { $shelf = $this->queries->shelves->findVisibleBySlugOrFail($slug); - $this->checkOwnablePermission('restrictions-manage', $shelf); + $this->checkOwnablePermission(Permission::RestrictionsManage, $shelf); $this->setPageTitle(trans('entities.shelves_permissions')); return view('shelves.permissions', [ @@ -135,7 +135,7 @@ class PermissionsController extends Controller public function updateForShelf(Request $request, string $slug) { $shelf = $this->queries->shelves->findVisibleBySlugOrFail($slug); - $this->checkOwnablePermission('restrictions-manage', $shelf); + $this->checkOwnablePermission(Permission::RestrictionsManage, $shelf); (new DatabaseTransaction(function () use ($shelf, $request) { $this->permissionsUpdater->updateFromPermissionsForm($shelf, $request); @@ -152,7 +152,7 @@ class PermissionsController extends Controller public function copyShelfPermissionsToBooks(string $slug) { $shelf = $this->queries->shelves->findVisibleBySlugOrFail($slug); - $this->checkOwnablePermission('restrictions-manage', $shelf); + $this->checkOwnablePermission(Permission::RestrictionsManage, $shelf); $updateCount = (new DatabaseTransaction(function () use ($shelf) { return $this->permissionsUpdater->updateBookPermissionsFromShelf($shelf); @@ -168,7 +168,7 @@ class PermissionsController extends Controller */ public function formRowForRole(string $entityType, string $roleId) { - $this->checkPermissionOr('restrictions-manage-all', fn() => userCan('restrictions-manage-own')); + $this->checkPermissionOr(Permission::RestrictionsManageAll, fn() => userCan(Permission::RestrictionsManageOwn)); $role = Role::query()->findOrFail($roleId); diff --git a/app/Settings/MaintenanceController.php b/app/Settings/MaintenanceController.php index ac9dd20cc..b2b2226bf 100644 --- a/app/Settings/MaintenanceController.php +++ b/app/Settings/MaintenanceController.php @@ -6,6 +6,7 @@ use BookStack\Activity\ActivityType; use BookStack\App\AppVersion; use BookStack\Entities\Tools\TrashCan; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\References\ReferenceStore; use BookStack\Uploads\ImageService; use Illuminate\Http\Request; @@ -17,7 +18,7 @@ class MaintenanceController extends Controller */ public function index(TrashCan $trashCan) { - $this->checkPermission('settings-manage'); + $this->checkPermission(Permission::SettingsManage); $this->setPageTitle(trans('settings.maint')); // Recycle bin details @@ -34,7 +35,7 @@ class MaintenanceController extends Controller */ public function cleanupImages(Request $request, ImageService $imageService) { - $this->checkPermission('settings-manage'); + $this->checkPermission(Permission::SettingsManage); $this->logActivity(ActivityType::MAINTENANCE_ACTION_RUN, 'cleanup-images'); $checkRevisions = !($request->get('ignore_revisions', 'false') === 'true'); @@ -62,7 +63,7 @@ class MaintenanceController extends Controller */ public function sendTestEmail() { - $this->checkPermission('settings-manage'); + $this->checkPermission(Permission::SettingsManage); $this->logActivity(ActivityType::MAINTENANCE_ACTION_RUN, 'send-test-email'); try { @@ -81,7 +82,7 @@ class MaintenanceController extends Controller */ public function regenerateReferences(ReferenceStore $referenceStore) { - $this->checkPermission('settings-manage'); + $this->checkPermission(Permission::SettingsManage); $this->logActivity(ActivityType::MAINTENANCE_ACTION_RUN, 'regenerate-references'); try { diff --git a/app/Settings/SettingController.php b/app/Settings/SettingController.php index 3b7ba74d5..f89bc6dab 100644 --- a/app/Settings/SettingController.php +++ b/app/Settings/SettingController.php @@ -5,6 +5,7 @@ namespace BookStack\Settings; use BookStack\Activity\ActivityType; use BookStack\App\AppVersion; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Users\Models\User; use Illuminate\Http\Request; @@ -24,7 +25,7 @@ class SettingController extends Controller public function category(string $category) { $this->ensureCategoryExists($category); - $this->checkPermission('settings-manage'); + $this->checkPermission(Permission::SettingsManage); $this->setPageTitle(trans('settings.settings')); return view('settings.categories.' . $category, [ @@ -41,7 +42,7 @@ class SettingController extends Controller { $this->ensureCategoryExists($category); $this->preventAccessInDemoMode(); - $this->checkPermission('settings-manage'); + $this->checkPermission(Permission::SettingsManage); $this->validate($request, [ 'app_logo' => ['nullable', ...$this->getImageValidationRules()], 'app_icon' => ['nullable', ...$this->getImageValidationRules()], diff --git a/app/Sorting/BookSortController.php b/app/Sorting/BookSortController.php index d70d0e656..7e2ee5465 100644 --- a/app/Sorting/BookSortController.php +++ b/app/Sorting/BookSortController.php @@ -7,6 +7,7 @@ use BookStack\Entities\Queries\BookQueries; use BookStack\Entities\Tools\BookContents; use BookStack\Facades\Activity; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Util\DatabaseTransaction; use Illuminate\Http\Request; @@ -23,7 +24,7 @@ class BookSortController extends Controller public function show(string $bookSlug) { $book = $this->queries->findVisibleBySlugOrFail($bookSlug); - $this->checkOwnablePermission('book-update', $book); + $this->checkOwnablePermission(Permission::BookUpdate, $book); $bookChildren = (new BookContents($book))->getTree(false); @@ -51,7 +52,7 @@ class BookSortController extends Controller public function update(Request $request, BookSorter $sorter, string $bookSlug) { $book = $this->queries->findVisibleBySlugOrFail($bookSlug); - $this->checkOwnablePermission('book-update', $book); + $this->checkOwnablePermission(Permission::BookUpdate, $book); $loggedActivityForBook = false; // Sort via map diff --git a/app/Sorting/BookSorter.php b/app/Sorting/BookSorter.php index e627d66fd..1152101d2 100644 --- a/app/Sorting/BookSorter.php +++ b/app/Sorting/BookSorter.php @@ -8,6 +8,7 @@ use BookStack\Entities\Models\Chapter; use BookStack\Entities\Models\Entity; use BookStack\Entities\Models\Page; use BookStack\Entities\Queries\EntityQueries; +use BookStack\Permissions\Permission; class BookSorter { @@ -187,11 +188,11 @@ class BookSorter $hasNewParent = $newBook->id !== $model->book_id || ($model instanceof Page && $model->chapter_id !== ($sortMapItem->parentChapterId ?? 0)); if ($model instanceof Chapter) { - $hasPermission = userCan('book-update', $currentParent) - && userCan('book-update', $newBook) - && userCan('chapter-update', $model) - && (!$hasNewParent || userCan('chapter-create', $newBook)) - && (!$hasNewParent || userCan('chapter-delete', $model)); + $hasPermission = userCan(Permission::BookUpdate, $currentParent) + && userCan(Permission::BookUpdate, $newBook) + && userCan(Permission::ChapterUpdate, $model) + && (!$hasNewParent || userCan(Permission::ChapterCreate, $newBook)) + && (!$hasNewParent || userCan(Permission::ChapterDelete, $model)); if (!$hasPermission) { return false; @@ -210,13 +211,13 @@ class BookSorter return false; } - $hasPageEditPermission = userCan('page-update', $model); + $hasPageEditPermission = userCan(Permission::PageUpdate, $model); $newParentInRightLocation = ($newParent instanceof Book || ($newParent instanceof Chapter && $newParent->book_id === $newBook->id)); $newParentPermission = ($newParent instanceof Chapter) ? 'chapter-update' : 'book-update'; $hasNewParentPermission = userCan($newParentPermission, $newParent); - $hasDeletePermissionIfMoving = (!$hasNewParent || userCan('page-delete', $model)); - $hasCreatePermissionIfMoving = (!$hasNewParent || userCan('page-create', $newParent)); + $hasDeletePermissionIfMoving = (!$hasNewParent || userCan(Permission::PageDelete, $model)); + $hasCreatePermissionIfMoving = (!$hasNewParent || userCan(Permission::PageCreate, $newParent)); $hasPermission = $hasCurrentParentPermission && $newParentInRightLocation diff --git a/app/Sorting/SortRuleController.php b/app/Sorting/SortRuleController.php index a124ffa9c..bb5540a2a 100644 --- a/app/Sorting/SortRuleController.php +++ b/app/Sorting/SortRuleController.php @@ -4,13 +4,14 @@ namespace BookStack\Sorting; use BookStack\Activity\ActivityType; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use Illuminate\Http\Request; class SortRuleController extends Controller { public function __construct() { - $this->middleware('can:settings-manage'); + $this->middleware(Permission::SettingsManage->middleware()); } public function create() diff --git a/app/Uploads/Controllers/AttachmentApiController.php b/app/Uploads/Controllers/AttachmentApiController.php index 87e00257c..b47d6ff8d 100644 --- a/app/Uploads/Controllers/AttachmentApiController.php +++ b/app/Uploads/Controllers/AttachmentApiController.php @@ -5,6 +5,7 @@ namespace BookStack\Uploads\Controllers; use BookStack\Entities\Queries\PageQueries; use BookStack\Exceptions\FileUploadException; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use BookStack\Uploads\Attachment; use BookStack\Uploads\AttachmentService; use Exception; @@ -45,12 +46,12 @@ class AttachmentApiController extends ApiController */ public function create(Request $request) { - $this->checkPermission('attachment-create-all'); + $this->checkPermission(Permission::AttachmentCreateAll); $requestData = $this->validate($request, $this->rules()['create']); $pageId = $request->get('uploaded_to'); $page = $this->pageQueries->findVisibleByIdOrFail($pageId); - $this->checkOwnablePermission('page-update', $page); + $this->checkOwnablePermission(Permission::PageUpdate, $page); if ($request->hasFile('file')) { $uploadedFile = $request->file('file'); @@ -137,9 +138,9 @@ class AttachmentApiController extends ApiController $attachment->uploaded_to = $requestData['uploaded_to']; } - $this->checkOwnablePermission('page-view', $page); - $this->checkOwnablePermission('page-update', $page); - $this->checkOwnablePermission('attachment-update', $attachment); + $this->checkOwnablePermission(Permission::PageView, $page); + $this->checkOwnablePermission(Permission::PageUpdate, $page); + $this->checkOwnablePermission(Permission::AttachmentUpdate, $attachment); if ($request->hasFile('file')) { $uploadedFile = $request->file('file'); @@ -160,7 +161,7 @@ class AttachmentApiController extends ApiController { /** @var Attachment $attachment */ $attachment = Attachment::visible()->findOrFail($id); - $this->checkOwnablePermission('attachment-delete', $attachment); + $this->checkOwnablePermission(Permission::AttachmentDelete, $attachment); $this->attachmentService->deleteFile($attachment); diff --git a/app/Uploads/Controllers/AttachmentController.php b/app/Uploads/Controllers/AttachmentController.php index 809cdfa58..0886193e4 100644 --- a/app/Uploads/Controllers/AttachmentController.php +++ b/app/Uploads/Controllers/AttachmentController.php @@ -7,6 +7,7 @@ use BookStack\Entities\Repos\PageRepo; use BookStack\Exceptions\FileUploadException; use BookStack\Exceptions\NotFoundException; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Uploads\Attachment; use BookStack\Uploads\AttachmentService; use Exception; @@ -40,8 +41,8 @@ class AttachmentController extends Controller $pageId = $request->get('uploaded_to'); $page = $this->pageQueries->findVisibleByIdOrFail($pageId); - $this->checkPermission('attachment-create-all'); - $this->checkOwnablePermission('page-update', $page); + $this->checkPermission(Permission::AttachmentCreateAll); + $this->checkOwnablePermission(Permission::PageUpdate, $page); $uploadedFile = $request->file('file'); @@ -67,9 +68,9 @@ class AttachmentController extends Controller /** @var Attachment $attachment */ $attachment = Attachment::query()->findOrFail($attachmentId); - $this->checkOwnablePermission('view', $attachment->page); - $this->checkOwnablePermission('page-update', $attachment->page); - $this->checkOwnablePermission('attachment-create', $attachment); + $this->checkOwnablePermission(Permission::PageView, $attachment->page); + $this->checkOwnablePermission(Permission::PageUpdate, $attachment->page); + $this->checkOwnablePermission(Permission::AttachmentUpdate, $attachment); $uploadedFile = $request->file('file'); @@ -90,8 +91,8 @@ class AttachmentController extends Controller /** @var Attachment $attachment */ $attachment = Attachment::query()->findOrFail($attachmentId); - $this->checkOwnablePermission('page-update', $attachment->page); - $this->checkOwnablePermission('attachment-create', $attachment); + $this->checkOwnablePermission(Permission::PageUpdate, $attachment->page); + $this->checkOwnablePermission(Permission::AttachmentCreate, $attachment); return view('attachments.manager-edit-form', [ 'attachment' => $attachment, @@ -118,9 +119,9 @@ class AttachmentController extends Controller ]), 422); } - $this->checkOwnablePermission('page-view', $attachment->page); - $this->checkOwnablePermission('page-update', $attachment->page); - $this->checkOwnablePermission('attachment-update', $attachment); + $this->checkOwnablePermission(Permission::PageView, $attachment->page); + $this->checkOwnablePermission(Permission::PageUpdate, $attachment->page); + $this->checkOwnablePermission(Permission::AttachmentUpdate, $attachment); $attachment = $this->attachmentService->updateFile($attachment, [ 'name' => $request->get('attachment_edit_name'), @@ -156,8 +157,8 @@ class AttachmentController extends Controller $page = $this->pageQueries->findVisibleByIdOrFail($pageId); - $this->checkPermission('attachment-create-all'); - $this->checkOwnablePermission('page-update', $page); + $this->checkPermission(Permission::AttachmentCreateAll); + $this->checkOwnablePermission(Permission::PageUpdate, $page); $attachmentName = $request->get('attachment_link_name'); $link = $request->get('attachment_link_url'); @@ -176,7 +177,6 @@ class AttachmentController extends Controller public function listForPage(int $pageId) { $page = $this->pageQueries->findVisibleByIdOrFail($pageId); - $this->checkOwnablePermission('page-view', $page); return view('attachments.manager-list', [ 'attachments' => $page->attachments->all(), @@ -195,7 +195,7 @@ class AttachmentController extends Controller 'order' => ['required', 'array'], ]); $page = $this->pageQueries->findVisibleByIdOrFail($pageId); - $this->checkOwnablePermission('page-update', $page); + $this->checkOwnablePermission(Permission::PageUpdate, $page); $attachmentOrder = $request->get('order'); $this->attachmentService->updateFileOrderWithinPage($attachmentOrder, $pageId); @@ -220,7 +220,7 @@ class AttachmentController extends Controller throw new NotFoundException(trans('errors.attachment_not_found')); } - $this->checkOwnablePermission('page-view', $page); + $this->checkOwnablePermission(Permission::PageView, $page); if ($attachment->external) { return redirect($attachment->path); @@ -246,7 +246,7 @@ class AttachmentController extends Controller { /** @var Attachment $attachment */ $attachment = Attachment::query()->findOrFail($attachmentId); - $this->checkOwnablePermission('attachment-delete', $attachment); + $this->checkOwnablePermission(Permission::AttachmentDelete, $attachment); $this->attachmentService->deleteFile($attachment); return response()->json(['message' => trans('entities.attachments_deleted')]); diff --git a/app/Uploads/Controllers/DrawioImageController.php b/app/Uploads/Controllers/DrawioImageController.php index 6293da4f7..f44acd997 100644 --- a/app/Uploads/Controllers/DrawioImageController.php +++ b/app/Uploads/Controllers/DrawioImageController.php @@ -4,6 +4,7 @@ namespace BookStack\Uploads\Controllers; use BookStack\Exceptions\ImageUploadException; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Uploads\ImageRepo; use BookStack\Uploads\ImageResizer; use BookStack\Util\OutOfMemoryHandler; @@ -57,7 +58,7 @@ class DrawioImageController extends Controller 'uploaded_to' => ['required', 'integer'], ]); - $this->checkPermission('image-create-all'); + $this->checkPermission(Permission::ImageCreateAll); $imageBase64Data = $request->get('image'); try { @@ -81,7 +82,7 @@ class DrawioImageController extends Controller return $this->jsonError(trans('errors.drawing_data_not_found'), 404); } - if ($image->type !== 'drawio' || !userCan('page-view', $image->getPage())) { + if ($image->type !== 'drawio' || !userCan(Permission::PageView, $image->getPage())) { return $this->jsonError(trans('errors.drawing_data_not_found'), 404); } diff --git a/app/Uploads/Controllers/GalleryImageController.php b/app/Uploads/Controllers/GalleryImageController.php index 1bc9da2d7..745efcde8 100644 --- a/app/Uploads/Controllers/GalleryImageController.php +++ b/app/Uploads/Controllers/GalleryImageController.php @@ -4,6 +4,7 @@ namespace BookStack\Uploads\Controllers; use BookStack\Exceptions\ImageUploadException; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Uploads\ImageRepo; use BookStack\Uploads\ImageResizer; use BookStack\Util\OutOfMemoryHandler; @@ -52,7 +53,7 @@ class GalleryImageController extends Controller */ public function create(Request $request) { - $this->checkPermission('image-create-all'); + $this->checkPermission(Permission::ImageCreateAll); try { $this->validate($request, [ diff --git a/app/Uploads/Controllers/ImageController.php b/app/Uploads/Controllers/ImageController.php index c68ffdf6b..da67639c1 100644 --- a/app/Uploads/Controllers/ImageController.php +++ b/app/Uploads/Controllers/ImageController.php @@ -6,6 +6,7 @@ use BookStack\Exceptions\ImageUploadException; use BookStack\Exceptions\NotFoundException; use BookStack\Exceptions\NotifyException; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Uploads\Image; use BookStack\Uploads\ImageRepo; use BookStack\Uploads\ImageResizer; @@ -50,7 +51,7 @@ class ImageController extends Controller $image = $this->imageRepo->getById($id); $this->checkImagePermission($image); - $this->checkOwnablePermission('image-update', $image); + $this->checkOwnablePermission(Permission::ImageUpdate, $image); $image = $this->imageRepo->updateImageDetails($image, $data); @@ -71,7 +72,7 @@ class ImageController extends Controller $image = $this->imageRepo->getById($id); $this->checkImagePermission($image); - $this->checkOwnablePermission('image-update', $image); + $this->checkOwnablePermission(Permission::ImageUpdate, $image); $file = $request->file('file'); new OutOfMemoryHandler(function () { @@ -125,7 +126,7 @@ class ImageController extends Controller public function destroy(string $id) { $image = $this->imageRepo->getById($id); - $this->checkOwnablePermission('image-delete', $image); + $this->checkOwnablePermission(Permission::ImageDelete, $image); $this->checkImagePermission($image); $this->imageRepo->destroyImage($image); @@ -140,7 +141,7 @@ class ImageController extends Controller { $image = $this->imageRepo->getById($id); $this->checkImagePermission($image); - $this->checkOwnablePermission('image-update', $image); + $this->checkOwnablePermission(Permission::ImageUpdate, $image); new OutOfMemoryHandler(function () { return $this->jsonError(trans('errors.image_thumbnail_memory_limit')); @@ -163,7 +164,7 @@ class ImageController extends Controller $relatedPage = $image->getPage(); if ($relatedPage) { - $this->checkOwnablePermission('page-view', $relatedPage); + $this->checkOwnablePermission(Permission::PageView, $relatedPage); } } } diff --git a/app/Uploads/Controllers/ImageGalleryApiController.php b/app/Uploads/Controllers/ImageGalleryApiController.php index d2a750c45..59696dc9a 100644 --- a/app/Uploads/Controllers/ImageGalleryApiController.php +++ b/app/Uploads/Controllers/ImageGalleryApiController.php @@ -4,6 +4,7 @@ namespace BookStack\Uploads\Controllers; use BookStack\Entities\Queries\PageQueries; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use BookStack\Uploads\Image; use BookStack\Uploads\ImageRepo; use BookStack\Uploads\ImageResizer; @@ -65,7 +66,7 @@ class ImageGalleryApiController extends ApiController */ public function create(Request $request) { - $this->checkPermission('image-create-all'); + $this->checkPermission(Permission::ImageCreateAll); $data = $this->validate($request, $this->rules()['create']); $page = $this->pageQueries->findVisibleByIdOrFail($data['uploaded_to']); @@ -102,8 +103,8 @@ class ImageGalleryApiController extends ApiController { $data = $this->validate($request, $this->rules()['update']); $image = $this->imageRepo->getById($id); - $this->checkOwnablePermission('page-view', $image->getPage()); - $this->checkOwnablePermission('image-update', $image); + $this->checkOwnablePermission(Permission::PageView, $image->getPage()); + $this->checkOwnablePermission(Permission::ImageUpdate, $image); $this->imageRepo->updateImageDetails($image, $data); if (isset($data['image'])) { @@ -121,8 +122,8 @@ class ImageGalleryApiController extends ApiController public function delete(string $id) { $image = $this->imageRepo->getById($id); - $this->checkOwnablePermission('page-view', $image->getPage()); - $this->checkOwnablePermission('image-delete', $image); + $this->checkOwnablePermission(Permission::PageView, $image->getPage()); + $this->checkOwnablePermission(Permission::ImageDelete, $image); $this->imageRepo->destroyImage($image); return response('', 204); diff --git a/app/Users/Controllers/RoleApiController.php b/app/Users/Controllers/RoleApiController.php index 2f3638cd3..93ecc549b 100644 --- a/app/Users/Controllers/RoleApiController.php +++ b/app/Users/Controllers/RoleApiController.php @@ -3,6 +3,7 @@ namespace BookStack\Users\Controllers; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use BookStack\Permissions\PermissionsRepo; use BookStack\Users\Models\Role; use Illuminate\Http\Request; @@ -10,8 +11,6 @@ use Illuminate\Support\Facades\DB; class RoleApiController extends ApiController { - protected PermissionsRepo $permissionsRepo; - protected array $fieldsToExpose = [ 'display_name', 'description', 'mfa_enforced', 'external_auth_id', 'created_at', 'updated_at', ]; @@ -35,13 +34,12 @@ class RoleApiController extends ApiController ] ]; - public function __construct(PermissionsRepo $permissionsRepo) - { - $this->permissionsRepo = $permissionsRepo; - + public function __construct( + protected PermissionsRepo $permissionsRepo + ) { // Checks for all endpoints in this controller $this->middleware(function ($request, $next) { - $this->checkPermission('user-roles-manage'); + $this->checkPermission(Permission::UserRolesManage); return $next($request); }); @@ -125,9 +123,9 @@ class RoleApiController extends ApiController } /** - * Format the given role model for single-result display. + * Format the given role model for a single-result display. */ - protected function singleFormatter(Role $role) + protected function singleFormatter(Role $role): void { $role->load('users:id,name,slug'); $role->unsetRelation('permissions'); diff --git a/app/Users/Controllers/RoleController.php b/app/Users/Controllers/RoleController.php index 0a7fdcc9b..549f6e0ac 100644 --- a/app/Users/Controllers/RoleController.php +++ b/app/Users/Controllers/RoleController.php @@ -4,6 +4,7 @@ namespace BookStack\Users\Controllers; use BookStack\Exceptions\PermissionsException; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Permissions\PermissionsRepo; use BookStack\Users\Models\Role; use BookStack\Users\Queries\RolesAllPaginatedAndSorted; @@ -23,7 +24,7 @@ class RoleController extends Controller */ public function index(Request $request) { - $this->checkPermission('user-roles-manage'); + $this->checkPermission(Permission::UserRolesManage); $listOptions = SimpleListOptions::fromRequest($request, 'roles')->withSortOptions([ 'display_name' => trans('common.sort_name'), @@ -49,7 +50,7 @@ class RoleController extends Controller */ public function create(Request $request) { - $this->checkPermission('user-roles-manage'); + $this->checkPermission(Permission::UserRolesManage); /** @var ?Role $role */ $role = null; @@ -71,7 +72,7 @@ class RoleController extends Controller */ public function store(Request $request) { - $this->checkPermission('user-roles-manage'); + $this->checkPermission(Permission::UserRolesManage); $data = $this->validate($request, [ 'display_name' => ['required', 'min:3', 'max:180'], 'description' => ['max:180'], @@ -92,7 +93,7 @@ class RoleController extends Controller */ public function edit(string $id) { - $this->checkPermission('user-roles-manage'); + $this->checkPermission(Permission::UserRolesManage); $role = $this->permissionsRepo->getRoleById($id); $this->setPageTitle(trans('settings.role_edit')); @@ -105,7 +106,7 @@ class RoleController extends Controller */ public function update(Request $request, string $id) { - $this->checkPermission('user-roles-manage'); + $this->checkPermission(Permission::UserRolesManage); $data = $this->validate($request, [ 'display_name' => ['required', 'min:3', 'max:180'], 'description' => ['max:180'], @@ -127,7 +128,7 @@ class RoleController extends Controller */ public function showDelete(string $id) { - $this->checkPermission('user-roles-manage'); + $this->checkPermission(Permission::UserRolesManage); $role = $this->permissionsRepo->getRoleById($id); $roles = $this->permissionsRepo->getAllRolesExcept($role); $blankRole = $role->newInstance(['display_name' => trans('settings.role_delete_no_migration')]); @@ -146,7 +147,7 @@ class RoleController extends Controller */ public function delete(Request $request, string $id) { - $this->checkPermission('user-roles-manage'); + $this->checkPermission(Permission::UserRolesManage); try { $migrateRoleId = intval($request->get('migrate_role_id') ?: "0"); diff --git a/app/Users/Controllers/UserAccountController.php b/app/Users/Controllers/UserAccountController.php index 708a91e9d..a8baba529 100644 --- a/app/Users/Controllers/UserAccountController.php +++ b/app/Users/Controllers/UserAccountController.php @@ -4,6 +4,7 @@ namespace BookStack\Users\Controllers; use BookStack\Access\SocialDriverManager; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Permissions\PermissionApplicator; use BookStack\Settings\UserNotificationPreferences; use BookStack\Settings\UserShortcutMap; @@ -62,9 +63,9 @@ class UserAccountController extends Controller 'profile_image' => array_merge(['nullable'], $this->getImageValidationRules()), ]); - $this->userRepo->update($user, $validated, userCan('users-manage')); + $this->userRepo->update($user, $validated, userCan(Permission::UsersManage)); - // Save profile image if in request + // Save the profile image if in request if ($request->hasFile('profile_image')) { $imageUpload = $request->file('profile_image'); $imageRepo->destroyImage($user->avatar); @@ -73,7 +74,7 @@ class UserAccountController extends Controller $user->save(); } - // Delete the profile image if reset option is in request + // Delete the profile image if the reset option is in request if ($request->has('profile_image_reset')) { $imageRepo->destroyImage($user->avatar); $user->image_id = 0; @@ -122,7 +123,7 @@ class UserAccountController extends Controller */ public function showNotifications(PermissionApplicator $permissions) { - $this->checkPermission('receive-notifications'); + $this->checkPermission(Permission::ReceiveNotifications); $preferences = (new UserNotificationPreferences(user())); @@ -145,7 +146,7 @@ class UserAccountController extends Controller public function updateNotifications(Request $request) { $this->preventAccessInDemoMode(); - $this->checkPermission('receive-notifications'); + $this->checkPermission(Permission::ReceiveNotifications); $data = $this->validate($request, [ 'preferences' => ['required', 'array'], 'preferences.*' => ['required', 'string'], @@ -218,7 +219,7 @@ class UserAccountController extends Controller $this->preventAccessInDemoMode(); $requestNewOwnerId = intval($request->get('new_owner_id')) ?: null; - $newOwnerId = userCan('users-manage') ? $requestNewOwnerId : null; + $newOwnerId = userCan(Permission::UsersManage) ? $requestNewOwnerId : null; $this->userRepo->destroy(user(), $newOwnerId); diff --git a/app/Users/Controllers/UserApiController.php b/app/Users/Controllers/UserApiController.php index 351893b03..9134b3cc1 100644 --- a/app/Users/Controllers/UserApiController.php +++ b/app/Users/Controllers/UserApiController.php @@ -4,9 +4,9 @@ namespace BookStack\Users\Controllers; use BookStack\Exceptions\UserUpdateException; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use BookStack\Users\Models\User; use BookStack\Users\UserRepo; -use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Validation\Rules\Password; @@ -26,7 +26,7 @@ class UserApiController extends ApiController // Checks for all endpoints in this controller $this->middleware(function ($request, $next) { - $this->checkPermission('users-manage'); + $this->checkPermission(Permission::UsersManage); $this->preventAccessInDemoMode(); return $next($request); @@ -125,7 +125,7 @@ class UserApiController extends ApiController { $data = $this->validate($request, $this->rules($id)['update']); $user = $this->userRepo->getById($id); - $this->userRepo->update($user, $data, userCan('users-manage')); + $this->userRepo->update($user, $data, userCan(Permission::UsersManage)); $this->singleFormatter($user); return response()->json($user); diff --git a/app/Users/Controllers/UserController.php b/app/Users/Controllers/UserController.php index c6e4326e9..494221b14 100644 --- a/app/Users/Controllers/UserController.php +++ b/app/Users/Controllers/UserController.php @@ -7,6 +7,7 @@ use BookStack\Access\UserInviteException; use BookStack\Exceptions\ImageUploadException; use BookStack\Exceptions\UserUpdateException; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Uploads\ImageRepo; use BookStack\Users\Models\Role; use BookStack\Users\Queries\UsersAllPaginatedAndSorted; @@ -32,7 +33,7 @@ class UserController extends Controller */ public function index(Request $request) { - $this->checkPermission('users-manage'); + $this->checkPermission(Permission::UsersManage); $listOptions = SimpleListOptions::fromRequest($request, 'users')->withSortOptions([ 'name' => trans('common.sort_name'), @@ -58,7 +59,7 @@ class UserController extends Controller */ public function create() { - $this->checkPermission('users-manage'); + $this->checkPermission(Permission::UsersManage); $authMethod = config('auth.method'); $roles = Role::query()->orderBy('display_name', 'asc')->get(); $this->setPageTitle(trans('settings.users_add_new')); @@ -73,7 +74,7 @@ class UserController extends Controller */ public function store(Request $request) { - $this->checkPermission('users-manage'); + $this->checkPermission(Permission::UsersManage); $authMethod = config('auth.method'); $sendInvite = ($request->get('send_invite', 'false') === 'true'); @@ -111,7 +112,7 @@ class UserController extends Controller */ public function edit(int $id, SocialDriverManager $socialDriverManager) { - $this->checkPermission('users-manage'); + $this->checkPermission(Permission::UsersManage); $user = $this->userRepo->getById($id); $user->load(['apiTokens', 'mfaValues']); @@ -141,7 +142,7 @@ class UserController extends Controller public function update(Request $request, int $id) { $this->preventAccessInDemoMode(); - $this->checkPermission('users-manage'); + $this->checkPermission(Permission::UsersManage); $validated = $this->validate($request, [ 'name' => ['min:1', 'max:100'], @@ -182,7 +183,7 @@ class UserController extends Controller */ public function delete(int $id) { - $this->checkPermission('users-manage'); + $this->checkPermission(Permission::UsersManage); $user = $this->userRepo->getById($id); $this->setPageTitle(trans('settings.users_delete_named', ['userName' => $user->name])); @@ -198,7 +199,7 @@ class UserController extends Controller public function destroy(Request $request, int $id) { $this->preventAccessInDemoMode(); - $this->checkPermission('users-manage'); + $this->checkPermission(Permission::UsersManage); $user = $this->userRepo->getById($id); $newOwnerId = intval($request->get('new_owner_id')) ?: null; diff --git a/app/Users/Controllers/UserSearchController.php b/app/Users/Controllers/UserSearchController.php index b6f37bce0..a2543b7ee 100644 --- a/app/Users/Controllers/UserSearchController.php +++ b/app/Users/Controllers/UserSearchController.php @@ -3,6 +3,7 @@ namespace BookStack\Users\Controllers; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Users\Models\User; use Illuminate\Http\Request; @@ -15,9 +16,9 @@ class UserSearchController extends Controller public function forSelect(Request $request) { $hasPermission = !user()->isGuest() && ( - userCan('users-manage') - || userCan('restrictions-manage-own') - || userCan('restrictions-manage-all') + userCan(Permission::UsersManage) + || userCan(Permission::RestrictionsManageOwn) + || userCan(Permission::RestrictionsManageAll) ); if (!$hasPermission) { diff --git a/app/Users/Models/Role.php b/app/Users/Models/Role.php index 912ae81c9..92be22458 100644 --- a/app/Users/Models/Role.php +++ b/app/Users/Models/Role.php @@ -57,7 +57,8 @@ class Role extends Model implements Loggable */ public function permissions(): BelongsToMany { - return $this->belongsToMany(RolePermission::class, 'permission_role', 'role_id', 'permission_id'); + return $this->belongsToMany(RolePermission::class, 'permission_role', 'role_id', 'permission_id') + ->select(['id', 'name']); } /** diff --git a/app/Users/Models/User.php b/app/Users/Models/User.php index f83e12088..0017653b5 100644 --- a/app/Users/Models/User.php +++ b/app/Users/Models/User.php @@ -12,6 +12,7 @@ use BookStack\Api\ApiToken; use BookStack\App\Model; use BookStack\App\SluggableInterface; use BookStack\Entities\Tools\SlugGenerator; +use BookStack\Permissions\Permission; use BookStack\Translation\LocaleDefinition; use BookStack\Translation\LocaleManager; use BookStack\Uploads\Image; @@ -26,7 +27,6 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Notifications\Notifiable; use Illuminate\Support\Collection; @@ -156,8 +156,9 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon /** * Check if the user has a particular permission. */ - public function can(string $permissionName): bool + public function can(string|Permission $permission): bool { + $permissionName = is_string($permission) ? $permission : $permission->value; return $this->permissions()->contains($permissionName); } @@ -181,9 +182,9 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon } /** - * Clear any cached permissions on this instance. + * Clear any cached permissions in this instance. */ - public function clearPermissionCache() + public function clearPermissionCache(): void { $this->permissions = null; } @@ -191,7 +192,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon /** * Attach a role to this user. */ - public function attachRole(Role $role) + public function attachRole(Role $role): void { $this->roles()->attach($role->id); $this->unsetRelation('roles'); @@ -207,15 +208,11 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon /** * Check if the user has a social account, - * If a driver is passed it checks for that single account type. - * - * @param bool|string $socialDriver - * - * @return bool + * If a driver is passed, it checks for that single account type. */ - public function hasSocialAccount($socialDriver = false) + public function hasSocialAccount(string $socialDriver = ''): bool { - if ($socialDriver === false) { + if (empty($socialDriver)) { return $this->socialAccounts()->count() > 0; } diff --git a/database/migrations/2025_09_02_111542_remove_comments_text_column.php b/database/migrations/2025_09_02_111542_remove_unused_columns.php similarity index 61% rename from database/migrations/2025_09_02_111542_remove_comments_text_column.php rename to database/migrations/2025_09_02_111542_remove_unused_columns.php index 8caa05e54..3a36e5ad6 100644 --- a/database/migrations/2025_09_02_111542_remove_comments_text_column.php +++ b/database/migrations/2025_09_02_111542_remove_unused_columns.php @@ -14,6 +14,11 @@ return new class extends Migration Schema::table('comments', function (Blueprint $table) { $table->dropColumn('text'); }); + + Schema::table('role_permissions', function (Blueprint $table) { + $table->dropColumn('display_name'); + $table->dropColumn('description'); + }); } /** @@ -24,5 +29,10 @@ return new class extends Migration Schema::table('comments', function (Blueprint $table) { $table->longText('text')->nullable(); }); + + Schema::table('role_permissions', function (Blueprint $table) { + $table->string('display_name')->nullable(); + $table->string('description')->nullable(); + }); } }; diff --git a/resources/views/attachments/manager-list.blade.php b/resources/views/attachments/manager-list.blade.php index 6314aa7b5..10ede4aae 100644 --- a/resources/views/attachments/manager-list.blade.php +++ b/resources/views/attachments/manager-list.blade.php @@ -16,7 +16,7 @@ type="button" title="{{ trans('entities.attachments_insert_link') }}" class="drag-card-action text-center text-link">@icon('link') - @if(userCan('attachment-update', $attachment)) + @if(userCan(\BookStack\Permissions\Permission::AttachmentUpdate, $attachment)) @endif - @if(userCan('attachment-delete', $attachment)) + @if(userCan(\BookStack\Permissions\Permission::AttachmentDelete, $attachment))
{{ trans('entities.books_empty') }}
- @if(userCan('book-create-all')) + @if(userCan(\BookStack\Permissions\Permission::BookCreateAll)){{ trans('entities.books_empty_contents') }}
{{ trans('entities.chapters_empty') }}