CRM-20441 always do acl checks from api
authoreileen <emcnaughton@wikimedia.org>
Tue, 25 Apr 2017 22:59:12 +0000 (10:59 +1200)
committerSeamus Lee <seamuslee001@gmail.com>
Wed, 26 Apr 2017 04:31:38 +0000 (14:31 +1000)
CRM/Activity/BAO/Activity.php
api/v3/Activity.php
tests/phpunit/CRM/Activity/BAO/ActivityTest.php
tests/phpunit/CiviTest/CiviUnitTestCase.php
tests/phpunit/api/v3/ACLPermissionTest.php

index 1c5f2d2b516ad6c72573a6b1ce0deebe57ef0c02..d2941abc0fd40ff37e35da2ce7fdbdea4a862a07 100644 (file)
@@ -664,46 +664,15 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
   public static function getActivities($params, $getCount = FALSE) {
     $activities = array();
 
-    // fetch all activity IDs whose target/assignee/source contact id is $params['contact_id']
-    // currently cannot be done via Activity.Get API so we are using SQL query instead
-    if (!empty($params['contact_id'])) {
-      $activityIDs = CRM_Core_DAO::singleValueQuery("SELECT GROUP_CONCAT(DISTINCT activity_id SEPARATOR ',')
-        FROM civicrm_activity_contact
-        WHERE contact_id = %1", array(1 => array($params['contact_id'], 'Integer')));
-
-      // if no activities found for given $params['contact_id'] then return empty array
-      if (empty($activityIDs)) {
-        return $getCount ? count($activities) : $activities;
-      }
-      $activityIDs = explode(',', $activityIDs);
-      // CRM-20441 Check if user has access to the activities.
-      // This is a temporary fix we need to figure out the rules around
-      // the right permissions to access Activities.
-      // This attempts to reduce fatal errors in 4.7.19 RC.
-      if (!empty($activityIDs)) {
-        foreach ($activityIDs as $key => $activityId) {
-          try {
-            civicrm_api3('Activity', 'get', array('id' => $activityId, 'check_permissions' => 1));
-          }
-          catch (Exception $e) {
-            unset($activityIDs[$key]);
-          }
-        }
-      }
-      if (empty($activityIDs)) {
-        return $getCount ? count($activities) : $activities;
-      }
-    }
-
     // fetch all active activity types
     $activityTypes = CRM_Core_OptionGroup::values('activity_type');
 
     // Activity.Get API params
     $activityParams = array(
-      'id' => (!empty($activityIDs)) ? array('IN' => $activityIDs) : NULL,
       'is_deleted' => 0,
       'is_current_revision' => 1,
       'is_test' => 0,
+      'contact_id' => CRM_Utils_Array::value('contact_id', $params),
       'return' => array(
         'activity_date_time',
         'source_record_id',
index 1b34761edf49a1f4094af6baf82661dfa4bbe15c..0d6a85de41c48cc709624ddd98693131aa99573d 100644 (file)
@@ -293,42 +293,6 @@ function _civicrm_api3_activity_get_spec(&$params) {
  * @throws \Civi\API\Exception\UnauthorizedException
  */
 function civicrm_api3_activity_get($params) {
-  if (!empty($params['check_permissions']) && !CRM_Core_Permission::check('view all activities')) {
-    // In absence of view all activities permission it's possible to see a specific activity by ACL.
-    // Note still allowing view all activities to override ACLs is based on the 'don't change too much
-    // if you are not sure principle' and it could be argued that the ACLs should always be applied.
-    if (empty($params['id']) || !empty($params['contact_id'])) {
-      // We fall back to the original blunt permissions if we don't have an id to check or we are about
-      // to go to the weird place that the legacy 'contact_id' parameter takes us to.
-      throw new \Civi\API\Exception\UnauthorizedException(
-        "Cannot access activities. Required permission: 'view all activities''"
-      );
-    }
-    $ids = array();
-    $allowed_operators = array(
-      'IN',
-    );
-    if (is_array($params['id'])) {
-      foreach ($params['id'] as $operator => $values) {
-        if (in_array($operator, CRM_Core_DAO::acceptedSQLOperators()) && in_array($operator, $allowed_operators)) {
-          $ids = $values;
-        }
-        else {
-          throw new \API_Exception(ts('Used an unsupported sql operator with Activity.get API'));
-        }
-      }
-    }
-    else {
-      $ids = array($params['id']);
-    }
-    foreach ($ids as $id) {
-      if (!CRM_Activity_BAO_Activity::checkPermission($id, CRM_Core_Action::VIEW)) {
-        throw new \Civi\API\Exception\UnauthorizedException(
-          'You do not have permission to view this activity'
-        );
-      }
-    }
-  }
 
   $sql = CRM_Utils_SQL_Select::fragment();
   $recordTypes = civicrm_api3('ActivityContact', 'getoptions', array('field' => 'record_type_id'));
@@ -339,6 +303,16 @@ function civicrm_api3_activity_get($params) {
     'source_contact_id' => array_search('Activity Source', $recordTypes),
     'assignee_contact_id' => array_search('Activity Assignees', $recordTypes),
   );
+  if (empty($params['target_contact_id']) && empty($params['source_contact_id'])
+    && empty($params['assignee_contact_id']) &&
+    !empty($params['check_permissions']) && !CRM_Core_Permission::check('view all activities')
+    && !CRM_Core_Permission::check('view all contacts')
+  ) {
+    // Force join on the activity contact table.
+    // @todo get this & other acl filters to work, remove check further down.
+    //$params['contact_id'] = array('IS NOT NULL' => TRUE);
+  }
+
   foreach ($activityContactOptions as $activityContactName => $activityContactValue) {
     if (!empty($params[$activityContactName])) {
       if (!is_array($params[$activityContactName])) {
@@ -379,6 +353,14 @@ function civicrm_api3_activity_get($params) {
     }
   }
   $activities = _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params, FALSE, 'Activity', $sql);
+  if (!empty($params['check_permissions']) && !CRM_Core_Permission::check('view all activities')) {
+    // @todo get this to work at the query level - see contact_id join above.
+    foreach ($activities as $activity) {
+      if (!CRM_Activity_BAO_Activity::checkPermission($activity['id'], CRM_Core_Action::VIEW)) {
+        unset($activities[$activity['id']]);
+      }
+    }
+  }
   $options = _civicrm_api3_get_options_from_params($params, FALSE, 'Activity', 'get');
   if ($options['is_count']) {
     return civicrm_api3_create_success($activities, $params, 'Activity', 'get');
index c1705cb2a07dd98a1885d3d88c66815238274ec7..e9305552f5e08802e865f50cee3d6738c62cc9c6 100644 (file)
@@ -434,6 +434,18 @@ class CRM_Activity_BAO_ActivityTest extends CiviUnitTestCase {
     $this->setUpForActivityDashboardTests();
     $activities = CRM_Activity_BAO_Activity::getActivities($this->_params);
     $this->assertEquals(0, count($activities));
+  }
+
+  /**
+   * Test getActivities BAO method.
+   */
+  public function testGetActivitiesForAdminDashboardAclLimitedViewContacts() {
+    CRM_Core_Config::singleton()->userPermissionClass->permissions = array('access CiviCRM');
+    $this->allowedContacts = array(1, 3, 4, 5);
+    $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'aclWhereMultipleContacts'));
+    $this->setUpForActivityDashboardTests();
+    $activities = CRM_Activity_BAO_Activity::getActivities($this->_params);
+    $this->assertEquals(1, count($activities));
 
   }
 
index d5a9b8449fc92b0683a1db0f7b73b70001e9f507..0e6d1264bb0dc54339c790b4ec21dc4db0f6f0d1 100644 (file)
@@ -3823,6 +3823,19 @@ AND    ( TABLE_NAME LIKE 'civicrm_value_%' )
   public function aclWhereHookNoResults($type, &$tables, &$whereTables, &$contactID, &$where) {
   }
 
+  /**
+   * Only specified contact returned.
+   * @implements CRM_Utils_Hook::aclWhereClause
+   * @param $type
+   * @param $tables
+   * @param $whereTables
+   * @param $contactID
+   * @param $where
+   */
+  public function aclWhereMultipleContacts($type, &$tables, &$whereTables, &$contactID, &$where) {
+    $where = " contact_a.id IN (" . implode(', ', $this->allowedContacts) . ")";
+  }
+
   /**
    * @implements CRM_Utils_Hook::selectWhereClause
    *
index 1618a1942147ed1df9c33f93722aa3934aa89a33..5fdf7ac53b00b8477f273819a35f3a3bb233bc4c 100644 (file)
@@ -466,19 +466,6 @@ class api_v3_ACLPermissionTest extends CiviUnitTestCase {
     $where = " contact_a.id = " . $this->allowedContactId;
   }
 
-  /**
-   * Only specified contact returned.
-   * @implements CRM_Utils_Hook::aclWhereClause
-   * @param $type
-   * @param $tables
-   * @param $whereTables
-   * @param $contactID
-   * @param $where
-   */
-  public function aclWhereMultipleContacts($type, &$tables, &$whereTables, &$contactID, &$where) {
-    $where = " contact_a.id IN (" . implode(', ', $this->allowedContacts) . ")";
-  }
-
   /**
    * Basic check that an unpermissioned call keeps working and permissioned call fails.
    */
@@ -501,9 +488,9 @@ class api_v3_ACLPermissionTest extends CiviUnitTestCase {
   /**
    * View all activities is required unless id is passed in.
    */
-  public function testGetActivityViewAllContactsNotEnoughWIthoutID() {
+  public function testGetActivityViewAllContactsEnoughWIthoutID() {
     $this->setPermissions(array('view all contacts', 'access CiviCRM'));
-    $this->callAPIFailure('Activity', 'get', array('check_permissions' => 1));
+    $this->callAPISuccess('Activity', 'get', array('check_permissions' => 1));
   }
 
   /**
@@ -630,8 +617,8 @@ class api_v3_ACLPermissionTest extends CiviUnitTestCase {
       'id' => array('NOT IN' => array($activity['id'], $activity2['id'])),
       'check_permissions' => TRUE,
     );
-    $result = $this->callAPIFailure('activity', 'get', $params);
-    $this->assertEquals('Used an unsupported sql operator with Activity.get API', $result['error_message']);
+    $result = $this->callAPISuccess('activity', 'get', $params);
+    $this->assertEquals(0, $result['count']);
   }
 
   /**