Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[6.x] Re-work super user authorization check #11516

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
1 change: 0 additions & 1 deletion src/Http/Middleware/CP/Authorize.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ public function handle($request, Closure $next)
}

if ($user->cant('access cp')) {
// dd('theres a user but they are unauthorized', $user);
throw new AuthorizationException('Unauthorized.');
}

Expand Down
7 changes: 6 additions & 1 deletion src/Policies/AssetContainerPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ class AssetContainerPolicy
{
public function before($user, $ability)
{
if (User::fromUser($user)->hasPermission('configure asset containers')) {
$user = User::fromUser($user);

if (
$user->isSuper() ||
$user->hasPermission('configure asset containers')
) {
return true;
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/Policies/AssetFolderPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@

class AssetFolderPolicy
{
public function before($user)
{
if (User::fromUser($user)->isSuper()) {
return true;
}
}

public function create($user, $assetContainer)
{
$user = User::fromUser($user);
Expand Down
5 changes: 4 additions & 1 deletion src/Policies/AssetPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ public function before($user)
{
$user = User::fromUser($user);

if ($user->hasPermission('configure asset containers')) {
if (
$user->isSuper() ||
$user->hasPermission('configure asset containers')
) {
return true;
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/Policies/CollectionPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ public function before($user)
{
$user = User::fromUser($user);

if ($user->hasPermission('configure collections')) {
if (
$user->isSuper() ||
$user->hasPermission('configure collections')
) {
return true;
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/Policies/EntryPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ public function before($user)
{
$user = User::fromUser($user);

if ($user->hasPermission('configure collections')) {
if (
$user->isSuper() ||
$user->hasPermission('configure collections')
) {
return true;
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/Policies/FieldsetPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ public function before($user, $ability, $fieldset)
{
$user = User::fromUser($user);

if ($user->hasPermission('configure fields')) {
if (
$user->isSuper() ||
$user->hasPermission('configure fields')
) {
return true;
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/Policies/FormPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ public function before($user, $ability)
{
$user = User::fromUser($user);

if ($user->hasPermission('configure forms')) {
if (
$user->isSuper() ||
$user->hasPermission('configure forms')
) {
return true;
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/Policies/FormSubmissionPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ public function before($user, $ability)
{
$user = User::fromUser($user);

if ($user->hasPermission('configure forms')) {
if (
$user->isSuper() ||
$user->hasPermission('configure forms')
) {
return true;
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/Policies/GlobalSetPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ public function before($user)
{
$user = User::fromUser($user);

if ($user->hasPermission('configure globals')) {
if (
$user->isSuper() ||
$user->hasPermission('configure globals')
) {
return true;
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/Policies/NavPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ public function before($user)
{
$user = User::fromUser($user);

if ($user->hasPermission('configure navs')) {
if (
$user->isSuper() ||
$user->hasPermission('configure navs')
) {
return true;
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/Policies/NavTreePolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ class NavTreePolicy extends NavPolicy
{
use Concerns\HasMultisitePolicy;

public function before($user)
{
if (User::fromUser($user)->isSuper()) {
return true;
}
}

public function view($user, $nav)
{
$user = User::fromUser($user);
Expand Down
7 changes: 7 additions & 0 deletions src/Policies/SitePolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@

class SitePolicy
{
public function before($user)
{
if (User::fromUser($user)->isSuper()) {
return true;
}
}

public function view($user, $site)
{
if (! Site::multiEnabled()) {
Expand Down
5 changes: 4 additions & 1 deletion src/Policies/TaxonomyPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ public function before($user)
{
$user = User::fromUser($user);

if ($user->hasPermission('configure taxonomies')) {
if (
$user->isSuper() ||
$user->hasPermission('configure taxonomies')
) {
return true;
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/Policies/TermPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ public function before($user)
{
$user = User::fromUser($user);

if ($user->hasPermission('configure taxonomies')) {
if (
$user->isSuper() ||
$user->hasPermission('configure taxonomies')
) {
return true;
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/Policies/UserPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@

class UserPolicy
{
public function before($user)
{
if (User::fromUser($user)->isSuper()) {
return true;
}
}

public function index($authed)
{
$authed = User::fromUser($authed);
Expand Down
9 changes: 8 additions & 1 deletion src/Providers/AuthServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Statamic\Contracts\Auth\RoleRepository;
use Statamic\Contracts\Auth\UserGroupRepository;
use Statamic\Contracts\Auth\UserRepository;
use Statamic\Facades\Permission;
use Statamic\Facades\User;
use Statamic\Policies;

Expand Down Expand Up @@ -84,7 +85,13 @@ public function boot()
});

Gate::before(function ($user, $ability) {
return optional(User::fromUser($user))->isSuper() ? true : null;
Permission::boot();
Copy link
Member Author

@duncanmcclean duncanmcclean Feb 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I think this approach is broadly on the right track, I don't really want to be booting permissions here. Although, I'm not sure what the best alternative is... 🤔

  • Without booting permissions here, users can't access the CP because the Authorize middleware checks if the user has the relevant permissions, however it happens before the BootPermissions middleware is called.
  • We can't reorder the middleware, otherwise it might cause issues with translations in permission labels, which Ability to defer permission and utility registration for translations #7343 solved.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's wrong with booting in there? It only happens when the closure is executed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I was just thinking it was weird since we're already booting them in a middleware, and that trying to boot them multiple times would hurt performance?

Maybe I'm wrong though, and it's fine?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could make Permissions::boot() return early and do nothing if it's already been booted once.


$isStatamicPermission = Permission::all()->first(fn ($permission) => $permission->value() === $ability);

if ($isStatamicPermission) {
return optional(User::fromUser($user))->isSuper() ? true : null;
}
});

Gate::after(function ($user, $ability) {
Expand Down
18 changes: 18 additions & 0 deletions tests/CP/Navigation/ActiveNavItemTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,9 @@ public function it_resolves_core_children_closure_and_can_check_when_parent_and_
#[Test]
public function it_can_check_if_parent_extension_with_array_based_children_item_is_active()
{
Facades\Permission::register('view seo reports');
Facades\Permission::register('edit seo section defaults');

Facades\CP\Nav::extend(function ($nav) {
$nav->tools('SEO Pro')
->url('/cp/seo-pro')
Expand All @@ -243,6 +246,9 @@ public function it_can_check_if_parent_extension_with_array_based_children_item_
#[Test]
public function it_can_check_when_parent_and_array_based_child_extension_items_are_active()
{
Facades\Permission::register('view seo reports');
Facades\Permission::register('edit seo section defaults');

Facades\CP\Nav::extend(function ($nav) {
$nav->tools('SEO Pro')
->url('/cp/seo-pro')
Expand All @@ -268,6 +274,9 @@ public function it_can_check_when_parent_and_array_based_child_extension_items_a
#[Test]
public function it_can_check_when_parent_and_array_based_descendant_of_child_extension_item_is_active()
{
Facades\Permission::register('view seo reports');
Facades\Permission::register('edit seo section defaults');

Facades\CP\Nav::extend(function ($nav) {
$nav->tools('SEO Pro')
->url('/cp/seo-pro')
Expand Down Expand Up @@ -319,6 +328,9 @@ public function it_builds_extension_children_closure_when_not_active()
#[Test]
public function it_resolves_extension_children_closure_and_can_check_when_parent_item_is_active()
{
Facades\Permission::register('view seo reports');
Facades\Permission::register('edit seo section defaults');

Facades\CP\Nav::extend(function ($nav) {
$nav->tools('SEO Pro')
->url('/cp/seo-pro')
Expand Down Expand Up @@ -346,6 +358,9 @@ public function it_resolves_extension_children_closure_and_can_check_when_parent
#[Test]
public function it_resolves_extension_children_closure_and_can_check_when_parent_and_child_item_are_active()
{
Facades\Permission::register('view seo reports');
Facades\Permission::register('edit seo section defaults');

Facades\CP\Nav::extend(function ($nav) {
$nav->tools('SEO Pro')
->url('/cp/seo-pro')
Expand Down Expand Up @@ -373,6 +388,9 @@ public function it_resolves_extension_children_closure_and_can_check_when_parent
#[Test]
public function it_resolves_extension_children_closure_and_can_check_when_parent_and_descendant_of_child_item_is_active()
{
Facades\Permission::register('view seo reports');
Facades\Permission::register('edit seo section defaults');

Facades\CP\Nav::extend(function ($nav) {
$nav->tools('SEO Pro')
->url('/cp/seo-pro')
Expand Down
7 changes: 4 additions & 3 deletions tests/CP/Navigation/NavTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,14 @@ public function it_can_create_a_nav_item_with_a_more_custom_config()
{
$this->actingAs(tap(User::make()->makeSuper())->save());

Facades\Permission::register('view droids');

Nav::droids('C-3PO')
->id('some::custom::id')
->active('threepio*')
->url('/human-cyborg-relations')
->view('cp.nav.importer')
->can('index', 'DroidsClass')
->can('view droids')
->attributes(['target' => '_blank', 'class' => 'red']);

$item = $this->build()->get('Droids')->first();
Expand All @@ -89,8 +91,7 @@ public function it_can_create_a_nav_item_with_a_more_custom_config()
$this->assertEquals('http://localhost/human-cyborg-relations', $item->url());
$this->assertEquals('cp.nav.importer', $item->view());
$this->assertEquals('threepio*', $item->active());
$this->assertEquals('index', $item->authorization()->ability);
$this->assertEquals('DroidsClass', $item->authorization()->arguments);
$this->assertEquals('view droids', $item->authorization()->ability);
$this->assertEquals(' target="_blank" class="red"', $item->attributes());
}

Expand Down
Loading