From e5e4a739adb0b438515f09113218c855cef67a4a Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 27 Feb 2023 18:55:10 +0000 Subject: [PATCH 01/19] Introduce new config setting --- config/cp.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/cp.php b/config/cp.php index 188b7d9f5d..e1587e28c0 100644 --- a/config/cp.php +++ b/config/cp.php @@ -15,6 +15,8 @@ 'route' => env('CP_ROUTE', 'cp'), + 'auth' => true, + /* |-------------------------------------------------------------------------- | Start Page From 895bbcf89cf17b0e6f27c360969425cfaca9c446 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 27 Feb 2023 18:55:25 +0000 Subject: [PATCH 02/19] Only register login/reset password routes when auth is enabled --- routes/cp.php | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/routes/cp.php b/routes/cp.php index 635d163d83..ce1413a1a4 100644 --- a/routes/cp.php +++ b/routes/cp.php @@ -6,14 +6,17 @@ use Statamic\Statamic; Route::group(['prefix' => 'auth', 'namespace' => 'Auth'], function () { - Route::get('login', 'LoginController@showLoginForm')->name('login'); - Route::post('login', 'LoginController@login'); - Route::get('logout', 'LoginController@logout')->name('logout'); + if (config('statamic.cp.auth', true)) { + Route::get('login', 'LoginController@showLoginForm')->name('login'); + Route::post('login', 'LoginController@login'); + + Route::get('password/reset', 'ForgotPasswordController@showLinkRequestForm')->name('password.request'); + Route::post('password/email', 'ForgotPasswordController@sendResetLinkEmail')->name('password.email'); + Route::get('password/reset/{token}', 'ResetPasswordController@showResetForm')->name('password.reset'); + Route::post('password/reset', 'ResetPasswordController@reset')->name('password.reset.action'); + } - Route::get('password/reset', 'ForgotPasswordController@showLinkRequestForm')->name('password.request'); - Route::post('password/email', 'ForgotPasswordController@sendResetLinkEmail')->name('password.email'); - Route::get('password/reset/{token}', 'ResetPasswordController@showResetForm')->name('password.reset'); - Route::post('password/reset', 'ResetPasswordController@reset')->name('password.reset.action'); + Route::get('logout', 'LoginController@logout')->name('logout'); Route::get('token', 'CsrfTokenController')->name('token'); Route::get('extend', 'ExtendSessionController')->name('extend'); From 3b0249d78772eeea1b9c283bd07865a91a651bea Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 27 Feb 2023 18:55:50 +0000 Subject: [PATCH 03/19] `abort(401)` when CP Auth is disabled --- src/Exceptions/AuthenticationException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Exceptions/AuthenticationException.php b/src/Exceptions/AuthenticationException.php index f7566d0777..9d0e5578fd 100644 --- a/src/Exceptions/AuthenticationException.php +++ b/src/Exceptions/AuthenticationException.php @@ -11,6 +11,6 @@ public function toResponse($request) { return $request->expectsJson() ? response()->json(['message' => $this->getMessage()], 401) - : redirect()->route('statamic.cp.login'); + : (config('statamic.cp.auth', true) ? redirect()->route('statamic.cp.login') : abort(401)); } } From 546ed77a3a9056cdaebbb95248aa603afc8f761c Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 27 Feb 2023 21:25:11 +0000 Subject: [PATCH 04/19] Add test to ensure front-end login form still works Even when CP Auth has been disabled --- tests/Tags/User/LoginFormTest.php | 41 +++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tests/Tags/User/LoginFormTest.php b/tests/Tags/User/LoginFormTest.php index dc539203cc..566841bb72 100644 --- a/tests/Tags/User/LoginFormTest.php +++ b/tests/Tags/User/LoginFormTest.php @@ -2,6 +2,7 @@ namespace Tests\Tags\User; +use Illuminate\Support\Facades\Config; use Statamic\Facades\Parse; use Statamic\Facades\User; use Statamic\Statamic; @@ -119,6 +120,46 @@ public function it_will_log_user_in_and_render_success() $this->assertEquals(['Login successful.'], $success[1]); } + /** @test */ + public function it_will_log_user_in_and_render_success_even_when_cp_auth_is_disabled() + { + Config::set('statamic.cp.auth', false); + + $this->assertFalse(auth()->check()); + + User::make() + ->email('san@holo.com') + ->password('chewy') + ->save(); + + $this + ->post('/!/auth/login', [ + 'token' => 'test-token', + 'email' => 'san@holo.com', + 'password' => 'chewy', + ]) + ->assertLocation('/'); + + $this->assertTrue(auth()->check()); + + $output = $this->tag(<<<'EOT' + {{ user:login_form }} + {{ errors }} +

{{ value }}

+ {{ /errors }} + +

{{ success }}

+ {{ /user:login_form }} + EOT + ); + + preg_match_all('/

(.+)<\/p>/U', $output, $errors); + preg_match_all('/

(.+)<\/p>/U', $output, $success); + + $this->assertEmpty($errors[1]); + $this->assertEquals(['Login successful.'], $success[1]); + } + /** @test */ public function it_will_log_user_in_and_follow_custom_redirect_with_success() { From 66abbd1727dd45c985b50fa58421a66c4a04844b Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 27 Feb 2023 21:43:11 +0000 Subject: [PATCH 05/19] Apply StyleCI changes --- tests/Tags/User/LoginFormTest.php | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/Tags/User/LoginFormTest.php b/tests/Tags/User/LoginFormTest.php index 566841bb72..fa743918a1 100644 --- a/tests/Tags/User/LoginFormTest.php +++ b/tests/Tags/User/LoginFormTest.php @@ -120,19 +120,19 @@ public function it_will_log_user_in_and_render_success() $this->assertEquals(['Login successful.'], $success[1]); } - /** @test */ - public function it_will_log_user_in_and_render_success_even_when_cp_auth_is_disabled() - { + /** @test */ + public function it_will_log_user_in_and_render_success_even_when_cp_auth_is_disabled() + { Config::set('statamic.cp.auth', false); - $this->assertFalse(auth()->check()); + $this->assertFalse(auth()->check()); - User::make() + User::make() ->email('san@holo.com') ->password('chewy') ->save(); - $this + $this ->post('/!/auth/login', [ 'token' => 'test-token', 'email' => 'san@holo.com', @@ -140,9 +140,9 @@ public function it_will_log_user_in_and_render_success_even_when_cp_auth_is_disa ]) ->assertLocation('/'); - $this->assertTrue(auth()->check()); + $this->assertTrue(auth()->check()); - $output = $this->tag(<<<'EOT' + $output = $this->tag(<<<'EOT' {{ user:login_form }} {{ errors }}

{{ value }}

@@ -153,12 +153,12 @@ public function it_will_log_user_in_and_render_success_even_when_cp_auth_is_disa EOT ); - preg_match_all('/

(.+)<\/p>/U', $output, $errors); - preg_match_all('/

(.+)<\/p>/U', $output, $success); + preg_match_all('/

(.+)<\/p>/U', $output, $errors); + preg_match_all('/

(.+)<\/p>/U', $output, $success); - $this->assertEmpty($errors[1]); - $this->assertEquals(['Login successful.'], $success[1]); - } + $this->assertEmpty($errors[1]); + $this->assertEquals(['Login successful.'], $success[1]); + } /** @test */ public function it_will_log_user_in_and_follow_custom_redirect_with_success() From 638e304656decf27010276c3492d90c96777b55a Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 25 Apr 2023 20:53:47 +0100 Subject: [PATCH 06/19] StyleCI fixes --- tests/Tags/User/LoginFormTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Tags/User/LoginFormTest.php b/tests/Tags/User/LoginFormTest.php index fa743918a1..e0f481cd14 100644 --- a/tests/Tags/User/LoginFormTest.php +++ b/tests/Tags/User/LoginFormTest.php @@ -151,7 +151,7 @@ public function it_will_log_user_in_and_render_success_even_when_cp_auth_is_disa

{{ success }}

{{ /user:login_form }} EOT - ); + ); preg_match_all('/

(.+)<\/p>/U', $output, $errors); preg_match_all('/

(.+)<\/p>/U', $output, $success); From fc66e424a24f6d4ed8f3feccb1a95eff39deae69 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 9 Nov 2023 11:34:33 +0000 Subject: [PATCH 07/19] Pint --- tests/Tags/User/LoginFormTest.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/Tags/User/LoginFormTest.php b/tests/Tags/User/LoginFormTest.php index 0b9f7c2a0e..75e5e47a7a 100644 --- a/tests/Tags/User/LoginFormTest.php +++ b/tests/Tags/User/LoginFormTest.php @@ -128,17 +128,17 @@ public function it_will_log_user_in_and_render_success_even_when_cp_auth_is_disa $this->assertFalse(auth()->check()); User::make() - ->email('san@holo.com') - ->password('chewy') - ->save(); + ->email('san@holo.com') + ->password('chewy') + ->save(); $this - ->post('/!/auth/login', [ - 'token' => 'test-token', - 'email' => 'san@holo.com', - 'password' => 'chewy', - ]) - ->assertLocation('/'); + ->post('/!/auth/login', [ + 'token' => 'test-token', + 'email' => 'san@holo.com', + 'password' => 'chewy', + ]) + ->assertLocation('/'); $this->assertTrue(auth()->check()); From 8bffc146a26d81e4c51848aef735a2d74b769b18 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 9 Nov 2023 11:42:14 +0000 Subject: [PATCH 08/19] Move logout route out of if statement --- routes/cp.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/routes/cp.php b/routes/cp.php index 94641adb7b..80f246c923 100644 --- a/routes/cp.php +++ b/routes/cp.php @@ -102,7 +102,6 @@ if (config('statamic.cp.auth', true)) { Route::get('login', [LoginController::class, 'showLoginForm'])->name('login'); Route::post('login', [LoginController::class, 'login']); - Route::get('logout', [LoginController::class, 'logout'])->name('logout'); Route::get('password/reset', [ForgotPasswordController::class, 'showLinkRequestForm'])->name('password.request'); Route::post('password/email', [ForgotPasswordController::class, 'sendResetLinkEmail'])->name('password.email'); @@ -110,6 +109,8 @@ Route::post('password/reset', [ResetPasswordController::class, 'reset'])->name('password.reset.action'); } + Route::get('logout', [LoginController::class, 'logout'])->name('logout'); + Route::get('token', CsrfTokenController::class)->name('token'); Route::get('extend', ExtendSessionController::class)->name('extend'); From e656efc494d4f660b1d2a0fbf2a08798a3ee85bf Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 9 Nov 2023 11:55:43 +0000 Subject: [PATCH 09/19] Ensure users can register via user:register_form tag when CP auth is disabled --- tests/Tags/User/RegisterFormTest.php | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/Tags/User/RegisterFormTest.php b/tests/Tags/User/RegisterFormTest.php index d83f5a7934..e099eae488 100644 --- a/tests/Tags/User/RegisterFormTest.php +++ b/tests/Tags/User/RegisterFormTest.php @@ -2,6 +2,7 @@ namespace Tests\Tags\User; +use Illuminate\Support\Facades\Config; use Statamic\Facades\Blueprint; use Statamic\Facades\Parse; use Statamic\Facades\User; @@ -242,6 +243,49 @@ public function it_will_register_user_and_render_success() $this->assertEmpty($inlineErrors[1]); } + /** @test */ + public function it_will_register_user_and_render_success_even_when_cp_auth_is_disabled() + { + Config::set('statamic.cp.auth', false); + + $this->assertNull(User::findByEmail('san@holo.com')); + $this->assertFalse(auth()->check()); + + $this + ->post('/!/auth/register', [ + 'email' => 'san@holo.com', + 'password' => 'chewbacca', + 'password_confirmation' => 'chewbacca', + ]) + ->assertSessionHasNoErrors() + ->assertLocation('/'); + + $this->assertNotNull(User::findByEmail('san@holo.com')); + $this->assertTrue(auth()->check()); + $this->assertEquals('san@holo.com', auth()->user()->email()); + + $output = $this->tag(<<<'EOT' +{{ user:register_form }} +

{{ success }}

+ {{ errors }} +

{{ value }}

+ {{ /errors }} + {{ fields }} +

{{ error }}

+ {{ /fields }} +{{ /user:register_form }} +EOT + ); + + preg_match_all('/

(.+)<\/p>/U', $output, $success); + preg_match_all('/

(.+)<\/p>/U', $output, $errors); + preg_match_all('/

(.+)<\/p>/U', $output, $inlineErrors); + + $this->assertEquals(['Registration successful.'], $success[1]); + $this->assertEmpty($errors[1]); + $this->assertEmpty($inlineErrors[1]); + } + /** @test */ public function it_will_register_user_and_follow_custom_redirect_with_success() { From 029ac6c2382c251d4b30850a7a2c6dac09b2e60a Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 9 Nov 2023 12:00:51 +0000 Subject: [PATCH 10/19] Ensure users can reset password via tags when CP auth is disabled --- tests/Tags/User/ForgotPasswordFormTest.php | 40 ++++++++++++++++++++++ tests/Tags/User/PasswordFormTest.php | 38 ++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/tests/Tags/User/ForgotPasswordFormTest.php b/tests/Tags/User/ForgotPasswordFormTest.php index c46df2e635..50af294812 100644 --- a/tests/Tags/User/ForgotPasswordFormTest.php +++ b/tests/Tags/User/ForgotPasswordFormTest.php @@ -2,6 +2,7 @@ namespace Tests\Tags\User; +use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Password; use Statamic\Facades\Parse; use Statamic\Facades\User; @@ -143,6 +144,45 @@ public function it_will_send_password_reset_email_and_render_success() $this->assertEquals([__(Password::RESET_LINK_SENT)], $emailSent[1]); } + /** @test */ + public function it_will_send_password_reset_email_and_render_success_even_when_cp_auth_is_disabled() + { + Config::set('statamic.cp.auth', false); + + $this->simulateSuccessfulPasswordResetEmail(); + + User::make() + ->email('san@holo.com') + ->password('chewy') + ->save(); + + $this + ->post('/!/auth/password/email', [ + 'email' => 'san@holo.com', + ]) + ->assertLocation('/'); + + $output = $this->tag(<<<'EOT' +{{ user:forgot_password_form }} + {{ errors }} +

{{ value }}

+ {{ /errors }} + +

{{ success }}

+ +{{ /user:forgot_password_form }} +EOT + ); + + preg_match_all('/

(.+)<\/p>/U', $output, $errors); + preg_match_all('/

(.+)<\/p>/U', $output, $success); + preg_match_all('/

{{ success }}

+ {{ errors }} +

{{ value }}

+ {{ /errors }} + {{ fields }} +

{{ error }}

+ {{ /fields }} +{{ /user:password_form }} +EOT + ); + + preg_match_all('/

(.+)<\/p>/U', $output, $success); + preg_match_all('/

(.+)<\/p>/U', $output, $errors); + preg_match_all('/

(.+)<\/p>/U', $output, $inlineErrors); + + $this->assertEquals(['Change successful.'], $success[1]); + $this->assertEmpty($errors[1]); + $this->assertEmpty($inlineErrors[1]); + } + /** @test */ public function it_will_update_password_and_follow_custom_redirect_with_success() { From 80ccbf5d370d1668e0daf607ae631e310b49d1d2 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 9 Nov 2023 12:59:50 +0000 Subject: [PATCH 11/19] Allow specifying a redirect URL for when authentication is disabled --- config/cp.php | 15 ++++++++++++++- src/Exceptions/AuthenticationException.php | 13 ++++++++++++- tests/Tags/User/ForgotPasswordFormTest.php | 2 +- tests/Tags/User/LoginFormTest.php | 2 +- tests/Tags/User/PasswordFormTest.php | 2 +- tests/Tags/User/RegisterFormTest.php | 2 +- 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/config/cp.php b/config/cp.php index 084907dbf7..39ce3d1bd2 100644 --- a/config/cp.php +++ b/config/cp.php @@ -15,7 +15,20 @@ 'route' => env('CP_ROUTE', 'cp'), - 'auth' => true, + /* + |-------------------------------------------------------------------------- + | Authentication + |-------------------------------------------------------------------------- + | + | Whether the Control Panel's authentication pages should be enabled, + | or if users should be redirected elsewhere. + | + */ + + 'auth' => [ + 'enabled' => true, + 'redirect_to' => null, + ], /* |-------------------------------------------------------------------------- diff --git a/src/Exceptions/AuthenticationException.php b/src/Exceptions/AuthenticationException.php index 9d0e5578fd..a61a019b71 100644 --- a/src/Exceptions/AuthenticationException.php +++ b/src/Exceptions/AuthenticationException.php @@ -11,6 +11,17 @@ public function toResponse($request) { return $request->expectsJson() ? response()->json(['message' => $this->getMessage()], 401) - : (config('statamic.cp.auth', true) ? redirect()->route('statamic.cp.login') : abort(401)); + : $this->handleRedirect(); + } + + protected function handleRedirect() + { + if (! config('statamic.cp.auth.enabled', true)) { + return config('statamic.cp.auth.redirect_to') + ? redirect(config('statamic.cp.auth.redirect_to')) + : abort(401); + } + + return redirect()->route('statamic.cp.login'); } } diff --git a/tests/Tags/User/ForgotPasswordFormTest.php b/tests/Tags/User/ForgotPasswordFormTest.php index 50af294812..74f909974a 100644 --- a/tests/Tags/User/ForgotPasswordFormTest.php +++ b/tests/Tags/User/ForgotPasswordFormTest.php @@ -147,7 +147,7 @@ public function it_will_send_password_reset_email_and_render_success() /** @test */ public function it_will_send_password_reset_email_and_render_success_even_when_cp_auth_is_disabled() { - Config::set('statamic.cp.auth', false); + Config::set('statamic.cp.auth', ['enabled' => false]); $this->simulateSuccessfulPasswordResetEmail(); diff --git a/tests/Tags/User/LoginFormTest.php b/tests/Tags/User/LoginFormTest.php index 75e5e47a7a..73189266b1 100644 --- a/tests/Tags/User/LoginFormTest.php +++ b/tests/Tags/User/LoginFormTest.php @@ -123,7 +123,7 @@ public function it_will_log_user_in_and_render_success() /** @test */ public function it_will_log_user_in_and_render_success_even_when_cp_auth_is_disabled() { - Config::set('statamic.cp.auth', false); + Config::set('statamic.cp.auth', ['enabled' => false]); $this->assertFalse(auth()->check()); diff --git a/tests/Tags/User/PasswordFormTest.php b/tests/Tags/User/PasswordFormTest.php index 8b24a56d50..8760610b9c 100644 --- a/tests/Tags/User/PasswordFormTest.php +++ b/tests/Tags/User/PasswordFormTest.php @@ -205,7 +205,7 @@ public function it_will_update_password_and_render_success() /** @test */ public function it_will_update_password_and_render_success_even_when_cp_auth_is_disabled() { - Config::set('statamic.cp.auth', false); + Config::set('statamic.cp.auth', ['enabled' => false]); $this->actingAs(User::make()->password('mypassword')->save()); diff --git a/tests/Tags/User/RegisterFormTest.php b/tests/Tags/User/RegisterFormTest.php index e099eae488..bcfb66a845 100644 --- a/tests/Tags/User/RegisterFormTest.php +++ b/tests/Tags/User/RegisterFormTest.php @@ -246,7 +246,7 @@ public function it_will_register_user_and_render_success() /** @test */ public function it_will_register_user_and_render_success_even_when_cp_auth_is_disabled() { - Config::set('statamic.cp.auth', false); + Config::set('statamic.cp.auth', ['enabled' => false]); $this->assertNull(User::findByEmail('san@holo.com')); $this->assertFalse(auth()->check()); From b54cb0a3decaddf2b8af2ef6f5e4501440160763 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Sat, 24 Feb 2024 23:12:19 +0000 Subject: [PATCH 12/19] Reference updated config key --- routes/cp.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/cp.php b/routes/cp.php index 80f246c923..3fc84f6114 100644 --- a/routes/cp.php +++ b/routes/cp.php @@ -99,7 +99,7 @@ use Statamic\Statamic; Route::group(['prefix' => 'auth'], function () { - if (config('statamic.cp.auth', true)) { + if (config('statamic.cp.auth.enabled', true)) { Route::get('login', [LoginController::class, 'showLoginForm'])->name('login'); Route::post('login', [LoginController::class, 'login']); From bc10aa2de63140b86838889a486bd0b3a5cfad70 Mon Sep 17 00:00:00 2001 From: Erin Dalzell Date: Sun, 21 Apr 2024 16:56:27 -0700 Subject: [PATCH 13/19] Make sure `intended` is set --- src/Exceptions/AuthenticationException.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Exceptions/AuthenticationException.php b/src/Exceptions/AuthenticationException.php index a61a019b71..ed3a97d6ac 100644 --- a/src/Exceptions/AuthenticationException.php +++ b/src/Exceptions/AuthenticationException.php @@ -4,6 +4,7 @@ use Illuminate\Auth\AuthenticationException as Exception; use Illuminate\Contracts\Support\Responsable; +use Illuminate\Support\Facades\Redirect; class AuthenticationException extends Exception implements Responsable { @@ -18,7 +19,7 @@ protected function handleRedirect() { if (! config('statamic.cp.auth.enabled', true)) { return config('statamic.cp.auth.redirect_to') - ? redirect(config('statamic.cp.auth.redirect_to')) + ? Redirect::guest(config('statamic.cp.auth.redirect_to')) : abort(401); } From 4711a500b3e22e3cc826929f0942a4a30a0096bd Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 22 Apr 2024 11:55:06 +0100 Subject: [PATCH 14/19] use the helper instead --- src/Exceptions/AuthenticationException.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Exceptions/AuthenticationException.php b/src/Exceptions/AuthenticationException.php index ed3a97d6ac..35f36e9080 100644 --- a/src/Exceptions/AuthenticationException.php +++ b/src/Exceptions/AuthenticationException.php @@ -4,7 +4,6 @@ use Illuminate\Auth\AuthenticationException as Exception; use Illuminate\Contracts\Support\Responsable; -use Illuminate\Support\Facades\Redirect; class AuthenticationException extends Exception implements Responsable { @@ -19,7 +18,7 @@ protected function handleRedirect() { if (! config('statamic.cp.auth.enabled', true)) { return config('statamic.cp.auth.redirect_to') - ? Redirect::guest(config('statamic.cp.auth.redirect_to')) + ? redirect()->guest(config('statamic.cp.auth.redirect_to')) : abort(401); } From 54a3e835e41636767e8596e96fa93d2280d25d41 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Mon, 13 May 2024 12:24:36 +0100 Subject: [PATCH 15/19] Redirect to login page after session expires --- resources/js/components/SessionExpiry.vue | 16 +++++++++++++--- .../views/partials/session-expiry.blade.php | 3 ++- .../View/Composers/SessionExpiryComposer.php | 1 + 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/resources/js/components/SessionExpiry.vue b/resources/js/components/SessionExpiry.vue index b8d82202ee..f8885993b5 100644 --- a/resources/js/components/SessionExpiry.vue +++ b/resources/js/components/SessionExpiry.vue @@ -59,7 +59,8 @@ export default { warnAt: Number, lifetime: Number, email: String, - oauthProvider: String + oauthProvider: String, + auth: Object, }, data() { @@ -102,7 +103,16 @@ export default { watch: { count(count) { - this.isShowingLogin = this.remaining <= 0; + let hasSessionExpired = this.remaining <= 0; + + // When the user's session has expired and Statamic's auth is disabled, + // we'll redirect to the login page. + if (hasSessionExpired && ! this.auth.enabled) { + window.location.href = this.auth.redirect_to; + return; + } + + this.isShowingLogin = hasSessionExpired; // While we're in the warning period, we'll check every second so that any // activity in another tab is picked up and the count will get restarted. @@ -120,7 +130,7 @@ export default { } this.lastCount = Vue.moment(); - } + }, }, diff --git a/resources/views/partials/session-expiry.blade.php b/resources/views/partials/session-expiry.blade.php index de483bb616..29488f3310 100644 --- a/resources/views/partials/session-expiry.blade.php +++ b/resources/views/partials/session-expiry.blade.php @@ -3,4 +3,5 @@ :warn-at="{{ $warnAt }}" :lifetime="{{ $lifetime }}" :oauth-provider="{{ json_encode($oauth) }}" -> \ No newline at end of file + :auth="{{ json_encode($auth) }}" +> diff --git a/src/Http/View/Composers/SessionExpiryComposer.php b/src/Http/View/Composers/SessionExpiryComposer.php index 1f7a5cd3f6..45d81ecf86 100644 --- a/src/Http/View/Composers/SessionExpiryComposer.php +++ b/src/Http/View/Composers/SessionExpiryComposer.php @@ -17,6 +17,7 @@ public function compose(View $view) 'lifetime' => config('session.lifetime') * 60, 'warnAt' => 60, 'oauth' => $this->oauth(), + 'auth' => config('statamic.cp.auth'), ]); } From 047c97dd0ef80a66cfc3b1780025ebde4df6ed2d Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Thu, 27 Jun 2024 15:27:06 -0400 Subject: [PATCH 16/19] Dont redirect at zero ... Instead, redirect when there's an actual 401. Apparently it's not completely accurate. You could get a few requests that return zero. It's fine for controlling when the modal pops up, but not a redirect. If you redirect, when you hit the login page your session will be extended, keeping you logged in. --- resources/js/components/SessionExpiry.vue | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/resources/js/components/SessionExpiry.vue b/resources/js/components/SessionExpiry.vue index 6e627246c1..d932542894 100644 --- a/resources/js/components/SessionExpiry.vue +++ b/resources/js/components/SessionExpiry.vue @@ -103,16 +103,7 @@ export default { watch: { count(count) { - let hasSessionExpired = this.remaining <= 0; - - // When the user's session has expired and Statamic's auth is disabled, - // we'll redirect to the login page. - if (hasSessionExpired && ! this.auth.enabled) { - window.location.href = this.auth.redirect_to; - return; - } - - this.isShowingLogin = hasSessionExpired; + this.isShowingLogin = this.auth.enabled && this.remaining <= 0; // While we're in the warning period, we'll check every second so that any // activity in another tab is picked up and the count will get restarted. @@ -130,7 +121,7 @@ export default { } this.lastCount = Vue.moment(); - }, + } }, @@ -155,7 +146,10 @@ export default { return this.$axios.get(cp_url('session-timeout')).then(response => { this.count = this.remaining = response.data; }).catch(e => { - if (e.response.status === 401) this.remaining = 0; + if (e.response.status === 401) { + this.remaining = 0; + if (!this.auth.enabled) window.location = this.auth.redirect_to || '/'; + } throw e; }).finally(response => { this.pinging = false; From a5198ed57aaa74350e4fe6890467f47a17ef9e63 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Thu, 27 Jun 2024 15:39:33 -0400 Subject: [PATCH 17/19] these tests dont really prove anything --- tests/Tags/User/ForgotPasswordFormTest.php | 40 -------------------- tests/Tags/User/LoginFormTest.php | 41 -------------------- tests/Tags/User/PasswordFormTest.php | 38 ------------------- tests/Tags/User/RegisterFormTest.php | 44 ---------------------- 4 files changed, 163 deletions(-) diff --git a/tests/Tags/User/ForgotPasswordFormTest.php b/tests/Tags/User/ForgotPasswordFormTest.php index 780bfd41a6..4c2ab6c4cb 100644 --- a/tests/Tags/User/ForgotPasswordFormTest.php +++ b/tests/Tags/User/ForgotPasswordFormTest.php @@ -2,7 +2,6 @@ namespace Tests\Tags\User; -use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Password; use PHPUnit\Framework\Attributes\Test; use Statamic\Facades\Parse; @@ -145,45 +144,6 @@ public function it_will_send_password_reset_email_and_render_success() $this->assertEquals([__(Password::RESET_LINK_SENT)], $emailSent[1]); } - #[Test] - public function it_will_send_password_reset_email_and_render_success_even_when_cp_auth_is_disabled() - { - Config::set('statamic.cp.auth', ['enabled' => false]); - - $this->simulateSuccessfulPasswordResetEmail(); - - User::make() - ->email('san@holo.com') - ->password('chewy') - ->save(); - - $this - ->post('/!/auth/password/email', [ - 'email' => 'san@holo.com', - ]) - ->assertLocation('/'); - - $output = $this->tag(<<<'EOT' -{{ user:forgot_password_form }} - {{ errors }} -

{{ value }}

- {{ /errors }} - -

{{ success }}

- -{{ /user:forgot_password_form }} -EOT - ); - - preg_match_all('/

(.+)<\/p>/U', $output, $errors); - preg_match_all('/

(.+)<\/p>/U', $output, $success); - preg_match_all('/

{{ value }}

- {{ /errors }} - -

{{ success }}

- {{ /user:login_form }} - EOT - ); - - preg_match_all('/

(.+)<\/p>/U', $output, $errors); - preg_match_all('/

(.+)<\/p>/U', $output, $success); - - $this->assertEmpty($errors[1]); - $this->assertEquals(['Login successful.'], $success[1]); - } - #[Test] public function it_will_log_user_in_and_follow_custom_redirect_with_success() { diff --git a/tests/Tags/User/PasswordFormTest.php b/tests/Tags/User/PasswordFormTest.php index aa1f79cda6..da22402334 100644 --- a/tests/Tags/User/PasswordFormTest.php +++ b/tests/Tags/User/PasswordFormTest.php @@ -2,7 +2,6 @@ namespace Tests\Tags\User; -use Illuminate\Support\Facades\Config; use PHPUnit\Framework\Attributes\Test; use Statamic\Facades\Parse; use Statamic\Facades\User; @@ -203,43 +202,6 @@ public function it_will_update_password_and_render_success() $this->assertEmpty($inlineErrors[1]); } - #[Test] - public function it_will_update_password_and_render_success_even_when_cp_auth_is_disabled() - { - Config::set('statamic.cp.auth', ['enabled' => false]); - - $this->actingAs(User::make()->password('mypassword')->save()); - - $this - ->post('/!/auth/password', [ - 'current_password' => 'mypassword', - 'password' => 'newpassword', - 'password_confirmation' => 'newpassword', - ]) - ->assertSessionHasNoErrors(); - - $output = $this->tag(<<<'EOT' -{{ user:password_form }} -

{{ success }}

- {{ errors }} -

{{ value }}

- {{ /errors }} - {{ fields }} -

{{ error }}

- {{ /fields }} -{{ /user:password_form }} -EOT - ); - - preg_match_all('/

(.+)<\/p>/U', $output, $success); - preg_match_all('/

(.+)<\/p>/U', $output, $errors); - preg_match_all('/

(.+)<\/p>/U', $output, $inlineErrors); - - $this->assertEquals(['Change successful.'], $success[1]); - $this->assertEmpty($errors[1]); - $this->assertEmpty($inlineErrors[1]); - } - #[Test] public function it_will_update_password_and_follow_custom_redirect_with_success() { diff --git a/tests/Tags/User/RegisterFormTest.php b/tests/Tags/User/RegisterFormTest.php index 4f661e1c19..b25fa1b81e 100644 --- a/tests/Tags/User/RegisterFormTest.php +++ b/tests/Tags/User/RegisterFormTest.php @@ -2,7 +2,6 @@ namespace Tests\Tags\User; -use Illuminate\Support\Facades\Config; use PHPUnit\Framework\Attributes\Test; use Statamic\Facades\Blueprint; use Statamic\Facades\Parse; @@ -246,49 +245,6 @@ public function it_will_register_user_and_render_success() $this->assertEmpty($inlineErrors[1]); } - #[Test] - public function it_will_register_user_and_render_success_even_when_cp_auth_is_disabled() - { - Config::set('statamic.cp.auth', ['enabled' => false]); - - $this->assertNull(User::findByEmail('san@holo.com')); - $this->assertFalse(auth()->check()); - - $this - ->post('/!/auth/register', [ - 'email' => 'san@holo.com', - 'password' => 'chewbacca', - 'password_confirmation' => 'chewbacca', - ]) - ->assertSessionHasNoErrors() - ->assertLocation('/'); - - $this->assertNotNull(User::findByEmail('san@holo.com')); - $this->assertTrue(auth()->check()); - $this->assertEquals('san@holo.com', auth()->user()->email()); - - $output = $this->tag(<<<'EOT' -{{ user:register_form }} -

{{ success }}

- {{ errors }} -

{{ value }}

- {{ /errors }} - {{ fields }} -

{{ error }}

- {{ /fields }} -{{ /user:register_form }} -EOT - ); - - preg_match_all('/

(.+)<\/p>/U', $output, $success); - preg_match_all('/

(.+)<\/p>/U', $output, $errors); - preg_match_all('/

(.+)<\/p>/U', $output, $inlineErrors); - - $this->assertEquals(['Registration successful.'], $success[1]); - $this->assertEmpty($errors[1]); - $this->assertEmpty($inlineErrors[1]); - } - #[Test] public function it_will_register_user_and_follow_custom_redirect_with_success() { From d4023658be70b773df740c4a5924cd0a07c15ac6 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Thu, 27 Jun 2024 15:41:06 -0400 Subject: [PATCH 18/19] words --- config/cp.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/cp.php b/config/cp.php index 97ae43b8cb..be058d6e9a 100644 --- a/config/cp.php +++ b/config/cp.php @@ -21,7 +21,7 @@ |-------------------------------------------------------------------------- | | Whether the Control Panel's authentication pages should be enabled, - | or if users should be redirected elsewhere. + | and where users should be redirected in order to authenticate. | */ From 710a1917eb22c3d2eb3e507d387739cc85403b67 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Thu, 27 Jun 2024 15:48:22 -0400 Subject: [PATCH 19/19] add tests --- tests/Feature/AuthenticationTest.php | 37 ++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 tests/Feature/AuthenticationTest.php diff --git a/tests/Feature/AuthenticationTest.php b/tests/Feature/AuthenticationTest.php new file mode 100644 index 0000000000..c416ffd1a3 --- /dev/null +++ b/tests/Feature/AuthenticationTest.php @@ -0,0 +1,37 @@ +getJson('/cp/anything')->assertStatus(401)->assertJson(['message' => 'Unauthenticated.']); + } + + #[Test] + public function redirects_to_login_page() + { + $this->get('/cp/anything')->assertRedirect('/cp/auth/login'); + } + + #[Test] + public function redirects_to_defined_login_page_when_auth_is_disabled() + { + config(['statamic.cp.auth' => ['enabled' => false, 'redirect_to' => '/my-login-page']]); + + $this->get('/cp/anything')->assertRedirect('/my-login-page'); + } + + #[Test] + public function responds_with_401_when_auth_is_disabled_and_no_redirect_is_defined() + { + config(['statamic.cp.auth' => ['enabled' => false]]); + + $this->get('/cp/anything')->assertStatus(401); + } +}