Add Password Reset
This commit is contained in:
parent
6f4ee0b02d
commit
17eaa2e564
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Auth;
|
||||||
|
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Support\Facades\Lang;
|
||||||
|
use Illuminate\Auth\Notifications\ResetPassword as BaseResetPassword;
|
||||||
|
|
||||||
|
class ResetPassword extends BaseResetPassword
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the reset password notification mail message for the given URL.
|
||||||
|
*
|
||||||
|
* @param string $url
|
||||||
|
* @return MailMessage
|
||||||
|
*/
|
||||||
|
protected function buildMailMessage($url)
|
||||||
|
{
|
||||||
|
return (new MailMessage)
|
||||||
|
->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.'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,8 @@ namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
use Inertia\Response;
|
||||||
|
|
||||||
class ForgotPasswordController extends Controller
|
class ForgotPasswordController extends Controller
|
||||||
{
|
{
|
||||||
|
@ -19,4 +21,9 @@ class ForgotPasswordController extends Controller
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use SendsPasswordResetEmails;
|
use SendsPasswordResetEmails;
|
||||||
|
|
||||||
|
public function showLinkRequestForm(): Response
|
||||||
|
{
|
||||||
|
return Inertia::render('authentication/PasswordReset');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ class LoginController extends Controller
|
||||||
{
|
{
|
||||||
session()->put('title', 'Anmelden');
|
session()->put('title', 'Anmelden');
|
||||||
|
|
||||||
return \Inertia::render('VLogin');
|
return \Inertia::render('authentication/VLogin');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -5,6 +5,9 @@ namespace App\Http\Controllers\Auth;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Providers\RouteServiceProvider;
|
use App\Providers\RouteServiceProvider;
|
||||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
use Inertia\Response;
|
||||||
|
|
||||||
class ResetPasswordController extends Controller
|
class ResetPasswordController extends Controller
|
||||||
{
|
{
|
||||||
|
@ -27,4 +30,21 @@ class ResetPasswordController extends Controller
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $redirectTo = RouteServiceProvider::HOME;
|
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,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
12
app/User.php
12
app/User.php
|
@ -2,12 +2,24 @@
|
||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
|
use App\Auth\ResetPassword;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
|
||||||
class User extends Authenticatable
|
class User extends Authenticatable
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
use Notifiable;
|
||||||
|
|
||||||
public $guarded = [];
|
public $guarded = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $token
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function sendPasswordResetNotification($token)
|
||||||
|
{
|
||||||
|
$this->notify(new ResetPassword($token));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('password_resets', function (Blueprint $table) {
|
||||||
|
$table->string('email')->index();
|
||||||
|
$table->string('token');
|
||||||
|
$table->timestamp('created_at')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('password_resets');
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,44 @@
|
||||||
|
<template>
|
||||||
|
<page-full-layout banner>
|
||||||
|
<template #heading>
|
||||||
|
<page-full-heading-banner>Passwort vergessen</page-full-heading-banner>
|
||||||
|
</template>
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<div class="grid gap-5">
|
||||||
|
<span class="text-gray-500 text-sm"
|
||||||
|
>Hier kannst du dein Passwort zurücksetzen.<br />
|
||||||
|
Gebe dafür die E-Mail-Adresse deines Benutzerkontos ein.<br />
|
||||||
|
Anschließend bekommst du eine E-Mail<br />
|
||||||
|
mit weiteren Anweisungen.</span
|
||||||
|
>
|
||||||
|
<f-text id="email" v-model="values.email" name="email" label="E-Mail-Adresse"></f-text>
|
||||||
|
<button type="submit" class="btn btn-primary">Passwort zurücksetzen</button>
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<button type="button" class="text-gray-500 text-sm hover:text-gray-300" @click.prevent="$inertia.visit('/login')">Zurück zum Login</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</page-full-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import FullLayout from '../../layouts/FullLayout.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
layout: FullLayout,
|
||||||
|
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
values: {
|
||||||
|
email: '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async submit() {
|
||||||
|
await this.axios.post('/password/email', this.values);
|
||||||
|
this.$success('Du hast weitere Instruktionen per E-Mail erhalten.');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,62 @@
|
||||||
|
<template>
|
||||||
|
<page-full-layout banner>
|
||||||
|
<template #heading>
|
||||||
|
<page-full-heading-banner>Passwort vergessen</page-full-heading-banner>
|
||||||
|
</template>
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<div class="grid gap-5">
|
||||||
|
<span class="text-gray-500 text-sm"
|
||||||
|
>Hier kannst du dein Passwort zurücksetzen.<br />
|
||||||
|
Gebe dafür ein neues Passwort ein.<br />
|
||||||
|
Merke oder notiere dir dieses Passwort, bevor du das Formular absendest.<br />
|
||||||
|
Danach wirst du zum Dashboard weitergeleitet.</span
|
||||||
|
>
|
||||||
|
<f-text id="password" v-model="values.password" type="password" name="password" label="Neues Passwort"></f-text>
|
||||||
|
<f-text id="password_confirmation" v-model="values.password_confirmation" type="password" name="password_confirmation" label="Neues Passwort widerholen"></f-text>
|
||||||
|
<button type="submit" class="btn btn-primary">Passwort zurücksetzen</button>
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<button type="button" class="text-gray-500 text-sm hover:text-gray-300" @click.prevent="$inertia.visit('/login')">Zurück zum Login</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</page-full-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import FullLayout from '../../layouts/FullLayout.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
layout: FullLayout,
|
||||||
|
|
||||||
|
props: {
|
||||||
|
token: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
values: {
|
||||||
|
password: '',
|
||||||
|
password_confirmation: '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async submit() {
|
||||||
|
await this.axios.post('/password/reset', {
|
||||||
|
...this.values,
|
||||||
|
email: this.email,
|
||||||
|
token: this.token,
|
||||||
|
});
|
||||||
|
this.$success('Dein Passwort wurde zurückgesetzt.');
|
||||||
|
this.$inertia.visit('/');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,40 @@
|
||||||
|
<template>
|
||||||
|
<page-full-layout banner>
|
||||||
|
<template #heading>
|
||||||
|
<page-full-heading-banner>Login</page-full-heading-banner>
|
||||||
|
</template>
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<div class="grid gap-5">
|
||||||
|
<f-text id="email" v-model="values.email" name="email" label="E-Mail-Adresse"></f-text>
|
||||||
|
<f-text id="password" v-model="values.password" name="password" type="password" label="Passwort"></f-text>
|
||||||
|
<button type="submit" class="btn btn-primary">Login</button>
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<button type="button" class="text-gray-500 text-sm hover:text-gray-300"
|
||||||
|
@click.prevent="$inertia.visit('/password/reset')">Passwort vergessen?</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</page-full-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import FullLayout from '../../layouts/FullLayout.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
layout: FullLayout,
|
||||||
|
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
values: {
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submit() {
|
||||||
|
this.$inertia.post('/login', this.values);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature\Authentication;
|
||||||
|
|
||||||
|
use App\Auth\ResetPassword;
|
||||||
|
use App\User;
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Illuminate\Support\Facades\Notification;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class ForgotPasswordTest extends TestCase
|
||||||
|
{
|
||||||
|
use DatabaseTransactions;
|
||||||
|
|
||||||
|
public function testItShowsResetForm(): void
|
||||||
|
{
|
||||||
|
$this->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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue