It's often necessary for store admins to hide particular orders from their creators, eg if an order has had to be cancelled for payment security reasons.

Comments

joachim’s picture

The chain of access for commerce entities is fairly complex, so here's a summary:

- commerce_order_ui_menu() defines the path /user/X/orders/Y with access callback commerce_order_customer_order_view_access()
- commerce_order_customer_order_view_access() calls the entity access callback, commerce_order_access()
- commerce_order_access() calls the generic access helper, commerce_entity_access()
- for view operations, this constructs an access query based on the entity, which relies on Database API invoking hook_query_ACCESS_TAG_alter(). For orders, this is hook_query_commerce_order_access_alter().
- commerce order's implementation of this hook, commerce_order_query_commerce_order_access_alter(), calls a generic implementation back in commerce module, commerce_entity_access_query_alter()
- commerce_entity_access_query_alter() checks entity type and user access permissions for the entity.

rszrama’s picture

Status: Active » Closed (works as designed)

And then to finish off the chain, commerce_entity_access_query_alter() builds an OR conditions array that you can alter for Orders using hook_commerce_entity_access_condition_commerce_order_alter(). Note that restricting access is different from granting access. To restrict access you'll actually need to alter out existing conditions that would otherwise grant the user access to view the order.

While there is no user interface for building this feature, which would be out of scope for Commerce itself anyways, it does seem the API supports what you need. I'm going to mark this works as designed, but feel free to post follow-up questions.

bennybobw’s picture

Status: Closed (works as designed) » Active

I'm a bit fuzzy on this. I'm trying to set order access based on a field value on the order (a reference to a user profile, like in this issue https://drupal.org/node/1434610).

This is how I've implemented it -- I've had to use 2 hooks. hook_query_commerce_order_access_alter and hook_commerce_entity_access_condition_commerce_order_alter.

function diver_admin_query_commerce_order_access_alter(&$query) {
  global $user;
  $account = $query->getMetaData('account');
  if (empty($account)) {
    $account = $user;
  }
   //profile2 caches profile, so we've already loaded it
   $profile = profile2_load_by_user($account, 'diver');
   if (!empty($profile)) {
     $tables = $query->getTables();
     if (!isset($tables['field_data_field_diver_profile_all'])) {
       $query->leftJoin('field_data_field_diver_profile_all', 'field_data_field_diver_profile_all', 
       '(field_data_field_diver_profile_all.entity_id = commerce_order.order_id AND  field_data_field_diver_profile_all.entity_type = commerce_order.type AND field_data_field_diver_profile_all.revision_id = commerce_order.revision_id)');
     }
   }
}

function diver_admin_commerce_entity_access_condition_commerce_order_alter(&$conditions, &$context) {
  $account = $context['account'];
  $profile = profile2_load_by_user($account, 'diver');
  if (!empty($profile)) {
    $conditions->condition('field_data_field_diver_profile_all.field_diver_profile_all_target_id', $profile->pid);
  }
}

Inside the condition hook, I add the field condition, but I don't have access to the query there, so I can't add the join. However, in the query hook, I don't necessarily know which $query->conditions() contains the access condition. For instance, one of my views nests the access condition inside of its own condition. So in the condition hook, I have to add the condition and in the query hook I add the condition.

Not sure if this is the best way to do it, but it's the only way I could figure it out.

rszrama’s picture

Status: Active » Closed (works as designed)

If what you've done works, there probably isn't any reason to change it. However, what you could do instead is just actually include the data in your condition that you'd be joining to. In other words, nothing prevents you in your query from having a condition that compares 1 and 2. If 1 is your target_id and 2 is your $profile->pid, it will be FALSE, no query level join required.

See for example commerce_cart_commerce_entity_access_condition_commerce_order_alter(), which restricts an anonymous user's access to only view orders whose IDs are present in the user's session:

  // If the user has access to view his own orders of any bundle...
  if (user_access('view own ' . $context['entity_type'] . ' entities', $context['account'])) {
    // Add a condition granting the user view access to any completed orders
    // in his session.
    $conditions->condition($context['base_table'] . '.order_id', $completed_order_ids, 'IN');
  }
mihai_brb’s picture

Issue summary: View changes
Status: Closed (works as designed) » Needs review

Just to summarize:
View access
+ can be granted by implementing hook_commerce_entity_access_condition_commerce_order_alter and adding a condition in the OR chain that will satisfy the query
Edit/Delete access
+ can be granted by implementing hook_commerce_entity_access

When implementing "view" access alter, I find it a little inconvenient that:
- the view page callback checks for entity access but also user_access('access administration pages')
- the edit page callback only checks for entity access
If for example I would like to grant access to a site manager to view certain orders, I would also have to add access to administration pages?
Isn't sufficient to check for entity access in the order view page callback?

Moreover, in "commerce_entity_access", it would ease the pain of getting the entity ID that is currently issued for viewed access if we would have the entity object, or at least entity_id or as metadata in context.

Thanks,
Mihai

mihai_brb’s picture

Title: access to orders on a per-order basis » Commerce Order entity access
StatusFileSize
new854 bytes

Added a patch that adds the entity as Metadata and passes the entity in context to hook_commerce_entity_access_condition().

This should fix the problem of not having the entity issued for view access.

Status: Needs review » Needs work

The last submitted patch, 6: commerce-order_entity_view_access-707484-5.patch, failed testing.

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 6: commerce-order_entity_view_access-707484-5.patch, failed testing.

Status: Needs work » Needs review

mglaman’s picture

Issue tags: +sprint
mglaman’s picture

Title: Commerce Order entity access » Pass entity to Commerce entity access check contexts
Status: Needs review » Reviewed & tested by the community

Adjusting title to be more fitting of patch in #6. More context is always better in my opinion.

rszrama’s picture

Status: Reviewed & tested by the community » Fixed

Works for me! Committed.

  • rszrama committed 9a6f63d on 7.x-1.x authored by mihai_brb
    Issue #1531602 by mihai_brb: pass entity to Commerce entity access check...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.