Any way to apply ECA Access logic to query tags, as for Views?

Created on 15 January 2025, about 2 months ago

Problem/Motivation

Once we have the complex logic set up (see 💬 ECA Access: Assigning [entity] to token of a different name Active ) we certainly want to re-use that for determining access for views results as well.

ECA's Access Entity runs on hook access.

Is there a way to set it to also run for "node_access" tagged queries?

https://api.drupal.org/api/drupal/core%21modules%21node%21node.module/gr...

Steps to reproduce

  1. Enable Node View Permissions module, leaving view access to Content Type A off.
  2. Give access to specific nodes in Content Type A with ECA (example recipe in 💬 ECA Access: Assigning [entity] to token of a different name Active )
  3. Rejoice at how well it works.
  4. Try to use a view that lists those same specific nodes of Content Type A, and stop rejoicing because the view lists nothing at all.

Proposed resolution

Add an option to the existing Determining entity access event, or add a new event, that is triggered for "node_access" tagged queries such as Views runs, with access actions that result in altering these queries.

Remaining tasks

User interface changes

API changes

Data model changes

💬 Support request
Status

Active

Version

2.1

Component

Code

Created by

🇺🇸United States mlncn Minneapolis, MN, USA

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @mlncn
  • 🇺🇸United States mlncn Minneapolis, MN, USA

    (anything based on this approach would generally be sub-optimal performance-wise, like a giant IN statement rather than a beautifully crafted set of joins, but it should still work…)

  • 🇩🇪Germany jurgenhaas Gottmadingen

    What's required to alter the access on views results is an event or a hook that's being dispatched by the views module so that other modules can alter the results. If there isn't an API for that, ECA won't be able to get involved. I'm not sure from top of my head if anything like that already exists? How would you alter the access on views results when you would do that with custom code in PHP?

  • 🇺🇸United States mlncn Minneapolis, MN, USA

    I had been thinking of doing this in https://api.drupal.org/api/drupal/core%21modules%21node%21node.module/fu...

    But the recommended/correct way for intervening for content access that works for queries is via giving grants to users and defining the

    https://api.drupal.org/api/drupal/core%21modules%21node%21node.api.php/f...

    https://api.drupal.org/api/drupal/core%21modules%21node%21node.api.php/f...

    And on further consideration those hooks are what we would want to make available to ECA.

  • 🇺🇸United States mlncn Minneapolis, MN, USA

    A fairly simple example, tested and working.

    ECA could definitely do this, it would be a completely separate capability from ECA Access (which uses hook_node_access), so i withdraw/redirect the initial request; Drupal basically has two different systems, and i think ECA could plug into this one as well. Maybe an eca_node_access module.

    /**
     * @file Access hooks for advocates.
     */
    
    use Drupal\Core\Session\AccountInterface;
    use Drupal\node\NodeInterface;
    
    /**
     * Implements hook_node_grants().
     */
    function hw_advocate_node_grants(AccountInterface $account, $operations) {
      $grants = [];
      if ($account->hasPermission('advocate for clients')) {
        $household_nodes =  \Drupal::entityTypeManager()->getStorage('node')->loadByProperties(
          [
            'type' => 'household',
            'field_advocate' => $account->id(),
          ]
        );
        foreach ($household_nodes as $node) {
          $grants['hw_advocate'][] = $node->id();
        }
      }
      return $grants;
    }
    
    /**
     * Implements hook_node_access_records().
     */
    function hw_advocate_node_access_records(NodeInterface $node) {
      $grants = [];
      if ($node->bundle() === 'household') {
        $grants[] = [
          'realm' => 'hw_advocate',
          'gid' => $node->id(),
          'grant_view' => 1, // TRUE, but docs say 1
          'grant_update' => 1,
          'grant_delete' => 0,
        ];
      }
      return $grants;
    }
    
  • 🇩🇪Germany jurgenhaas Gottmadingen

    That sounds like a good idea to add support for hook_node_grants and hook_node_access_records, maybe also for hook_node_grants_alter and hook_node_access_records_alter.

    It's just unfortunate, that this is only available for nodes, and not for entities in general.

    However, I'd say this functionality belongs to the eca_access sub-module.

  • 🇺🇸United States mlncn Minneapolis, MN, USA

    mlncn changed the visibility of the branch 3500062-any-way-to to hidden.

  • 🇺🇸United States mlncn Minneapolis, MN, USA
  • 🇺🇸United States mlncn Minneapolis, MN, USA
  • Pipeline finished with Failed
    about 1 month ago
    Total: 2152s
    #403941
  • 🇺🇸United States mlncn Minneapolis, MN, USA

    Sorry, pushed what i've got as a start, just trying to set the stage for what is needed, but it's erroring immediately:

    There must be only one distinct plugin definition for each system event. Affected system event : eca_access.entity

    I think i'm really struggling with trying to do it in the ECA Access module when the ECA way of doing different events in one module seems to involve a ton of conditional logic that i'm trying to avoid.

  • 🇩🇪Germany jurgenhaas Gottmadingen

    This looks indeed pretty complicated and when I read through all the grant API documentation, things suddenly started to get familiar. I thought, haven't we done that somewhere already? And yes, we did.

    There is the content_access module which implements the node grant hooks already. And with the eca_content_access module, we're allowing ECA to either grant or revoke access.

    The only limitation is that content_access doesn't support D11 yet, but the patch is available and RTBC'd, so let's hope it's landing soon.

  • 🇺🇸United States mlncn Minneapolis, MN, USA

    Finally back to this, and i don't think ECA Content Access is what i am looking for.

    First, there should not be a need for the Content Access contrib module.

    Second, and most important, it lets the access for a role to be changed from the configured Content Access default for an individual node, but it does not allow varying the access per user account.

    Third, to do things in the way node access works best, we really should be able to set grants on the account and grants on a node. Content access is using node_grants but not exposing the full flexibility. (Also, though i don't know if it matters, it is not defining a realm at all.)

    Here is the underlying method eca_content_access uses, from NodeAccessControlHandler:

          * @param \Drupal\node\NodeInterface $node
         *   The node whose grants are being written.
         * @param array $grants
         *   A list of grants to write. Each grant is an array that must contain the
         *   following keys: realm, gid, grant_view, grant_update, grant_delete.
         *   The realm is specified by a particular module; the gid is as well, and
         *   is a module-defined id to define grant privileges. each grant_* field
         *   is a boolean value.
         * @param string $realm
         *   (optional) If provided, read/write grants for that realm only. Defaults
         *   to NULL.
         * @param bool $delete
         *   (optional) If false, does not delete records. This is only for
         *   optimization purposes, and assumes the caller has already performed a
         *   mass delete of some form. Defaults to TRUE.
         */
        public function write(NodeInterface $node, array $grants, $realm = NULL, $delete = TRUE);
    

    Used here:

        if ($this->updateSettings($bundleSettings, $this->configuration['operation'], $this->configuration['role'])) {
          $this->database->merge('content_access')
            ->keys(['nid'], [$entity->id()])
            ->fields(['settings' => json_encode($bundleSettings)])
            ->execute();
          /**
           * @var \Drupal\node\NodeAccessControlHandler $controllHandler
           */
          $controllHandler = $this->entityTypeManager->getAccessControlHandler('node');
          // Apply new settings.
          $grants = $controllHandler->acquireGrants($entity);
          $this->grantStorage->write($entity, $grants);
    
  • Merge request !470Draft: Resolve #3500062 "Node access submodule" → (Open) created by mlncn
  • Pipeline finished with Failed
    11 days ago
    Total: 469s
    #428651
  • 🇺🇸United States mlncn Minneapolis, MN, USA

    Active branch does everything… except token replacement for the actions! Something obvious i am sure but way out of time to work on this for a while.

  • Pipeline finished with Failed
    11 days ago
    Total: 471s
    #428767
  • 🇩🇪Germany jurgenhaas Gottmadingen

    Left a few comments in one of the action plugins with regard to handling config values and tokens. Before a full review please ensure that tests are green. Then please set the issue status to "Needs review" so that we get notified accordingly.

  • Pipeline finished with Failed
    5 days ago
    Total: 654s
    #433644
  • Pipeline finished with Canceled
    5 days ago
    Total: 151s
    #433701
  • Pipeline finished with Canceled
    5 days ago
    Total: 143s
    #433703
  • Pipeline finished with Canceled
    5 days ago
    Total: 75s
    #433705
  • Pipeline finished with Canceled
    5 days ago
    Total: 76s
    #433706
  • Pipeline finished with Failed
    5 days ago
    Total: 469s
    #433707
  • Pipeline finished with Failed
    5 days ago
    Total: 468s
    #433749
  • Pipeline finished with Failed
    5 days ago
    Total: 482s
    #433759
  • 🇺🇸United States mlncn Minneapolis, MN, USA
     17 | ERROR | [ ] Parameter $account is not described in comment
        |       |     (Drupal.Commenting.FunctionComment.ParamMissingDefinition)
     20 | ERROR | [ ] Doc comment for parameter $account; does not match actual
        |       |     variable name $account
        |       |     (Drupal.Commenting.FunctionComment.ParamNameNoMatch)
    
      /**
       * Triggers gathering grants.
       *
       * @param \Drupal\Core\Session\AccountInterface $account;
       *   The user account.
       */
      public function grants(AccountInterface $account) {
    

    Is PHPCS drunk?

  • Pipeline finished with Failed
    5 days ago
    Total: 479s
    #433769
  • Pipeline finished with Failed
    5 days ago
    Total: 570s
    #433939
  • Pipeline finished with Failed
    5 days ago
    Total: 730s
    #434074
  • Pipeline finished with Failed
    5 days ago
    Total: 506s
    #434093
  • Pipeline finished with Failed
    5 days ago
    Total: 694s
    #434216
  • Pipeline finished with Failed
    5 days ago
    Total: 584s
    #434263
  • Pipeline finished with Failed
    3 days ago
    Total: 704s
    #436316
  • Pipeline finished with Failed
    3 days ago
    Total: 592s
    #436336
  • Pipeline finished with Failed
    3 days ago
    Total: 479s
    #436362
Production build 0.71.5 2024