Give mail delivery its own admin page and move the origin probe off /.well-known

The mail page under System tries SMTP, PHP mail() and both sendmail
modes one by one or all at once, and saves the chosen way to .env
without ever showing the password. IONOS answers everything under
/.well-known/ itself with 403, so the address and HTTPS checks never
reached the installation; the probe now lives at /kantsites-origin
(ADR 122).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
kantarciandClaude Opus 5.5 committed 2026-10-12 21:07:27 +02:00
1 parent 2497b8cb69
commit b982cb8944
17 files changed
+597 -148

No files matched your search

+1 -52
View File
@@ -11,7 +11,6 @@ use App\Ops\Backup;
use App\Ops\Environment;
use App\Ops\HttpsRedirect;
use App\Ops\Installation;
use App\Ops\MailCheck;
use App\Ops\OriginCheck;
use App\Ops\SitePackage;
use App\Ops\Updater;
@@ -20,8 +19,6 @@ use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Routing\Attribute\Route;
/**
@@ -33,7 +30,7 @@ use Symfony\Component\Routing\Attribute\Route;
*/
final class SystemController extends AbstractController
{
public function __construct(private Updater $updater, private Backup $backup, private ArchiveUpdate $archive, private Installation $installation, private string $adminPath, private string $databaseUrl, private string $mediaDirectory, private string $projectDir, private string $siteOrigin, private OriginCheck $originCheck, private HttpKernelInterface $kernel, private PageRepository $pages, private SiteSettings $settings, private SitePackage $packages, private HttpsRedirect $https, private MailCheck $mail)
public function __construct(private Updater $updater, private Backup $backup, private ArchiveUpdate $archive, private Installation $installation, private string $adminPath, private string $databaseUrl, private string $mediaDirectory, private string $projectDir, private string $siteOrigin, private OriginCheck $originCheck, private HttpKernelInterface $kernel, private PageRepository $pages, private SiteSettings $settings, private SitePackage $packages, private HttpsRedirect $https)
{
}
@@ -74,9 +71,6 @@ final class SystemController extends AbstractController
// HTTPS erzwingen und HSTS (ADR 120): der Stand in public/.htaccess.
'https' => $this->https->state(),
'secure' => $request->isSecure(),
// E-Mail-Versand (ADR 121): Weg ohne Passwort, Testmail, die letzten Versuche.
'mail' => $this->mailFacts(),
'mailResult' => $request->getSession()->remove('mail_result'),
]);
}
@@ -526,51 +520,6 @@ final class SystemController extends AbstractController
return $this->back($result['message'], $result['ok'] ? 'success' : 'error');
}
/**
* Eine Testmail an eine frei eingetragene Adresse (ADR 121). Das Ergebnis - Meldung, Hinweis,
* Antworten des Servers - steht danach auf der Seite und in var/log/mail.log.
*/
#[Route('/%kant.admin_path%/system/testmail', name: 'admin_system_testmail', methods: ['POST'])]
public function testMail(Request $request): Response
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
if (!$this->isCsrfTokenValid('system', $request->request->getString('_csrf_token'))) {
return $this->back('Deine Sitzung ist abgelaufen. Bitte melde dich erneut an.');
}
$an = trim($request->request->getString('to'));
$von = trim($request->request->getString('from')) ?: $an;
foreach ([$an, $von] as $adresse) {
if (false === filter_var($adresse, FILTER_VALIDATE_EMAIL)) {
return $this->back(sprintf('„%s“ ist keine gültige E-Mail-Adresse.', mb_substr($adresse, 0, 100)));
}
}
$website = rtrim($this->siteOrigin, '/') ?: $request->getSchemeAndHttpHost();
$email = (new Email())
->from(new Address($von))
->to(new Address($an))
->subject('Testmail von '.(parse_url($website, PHP_URL_HOST) ?: $website))
->text("Diese Testmail kommt aus der Verwaltung von {$website} (System → E-Mail-Versand).
Wenn sie angekommen ist, funktioniert der Versand der Formulare auf demselben Weg.
");
$request->getSession()->set('mail_result', ['an' => $an, 'von' => $von] + $this->mail->send($email, 'Testmail'));
return $this->redirect($this->generateUrl('admin_system').'#mail');
}
/** @return array<string, mixed> */
private function mailFacts(): array
{
$route = $this->mail->route();
return $route + [
'quelle' => $this->environmentSource('MAILER_DSN'),
// Wer sich mit einer Adresse anmeldet, versendet meist auch nur unter ihr.
'absender' => false !== filter_var($route['benutzer'], FILTER_VALIDATE_EMAIL) ? $route['benutzer'] : '',
'protokoll' => $this->mail->recent(),
];
}
/** Die Gegenstelle zur Prüfung oben: öffentlich, und sie verrät nichts als diese Kennung. */
#[Route(OriginCheck::PATH, name: 'site_origin_probe', methods: ['GET'], priority: 10)]
public function originProbe(): Response