Scroll
Restringir el acceso al vocabulario de términos de taxonomía utilizando un Event Subscriber
A veces necesitas Categorías fijas y permanentes en el sitio, que no deben actualizarse accidentalmente. En este caso, puedes utilizar código personalizado con un Event Subscriber.
Vamos a agregar una nueva clase Event Subscriber en un módulo personalizado.
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 }
Y añadimos nuestro Event Subscriber en 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;
/**
* Redirige a los no administradores desde las páginas de administración del vocabulario "Tag".
*
* Un subscriber a nivel de Request se ejecuta temprano, permitiéndonos interrumpir
* la solicitud y devolver una redirección antes de que se ejecute el controlador correspondiente.
*/
class TagRedirectSubscriber implements EventSubscriberInterface {
/**
* El servicio de gestor de tipos de entidad.
*
* Se mantiene como dependencia de ejemplo; no es estrictamente necesario para la lógica actual,
* pero resulta útil si en el futuro se requieren operaciones sobre entidades.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected EntityTypeManagerInterface $entityTypeManager;
/**
* El servicio de usuario actual (proxy).
*
* Se utiliza para comprobaciones rápidas de roles, permitiendo evitar la redirección para
* los administradores.
*
* @var \Drupal\Core\Session\AccountProxyInterface
*/
protected AccountProxyInterface $currentUser;
/**
* Constructor del subscriber.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* El gestor de tipos de entidad.
* @param \Drupal\Core\Session\AccountProxyInterface $current_user
* El usuario que realiza la solicitud.
*/
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 {
// La prioridad 32 asegura que los parámetros de ruta estén disponibles,
// pero el controlador aún no ha sido ejecutado.
return [
KernelEvents::REQUEST => ['onKernelRequest', 32],
];
}
/**
* Realiza la redirección cuando un usuario no administrador accede a rutas de administración de "Tag".
*
* @param \Symfony\Component\HttpKernel\Event\RequestEvent $event
* El evento del kernel que transporta la solicitud.
*/
public function onKernelRequest(RequestEvent $event): void {
// Actuar solo sobre la solicitud principal (master).
if (!$event->isMainRequest()) {
return;
}
// Permitir a los administradores continuar sin redirección.
if ($this->currentUser->hasRole('administrator')) {
return;
}
$request = $event->getRequest();
$route_name = $request->attributes->get('_route');
// Destino para todos los intentos bloqueados.
$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':
// Confirmar que se trata del vocabulario "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() devuelve el nombre de máquina del vocabulario.
if ($term && $term->bundle() === 'tag') {
$event->setResponse(new TrustedRedirectResponse($redirect_to));
}
return;
default:
return;
}
}
}
La clase TagRedirectSubscriber
es un Event Subscriber personalizado para Drupal, diseñado para restringir el acceso a las páginas de administración de un vocabulario de taxonomía específico (en este caso, "tag") a los usuarios que no sean administradores. Aquí tienes un desglose de su estructura y los aspectos clave presentes en el código:
1. Propósito y caso de uso
- Objetivo: Evitar actualizaciones accidentales o no autorizadas en el vocabulario "tag" redirigiendo a los usuarios no administradores fuera de sus rutas administrativas.
- Beneficio: Proporciona una capa de control de acceso basada en la interfaz (UI/UX) para vocabularios críticos, garantizando la estabilidad de categorías fijas.
2. Estructura de la clase y dependencias
- La clase implementa
EventSubscriberInterface
, haciéndola compatible con el sistema de eventos de Symfony usado por Drupal. - Dependencias inyectadas mediante el constructor:
EntityTypeManagerInterface
: Incluida para posibles operaciones futuras con entidades. No es necesaria para la lógica actual, pero facilita la extensibilidad.AccountProxyInterface
: Utilizada para recuperar y comprobar de manera eficiente los roles del usuario actual.
3. Eventos suscritos
- La clase se suscribe al evento
KernelEvents::REQUEST
con una prioridad de 32.
Esta prioridad garantiza:- Que los parámetros de ruta ya están resueltos.
- Que el controlador de la ruta aún no se ha ejecutado, permitiendo al subscriber interceptar y detener la solicitud con una redirección si es necesario.
4. Lógica de redirección
- El método
onKernelRequest()
realiza todas las comprobaciones de acceso y la lógica de redirección:- Actúa solo sobre la solicitud principal: Evita la gestión duplicada en sub-solicitudes.
- Permite a los administradores: Si el usuario tiene el rol
administrator
, siempre se le permite el acceso. - Comprueba los nombres de las rutas: Solo considera ciertas rutas relacionadas con el vocabulario de taxonomía o sus términos.
- Redirige a los no administradores:
- Para rutas de vista general, añadir o listar (
entity.taxonomy_vocabulary.overview_form
,entity.taxonomy_vocabulary.overview_terms
,entity.taxonomy_term.add_form
), comprueba si el vocabulario estag
. - Para rutas de edición y eliminación (
entity.taxonomy_term.edit_form
,entity.taxonomy_term.delete_form
), comprueba si elbundle()
del término (nombre de máquina del vocabulario) estag
.
- Para rutas de vista general, añadir o listar (
- Utiliza una redirección confiable (TrustedRedirect): Si se cumplen las condiciones, el usuario es redirigido a una página segura de administración del vocabulario "tag".
- Extensibilidad: La lógica es fácil de ampliar para incluir otros vocabularios o roles ajustando las condiciones.
5. Seguridad y buenas prácticas
- Intercepción temprana: Al ejecutarse sobre el evento de la solicitud, el subscriber puede aplicar restricciones de acceso antes de que se procese o muestre cualquier dato sensible.
- Omisión basada en roles: Comprueba eficientemente los roles de usuario para no bloquear a administradores o constructores del sitio.
- Separación clara de responsabilidades: Mantiene la lógica de rutas, comprobaciones de usuario y redirecciones claramente separadas para facilitar el mantenimiento.
6. Posibles mejoras
- Ya que
EntityTypeManagerInterface
está inyectado, puedes agregar fácilmente comprobaciones basadas en entidades en el futuro (por ejemplo, permisos basados en propiedades específicas del término o contenido relacionado). - Puedes generalizar la clase para gestionar múltiples vocabularios o proporcionar redirecciones personalizables mediante configuración.
7. Conclusiones clave
- Este Event Subscriber demuestra un enfoque práctico para el control de acceso en Drupal, aprovechando la arquitectura orientada a eventos de Symfony para gestionar las solicitudes de forma temprana y eficiente.
- El enfoque es ideal para proteger vocabularios de taxonomía que solo deben ser gestionados por usuarios de confianza, reduciendo el riesgo de cambios accidentales.