Drupal is a registered trademark of Dries Buytaert
drupal 10.5.11 Update released for Drupal core (10.5.11)! drupal 11.3.11 Update released for Drupal core (11.3.11)! drupal 11.2.13 Update released for Drupal core (11.2.13)! drupal 10.6.10 Update released for Drupal core (10.6.10)! cms 2.1.2 Update released for Drupal core (2.1.2)! drupal 11.1.10 Update released for Drupal core (11.1.10)! drupal 10.5.10 Update released for Drupal core (10.5.10)! drupal 10.4.10 Update released for Drupal core (10.4.10)! drupal 11.2.12 Update released for Drupal core (11.2.12)! drupal 11.3.10 Update released for Drupal core (11.3.10)! drupal 10.6.9 Update released for Drupal core (10.6.9)! drupal 10.6.8 Update released for Drupal core (10.6.8)! drupal 11.3.9 Update released for Drupal core (11.3.9)! drupal 11.3.8 Update released for Drupal core (11.3.8)! drupal 11.3.7 Update released for Drupal core (11.3.7)! drupal 11.2.11 Update released for Drupal core (11.2.11)! drupal 10.6.7 Update released for Drupal core (10.6.7)! drupal 10.5.9 Update released for Drupal core (10.5.9)! cms 2.1.1 Update released for Drupal core (2.1.1)! drupal 11.3.6 Update released for Drupal core (11.3.6)!

Component Permission Lock lets you lock individual pieces of content behind site roles — and optionally group roles — without removing the layout slot from the page. Editors get a per-entity checkbox plus a list of roles right on the edit form. Visitors who don't hold one of the chosen roles see a locked placeholder (a lock icon and a short notice) in place of the card, paragraph, or block — the protected content itself is never sent to their browser. Everyone else sees the unlocked, fully interactive content.

This is the right module when you want to advertise that gated content exists — for example, a tile telling visitors a members-only resource is available — while keeping the content itself protected. The lock is a server-side access control: the protected markup is discarded before rendering, so it cannot be recovered via View Source or DevTools. A placeholder is rendered in its place so the visible layout doesn't collapse.

The optional companion submodule Component Permission Lock Group extends the same mechanism with group roles from the Group module.

Features

  • Custom field type permission_lock that stores: an on/off toggle, a list of site role IDs, and a list of group role IDs.
  • Custom field widget with progressive disclosure — the role selectors only appear when the editor ticks the "Set permission" checkbox. The Group role selector only appears when the optional submodule is enabled.
  • Automatic enforcement via hook_entity_view_alter — drop the field on any fieldable entity (node, paragraph, taxonomy term, block_content, custom entity types) and locking is applied at render time. No template changes required for entities rendered through Drupal core's normal entity view pipeline.
  • Twig integration for templates that bypass entity rendering — Single Directory Components and custom Twig that pull field values directly can call permission_lock_is_locked(entity) and conditionally render the locked variant inline.
  • Configuration permission use component permission lock widget — hide the lock controls from editors who shouldn't manage access while preserving their saved values silently.
  • Service decorator architecture — group-role support is added by a Symfony service decorator from the optional submodule. Disabling the submodule reverts to site-role-only evaluation with zero data loss; stored group role IDs round-trip if the submodule is re-enabled.
  • Themeable locked wrapper with BEM-style CSS classes that themes can override.
  • Kernel tests for the access checker.

Typical use cases:

  • Marketing landing pages with cards pointing to gated documents — every visitor sees a locked placeholder so they know the resource exists, but only authenticated members receive and can open the actual content.
  • Multi-tenant sites built on the Group module where members see additional paragraphs in the same node without rebuilding the page per role.
  • Editorial draft-hiding — lock a card with no roles selected, and only user 1 (the editorial super-admin) sees it.
  • Personalising entry points without altering site information architecture.

Post-Installation

After enabling the module:

  1. Grant the configuration permission. Go to /admin/people/permissions and grant Configure component permission lock to the roles that should be allowed to set the lock on content (typically content editors and administrators). Without this permission the widget is hidden from the editor; users still don't lose any previously saved values.
  2. Attach the field to a bundle. Go to Structure → [Content / Paragraph / Taxonomy / etc.] types → [your bundle] → Manage fields. Click Add field, choose Component permission lock from the General group, and give it a machine name (e.g. field_permission_lock). The field storage is per entity type, so attaching the same field to additional bundles of the same entity type only adds an instance — no extra database migration.
  3. Place the widget on the form display. Open Manage form display for the bundle. The widget defaults to Permission lock. Drag it where editors should see it.
  4. Configure the lock on a piece of content. Edit any entity that now carries the field. You'll see a collapsible Permission lock section. Tick Set permission, choose the roles that should bypass the lock, and save.

That's it for nodes, paragraphs, and other entities rendered through Drupal core's normal entity view pipeline — they're automatically wrapped in the locked variant when the current user fails the check.

For Single Directory Components and custom templates that pull field values directly (e.g. child.field_title.value) rather than rendering the child entity, you'll need to call the provided Twig function in your template:

{{ attach_library('component_permission_lock/lock') }}

{% if permission_lock_is_locked(child) %}
  {# Do NOT render the protected markup here — not even hidden with CSS.    #}
  {# Anything emitted reaches the client regardless of styling. Render only #}
  {# the generic placeholder.                                              #}
  <div class="component-permission-lock" role="group" aria-label="{{ 'Locked content'|t }}">
    <div class="component-permission-lock__inner">
      <p class="component-permission-lock__message">{{ 'This content is locked.'|t }}</p>
    </div>
    <button type="button" class="component-permission-lock__icon" disabled
            aria-label="{{ 'Locked'|t }}">
      {# lock SVG #}
    </button>
  </div>
{% else %}
  <a href="{{ url }}">{# … card markup … #}</a>
{% endif %}

Access semantics: a user sees the unlocked output when any of the following is true — the lock checkbox is unchecked, they are user 1 (editorial recovery), they hold any of the configured site roles, or (when the group submodule is enabled) they hold any of the configured group roles in any group they belong to. Otherwise they see the locked placeholder.

Programmatic access checks. Inject Drupal\component_permission_lock\Service\PermissionLockCheckerInterface and call isLocked() on the field item list. The interface resolves to the right implementation automatically based on whether the submodule is enabled — your code stays decoupled.

Security model

The lock is a server-side access control, not a visual effect. When a user fails the check, the protected entity build is discarded before rendering, so the markup never reaches their browser — it cannot be recovered via View Source, DevTools, disabling CSS, or curl. Only a generic placeholder is sent.

Two behaviours to understand before relying on it:

  • User 1 always bypasses the lock (editorial recovery) and therefore sees every locked entity regardless of role configuration.
  • The Group submodule matches a role held in any group the user belongs to, not the specific group the content belongs to — see Limitations to scope it.

Scope: the lock is enforced on the view render path (hook_entity_view_alter, and the Twig function where you call it). It does not alter entity query access, search indexing, or REST/JSON:API output — those paths can still expose the field data. If the content is sensitive on those channels too, pair this module with core/contrib access control (entity access, node grants, or the Group module).

Additional Requirements

The main module has no dependencies beyond Drupal core 10.3+ or 11. It uses only the Field and User subsystems.

The optional submodule Component Permission Lock Group requires:

  • Group — required for the optional Component Permission Lock Group submodule, which adds group-role evaluation. Recommended if your site already gates content by group membership.
  • Paragraphs — the most common entity type to attach this field to. The module works equally well with nodes, terms, blocks, and custom entities.
  • Single Directory Components (SDC) — if your theme uses SDCs that render fields manually, this is where the Twig integration shines.
  • Field UI Extras — pairs nicely for editors who want a tidier field configuration screen when many fields are attached.

Similar projects

Component Permission Lock occupies a niche between "hide the entity entirely" modules and "no access control at all". A few points of comparison:

  • Field Permissions hides individual fields from view based on role; the surrounding entity still renders. Use it when you want to hide a phone number but keep the rest of the user profile. Use Component Permission Lock when you want to lock the whole rendered entity behind roles instead.
  • Permissions by Term grants or denies access to entities tagged with specific taxonomy terms. It's term-driven and node-focused. Component Permission Lock is per-entity and role-driven: it suppresses the protected output server-side and renders a placeholder in its slot, so the page layout doesn't collapse.
  • Content Access manages access at the node/bundle level, blocking the entity from being viewed at all. Component Permission Lock renders a styled placeholder instead — the user still sees that something exists, without receiving its content.
  • Group with group permissions controls who can see a group's content. Component Permission Lock + the Group submodule sits on top of that: it lets you lock individual entities (e.g. one paragraph inside a node) based on group roles, regardless of whether the host entity itself belongs to a group.

Supporting this Module

If you find this module useful, please file issues with reproducible steps in the project's issue queue. Patches and merge requests are welcome — especially for additional Kernel test coverage, plugin-based lock evaluators, and group-context-aware checking.

If your organisation depends on this module commercially and wants to sponsor a feature or a release, please open an issue tagged Sponsored development describing the requirement.

Community Documentation

  • README.md in the module repository — full integration guide, including the Twig function and the programmatic API.
  • Kernel tests — the access checker test file doubles as the most accurate specification of the lock semantics.

Architecture notes for developers

The module is deliberately small and built around three seams that contributed modules can extend:

  • Field type (permission_lock) — stores the configuration. Three columns: enabled, site_roles, group_roles.
  • Checker service (PermissionLockCheckerInterface) — single seam for access decisions. The default implementation evaluates site roles. The group submodule registers a service decorator that adds group-role evaluation. Third-party modules can add their own decorators (e.g. for ECA, OpenID Connect group claims, Commerce roles) without forking the main module.
  • Rendering integration — one hook_entity_view_alter for normal entity rendering, plus one Twig function for templates that bypass entity rendering.

This means contributing more access dimensions later is additive — a new submodule, a new decorator, no schema changes.

Limitations

  • Only the first permission_lock field per entity is evaluated. Multiple lock fields on the same entity are unsupported.
  • SDCs and templates that render fields manually require a per-template edit to call the Twig function. Templates that render child entities normally (via entity_view) do not need any changes.
  • The group submodule matches "user holds the role in any group". If you need it scoped to a specific group context (e.g. only the group the page belongs to), override GroupPermissionLockChecker with a service that consumes a group context.

Roadmap

  • Plugin-based lock evaluators so additional integrations can be added without service decoration.
  • Group-context awareness — match against group roles in the current page's group rather than any group.
  • Functional Browser tests covering the widget UI and the rendered locked variant.
  • Optional submodule for OpenID Connect / SSO group claims.

Activity

Total releases
1
First release
May 2026
Latest release
9 hours ago
Release cadence
Stability
100% stable

Releases

Version Type Release date
1.0.1 Stable May 30, 2026