The change in https://www.drupal.org/node/2821899 is excellent, it lets people see the content if you're checking for a "View" permission on content.

However, when we're checking if something could be updated or deleted, it's also useful to add in whether it is unpublished or not. The use case is if we had a View filter looking for the "update" op, it would not grant the ability for the View to display the entities that could be updated/deleted.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Anonymous’s picture

vilepickle created an issue. See original summary.

Anonymous’s picture

kristiaanvandeneynde’s picture

The use case is if we had a View filter looking for the "update" op, it would not grant the ability for the View to display the entities that could be updated/deleted.

This sounds like a bug. If you are trying to build a view that lists what you can edit, it should use the 'update' operation for node grants instead of 'view'.

The reason we only care about published vs unpublished when viewing nodes is because that's the only thing we have a permission for. We do not have something like "Edit own unpublished nodes".

Anonymous’s picture

Yeah I think a better solution would be to have a permission for "edit own unpublished nodes" instead of just the view one for something like this.

Here's our views plugin to show what we're doing. It is looking at the update permission, but doesn't work as a Views filter (like for admin/content) unless the patch in #2 is applied currently.

<?php

namespace Drupal\hp_views\Plugin\views\filter;

use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\filter\FilterPluginBase;

/**
 * Filter by node_access records.
 *
 * @ingroup views_filter_handlers
 *
 * @ViewsFilter("hp_views_gnode_update_access")
 */
class GroupNodeUpdateAccess extends FilterPluginBase {

  public function adminSummary() { }
  protected function operatorForm(&$form, FormStateInterface $form_state) { }
  public function canExpose() {
    return FALSE;
  }

  /**
   * See _node_access_where_sql() for a non-views query based implementation.
   */
  public function query() {
    $account = $this->view->getUser();

    // Get the list of grants from the gnode module.
    $grants_list = gnode_node_grants($account, 'update');

    if(empty($grants_list)) {
      return;
    }

    if (!$account->hasPermission('bypass node access')) {
      $table = $this->ensureMyTable();
      $grants = db_or();

      foreach ($grants_list as $realm => $gids) {
        foreach ($gids as $gid) {
          $grants->condition(db_and()
            ->condition($table . '.gid', $gid)
            ->condition($table . '.realm', $realm)
          );
        }
      }

      $this->query->addWhere('AND', $grants);
      $this->query->addWhere('AND', $table . '.grant_update', 1, '>=');
      $this->query->addTag('node_access');

     // print_r($this->query->query()->__toString());
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheContexts() {
    $contexts = parent::getCacheContexts();

    $contexts[] = 'user.node_grants:update';

    return $contexts;
  }

}
kristiaanvandeneynde’s picture

You forgot to tell the query that you are looking for nodes you can update:

$this->query->addTag('node_access');
$this->query->addMetaData('op', 'update');

I don't think we should ever have an "edit own unpublished nodes" permission. Core doesn't have it either. I was just mentioning that we don't have such a permission, not that we should have one :)

Anonymous’s picture

Thanks for pointing that out. So I was doing some digging on this issue today. In our app, we've discovered that if a separate author in the group creates an unpublished node, but then a different user tries to view it with the filter above, it does not show up in the list of content.

The root cause doesn't seem to be the patch I presented originally since that function checks the node_access after the record has been entered in the first place.

What makes sense is down below in the gnode.module file:

    // Add the non-author record for viewing nodes.
    $prefix = $node->isPublished() ? 'gnode' : 'gnode_unpublished';
    $records[] = ['gid' => $gid, 'realm' => "$prefix:$type"] + $base;

It should probably be:

    // Add the non-author record for viewing nodes.
    if(!$node->isPublished()) {
      $records[] = ['gid' => $gid, 'realm' => "gnode_unpublished:$type"] + $base;
    }
    $records[] = ['gid' => $gid, 'realm' => "gnode:$type"] + $base;

I was seeing only two records in the db for the gid:
gnode_unpublished:news
gnode_author:1:news

Where it should have "gnode:news" as well to be able to show the unpublished node.

This will also require an access rebuild I believe...

Not sure if this approach is best but feedback welcome.

Status: Needs review » Needs work
Anonymous’s picture

Btw, that metaData line actually causes a fatal:

Fatal error: Call to undefined method Drupal\views\Plugin\views\query\Sql::addMetaData()

Anonymous’s picture

Anonymous’s picture

I believe this is how node access is rebuilt on updb.

kristiaanvandeneynde’s picture

Hmm I'd have to look into this but you may be right. Although you are setting an extra access record where we shouldn't do so. I'd rather hand out an extra grant to users checking for update/delete:

          // If you can act on any node, there's no need for the author grant.
          if ($group_role->hasPermission("$op any $plugin_id entity")) {
            $grants_ao["gnode:$node_type_id"] = $gids;
          }

// becomes...

          // If you can act on any node, there's no need for the author grant.
          if ($group_role->hasPermission("$op any $plugin_id entity")) {
            $grants_ao["gnode:$node_type_id"] = $gids;
            $grants_ao["gnode_unpublished:$node_type_id"] = $gids;
          }

Keep in mind this needs to happen twice ($grants_m as well).

kristiaanvandeneynde’s picture

Also your patch would grant view access to unpublished nodes to anyone with the "view published nodes" permission. So in order to fix that, we'd also need to change this elseif to an if:

           elseif ($group_role->hasPermission("view unpublished $plugin_id entity")) {
mikemccaffrey’s picture

Here is an updated patch (but without testing or rebuild on update) based on @kristiaanvandeneynde's suggestion in #11.

mikemccaffrey’s picture