CluPilotCloud/app/Livewire/Concerns/ChangesOwnPassword.php

107 lines
3.9 KiB
PHP

<?php
namespace App\Livewire\Concerns;
use App\Actions\Fortify\PasswordValidationRules;
use App\Actions\Fortify\UpdateUserPassword;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
/**
* Changing your own password, from wherever you are signed in.
*
* There was no way to do it at all: Fortify's updatePasswords feature was off,
* so an account created with a generated password kept it until someone opened
* a shell on the server. Shared by the console and the customer portal because
* the rules are the same in both, and having two copies of a password rule is
* how one of them ends up weaker.
*
* Goes through Fortify's own action rather than hashing here, so the password
* rules stay in the single place that already defines them — on the `web`
* guard, which is the only one UpdateUserPassword knows: it is typed to
* `User` and pins its own `current_password` rule to `web`. An operator
* validates against the SAME PasswordValidationRules and writes its own hash
* here instead, rather than routing through an action written for a
* different model on a different guard.
*/
trait ChangesOwnPassword
{
use PasswordValidationRules;
public string $currentPassword = '';
public string $newPassword = '';
public string $newPasswordConfirmation = '';
/**
* Which guard's account this component changes the password of.
*
* Overridden to 'operator' by the console's Settings page. A default of
* 'web' keeps the portal working unchanged.
*/
protected function passwordAccountGuard(): string
{
return 'web';
}
public function updateOwnPassword(): void
{
// The current password is asked for, and checked here as well as in the
// action: an attacker on an unlocked machine should not be able to lock
// the owner out of their own account with two keystrokes.
$this->validate([
'currentPassword' => 'required|string',
'newPassword' => 'required|string',
'newPasswordConfirmation' => 'required|string',
]);
$guard = $this->passwordAccountGuard();
$account = Auth::guard($guard)->user();
if (! Hash::check($this->currentPassword, $account->password)) {
$this->addError('currentPassword', __('admin_settings.password_wrong'));
return;
}
if ($guard === 'web') {
try {
app(UpdateUserPassword::class)->update($account, [
'current_password' => $this->currentPassword,
'password' => $this->newPassword,
'password_confirmation' => $this->newPasswordConfirmation,
]);
} catch (ValidationException $e) {
foreach ($e->errors() as $field => $messages) {
$this->addError(match ($field) {
'current_password' => 'currentPassword',
'password' => 'newPassword',
default => $field,
}, $messages[0]);
}
return;
}
} else {
try {
Validator::make([
'password' => $this->newPassword,
'password_confirmation' => $this->newPasswordConfirmation,
], ['password' => $this->passwordRules()])->validate();
} catch (ValidationException $e) {
$this->addError('newPassword', $e->errors()['password'][0]);
return;
}
$account->forceFill(['password' => Hash::make($this->newPassword)])->save();
}
$this->reset('currentPassword', 'newPassword', 'newPasswordConfirmation');
$this->dispatch('notify', message: __('admin_settings.password_changed'));
}
}