4 +--------------------------------------------------------------------+
5 | Copyright CiviCRM LLC. All rights reserved. |
7 | This work is published under the GNU AGPLv3 license with some |
8 | permitted exceptions and without any warranty. For full license |
9 | and copyright information, see https://civicrm.org/licensing |
10 +--------------------------------------------------------------------+
16 * @copyright CiviCRM LLC https://civicrm.org/licensing
20 namespace api\v
4\Custom
;
23 use Civi\Api4\Contact
;
24 use Civi\Api4\CustomField
;
25 use Civi\Api4\CustomGroup
;
26 use Civi\Api4\CustomValue
;
31 class CustomGroupACLTest
extends CustomTestBase
{
33 public function tearDown(): void
{
36 ACL
::delete(FALSE)->addWhere('id', '>', 0)->execute();
37 unset(\Civi
::$statics['CRM_Contact_BAO_Contact_Permission']);
38 \CRM_Core_DAO
::executeQuery('TRUNCATE civicrm_acl_contact_cache');
41 public function testViewEditCustomGroupACLs() {
42 $groups = ['readWrite' => 'Edit', 'readOnly' => 'View', 'superSecret' => NULL];
45 foreach ($groups as $name => $access) {
46 $singleGroup = CustomGroup
::create(FALSE)
47 ->addValue('title', 'My' . ucfirst($name) . 'Single')
48 ->addValue('extends', 'Individual')
49 ->addChain('field', CustomField
::create()
50 ->addValue('label', 'MyField')
51 ->addValue('html_type', 'Text')
52 ->addValue('custom_group_id', '$id'), 0)
53 ->execute()->single();
54 $v3['single'][$name] = 'custom_' . $singleGroup['field']['id'];
55 $multiGroup = CustomGroup
::create(FALSE)
56 ->addValue('title', 'My' . ucfirst($name) . 'Multi')
57 ->addValue('extends', 'Individual')
58 ->addValue('is_multiple', TRUE)
59 ->addChain('field', CustomField
::create()
60 ->addValue('label', 'MyField')
61 ->addValue('html_type', 'Text')
62 ->addValue('custom_group_id', '$id'), 0)
63 ->execute()->single();
64 $v3['multi'][$name] = 'custom_' . $multiGroup['field']['id'];
66 ACL
::create(FALSE)->setValues([
67 'name' => $name . 'Single',
69 'operation' => $access,
70 'object_table' => 'civicrm_custom_group',
71 'object_id' => $singleGroup['id'],
73 ACL
::create(FALSE)->setValues([
74 'name' => $name . 'Multi',
76 'operation' => $access,
77 'object_table' => 'civicrm_custom_group',
78 'object_id' => $multiGroup['id'],
83 $this->createLoggedInUser();
84 \CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= ['access all custom data', 'access CiviCRM', 'add contacts'];
86 $cid = Contact
::create()->setValues([
87 'contact_type' => 'Individual',
88 'first_name' => 'test123',
89 'MyReadWriteSingle.MyField' => '123',
90 'MyReadOnlySingle.MyField' => '456',
91 'MySuperSecretSingle.MyField' => '789',
92 ])->execute()->first()['id'];
94 // TEST SINGLE-VALUE CUSTOM GROUPS
96 \CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= ['access CiviCRM', 'view all contacts', 'edit all contacts'];
98 // Ensure ACLs apply to APIv4 get
99 $result = Contact
::get()
100 ->addWhere('id', '=', $cid)
101 ->addSelect('custom.*')
102 ->execute()->single();
103 $this->assertEquals('123', $result['MyReadWriteSingle.MyField']);
104 $this->assertEquals('456', $result['MyReadOnlySingle.MyField']);
105 $this->assertArrayNotHasKey('MySuperSecretSingle.MyField', $result);
107 // Ensure ACLs apply to APIv3 get
108 $result = civicrm_api3('Contact', 'get', [
110 'check_permissions' => 1,
111 'return' => [$v3['single']['readWrite'], $v3['single']['readOnly'], $v3['single']['superSecret']],
113 $this->assertEquals('123', $result[$v3['single']['readWrite']]);
114 $this->assertArrayNotHasKey($v3['single']['superSecret'], $result);
115 $this->assertEquals('456', $result[$v3['single']['readOnly']]);
117 // Try to update all fields - ACLs will restrict based on write access
118 Contact
::update()->setValues([
120 'first_name' => 'test1234',
121 'MyReadWriteSingle.MyField' => '1234',
122 'MyReadOnlySingle.MyField' => '4567',
123 'MySuperSecretSingle.MyField' => '7890',
126 // Verify only first name & readWrite field were altered by APIv4
127 \CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= ['access all custom data', 'access CiviCRM', 'view all contacts', 'edit all contacts'];
128 $result = Contact
::get()
129 ->addWhere('id', '=', $cid)
130 ->addSelect('first_name', 'custom.*')
131 ->execute()->single();
132 $this->assertEquals('test1234', $result['first_name']);
133 $this->assertEquals('1234', $result['MyReadWriteSingle.MyField']);
134 $this->assertEquals('456', $result['MyReadOnlySingle.MyField']);
135 $this->assertEquals('789', $result['MySuperSecretSingle.MyField']);
137 // Try updating all fields with APIv3 - ACLs will restrict based on write access
138 \CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= ['access CiviCRM', 'view all contacts', 'edit all contacts'];
139 civicrm_api3('Contact', 'create', [
140 'check_permissions' => 1,
142 'first_name' => 'test12345',
143 $v3['single']['readWrite'] => '12345',
144 $v3['single']['readOnly'] => '45678',
145 $v3['single']['superSecret'] => '7890!',
148 // Verify only first name & readWrite field were altered by APIv3
149 \CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= ['access all custom data', 'access CiviCRM', 'view all contacts', 'edit all contacts'];
150 $result = Contact
::get()
151 ->addWhere('id', '=', $cid)
152 ->addSelect('first_name', 'custom.*')
153 ->execute()->single();
154 $this->assertEquals('test12345', $result['first_name']);
155 $this->assertEquals('12345', $result['MyReadWriteSingle.MyField']);
156 $this->assertEquals('456', $result['MyReadOnlySingle.MyField']);
157 $this->assertEquals('789', $result['MySuperSecretSingle.MyField']);
159 // TEST MULTI-VALUE CUSTOM GROUPS
162 'MyReadWriteMulti' => ['red', 'blue'],
163 'MyReadOnlyMulti' => ['purple', 'orange'],
164 'MySuperSecretMulti' => ['brown', 'black'],
166 foreach ($multiValues as $groupName => $values) {
167 \CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= ['access all custom data', 'access CiviCRM', 'view all contacts', 'edit all contacts'];
168 foreach ($values as $value) {
169 CustomValue
::create($groupName)
170 ->addValue('MyField', $value)
171 ->addValue('entity_id', $cid)
174 // Check that all but SuperSecret values can be read
176 \CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= ['access CiviCRM', 'view all contacts', 'edit all contacts'];
177 $result = CustomValue
::get($groupName)
178 ->addWhere('entity_id', '=', $cid)
181 if ($groupName === 'MySuperSecretMulti') {
182 $this->fail('API call should have failed');
184 $this->assertEquals($values, $result->column('MyField'));
186 catch (\API_Exception
$e) {
187 if ($groupName !== 'MySuperSecretMulti') {
188 $this->fail('API get should have succeeded');
191 // Check that it works via join also
192 $result = Contact
::get()
193 ->addWhere('id', '=', $cid)
194 ->addJoin("Custom_$groupName AS customGroup")
195 ->addSelect("customGroup.MyField")
197 if ($groupName !== 'MySuperSecretMulti') {
198 $this->assertEquals($values, $result->column("customGroup.MyField"));
201 foreach ($result as $row) {
202 $this->assertArrayNotHasKey("customGroup.MyField", $row);
206 CustomValue
::create($groupName)
207 ->addValue('MyField', 'new')
208 ->addValue('entity_id', $cid)
210 if ($groupName !== 'MyReadWriteMulti') {
211 $this->fail('API call should have failed');
214 catch (\API_Exception
$e) {
215 if ($groupName === 'MyReadWriteMulti') {
216 $this->fail('API create should have succeeded');
220 // Try updating with APIv3
221 civicrm_api3('Contact', 'create', [
222 'check_permissions' => 1,
224 'first_name' => 'test12345',
225 // Should update the first record in the "readWrite" group
226 $v3['multi']['readWrite'] . '_1' => 'changed1',
227 // These 2 updates should fail due to ACLs
228 $v3['multi']['readOnly'] . '_1' => 'changed2',
229 $v3['multi']['superSecret'] . '_1' => 'changed3',
232 // Ensure only readWrite group has been modified
233 \CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= ['access all custom data', 'access CiviCRM', 'view all contacts', 'edit all contacts'];
235 'MyReadWriteMulti' => ['changed1', 'blue', 'new'],
236 'MyReadOnlyMulti' => ['purple', 'orange'],
237 'MySuperSecretMulti' => ['brown', 'black'],
239 foreach ($expectedValues as $groupName => $expected) {
240 $result = CustomValue
::get($groupName)
241 ->addWhere('entity_id', '=', $cid)
244 $this->assertEquals($expected, $result->column('MyField'));