From 781b0f89de4ec7d7fa9a03c5592fdfada9d4aee1 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Mon, 15 Aug 2022 17:15:01 -0400 Subject: [PATCH] APIv4 - Expand Group.create permissions to include CiviMail Fixes dev/core#3755 Before: Creating a new group required 'edit groups' permission. After: When creating a 'Mailing List' group, either 'edit groups' OR 'access CiviMail' OR 'create mailings' will work. This allows users without 'edit groups' permission to still create a mailing from SearchKit. --- CRM/Contact/BAO/Group.php | 30 ++++++++ Civi/Api4/Group.php | 15 ++++ tests/phpunit/api/v4/Entity/GroupTest.php | 87 +++++++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 tests/phpunit/api/v4/Entity/GroupTest.php diff --git a/CRM/Contact/BAO/Group.php b/CRM/Contact/BAO/Group.php index b7e0397610..b5db2bc37f 100644 --- a/CRM/Contact/BAO/Group.php +++ b/CRM/Contact/BAO/Group.php @@ -1411,4 +1411,34 @@ WHERE {$whereClause}"; return reset($parentArray); } + /** + * @param string $entityName + * @param string $action + * @param array $record + * @param $userID + * @return bool + * @see CRM_Core_DAO::checkAccess + */ + public static function _checkAccess(string $entityName, string $action, array $record, $userID): bool { + switch ($action) { + case 'create': + $groupType = (array) ($record['group_type:name'] ?? []); + // If not already in :name format, transform to name + foreach ((array) ($record['group_type'] ?? []) as $typeId) { + $groupType[] = CRM_Core_PseudoConstant::getName(self::class, 'group_type', $typeId); + } + if ($groupType === ['Mailing List']) { + // If it's only a Mailing List, edit groups OR create mailings will work + return CRM_Core_Permission::check(['access CiviCRM', ['edit groups', 'access CiviMail', 'create mailings']], $userID); + } + else { + return CRM_Core_Permission::check(['access CiviCRM', 'edit groups'], $userID); + } + + default: + // All other actions just rely on gatekeeper permissions + return TRUE; + } + } + } diff --git a/Civi/Api4/Group.php b/Civi/Api4/Group.php index e92567a9d4..67a34edeef 100644 --- a/Civi/Api4/Group.php +++ b/Civi/Api4/Group.php @@ -22,4 +22,19 @@ namespace Civi\Api4; class Group extends Generic\DAOEntity { use Generic\Traits\ManagedEntity; + /** + * Provides more-open permissions that will be further restricted by checkAccess + * + * @see \CRM_Contact_BAO_Group::_checkAccess() + * @return array + */ + public static function permissions():array { + $permissions = parent::permissions(); + + return [ + // Create permission depends on the group type (see CRM_Contact_BAO_Group::_checkAccess). + 'create' => ['access CiviCRM', ['edit groups', 'access CiviMail', 'create mailings']], + ] + $permissions; + } + } diff --git a/tests/phpunit/api/v4/Entity/GroupTest.php b/tests/phpunit/api/v4/Entity/GroupTest.php new file mode 100644 index 0000000000..d34d5bbbaf --- /dev/null +++ b/tests/phpunit/api/v4/Entity/GroupTest.php @@ -0,0 +1,87 @@ +createLoggedInUser(); + \CRM_Core_Config::singleton()->userPermissionClass->permissions = [ + 'access CiviCRM', + 'edit groups', + ]; + + $types = array_flip(\CRM_Contact_BAO_Group::buildOptions('group_type')); + + Group::create(TRUE) + ->addValue('title', uniqid()) + ->addValue('group_type:name', 'Access Control') + ->execute(); + + \CRM_Core_Config::singleton()->userPermissionClass->permissions = [ + 'access CiviCRM', + 'create mailings', + ]; + + // Cannot create any group other than ['Mailing List'] without 'edit groups' + try { + Group::create(TRUE) + ->addValue('title', uniqid()) + ->addValue('group_type:name', 'Access Control') + ->execute(); + $this->fail(); + } + catch (UnauthorizedException $e) { + } + try { + Group::create(TRUE) + ->addValue('title', uniqid()) + ->execute(); + $this->fail(); + } + catch (UnauthorizedException $e) { + } + + // Can create a mailing group without 'edit groups' + Group::create(TRUE) + ->addValue('title', uniqid()) + ->addValue('group_type', [$types['Mailing List']]) + ->execute(); + + \CRM_Core_Config::singleton()->userPermissionClass->permissions = [ + 'access CiviCRM', + 'access CiviMail', + ]; + + // Also works with pseudoconstant notation + Group::create(TRUE) + ->addValue('title', uniqid()) + ->addValue('group_type:name', 'Mailing List') + ->execute(); + } + +} -- 2.25.1