From f41f91a124c4709168c0ea98377a2a7b229bf103 Mon Sep 17 00:00:00 2001 From: Stephen Jacques Date: Thu, 7 Nov 2024 12:23:58 +0100 Subject: [PATCH 1/6] chore: Apply rebase develop --- app/Actions/Fortify/CreateNewUser.php | 53 -- .../Fortify/PasswordValidationRules.php | 26 - app/Actions/Fortify/ResetUserPassword.php | 26 - app/Actions/Fortify/UpdateUserPassword.php | 31 - .../Fortify/UpdateUserProfileInformation.php | 48 -- .../Auth/VerifyEmailController.php | 27 + app/Providers/FortifyServiceProvider.php | 47 -- app/Providers/VoltServiceProvider.php | 28 + composer.json | 5 +- composer.lock | 551 ++++++++---------- config/app.php | 1 - config/fortify.php | 147 ----- .../pages}/auth/forgot-password.blade.php | 0 .../{ => livewire/pages}/auth/login.blade.php | 84 ++- .../pages}/auth/register.blade.php | 0 .../pages}/auth/reset-password.blade.php | 0 .../pages}/auth/verify-email.blade.php | 0 routes/auth.php | 31 + routes/web.php | 2 + tests/Feature/Auth/AuthenticationTest.php | 72 +++ tests/Feature/Auth/EmailVerificationTest.php | 46 ++ .../Feature/Auth/PasswordConfirmationTest.php | 46 ++ tests/Feature/Auth/PasswordResetTest.php | 73 +++ tests/Feature/Auth/PasswordUpdateTest.php | 41 ++ tests/Feature/Auth/RegistrationTest.php | 27 + 25 files changed, 710 insertions(+), 702 deletions(-) delete mode 100644 app/Actions/Fortify/CreateNewUser.php delete mode 100644 app/Actions/Fortify/PasswordValidationRules.php delete mode 100644 app/Actions/Fortify/ResetUserPassword.php delete mode 100644 app/Actions/Fortify/UpdateUserPassword.php delete mode 100644 app/Actions/Fortify/UpdateUserProfileInformation.php create mode 100644 app/Http/Controllers/Auth/VerifyEmailController.php delete mode 100644 app/Providers/FortifyServiceProvider.php create mode 100644 app/Providers/VoltServiceProvider.php delete mode 100644 config/fortify.php rename resources/views/{ => livewire/pages}/auth/forgot-password.blade.php (100%) rename resources/views/{ => livewire/pages}/auth/login.blade.php (52%) rename resources/views/{ => livewire/pages}/auth/register.blade.php (100%) rename resources/views/{ => livewire/pages}/auth/reset-password.blade.php (100%) rename resources/views/{ => livewire/pages}/auth/verify-email.blade.php (100%) create mode 100644 routes/auth.php create mode 100644 tests/Feature/Auth/AuthenticationTest.php create mode 100644 tests/Feature/Auth/EmailVerificationTest.php create mode 100644 tests/Feature/Auth/PasswordConfirmationTest.php create mode 100644 tests/Feature/Auth/PasswordResetTest.php create mode 100644 tests/Feature/Auth/PasswordUpdateTest.php create mode 100644 tests/Feature/Auth/RegistrationTest.php diff --git a/app/Actions/Fortify/CreateNewUser.php b/app/Actions/Fortify/CreateNewUser.php deleted file mode 100644 index dc91af02..00000000 --- a/app/Actions/Fortify/CreateNewUser.php +++ /dev/null @@ -1,53 +0,0 @@ - ['required', 'string', 'max:255'], - 'email' => [ - 'required', - 'string', - 'email', - 'max:255', - Rule::unique(User::class), - ], - 'username' => [ - 'required', - 'string', - 'min:6', - 'max:20', - 'alpha_dash', - Rule::unique(User::class, 'username'), - ], - 'password' => $this->passwordRules(), - ])->validate(); - - /** @var User $user */ - $user = User::create([ - 'name' => $input['name'], - 'email' => $input['email'], - 'username' => Str::lower($input['username']), - 'password' => Hash::make($input['password']), - 'opt_in' => isset($input['opt_in']), - ]); - - $user->assignRole('user'); - - return $user; - } -} diff --git a/app/Actions/Fortify/PasswordValidationRules.php b/app/Actions/Fortify/PasswordValidationRules.php deleted file mode 100644 index 50f9221a..00000000 --- a/app/Actions/Fortify/PasswordValidationRules.php +++ /dev/null @@ -1,26 +0,0 @@ - - */ - protected function passwordRules(): array - { - return [ - 'required', - 'string', - ! app()->environment('production') ? - Rules\Password::min(6) : - Rules\Password::min(6)->uncompromised(), - ]; - } -} diff --git a/app/Actions/Fortify/ResetUserPassword.php b/app/Actions/Fortify/ResetUserPassword.php deleted file mode 100644 index 45d145ae..00000000 --- a/app/Actions/Fortify/ResetUserPassword.php +++ /dev/null @@ -1,26 +0,0 @@ - $this->passwordRules(), - ])->validate(); - - $user->forceFill([ - 'password' => Hash::make($input['password']), - ])->save(); - } -} diff --git a/app/Actions/Fortify/UpdateUserPassword.php b/app/Actions/Fortify/UpdateUserPassword.php deleted file mode 100644 index f615e631..00000000 --- a/app/Actions/Fortify/UpdateUserPassword.php +++ /dev/null @@ -1,31 +0,0 @@ - ['required', 'string'], - 'password' => $this->passwordRules(), - ])->after(function ($validator) use ($user, $input): void { - if (! isset($input['current_password']) || ! Hash::check($input['current_password'], $user->password)) { - $validator->errors()->add('current_password', __('Le mot de passe fourni ne correspond pas à votre mot de passe actuel.')); - } - })->validateWithBag('updatePassword'); - - $user->forceFill([ - 'password' => Hash::make($input['password']), - ])->save(); - } -} diff --git a/app/Actions/Fortify/UpdateUserProfileInformation.php b/app/Actions/Fortify/UpdateUserProfileInformation.php deleted file mode 100644 index bb88729c..00000000 --- a/app/Actions/Fortify/UpdateUserProfileInformation.php +++ /dev/null @@ -1,48 +0,0 @@ - ['required', 'string', 'max:255'], - - 'email' => [ - 'required', - 'string', - 'email', - 'max:255', - Rule::unique('users')->ignore($user->id), - ], - ])->validateWithBag('updateProfileInformation'); - - if ($input['email'] !== $user->email) { - $this->updateVerifiedUser($user, $input); - } else { - $user->forceFill([ - 'name' => $input['name'], - 'email' => $input['email'], - ])->save(); - } - } - - private function updateVerifiedUser(User $user, array $input): void - { - $user->forceFill([ - 'name' => $input['name'], - 'email' => $input['email'], - 'email_verified_at' => null, - ])->save(); - - $user->sendEmailVerificationNotification(); - } -} diff --git a/app/Http/Controllers/Auth/VerifyEmailController.php b/app/Http/Controllers/Auth/VerifyEmailController.php new file mode 100644 index 00000000..784765e3 --- /dev/null +++ b/app/Http/Controllers/Auth/VerifyEmailController.php @@ -0,0 +1,27 @@ +user()->hasVerifiedEmail()) { + return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); + } + + if ($request->user()->markEmailAsVerified()) { + event(new Verified($request->user())); + } + + return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); + } +} diff --git a/app/Providers/FortifyServiceProvider.php b/app/Providers/FortifyServiceProvider.php deleted file mode 100644 index af8a3d28..00000000 --- a/app/Providers/FortifyServiceProvider.php +++ /dev/null @@ -1,47 +0,0 @@ - view('auth.login')); - Fortify::registerView(fn () => view('auth.register')); - Fortify::requestPasswordResetLinkView(fn () => view('auth.forgot-password')); - Fortify::resetPasswordView(fn (Request $request) => view('auth.reset-password', ['request' => $request])); - Fortify::verifyEmailView(fn () => view('auth.verify-email')); - - Fortify::createUsersUsing(CreateNewUser::class); - Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class); - Fortify::updateUserPasswordsUsing(UpdateUserPassword::class); - Fortify::resetUserPasswordsUsing(ResetUserPassword::class); - - RateLimiter::for( - 'login', - fn (Request $request) => Limit::perMinute(5) - ->by($request->email.$request->ip()) - ); - - RateLimiter::for( - 'two-factor', - fn (Request $request) => Limit::perMinute(5) - ->by($request->session()->get('login.id')) - ); - } -} diff --git a/app/Providers/VoltServiceProvider.php b/app/Providers/VoltServiceProvider.php new file mode 100644 index 00000000..e61d9845 --- /dev/null +++ b/app/Providers/VoltServiceProvider.php @@ -0,0 +1,28 @@ +=7.1 <9.0" - }, - "require-dev": { - "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", - "squizlabs/php_codesniffer": "*" - }, - "type": "library", - "autoload": { - "psr-4": { - "DASPRiD\\Enum\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-2-Clause" - ], - "authors": [ - { - "name": "Ben Scholzen 'DASPRiD'", - "email": "mail@dasprids.de", - "homepage": "https://dasprids.de/", - "role": "Developer" - } - ], - "description": "PHP 7.1 enum implementation", - "keywords": [ - "enum", - "map" - ], - "support": { - "issues": "https://github.com/DASPRiD/Enum/issues", - "source": "https://github.com/DASPRiD/Enum/tree/1.0.6" - }, - "time": "2024-08-09T14:30:48+00:00" - }, { "name": "daverandom/libdns", "version": "v2.1.0", @@ -2592,16 +2488,16 @@ }, { "name": "filament/actions", - "version": "v3.2.122", + "version": "v3.2.123", "source": { "type": "git", "url": "https://github.com/filamentphp/actions.git", - "reference": "3badf1a1589bf70fdc625130f6dfc1ca2146a32f" + "reference": "de0a2c9d453ceb6546b1a804a8240d0b8367b1ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/actions/zipball/3badf1a1589bf70fdc625130f6dfc1ca2146a32f", - "reference": "3badf1a1589bf70fdc625130f6dfc1ca2146a32f", + "url": "https://api.github.com/repos/filamentphp/actions/zipball/de0a2c9d453ceb6546b1a804a8240d0b8367b1ce", + "reference": "de0a2c9d453ceb6546b1a804a8240d0b8367b1ce", "shasum": "" }, "require": { @@ -2641,20 +2537,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-10-31T13:38:12+00:00" + "time": "2024-11-06T08:50:46+00:00" }, { "name": "filament/filament", - "version": "v3.2.122", + "version": "v3.2.123", "source": { "type": "git", "url": "https://github.com/filamentphp/panels.git", - "reference": "076f5367a3dfe5f6864d117f6826ca7821586931" + "reference": "5c4bf4225106e5d2a1348de4e717a1ef8432b332" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/panels/zipball/076f5367a3dfe5f6864d117f6826ca7821586931", - "reference": "076f5367a3dfe5f6864d117f6826ca7821586931", + "url": "https://api.github.com/repos/filamentphp/panels/zipball/5c4bf4225106e5d2a1348de4e717a1ef8432b332", + "reference": "5c4bf4225106e5d2a1348de4e717a1ef8432b332", "shasum": "" }, "require": { @@ -2706,20 +2602,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-10-31T13:38:14+00:00" + "time": "2024-11-06T08:50:55+00:00" }, { "name": "filament/forms", - "version": "v3.2.122", + "version": "v3.2.123", "source": { "type": "git", "url": "https://github.com/filamentphp/forms.git", - "reference": "c863b5765b871485a2c624c43a0eb6e957a04b54" + "reference": "f75386dc6f3c41fd3178265a71806f1ed4be0478" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/forms/zipball/c863b5765b871485a2c624c43a0eb6e957a04b54", - "reference": "c863b5765b871485a2c624c43a0eb6e957a04b54", + "url": "https://api.github.com/repos/filamentphp/forms/zipball/f75386dc6f3c41fd3178265a71806f1ed4be0478", + "reference": "f75386dc6f3c41fd3178265a71806f1ed4be0478", "shasum": "" }, "require": { @@ -2762,11 +2658,11 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-10-31T13:38:16+00:00" + "time": "2024-11-06T08:50:57+00:00" }, { "name": "filament/infolists", - "version": "v3.2.122", + "version": "v3.2.123", "source": { "type": "git", "url": "https://github.com/filamentphp/infolists.git", @@ -2817,7 +2713,7 @@ }, { "name": "filament/notifications", - "version": "v3.2.122", + "version": "v3.2.123", "source": { "type": "git", "url": "https://github.com/filamentphp/notifications.git", @@ -2869,7 +2765,7 @@ }, { "name": "filament/support", - "version": "v3.2.122", + "version": "v3.2.123", "source": { "type": "git", "url": "https://github.com/filamentphp/support.git", @@ -2928,16 +2824,16 @@ }, { "name": "filament/tables", - "version": "v3.2.122", + "version": "v3.2.123", "source": { "type": "git", "url": "https://github.com/filamentphp/tables.git", - "reference": "56a852f7992a01ad8d7b85034cdbb2ae8a21086a" + "reference": "d066eed11528f1928a76cbe9075c54ddfe26fd1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/tables/zipball/56a852f7992a01ad8d7b85034cdbb2ae8a21086a", - "reference": "56a852f7992a01ad8d7b85034cdbb2ae8a21086a", + "url": "https://api.github.com/repos/filamentphp/tables/zipball/d066eed11528f1928a76cbe9075c54ddfe26fd1e", + "reference": "d066eed11528f1928a76cbe9075c54ddfe26fd1e", "shasum": "" }, "require": { @@ -2976,11 +2872,11 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-10-31T13:38:27+00:00" + "time": "2024-11-06T08:51:06+00:00" }, { "name": "filament/widgets", - "version": "v3.2.122", + "version": "v3.2.123", "source": { "type": "git", "url": "https://github.com/filamentphp/widgets.git", @@ -4136,16 +4032,16 @@ }, { "name": "jaybizzle/crawler-detect", - "version": "v1.2.120", + "version": "v1.2.121", "source": { "type": "git", "url": "https://github.com/JayBizzle/Crawler-Detect.git", - "reference": "2b325bdce46bbb8a2e96dc740ad37c743c9d8617" + "reference": "40ecda6322d4163fe2c6e1dd47c574f580b8487f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/JayBizzle/Crawler-Detect/zipball/2b325bdce46bbb8a2e96dc740ad37c743c9d8617", - "reference": "2b325bdce46bbb8a2e96dc740ad37c743c9d8617", + "url": "https://api.github.com/repos/JayBizzle/Crawler-Detect/zipball/40ecda6322d4163fe2c6e1dd47c574f580b8487f", + "reference": "40ecda6322d4163fe2c6e1dd47c574f580b8487f", "shasum": "" }, "require": { @@ -4182,9 +4078,9 @@ ], "support": { "issues": "https://github.com/JayBizzle/Crawler-Detect/issues", - "source": "https://github.com/JayBizzle/Crawler-Detect/tree/v1.2.120" + "source": "https://github.com/JayBizzle/Crawler-Detect/tree/v1.2.121" }, - "time": "2024-09-15T14:31:21+00:00" + "time": "2024-10-20T21:42:39+00:00" }, { "name": "jean85/pretty-package-versions", @@ -4623,71 +4519,6 @@ }, "time": "2024-10-02T18:06:08+00:00" }, - { - "name": "laravel/fortify", - "version": "v1.24.4", - "source": { - "type": "git", - "url": "https://github.com/laravel/fortify.git", - "reference": "5bd3bdd535acf4054865c64eec6d8bb8c60cc127" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/fortify/zipball/5bd3bdd535acf4054865c64eec6d8bb8c60cc127", - "reference": "5bd3bdd535acf4054865c64eec6d8bb8c60cc127", - "shasum": "" - }, - "require": { - "bacon/bacon-qr-code": "^3.0", - "ext-json": "*", - "illuminate/support": "^10.0|^11.0", - "php": "^8.1", - "pragmarx/google2fa": "^8.0", - "symfony/console": "^6.0|^7.0" - }, - "require-dev": { - "mockery/mockery": "^1.0", - "orchestra/testbench": "^8.16|^9.0", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^10.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - }, - "laravel": { - "providers": [ - "Laravel\\Fortify\\FortifyServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Laravel\\Fortify\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "Backend controllers and scaffolding for Laravel authentication.", - "keywords": [ - "auth", - "laravel" - ], - "support": { - "issues": "https://github.com/laravel/fortify/issues", - "source": "https://github.com/laravel/fortify" - }, - "time": "2024-10-29T13:59:23+00:00" - }, { "name": "laravel/framework", "version": "v10.48.22", @@ -6278,6 +6109,78 @@ ], "time": "2024-10-15T19:35:06+00:00" }, + { + "name": "livewire/volt", + "version": "v1.6.5", + "source": { + "type": "git", + "url": "https://github.com/livewire/volt.git", + "reference": "d09fdb4cb52c6ce821d255683195bb6d446fe141" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/livewire/volt/zipball/d09fdb4cb52c6ce821d255683195bb6d446fe141", + "reference": "d09fdb4cb52c6ce821d255683195bb6d446fe141", + "shasum": "" + }, + "require": { + "laravel/framework": "^10.38.2|^11.0", + "livewire/livewire": "^3.0", + "php": "^8.1" + }, + "require-dev": { + "laravel/folio": "^1.1", + "mockery/mockery": "^1.6", + "orchestra/testbench": "^8.15.0|^9.0", + "pestphp/pest": "^2.9.5", + "phpstan/phpstan": "^1.10" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + }, + "laravel": { + "providers": [ + "Livewire\\Volt\\VoltServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "functions.php" + ], + "psr-4": { + "Livewire\\Volt\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "An elegantly crafted functional API for Laravel Livewire.", + "homepage": "https://github.com/livewire/volt", + "keywords": [ + "laravel", + "livewire", + "volt" + ], + "support": { + "issues": "https://github.com/livewire/volt/issues", + "source": "https://github.com/livewire/volt" + }, + "time": "2024-05-31T19:07:20+00:00" + }, { "name": "maennchen/zipstream-php", "version": "3.1.1", @@ -8076,16 +7979,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.4.1", + "version": "5.5.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c" + "reference": "0c70d2c566e899666f367ab7b80986beb3581e6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/0c70d2c566e899666f367ab7b80986beb3581e6f", + "reference": "0c70d2c566e899666f367ab7b80986beb3581e6f", "shasum": "" }, "require": { @@ -8098,13 +8001,13 @@ "webmozart/assert": "^1.9.1" }, "require-dev": { - "mockery/mockery": "~1.3.5", + "mockery/mockery": "~1.3.5 || ~1.6.0", "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.8", "phpstan/phpstan-mockery": "^1.1", "phpstan/phpstan-webmozart-assert": "^1.2", "phpunit/phpunit": "^9.5", - "vimeo/psalm": "^5.13" + "psalm/phar": "^5.26" }, "type": "library", "extra": { @@ -8134,9 +8037,9 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.5.1" }, - "time": "2024-05-21T05:55:05+00:00" + "time": "2024-11-06T11:58:54+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -8428,58 +8331,6 @@ }, "time": "2024-10-13T11:25:22+00:00" }, - { - "name": "pragmarx/google2fa", - "version": "v8.0.3", - "source": { - "type": "git", - "url": "https://github.com/antonioribeiro/google2fa.git", - "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", - "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", - "shasum": "" - }, - "require": { - "paragonie/constant_time_encoding": "^1.0|^2.0|^3.0", - "php": "^7.1|^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^7.5.15|^8.5|^9.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "PragmaRX\\Google2FA\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Antonio Carlos Ribeiro", - "email": "acr@antoniocarlosribeiro.com", - "role": "Creator & Designer" - } - ], - "description": "A One Time Password Authentication package, compatible with Google Authenticator.", - "keywords": [ - "2fa", - "Authentication", - "Two Factor Authentication", - "google2fa" - ], - "support": { - "issues": "https://github.com/antonioribeiro/google2fa/issues", - "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.3" - }, - "time": "2024-09-05T11:56:40+00:00" - }, { "name": "psr/cache", "version": "3.0.0", @@ -10174,16 +10025,16 @@ }, { "name": "spatie/image-optimizer", - "version": "1.7.5", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/spatie/image-optimizer.git", - "reference": "43aff6725cd87bb78ccd8532633cfa8bdc962505" + "reference": "4fd22035e81d98fffced65a8c20d9ec4daa9671c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/image-optimizer/zipball/43aff6725cd87bb78ccd8532633cfa8bdc962505", - "reference": "43aff6725cd87bb78ccd8532633cfa8bdc962505", + "url": "https://api.github.com/repos/spatie/image-optimizer/zipball/4fd22035e81d98fffced65a8c20d9ec4daa9671c", + "reference": "4fd22035e81d98fffced65a8c20d9ec4daa9671c", "shasum": "" }, "require": { @@ -10223,9 +10074,9 @@ ], "support": { "issues": "https://github.com/spatie/image-optimizer/issues", - "source": "https://github.com/spatie/image-optimizer/tree/1.7.5" + "source": "https://github.com/spatie/image-optimizer/tree/1.8.0" }, - "time": "2024-05-16T08:48:33+00:00" + "time": "2024-11-04T08:24:54+00:00" }, { "name": "spatie/invade", @@ -11280,16 +11131,16 @@ }, { "name": "symfony/console", - "version": "v6.4.13", + "version": "v6.4.14", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "f793dd5a7d9ae9923e35d0503d08ba734cec1d79" + "reference": "897c2441ed4eec8a8a2c37b943427d24dba3f26b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/f793dd5a7d9ae9923e35d0503d08ba734cec1d79", - "reference": "f793dd5a7d9ae9923e35d0503d08ba734cec1d79", + "url": "https://api.github.com/repos/symfony/console/zipball/897c2441ed4eec8a8a2c37b943427d24dba3f26b", + "reference": "897c2441ed4eec8a8a2c37b943427d24dba3f26b", "shasum": "" }, "require": { @@ -11354,7 +11205,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.13" + "source": "https://github.com/symfony/console/tree/v6.4.14" }, "funding": [ { @@ -11370,7 +11221,7 @@ "type": "tidelift" } ], - "time": "2024-10-09T08:40:40+00:00" + "time": "2024-11-05T15:34:40+00:00" }, { "name": "symfony/css-selector", @@ -11573,16 +11424,16 @@ }, { "name": "symfony/error-handler", - "version": "v6.4.13", + "version": "v6.4.14", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "e3c78742f86a5b65fe2ac4c4b6b776d92fd7ca0c" + "reference": "9e024324511eeb00983ee76b9aedc3e6ecd993d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/e3c78742f86a5b65fe2ac4c4b6b776d92fd7ca0c", - "reference": "e3c78742f86a5b65fe2ac4c4b6b776d92fd7ca0c", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/9e024324511eeb00983ee76b9aedc3e6ecd993d9", + "reference": "9e024324511eeb00983ee76b9aedc3e6ecd993d9", "shasum": "" }, "require": { @@ -11628,7 +11479,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v6.4.13" + "source": "https://github.com/symfony/error-handler/tree/v6.4.14" }, "funding": [ { @@ -11644,7 +11495,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:18:03+00:00" + "time": "2024-11-05T15:34:40+00:00" }, { "name": "symfony/event-dispatcher", @@ -11937,16 +11788,16 @@ }, { "name": "symfony/http-client", - "version": "v6.4.13", + "version": "v6.4.14", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "509d0e8a798bf5e41e0b6317e9bce1140af47376" + "reference": "05d88cbd816ad6e0202edd9a9963cb9d615b8826" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/509d0e8a798bf5e41e0b6317e9bce1140af47376", - "reference": "509d0e8a798bf5e41e0b6317e9bce1140af47376", + "url": "https://api.github.com/repos/symfony/http-client/zipball/05d88cbd816ad6e0202edd9a9963cb9d615b8826", + "reference": "05d88cbd816ad6e0202edd9a9963cb9d615b8826", "shasum": "" }, "require": { @@ -12010,7 +11861,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v6.4.13" + "source": "https://github.com/symfony/http-client/tree/v6.4.14" }, "funding": [ { @@ -12026,7 +11877,7 @@ "type": "tidelift" } ], - "time": "2024-10-14T18:15:01+00:00" + "time": "2024-11-05T16:39:55+00:00" }, { "name": "symfony/http-client-contracts", @@ -12108,16 +11959,16 @@ }, { "name": "symfony/http-foundation", - "version": "v6.4.13", + "version": "v6.4.14", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "4c0341b3e0a7291e752c69d2a1ed9a84b68d604c" + "reference": "ba020a321a95519303a3f09ec2824d34d601c388" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/4c0341b3e0a7291e752c69d2a1ed9a84b68d604c", - "reference": "4c0341b3e0a7291e752c69d2a1ed9a84b68d604c", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/ba020a321a95519303a3f09ec2824d34d601c388", + "reference": "ba020a321a95519303a3f09ec2824d34d601c388", "shasum": "" }, "require": { @@ -12165,7 +12016,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.4.13" + "source": "https://github.com/symfony/http-foundation/tree/v6.4.14" }, "funding": [ { @@ -12181,20 +12032,20 @@ "type": "tidelift" } ], - "time": "2024-10-11T19:20:58+00:00" + "time": "2024-11-05T16:39:55+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.4.13", + "version": "v6.4.14", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "4474015c363ec0cd3bf47d55657e68630dbae66e" + "reference": "8278a947d0369754a47b758a9e17b72cab970951" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/4474015c363ec0cd3bf47d55657e68630dbae66e", - "reference": "4474015c363ec0cd3bf47d55657e68630dbae66e", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/8278a947d0369754a47b758a9e17b72cab970951", + "reference": "8278a947d0369754a47b758a9e17b72cab970951", "shasum": "" }, "require": { @@ -12279,7 +12130,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.4.13" + "source": "https://github.com/symfony/http-kernel/tree/v6.4.14" }, "funding": [ { @@ -12295,7 +12146,7 @@ "type": "tidelift" } ], - "time": "2024-10-27T13:00:29+00:00" + "time": "2024-11-06T09:45:21+00:00" }, { "name": "symfony/mailer", @@ -13236,16 +13087,16 @@ }, { "name": "symfony/process", - "version": "v6.4.13", + "version": "v6.4.14", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "1f9f59b46880201629df3bd950fc5ae8c55b960f" + "reference": "25214adbb0996d18112548de20c281be9f27279f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/1f9f59b46880201629df3bd950fc5ae8c55b960f", - "reference": "1f9f59b46880201629df3bd950fc5ae8c55b960f", + "url": "https://api.github.com/repos/symfony/process/zipball/25214adbb0996d18112548de20c281be9f27279f", + "reference": "25214adbb0996d18112548de20c281be9f27279f", "shasum": "" }, "require": { @@ -13277,7 +13128,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.4.13" + "source": "https://github.com/symfony/process/tree/v6.4.14" }, "funding": [ { @@ -13293,7 +13144,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:18:03+00:00" + "time": "2024-11-06T09:25:01+00:00" }, { "name": "symfony/psr-http-message-bridge", @@ -13886,16 +13737,16 @@ }, { "name": "symfony/var-dumper", - "version": "v6.4.13", + "version": "v6.4.14", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "2acb151474ed8cb43624e3f41a0bf7c4c8ce4f41" + "reference": "93c09246038178717a9c14b809ea8151ffcf7091" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2acb151474ed8cb43624e3f41a0bf7c4c8ce4f41", - "reference": "2acb151474ed8cb43624e3f41a0bf7c4c8ce4f41", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/93c09246038178717a9c14b809ea8151ffcf7091", + "reference": "93c09246038178717a9c14b809ea8151ffcf7091", "shasum": "" }, "require": { @@ -13951,7 +13802,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.4.13" + "source": "https://github.com/symfony/var-dumper/tree/v6.4.14" }, "funding": [ { @@ -13967,7 +13818,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:18:03+00:00" + "time": "2024-11-05T15:34:40+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -14993,6 +14844,68 @@ ], "time": "2024-10-19T23:04:40+00:00" }, + { + "name": "laravel/breeze", + "version": "v1.29.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/breeze.git", + "reference": "22c53b84b7fff91b01a318d71a10dfc251e92849" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/breeze/zipball/22c53b84b7fff91b01a318d71a10dfc251e92849", + "reference": "22c53b84b7fff91b01a318d71a10dfc251e92849", + "shasum": "" + }, + "require": { + "illuminate/console": "^10.17", + "illuminate/filesystem": "^10.17", + "illuminate/support": "^10.17", + "illuminate/validation": "^10.17", + "php": "^8.1.0" + }, + "require-dev": { + "orchestra/testbench": "^8.0", + "phpstan/phpstan": "^1.10" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Breeze\\BreezeServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Breeze\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Minimal Laravel authentication scaffolding with Blade and Tailwind.", + "keywords": [ + "auth", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/breeze/issues", + "source": "https://github.com/laravel/breeze" + }, + "time": "2024-03-04T14:35:21+00:00" + }, { "name": "laravel/pint", "version": "v1.18.1", @@ -15958,16 +15871,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.7", + "version": "1.12.8", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0" + "reference": "f6a60a4d66142b8156c9da923f1972657bc4748c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", - "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f6a60a4d66142b8156c9da923f1972657bc4748c", + "reference": "f6a60a4d66142b8156c9da923f1972657bc4748c", "shasum": "" }, "require": { @@ -16012,7 +15925,7 @@ "type": "github" } ], - "time": "2024-10-18T11:12:07+00:00" + "time": "2024-11-06T19:06:49+00:00" }, { "name": "phpunit/php-code-coverage", diff --git a/config/app.php b/config/app.php index af1a0c9e..3945225d 100644 --- a/config/app.php +++ b/config/app.php @@ -167,7 +167,6 @@ /* * Package Service Providers... */ - App\Providers\FortifyServiceProvider::class, App\Providers\Filament\AdminPanelProvider::class, LaravelFeature\Provider\FeatureServiceProvider::class, diff --git a/config/fortify.php b/config/fortify.php deleted file mode 100644 index 866d2aaa..00000000 --- a/config/fortify.php +++ /dev/null @@ -1,147 +0,0 @@ - 'web', - - /* - |-------------------------------------------------------------------------- - | Fortify Password Broker - |-------------------------------------------------------------------------- - | - | Here you may specify which password broker Fortify can use when a user - | is resetting their password. This configured value should match one - | of your password brokers setup in your "auth" configuration file. - | - */ - - 'passwords' => 'users', - - /* - |-------------------------------------------------------------------------- - | Username / Email - |-------------------------------------------------------------------------- - | - | This value defines which model attribute should be considered as your - | application's "username" field. Typically, this might be the email - | address of the users but you are free to change this value here. - | - | Out of the box, Fortify expects forgot password and reset password - | requests to have a field named 'email'. If the application uses - | another name for the field you may define it below as needed. - | - */ - - 'username' => 'email', - - 'email' => 'email', - - /* - |-------------------------------------------------------------------------- - | Home Path - |-------------------------------------------------------------------------- - | - | Here you may configure the path where users will get redirected during - | authentication or password reset when the operations are successful - | and the user is authenticated. You are free to change this value. - | - */ - - 'home' => RouteServiceProvider::HOME, - - /* - |-------------------------------------------------------------------------- - | Fortify Routes Prefix / Subdomain - |-------------------------------------------------------------------------- - | - | Here you may specify which prefix Fortify will assign to all the routes - | that it registers with the application. If necessary, you may change - | subdomain under which all of the Fortify routes will be available. - | - */ - - 'prefix' => '', - - 'domain' => null, - - /* - |-------------------------------------------------------------------------- - | Fortify Routes Middleware - |-------------------------------------------------------------------------- - | - | Here you may specify which middleware Fortify will assign to the routes - | that it registers with the application. If necessary, you may change - | these middleware but typically this provided default is preferred. - | - */ - - 'middleware' => ['web'], - - /* - |-------------------------------------------------------------------------- - | Rate Limiting - |-------------------------------------------------------------------------- - | - | By default, Fortify will throttle logins to five requests per minute for - | every email and IP address combination. However, if you would like to - | specify a custom rate limiter to call then you may specify it here. - | - */ - - 'limiters' => [ - 'login' => 'login', - 'two-factor' => 'two-factor', - ], - - /* - |-------------------------------------------------------------------------- - | Register View Routes - |-------------------------------------------------------------------------- - | - | Here you may specify if the routes returning views should be disabled as - | you may not need them when building your own application. This may be - | especially true if you're writing a custom single-page application. - | - */ - - 'views' => true, - - /* - |-------------------------------------------------------------------------- - | Features - |-------------------------------------------------------------------------- - | - | Some of the Fortify features are optional. You may disable the features - | by removing them from this array. You're free to only remove some of - | these features or you can even remove all of these if you need to. - | - */ - - 'features' => [ - Features::registration(), - Features::resetPasswords(), - Features::emailVerification(), - Features::updateProfileInformation(), - Features::updatePasswords(), - Features::twoFactorAuthentication([ - 'confirmPassword' => true, - ]), - ], - -]; diff --git a/resources/views/auth/forgot-password.blade.php b/resources/views/livewire/pages/auth/forgot-password.blade.php similarity index 100% rename from resources/views/auth/forgot-password.blade.php rename to resources/views/livewire/pages/auth/forgot-password.blade.php diff --git a/resources/views/auth/login.blade.php b/resources/views/livewire/pages/auth/login.blade.php similarity index 52% rename from resources/views/auth/login.blade.php rename to resources/views/livewire/pages/auth/login.blade.php index 5f3d7079..414f95e8 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/livewire/pages/auth/login.blade.php @@ -1,4 +1,80 @@ - +validate(); + + $this->form->authenticate(); + + Session::regenerate(); + + $this->redirectIntended(default: route('dashboard', absolute: false), navigate: true); + } +}; ?> + +
+ + + +
+ +
+ + + +
+ + +
+ + + + + +
+ + +
+ +
+ +
+ @if (Route::has('password.request')) + + {{ __('Forgot your password?') }} + + @endif + + + {{ __('Log in') }} + +
+
+
+ + + +{{--
@@ -65,4 +141,8 @@ class="font-medium text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hov - + --}} + + + + diff --git a/resources/views/auth/register.blade.php b/resources/views/livewire/pages/auth/register.blade.php similarity index 100% rename from resources/views/auth/register.blade.php rename to resources/views/livewire/pages/auth/register.blade.php diff --git a/resources/views/auth/reset-password.blade.php b/resources/views/livewire/pages/auth/reset-password.blade.php similarity index 100% rename from resources/views/auth/reset-password.blade.php rename to resources/views/livewire/pages/auth/reset-password.blade.php diff --git a/resources/views/auth/verify-email.blade.php b/resources/views/livewire/pages/auth/verify-email.blade.php similarity index 100% rename from resources/views/auth/verify-email.blade.php rename to resources/views/livewire/pages/auth/verify-email.blade.php diff --git a/routes/auth.php b/routes/auth.php new file mode 100644 index 00000000..131252e7 --- /dev/null +++ b/routes/auth.php @@ -0,0 +1,31 @@ +group(function () { + Volt::route('register', 'pages.auth.register') + ->name('register'); + + Volt::route('login', 'pages.auth.login') + ->name('login'); + + Volt::route('forgot-password', 'pages.auth.forgot-password') + ->name('password.request'); + + Volt::route('reset-password/{token}', 'pages.auth.reset-password') + ->name('password.reset'); +}); + +Route::middleware('auth')->group(function () { + Volt::route('verify-email', 'pages.auth.verify-email') + ->name('verification.notice'); + + Route::get('verify-email/{id}/{hash}', VerifyEmailController::class) + ->middleware(['signed', 'throttle:6,1']) + ->name('verification.verify'); + + Volt::route('confirm-password', 'pages.auth.confirm-password') + ->name('password.confirm'); +}); diff --git a/routes/web.php b/routes/web.php index d4a537cb..1a7edfcc 100644 --- a/routes/web.php +++ b/routes/web.php @@ -62,3 +62,5 @@ Route::get('callback-payment', NotchPayCallBackController::class)->name('notchpay-callback'); require __DIR__.'/features/account.php'; + +require __DIR__.'/auth.php'; diff --git a/tests/Feature/Auth/AuthenticationTest.php b/tests/Feature/Auth/AuthenticationTest.php new file mode 100644 index 00000000..ec9910fe --- /dev/null +++ b/tests/Feature/Auth/AuthenticationTest.php @@ -0,0 +1,72 @@ +get('/login'); + + $response + ->assertOk() + ->assertSeeVolt('pages.auth.login'); +}); + +test('users can authenticate using the login screen', function () { + $user = User::factory()->create(); + + $component = Volt::test('pages.auth.login') + ->set('form.email', $user->email) + ->set('form.password', 'password'); + + $component->call('login'); + + $component + ->assertHasNoErrors() + ->assertRedirect(route('dashboard', absolute: false)); + + $this->assertAuthenticated(); +}); + +test('users can not authenticate with invalid password', function () { + $user = User::factory()->create(); + + $component = Volt::test('pages.auth.login') + ->set('form.email', $user->email) + ->set('form.password', 'wrong-password'); + + $component->call('login'); + + $component + ->assertHasErrors() + ->assertNoRedirect(); + + $this->assertGuest(); +}); + +test('navigation menu can be rendered', function () { + $user = User::factory()->create(); + + $this->actingAs($user); + + $response = $this->get('/dashboard'); + + $response + ->assertOk() + ->assertSeeVolt('layout.navigation'); +}); + +test('users can logout', function () { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('layout.navigation'); + + $component->call('logout'); + + $component + ->assertHasNoErrors() + ->assertRedirect('/'); + + $this->assertGuest(); +}); diff --git a/tests/Feature/Auth/EmailVerificationTest.php b/tests/Feature/Auth/EmailVerificationTest.php new file mode 100644 index 00000000..f282dff0 --- /dev/null +++ b/tests/Feature/Auth/EmailVerificationTest.php @@ -0,0 +1,46 @@ +unverified()->create(); + + $response = $this->actingAs($user)->get('/verify-email'); + + $response->assertStatus(200); +}); + +test('email can be verified', function () { + $user = User::factory()->unverified()->create(); + + Event::fake(); + + $verificationUrl = URL::temporarySignedRoute( + 'verification.verify', + now()->addMinutes(60), + ['id' => $user->id, 'hash' => sha1($user->email)] + ); + + $response = $this->actingAs($user)->get($verificationUrl); + + Event::assertDispatched(Verified::class); + expect($user->fresh()->hasVerifiedEmail())->toBeTrue(); + $response->assertRedirect(route('dashboard', absolute: false).'?verified=1'); +}); + +test('email is not verified with invalid hash', function () { + $user = User::factory()->unverified()->create(); + + $verificationUrl = URL::temporarySignedRoute( + 'verification.verify', + now()->addMinutes(60), + ['id' => $user->id, 'hash' => sha1('wrong-email')] + ); + + $this->actingAs($user)->get($verificationUrl); + + expect($user->fresh()->hasVerifiedEmail())->toBeFalse(); +}); diff --git a/tests/Feature/Auth/PasswordConfirmationTest.php b/tests/Feature/Auth/PasswordConfirmationTest.php new file mode 100644 index 00000000..21c28c39 --- /dev/null +++ b/tests/Feature/Auth/PasswordConfirmationTest.php @@ -0,0 +1,46 @@ +create(); + + $response = $this->actingAs($user)->get('/confirm-password'); + + $response + ->assertSeeVolt('pages.auth.confirm-password') + ->assertStatus(200); +}); + +test('password can be confirmed', function () { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('pages.auth.confirm-password') + ->set('password', 'password'); + + $component->call('confirmPassword'); + + $component + ->assertRedirect('/dashboard') + ->assertHasNoErrors(); +}); + +test('password is not confirmed with invalid password', function () { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('pages.auth.confirm-password') + ->set('password', 'wrong-password'); + + $component->call('confirmPassword'); + + $component + ->assertNoRedirect() + ->assertHasErrors('password'); +}); diff --git a/tests/Feature/Auth/PasswordResetTest.php b/tests/Feature/Auth/PasswordResetTest.php new file mode 100644 index 00000000..c478bce9 --- /dev/null +++ b/tests/Feature/Auth/PasswordResetTest.php @@ -0,0 +1,73 @@ +get('/forgot-password'); + + $response + ->assertSeeVolt('pages.auth.forgot-password') + ->assertStatus(200); +}); + +test('reset password link can be requested', function () { + Notification::fake(); + + $user = User::factory()->create(); + + Volt::test('pages.auth.forgot-password') + ->set('email', $user->email) + ->call('sendPasswordResetLink'); + + Notification::assertSentTo($user, ResetPassword::class); +}); + +test('reset password screen can be rendered', function () { + Notification::fake(); + + $user = User::factory()->create(); + + Volt::test('pages.auth.forgot-password') + ->set('email', $user->email) + ->call('sendPasswordResetLink'); + + Notification::assertSentTo($user, ResetPassword::class, function ($notification) { + $response = $this->get('/reset-password/'.$notification->token); + + $response + ->assertSeeVolt('pages.auth.reset-password') + ->assertStatus(200); + + return true; + }); +}); + +test('password can be reset with valid token', function () { + Notification::fake(); + + $user = User::factory()->create(); + + Volt::test('pages.auth.forgot-password') + ->set('email', $user->email) + ->call('sendPasswordResetLink'); + + Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) { + $component = Volt::test('pages.auth.reset-password', ['token' => $notification->token]) + ->set('email', $user->email) + ->set('password', 'password') + ->set('password_confirmation', 'password'); + + $component->call('resetPassword'); + + $component + ->assertRedirect('/login') + ->assertHasNoErrors(); + + return true; + }); +}); diff --git a/tests/Feature/Auth/PasswordUpdateTest.php b/tests/Feature/Auth/PasswordUpdateTest.php new file mode 100644 index 00000000..33b1d4b5 --- /dev/null +++ b/tests/Feature/Auth/PasswordUpdateTest.php @@ -0,0 +1,41 @@ +create(); + + $this->actingAs($user); + + $component = Volt::test('profile.update-password-form') + ->set('current_password', 'password') + ->set('password', 'new-password') + ->set('password_confirmation', 'new-password') + ->call('updatePassword'); + + $component + ->assertHasNoErrors() + ->assertNoRedirect(); + + $this->assertTrue(Hash::check('new-password', $user->refresh()->password)); +}); + +test('correct password must be provided to update password', function () { + $user = User::factory()->create(); + + $this->actingAs($user); + + $component = Volt::test('profile.update-password-form') + ->set('current_password', 'wrong-password') + ->set('password', 'new-password') + ->set('password_confirmation', 'new-password') + ->call('updatePassword'); + + $component + ->assertHasErrors(['current_password']) + ->assertNoRedirect(); +}); diff --git a/tests/Feature/Auth/RegistrationTest.php b/tests/Feature/Auth/RegistrationTest.php new file mode 100644 index 00000000..5d500390 --- /dev/null +++ b/tests/Feature/Auth/RegistrationTest.php @@ -0,0 +1,27 @@ +get('/register'); + + $response + ->assertOk() + ->assertSeeVolt('pages.auth.register'); +}); + +test('new users can register', function () { + $component = Volt::test('pages.auth.register') + ->set('name', 'Test User') + ->set('email', 'test@example.com') + ->set('password', 'password') + ->set('password_confirmation', 'password'); + + $component->call('register'); + + $component->assertRedirect(route('dashboard', absolute: false)); + + $this->assertAuthenticated(); +}); From 1810361d0453f69e543041033d32d514ac735ec7 Mon Sep 17 00:00:00 2001 From: Stephen Jacques Date: Thu, 7 Nov 2024 12:24:43 +0100 Subject: [PATCH 2/6] chore: Apply rebase develop --- app/Livewire/Forms/LoginForm.php | 72 +++++++ app/Livewire/Pages/Auth/Login.php | 39 ++++ .../views/components/input-error.blade.php | 9 + .../views/components/input-label.blade.php | 5 + .../views/components/primary-button.blade.php | 3 + .../views/components/text-input.blade.php | 3 + .../views/livewire/pages/auth/login.blade.php | 137 ++++++++----- .../livewire/pages/auth/register.blade.php | 189 ++++++++++++++++++ 8 files changed, 410 insertions(+), 47 deletions(-) create mode 100644 app/Livewire/Forms/LoginForm.php create mode 100644 app/Livewire/Pages/Auth/Login.php create mode 100644 resources/views/components/input-error.blade.php create mode 100644 resources/views/components/input-label.blade.php create mode 100644 resources/views/components/primary-button.blade.php create mode 100644 resources/views/components/text-input.blade.php diff --git a/app/Livewire/Forms/LoginForm.php b/app/Livewire/Forms/LoginForm.php new file mode 100644 index 00000000..371e8f16 --- /dev/null +++ b/app/Livewire/Forms/LoginForm.php @@ -0,0 +1,72 @@ +ensureIsNotRateLimited(); + + if (! Auth::attempt(['email' => $this->email, 'password' => $this->password], $this->remember)) { + RateLimiter::hit($this->throttleKey()); + + throw ValidationException::withMessages([ + 'form.email' => trans('auth.failed'), + ]); + } + + RateLimiter::clear($this->throttleKey()); + } + + /** + * Ensure the authentication request is not rate limited. + */ + protected function ensureIsNotRateLimited(): void + { + if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { + return; + } + + event(new Lockout(request())); + + $seconds = RateLimiter::availableIn($this->throttleKey()); + + throw ValidationException::withMessages([ + 'form.email' => trans('auth.throttle', [ + 'seconds' => $seconds, + 'minutes' => ceil($seconds / 60), + ]), + ]); + } + + /** + * Get the authentication rate limiting throttle key. + */ + protected function throttleKey(): string + { + return Str::transliterate(Str::lower($this->email).'|'.request()->ip()); + } +} \ No newline at end of file diff --git a/app/Livewire/Pages/Auth/Login.php b/app/Livewire/Pages/Auth/Login.php new file mode 100644 index 00000000..4d1c50d7 --- /dev/null +++ b/app/Livewire/Pages/Auth/Login.php @@ -0,0 +1,39 @@ +form = new LoginForm($this); + } + + public function login(): void + { + $this->validate([ + 'form.email' => ['required', 'email'], + 'form.password' => ['required'], + 'form.remember' => ['boolean'], + ]); + + $this->form->authenticate(); + + Session::regenerate(); + + $this->redirectIntended(route('dashboard')); + } + + public function render() + { + return view('livewire.pages.auth.login'); + } +} \ No newline at end of file diff --git a/resources/views/components/input-error.blade.php b/resources/views/components/input-error.blade.php new file mode 100644 index 00000000..ad95f6b5 --- /dev/null +++ b/resources/views/components/input-error.blade.php @@ -0,0 +1,9 @@ +@props(['messages']) + +@if ($messages) +
    merge(['class' => 'text-sm text-red-600 dark:text-red-400 space-y-1']) }}> + @foreach ((array) $messages as $message) +
  • {{ $message }}
  • + @endforeach +
+@endif diff --git a/resources/views/components/input-label.blade.php b/resources/views/components/input-label.blade.php new file mode 100644 index 00000000..0419a49e --- /dev/null +++ b/resources/views/components/input-label.blade.php @@ -0,0 +1,5 @@ +@props(['value']) + +merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-gray-800 dark:bg-gray-200 border border-transparent rounded-md font-semibold text-xs text-white dark:text-gray-800 uppercase tracking-widest hover:bg-gray-700 dark:hover:bg-white focus:bg-gray-700 dark:focus:bg-white active:bg-gray-900 dark:active:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150']) }}> + {{ $slot }} + diff --git a/resources/views/components/text-input.blade.php b/resources/views/components/text-input.blade.php new file mode 100644 index 00000000..3f44b2f7 --- /dev/null +++ b/resources/views/components/text-input.blade.php @@ -0,0 +1,3 @@ +@props(['disabled' => false]) + +merge(['class' => 'border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm']) }}> diff --git a/resources/views/livewire/pages/auth/login.blade.php b/resources/views/livewire/pages/auth/login.blade.php index 414f95e8..f1eb4b48 100644 --- a/resources/views/livewire/pages/auth/login.blade.php +++ b/resources/views/livewire/pages/auth/login.blade.php @@ -25,53 +25,96 @@ public function login(): void $this->redirectIntended(default: route('dashboard', absolute: false), navigate: true); } }; ?> - -
- - - -
- -
- - - -
- - -
- - - - - -
- - -
- -
- -
- @if (Route::has('password.request')) - - {{ __('Forgot your password?') }} - - @endif - - - {{ __('Log in') }} - -
-
-
- +{{-- --}} +
+ +
+
+

+ {{ __('pages/auth.login.title') }} +

+
+
+ @csrf +
+ +
+ + + @error('form.email') + {{ $message }} + @enderror +
+ + +
+ + + @error('form.password') + {{ $message }} + @enderror +
+
+ +
+ +
+ + +
+ + + +
+ +
+ + + + {{ __('pages/auth.login.submit') }} + +
+
+ @include('partials._socials-link') +
+
+ + +
+ +{{--
--}} {{-- diff --git a/resources/views/livewire/pages/auth/register.blade.php b/resources/views/livewire/pages/auth/register.blade.php index 37412f20..b524c927 100644 --- a/resources/views/livewire/pages/auth/register.blade.php +++ b/resources/views/livewire/pages/auth/register.blade.php @@ -1,3 +1,4 @@ +<<<<<<< HEAD
@@ -136,3 +137,191 @@ +======= +validate([ + 'name' => ['required', 'string', 'max:255'], + 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class], + 'username' => ['required', 'string', 'max:255', 'unique:'.User::class], + 'password' => ['required', 'string', 'confirmed', Rules\Password::defaults()], + ]); + + $validated['password'] = Hash::make($validated['password']); + + event(new Registered($user = User::create($validated))); + + Auth::login($user); + + $this->redirect(route('dashboard'), navigate: true); + } +}; +?> + +{{-- --}} +
+ +
+ + +
+
+

+ {{ __('pages/auth.register.join_us') }} +

+ +

+ {{ __('pages/auth.register.joins_description') }} +

+
+ +
+ + +
+ @csrf +
+ +
+ + + @error('name') {{ $message }} @enderror +
+ + +
+ + + @error('email') {{ $message }} @enderror +
+ + +
+ + + @error('username') {{ $message }} @enderror +
+ + +
+ + + @error('password') {{ $message }} @enderror +
+ + +
+ + + @error('password_confirmation') {{ $message }} @enderror +
+
+ +
+ + + + {{ __('pages/auth.register.submit') }} + +
+
+
+ + @include('partials._socials-link') +
+
+
+ + + +
+{{--
--}} +>>>>>>> c512f79 (feat: (LAR-77) Mise en place de la class Login, et correction de la connexion) From f36996fb50b47ff7b783f34608efdc883df54f33 Mon Sep 17 00:00:00 2001 From: Stephen Jacques Date: Thu, 7 Nov 2024 12:25:18 +0100 Subject: [PATCH 3/6] chore: Apply rebase develop --- .../Controllers/Auth/VerifyEmailController.php | 16 ++++++++++++---- app/Livewire/Forms/LoginForm.php | 9 +++++---- app/Livewire/Pages/Auth/Login.php | 13 ++++++++----- app/Providers/VoltServiceProvider.php | 4 +++- routes/auth.php | 6 ++++-- tests/Feature/Auth/AuthenticationTest.php | 12 +++++++----- tests/Feature/Auth/EmailVerificationTest.php | 8 +++++--- tests/Feature/Auth/PasswordConfirmationTest.php | 8 +++++--- tests/Feature/Auth/PasswordResetTest.php | 10 ++++++---- tests/Feature/Auth/PasswordUpdateTest.php | 6 ++++-- tests/Feature/Auth/RegistrationTest.php | 6 ++++-- 11 files changed, 63 insertions(+), 35 deletions(-) diff --git a/app/Http/Controllers/Auth/VerifyEmailController.php b/app/Http/Controllers/Auth/VerifyEmailController.php index 784765e3..b273f1c9 100644 --- a/app/Http/Controllers/Auth/VerifyEmailController.php +++ b/app/Http/Controllers/Auth/VerifyEmailController.php @@ -1,5 +1,7 @@ user()->hasVerifiedEmail()) { + $user = $request->user(); + + if ($user === null) { + return redirect()->route('login'); + } + + if ($user->hasVerifiedEmail()) { return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); } - if ($request->user()->markEmailAsVerified()) { - event(new Verified($request->user())); + if ($user->markEmailAsVerified()) { + event(new Verified($user)); } return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); diff --git a/app/Livewire/Forms/LoginForm.php b/app/Livewire/Forms/LoginForm.php index 371e8f16..4d62b53a 100644 --- a/app/Livewire/Forms/LoginForm.php +++ b/app/Livewire/Forms/LoginForm.php @@ -16,8 +16,9 @@ class LoginForm extends Form public string $password = ''; public bool $remember = false; - public function __construct(Component $component = null, $name = 'form') + public function __construct(Component $component = null, string $name = 'form') { + $component = $component ?? app(Component::class); parent::__construct($component, $name); } @@ -30,7 +31,7 @@ public function authenticate(): void { $this->ensureIsNotRateLimited(); - if (! Auth::attempt(['email' => $this->email, 'password' => $this->password], $this->remember)) { + if (!Auth::attempt(['email' => $this->email, 'password' => $this->password], $this->remember)) { RateLimiter::hit($this->throttleKey()); throw ValidationException::withMessages([ @@ -46,7 +47,7 @@ public function authenticate(): void */ protected function ensureIsNotRateLimited(): void { - if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { + if (!RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { return; } @@ -67,6 +68,6 @@ protected function ensureIsNotRateLimited(): void */ protected function throttleKey(): string { - return Str::transliterate(Str::lower($this->email).'|'.request()->ip()); + return Str::transliterate(Str::lower($this->email) . '|' . request()->ip()); } } \ No newline at end of file diff --git a/app/Livewire/Pages/Auth/Login.php b/app/Livewire/Pages/Auth/Login.php index 4d1c50d7..1fda1c40 100644 --- a/app/Livewire/Pages/Auth/Login.php +++ b/app/Livewire/Pages/Auth/Login.php @@ -1,18 +1,21 @@ form = new LoginForm($this); } @@ -32,8 +35,8 @@ public function login(): void $this->redirectIntended(route('dashboard')); } - public function render() + public function render(): View { return view('livewire.pages.auth.login'); } -} \ No newline at end of file +} diff --git a/app/Providers/VoltServiceProvider.php b/app/Providers/VoltServiceProvider.php index e61d9845..44f21b71 100644 --- a/app/Providers/VoltServiceProvider.php +++ b/app/Providers/VoltServiceProvider.php @@ -1,11 +1,13 @@ group(function () { +Route::middleware('guest')->group(function (): void { Volt::route('register', 'pages.auth.register') ->name('register'); @@ -18,7 +20,7 @@ ->name('password.reset'); }); -Route::middleware('auth')->group(function () { +Route::middleware('auth')->group(function (): void { Volt::route('verify-email', 'pages.auth.verify-email') ->name('verification.notice'); diff --git a/tests/Feature/Auth/AuthenticationTest.php b/tests/Feature/Auth/AuthenticationTest.php index ec9910fe..5f089bbb 100644 --- a/tests/Feature/Auth/AuthenticationTest.php +++ b/tests/Feature/Auth/AuthenticationTest.php @@ -1,9 +1,11 @@ get('/login'); $response @@ -11,7 +13,7 @@ ->assertSeeVolt('pages.auth.login'); }); -test('users can authenticate using the login screen', function () { +test('users can authenticate using the login screen', function (): void { $user = User::factory()->create(); $component = Volt::test('pages.auth.login') @@ -27,7 +29,7 @@ $this->assertAuthenticated(); }); -test('users can not authenticate with invalid password', function () { +test('users can not authenticate with invalid password', function (): void { $user = User::factory()->create(); $component = Volt::test('pages.auth.login') @@ -43,7 +45,7 @@ $this->assertGuest(); }); -test('navigation menu can be rendered', function () { +test('navigation menu can be rendered', function (): void { $user = User::factory()->create(); $this->actingAs($user); @@ -55,7 +57,7 @@ ->assertSeeVolt('layout.navigation'); }); -test('users can logout', function () { +test('users can logout', function (): void { $user = User::factory()->create(); $this->actingAs($user); diff --git a/tests/Feature/Auth/EmailVerificationTest.php b/tests/Feature/Auth/EmailVerificationTest.php index f282dff0..ededd4dd 100644 --- a/tests/Feature/Auth/EmailVerificationTest.php +++ b/tests/Feature/Auth/EmailVerificationTest.php @@ -1,11 +1,13 @@ unverified()->create(); $response = $this->actingAs($user)->get('/verify-email'); @@ -13,7 +15,7 @@ $response->assertStatus(200); }); -test('email can be verified', function () { +test('email can be verified', function (): void { $user = User::factory()->unverified()->create(); Event::fake(); @@ -31,7 +33,7 @@ $response->assertRedirect(route('dashboard', absolute: false).'?verified=1'); }); -test('email is not verified with invalid hash', function () { +test('email is not verified with invalid hash', function (): void { $user = User::factory()->unverified()->create(); $verificationUrl = URL::temporarySignedRoute( diff --git a/tests/Feature/Auth/PasswordConfirmationTest.php b/tests/Feature/Auth/PasswordConfirmationTest.php index 21c28c39..2ca9ce48 100644 --- a/tests/Feature/Auth/PasswordConfirmationTest.php +++ b/tests/Feature/Auth/PasswordConfirmationTest.php @@ -1,11 +1,13 @@ create(); $response = $this->actingAs($user)->get('/confirm-password'); @@ -15,7 +17,7 @@ ->assertStatus(200); }); -test('password can be confirmed', function () { +test('password can be confirmed', function (): void { $user = User::factory()->create(); $this->actingAs($user); @@ -30,7 +32,7 @@ ->assertHasNoErrors(); }); -test('password is not confirmed with invalid password', function () { +test('password is not confirmed with invalid password', function (): void { $user = User::factory()->create(); $this->actingAs($user); diff --git a/tests/Feature/Auth/PasswordResetTest.php b/tests/Feature/Auth/PasswordResetTest.php index c478bce9..ae18ea13 100644 --- a/tests/Feature/Auth/PasswordResetTest.php +++ b/tests/Feature/Auth/PasswordResetTest.php @@ -1,5 +1,7 @@ get('/forgot-password'); $response @@ -15,7 +17,7 @@ ->assertStatus(200); }); -test('reset password link can be requested', function () { +test('reset password link can be requested', function (): void { Notification::fake(); $user = User::factory()->create(); @@ -27,7 +29,7 @@ Notification::assertSentTo($user, ResetPassword::class); }); -test('reset password screen can be rendered', function () { +test('reset password screen can be rendered', function (): void { Notification::fake(); $user = User::factory()->create(); @@ -47,7 +49,7 @@ }); }); -test('password can be reset with valid token', function () { +test('password can be reset with valid token', function (): void { Notification::fake(); $user = User::factory()->create(); diff --git a/tests/Feature/Auth/PasswordUpdateTest.php b/tests/Feature/Auth/PasswordUpdateTest.php index 33b1d4b5..3a0ff291 100644 --- a/tests/Feature/Auth/PasswordUpdateTest.php +++ b/tests/Feature/Auth/PasswordUpdateTest.php @@ -1,12 +1,14 @@ create(); $this->actingAs($user); @@ -24,7 +26,7 @@ $this->assertTrue(Hash::check('new-password', $user->refresh()->password)); }); -test('correct password must be provided to update password', function () { +test('correct password must be provided to update password', function (): void { $user = User::factory()->create(); $this->actingAs($user); diff --git a/tests/Feature/Auth/RegistrationTest.php b/tests/Feature/Auth/RegistrationTest.php index 5d500390..19a1f2d0 100644 --- a/tests/Feature/Auth/RegistrationTest.php +++ b/tests/Feature/Auth/RegistrationTest.php @@ -1,10 +1,12 @@ get('/register'); $response @@ -12,7 +14,7 @@ ->assertSeeVolt('pages.auth.register'); }); -test('new users can register', function () { +test('new users can register', function (): void { $component = Volt::test('pages.auth.register') ->set('name', 'Test User') ->set('email', 'test@example.com') From 44b9bf24a6df0b4fdac9da8a889adcf4b3c3ea3f Mon Sep 17 00:00:00 2001 From: Stephen Jacques Date: Thu, 7 Nov 2024 12:29:17 +0100 Subject: [PATCH 4/6] chore: Apply rebase develop --- app/Livewire/Forms/LoginForm.php | 11 - app/Livewire/Pages/Auth/Login.php | 1 - app/Providers/VoltServiceProvider.php | 8 +- .../views/livewire/pages/auth/login.blade.php | 80 -------- .../livewire/pages/auth/register.blade.php | 191 +----------------- 5 files changed, 2 insertions(+), 289 deletions(-) diff --git a/app/Livewire/Forms/LoginForm.php b/app/Livewire/Forms/LoginForm.php index 4d62b53a..1a436b27 100644 --- a/app/Livewire/Forms/LoginForm.php +++ b/app/Livewire/Forms/LoginForm.php @@ -22,11 +22,6 @@ public function __construct(Component $component = null, string $name = 'form') parent::__construct($component, $name); } - /** - * Attempt to authenticate the request's credentials. - * - * @throws \Illuminate\Validation\ValidationException - */ public function authenticate(): void { $this->ensureIsNotRateLimited(); @@ -42,9 +37,6 @@ public function authenticate(): void RateLimiter::clear($this->throttleKey()); } - /** - * Ensure the authentication request is not rate limited. - */ protected function ensureIsNotRateLimited(): void { if (!RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { @@ -63,9 +55,6 @@ protected function ensureIsNotRateLimited(): void ]); } - /** - * Get the authentication rate limiting throttle key. - */ protected function throttleKey(): string { return Str::transliterate(Str::lower($this->email) . '|' . request()->ip()); diff --git a/app/Livewire/Pages/Auth/Login.php b/app/Livewire/Pages/Auth/Login.php index 1fda1c40..ef8ef64a 100644 --- a/app/Livewire/Pages/Auth/Login.php +++ b/app/Livewire/Pages/Auth/Login.php @@ -10,7 +10,6 @@ use Livewire\Attributes\Layout; use Livewire\Component; -// #[Layout('layouts.guest')] final class Login extends Component { public LoginForm $form; diff --git a/app/Providers/VoltServiceProvider.php b/app/Providers/VoltServiceProvider.php index 44f21b71..7b9052ab 100644 --- a/app/Providers/VoltServiceProvider.php +++ b/app/Providers/VoltServiceProvider.php @@ -9,17 +9,11 @@ final class VoltServiceProvider extends ServiceProvider { - /** - * Register services. - */ public function register(): void { - // + } - /** - * Bootstrap services. - */ public function boot(): void { Volt::mount([ diff --git a/resources/views/livewire/pages/auth/login.blade.php b/resources/views/livewire/pages/auth/login.blade.php index f1eb4b48..10587463 100644 --- a/resources/views/livewire/pages/auth/login.blade.php +++ b/resources/views/livewire/pages/auth/login.blade.php @@ -11,9 +11,6 @@ { public LoginForm $form; - /** - * Handle an incoming authentication request. - */ public function login(): void { $this->validate(); @@ -25,7 +22,6 @@ public function login(): void $this->redirectIntended(default: route('dashboard', absolute: false), navigate: true); } }; ?> -{{-- --}}
@@ -37,7 +33,6 @@ public function login(): void
@csrf
-
-
-
- - -{{-- --}} - - -{{-- - -
-
-

- {{ __('pages/auth.login.title') }} -

-
- - @csrf -
- - - - - - -
- -
-
- -
- -
- - {{ __('pages/auth.login.forgot_password') }} - -
-
- -
- - - - {{ __('pages/auth.login.submit') }} - -
- - - @include('partials._socials-link') -
-
- - -
--}} diff --git a/resources/views/livewire/pages/auth/register.blade.php b/resources/views/livewire/pages/auth/register.blade.php index b524c927..2f4ffbfd 100644 --- a/resources/views/livewire/pages/auth/register.blade.php +++ b/resources/views/livewire/pages/auth/register.blade.php @@ -1,4 +1,3 @@ -<<<<<<< HEAD
@@ -136,192 +135,4 @@ - -======= -validate([ - 'name' => ['required', 'string', 'max:255'], - 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class], - 'username' => ['required', 'string', 'max:255', 'unique:'.User::class], - 'password' => ['required', 'string', 'confirmed', Rules\Password::defaults()], - ]); - - $validated['password'] = Hash::make($validated['password']); - - event(new Registered($user = User::create($validated))); - - Auth::login($user); - - $this->redirect(route('dashboard'), navigate: true); - } -}; -?> - -{{-- --}} -
- -
- - -
-
-

- {{ __('pages/auth.register.join_us') }} -

- -

- {{ __('pages/auth.register.joins_description') }} -

-
- -
- - -
- @csrf -
- -
- - - @error('name') {{ $message }} @enderror -
- - -
- - - @error('email') {{ $message }} @enderror -
- - -
- - - @error('username') {{ $message }} @enderror -
- - -
- - - @error('password') {{ $message }} @enderror -
- - -
- - - @error('password_confirmation') {{ $message }} @enderror -
-
- -
- - - - {{ __('pages/auth.register.submit') }} - -
-
-
- - @include('partials._socials-link') -
-
-
- - - -
-{{--
--}} ->>>>>>> c512f79 (feat: (LAR-77) Mise en place de la class Login, et correction de la connexion) + \ No newline at end of file From e18e87dbd4c926a9d373740ffd9d72254ffff409 Mon Sep 17 00:00:00 2001 From: Stephen Jacques Date: Wed, 6 Nov 2024 17:04:59 +0100 Subject: [PATCH 5/6] feat: (LAR-77) Ajout du message d'erreur sur l'interface de connexion et refractoring du code --- app/Livewire/Forms/LoginForm.php | 32 ++++--- app/Livewire/Pages/Auth/Login.php | 41 --------- .../views/components/input-label.blade.php | 5 -- .../views/components/text-input.blade.php | 3 - .../views/livewire/pages/auth/login.blade.php | 83 ++++++++----------- 5 files changed, 57 insertions(+), 107 deletions(-) delete mode 100644 app/Livewire/Pages/Auth/Login.php delete mode 100644 resources/views/components/input-label.blade.php delete mode 100644 resources/views/components/text-input.blade.php diff --git a/app/Livewire/Forms/LoginForm.php b/app/Livewire/Forms/LoginForm.php index 1a436b27..3c20c374 100644 --- a/app/Livewire/Forms/LoginForm.php +++ b/app/Livewire/Forms/LoginForm.php @@ -7,26 +7,30 @@ use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\Str; use Illuminate\Validation\ValidationException; +use Livewire\Attributes\Validate; use Livewire\Form; -use Livewire\Component; class LoginForm extends Form { + #[Validate('required|string|email')] public string $email = ''; + + #[Validate('required|string')] public string $password = ''; - public bool $remember = false; - public function __construct(Component $component = null, string $name = 'form') - { - $component = $component ?? app(Component::class); - parent::__construct($component, $name); - } + #[Validate('boolean')] + public bool $remember = false; + /** + * Attempt to authenticate the request's credentials. + * + * @throws \Illuminate\Validation\ValidationException + */ public function authenticate(): void { $this->ensureIsNotRateLimited(); - if (!Auth::attempt(['email' => $this->email, 'password' => $this->password], $this->remember)) { + if (! Auth::attempt($this->only(['email', 'password']), $this->remember)) { RateLimiter::hit($this->throttleKey()); throw ValidationException::withMessages([ @@ -37,9 +41,12 @@ public function authenticate(): void RateLimiter::clear($this->throttleKey()); } + /** + * Ensure the authentication request is not rate limited. + */ protected function ensureIsNotRateLimited(): void { - if (!RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { + if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { return; } @@ -55,8 +62,11 @@ protected function ensureIsNotRateLimited(): void ]); } + /** + * Get the authentication rate limiting throttle key. + */ protected function throttleKey(): string { - return Str::transliterate(Str::lower($this->email) . '|' . request()->ip()); + return Str::transliterate(Str::lower($this->email).'|'.request()->ip()); } -} \ No newline at end of file +} diff --git a/app/Livewire/Pages/Auth/Login.php b/app/Livewire/Pages/Auth/Login.php deleted file mode 100644 index ef8ef64a..00000000 --- a/app/Livewire/Pages/Auth/Login.php +++ /dev/null @@ -1,41 +0,0 @@ -form = new LoginForm($this); - } - - public function login(): void - { - $this->validate([ - 'form.email' => ['required', 'email'], - 'form.password' => ['required'], - 'form.remember' => ['boolean'], - ]); - - $this->form->authenticate(); - - Session::regenerate(); - - $this->redirectIntended(route('dashboard')); - } - - public function render(): View - { - return view('livewire.pages.auth.login'); - } -} diff --git a/resources/views/components/input-label.blade.php b/resources/views/components/input-label.blade.php deleted file mode 100644 index 0419a49e..00000000 --- a/resources/views/components/input-label.blade.php +++ /dev/null @@ -1,5 +0,0 @@ -@props(['value']) - - false]) - -merge(['class' => 'border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm']) }}> diff --git a/resources/views/livewire/pages/auth/login.blade.php b/resources/views/livewire/pages/auth/login.blade.php index 10587463..24c568e6 100644 --- a/resources/views/livewire/pages/auth/login.blade.php +++ b/resources/views/livewire/pages/auth/login.blade.php @@ -7,7 +7,7 @@ use Livewire\Attributes\Layout; use Livewire\Volt\Component; -new #[Layout('layouts.guest')] class extends Component +new class extends Component { public LoginForm $form; @@ -22,74 +22,62 @@ public function login(): void $this->redirectIntended(default: route('dashboard', absolute: false), navigate: true); } }; ?> +
From 0576bc94ae7f5958abb6a8e95f4ecce7a250f7cf Mon Sep 17 00:00:00 2001 From: Stephen Jacques Date: Thu, 7 Nov 2024 12:08:18 +0100 Subject: [PATCH 6/6] fix: (LAR-77) Authentication view and tests --- app/Livewire/Forms/LoginForm.php | 4 +- app/Providers/VoltServiceProvider.php | 5 +- .../views/components/input-error.blade.php | 9 -- .../views/components/primary-button.blade.php | 3 - .../views/livewire/pages/auth/login.blade.php | 133 +++++++++--------- tests/Feature/Auth/AuthenticationTest.php | 12 -- tests/Feature/Auth/EmailVerificationTest.php | 48 ------- tests/Feature/Auth/LoginTest.php | 3 - .../Feature/Auth/PasswordConfirmationTest.php | 48 ------- tests/Feature/Auth/PasswordResetTest.php | 75 ---------- tests/Feature/Auth/PasswordUpdateTest.php | 43 ------ tests/Feature/Auth/RegistrationTest.php | 29 ---- 12 files changed, 69 insertions(+), 343 deletions(-) delete mode 100644 resources/views/components/input-error.blade.php delete mode 100644 resources/views/components/primary-button.blade.php delete mode 100644 tests/Feature/Auth/EmailVerificationTest.php delete mode 100644 tests/Feature/Auth/LoginTest.php delete mode 100644 tests/Feature/Auth/PasswordConfirmationTest.php delete mode 100644 tests/Feature/Auth/PasswordResetTest.php delete mode 100644 tests/Feature/Auth/PasswordUpdateTest.php delete mode 100644 tests/Feature/Auth/RegistrationTest.php diff --git a/app/Livewire/Forms/LoginForm.php b/app/Livewire/Forms/LoginForm.php index 3c20c374..352d41e0 100644 --- a/app/Livewire/Forms/LoginForm.php +++ b/app/Livewire/Forms/LoginForm.php @@ -1,5 +1,7 @@ merge(['class' => 'text-sm text-red-600 dark:text-red-400 space-y-1']) }}> - @foreach ((array) $messages as $message) -
  • {{ $message }}
  • - @endforeach - -@endif diff --git a/resources/views/components/primary-button.blade.php b/resources/views/components/primary-button.blade.php deleted file mode 100644 index 99bf3890..00000000 --- a/resources/views/components/primary-button.blade.php +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/resources/views/livewire/pages/auth/login.blade.php b/resources/views/livewire/pages/auth/login.blade.php index 24c568e6..da144ea3 100644 --- a/resources/views/livewire/pages/auth/login.blade.php +++ b/resources/views/livewire/pages/auth/login.blade.php @@ -4,7 +4,7 @@ use App\Livewire\Forms\LoginForm; use Illuminate\Support\Facades\Session; -use Livewire\Attributes\Layout; +use Livewire\Attributes\Title; use Livewire\Volt\Component; new class extends Component @@ -23,78 +23,75 @@ public function login(): void } }; ?> -
    - -
    -
    - -

    - {{ __('pages/auth.login.title') }} -

    -
    -
    - @csrf -
    - - - - - - -
    +
    + +
    +
    + -
    -
    - -
    +

    + {{ __('pages/auth.login.title') }} +

    +
    + + @csrf +
    + + + + + + +
    -
    - - {{ __('pages/auth.login.forgot_password') }} - -
    +
    +
    +
    -
    - - - - {{ __('pages/auth.login.submit') }} - +
    + + {{ __('pages/auth.login.forgot_password') }} +
    - - - @include('partials._socials-link') -
    - - - -
    - +
    +
    + + + + {{ __('pages/auth.login.submit') }} + +
    + + @include('partials._socials-link') +
    +
    + +
    diff --git a/tests/Feature/Auth/AuthenticationTest.php b/tests/Feature/Auth/AuthenticationTest.php index 5f089bbb..580ca08b 100644 --- a/tests/Feature/Auth/AuthenticationTest.php +++ b/tests/Feature/Auth/AuthenticationTest.php @@ -45,18 +45,6 @@ $this->assertGuest(); }); -test('navigation menu can be rendered', function (): void { - $user = User::factory()->create(); - - $this->actingAs($user); - - $response = $this->get('/dashboard'); - - $response - ->assertOk() - ->assertSeeVolt('layout.navigation'); -}); - test('users can logout', function (): void { $user = User::factory()->create(); diff --git a/tests/Feature/Auth/EmailVerificationTest.php b/tests/Feature/Auth/EmailVerificationTest.php deleted file mode 100644 index ededd4dd..00000000 --- a/tests/Feature/Auth/EmailVerificationTest.php +++ /dev/null @@ -1,48 +0,0 @@ -unverified()->create(); - - $response = $this->actingAs($user)->get('/verify-email'); - - $response->assertStatus(200); -}); - -test('email can be verified', function (): void { - $user = User::factory()->unverified()->create(); - - Event::fake(); - - $verificationUrl = URL::temporarySignedRoute( - 'verification.verify', - now()->addMinutes(60), - ['id' => $user->id, 'hash' => sha1($user->email)] - ); - - $response = $this->actingAs($user)->get($verificationUrl); - - Event::assertDispatched(Verified::class); - expect($user->fresh()->hasVerifiedEmail())->toBeTrue(); - $response->assertRedirect(route('dashboard', absolute: false).'?verified=1'); -}); - -test('email is not verified with invalid hash', function (): void { - $user = User::factory()->unverified()->create(); - - $verificationUrl = URL::temporarySignedRoute( - 'verification.verify', - now()->addMinutes(60), - ['id' => $user->id, 'hash' => sha1('wrong-email')] - ); - - $this->actingAs($user)->get($verificationUrl); - - expect($user->fresh()->hasVerifiedEmail())->toBeFalse(); -}); diff --git a/tests/Feature/Auth/LoginTest.php b/tests/Feature/Auth/LoginTest.php deleted file mode 100644 index 174d7fd7..00000000 --- a/tests/Feature/Auth/LoginTest.php +++ /dev/null @@ -1,3 +0,0 @@ -create(); - - $response = $this->actingAs($user)->get('/confirm-password'); - - $response - ->assertSeeVolt('pages.auth.confirm-password') - ->assertStatus(200); -}); - -test('password can be confirmed', function (): void { - $user = User::factory()->create(); - - $this->actingAs($user); - - $component = Volt::test('pages.auth.confirm-password') - ->set('password', 'password'); - - $component->call('confirmPassword'); - - $component - ->assertRedirect('/dashboard') - ->assertHasNoErrors(); -}); - -test('password is not confirmed with invalid password', function (): void { - $user = User::factory()->create(); - - $this->actingAs($user); - - $component = Volt::test('pages.auth.confirm-password') - ->set('password', 'wrong-password'); - - $component->call('confirmPassword'); - - $component - ->assertNoRedirect() - ->assertHasErrors('password'); -}); diff --git a/tests/Feature/Auth/PasswordResetTest.php b/tests/Feature/Auth/PasswordResetTest.php deleted file mode 100644 index ae18ea13..00000000 --- a/tests/Feature/Auth/PasswordResetTest.php +++ /dev/null @@ -1,75 +0,0 @@ -get('/forgot-password'); - - $response - ->assertSeeVolt('pages.auth.forgot-password') - ->assertStatus(200); -}); - -test('reset password link can be requested', function (): void { - Notification::fake(); - - $user = User::factory()->create(); - - Volt::test('pages.auth.forgot-password') - ->set('email', $user->email) - ->call('sendPasswordResetLink'); - - Notification::assertSentTo($user, ResetPassword::class); -}); - -test('reset password screen can be rendered', function (): void { - Notification::fake(); - - $user = User::factory()->create(); - - Volt::test('pages.auth.forgot-password') - ->set('email', $user->email) - ->call('sendPasswordResetLink'); - - Notification::assertSentTo($user, ResetPassword::class, function ($notification) { - $response = $this->get('/reset-password/'.$notification->token); - - $response - ->assertSeeVolt('pages.auth.reset-password') - ->assertStatus(200); - - return true; - }); -}); - -test('password can be reset with valid token', function (): void { - Notification::fake(); - - $user = User::factory()->create(); - - Volt::test('pages.auth.forgot-password') - ->set('email', $user->email) - ->call('sendPasswordResetLink'); - - Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) { - $component = Volt::test('pages.auth.reset-password', ['token' => $notification->token]) - ->set('email', $user->email) - ->set('password', 'password') - ->set('password_confirmation', 'password'); - - $component->call('resetPassword'); - - $component - ->assertRedirect('/login') - ->assertHasNoErrors(); - - return true; - }); -}); diff --git a/tests/Feature/Auth/PasswordUpdateTest.php b/tests/Feature/Auth/PasswordUpdateTest.php deleted file mode 100644 index 3a0ff291..00000000 --- a/tests/Feature/Auth/PasswordUpdateTest.php +++ /dev/null @@ -1,43 +0,0 @@ -create(); - - $this->actingAs($user); - - $component = Volt::test('profile.update-password-form') - ->set('current_password', 'password') - ->set('password', 'new-password') - ->set('password_confirmation', 'new-password') - ->call('updatePassword'); - - $component - ->assertHasNoErrors() - ->assertNoRedirect(); - - $this->assertTrue(Hash::check('new-password', $user->refresh()->password)); -}); - -test('correct password must be provided to update password', function (): void { - $user = User::factory()->create(); - - $this->actingAs($user); - - $component = Volt::test('profile.update-password-form') - ->set('current_password', 'wrong-password') - ->set('password', 'new-password') - ->set('password_confirmation', 'new-password') - ->call('updatePassword'); - - $component - ->assertHasErrors(['current_password']) - ->assertNoRedirect(); -}); diff --git a/tests/Feature/Auth/RegistrationTest.php b/tests/Feature/Auth/RegistrationTest.php deleted file mode 100644 index 19a1f2d0..00000000 --- a/tests/Feature/Auth/RegistrationTest.php +++ /dev/null @@ -1,29 +0,0 @@ -get('/register'); - - $response - ->assertOk() - ->assertSeeVolt('pages.auth.register'); -}); - -test('new users can register', function (): void { - $component = Volt::test('pages.auth.register') - ->set('name', 'Test User') - ->set('email', 'test@example.com') - ->set('password', 'password') - ->set('password_confirmation', 'password'); - - $component->call('register'); - - $component->assertRedirect(route('dashboard', absolute: false)); - - $this->assertAuthenticated(); -});