116 lines
4.1 KiB
PHP
116 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Auth;
|
|
|
|
use App\Models\Operator;
|
|
use App\Support\PasswordPolicy;
|
|
use Illuminate\Auth\Events\PasswordReset;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Illuminate\Support\Facades\Password;
|
|
use Illuminate\Support\Str;
|
|
use Livewire\Attributes\Layout;
|
|
use Livewire\Attributes\Validate;
|
|
use Livewire\Component;
|
|
|
|
/**
|
|
* Das andere Ende des Einladungslinks: hier vergibt ein eingeladener Betreiber
|
|
* sein Passwort.
|
|
*
|
|
* Eigene Seite und nicht die des Portals (R21). Auch wenn beide Seiten dasselbe
|
|
* Formular zeigen, lösen sie gegen verschiedene Broker und damit gegen
|
|
* verschiedene Tabellen ein — ein Betreiber-Token gilt hier und sonst nirgends,
|
|
* ein Kunden-Token nirgends hier. Eine gemeinsame Seite wäre genau die
|
|
* Vermischung, die R21 beendet hat.
|
|
*
|
|
* Der Token wird von Laravels Broker geprüft, nicht hier: er hält den Hash, die
|
|
* Frist und das Löschen nach dem Einlösen. Ein Link, der zweimal funktioniert,
|
|
* entsteht dadurch, dass jemand eines dieser drei Dinge nachbaut.
|
|
*
|
|
* Angemeldet wird danach NIEMAND. Der Link kam per E-Mail, und wer dieses
|
|
* Postfach lesen kann, bekäme sonst die Sitzung gleich dazu — auf einer
|
|
* Konsole, die die ganze Flotte verwaltet. Der Eingeladene tippt das Passwort
|
|
* einmal mehr auf der Anmeldeseite, und dort greift dann auch die
|
|
* Zwei-Faktor-Pflicht, falls der Inhaber sie eingeschaltet hat.
|
|
*/
|
|
#[Layout('layouts.portal')]
|
|
class OperatorSetPassword extends Component
|
|
{
|
|
public string $token = '';
|
|
|
|
#[Validate('required|email|max:255')]
|
|
public string $email = '';
|
|
|
|
public string $password = '';
|
|
|
|
public string $password_confirmation = '';
|
|
|
|
public bool $done = false;
|
|
|
|
/**
|
|
* Die Länge kommt aus PasswordPolicy, nicht aus einem `#[Validate]`-Attribut:
|
|
* ein Attribut ist ein fester String und kann eine Einstellung nicht lesen.
|
|
*
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function rules(): array
|
|
{
|
|
return ['password' => [...PasswordPolicy::rules(), 'confirmed']];
|
|
}
|
|
|
|
public function mount(string $token): void
|
|
{
|
|
$this->token = $token;
|
|
// Aus der Adresszeile vorbelegt, aber als Feld sichtbar: eine Mail, die
|
|
// unterwegs umgebrochen wurde, lässt sich so noch von Hand retten.
|
|
$this->email = (string) request()->query('email', '');
|
|
}
|
|
|
|
/**
|
|
* NICHT reset(): Livewire\Component::reset() leert Eigenschaften, und eine
|
|
* Komponente, die sie neu deklariert, lädt gar nicht erst.
|
|
*/
|
|
public function save(): void
|
|
{
|
|
$this->validate();
|
|
|
|
$status = Password::broker('operators')->reset(
|
|
[
|
|
'email' => $this->email,
|
|
'password' => $this->password,
|
|
'password_confirmation' => $this->password_confirmation,
|
|
'token' => $this->token,
|
|
],
|
|
function (Operator $operator, string $password) {
|
|
$operator->forceFill([
|
|
'password' => Hash::make($password),
|
|
// Jede bestehende „Angemeldet bleiben"-Sitzung stirbt mit.
|
|
// Beim ersten Setzen ist keine da; beim zweiten Weg über
|
|
// diese Seite — einer erneuten Einladung — sehr wohl.
|
|
'remember_token' => Str::random(60),
|
|
])->save();
|
|
|
|
event(new PasswordReset($operator));
|
|
},
|
|
);
|
|
|
|
if ($status !== Password::PASSWORD_RESET) {
|
|
// EINE Meldung für alle Fehlschläge, und ausdrücklich nicht
|
|
// __($status): Laravels Statusschlüssel (passwords.token,
|
|
// passwords.user) haben in dieser Installation keine
|
|
// Übersetzungsdatei und stünden roh als „passwords.token" auf der
|
|
// Seite. Sie zu unterscheiden wäre ohnehin falsch — „diese Adresse
|
|
// kennen wir nicht" verrät einem Fremden, wer Betreiber ist.
|
|
$this->addError('email', __('auth.console_invite_invalid'));
|
|
|
|
return;
|
|
}
|
|
|
|
$this->done = true;
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
return view('livewire.auth.operator-set-password');
|
|
}
|
|
}
|