Scroll
Den Zugriff auf das Taxonomie-Vokabular mithilfe eines Event Subscribers einschränken
Manchmal benötigt man feste, permanente Kategorien auf der Website, die nicht versehentlich aktualisiert werden sollen. In diesem Fall kann man eigenen Code mit einem Event Subscriber verwenden.
Fügen wir eine neue Event Subscriber-Klasse in einem benutzerdefinierten Modul hinzu.
drupalbook_custom.services.yml
services:
drupalbook_custom.tag_redirect_subscriber:
class: Drupal\drupalbook_custom\EventSubscriber\TagRedirectSubscriber
arguments:
- '@entity_type.manager'
- '@current_user'
tags:
- { name: event_subscriber }
Und wir binden unseren Event Subscriber unter drupalbook_custom/src/EventSubscriber/TagRedirectSubscriber ein:
<?php
namespace Drupal\drupalbook_custom\EventSubscriber;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Routing\TrustedRedirectResponse;
use Drupal\Core\Session\AccountProxyInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Leitet Nicht-Administratoren von den Administrationsseiten des Tag-Vokabulars um.
*
* Ein Subscriber auf Request-Ebene läuft frühzeitig und erlaubt es uns,
* die Anfrage zu unterbrechen und eine Weiterleitung zu senden, bevor der
* zugehörige Controller ausgeführt wird.
*/
class TagRedirectSubscriber implements EventSubscriberInterface {
/**
* Der Entity-Type-Manager Service.
*
* Wird als Beispiel für Abhängigkeiten behalten; ist für die aktuelle
* Logik nicht zwingend erforderlich, aber nützlich, falls zukünftige
* Erweiterungen das Laden von Entitäten erfordern.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected EntityTypeManagerInterface $entityTypeManager;
/**
* Der Current User Proxy Service.
*
* Wird für schnelle Rollenprüfungen genutzt, um Administratoren
* von der Umleitung auszunehmen.
*
* @var \Drupal\Core\Session\AccountProxyInterface
*/
protected AccountProxyInterface $currentUser;
/**
* Konstruktor des Subscribers.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* Der Entity-Type-Manager.
* @param \Drupal\Core\Session\AccountProxyInterface $current_user
* Der aktuelle Benutzer der Anfrage.
*/
public function __construct(
EntityTypeManagerInterface $entity_type_manager,
AccountProxyInterface $current_user,
) {
$this->entityTypeManager = $entity_type_manager;
$this->currentUser = $current_user;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
// Priorität 32 stellt sicher, dass Routen-Parameter verfügbar sind,
// der Controller aber noch nicht ausgeführt wurde.
return [
KernelEvents::REQUEST => ['onKernelRequest', 32],
];
}
/**
* Führt die Umleitung durch, wenn ein Nicht-Administrator Tag-Admin-Routen aufruft.
*
* @param \Symfony\Component\HttpKernel\Event\RequestEvent $event
* Das Kernel-Event mit der Anfrage.
*/
public function onKernelRequest(RequestEvent $event): void {
// Nur bei der Hauptanfrage aktiv werden.
if (!$event->isMainRequest()) {
return;
}
// Administratoren werden nicht umgeleitet.
if ($this->currentUser->hasRole('administrator')) {
return;
}
$request = $event->getRequest();
$route_name = $request->attributes->get('_route');
// Zielseite für alle blockierten Versuche.
$redirect_to = 'https://drupalbook.org/admin/structure'
. '/taxonomy/manage/tag/overview';
switch ($route_name) {
case 'entity.taxonomy_vocabulary.overview_form':
case 'entity.taxonomy_vocabulary.overview_terms':
case 'entity.taxonomy_term.add_form':
// Prüfen, ob es sich um das „tag“-Vokabular handelt.
$vocabulary = $request->attributes->get('taxonomy_vocabulary');
if (!empty($vocabulary) && $vocabulary->id() === 'tag') {
$event->setResponse(new TrustedRedirectResponse($redirect_to));
}
return;
case 'entity.taxonomy_term.edit_form':
case 'entity.taxonomy_term.delete_form':
/** @var \Drupal\taxonomy\Entity\Term|null $term */
$term = $request->attributes->get('taxonomy_term');
// bundle() gibt den maschinenlesbaren Namen des Vokabulars zurück.
if ($term && $term->bundle() === 'tag') {
$event->setResponse(new TrustedRedirectResponse($redirect_to));
}
return;
default:
return;
}
}
}
Die TagRedirectSubscriber
-Klasse ist ein benutzerdefinierter Event Subscriber für Drupal, der den Zugriff auf Administrationsseiten eines bestimmten Taxonomie-Vokabulars (hier „tag“) für Nicht-Administratoren einschränkt. Hier ist eine Übersicht der Struktur und der wichtigsten Aspekte aus dem Code:
1. Zweck und Anwendungsfall
- Ziel: Verhindern von versehentlichen oder unautorisierten Änderungen am „tag“-Vokabular durch Umleitung nicht-administrativer Nutzer von dessen Admin-Routen.
- Vorteil: Bietet eine UI/UX-basierte Zugriffskontrolle für kritische Taxonomien und sorgt für Stabilität bei festen Kategorien.
2. Klassenstruktur und Abhängigkeiten
- Die Klasse implementiert
EventSubscriberInterface
und ist damit kompatibel mit Symfony’s Event-System, wie es in Drupal verwendet wird. - Abhängigkeiten per Konstruktor-Injektion:
EntityTypeManagerInterface
: Für künftige Entity-Operationen. Nicht notwendig für die aktuelle Logik, aber leicht erweiterbar.AccountProxyInterface
: Für effizientes Auslesen und Prüfen der Rollen des aktuellen Benutzers.
3. Registrierte Events
- Die Klasse registriert sich für das
KernelEvents::REQUEST
-Event mit Priorität 32.
Diese Priorität gewährleistet:- Routenparameter stehen zur Verfügung (Routing ist aufgelöst).
- Der Controller für die Route wurde noch nicht ausgeführt, sodass der Subscriber die Anfrage frühzeitig mit einem Redirect abfangen kann.
4. Umleitungs-Logik
- Die Methode
onKernelRequest()
übernimmt alle Zugriffsprüfungen und Umleitungs-Logik:- Nur bei Hauptanfragen aktiv: Vermeidet doppelte Behandlung bei Subrequests.
- Administratoren dürfen passieren: Hat der Benutzer die Rolle
administrator
, wird er nicht umgeleitet. - Prüft Routennamen: Nur bestimmte Routen im Zusammenhang mit Taxonomie-Vokabular oder -Begriffen werden beachtet.
- Leitet Nicht-Admins um:
- Bei Übersicht, Hinzufügen oder Listen-Routen (
entity.taxonomy_vocabulary.overview_form
,entity.taxonomy_vocabulary.overview_terms
,entity.taxonomy_term.add_form
) wird geprüft, ob das Vokabulartag
ist. - Bei Bearbeiten- und Löschen-Routen (
entity.taxonomy_term.edit_form
,entity.taxonomy_term.delete_form
) wird geprüft, ob das bundle() (maschinenlesbarer Name) des Begriffstag
ist.
- Bei Übersicht, Hinzufügen oder Listen-Routen (
- Verwendet Trusted Redirect: Trifft eine Bedingung zu, wird der Nutzer auf eine sichere Admin-Übersichtsseite des „tag“-Vokabulars umgeleitet.
- Erweiterbarkeit: Die Logik ist einfach für weitere Vokabulare oder Rollen anpassbar, indem die Bedingungen erweitert werden.
5. Sicherheit und Best Practices
- Frühes Abfangen: Da der Subscriber bereits auf der Request-Ebene eingreift, kann Zugriffsschutz erfolgen, bevor sensible Daten verarbeitet oder angezeigt werden.
- Rollenbasierte Ausnahme: Rollen werden effizient geprüft, damit Site Builder oder Administratoren nicht ausgesperrt werden.
- Klare Trennung der Logik: Routing, Benutzerprüfung und Umleitung sind sauber voneinander getrennt und so besser wartbar.
6. Mögliche Erweiterungen
- Da
EntityTypeManagerInterface
injiziert wird, können zukünftig problemlos entity-basierte Prüfungen (z.B. Berechtigungen abhängig von Eigenschaften eines Begriffs oder verknüpften Inhalten) ergänzt werden. - Die Klasse könnte generalisiert werden, um mehrere Vokabulare oder konfigurierbare Weiterleitungen per Konfiguration zu unterstützen.
7. Wichtigste Erkenntnisse
- Dieser Event Subscriber zeigt einen praxisnahen Ansatz für Zugriffskontrolle in Drupal, wobei Symfony’s Event-Architektur für frühzeitiges, effizientes Request-Handling genutzt wird.
- Dieser Ansatz eignet sich ideal zum Schutz von Taxonomie-Vokabularen, die nur von vertrauenswürdigen Nutzern verwaltet werden sollen, und senkt das Risiko versehentlicher Änderungen.