logo

パレット - カラフルに🎨

Palette — ビジュアルページビルダー、デザインの専門知識は不要です。

ライブデモ パレットをダウンロード

Scroll

イベントサブスクライバーを使ってタクソノミー用語ボキャブラリへのアクセスを制限する

11/06/2025, by Ivan

サイトに固定された恒久的なカテゴリが必要で、誤って更新されては困る場合があります。このような場合には、イベントサブスクライバーを使ったカスタムコードを利用できます。 

カスタムモジュールに新しいイベントサブスクライバークラスを追加しましょう。

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 }

そして、イベントサブスクライバーを 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;

/**
 * Redirects non-administrators from Tag vocabulary admin pages.
 *
 * A Request-level subscriber runs early, allowing us to short-circuit the
 * request and return a redirect response before the matched controller
 * executes.
 */
class TagRedirectSubscriber implements EventSubscriberInterface {

  /**
   * The entity-type manager service.
   *
   * Kept as an example dependency; not strictly required for the current
   * logic but useful if future enhancements require entity loading.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected EntityTypeManagerInterface $entityTypeManager;

  /**
   * The current user proxy service.
   *
   * Used for quick role checks in order to bypass the redirect for
   * administrators.
   *
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected AccountProxyInterface $currentUser;

  /**
   * Constructs the subscriber.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity-type manager.
   * @param \Drupal\Core\Session\AccountProxyInterface $current_user
   *   The user currently making the request.
   */
  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 {
    // Priority 32 ensures route parameters are available but the controller
    // has not yet executed.
    return [
      KernelEvents::REQUEST => ['onKernelRequest', 32],
    ];
  }

  /**
   * Performs the redirect when a non-administrator accesses Tag admin routes.
   *
   * @param \Symfony\Component\HttpKernel\Event\RequestEvent $event
   *   The kernel event carrying the request.
   */
  public function onKernelRequest(RequestEvent $event): void {
    // Only act on the main (master) request.
    if (!$event->isMainRequest()) {
      return;
    }

    // Allow administrators through without redirection.
    if ($this->currentUser->hasRole('administrator')) {
      return;
    }

    $request = $event->getRequest();
    $route_name = $request->attributes->get('_route');

    // Destination for all blocked attempts.
    $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':
        // Confirm we are dealing with the "tag" vocabulary.
        $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() returns the vocabulary machine name.
        if ($term && $term->bundle() === 'tag') {
          $event->setResponse(new TrustedRedirectResponse($redirect_to));
        }
        return;

      default:
        return;
    }
  }

}

TagRedirectSubscriberクラスは、Drupal用のカスタムイベントサブスクライバーで、特定のタクソノミーボキャブラリ(ここでは「tag」)の管理ページへのアクセスを管理者以外のユーザーに制限するよう設計されています。以下は、その構造とコード内に見られる重要な価値ある点の内訳です:

1. 目的と使用ケース

  • 目標:管理者以外のユーザーを「tag」ボキャブラリの管理ルートからリダイレクトして、「tag」ボキャブラリへの誤ったまたは許可されていない更新を防ぐこと。
  • 利点:重要なタクソノミーボキャブラリにUI/UXベースのアクセス制御の層を提供し、固定カテゴリの安定性を強化します。

2. クラス構造と依存関係

  • クラスはEventSubscriberInterfaceを実装しており、Drupalが使用するSymfonyのイベントシステムと互換性があります。
  • コンストラクタ経由で注入される依存関係:
    • EntityTypeManagerInterface:将来のエンティティ操作に備えて含められます。現在のロジックには必須ではありませんが、簡単に拡張できます。
    • AccountProxyInterface:現在のユーザーのロールを効率的に取得・確認するために使用されます。
    依存性注入により、サブスクライバーがテスト可能で再利用可能になり、Drupalのサービスコンテナのベストプラクティスに準拠します。

3. サブスクライブされるイベント

  • クラスは優先度32KernelEvents::REQUESTイベントにサブスクライブします。
    この優先度により、以下が保証されます:
    • ルートパラメータが利用可能(ルーティングが解決済み)であること。
    • ルートのコントローラがまだ実行されておらず、必要に応じてリダイレクトでリクエストをインターセプトしてショートサーキットできること。

4. リダイレクトのロジック

  • onKernelRequest()メソッドが、すべてのアクセスチェックとリダイレクトのロジックを実行します:
    • メインリクエストのみで動作:サブリクエストでの重複処理を回避します。
    • 管理者を許可:ユーザーがadministratorロールを持っている場合は、常にアクセスが許可されます。
    • ルート名をチェック:タクソノミーボキャブラリまたはその用語に関連する特定のルートのみが考慮されます。
    • 管理者以外をリダイレクト:
      • 概要・追加・一覧ルート(entity.taxonomy_vocabulary.overview_formentity.taxonomy_vocabulary.overview_termsentity.taxonomy_term.add_form)では、ボキャブラリがtagであるかどうかをチェックします。
      • 編集・削除ルート(entity.taxonomy_term.edit_formentity.taxonomy_term.delete_form)では、用語のbundle()(ボキャブラリのマシン名)がtagであるかどうかをチェックします。
    • 信頼済みリダイレクトを使用:条件が一致した場合、ユーザーは「tag」ボキャブラリの安全な管理概要ページにリダイレクトされます。
  • 拡張性:ロジックは条件を調整することで、追加のボキャブラリやロールに簡単に拡張できます。

5. セキュリティとベストプラクティス

  • 早期のインターセプト:リクエストイベントで実行することで、機密データが処理・表示される前にアクセスを強制できます。
  • ロールベースのバイパス:サイトビルダーや管理者をブロックしないよう、ユーザーロールを効率的にチェックします。
  • 関心事の明確な分離:ルーティングロジック、ユーザーチェック、リダイレクトを保守性のため明確に分離します。

6. 考えられる拡張

  • EntityTypeManagerInterfaceが注入されているため、将来エンティティベースのチェックを簡単に追加できます(例:特定の用語プロパティや関連コンテンツに基づく権限)。
  • クラスを一般化して、複数のボキャブラリを処理したり、設定によってカスタマイズ可能なリダイレクトを提供したりできます。

7. 重要なポイント

  • このイベントサブスクライバーは、Symfonyのイベント駆動アーキテクチャを活用したDrupalでの実用的なアクセス制御のアプローチを示しており、早期かつ効率的なリクエスト処理を実現します。
  • このアプローチは、信頼できるユーザーのみが管理すべきタクソノミーボキャブラリの保護に最適で、誤った変更のリスクを軽減します。