CluPilotCloud/app/Mail/SecurityBlockMail.php

102 lines
4.1 KiB
PHP

<?php
namespace App\Mail;
use App\Mail\Concerns\RidesALane;
use App\Mail\Concerns\SendsFromMailbox;
use App\Models\SecurityBlock;
use App\Services\Mail\MailCatalogue;
use App\Services\Mail\MailPurpose;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
/**
* "Eine Adresse wurde wegen Anmeldeversuchen gesperrt."
*
* ZWEI Empfängerkreise, eine Mailart — und das ist keine Bequemlichkeit,
* sondern der Grund, warum diese Klasse beide Fälle ausdrücklich auseinander
* hält:
*
* - **Instanz-Sperre** → an den Inhaber. Text in der zweiten Person („bei
* Ihrer Cloud"), Anrede aus dem Kundendatensatz, Knopf auf die
* Sicherheitsseite des Portals (`portal.security`), wo er die Sperre
* einsehen und vorzeitig aufheben kann.
* - **Host-Sperre** → an die Betreiber mit `hosts.manage` (siehe
* `BlockAddress::notifyHostManagers()`). Eigener Text mit dem NAMEN des
* Hosts, keine Anrede, und der Knopf zeigt auf die Host-Detailseite der
* Konsole.
*
* Der Unterschied ist nicht kosmetisch: ein Betreiber hat im Portal kein
* Konto, und Host-Sperren erscheinen dort ohnehin nie. Ein Knopf auf
* `portal.security` in einer Host-Mail führt also zwangsläufig ins Leere —
* und ohne den Hostnamen im Text stünde in einer Meldung an jemanden mit
* einem Dutzend Maschinen nicht, welche gemeint ist.
*
* Aus dem SYSTEM-Postfach, genau wie NewDeviceSignInMail: das ist kein
* Gespräch, und eine Antwort darauf soll kein Ticket öffnen. Über die Wegwahl
* (`MailRoute`, Mailart `security-block`) kann der Betreiber sie auf ein
* anderes Postfach legen — dann geht sie auch über DESSEN SMTP-Konto, nicht
* nur mit dessen Absenderzeile.
*
* Die Drossel — höchstens eine Mail je Subjekt und Stunde — sitzt in
* BlockAddress, nicht hier: diese Klasse weiß nichts davon, ob sie gerade die
* erste oder die zehnte Sperre dieser Stunde beschreibt, und muss es auch
* nicht wissen.
*/
class SecurityBlockMail extends Mailable implements ShouldQueue
{
use Queueable, RidesALane, SendsFromMailbox, SerializesModels;
public function __construct(public SecurityBlock $block)
{
$this->mailer(MailCatalogue::mailer('security-block'));
}
public function envelope(): Envelope
{
$host = $this->block->host;
return $this->mailboxEnvelope(
MailPurpose::SYSTEM,
$host !== null
? __('security.mail_subject_host', ['host' => $host->name])
: __('security.mail_subject'),
'security-block',
);
}
public function content(): Content
{
$host = $this->block->host;
// instance() statt eines eigenen Customer-Parameters: der Aufrufer
// (BlockAddress) hat ohnehin nur den Datensatz, und ein zweiter Weg,
// an dieselbe Instanz zu kommen, wäre eine zweite Stelle, an der der
// Name veralten könnte.
$customer = $this->block->instance?->customer;
return new Content(view: 'mail.security-block', with: [
// Der Name des Hosts ist das Unterscheidungsmerkmal für die
// Ansicht: gesetzt heißt Host-Sperre, null heißt Instanz-Sperre.
// Ein zweites Flag daneben wäre eine zweite Wahrheit.
'hostName' => $host?->name,
'name' => (string) ($customer?->contact_name ?: $customer?->name ?: ''),
'ip' => $this->block->ip,
'attempts' => $this->block->attempts,
'blockedAt' => $this->block->blocked_at,
'expiresAt' => $this->block->expires_at,
// Der Betreiber hat im Portal kein Konto, und Host-Sperren stehen
// dort nie. Sein Weg zum Handgriff ist die Host-Detailseite der
// Konsole, auf der die Host-Sperren mit ihrem Aufheben-Knopf
// stehen (Aufgabe 6, App\Livewire\Admin\HostDetail).
'securityUrl' => $host !== null
? route('admin.hosts.show', $host)
: route('portal.security'),
]);
}
}