From 8948cfdb9ca7449b7bdac74ce8e0d359a3b758a4 Mon Sep 17 00:00:00 2001 From: doefom Date: Sun, 26 May 2024 11:03:30 +0200 Subject: [PATCH] - Move EntryRepository from Contracts to Stache directory - Remove custom permissions - Introduce customizable restriction check - Adjust EntryQueryBuilder and EntryPolicy to new restriction logic --- README.md | 2 +- src/Facades/Restrict.php | 18 +++++ src/Policies/EntryPolicy.php | 7 +- src/Restrict.php | 66 +++++++++++++++++++ src/ServiceProvider.php | 15 ++--- src/Stache/Query/EntryQueryBuilder.php | 64 ++---------------- .../Repositories}/EntryRepository.php | 2 +- 7 files changed, 101 insertions(+), 73 deletions(-) create mode 100644 src/Facades/Restrict.php create mode 100644 src/Restrict.php rename src/{Contracts/Entries => Stache/Repositories}/EntryRepository.php (88%) diff --git a/README.md b/README.md index 520076b..5208858 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ _Restrict_ works by overriding and rebinding some classes in the addon's Service ```php Statamic::repository( \Statamic\Contracts\Entries\EntryRepository::class, - \Doefom\Restrict\Contracts\Entries\EntryRepository::class + \Doefom\Restrict\Stache\Repositories\EntryRepository::class ); $this->app->bind( diff --git a/src/Facades/Restrict.php b/src/Facades/Restrict.php new file mode 100644 index 0000000..a2236a1 --- /dev/null +++ b/src/Facades/Restrict.php @@ -0,0 +1,18 @@ +hasAnotherAuthor($user, $entry)) { - return $default && $user->hasPermission("view other authors' {$entry->collectionHandle()} entries"); + if (! Restrict::isRestricted($user)) { + return $default; } - return $default; + return $default && Restrict::isAuthorized($user, $entry); } } diff --git a/src/Restrict.php b/src/Restrict.php new file mode 100644 index 0000000..7ad9d13 --- /dev/null +++ b/src/Restrict.php @@ -0,0 +1,66 @@ +callback = fn (User $user, Entry $entry) => true; + } + + /** + * Set the restriction callback used to determine if the current user is authorized to view the entry. + * The callback expects a User and Entry instance and should return a boolean. + * + * @param callable $callback + * @return void + */ + public function setRestriction(callable $callback): void + { + $this->callback = $callback; + } + + /** + * Check if the current user is authorized to view the entry. + * + * @param User $user + * @param Entry $entry + * @return bool + */ + public function isAuthorized(User $user, Entry $entry): bool + { + return call_user_func($this->callback, $user, $entry); + } + + /** + * Determine if the current user and route require restriction checks. Restriction + * checks are only required for authenticated CP routes and non-super users. + * + * @param User|null $user + * @return bool + */ + public function isRestricted(?User $user): bool + { + return $this->isAuthenticatedCpRoute() && $user && ! $user->isSuper(); + } + + /** + * Check if the current route is a CP route that requires authentication by checking + * if the 'statamic.cp.authenticated' middleware is applied to the route. + * + * @return bool + */ + protected function isAuthenticatedCpRoute(): bool + { + return in_array('statamic.cp.authenticated', Route::current()->gatherMiddleware()); + } +} diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 0affe09..27cac70 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -2,17 +2,21 @@ namespace Doefom\Restrict; -use Statamic\Facades\Permission; use Statamic\Providers\AddonServiceProvider; use Statamic\Statamic; class ServiceProvider extends AddonServiceProvider { + public function register() + { + $this->app->singleton('restrict', fn() => new Restrict); + } + public function bootAddon(): void { Statamic::repository( \Statamic\Contracts\Entries\EntryRepository::class, - \Doefom\Restrict\Contracts\Entries\EntryRepository::class + \Doefom\Restrict\Stache\Repositories\EntryRepository::class ); // Note: This is just a precaution, as the EntryRepository would already make sure that unauthorized entries @@ -21,12 +25,5 @@ public function bootAddon(): void \Statamic\Policies\EntryPolicy::class, \Doefom\Restrict\Policies\EntryPolicy::class ); - - Permission::extend(function () { - Permission::get('view {collection} entries')->addChild( - Permission::make("view other authors' {collection} entries") - ->label("View other authors' entries") - ); - }); } } diff --git a/src/Stache/Query/EntryQueryBuilder.php b/src/Stache/Query/EntryQueryBuilder.php index ebd57db..b3f41c8 100644 --- a/src/Stache/Query/EntryQueryBuilder.php +++ b/src/Stache/Query/EntryQueryBuilder.php @@ -2,83 +2,29 @@ namespace Doefom\Restrict\Stache\Query; -use Illuminate\Support\Facades\Route; -use Statamic\Contracts\Auth\User as UserContract; -use Statamic\Entries\Entry; +use Doefom\Restrict\Facades\Restrict; use Statamic\Facades\User; use Statamic\Stache\Query\EntryQueryBuilder as StatamicEntryQueryBuilder; -use Statamic\Support\Str; class EntryQueryBuilder extends StatamicEntryQueryBuilder { + protected function getFilteredKeys() { $resKeys = parent::getFilteredKeys(); $user = User::current(); - if ($user === null) { + if (! Restrict::isRestricted($user)) { return $resKeys; } - // Only apply the restriction if: - // 1. The current route is a CP route that requires authentication - // 2. The current user is not a super user - if (! $this->isAuthenticatedCpRoute() || $user->isSuper()) { - return $resKeys; - } - - // Note: At this point, we know there is an authenticated user. + // Note: At this point, we know there is an authenticated user that has restricted access. return $resKeys->filter(function ($key) use ($user) { $entry = $this->store->getItem($key); - return $this->isAuthorized($user, $entry); + return Restrict::isAuthorized($user, $entry); }); } - /** - * Check if the current user is authorized to view the entry. This is done by checking if the user has permission to - * view other authors' entries in the entry's collection or if the user is the author of the entry. - */ - protected function isAuthorized(UserContract $user, Entry $entry): bool - { - if ($this->hasAnotherAuthor($user, $entry)) { - return $this->isInAuthorizedCollections($user, $entry); - } - - return true; - } - - protected function isInAuthorizedCollections(UserContract $user, Entry $entry): bool - { - // Get all collections where the user has permission to view other authors' entries - $authorizedCollections = $user->permissions() - ->filter(fn ($permission) => Str::contains($permission, "view other authors'")) - ->map(function ($permission) { - return Str::between($permission, "view other authors' ", ' entries'); - }) - ->values() - ->toArray(); - - // Check if the entry's collection is in the list of authorized collections - return in_array($entry->collectionHandle(), $authorizedCollections); - } - - /** - * Check if the current route is a CP route that requires authentication by checking if the - * 'statamic.cp.authenticated' middleware is applied to the route. - */ - protected function isAuthenticatedCpRoute(): bool - { - return in_array('statamic.cp.authenticated', Route::current()->gatherMiddleware()); - } - - protected function hasAnotherAuthor(UserContract $user, $entry): bool - { - if ($entry->blueprint()->hasField('author') === false) { - return false; - } - - return ! $entry->authors()->contains($user->id()); - } } diff --git a/src/Contracts/Entries/EntryRepository.php b/src/Stache/Repositories/EntryRepository.php similarity index 88% rename from src/Contracts/Entries/EntryRepository.php rename to src/Stache/Repositories/EntryRepository.php index d4a60c8..6813b8e 100644 --- a/src/Contracts/Entries/EntryRepository.php +++ b/src/Stache/Repositories/EntryRepository.php @@ -1,6 +1,6 @@