CRM-17795 - Add aclWhere clause to contact-related apis
authorColeman Watts <coleman@civicrm.org>
Fri, 8 Jan 2016 14:13:49 +0000 (09:13 -0500)
committerColeman Watts <coleman@civicrm.org>
Sat, 9 Jan 2016 01:51:03 +0000 (20:51 -0500)
CRM/Contact/BAO/Contact/Permission.php
Civi/API/SelectQuery.php

index dfb82a2aa7a0650c50755eab9f9875302e3fdbb9..761b6572523c3c55a1ee1f81cac82bb649744179 100644 (file)
@@ -182,11 +182,10 @@ AND    $operationClause LIMIT 1";
 
   /**
    * @param string $contactAlias
-   * @param int $contactID
    *
    * @return array
    */
-  public static function cacheClause($contactAlias = 'contact_a', $contactID = NULL) {
+  public static function cacheClause($contactAlias = 'contact_a') {
     if (CRM_Core_Permission::check('view all contacts') ||
       CRM_Core_Permission::check('edit all contacts')
     ) {
@@ -204,13 +203,7 @@ AND    $operationClause LIMIT 1";
       }
     }
 
-    $session = CRM_Core_Session::singleton();
-    $contactID = $session->get('userID');
-    if (!$contactID) {
-      $contactID = 0;
-    }
-    $contactID = CRM_Utils_Type::escape($contactID, 'Integer');
-
+    $contactID = (int) CRM_Core_Session::getLoggedInContactID();
     self::cache($contactID);
 
     if (is_array($contactAlias) && !empty($contactAlias)) {
@@ -231,6 +224,26 @@ AND    $operationClause LIMIT 1";
     return array($fromClause, $whereClase);
   }
 
+  /**
+   * Generate acl subquery that can be placed in the WHERE clause of a query or the ON clause of a JOIN
+   *
+   * @param string $contactIdField
+   *   Full "table_name.field_name" for the field containing a contact id
+   * @return string
+   */
+  public static function cacheSubquery($contactIdField) {
+    $clauses = array();
+    if (!CRM_Core_Permission::check(array(array('view all contacts', 'edit all contacts')))) {
+      $contactID = (int) CRM_Core_Session::getLoggedInContactID();
+      self::cache($contactID);
+      $clauses[] = "$contactIdField IN (SELECT contact_id FROM civicrm_acl_contact_cache WHERE user_id = $contactID)";
+    }
+    if (!CRM_Core_Permission::check('access deleted contacts')) {
+      $clauses[] = "$contactIdField NOT IN (SELECT id FROM civicrm_contact WHERE is_deleted = 1)";
+    }
+    return $clauses ? implode(' AND ', $clauses) : '1';
+  }
+
   /**
    * Get the permission base on its relationship.
    *
index ca054b4db6dca34d338740470e85d5de9fa2751b..cbc1cb0bb339a12cea2cfff371932c6732413295 100644 (file)
@@ -272,6 +272,9 @@ class SelectQuery {
       $this->query->limit($this->options['limit'], $this->options['offset']);
     }
 
+    // ACLs
+    $this->addAclClause();
+
     $result_entities = array();
     $result_dao = \CRM_Core_DAO::executeQuery($this->query->toSQL());
 
@@ -449,4 +452,16 @@ class SelectQuery {
     return \Civi::service('civi_api_kernel')->runAuthorize($entity, 'get', $params);
   }
 
+  /**
+   * If this entity has a `contact_id` field, add appropriate acl clause
+   */
+  private function addAclClause() {
+    if (in_array('contact_id', $this->entityFieldNames)) {
+      $clause = \CRM_Contact_BAO_Contact_Permission::cacheSubquery('a.contact_id');
+      if ($clause !== '1') {
+        $this->query->where($clause);
+      }
+    }
+  }
+
 }