-1

What is the best way to grant a user permission dynamically for a single page load? In other words, I want Drupal to behave as if the user has a certain permission when it loads the page, depending on conditions. But I don't want to save the permission to the user's account.

It's fine if Drupal doesn't respect the temporary permission after the page loads. The user can interact with the page as if they no longer have that permission. But I want the page to load as it would if the user had the dynamically assigned permission.

Use case:

The site uses Workbench Access to control access in different content sections, and the core Content Moderation module to provide workflow transitions and associated permissions.

The project requires users to have access to different workflow transitions per content section. However, Content Moderation does not provide workflow transition permissions per role per content section -- it does not directly integrate with the contributed Workbench Access module, in other words.

Thus, out of the box, it is not possible to give a user access to workflow transition X for one content section, and transition Y for a different content section. So far, the only solutions I have been able to think of are, arguably, hacks. For example, we can check for a section-specific role in hook_form_alter and add/remove transition options from the state dropdown (Draft, Needs Review, Published, etc) accordingly. Another possible solution, arguably also a hack, is to modify the user's permissions conditionally when the node edit form loads, effectively presenting the state dropdown as if the user has (or does not have) permission for the relevant workflow transition(s) for that content section. In this way, we could permit Jane Smith to publish content to one section (defined in Workbench Access), while allowing Jane Smith to submit drafts, but not publish anything, in a different content section.

Update: The more I think of it, this use case will likely be very difficult to support. It would not suffice to alter the user's role/permission when the node edit form loads. The project requires that the user be able to re-assign content from one section to another, which would in turn require that the state options be altered each time the user changes the value of the taxonomy term field responsible for assigning the content to a section. It is, strictly speaking, possible to achieve, but would require AJAX. It seems unlikely any solution exists that is reasonably clean and maintainable.

3
  • 6
    This doesn't really make sense in a Drupal sense. Drupal will have access rules for any given route, and then individual components will have access permissions on the components, based on the permissions of the viewing user. There isn't a single permission for a page load. You've clearly got an idea of how you are trying to solve some problem, but haven't told us your use case, only how you are trying to solve it. If you tell us your use case, we will be more able to provide support.
    – Jaypan
    Jun 2, 2023 at 23:18
  • 2
    Using a custom access checker, you can programmatically give access to the route showing the page to any account, without changing the given permissions. Those accounts could not be able to use any link given in that page, for example. As @Jaypan said, the question should make clear what you are trying to achieve.
    – apaderno
    Jun 3, 2023 at 8:18
  • @Jaypan I added the use case.
    – arnoldbird
    Jun 5, 2023 at 12:28

1 Answer 1

3

Grant a permission by adding a role temporarily when the account is set for the page load, which triggers the AccountEvents::SET_USER event:

/mymodule/src/EventSubscriber/SetAccountSubscriber.php

<?php

namespace Drupal\mymodule\EventSubscriber;

use Drupal\Core\Session\AccountEvents;
use Drupal\Core\Session\AccountSetEvent;
use Drupal\Core\Session\UserSession;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * mymodule event subscriber.
 */
class SetAccountSubscriber implements EventSubscriberInterface {

  /**
   * Account set event handler.
   *
   * @param \Drupal\Core\Session\AccountSetEvent $event
   *   Account set event.
   */
  public function onAccountSet(AccountSetEvent $event) {
    $account = $event->getAccount();
    if ($account->isAuthenticated()) {
      $roles = $account->getRoles();
      if (!in_array('administrator', $roles)) {
        $roles[] = 'administrator';
        $account = new UserSession([
          'uid' => $account->id(),
          'access' => $account->getLastAccessedTime(),
          'roles' => $roles,
          'name' => $account->getAccountName(),
          'preferred_langcode' => $account->getPreferredLangcode(),
          'preferred_admin_langcode' => $account->getPreferredAdminLangcode(),
          'mail' => $account->getEmail(),
          'time_zone' => $account->getTimeZone(),
        ]);
        \Drupal::currentUser()->setAccount($account);
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    return [
      AccountEvents::SET_USER => ['onAccountSet'],
    ];
  }

}

This code is only for demonstration purposes, it adds admin privilege to all logged-in users. Also, you have to check for the added role because this event is triggered again when setting the new account, to avoid an infinite loop.

You might want to create a specific role containing the permission you wish to grant for the page load.

Register the service:

/mymodule/mymodule.services.yml

services:
  mymodule.set_account_subscriber:
    class: Drupal\mymodule\EventSubscriber\SetAccountSubscriber
    tags:
      - { name: event_subscriber } 

"Use Case" & "Update"

In the use case added to the question things are getting more dynamic and I think are too complicated to be answered in one question. If you want to continue on this path you can try to define a custom account class implementing AccountInterface which allows you to change the account roles dynamically or even make the account object more intelligent to calculate permissions depending on states you set during the request. Of course, you can also try to tackle the problem from the other side, with access code you add to the components involved, as described in the comments.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.