I have created a view of Orders -> relationship to Line Items -> relationship to Products.

When I look at the view as user 1, I can see all the orders on the system.

However, when I look at the view as user 3, who has permission to view only one particular type of product, and only if that user created the particular product (which this user has permission to do), the view is empty.

If I take out the Product relationship, user 3 can see all the orders.

When I grant this user permission "view own products of any type", user 3 can see all the orders.

I am also able to create a view of Products (leaving out the orders and line items) and user 3 can use that view to see Products (without the extra permission "view own products of any type").

So it looks like the permission checking in the Product relationship is too strict. I haven't delved into the code to figure out where it is happening, sorry.... For this site, it is not a problem for me to grant the user a "view any product you created" type of permission, since they only have permission to create one product type anyway, so I am not too motivated to find where this bug is and fix it... but it was still an annoying bug to come across since the dashboard I had built did not work when I logged in as user 3 to test.

Also... note that this might be a duplicate of
#1276450: Views results empty for unprivileged user when using Relationship: Content: Referenced Product
but since that issue has over 100 comments and no summary, I wasn't sure if it was the same or not.

Comments

jhodgdon’s picture

As a follow-up, I just created another similar view for admins. User 2 is an admin, with permission to view any products of certain types.

Similar to in the original issue report, this user was unable to see the orders for other users and their products until I granted this user "view all products of any type" permission, even though the user *did* have permission to view the products that were actually being displayed, through a more specific "view any (particular product type) product" permission.

rszrama’s picture

Status: Active » Closed (duplicate)

Yeah, it's basically the same issue as reported in that thread, and I do believe there's a secondary thread that's related but was attempting to sidestep the issue by downgrading the permission. We haven't done it yet for fear of unintended side effects, tbh. I think for most people, the permission would be harmless to make generally required, but I'd hate to change behavior that some site depends on to restrict access to products. It's a complicated situation since what we really want is to provide view access to products that are referenced by nodes the customer has access to view. But perhaps we could solve this particular issue by providing view access to products that are referenced by line items on orders a user owns. Not sure if I've proposed that in the related issue or not, but it should technically be possible and solve the majority use case (joining to products from Views; happens in front-end scenarios w/ product catalog Views as well).

Any other ideas? I'm generally familiar w/ how Views is running access checks on entities whose fields get pulled into a View, but perhaps there's a third (or fourth, etc.) way to go about this that would protect existing users while getting rid of this oddball behavior that requires either granting an "admin" permission to untrusted users or disabling rewriting on a View.

jhodgdon’s picture

Status: Closed (duplicate) » Active

I am reopening this issue temporarily since you asked for feedback and that other one has about 100 irrelevant comments on it and no summary, and searching did not find the possible other issue that you mentioned.

So here is some more information about the use case behind this issue.

I'm helping to build build a site with several different roles, where certain users create their own unique products, and then they create orders on the site in order to transact business related to those products. There are no nodes involved. Here is the setup:
- Role A can create products of type A (representing a particular instance of a training class of type A) and view/purchase only their own products. Once a product is purchased, its status is set to 0 so it cannot be purchased again. (The orders are for ordering student certificates.)
- Role B can create products of type B and view/purchase only their own.
- Role C is an admin for products of A/B, but not for other types of products. They should be able to view any products of type A and B regardless of who created them.
- Workflow: Roles A and B first create their custom products several weeks before they teach a class. Then they come back to the site after the class is taught, fill in some more details, and place an order for certificates for the students. Role C reviews the orders, prints the certificates, and sends them to the trainers.
- So roles A and B need to be able to see lists of the products they've created but not yet purchased, and also their orders; the order view needs some information from the product being ordered (the class type and the class dates) in order to be useful.
- Role C also needs to be able to list the products created by all users that haven't yet been ordered, and list the orders from all users (with information from the products in the view) in order to select orders to batch print certificates, etc.

So... That is why we need the permissions to work correctly on the Views that have a base table of orders and a relationship to Products. The product-only views are working correctly for all users. But since the order-relationship-product views are not obeying the permissions, I have a problem.

To make it work, I've had to grant "view own products of any type" permission to roles A and B, which is not a huge problem since they only have permission to create products of the one type anyway. But I've also had to grant "view all products of all types" to role C, which could be an issue if we have a different subset of products with different administrators. So that permission is more permissive than I really wanted to do.

So... I don't see why it would be a problem for the views Product relationship to follow the same permissions as the base Product views already are following, and I don't see why it isn't a bug if this is not the case?

rszrama’s picture

I don't know enough about how Views handles access control for fields of entities referenced through relationships. It's my understanding that it ought to be using the same checks for product access in both places, but if it isn't I can't say if it's a problem with Views or a problem with Commerce. I can say I don't recall anything specific in our code to Views relationships, so my hunch would be a higher level issue. I'd be happy to fix Commerce if it was on us. Any other ideas?

jhodgdon’s picture

I looked at Views, and given that this relationship is just using the default relationship handler, the only access check that is being done is adding any query tags that the base table has specified to the query. In this case, that means adding the query tag 'commerce_product_access' to the query.

So then I looked at the commerce product module, and when this query tag is encountered, it is calling the Commerce core commerce_entity_access_query_alter() function to actually alter the query.

So it looks like that function is not doing the right thing for my queries, because even though there is definite code in there that seems to be checking for the user-specific "view own" types of permissions, in practice these permissions are not being obeyed for the example I gave above. I am pretty sure the bug must be there since that is where the query is being altered, and the entries only vanish for queries when the product relationship is added.

jhodgdon’s picture

Oh, I think I see what the problem is. That function is assuming that the base table of the view is the one that needs to have conditions applied. But I am using several relationships: Orders -> relationship to Line Items -> relationship to Products.

So when this function is called, it is checking user_access based on the commerce_product "view any commerce_product entity of bundle X" and "view own commerce_product entity of bundle X" permissions, but then it is applying the query conditions to $base_table, which is the Orders table in this View, and saying $base_table.bundle == X, which is never going to be TRUE, hence the vanishing view.

In other words, this query alter is not doing the right thing. It needs to search through the tables in $query and apply the permissions to the table that matches the entity type, not just blindly apply them to the base table of the query.

Either that or the functions that call this Commerce core query alter function need to do that search, and pass in $base_table as an argument to the function.

But the way the query alter is being done now is not correct.

jhodgdon’s picture

I have some code in the API module that does a similar thing to what you need to do. Basically, when you have an access query alter, you need to make sure that you are using the right table alias because someone could have multiple copies of the table you are interested in, in any query (especially if Views is involved in making the query). You need to do something like this:

function api_query_node_access_alter($query) {
...

  // Find all instances of the 'node' table, and make sure they exclude API
  // nodes.
  $tables = $query->getTables();
  foreach ($tables as $alias => $table_info) {
    if (is_array($table_info) && isset($table_info['table']) && $table_info['table'] == 'node') {
      // substitute your query conditions for these couple of lines!
      $or = db_or()
        ->isNull($alias . '.type')
        ->condition($alias . '.type', 'api', '<>');
      $query->condition($or);
    }
  }
}
jhodgdon’s picture

Or see how Core does it for node access tables, which is even more complicated:
https://api.drupal.org/api/drupal/modules!node!node.module/function/_nod...

Basically, after determining that {node} is the table it needs to check access on, it does a similar loop to what I put in that previous comment, to find all instances of this table in the query and alter all of the aliases with the right conditions.

Whereas the function in commerce.module just assumes the first table is the one that needs the conditions, which is likely not to be the case in Views with Relationships.

rszrama’s picture

There's a fine lead, and yeah, this is definitely separate from the other issue. I thought we'd committed a fix to arrive at the proper base table, but that must have been an unrelated issue (unless it's still open and I'm also imagining the fix : ).

jhodgdon’s picture

I just checked the repository on drupal.org and I'm not seeing any such commit, so unless you have a different repo somewhere else, I guess not! I also had searched for other issues before I created this one, and didn't find anything that I thought was a duplicate except perhaps
#1276450: Views results empty for unprivileged user when using Relationship: Content: Referenced Product
which we've now agreed isn't a duplicate.

rszrama’s picture

Yeah, I think I may have been thinking of an issue in the Views queue. I'll have to do some hunting.

rszrama’s picture

Issue summary: View changes

fix issue reference