dev/core#4068 Prevent adding in addSelectWhereClause for civicrm_group when filtering...
authorSeamus Lee <seamuslee001@gmail.com>
Wed, 8 Feb 2023 02:40:19 +0000 (13:40 +1100)
committerSeamus Lee <seamuslee001@gmail.com>
Wed, 8 Feb 2023 08:22:18 +0000 (19:22 +1100)
Add in failing test to demonstrate issue with Group add SelectWhereClause on reports

CRM/Report/Form.php
tests/phpunit/api/v3/ReportTemplateTest.php

index e659a503576429be6da6a7e52b0a394b5cfb0bb1..0c401cae780c72ba2622196cf1716be56937b2e1 100644 (file)
@@ -3770,14 +3770,18 @@ WHERE cg.extends IN ('" . implode("','", $this->_customGroupExtends) . "') AND
     }
 
     CRM_Contact_BAO_GroupContactCache::check($smartGroups);
-
+    $aclFilter = NULL;
+    $selectWhereClauses = array_filter(CRM_Contact_BAO_Group::getSelectWhereClause('group'));
+    $aclFilter = implode(' AND ', $selectWhereClauses);
+    $aclFilter = !empty($aclFilter) ? ' AND ' . $aclFilter : '';
     $smartGroupQuery = '';
     if (!empty($smartGroups)) {
       $smartGroups = implode(',', $smartGroups);
       $smartGroupQuery = " UNION DISTINCT
                   SELECT DISTINCT smartgroup_contact.contact_id
                   FROM civicrm_group_contact_cache smartgroup_contact
-                  WHERE smartgroup_contact.group_id IN ({$smartGroups}) ";
+                  INNER JOIN civicrm_group group ON group.id = smartgroup_contact.group_id
+                  WHERE smartgroup_contact.group_id IN ({$smartGroups}) {$aclFilter}";
     }
 
     $sqlOp = $this->getSQLOperator($op);
@@ -3796,7 +3800,8 @@ WHERE cg.extends IN ('" . implode("','", $this->_customGroupExtends) . "') AND
     return " {$contactAlias}.id {$sqlOp} (
                           SELECT DISTINCT {$this->_aliases['civicrm_group']}.contact_id
                           FROM civicrm_group_contact {$this->_aliases['civicrm_group']}
-                          WHERE {$clause} AND {$this->_aliases['civicrm_group']}.status = 'Added'
+                          INNER JOIN civicrm_group group ON group.id = smartgroup_contact.group_id
+                          WHERE {$clause} AND {$this->_aliases['civicrm_group']}.status = 'Added' {$aclFilter}
                           {$smartGroupQuery} ) ";
   }
 
@@ -3950,6 +3955,10 @@ WHERE cg.extends IN ('" . implode("','", $this->_customGroupExtends) . "') AND
     $ret = [];
     foreach ($this->selectedTables() as $tableName) {
       $baoName = str_replace('_DAO_', '_BAO_', (CRM_Core_DAO_AllCoreTables::getClassForTable($tableName) ?? ''));
+      // Do not include CiviCRM group add Select Where clause because we don't necessarily join here for reports with optimisedGroupFilters
+      if ($baoName === 'CRM_Contact_BAO_Group') {
+        continue;
+      }
       if ($baoName && class_exists($baoName) && !empty($this->_columns[$tableName]['alias'])) {
         $tableAlias = $this->_columns[$tableName]['alias'];
         $clauses = array_filter($baoName::getSelectWhereClause($tableAlias));
index 03bfc6636a0b27e9377af0f38139daac02299251..73eb27a5e70b906694873155fa29de63374d75df 100644 (file)
@@ -24,6 +24,8 @@ class api_v3_ReportTemplateTest extends CiviUnitTestCase {
 
   protected $contactIDs = [];
 
+  protected $aclGroupId = NULL;
+
   /**
    * Our group reports use an alter so transaction cleanup won't work.
    *
@@ -33,6 +35,9 @@ class api_v3_ReportTemplateTest extends CiviUnitTestCase {
     $this->quickCleanUpFinancialEntities();
     $this->quickCleanup(['civicrm_group', 'civicrm_saved_search', 'civicrm_group_contact', 'civicrm_group_contact_cache', 'civicrm_group'], TRUE);
     (new CRM_Logging_Schema())->dropAllLogTables();
+    CRM_Utils_Hook::singleton()->reset();
+    $config = CRM_Core_Config::singleton();
+    unset($config->userPermissionClass->permissions);
     parent::tearDown();
   }
 
@@ -684,6 +689,33 @@ class api_v3_ReportTemplateTest extends CiviUnitTestCase {
     $this->assertNumberOfContactsInResult(1, $rows, $template);
   }
 
+  /**
+   * Test the group filter works on various reports when ACLed user is in play
+   *
+   * @dataProvider getMembershipAndContributionReportTemplatesForGroupTests
+   *
+   * @param string $template
+   *   Report template unique identifier.
+   *
+   * @throws \CRM_Core_Exception
+   */
+  public function testReportsWithNonSmartGroupFilterWithACL($template) {
+    $this->aclGroupId = $this->setUpPopulatedGroup();
+    $this->createLoggedInUser();
+    CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM'];
+    $this->callAPISuccessGetCount('Group', ['check_permissions' => 1], 0);
+    $this->hookClass->setHook('civicrm_aclGroup', [$this, 'aclGroupOnly']);
+    $this->hookClass->setHook('civicrm_aclWhereClause', [$this, 'aclGroupContactsOnly']);
+    unset(Civi::$statics['CRM_ACL_API']['group_permission']);
+    $rows = $this->callAPISuccess('report_template', 'getrows', [
+      'report_id' => $template,
+      'gid_value' => [$this->aclGroupId],
+      'gid_op' => 'in',
+      'options' => ['metadata' => ['sql']],
+    ]);
+    $this->assertNumberOfContactsInResult(1, $rows, $template);
+  }
+
   /**
    * Assert the included results match the expected.
    *
@@ -1774,4 +1806,33 @@ class api_v3_ReportTemplateTest extends CiviUnitTestCase {
     \Civi::service('sql_triggers')->rebuild(NULL, TRUE);
   }
 
+  /**
+   * Implement hook to restrict to test group 1.
+   *
+   * @param string $type
+   * @param int $contactID
+   * @param string $tableName
+   * @param array $allGroups
+   * @param array $ids
+   */
+  public function aclGroupOnly($type, $contactID, $tableName, $allGroups, &$ids) {
+    $ids = [$this->aclGroupId];
+  }
+
+  /**
+   * Implements hook to limit to contacts only in the aclGroup
+   *
+   * @param string $type
+   * @param array $tables
+   * @param array $whereTables
+   * @param int|null $contactID
+   * @param string $where
+   */
+  public function aclGroupContactsOnly($type, &$tables, &$whereTables, &$contactID, &$where) {
+    if (!empty($where)) {
+      $where .= ' AND ';
+    }
+    $where .= 'contact_a.id IN (SELECT contact_id FROM civicrm_group_contact WHERE status = \'Added\' AND group_id = ' . $this->aclGroupId . ')';
+  }
+
 }