<?php
namespace App\EventSubscriber;
use App\Entity\User;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Http\Authenticator\Token\PostAuthenticationToken;
/**
* Steuert den Zugang zu /install/action/{tgaHash}:
*
* - Jeder neue tgaHash (neuer QR-Code) löscht die bestehende qrcode-Session,
* damit alte Sitzungen nicht wiederverwendet werden können.
* - Dashboard-Benutzer (onlyQrCodeUser = false) werden automatisch in die
* qrcode-Firewall eingeloggt, ohne AppCode eingeben zu müssen.
* - onlyQrCodeUser muss sich immer mit dem AppCode am QR-Login anmelden.
*/
class InstallAccessSubscriber implements EventSubscriberInterface
{
private const SESSION_KEY_QRCODE = '_security_qrcode';
private const SESSION_KEY_DEFAULT = '_security_default';
private const SESSION_KEY_INSTALL_HASH = '_install_tgaHash';
private const INSTALL_ENTRY_ROUTE = 'install_chose_action';
private LoggerInterface $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public static function getSubscribedEvents(): array
{
// Nach RouterListener (32) aber vor Security-Firewall (8)
return [
KernelEvents::REQUEST => ['onKernelRequest', 9],
];
}
public function onKernelRequest(RequestEvent $event): void
{
if (!$event->isMainRequest()) {
return;
}
$request = $event->getRequest();
if ($request->attributes->get('_route') !== self::INSTALL_ENTRY_ROUTE) {
return;
}
if (!$request->hasSession()) {
return;
}
$tgaHash = (string) $request->attributes->get('tgaHash', '');
$session = $request->getSession();
$hasQrcodeSession = (bool) $session->get(self::SESSION_KEY_QRCODE);
$storedHash = (string) $session->get(self::SESSION_KEY_INSTALL_HASH, '');
// Gleicher tgaHash und gültige qrcode-Session → einfach durchlassen
// (auch wenn tgaHash leer ist, z.B. nach Logout ohne Hash-Kontext)
if ($hasQrcodeSession && $storedHash === $tgaHash) {
$this->logger->debug('InstallAccessSubscriber: same tgaHash, passing through.');
return;
}
// Neuer tgaHash oder abgelaufene Session → alles löschen
$session->remove(self::SESSION_KEY_QRCODE);
$session->remove(self::SESSION_KEY_INSTALL_HASH);
// Prüfen ob Dashboard-Session vorhanden
$defaultTokenSerialized = $session->get(self::SESSION_KEY_DEFAULT);
if (!$defaultTokenSerialized) {
$this->logger->debug('InstallAccessSubscriber: no default session, redirecting to QR login.');
return;
}
try {
$defaultToken = unserialize($defaultTokenSerialized);
if (!is_object($defaultToken) || !method_exists($defaultToken, 'getUser')) {
return;
}
$user = $defaultToken->getUser();
if (!$user instanceof User) {
return;
}
if ($user->getOnlyQrCodeUser()) {
$this->logger->debug('InstallAccessSubscriber: onlyQrCodeUser, must use QR login.');
return;
}
// Dashboard-Benutzer automatisch in qrcode-Firewall einloggen
$qrcodeToken = new PostAuthenticationToken($user, 'qrcode', $user->getRoles());
$session->set(self::SESSION_KEY_QRCODE, serialize($qrcodeToken));
$session->set(self::SESSION_KEY_INSTALL_HASH, $tgaHash);
$this->logger->debug(sprintf(
'InstallAccessSubscriber: auto-login user %d (%s) into qrcode firewall for tgaHash=%s',
$user->getId(),
$user->getEmail(),
$tgaHash
));
} catch (\Throwable $e) {
$this->logger->warning('InstallAccessSubscriber: failed to process token: '.$e->getMessage());
}
}
}