Skip to content

Commit ac26e83

Browse files
authored
Merge pull request #18144 from craftcms/feature/authorization
[6.x] Use Laravel Gate authorization for permissions
2 parents 2037eb5 + 4fd6775 commit ac26e83

File tree

19 files changed

+118
-112
lines changed

19 files changed

+118
-112
lines changed

src/Field/Assets.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
use CraftCms\Cms\Support\Html;
3737
use GraphQL\Type\Definition\Type;
3838
use Illuminate\Support\Collection;
39+
use Illuminate\Support\Facades\Gate;
3940
use Illuminate\Validation\Rule;
4041
use Override;
4142

@@ -694,7 +695,7 @@ public function getInputSources(?ElementInterface $element = null): array
694695
// (Use restrictedLocationSource here because the actual folder could belong to a temp volume)
695696
$volume = $this->_volumeBySourceKey($this->restrictedLocationSource);
696697

697-
if (! $volume || ! Craft::$app->getUser()->checkPermission("viewAssets:$volume->uid")) {
698+
if (! $volume || ! Gate::check("viewAssets:$volume->uid")) {
698699
return [];
699700
}
700701
}
@@ -724,19 +725,18 @@ public function getInputSources(?ElementInterface $element = null): array
724725

725726
// Now enforce the showUnpermittedVolumes setting
726727
if (! $this->showUnpermittedVolumes && ! empty($sources)) {
727-
$userService = Craft::$app->getUser();
728728
$volumesService = Craft::$app->getVolumes();
729729

730730
return Collection::make($sources)
731-
->filter(function (string $source) use ($volumesService, $userService) {
731+
->filter(function (string $source) use ($volumesService) {
732732
// If it’s not a volume folder, let it through
733733
if (! str_starts_with($source, 'volume:')) {
734734
return true;
735735
}
736736

737737
// Only show it if they have permission to view it, or if it's the temp volume
738738
$volumeUid = explode(':', $source)[1];
739-
if ($userService->checkPermission("viewAssets:$volumeUid")) {
739+
if (Gate::check("viewAssets:$volumeUid")) {
740740
return true;
741741
}
742742

@@ -767,7 +767,7 @@ protected function inputTemplateVariables(array|ElementQueryInterface|null $valu
767767
$this->allowUploads &&
768768
$uploadVolume &&
769769
$uploadFs &&
770-
Craft::$app->getUser()->checkPermission("saveAssets:$uploadVolume->uid")
770+
Gate::check("saveAssets:$uploadVolume->uid")
771771
);
772772
$variables['defaultFieldLayoutId'] = $uploadVolume->fieldLayoutId ?? null;
773773

src/Field/LinkTypes/Asset.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use craft\models\Volume;
1313
use CraftCms\Cms\Field\Link;
1414
use Illuminate\Support\Collection;
15+
use Illuminate\Support\Facades\Gate;
1516

1617
use function CraftCms\Cms\t;
1718

@@ -91,8 +92,7 @@ protected function availableSourceKeys(): array
9192
->filter(fn (Volume $volume) => $volume->getFs()->hasUrls);
9293

9394
if (! $this->showUnpermittedVolumes) {
94-
$userService = Craft::$app->getUser();
95-
$volumes = $volumes->filter(fn (Volume $volume) => $userService->checkPermission("viewAssets:$volume->uid"));
95+
$volumes = $volumes->filter(fn (Volume $volume) => Gate::check("viewAssets:$volume->uid"));
9696
}
9797

9898
return $volumes
@@ -126,17 +126,16 @@ protected function elementSelectConfig(): array
126126
$sourceKeys = $this->sources ?? Collection::make($this->availableSources())
127127
->map(fn (array $source) => $source['key'])
128128
->all();
129-
$userService = Craft::$app->getUser();
130129
$config['sources'] = Collection::make($sourceKeys)
131-
->filter(function (string $source) use ($userService) {
130+
->filter(function (string $source) {
132131
// If it’s not a volume folder, let it through
133132
if (! str_starts_with($source, 'volume:')) {
134133
return true;
135134
}
136135
// Only show it if they have permission to view it, or if it's the temp volume
137136
$volumeUid = explode(':', $source)[1];
138137

139-
return $userService->checkPermission("viewAssets:$volumeUid");
138+
return Gate::check("viewAssets:$volumeUid");
140139
})
141140
->all();
142141
}

src/Translation/I18N.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Illuminate\Container\Attributes\Singleton;
1414
use Illuminate\Support\Collection;
1515
use Illuminate\Support\Facades\Auth;
16+
use Illuminate\Support\Facades\Gate;
1617
use InvalidArgumentException;
1718
use ResourceBundle;
1819
use Stringable;
@@ -254,7 +255,7 @@ public function getEditableLocales(): Collection
254255
return $this->getSiteLocales();
255256
}
256257

257-
return $this->getSiteLocales()->filter(fn (Locale $locale) => Craft::$app->getUser()->checkPermission('editLocale:'.$locale->id));
258+
return $this->getSiteLocales()->filter(fn (Locale $locale) => Gate::check('editLocale:'.$locale->id));
258259
}
259260

260261
/**

src/User/Models/User.php

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
use CraftCms\Cms\Site\Models\Site;
1313
use CraftCms\Cms\Support\Arr;
1414
use CraftCms\Cms\Support\Facades\UserGroups;
15-
use CraftCms\Cms\Support\Facades\UserPermissions;
1615
use Illuminate\Auth\Authenticatable;
1716
use Illuminate\Auth\MustVerifyEmail;
1817
use Illuminate\Auth\Passwords\CanResetPassword;
@@ -84,30 +83,6 @@ protected function newBaseQueryBuilder(): Builder
8483
);
8584
}
8685

87-
/**
88-
* Returns whether the user has permission to perform a given action.
89-
*
90-
* @param string $abilities
91-
*
92-
* @todo Permissions to Laravel Gates
93-
*/
94-
#[Override]
95-
public function can($abilities, $arguments = []): bool
96-
{
97-
if (
98-
$this->admin ||
99-
Edition::get() === Edition::Solo
100-
) {
101-
return true;
102-
}
103-
104-
if (! isset($this->id)) {
105-
return false;
106-
}
107-
108-
return UserPermissions::doesUserHavePermission($this->id, $abilities);
109-
}
110-
11186
public function asElement(): \CraftCms\Cms\User\Elements\User
11287
{
11388
return new \CraftCms\Cms\User\Elements\User(Arr::except($this->toArray(), [

src/User/UserServiceProvider.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace CraftCms\Cms\User;
66

7+
use CraftCms\Cms\Edition;
78
use CraftCms\Cms\User\Commands\ActivationUrlCommand;
89
use CraftCms\Cms\User\Commands\CreateCommand;
910
use CraftCms\Cms\User\Commands\DeleteCommand;
@@ -14,10 +15,45 @@
1415
use CraftCms\Cms\User\Commands\Remove2faCommand;
1516
use CraftCms\Cms\User\Commands\SetPasswordCommand;
1617
use CraftCms\Cms\User\Commands\UnlockCommand;
18+
use CraftCms\Cms\User\Models\User;
19+
use Illuminate\Contracts\Auth\Access\Authorizable;
20+
use Illuminate\Support\Facades\Gate;
1721
use Illuminate\Support\ServiceProvider;
22+
use Override;
1823

1924
final class UserServiceProvider extends ServiceProvider
2025
{
26+
#[Override]
27+
public function register(): void
28+
{
29+
/**
30+
* This hooks our permission system into
31+
* Laravel's Gate authorization system
32+
*/
33+
Gate::before(function (Authorizable $user, string $ability) {
34+
if (! $user instanceof User && ! $user instanceof \craft\elements\User) {
35+
return null;
36+
}
37+
38+
if (
39+
$user->admin ||
40+
Edition::get() === Edition::Solo
41+
) {
42+
return true;
43+
}
44+
45+
if (! isset($user->id)) {
46+
return null;
47+
}
48+
49+
if (! app(UserPermissions::class)->doesUserHavePermission($user->id, $ability)) {
50+
return null;
51+
}
52+
53+
return true;
54+
});
55+
}
56+
2157
public function boot(): void
2258
{
2359
$this->commands([

yii2-adapter/legacy/controllers/AppController.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
use CraftCms\DependencyAwareCache\Facades\DependencyCache;
3838
use DateInterval;
3939
use Illuminate\Support\Facades\Cache;
40+
use Illuminate\Support\Facades\Gate;
4041
use Illuminate\Support\Facades\Http;
4142
use InvalidArgumentException;
4243
use yii\base\InvalidConfigException;
@@ -76,7 +77,7 @@ public function behaviors(): array
7677
'class' => UtilityAccess::class,
7778
'utility' => UpdatesUtility::class,
7879
'only' => ['check-for-updates', 'cache-updates'],
79-
'when' => fn() => !Craft::$app->getUser()->checkPermission('performUpdates'),
80+
'when' => fn() => !Gate::check('performUpdates'),
8081
],
8182
]);
8283
}

yii2-adapter/legacy/controllers/GlobalsController.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use CraftCms\Cms\Field\Fields;
1616
use CraftCms\Cms\Support\Facades\Sites;
1717
use CraftCms\Cms\Support\Json;
18+
use Illuminate\Support\Facades\Gate;
1819
use yii\web\BadRequestHttpException;
1920
use yii\web\ForbiddenHttpException;
2021
use yii\web\NotFoundHttpException;
@@ -161,7 +162,7 @@ public function actionEditContent(string $globalSetHandle, ?GlobalSet $globalSet
161162
->all();
162163

163164
foreach ($globalSets as $thisGlobalSet) {
164-
if (Craft::$app->getUser()->checkPermission('editGlobalSet:' . $thisGlobalSet->uid)) {
165+
if (Gate::check('editGlobalSet:' . $thisGlobalSet->uid)) {
165166
$editableGlobalSets[$thisGlobalSet->handle] = $thisGlobalSet;
166167
}
167168
}

yii2-adapter/legacy/controllers/UsersController.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
use CraftCms\Cms\User\Events\DefineEditUserScreens;
6262
use CraftCms\Cms\User\Events\GroupsAndPermissionsAssigned;
6363
use Illuminate\Support\Facades\Event;
64+
use Illuminate\Support\Facades\Gate;
6465
use Throwable;
6566
use yii\base\Exception;
6667
use yii\base\InvalidArgumentException;
@@ -522,7 +523,7 @@ public function actionSendPasswordResetEmail(): ?Response
522523
$loginName = null;
523524

524525
// If someone's logged in and they're allowed to edit other users, then see if a userId was submitted
525-
if (Craft::$app->getUser()->checkPermission('editUsers')) {
526+
if (Gate::check('editUsers')) {
526527
$userId = $this->request->getBodyParam('userId');
527528

528529
if ($userId) {
@@ -1053,10 +1054,9 @@ public function actionAddresses(?int $userId = null): Response
10531054
$response = $this->asEditUserScreen($user, self::SCREEN_ADDRESSES);
10541055

10551056
$response->contentHtml(function() use ($user) {
1056-
$canEditUsers = Craft::$app->getUser()->checkPermission('editUsers');
10571057
$config = [
10581058
'showInGrid' => true,
1059-
'canCreate' => $canEditUsers,
1059+
'canCreate' => Gate::check('editUsers'),
10601060
];
10611061

10621062
// Use an element index view if there's more than 50 addresses

yii2-adapter/legacy/elements/Asset.php

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
use GraphQL\Type\Definition\Type;
8080
use Illuminate\Support\Collection;
8181
use Illuminate\Support\Facades\DB as DbFacade;
82+
use Illuminate\Support\Facades\Gate;
8283
use Throwable;
8384
use Twig\Markup;
8485
use yii\base\ErrorHandler;
@@ -503,7 +504,7 @@ protected static function defineActions(string $source): array
503504
$actions[] = DownloadAssetFile::class;
504505

505506
$userSession = Craft::$app->getUser();
506-
if ($isTemp || $userSession->checkPermission("replaceFiles:$volume->uid")) {
507+
if ($isTemp || Gate::check("replaceFiles:$volume->uid")) {
507508
// Rename/Replace File
508509
$actions[] = RenameFile::class;
509510
$actions[] = ReplaceFile::class;
@@ -530,7 +531,7 @@ protected static function defineActions(string $source): array
530531
$actions[] = CopyReferenceTag::class;
531532

532533
// Edit Image
533-
if ($isTemp || $userSession->checkPermission("editImages:$volume->uid")) {
534+
if ($isTemp || Gate::check("editImages:$volume->uid")) {
534535
$actions[] = EditImage::class;
535536
}
536537

@@ -544,7 +545,7 @@ protected static function defineActions(string $source): array
544545
];
545546

546547
// Delete
547-
if ($userSession->checkPermission("deletePeerAssets:$volume->uid")) {
548+
if (Gate::check("deletePeerAssets:$volume->uid")) {
548549
$actions[] = DeleteAssets::class;
549550
}
550551
}
@@ -998,12 +999,12 @@ private static function _assembleSourceInfoForFolder(VolumeFolder $folder, ?User
998999
}
9991000

10001001
$userSession = Craft::$app->getUser();
1001-
$canUpload = $userSession->checkPermission("saveAssets:$volume->uid");
1002-
$canMoveTo = $canUpload && $userSession->checkPermission("deleteAssets:$volume->uid");
1002+
$canUpload = Gate::check("saveAssets:$volume->uid");
1003+
$canMoveTo = $canUpload && Gate::check("deleteAssets:$volume->uid");
10031004
$canMovePeerFilesTo = (
10041005
$canMoveTo &&
1005-
$userSession->checkPermission("savePeerAssets:$volume->uid") &&
1006-
$userSession->checkPermission("deletePeerAssets:$volume->uid")
1006+
Gate::check("savePeerAssets:$volume->uid") &&
1007+
Gate::check("deletePeerAssets:$volume->uid")
10071008
);
10081009

10091010
$sourcePathInfo = $folder->getSourcePathInfo();
@@ -1794,8 +1795,8 @@ protected function safeActionMenuItems(): array
17941795
// Image editor
17951796
if (
17961797
$this->getSupportsImageEditor() &&
1797-
$userSession->checkPermission("editImages:$volume->uid") &&
1798-
($userSession->getId() == $this->uploaderId || $userSession->checkPermission("editPeerImages:$volume->uid"))
1798+
Gate::check("editImages:$volume->uid") &&
1799+
($userSession->getId() == $this->uploaderId || Gate::check("editPeerImages:$volume->uid"))
17991800
) {
18001801
$editImageId = sprintf('action-image-edit-%s', mt_rand());
18011802
$items[] = [
@@ -2851,8 +2852,8 @@ public function getPreviewHtml(): string
28512852
$previewable = Craft::$app->getAssets()->getAssetPreviewHandler($this) !== null;
28522853
$editable = (
28532854
$this->getSupportsImageEditor() &&
2854-
$userSession->checkPermission("editImages:$volume->uid") &&
2855-
($userSession->getId() == $this->uploaderId || $userSession->checkPermission("editPeerImages:$volume->uid"))
2855+
Gate::check("editImages:$volume->uid") &&
2856+
($userSession->getId() == $this->uploaderId || Gate::check("editPeerImages:$volume->uid"))
28562857
);
28572858

28582859
switch ($this->kind) {
@@ -3409,8 +3410,8 @@ public function getHtmlAttributes(string $context): array
34093410
$userSession = Craft::$app->getUser();
34103411

34113412
if (
3412-
$userSession->checkPermission("savePeerAssets:$volume->uid") &&
3413-
$userSession->checkPermission("deletePeerAssets:$volume->uid")
3413+
Gate::check("savePeerAssets:$volume->uid") &&
3414+
Gate::check("deletePeerAssets:$volume->uid")
34143415
) {
34153416
$attributes['data']['movable'] = true;
34163417
}
@@ -3450,13 +3451,13 @@ protected function htmlAttributes(string $context): array
34503451
} else {
34513452
$attributes['data']['peer-file'] = true;
34523453
$movable = (
3453-
$userSession->checkPermission("savePeerAssets:$volume->uid") &&
3454-
$userSession->checkPermission("deletePeerAssets:$volume->uid")
3454+
Gate::check("savePeerAssets:$volume->uid") &&
3455+
Gate::check("deletePeerAssets:$volume->uid")
34553456
);
3456-
$replaceable = $userSession->checkPermission("replacePeerFiles:$volume->uid");
3457+
$replaceable = Gate::check("replacePeerFiles:$volume->uid");
34573458
$imageEditable = (
34583459
$imageEditable &&
3459-
($userSession->checkPermission("editPeerImages:$volume->uid"))
3460+
(Gate::check("editPeerImages:$volume->uid"))
34603461
);
34613462
}
34623463

yii2-adapter/legacy/elements/Category.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
use CraftCms\Cms\Support\Str;
3535
use GraphQL\Type\Definition\Type;
3636
use Illuminate\Support\Facades\DB;
37+
use Illuminate\Support\Facades\Gate;
3738
use yii\base\Exception;
3839
use yii\base\InvalidConfigException;
3940
use function CraftCms\Cms\t;
@@ -202,7 +203,7 @@ protected static function defineSources(string $context): array
202203
'data' => ['handle' => $group->handle],
203204
'criteria' => ['groupId' => $group->id],
204205
'structureId' => $group->structureId,
205-
'structureEditable' => Craft::$app->getRequest()->getIsConsoleRequest() || Craft::$app->getUser()->checkPermission("viewCategories:$group->uid"),
206+
'structureEditable' => app()->runningInConsole() || Gate::check("viewCategories:$group->uid"),
206207
];
207208
}
208209

0 commit comments

Comments
 (0)