diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php deleted file mode 100644 index 8dec071d..00000000 --- a/app/Http/Controllers/Auth/LoginController.php +++ /dev/null @@ -1,60 +0,0 @@ -middleware('guest')->except('logout'); - } - - public function showLoginForm(): Response - { - session()->put('title', 'Anmelden'); - - return \Inertia::render('authentication/VLogin'); - } - - /** - * Validate the user login request. - */ - protected function validateLogin(Request $request): void - { - $request->validate([ - $this->username() => 'required|max:255|string|email', - 'password' => 'required|string', - ]); - } -} diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php deleted file mode 100644 index edb9c4a4..00000000 --- a/app/Http/Controllers/Auth/RegisterController.php +++ /dev/null @@ -1,71 +0,0 @@ -middleware('guest'); - } - - /** - * Get a validator for an incoming registration request. - * - * @return \Illuminate\Contracts\Validation\Validator - */ - protected function validator(array $data) - { - return Validator::make($data, [ - 'name' => ['required', 'string', 'max:255'], - 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], - 'password' => ['required', 'string', 'min:8', 'confirmed'], - ]); - } - - /** - * Create a new user instance after a valid registration. - * - * @return \App\User - */ - protected function create(array $data) - { - return User::create([ - 'name' => $data['name'], - 'email' => $data['email'], - 'password' => Hash::make($data['password']), - ]); - } -} diff --git a/app/View/Page/Full.php b/app/View/Page/Full.php new file mode 100644 index 00000000..27ca702c --- /dev/null +++ b/app/View/Page/Full.php @@ -0,0 +1,32 @@ +put('title', $title); + } + + public function render() + { + return <<<'HTML' +
+ @if ($heading) +
+ {{$heading}} + +
+ @endif + +
+ {{ $slot }} +
+
+ HTML; + } +} diff --git a/app/View/Ui/Button.php b/app/View/Ui/Button.php new file mode 100644 index 00000000..e1f1129e --- /dev/null +++ b/app/View/Ui/Button.php @@ -0,0 +1,22 @@ + + {{$slot}} + + HTML; + } +} diff --git a/config/app.php b/config/app.php index 175422f4..67ccda3b 100644 --- a/config/app.php +++ b/config/app.php @@ -183,6 +183,7 @@ return [ Modules\Invoice\InvoiceServiceProvider::class, Modules\Mailgateway\MailgatewayServiceProvider::class, Modules\Nami\NamiServiceProvider::class, + Modules\Auth\AuthServiceProvider::class, ], /* diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index a8bd37bb..fc90d337 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -27,4 +27,9 @@ class UserFactory extends Factory 'lastname' => $this->faker->lastName, ]; } + + public function loginData(string $email, string $password): self + { + return $this->state(['email' => $email, 'password' => Hash::make($password)]); + } } diff --git a/modules/Auth/AuthServiceProvider.php b/modules/Auth/AuthServiceProvider.php new file mode 100644 index 00000000..85e58e7d --- /dev/null +++ b/modules/Auth/AuthServiceProvider.php @@ -0,0 +1,31 @@ +middleware(['web', 'guest'])->group(function ($router) { + $router->get('/login', LoginForm::class)->name('login'); + }); + } +} diff --git a/modules/Auth/Components/LoginForm.php b/modules/Auth/Components/LoginForm.php new file mode 100644 index 00000000..3a6d2f8f --- /dev/null +++ b/modules/Auth/Components/LoginForm.php @@ -0,0 +1,62 @@ +validate([ + 'email' => 'required|max:255|string|email', + 'password' => 'required|string', + ]); + } + + protected function credentials(Request $request) + { + return ['email' => $this->email, 'password' => $this->password]; + } + + protected function sendLoginResponse(Request $request) + { + return redirect()->intended('/'); + } + + public function submit() + { + return $this->login(request()); + } + + #[Layout('components.layouts.full')] + public function render(): string + { + return <<<'HTML' + +
+
+ + + Login +
+ +
+
+
+
+ HTML; + } +} diff --git a/modules/Auth/Components/LoginFormTest.php b/modules/Auth/Components/LoginFormTest.php new file mode 100644 index 00000000..318598cd --- /dev/null +++ b/modules/Auth/Components/LoginFormTest.php @@ -0,0 +1,69 @@ +get('/')->assertRedirect('/login'); +}); + +it('displays component', function () { + test()->get('/login')->assertSeeLivewire(LoginForm::class)->assertDontSee('Dashboard'); +}); + +it('displays form', function () { + Livewire::test(LoginForm::class) + ->assertSee('Login') + ->assertSee('Passwort vergessen'); +}); + +it('loggs in', function () { + User::factory()->loginData('admin@example.com', 'secret')->create(); + Livewire::test(LoginForm::class) + ->set('email', 'admin@example.com') + ->set('password', 'secret') + ->call('submit') + ->assertRedirect('/'); + + $this->assertEquals('admin@example.com', auth()->user()->email); +}); + +it('displays failed login response', function () { + Livewire::test(LoginForm::class) + ->set('email', 'admin@example.com') + ->set('password', 'secret') + ->call('submit') + ->assertHasErrors(['email' => 'Login fehlgeschlagen.']); +}); + +it('increments login attempts', function () { + Event::fake([Lockout::class]); + Livewire::test(LoginForm::class) + ->set('email', 'admin@example.com') + ->set('password', 'secret') + ->call('submit') + ->call('submit') + ->call('submit') + ->call('submit') + ->call('submit') + ->call('submit'); + Event::assertDispatchedTimes(Lockout::class); +}); + +it('requires email and password', function () { + User::factory()->loginData('admin@example.com', 'secret')->create(); + Livewire::test(LoginForm::class) + ->set('email', '') + ->set('password', '') + ->call('submit') + ->assertHasErrors(['email', 'password']); +}); diff --git a/resources/js/components/page/FullHeadingBanner.vue b/resources/js/components/page/FullHeadingBanner.vue deleted file mode 100644 index f686f8bb..00000000 --- a/resources/js/components/page/FullHeadingBanner.vue +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/resources/js/views/VLogin.vue b/resources/js/views/VLogin.vue deleted file mode 100644 index c65ea511..00000000 --- a/resources/js/views/VLogin.vue +++ /dev/null @@ -1,36 +0,0 @@ - - - diff --git a/resources/js/views/authentication/VLogin.vue b/resources/js/views/authentication/VLogin.vue deleted file mode 100644 index 9d4ee1d4..00000000 --- a/resources/js/views/authentication/VLogin.vue +++ /dev/null @@ -1,39 +0,0 @@ - - - diff --git a/resources/lang/de/auth.php b/resources/lang/de/auth.php index 24d74c85..cf287535 100644 --- a/resources/lang/de/auth.php +++ b/resources/lang/de/auth.php @@ -12,6 +12,6 @@ return [ | */ - 'failed' => 'Diese Kombination aus Zugangsdaten wurde nicht in unserer Datenbank gefunden.', + 'failed' => 'Login fehlgeschlagen.', 'throttle' => 'Zu viele Loginversuche. Versuchen Sie es bitte in :seconds Sekunden nochmal.', ]; diff --git a/resources/views/components/layouts/full.blade.php b/resources/views/components/layouts/full.blade.php new file mode 100644 index 00000000..a2102f9d --- /dev/null +++ b/resources/views/components/layouts/full.blade.php @@ -0,0 +1,8 @@ + + + + + {{ $slot }} + @livewireScriptConfig + + diff --git a/routes/web.php b/routes/web.php index 21d0ac81..cb4725d3 100644 --- a/routes/web.php +++ b/routes/web.php @@ -76,9 +76,7 @@ use App\Membership\Actions\MembershipStoreAction; use App\Membership\Actions\MembershipUpdateAction; use App\Payment\SubscriptionController; -Route::group(['namespace' => 'App\\Http\\Controllers'], function (): void { - Auth::routes(['register' => false]); -}); + Route::group(['middleware' => 'auth:web'], function (): void { Route::post('/nami/login-check', NamiLoginCheckAction::class)->name('nami.login-check');