From 17f3318ddb803ff4c996fd92321bd3920f017343 Mon Sep 17 00:00:00 2001 From: Alexander Stoffel Date: Fri, 3 Jul 2026 07:04:58 +0700 Subject: [PATCH 1/3] [6.x] Add config to control default state of Super Admin toggle The user creation wizard defaults the "Super Admin" toggle to on for any super user creating an account, making it easy to grant super admin unintentionally. This adds a `statamic.users.wizard_default_super` config option (default `true`, preserving current behaviour) so the toggle can be made an explicit opt-in by setting it to `false`. The toggle default is still gated on `canCreateSupers`, so a non-super creator can never end up with a pre-checked super toggle, and the server-side guard in the store method is unchanged. Co-Authored-By: Claude Opus 4.8 --- config/users.php | 14 ++++++++ resources/js/components/users/Wizard.vue | 3 +- resources/js/pages/users/Create.vue | 2 ++ .../Controllers/CP/Users/UsersController.php | 1 + tests/Feature/Users/CreateUserTest.php | 32 +++++++++++++++++++ 5 files changed, 51 insertions(+), 1 deletion(-) diff --git a/config/users.php b/config/users.php index da2d293b3e2..a4794607423 100644 --- a/config/users.php +++ b/config/users.php @@ -101,6 +101,20 @@ 'wizard_invitation' => true, + /* + |-------------------------------------------------------------------------- + | User Wizard Default Super Admin + |-------------------------------------------------------------------------- + | + | When creating new users through the wizard in the control panel, the + | "Super Admin" toggle defaults to enabled for super users. Set this + | to false to have the toggle default to off, so that granting a + | new user super admin becomes an explicit, opt-in action. + | + */ + + 'wizard_default_super' => true, + /* |-------------------------------------------------------------------------- | Password Brokers diff --git a/resources/js/components/users/Wizard.vue b/resources/js/components/users/Wizard.vue index 99eacf0c778..54a908fb550 100644 --- a/resources/js/components/users/Wizard.vue +++ b/resources/js/components/users/Wizard.vue @@ -221,6 +221,7 @@ export default { usersCreateUrl: { type: String }, usersIndexUrl: { type: String }, canCreateSupers: { type: Boolean }, + defaultSuper: { type: Boolean, default: true }, canAssignRoles: { type: Boolean }, canAssignGroups: { type: Boolean }, activationExpiry: { type: Number }, @@ -235,7 +236,7 @@ export default { data() { return { user: { - super: this.canCreateSupers, + super: this.canCreateSupers && this.defaultSuper, roles: [], groups: [], }, diff --git a/resources/js/pages/users/Create.vue b/resources/js/pages/users/Create.vue index 49fb34870ac..92363373aa8 100644 --- a/resources/js/pages/users/Create.vue +++ b/resources/js/pages/users/Create.vue @@ -8,6 +8,7 @@ defineProps([ 'usersIndexUrl', 'usersCreateUrl', 'canCreateSupers', + 'defaultSuper', 'canAssignRoles', 'canAssignGroups', 'activationExpiry', @@ -30,6 +31,7 @@ useArchitecturalBackground(); :users-index-url="usersIndexUrl" :users-create-url="usersCreateUrl" :can-create-supers="canCreateSupers" + :default-super="defaultSuper" :can-assign-roles="canAssignRoles" :can-assign-groups="canAssignGroups" :activation-expiry="activationExpiry" diff --git a/src/Http/Controllers/CP/Users/UsersController.php b/src/Http/Controllers/CP/Users/UsersController.php index 5b9fddd7e42..17f3b9d12b0 100644 --- a/src/Http/Controllers/CP/Users/UsersController.php +++ b/src/Http/Controllers/CP/Users/UsersController.php @@ -178,6 +178,7 @@ public function create(Request $request) 'usersIndexUrl' => cp_route('users.index'), 'usersCreateUrl' => cp_route('users.create'), 'canCreateSupers' => User::current()->isSuper(), + 'defaultSuper' => config('statamic.users.wizard_default_super', true), 'canAssignRoles' => User::current()->can('assign roles'), 'canAssignGroups' => User::current()->can('assign user groups'), 'activationExpiry' => $expiry, diff --git a/tests/Feature/Users/CreateUserTest.php b/tests/Feature/Users/CreateUserTest.php index 736f13481a7..8335239d253 100644 --- a/tests/Feature/Users/CreateUserTest.php +++ b/tests/Feature/Users/CreateUserTest.php @@ -40,4 +40,36 @@ public function it_requires_an_elevated_session() ->get(route('statamic.cp.users.create')) ->assertRedirectToConfirmPasswordForElevatedSession(); } + + #[Test] + public function the_super_toggle_defaults_to_enabled() + { + $this->setTestRoles(['test' => ['access cp', 'create users']]); + $me = tap(User::make()->email('admin@domain.com')->assignRole('test'))->save(); + + $this + ->actingAsWithElevatedSession($me) + ->get(route('statamic.cp.users.create')) + ->assertOk() + ->assertInertia(fn ($page) => $page + ->component('users/Create') + ->where('defaultSuper', true)); + } + + #[Test] + public function the_super_toggle_default_can_be_disabled_via_config() + { + config(['statamic.users.wizard_default_super' => false]); + + $this->setTestRoles(['test' => ['access cp', 'create users']]); + $me = tap(User::make()->email('admin@domain.com')->assignRole('test'))->save(); + + $this + ->actingAsWithElevatedSession($me) + ->get(route('statamic.cp.users.create')) + ->assertOk() + ->assertInertia(fn ($page) => $page + ->component('users/Create') + ->where('defaultSuper', false)); + } } From abca1d0da1e9f52ec4cea39d4e39a183741898f5 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Fri, 3 Jul 2026 12:37:35 -0400 Subject: [PATCH 2/3] Default the Super Admin toggle to off in the user wizard --- config/users.php | 14 -------- resources/js/components/users/Wizard.vue | 3 +- resources/js/pages/users/Create.vue | 2 -- .../Controllers/CP/Users/UsersController.php | 1 - tests/Feature/Users/CreateUserTest.php | 32 ------------------- 5 files changed, 1 insertion(+), 51 deletions(-) diff --git a/config/users.php b/config/users.php index a4794607423..da2d293b3e2 100644 --- a/config/users.php +++ b/config/users.php @@ -101,20 +101,6 @@ 'wizard_invitation' => true, - /* - |-------------------------------------------------------------------------- - | User Wizard Default Super Admin - |-------------------------------------------------------------------------- - | - | When creating new users through the wizard in the control panel, the - | "Super Admin" toggle defaults to enabled for super users. Set this - | to false to have the toggle default to off, so that granting a - | new user super admin becomes an explicit, opt-in action. - | - */ - - 'wizard_default_super' => true, - /* |-------------------------------------------------------------------------- | Password Brokers diff --git a/resources/js/components/users/Wizard.vue b/resources/js/components/users/Wizard.vue index 54a908fb550..8e8b6a05858 100644 --- a/resources/js/components/users/Wizard.vue +++ b/resources/js/components/users/Wizard.vue @@ -221,7 +221,6 @@ export default { usersCreateUrl: { type: String }, usersIndexUrl: { type: String }, canCreateSupers: { type: Boolean }, - defaultSuper: { type: Boolean, default: true }, canAssignRoles: { type: Boolean }, canAssignGroups: { type: Boolean }, activationExpiry: { type: Number }, @@ -236,7 +235,7 @@ export default { data() { return { user: { - super: this.canCreateSupers && this.defaultSuper, + super: false, roles: [], groups: [], }, diff --git a/resources/js/pages/users/Create.vue b/resources/js/pages/users/Create.vue index 92363373aa8..49fb34870ac 100644 --- a/resources/js/pages/users/Create.vue +++ b/resources/js/pages/users/Create.vue @@ -8,7 +8,6 @@ defineProps([ 'usersIndexUrl', 'usersCreateUrl', 'canCreateSupers', - 'defaultSuper', 'canAssignRoles', 'canAssignGroups', 'activationExpiry', @@ -31,7 +30,6 @@ useArchitecturalBackground(); :users-index-url="usersIndexUrl" :users-create-url="usersCreateUrl" :can-create-supers="canCreateSupers" - :default-super="defaultSuper" :can-assign-roles="canAssignRoles" :can-assign-groups="canAssignGroups" :activation-expiry="activationExpiry" diff --git a/src/Http/Controllers/CP/Users/UsersController.php b/src/Http/Controllers/CP/Users/UsersController.php index 17f3b9d12b0..5b9fddd7e42 100644 --- a/src/Http/Controllers/CP/Users/UsersController.php +++ b/src/Http/Controllers/CP/Users/UsersController.php @@ -178,7 +178,6 @@ public function create(Request $request) 'usersIndexUrl' => cp_route('users.index'), 'usersCreateUrl' => cp_route('users.create'), 'canCreateSupers' => User::current()->isSuper(), - 'defaultSuper' => config('statamic.users.wizard_default_super', true), 'canAssignRoles' => User::current()->can('assign roles'), 'canAssignGroups' => User::current()->can('assign user groups'), 'activationExpiry' => $expiry, diff --git a/tests/Feature/Users/CreateUserTest.php b/tests/Feature/Users/CreateUserTest.php index 8335239d253..736f13481a7 100644 --- a/tests/Feature/Users/CreateUserTest.php +++ b/tests/Feature/Users/CreateUserTest.php @@ -40,36 +40,4 @@ public function it_requires_an_elevated_session() ->get(route('statamic.cp.users.create')) ->assertRedirectToConfirmPasswordForElevatedSession(); } - - #[Test] - public function the_super_toggle_defaults_to_enabled() - { - $this->setTestRoles(['test' => ['access cp', 'create users']]); - $me = tap(User::make()->email('admin@domain.com')->assignRole('test'))->save(); - - $this - ->actingAsWithElevatedSession($me) - ->get(route('statamic.cp.users.create')) - ->assertOk() - ->assertInertia(fn ($page) => $page - ->component('users/Create') - ->where('defaultSuper', true)); - } - - #[Test] - public function the_super_toggle_default_can_be_disabled_via_config() - { - config(['statamic.users.wizard_default_super' => false]); - - $this->setTestRoles(['test' => ['access cp', 'create users']]); - $me = tap(User::make()->email('admin@domain.com')->assignRole('test'))->save(); - - $this - ->actingAsWithElevatedSession($me) - ->get(route('statamic.cp.users.create')) - ->assertOk() - ->assertInertia(fn ($page) => $page - ->component('users/Create') - ->where('defaultSuper', false)); - } } From 8b8aa1d210e9e37f445c102f716c131b72234f8f Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Fri, 3 Jul 2026 12:56:59 -0400 Subject: [PATCH 3/3] improve coverage --- tests/Feature/Users/StoreUserTest.php | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/Feature/Users/StoreUserTest.php b/tests/Feature/Users/StoreUserTest.php index e227b9234b2..143efd514db 100644 --- a/tests/Feature/Users/StoreUserTest.php +++ b/tests/Feature/Users/StoreUserTest.php @@ -35,6 +35,8 @@ public function it_creates_a_user() ->actingAsWithElevatedSession($me) ->store() ->assertOk(); + + $this->assertFalse(User::findByEmail('test@domain.com')->isSuper()); } #[Test] @@ -48,4 +50,32 @@ public function it_requires_an_elevated_session() ->store() ->assertElevatedSessionRequiredJsonResponse(); } + + #[Test] + public function super_users_can_create_a_super_user() + { + $this->setTestRoles(['test' => ['access cp', 'create users']]); + $me = tap(User::make()->email('admin@domain.com')->assignRole('test')->makeSuper())->save(); + + $this + ->actingAsWithElevatedSession($me) + ->store(['super' => true]) + ->assertOk(); + + $this->assertTrue(User::findByEmail('test@domain.com')->isSuper()); + } + + #[Test] + public function non_super_users_cannot_create_a_super_user() + { + $this->setTestRoles(['test' => ['access cp', 'create users']]); + $me = tap(User::make()->email('admin@domain.com')->assignRole('test'))->save(); + + $this + ->actingAsWithElevatedSession($me) + ->store(['super' => true]) + ->assertOk(); + + $this->assertFalse(User::findByEmail('test@domain.com')->isSuper()); + } }