From 2bf243614e0b8622e24d767c7f39f3541cc89908 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Sun, 28 Nov 2021 16:14:42 -0500 Subject: [PATCH] APIv4 - Limit SortableEntity exports by domain --- CRM/Admin/Form/Options.php | 4 +-- CRM/Campaign/Form/SurveyType.php | 2 +- CRM/Core/BAO/OptionValue.php | 4 +-- CRM/Core/OptionGroup.php | 16 +++++++++--- CRM/Core/OptionValue.php | 4 +-- Civi/Api4/Generic/ExportAction.php | 25 ++++++++++++++++--- .../api/v4/Entity/ManagedEntityTest.php | 5 +++- 7 files changed, 45 insertions(+), 15 deletions(-) diff --git a/CRM/Admin/Form/Options.php b/CRM/Admin/Form/Options.php index 123eee121f..dc9fe05768 100644 --- a/CRM/Admin/Form/Options.php +++ b/CRM/Admin/Form/Options.php @@ -69,7 +69,7 @@ class CRM_Admin_Form_Options extends CRM_Admin_Form { 'name' ); $this->_gLabel = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', $this->_gid, 'title'); - $this->_domainSpecific = in_array($this->_gName, CRM_Core_OptionGroup::$_domainIDGroups); + $this->_domainSpecific = CRM_Core_OptionGroup::isDomainOptionGroup($this->_gName); $url = "civicrm/admin/options/{$this->_gName}"; $params = "reset=1"; @@ -96,7 +96,7 @@ class CRM_Admin_Form_Options extends CRM_Admin_Form { $session->pushUserContext(CRM_Utils_System::url($url, $params)); $this->assign('id', $this->_id); - if ($this->_id && in_array($this->_gName, CRM_Core_OptionGroup::$_domainIDGroups)) { + if ($this->_id && CRM_Core_OptionGroup::isDomainOptionGroup($this->_gName)) { $domainID = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', $this->_id, 'domain_id', 'id'); if (CRM_Core_Config::domainID() != $domainID) { CRM_Core_Error::statusBounce(ts('You do not have permission to access this page.')); diff --git a/CRM/Campaign/Form/SurveyType.php b/CRM/Campaign/Form/SurveyType.php index 08c0344169..7138304059 100644 --- a/CRM/Campaign/Form/SurveyType.php +++ b/CRM/Campaign/Form/SurveyType.php @@ -58,7 +58,7 @@ class CRM_Campaign_Form_SurveyType extends CRM_Admin_Form { $url = CRM_Utils_System::url('civicrm/admin/campaign/surveyType', 'reset=1'); $session->pushUserContext($url); - if ($this->_id && in_array($this->_gName, CRM_Core_OptionGroup::$_domainIDGroups)) { + if ($this->_id && CRM_Core_OptionGroup::isDomainOptionGroup($this->_gName)) { $domainID = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', $this->_id, 'domain_id', 'id'); if (CRM_Core_Config::domainID() != $domainID) { CRM_Core_Error::statusBounce(ts('You do not have permission to access this page.')); diff --git a/CRM/Core/BAO/OptionValue.php b/CRM/Core/BAO/OptionValue.php index 6c1901caf9..69d78bf677 100644 --- a/CRM/Core/BAO/OptionValue.php +++ b/CRM/Core/BAO/OptionValue.php @@ -169,7 +169,7 @@ class CRM_Core_BAO_OptionValue extends CRM_Core_DAO_OptionValue { $optionValue = new CRM_Core_DAO_OptionValue(); $optionValue->copyValues($params); - $isDomainOptionGroup = in_array($groupName, CRM_Core_OptionGroup::$_domainIDGroups); + $isDomainOptionGroup = CRM_Core_OptionGroup::isDomainOptionGroup($groupName); // When creating a new option for a group that requires a domain, set default domain if ($isDomainOptionGroup && empty($params['id']) && (empty($params['domain_id']) || CRM_Utils_System::isNull($params['domain_id']))) { $optionValue->domain_id = CRM_Core_Config::domainID(); @@ -215,7 +215,7 @@ class CRM_Core_BAO_OptionValue extends CRM_Core_DAO_OptionValue { // CRM-21737 languages option group does not use unique values but unique names. $dao->name = $params['name']; } - if (in_array($groupName, CRM_Core_OptionGroup::$_domainIDGroups)) { + if (CRM_Core_OptionGroup::isDomainOptionGroup($groupName)) { $dao->domain_id = $optionValue->domain_id; } $dao->option_group_id = $params['option_group_id']; diff --git a/CRM/Core/OptionGroup.php b/CRM/Core/OptionGroup.php index 6215f8e690..0b2b3653d5 100644 --- a/CRM/Core/OptionGroup.php +++ b/CRM/Core/OptionGroup.php @@ -21,6 +21,8 @@ class CRM_Core_OptionGroup { /** * $_domainIDGroups array maintains the list of option groups for whom * domainID is to be considered. + * + * FIXME: Hardcoded list = bad. It would be better to make this a column in the civicrm_option_group table * @var array */ public static $_domainIDGroups = [ @@ -28,6 +30,14 @@ class CRM_Core_OptionGroup { 'grant_type', ]; + /** + * @param $groupName + * @return bool + */ + public static function isDomainOptionGroup($groupName) { + return in_array($groupName, self::$_domainIDGroups, TRUE); + } + /** * @param CRM_Core_DAO $dao * @param bool $flip @@ -106,7 +116,7 @@ class CRM_Core_OptionGroup { $orderBy = 'weight' ) { $cache = CRM_Utils_Cache::singleton(); - if (in_array($name, self::$_domainIDGroups)) { + if (self::isDomainOptionGroup($name)) { $cacheKey = self::createCacheKey($name, CRM_Core_I18n::getLocale(), $flip, $grouping, $localize, $condition, $labelColumnName, $onlyActive, $keyColumnName, $orderBy, CRM_Core_Config::domainID()); } else { @@ -144,7 +154,7 @@ WHERE v.option_group_id = g.id } $query .= " AND ($componentClause) "; } - if (in_array($name, self::$_domainIDGroups)) { + if (self::isDomainOptionGroup($name)) { $query .= " AND v.domain_id = " . CRM_Core_Config::domainID(); } @@ -433,7 +443,7 @@ WHERE v.option_group_id = g.id AND g.is_active = 1 AND v.is_default = 1 "; - if (in_array($groupName, self::$_domainIDGroups)) { + if (self::isDomainOptionGroup($groupName)) { $query .= " AND v.domain_id = " . CRM_Core_Config::domainID(); } diff --git a/CRM/Core/OptionValue.php b/CRM/Core/OptionValue.php index 409e73fc95..e1219e982e 100644 --- a/CRM/Core/OptionValue.php +++ b/CRM/Core/OptionValue.php @@ -82,7 +82,7 @@ class CRM_Core_OptionValue { if ($optionGroupID) { $dao->option_group_id = $optionGroupID; - if (in_array($groupName, CRM_Core_OptionGroup::$_domainIDGroups)) { + if (CRM_Core_OptionGroup::isDomainOptionGroup($groupName)) { $dao->domain_id = CRM_Core_Config::domainID(); } @@ -438,7 +438,7 @@ FROM $params[2] = [$groupName, 'String']; } - if (in_array($groupName, CRM_Core_OptionGroup::$_domainIDGroups)) { + if (CRM_Core_OptionGroup::isDomainOptionGroup($groupName)) { $where .= " AND option_value.domain_id = " . CRM_Core_Config::domainID(); } diff --git a/Civi/Api4/Generic/ExportAction.php b/Civi/Api4/Generic/ExportAction.php index a935eda87a..5c6869ffbf 100644 --- a/Civi/Api4/Generic/ExportAction.php +++ b/Civi/Api4/Generic/ExportAction.php @@ -110,13 +110,21 @@ class ExportAction extends AbstractAction { $record[$fieldName] = NULL; } } - // Special handing of current_domain + // Should references be limited to the current domain? + $limitRefsByDomain = $entityType === 'OptionGroup' && \CRM_Core_OptionGroup::isDomainOptionGroup($record['name']) ? \CRM_Core_BAO_Domain::getDomain()->id : FALSE; foreach ($allFields as $fieldName => $field) { if (($field['fk_entity'] ?? NULL) === 'Domain') { $alias = $fieldName . '.name'; - if (isset($record[$alias]) && $record[$alias] === \CRM_Core_BAO_Domain::getDomain()->name) { - unset($record[$alias]); - $record[$fieldName] = 'current_domain'; + if (isset($record[$alias])) { + // If this entity is for a specific domain, limit references to that same domain + if ($fieldName === 'domain_id') { + $limitRefsByDomain = \CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Domain', $record[$alias], 'id', 'name'); + } + // Swap current domain for special API keyword + if ($record[$alias] === \CRM_Core_BAO_Domain::getDomain()->name) { + unset($record[$alias]); + $record[$fieldName] = 'current_domain'; + } } } } @@ -140,6 +148,15 @@ class ExportAction extends AbstractAction { $references = []; foreach ($dao->findReferences() as $reference) { $refEntity = \CRM_Utils_Array::first($reference::fields())['entity'] ?? ''; + // Limit references by domain + if (property_exists($reference, 'domain_id')) { + if (!isset($reference->domain_id)) { + $reference->find(TRUE); + } + if (isset($reference->domain_id) && $reference->domain_id != $limitRefsByDomain) { + continue; + } + } $references[$refEntity][] = $reference; } foreach ($references as $refEntity => $records) { diff --git a/tests/phpunit/api/v4/Entity/ManagedEntityTest.php b/tests/phpunit/api/v4/Entity/ManagedEntityTest.php index 8d185581c6..844c578e97 100644 --- a/tests/phpunit/api/v4/Entity/ManagedEntityTest.php +++ b/tests/phpunit/api/v4/Entity/ManagedEntityTest.php @@ -359,9 +359,12 @@ class ManagedEntityTest extends UnitTestCase implements TransactionalInterface, ->addChain('export', OptionGroup::export()->setId('$id')) ->execute()->first(); $this->assertEquals('from_email_address', $result['export'][1]['params']['values']['option_group_id.name']); - $this->assertEquals('current_domain', $result['export'][1]['params']['values']['domain_id']); $this->assertNull($result['export'][1]['params']['values']['visibility_id']); $this->assertStringStartsWith('OptionGroup_from_email_address_OptionValue_', $result['export'][1]['name']); + // All references should be from the current domain + foreach (array_slice($result['export'], 1) as $reference) { + $this->assertEquals('current_domain', $reference['params']['values']['domain_id']); + } } public function testManagedNavigationWeights() { -- 2.25.1