Scorri
Limitare l'accesso al vocabolario dei termini di tassonomia utilizzando un Event Subscriber
A volte hai bisogno di Categorie fisse e permanenti sul sito, che non dovrebbero essere modificate accidentalmente. In questo caso puoi utilizzare del codice personalizzato con un Event Subscriber.
Aggiungiamo una nuova classe Event Subscriber in un modulo personalizzato.
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 }
Includiamo poi il nostro Event Subscriber in drupalbook_custom/src/EventSubscriber/TagRedirectSubscriber:
<?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;
/**
* Reindirizza gli utenti non amministratori dalle pagine di amministrazione del vocabolario Tag.
*
* Un subscriber a livello di Request viene eseguito precocemente, permettendoci di interrompere
* la richiesta e restituire una risposta di redirect prima che venga eseguito il controller.
*/
class TagRedirectSubscriber implements EventSubscriberInterface {
/**
* Il servizio entity-type manager.
*
* Mantenuto come dipendenza d'esempio; non è strettamente richiesto dalla logica attuale,
* ma utile se in futuro si necessitano caricamenti di entità.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected EntityTypeManagerInterface $entityTypeManager;
/**
* Il servizio proxy dell'utente corrente.
*
* Utilizzato per controlli rapidi sui ruoli per bypassare il redirect
* per gli amministratori.
*
* @var \Drupal\Core\Session\AccountProxyInterface
*/
protected AccountProxyInterface $currentUser;
/**
* Costruisce il subscriber.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* L'entity-type manager.
* @param \Drupal\Core\Session\AccountProxyInterface $current_user
* L'utente che effettua la richiesta.
*/
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à 32 per garantire che i parametri della route siano disponibili,
// ma il controller non sia ancora stato eseguito.
return [
KernelEvents::REQUEST => ['onKernelRequest', 32],
];
}
/**
* Esegue il redirect quando un non-amministratore accede alle route admin del vocabolario Tag.
*
* @param \Symfony\Component\HttpKernel\Event\RequestEvent $event
* L'evento kernel con la richiesta.
*/
public function onKernelRequest(RequestEvent $event): void {
// Agisce solo sulla richiesta principale.
if (!$event->isMainRequest()) {
return;
}
// Permette agli amministratori di procedere.
if ($this->currentUser->hasRole('administrator')) {
return;
}
$request = $event->getRequest();
$route_name = $request->attributes->get('_route');
// Destinazione per tutti i tentativi bloccati.
$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':
// Controlla che si stia operando sul vocabolario "tag".
$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() restituisce il nome macchina del vocabolario.
if ($term && $term->bundle() === 'tag') {
$event->setResponse(new TrustedRedirectResponse($redirect_to));
}
return;
default:
return;
}
}
}
La classe TagRedirectSubscriber
è un Event Subscriber personalizzato per Drupal, progettato per limitare l'accesso alle pagine di amministrazione di un vocabolario di tassonomia specifico (in questo caso, "tag") agli utenti non amministratori. Ecco un riepilogo della sua struttura e degli aspetti principali presenti nel codice:
1. Scopo e Caso d'Uso
- Obiettivo: Evitare aggiornamenti accidentali o non autorizzati al vocabolario "tag" reindirizzando gli utenti non amministratori lontano dalle route amministrative.
- Beneficio: Fornisce un livello di controllo UI/UX sugli accessi per tassonomie critiche, mantenendo stabili le categorie fisse.
2. Struttura della Classe e Dipendenze
- La classe implementa
EventSubscriberInterface
, rendendola compatibile con il sistema eventi di Symfony usato da Drupal. - Dipendenze iniettate via costruttore:
EntityTypeManagerInterface
: incluso per possibili operazioni future sulle entità. Non necessario nella logica attuale, ma utile per estensioni.AccountProxyInterface
: usato per ottenere e verificare i ruoli dell'utente corrente.
3. Eventi Sottoscritti
- La classe sottoscrive l’evento
KernelEvents::REQUEST
con una priorità di 32.
Questa priorità garantisce:- La disponibilità dei parametri di route (routing già risolto).
- Il controller non ancora eseguito, permettendo al subscriber di intercettare e restituire un redirect.
4. Logica di Redirect
- Il metodo
onKernelRequest()
gestisce controlli di accesso e redirect:- Agisce solo sulle richieste principali: evita esecuzioni multiple su sub-request.
- Permette gli amministratori: l'utente con ruolo
administrator
passa sempre. - Controlla i nomi delle route: agisce solo su route legate alla tassonomia o ai suoi termini.
- Reindirizza i non-amministratori:
- Per overview, aggiunta o lista (
entity.taxonomy_vocabulary.overview_form
,entity.taxonomy_vocabulary.overview_terms
,entity.taxonomy_term.add_form
), verifica che il vocabolario siatag
. - Per modifica e cancellazione (
entity.taxonomy_term.edit_form
,entity.taxonomy_term.delete_form
), verifica che ilbundle()
del termine siatag
.
- Per overview, aggiunta o lista (
- Usa un trusted redirect: se le condizioni corrispondono, l’utente viene reindirizzato a una pagina admin sicura del vocabolario "tag".
- Estendibilità: la logica è facilmente estendibile per altri vocabolari o ruoli semplicemente modificando le condizioni.
5. Sicurezza e Best Practice
- Intercettazione precoce: Eseguito all'inizio della richiesta, il subscriber può impedire accessi prima che vengano caricati o mostrati dati sensibili.
- Bypass basato sui ruoli: Controlla rapidamente i ruoli degli utenti per non ostacolare gli admin.
- Chiara separazione delle responsabilità: La logica di routing, i controlli utente e il redirect sono ben separati e manutenibili.
6. Potenziali Miglioramenti
- Poiché viene iniettato
EntityTypeManagerInterface
, è facile aggiungere in futuro controlli sulle entità (es. permessi basati su proprietà del termine o contenuti correlati). - È possibile generalizzare la classe per gestire più vocabolari o reindirizzamenti configurabili via interfaccia.
7. Conclusioni Chiave
- Questo Event Subscriber mostra un approccio pratico per il controllo degli accessi in Drupal, sfruttando l’architettura ad eventi di Symfony per un’intercettazione rapida ed efficiente delle richieste.
- L’approccio è ideale per proteggere i vocabolari di tassonomia che dovrebbero essere gestiti solo da utenti fidati, riducendo il rischio di modifiche accidentali.