Skip to content

Commit 3bb9286

Browse files
1stevengrantclaudenicboveejasonvarga
authored
[6.x] Add config for disabling Elevated Sessions (#14464)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Nic Bovee <nic@nicbovee.com> Co-authored-by: Jason Varga <jason@pixelfear.com>
1 parent 79cdda5 commit 3bb9286

10 files changed

Lines changed: 139 additions & 13 deletions

File tree

config/users.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,12 @@
176176
| Users may be required to reauthorize before performing certain
177177
| sensitive actions. This is called an elevated session. Here
178178
| you may configure the duration of the session in minutes.
179+
| You may also disable the elevated session entirely.
179180
|
180181
*/
181182

183+
'elevated_sessions_enabled' => env('STATAMIC_ELEVATED_SESSIONS_ENABLED', true),
184+
182185
'elevated_session_duration' => 15,
183186

184187
'elevated_session_url' => null,

resources/js/components/elevated-sessions/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import axios from 'axios';
22

33
export async function requireElevatedSession() {
4+
if (!Statamic.$config.get('elevatedSessionsEnabled')) return;
5+
46
const response = await axios.get(cp_url('elevated-session'));
57

68
if (response.data.elevated) return;

routes/cp.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -443,11 +443,13 @@
443443

444444
Route::get('session-timeout', SessionTimeoutController::class)->name('session.timeout');
445445

446-
Route::get('auth/confirm-password', [ElevatedSessionController::class, 'showForm'])->name('confirm-password');
447-
Route::get('elevated-session', [ElevatedSessionController::class, 'status'])->name('elevated-session.status');
448-
Route::get('elevated-session/passkey-options', [ElevatedSessionController::class, 'options'])->name('elevated-session.passkey-options')->middleware('throttle:statamic.cp.passkeys');
449-
Route::post('elevated-session', [ElevatedSessionController::class, 'confirm'])->name('elevated-session.confirm')->middleware('throttle:statamic.cp.auth');
450-
Route::get('elevated-session/resend-code', [ElevatedSessionController::class, 'resendCode'])->name('elevated-session.resend-code')->middleware('throttle:send-elevated-session-code');
446+
if (config('statamic.users.elevated_sessions_enabled')) {
447+
Route::get('auth/confirm-password', [ElevatedSessionController::class, 'showForm'])->name('confirm-password');
448+
Route::get('elevated-session', [ElevatedSessionController::class, 'status'])->name('elevated-session.status');
449+
Route::get('elevated-session/passkey-options', [ElevatedSessionController::class, 'options'])->name('elevated-session.passkey-options')->middleware('throttle:statamic.cp.passkeys');
450+
Route::post('elevated-session', [ElevatedSessionController::class, 'confirm'])->name('elevated-session.confirm')->middleware('throttle:statamic.cp.auth');
451+
Route::get('elevated-session/resend-code', [ElevatedSessionController::class, 'resendCode'])->name('elevated-session.resend-code')->middleware('throttle:send-elevated-session-code');
452+
}
451453

452454
Route::get('playground', PlaygroundController::class)->name('playground');
453455

routes/web.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,14 @@
5454
Route::get('password/reset/{token}', [ResetPasswordController::class, 'showResetForm'])->name('password.reset');
5555
Route::post('password/reset', [ResetPasswordController::class, 'reset'])->middleware('throttle:statamic.auth')->name('password.reset.action');
5656

57-
Route::middleware('auth')->group(function () {
58-
Route::get('confirm-password', [ElevatedSessionController::class, 'showForm'])->name('elevated-session')->middleware([HandleInertiaRequests::class]);
59-
Route::post('elevated-session', [ElevatedSessionController::class, 'confirm'])->name('elevated-session.confirm')->middleware('throttle:statamic.auth');
60-
Route::get('elevated-session/passkey-options', [ElevatedSessionController::class, 'options'])->name('elevated-session.passkey-options')->middleware('throttle:statamic.passkeys');
61-
Route::get('elevated-session/resend-code', [ElevatedSessionController::class, 'resendCode'])->name('elevated-session.resend-code')->middleware('throttle:send-elevated-session-code');
62-
});
57+
if (config('statamic.users.elevated_sessions_enabled')) {
58+
Route::middleware('auth')->group(function () {
59+
Route::get('confirm-password', [ElevatedSessionController::class, 'showForm'])->name('elevated-session')->middleware([HandleInertiaRequests::class]);
60+
Route::post('elevated-session', [ElevatedSessionController::class, 'confirm'])->name('elevated-session.confirm')->middleware('throttle:statamic.auth');
61+
Route::get('elevated-session/passkey-options', [ElevatedSessionController::class, 'options'])->name('elevated-session.passkey-options')->middleware('throttle:statamic.passkeys');
62+
Route::get('elevated-session/resend-code', [ElevatedSessionController::class, 'resendCode'])->name('elevated-session.resend-code')->middleware('throttle:send-elevated-session-code');
63+
});
64+
}
6365

6466
Route::group(['prefix' => 'passkeys'], function () {
6567
Route::middleware('throttle:statamic.passkeys')->group(function () {

src/Http/Controllers/CP/CpController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public function authorizeProIf($condition)
7272

7373
public function requireElevatedSession(): void
7474
{
75-
if (! request()->hasElevatedSession()) {
75+
if (config('statamic.users.elevated_sessions_enabled') && ! request()->hasElevatedSession()) {
7676
throw new ElevatedSessionAuthorizationException;
7777
}
7878
}

src/Http/Middleware/RequireElevatedSession.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class RequireElevatedSession
99
{
1010
public function handle($request, Closure $next)
1111
{
12-
if (! $request->hasElevatedSession()) {
12+
if (config('statamic.users.elevated_sessions_enabled') && ! $request->hasElevatedSession()) {
1313
throw new ElevatedSessionAuthorizationException;
1414
}
1515

src/Http/View/Composers/JavascriptComposer.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ private function protectedVariables()
6464
'ajaxTimeout' => config('statamic.system.ajax_timeout'),
6565
'googleDocsViewer' => config('statamic.assets.google_docs_viewer'),
6666
'focalPointEditorEnabled' => config('statamic.assets.focal_point_editor'),
67+
'elevatedSessionsEnabled' => config('statamic.users.elevated_sessions_enabled'),
6768
'user' => $this->user($user),
6869
'defaultPreferences' => Preference::default()->all(),
6970
'paginationSize' => config('statamic.cp.pagination_size'),
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
namespace Tests\Auth;
4+
5+
use PHPUnit\Framework\Attributes\Group;
6+
use PHPUnit\Framework\Attributes\Test;
7+
use Statamic\Facades\User;
8+
use Tests\PreventSavingStacheItemsToDisk;
9+
use Tests\TestCase;
10+
11+
#[Group('elevated-session')]
12+
class ElevatedSessionDisabledTest extends TestCase
13+
{
14+
use PreventSavingStacheItemsToDisk;
15+
16+
private $user;
17+
18+
protected function setUp(): void
19+
{
20+
parent::setUp();
21+
22+
$this->user = User::make()->email('foo@bar.com')->makeSuper()->password('secret');
23+
$this->user->save();
24+
}
25+
26+
protected function getEnvironmentSetUp($app)
27+
{
28+
parent::getEnvironmentSetUp($app);
29+
30+
$app['config']->set('statamic.users.elevated_sessions_enabled', false);
31+
}
32+
33+
#[Test]
34+
public function cp_elevated_session_routes_are_not_registered()
35+
{
36+
$this->actingAs($this->user);
37+
38+
$this->get('/cp/elevated-session')->assertNotFound();
39+
$this->get('/cp/elevated-session/passkey-options')->assertNotFound();
40+
$this->post('/cp/elevated-session')->assertNotFound();
41+
$this->get('/cp/elevated-session/resend-code')->assertNotFound();
42+
$this->get('/cp/auth/confirm-password')->assertNotFound();
43+
}
44+
45+
#[Test]
46+
public function frontend_elevated_session_routes_are_not_registered()
47+
{
48+
$this->actingAs($this->user);
49+
50+
$this->get('/!/auth/confirm-password')->assertNotFound();
51+
$this->post('/!/auth/elevated-session')->assertNotFound();
52+
$this->get('/!/auth/elevated-session/passkey-options')->assertNotFound();
53+
$this->get('/!/auth/elevated-session/resend-code')->assertNotFound();
54+
}
55+
}

tests/Auth/ElevatedSessionTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,47 @@ public function middleware_denies_request_when_elevated_session_has_expired_via_
321321
->assertJson(['message' => __('Requires an elevated session.')]);
322322
}
323323

324+
#[Test]
325+
public function middleware_does_not_require_elevated_session_when_elevated_session_is_disabled()
326+
{
327+
config(['statamic.users.elevated_sessions_enabled' => false]);
328+
329+
$this->actingAs($this->user);
330+
331+
$this
332+
->get('/requires-elevated-session')
333+
->assertOk()
334+
->assertSee('ok');
335+
}
336+
337+
#[Test]
338+
public function middleware_does_not_require_elevated_session_when_elevated_session_is_disabled_even_if_session_expired()
339+
{
340+
config(['statamic.users.elevated_sessions_enabled' => false]);
341+
342+
$this->actingAs($this->user);
343+
344+
$this
345+
->withElevatedSession(now()->subMinutes(16))
346+
->get('/requires-elevated-session')
347+
->assertOk()
348+
->assertSee('ok');
349+
}
350+
351+
#[Test]
352+
public function middleware_does_not_require_elevated_session_when_elevated_session_is_disabled_via_json()
353+
{
354+
config(['statamic.users.elevated_sessions_enabled' => false]);
355+
356+
$this->actingAs($this->user);
357+
358+
$this
359+
->withElevatedSession(now()->subMinutes(16))
360+
->getJson('/requires-elevated-session')
361+
->assertOk()
362+
->assertSee('ok');
363+
}
364+
324365
#[Test]
325366
public function the_session_is_elevated_upon_login()
326367
{

tests/Feature/Roles/StoreRoleTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,26 @@ public function it_denies_access_without_active_elevated_session()
6868
->assertRedirect('/cp/auth/confirm-password');
6969
}
7070

71+
#[Test]
72+
public function it_allows_storing_a_role_without_elevated_session_when_elevated_sessions_are_disabled()
73+
{
74+
config(['statamic.users.elevated_sessions_enabled' => false]);
75+
76+
$this
77+
->actingAsUserWithPermissions(['edit roles'])
78+
->store([
79+
'title' => 'No Elevated Session',
80+
'handle' => 'no_elevated_session',
81+
'permissions' => ['one', 'two'],
82+
])
83+
->assertOk()
84+
->assertJson(['redirect' => cp_route('roles.index')]);
85+
86+
$role = Role::find('no_elevated_session');
87+
$this->assertEquals('No Elevated Session', $role->title());
88+
$this->assertEquals(['one', 'two'], $role->permissions()->all());
89+
}
90+
7191
#[Test]
7292
public function it_stores_a_role()
7393
{

0 commit comments

Comments
 (0)