Fix ACLs to use the correct name of the civicrm_group table
authorcolemanw <coleman@civicrm.org>
Fri, 23 Jun 2023 00:42:59 +0000 (17:42 -0700)
committercolemanw <coleman@civicrm.org>
Fri, 23 Jun 2023 04:41:16 +0000 (21:41 -0700)
For unknown historical reasons, group-based ACLs incorrectly used 'civicrm_saved_search'
as the value for 'object_table' when referencing a group id.

This updates all ACL records, tests, and adds legacy handling for hook subscribers with a noisy deprecation notice.

17 files changed:
CRM/ACL/API.php
CRM/ACL/BAO/ACL.php
CRM/ACL/Form/ACL.php
CRM/ACL/Page/ACL.php
CRM/Bridge/OG/Drupal.php
CRM/Contact/BAO/Group.php
CRM/Core/Permission/Base.php
CRM/Upgrade/Incremental/sql/5.64.alpha1.mysql.tpl
CRM/Utils/Hook.php
Civi/Test/ACLPermissionTrait.php
tests/phpunit/CRM/Group/Page/AjaxTest.php
tests/phpunit/CRM/Mailing/BAO/MailingTest.php
tests/phpunit/CiviTest/CiviUnitTestCase.php
tests/phpunit/api/v3/GroupTest.php
tests/phpunit/api/v3/RelationshipTest.php
tests/phpunit/api/v3/ReportTemplateTest.php
xml/templates/civicrm_acl.tpl

index d9739d48d78120e77e6fe1f88d967684d6b3a696..ce2fc6ae7f538533ace043816d142635932d86ab 100644 (file)
@@ -148,7 +148,7 @@ class CRM_ACL_API {
   public static function group(
     $type,
     $contactID = NULL,
-    $tableName = 'civicrm_saved_search',
+    $tableName = 'civicrm_group',
     $allGroups = NULL,
     $includedGroups = []
   ) {
@@ -181,7 +181,7 @@ class CRM_ACL_API {
     $type,
     $groupID,
     $contactID = NULL,
-    $tableName = 'civicrm_saved_search',
+    $tableName = 'civicrm_group',
     $allGroups = NULL,
     $includedGroups = NULL
   ) {
index 10ff5e51bda59d77f73d1d2a357f330c41e19031..77311c8cf741dd1d74e7a5b8e2341a7618f4f2e9 100644 (file)
@@ -234,7 +234,7 @@ SELECT   a.operation, a.object_id
   FROM   civicrm_acl_cache c, civicrm_acl a
  WHERE   c.acl_id       =  a.id
    AND   a.is_active    =  1
-   AND   a.object_table = 'civicrm_saved_search'
+   AND   a.object_table = 'civicrm_group'
    AND   a.id        IN ( $aclKeys )
    AND   a.deny         = 0
 ORDER BY a.object_id
@@ -259,7 +259,7 @@ ORDER BY a.object_id
   FROM   civicrm_acl_cache c, civicrm_acl a
  WHERE   c.acl_id       =  a.id
    AND   a.is_active    =  1
-   AND   a.object_table = 'civicrm_saved_search'
+   AND   a.object_table = 'civicrm_group'
    AND   a.id        IN ( $aclKeys )
    AND   a.deny         = 1
    AND   a.object_id IN (%1)
@@ -333,7 +333,7 @@ SELECT g.*
   public static function group(
     $type,
     $contactID = NULL,
-    $tableName = 'civicrm_saved_search',
+    $tableName = 'civicrm_group',
     $allGroups = NULL,
     $includedGroups = []
   ) {
index a86ebb1fcb864a1c3a24ad778d7935d35ed027e0..7b80b81543a76029a43ef7f3d56c49fc150049de 100644 (file)
@@ -37,7 +37,7 @@ class CRM_ACL_Form_ACL extends CRM_Admin_Form {
 
     if (isset($defaults['object_table'])) {
       switch ($defaults['object_table']) {
-        case 'civicrm_saved_search':
+        case 'civicrm_group':
           $defaults['group_id'] = $defaults['object_id'];
           $defaults['object_type'] = 1;
           $showHide->addShow("id-group-acl");
@@ -265,7 +265,7 @@ class CRM_ACL_Form_ACL extends CRM_Admin_Form {
       // Figure out which type of object we're permissioning on and set object_table and object_id.
       switch ($params['object_type']) {
         case 1:
-          $params['object_table'] = 'civicrm_saved_search';
+          $params['object_table'] = 'civicrm_group';
           $params['object_id'] = $params['group_id'];
           break;
 
index 9aea585919088a437302fe70b77cabb87c4afc46..78c7fe26e7dfe7b7a68f1720ac343c0a0d09f4da 100644 (file)
@@ -142,7 +142,7 @@ class CRM_ACL_Page_ACL extends CRM_Core_Page_Basic {
       }
 
       switch ($acl[$dao->id]['object_table']) {
-        case 'civicrm_saved_search':
+        case 'civicrm_group':
           $acl[$dao->id]['object'] = $group[$acl[$dao->id]['object_id']] ?? NULL;
           $acl[$dao->id]['object_name'] = ts('Group');
           break;
index 7a8b0be8b574e950272ab947dbcb3d15875a2888..6933ac0fcc7704e5a5e0e3f3cab541cbff70d169 100644 (file)
@@ -175,7 +175,7 @@ SELECT v.id
   public static function updateCiviACL(&$params, $op) {
     $dao = new CRM_ACL_DAO_ACL();
 
-    $dao->object_table = 'civicrm_saved_search';
+    $dao->object_table = 'civicrm_group';
     $dao->object_id = $params['civicrm_group_id'];
 
     if ($op == 'delete') {
index 6563b61d57c2395a41d442c7712c717ee86c8b92..f2011648444fa3b5bf8677eea3522e1b10610d10 100644 (file)
@@ -285,7 +285,7 @@ class CRM_Contact_BAO_Group extends CRM_Contact_DAO_Group {
     $permissions = NULL;
     if (CRM_Core_Permission::check('edit all contacts') ||
       CRM_ACL_API::groupPermission(CRM_ACL_API::EDIT, $id, NULL,
-        'civicrm_saved_search', $allGroups
+        'civicrm_group', $allGroups
       )
     ) {
       $permissions[] = CRM_Core_Permission::EDIT;
@@ -293,7 +293,7 @@ class CRM_Contact_BAO_Group extends CRM_Contact_DAO_Group {
 
     if (CRM_Core_Permission::check('view all contacts') ||
       CRM_ACL_API::groupPermission(CRM_ACL_API::VIEW, $id, NULL,
-        'civicrm_saved_search', $allGroups
+        'civicrm_group', $allGroups
       )
     ) {
       $permissions[] = CRM_Core_Permission::VIEW;
@@ -316,8 +316,7 @@ class CRM_Contact_BAO_Group extends CRM_Contact_DAO_Group {
     $clauses = [];
     if (!CRM_Core_Permission::check([['edit all contacts', 'view all contacts']])) {
       $allGroups = CRM_Core_PseudoConstant::allGroup(NULL, FALSE);
-      // FIXME: TableName 'civicrm_saved_search' seems wrong but is consistent with self::checkPermission
-      $allowedGroups = \CRM_ACL_API::group(CRM_ACL_API::VIEW, NULL, 'civicrm_saved_search', $allGroups);
+      $allowedGroups = \CRM_ACL_API::group(CRM_ACL_API::VIEW, NULL, 'civicrm_group', $allGroups);
       $groupsIn = $allowedGroups ? implode(',', $allowedGroups) : '0';
       $clauses['id'][] = "IN ($groupsIn)";
     }
index 9b1ae5709bbcfa3486bfe22485ba5a401efe33ef..ac9fbea5499a180a5f14699cb3ba6b48460c97e0 100644 (file)
@@ -169,7 +169,7 @@ class CRM_Core_Permission_Base {
         Civi::$statics[__CLASS__]['viewPermissionedGroups_' . $domainId . '_' . $userId][$groupKey] = $groups;
       }
 
-      $ids = CRM_ACL_API::group(CRM_Core_Permission::VIEW, NULL, 'civicrm_saved_search', $groups);
+      $ids = CRM_ACL_API::group(CRM_Core_Permission::VIEW, NULL, 'civicrm_group', $groups);
       if (!empty($ids)) {
         foreach (array_values($ids) as $id) {
           $title = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Group', $id, 'title');
@@ -178,7 +178,7 @@ class CRM_Core_Permission_Base {
         }
       }
 
-      $ids = CRM_ACL_API::group(CRM_Core_Permission::EDIT, NULL, 'civicrm_saved_search', $groups);
+      $ids = CRM_ACL_API::group(CRM_Core_Permission::EDIT, NULL, 'civicrm_group', $groups);
       if (!empty($ids)) {
         foreach (array_values($ids) as $id) {
           $title = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Group', $id, 'title');
index f1f12629b23bb19569cb9c9828d8a1926db94bfb..d44f0b8c4fcd987bd186ddfd5d25636d814520c2 100644 (file)
@@ -4,7 +4,10 @@ UPDATE `civicrm_acl` SET `priority` = `id`;
 
 -- Remove obsolete "Basic ACLs"
 DELETE FROM civicrm_acl
-WHERE object_table NOT IN ('civicrm_saved_search', 'civicrm_uf_group', 'civicrm_custom_group', 'civicrm_event');
+WHERE object_table NOT IN ('civicrm_group', 'civicrm_saved_search', 'civicrm_uf_group', 'civicrm_custom_group', 'civicrm_event');
+
+-- Fix wrong table name
+UPDATE `civicrm_acl` SET `object_table` = 'civicrm_group' WHERE `object_table` = 'civicrm_saved_search';
 
 -- fix mis-casing of field name. Note the php function doesn't permit the name change hence it is here
 -- but field is not localised.
index 94039470135b46944040ce363cca79d8d3bf2ae3..5e6005b77bdf25f06a48482784e03804d603bfb3 100644 (file)
@@ -598,9 +598,7 @@ abstract class CRM_Utils_Hook {
    * @param int $contactID
    *   User contactID for whom the check is made.
    * @param string $tableName
-   *   Table name of group, e.g. `civicrm_uf_group` or `civicrm_custom_group`.
-   *   Note: for some weird reason when this hook is called for contact groups, this
-   *   value will be `civicrm_saved_search` instead of `civicrm_group` as you'd expect.
+   *   Table name of group, e.g. 'civicrm_group' or 'civicrm_uf_group' or 'civicrm_custom_group'.
    * @param array $allGroups
    *   All groups from the above table, keyed by id.
    * @param int[] $currentGroups
@@ -611,6 +609,17 @@ abstract class CRM_Utils_Hook {
    */
   public static function aclGroup($type, $contactID, $tableName, &$allGroups, &$currentGroups) {
     $null = NULL;
+    // Legacy support for hooks that still expect 'civicrm_group' to be 'civicrm_saved_search'
+    // This was changed in 5.64
+    if ($tableName === 'civicrm_group') {
+      $initialValue = $currentGroups;
+      $legacyTableName = 'civicrm_saved_search';
+      self::singleton()
+        ->invoke(['type', 'contactID', 'tableName', 'allGroups', 'currentGroups'], $type, $contactID, $legacyTableName, $allGroups, $currentGroups, $null, 'civicrm_aclGroup');
+      if ($initialValue != $currentGroups) {
+        CRM_Core_Error::deprecatedWarning('Since 5.64 hook_civicrm_aclGroup passes "civicrm_group" instead of "civicrm_saved_search" for the $tableName when referring to Groups. Hook listeners should be updated.');
+      }
+    }
     return self::singleton()
       ->invoke(['type', 'contactID', 'tableName', 'allGroups', 'currentGroups'], $type, $contactID, $tableName, $allGroups, $currentGroups, $null, 'civicrm_aclGroup');
   }
index 91b6681cf6d79ce32e2d51d900bd0370b76d282a..7ff1705e310730fe38404afdd96e266d6b38009e 100644 (file)
@@ -125,7 +125,7 @@ trait ACLPermissionTrait {
    * @throws CRM_Core_Exception
    */
   public function setupCoreACLPermittedAcl($permissionedEntities = [], $groupAllowedAccess = 'Everyone', $operation = 'View', $entity = 'Group') {
-    $tableMap = ['Group' => 'civicrm_saved_search', 'CustomGroup' => 'civicrm_custom_group', 'Profile' => 'civicrm_uf_match', 'Event' => 'civicrm_event'];
+    $tableMap = ['Group' => 'civicrm_group', 'CustomGroup' => 'civicrm_custom_group', 'Profile' => 'civicrm_uf_group', 'Event' => 'civicrm_event'];
     $entityTable = $tableMap[$entity];
 
     $permittedRoleID = ($groupAllowedAccess === 'Everyone') ? 0 : $groupAllowedAccess;
index acb7ad4bc73af81858a3bf00758fb9e4c5e243cd..6f52b0805cda5da95d39097c2b51b372b4db994e 100644 (file)
@@ -666,6 +666,9 @@ class CRM_Group_Page_AjaxTest extends CiviUnitTestCase {
    * @param array $currentGroups
    */
   public function hook_civicrm_aclGroup($type, $contactID, $tableName, &$allGroups, &$currentGroups) {
+    if ($tableName !== 'civicrm_group') {
+      return;
+    }
     //don't use api - you will get a loop
     $sql = " SELECT * FROM civicrm_group WHERE name LIKE '%pick%'";
     $groups = [];
@@ -734,7 +737,7 @@ class CRM_Group_Page_AjaxTest extends CiviUnitTestCase {
       `name`, `entity_table`, `entity_id`, `operation`, `object_table`, `object_id`, `is_active`
       )
       VALUES (
-      'core-580', 'civicrm_acl_role', 55, 'Edit', 'civicrm_saved_search', 0, 1
+      'core-580', 'civicrm_acl_role', 55, 'Edit', 'civicrm_group', 0, 1
       );
       ");
 
index 93d22895399823b24bfc36fa5a9b564983a9d498..9af4831d169a83f234f3947409966e8f83057650 100644 (file)
@@ -225,6 +225,9 @@ class CRM_Mailing_BAO_MailingTest extends CiviUnitTestCase {
    * @param array $currentGroups
    */
   public function hook_civicrm_aclGroup($type, $contactID, $tableName, &$allGroups, &$currentGroups) {
+    if ($tableName !== 'civicrm_group') {
+      return;
+    }
     //don't use api - you will get a loop
     $sql = " SELECT * FROM civicrm_group";
     $groups = [];
index df2a5f27742fab02bdbeaba4f4c23b0aa7a11dee..48eb3612f9d00dcdcb9185d9ba0b7bcbe3c653d3 100644 (file)
@@ -2182,7 +2182,7 @@ class CiviUnitTestCase extends PHPUnit\Framework\TestCase {
       `name`, `entity_table`, `entity_id`, `operation`, `object_table`, `object_id`, `is_active`
       )
       VALUES (
-      'view picked', 'civicrm_group', $this->_permissionedGroup , 'Edit', 'civicrm_saved_search', {$this->_permissionedGroup}, 1
+      'view picked', 'civicrm_group', $this->_permissionedGroup , 'Edit', 'civicrm_group', {$this->_permissionedGroup}, 1
       );
       ");
 
@@ -2191,7 +2191,7 @@ class CiviUnitTestCase extends PHPUnit\Framework\TestCase {
       `name`, `entity_table`, `entity_id`, `operation`, `object_table`, `object_id`, `is_active`
       )
       VALUES (
-      'view picked', 'civicrm_group',  $this->_permissionedGroup, 'Edit', 'civicrm_saved_search', {$this->_permissionedDisabledGroup}, 1
+      'view picked', 'civicrm_group',  $this->_permissionedGroup, 'Edit', 'civicrm_group', {$this->_permissionedDisabledGroup}, 1
       );
       ");
     }
index 70b542dfd9ecd38d07a2246d71a82f0d53fedfdd..26f721a6c146ffd1be08f52bf6729b76a7ea2cfc 100644 (file)
@@ -362,8 +362,10 @@ class api_v3_GroupTest extends CiviUnitTestCase {
    * @param array $ids
    */
   public function aclGroupAllGroups($type, $contactID, $tableName, $allGroups, &$ids) {
-    $group = $this->callAPISuccess('Group', 'get', ['name' => 'Test Group 1']);
-    $ids = array_keys($group['values']);
+    if ($tableName === 'civicrm_group') {
+      $group = $this->callAPISuccess('Group', 'get', ['name' => 'Test Group 1']);
+      $ids = array_keys($group['values']);
+    }
   }
 
 }
index 91ace3a2e3720fd0983579111f0eb373fc8b690d..09b6d694a8a5aea85ecf0c51b11737e11e8eb629 100644 (file)
@@ -1477,8 +1477,7 @@ class api_v3_RelationshipTest extends CiviUnitTestCase {
    * this from civicrm_generated.mysql
    */
   private function setUpACLByCheating() {
-    CRM_Core_DAO::executeQuery("INSERT INTO civicrm_acl (name, deny, entity_table, entity_id, operation, object_table, object_id, acl_table, acl_id, is_active) VALUES ('Edit All Contacts', 0, 'civicrm_acl_role', 1, 'Edit', 'civicrm_saved_search', 0, NULL, NULL, 1)");
-    CRM_Core_DAO::executeQuery("INSERT INTO civicrm_acl (name, deny, entity_table, entity_id, operation, object_table, object_id, acl_table, acl_id, is_active) VALUES ('Core ACL',0,'civicrm_acl_role',0,'All','access CiviMail subscribe/unsubscribe pages',NULL,NULL,NULL,1)");
+    CRM_Core_DAO::executeQuery("INSERT INTO civicrm_acl (name, deny, entity_table, entity_id, operation, object_table, object_id, acl_table, acl_id, is_active) VALUES ('Edit All Contacts', 0, 'civicrm_acl_role', 1, 'Edit', 'civicrm_group', 0, NULL, NULL, 1)");
   }
 
 }
index 723dd7f7c313f224e4eda8090b010fefbde7fbd8..f824fd346d32d0d04873e1bb1e6691dc23225bbd 100644 (file)
@@ -1811,7 +1811,9 @@ class api_v3_ReportTemplateTest extends CiviUnitTestCase {
    * @param array $ids
    */
   public function aclGroupOnly($type, $contactID, $tableName, $allGroups, &$ids) {
-    $ids = [$this->aclGroupID];
+    if ($tableName === 'civicrm_group') {
+      $ids = [$this->aclGroupID];
+    }
   }
 
   /**
index 0877ac3d468544c89cd53b7e8d576f450b42c50c..afa070c7a54010faf256a1bf14c6437504feb71c 100644 (file)
@@ -14,7 +14,7 @@
 -- Create ACL to edit and view contacts in all groups
 INSERT INTO civicrm_acl (name, deny, entity_table, entity_id, operation, object_table, object_id, acl_table, acl_id, is_active, priority)
 VALUES
-('Edit All Contacts', 0, 'civicrm_acl_role', 1, 'Edit', 'civicrm_saved_search', 0, NULL, NULL, 1, 1);
+('Edit All Contacts', 0, 'civicrm_acl_role', 1, 'Edit', 'civicrm_group', 0, NULL, NULL, 1, 1);
 
 -- Create default Groups for User Permissioning
 INSERT INTO civicrm_group (`id`, `name`, `title`, `description`, `source`, `saved_search_id`, `is_active`, `visibility`, `group_type`)