From 0ddebb817761e7f2f5dc7652ecc4a2853e4db455 Mon Sep 17 00:00:00 2001 From: Martin Kluska Date: Tue, 29 Nov 2022 14:20:11 +0100 Subject: [PATCH] feat(User): Add ability to auto login a user via Auto-Login header in local environment 1. Use `LaraStrict\User\Http\Middlewares\Authenticate` middleware 2. Implement `GetUserForAutoLoginActionContract` (add it to App/User/Actions) 3. Bind your implementation in service provider (your UserServiceProvider or AppServiceProvider) ```php class UserServiceProvider extends ServiceProvider { public array $bindings = [ GetUserForAutoLoginActionContract::class => GetUserForAutoLoginAction::class, ]; } ``` --- .../GetUserForAutoLoginActionContract.php | 12 +++ src/User/Http/Middlewares/Authenticate.php | 73 +++++++++++++ .../Http/Middlewares/AuthenticateTest.php | 102 ++++++++++++++++++ .../GetUserForAutoLoginTestAction.php | 24 +++++ 4 files changed, 211 insertions(+) create mode 100644 src/User/Contracts/GetUserForAutoLoginActionContract.php create mode 100644 src/User/Http/Middlewares/Authenticate.php create mode 100644 tests/Feature/User/Http/Middlewares/AuthenticateTest.php create mode 100644 tests/Feature/User/Http/Middlewares/GetUserForAutoLoginTestAction.php diff --git a/src/User/Contracts/GetUserForAutoLoginActionContract.php b/src/User/Contracts/GetUserForAutoLoginActionContract.php new file mode 100644 index 00000000..57efa108 --- /dev/null +++ b/src/User/Contracts/GetUserForAutoLoginActionContract.php @@ -0,0 +1,12 @@ +autoLoginFirstUserOnLocalIfNeeded($request, $guards); + + parent::authenticate($request, $guards); + } + + /** + * Get the path the user should be redirected to when they are not authenticated. + * + * @param Request $request + * + * @return string|null + */ + protected function redirectTo($request) + { + if ($request->expectsJson() === false) { + return route('login'); + } + } + + protected function autoLoginFirstUserOnLocalIfNeeded(Request $request, array $guards): void + { + $autoLogin = $request->header('Auto-Login'); + if ($this->application->environment([EnvironmentType::Local->value]) === false || + $autoLogin === null) { + return; + } + + foreach ($guards as $guard) { + $guardInstance = $this->auth->guard($guard); + + if ($guardInstance->check()) { + break; + } + + $user = $this->getUserForAutoLoginActionContract->execute($autoLogin); + + if ($user !== null) { + $guardInstance->setUser($user); + } + + break; + } + } +} diff --git a/tests/Feature/User/Http/Middlewares/AuthenticateTest.php b/tests/Feature/User/Http/Middlewares/AuthenticateTest.php new file mode 100644 index 00000000..b8e695cb --- /dev/null +++ b/tests/Feature/User/Http/Middlewares/AuthenticateTest.php @@ -0,0 +1,102 @@ + [ + static fn (self $self) => $self->assert(value: '', expectedValue: '',), + ], + 'true' => [ + static fn (self $self) => $self->assert(value: true, expectedValue: '1',), + ], + 'false' => [ + static fn (self $self) => $self->assert(value: false, expectedValue: '',), + ], + 'true string' => [ + static fn (self $self) => $self->assert(value: 'true', expectedValue: 'true',), + ], + 'false string' => [ + static fn (self $self) => $self->assert(value: 'false', expectedValue: 'false',), + ], + 'null' => [ + static fn (self $self) => $self->assert(value: null, expectedValue: null,), + ], + 'valid but different env - testing' => [ + static fn (self $self) => $self->assert( + value: '1', + expectedValue: null, + environment: EnvironmentType::Testing, + ), + ], + 'valid but different env - production' => [ + static fn (self $self) => $self->assert( + value: '1', + expectedValue: null, + environment: EnvironmentType::Production, + ), + ], + ]; + } + + public function assert( + string|bool|null $value, + string|null $expectedValue, + EnvironmentType $environment = EnvironmentType::Local, + ): void { + $this->app() + ->detectEnvironment(static fn () => $environment->value); + + $request = $this->createPostRequest( + application: $this->app(), + requestClass: TestRequest::class, + data: [ + 'test' => 'test', + ], + server: $value === null ? [] : [ + 'HTTP_Auto-Login' => $value, + ] + ); + + $this->app() + ->bind( + GetUserForAutoLoginActionContract::class, + static fn () => new GetUserForAutoLoginTestAction($expectedValue) + ); + + /** @var Authenticate $middleware */ + $middleware = $this->app() + ->make(Authenticate::class); + + if ($expectedValue === null) { + $this->expectException(AuthenticationException::class); + $middleware->handle($request, static function () { + }); + return; + } + + $called = false; + $middleware->handle($request, static function () use (&$called) { + $called = true; + }); + $this->assertEquals(true, $called); + } +} diff --git a/tests/Feature/User/Http/Middlewares/GetUserForAutoLoginTestAction.php b/tests/Feature/User/Http/Middlewares/GetUserForAutoLoginTestAction.php new file mode 100644 index 00000000..2f2b0cc4 --- /dev/null +++ b/tests/Feature/User/Http/Middlewares/GetUserForAutoLoginTestAction.php @@ -0,0 +1,24 @@ +expectedAutoLogin, $autoLogin, 'Auto login value'); + return new User(); + } +}