CluPilotCloud/app/Livewire/EditMailbox.php

101 lines
3.2 KiB
PHP

<?php
namespace App\Livewire;
use App\Models\Mailbox;
use App\Services\Secrets\SecretCipher;
use LivewireUI\Modal\ModalComponent;
/**
* Editing a mailbox, in a modal (R20).
*
* A modal is reachable WITHOUT the page's route middleware, so it authorises
* itself and re-reads the record instead of trusting a property the browser
* hydrated. save() re-authorises independently of mount() for the same reason
* Secrets::guard() checks on every action rather than once: a Livewire action
* is reachable by anyone who can post to /livewire/update, and a mount that
* succeeded earlier in the session proves nothing about the request now.
*/
class EditMailbox extends ModalComponent
{
public string $uuid = '';
public string $address = '';
public string $displayName = '';
public string $username = '';
/** Always starts empty: a stored password never travels to the browser. */
public string $password = '';
public bool $noReply = false;
public bool $active = true;
public function mount(string $uuid): void
{
$this->authorize('mail.manage');
$box = Mailbox::query()->where('uuid', $uuid)->firstOrFail();
$this->uuid = $box->uuid;
$this->address = $box->address;
$this->displayName = (string) $box->display_name;
$this->username = (string) $box->username;
$this->noReply = $box->no_reply;
$this->active = $box->active;
}
public function save(): void
{
$this->authorize('mail.manage');
$this->validate([
'address' => ['required', 'email', 'max:255'],
'displayName' => ['nullable', 'string', 'max:255'],
'username' => ['nullable', 'string', 'max:255'],
'password' => ['nullable', 'string', 'max:255'],
]);
// A password can only be STORED where SECRETS_KEY exists to encrypt it
// under — the same condition the page's own banner already names.
// Checked here rather than left for Mailbox::setPasswordAttribute() to
// throw through: an uncaught RuntimeException would render Laravel's
// debug page, whose request-payload inspector can show the very
// password just typed. The page must say so, not crash on it.
if ($this->password !== '' && ! app(SecretCipher::class)->isUsable()) {
$this->addError('password', __('mail_settings.no_key'));
return;
}
$box = Mailbox::query()->where('uuid', $this->uuid)->firstOrFail();
$box->address = $this->address;
$box->display_name = $this->displayName ?: null;
$box->username = $this->username ?: null;
$box->no_reply = $this->noReply;
$box->active = $this->active;
// Empty means "leave it alone", not "delete it" — otherwise every edit
// of an address would silently drop the password.
if ($this->password !== '') {
$box->password = $this->password;
$box->last_verified_at = null;
}
$box->save();
$this->password = '';
$this->dispatch('notify', message: __('mail_settings.saved'));
$this->dispatch('mailbox-saved');
$this->closeModal();
}
public function render()
{
return view('livewire.edit-mailbox');
}
}