107 lines
3.9 KiB
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'));
|
|
}
|
|
}
|