diff --git a/app/Auth/ResetPassword.php b/app/Auth/ResetPassword.php new file mode 100644 index 00000000..61ce8508 --- /dev/null +++ b/app/Auth/ResetPassword.php @@ -0,0 +1,26 @@ +subject(Lang::get('Passwort zurücksetzen | Adrema')) + ->line(Lang::get('Du erhälst diese E-Mail, weil du eine Anfrage zum zurücksetzen deines Account-Passworts gestellt hast.')) + ->action(Lang::get('Passwort zurücksetzen'), $url) + ->line(Lang::get('Dieser Link wird in :count Minuten ablaufen.', ['count' => config('auth.passwords.' . config('auth.defaults.passwords') . '.expire')])) + ->line(Lang::get('Wenn du die Anfrage nicht selbst gestellt hast, ist keine weitere Aktion erforderlich.')); + } +} diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php index 465c39cc..f912e2d1 100644 --- a/app/Http/Controllers/Auth/ForgotPasswordController.php +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -4,6 +4,8 @@ namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use Illuminate\Foundation\Auth\SendsPasswordResetEmails; +use Inertia\Inertia; +use Inertia\Response; class ForgotPasswordController extends Controller { @@ -19,4 +21,9 @@ class ForgotPasswordController extends Controller */ use SendsPasswordResetEmails; + + public function showLinkRequestForm(): Response + { + return Inertia::render('authentication/PasswordReset'); + } } diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 34914873..8dec071d 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -44,7 +44,7 @@ class LoginController extends Controller { session()->put('title', 'Anmelden'); - return \Inertia::render('VLogin'); + return \Inertia::render('authentication/VLogin'); } /** diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php index b1726a36..a2d83dcc 100644 --- a/app/Http/Controllers/Auth/ResetPasswordController.php +++ b/app/Http/Controllers/Auth/ResetPasswordController.php @@ -5,6 +5,9 @@ namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use App\Providers\RouteServiceProvider; use Illuminate\Foundation\Auth\ResetsPasswords; +use Illuminate\Http\Request; +use Inertia\Inertia; +use Inertia\Response; class ResetPasswordController extends Controller { @@ -27,4 +30,21 @@ class ResetPasswordController extends Controller * @var string */ protected $redirectTo = RouteServiceProvider::HOME; + + /** + * Display the password reset view for the given token. + * + * If no token is present, display the link request form. + * + * @param \Illuminate\Http\Request $request + */ + public function showResetForm(Request $request): Response + { + $token = $request->route()->parameter('token'); + + return Inertia::render('authentication/PasswordResetConfirm', [ + 'token' => $token, + 'email' => $request->email, + ]); + } } diff --git a/app/User.php b/app/User.php index 4b7c8d95..690c256a 100644 --- a/app/User.php +++ b/app/User.php @@ -2,12 +2,24 @@ namespace App; +use App\Auth\ResetPassword; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; +use Illuminate\Notifications\Notifiable; class User extends Authenticatable { use HasFactory; + use Notifiable; public $guarded = []; + + /** + * @param string $token + * @return void + */ + public function sendPasswordResetNotification($token) + { + $this->notify(new ResetPassword($token)); + } } diff --git a/database/migrations/2014_10_12_100000_create_password_resets_table.php b/database/migrations/2014_10_12_100000_create_password_resets_table.php new file mode 100644 index 00000000..fcacb80b --- /dev/null +++ b/database/migrations/2014_10_12_100000_create_password_resets_table.php @@ -0,0 +1,32 @@ +string('email')->index(); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('password_resets'); + } +}; diff --git a/resources/js/views/authentication/PasswordReset.vue b/resources/js/views/authentication/PasswordReset.vue new file mode 100644 index 00000000..ac46418a --- /dev/null +++ b/resources/js/views/authentication/PasswordReset.vue @@ -0,0 +1,44 @@ + + + diff --git a/resources/js/views/authentication/PasswordResetConfirm.vue b/resources/js/views/authentication/PasswordResetConfirm.vue new file mode 100644 index 00000000..b4c11f0b --- /dev/null +++ b/resources/js/views/authentication/PasswordResetConfirm.vue @@ -0,0 +1,62 @@ + + + diff --git a/resources/js/views/authentication/VLogin.vue b/resources/js/views/authentication/VLogin.vue new file mode 100644 index 00000000..1a6dbd4f --- /dev/null +++ b/resources/js/views/authentication/VLogin.vue @@ -0,0 +1,40 @@ + + + diff --git a/tests/Feature/Authentication/ForgotPasswordTest.php b/tests/Feature/Authentication/ForgotPasswordTest.php new file mode 100644 index 00000000..81682a13 --- /dev/null +++ b/tests/Feature/Authentication/ForgotPasswordTest.php @@ -0,0 +1,46 @@ +withoutExceptionHandling(); + $response = $this->get('/password/reset'); + + $this->assertComponent('authentication/PasswordReset', $response); + } + + public function testItRequiresAnEmailAddress(): void + { + $this->postJson('/password/email')->assertJsonValidationErrors(['email' => 'E-Mail Adresse ist erforderlich.']); + } + + public function testItNeedsAnActiveUser(): void + { + $this->postJson('/password/email', [ + 'email' => 'test@aa.de', + ])->assertJsonValidationErrors(['email' => 'Es konnte leider kein Nutzer mit dieser E-Mail-Adresse gefunden werden.']); + } + + public function testItSendsPasswordResetLink(): void + { + Notification::fake(); + + $user = User::factory()->create(['email' => 'test@aa.de']); + $this->postJson('/password/email', [ + 'email' => 'test@aa.de', + ])->assertOk(); + + Notification::assertSentTo($user, ResetPassword::class); + } +}