Merge pull request #13984 from seamuslee001/nfc_comment_fix_ang
[civicrm-core.git] / tests / phpunit / CRM / ACL / ListTest.php
CommitLineData
ea8011f6 1<?php
2
3/**
4 * Class CRM_ACL_Test
5 *
6 * This test focuses on testing the (new) ID list-based functions:
7 * CRM_Contact_BAO_Contact_Permission::allowList()
8 * CRM_Contact_BAO_Contact_Permission::relationshipList()
9 * @group headless
10 */
11class CRM_ACL_ListTest extends CiviUnitTestCase {
12
13 /**
14 * Set up function.
15 */
16 public function setUp() {
17 parent::setUp();
3c645834 18 // $this->quickCleanup(array('civicrm_acl_contact_cache'), TRUE);
ea8011f6 19 $this->useTransaction(TRUE);
c1ebd31f 20 $this->allowedContactsACL = array();
ea8011f6 21 }
22
23 /**
24 * general test for the 'view all contacts' permission
25 */
26 public function testViewAllPermission() {
e4541c56 27 // create test contacts
134b2b64 28 $contacts = $this->createScenarioPlain();
ea8011f6 29
134b2b64 30 // test WITH all permissions
340be2e7 31 CRM_Core_Config::singleton()->userPermissionClass->permissions = NULL; // NULL means 'all permissions' in UnitTests environment
ea8011f6 32 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts);
134b2b64 33 sort($result);
34 $this->assertEquals($result, $contacts, "Contacts should be viewable when 'view all contacts'");
ea8011f6 35
ea8011f6 36 // test WITH explicit permission
37 CRM_Core_Config::singleton()->userPermissionClass->permissions = array('view all contacts');
38 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts, CRM_Core_Permission::VIEW);
134b2b64 39 sort($result);
40 $this->assertEquals($result, $contacts, "Contacts should be viewable when 'view all contacts'");
ea8011f6 41
340be2e7 42 // test WITH EDIT permissions (should imply VIEW)
43 CRM_Core_Config::singleton()->userPermissionClass->permissions = array('edit all contacts');
44 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts, CRM_Core_Permission::VIEW);
45 sort($result);
46 $this->assertEquals($result, $contacts, "Contacts should be viewable when 'edit all contacts'");
47
ea8011f6 48 // test WITHOUT permission
49 CRM_Core_Config::singleton()->userPermissionClass->permissions = array();
50 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts);
134b2b64 51 sort($result);
ea8011f6 52 $this->assertEmpty($result, "Contacts should NOT be viewable when 'view all contacts' is not set");
53 }
54
55
56 /**
57 * general test for the 'view all contacts' permission
58 */
59 public function testEditAllPermission() {
60 // create test contacts
134b2b64 61 $contacts = $this->createScenarioPlain();
ea8011f6 62
63 // test WITH explicit permission
64 CRM_Core_Config::singleton()->userPermissionClass->permissions = array('edit all contacts');
65 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts, CRM_Core_Permission::EDIT);
134b2b64 66 sort($result);
67 $this->assertEquals($result, $contacts, "Contacts should be viewable when 'edit all contacts'");
ea8011f6 68
ea8011f6 69 // test WITHOUT permission
70 CRM_Core_Config::singleton()->userPermissionClass->permissions = array();
71 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts);
134b2b64 72 sort($result);
ea8011f6 73 $this->assertEmpty($result, "Contacts should NOT be viewable when 'edit all contacts' is not set");
74 }
75
76
77 /**
134b2b64 78 * Test access related to the 'access deleted contact' permission
ea8011f6 79 */
80 public function testViewEditDeleted() {
134b2b64 81 // create test contacts
82 $contacts = $this->createScenarioPlain();
83
84 // delete one contact
85 $deleted_contact_id = $contacts[2];
86 $this->callAPISuccess('Contact', 'create', array('id' => $deleted_contact_id, 'contact_is_deleted' => 1));
87 $deleted_contact = $this->callAPISuccess('Contact', 'getsingle', array('id' => $deleted_contact_id));
88 $this->assertEquals($deleted_contact['contact_is_deleted'], 1, "Contact should've been deleted");
89
90 // test WITH explicit permission
ea8011f6 91 CRM_Core_Config::singleton()->userPermissionClass->permissions = array('edit all contacts', 'view all contacts');
134b2b64 92 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts, CRM_Core_Permission::EDIT);
93 sort($result);
94 $this->assertNotContains($deleted_contact_id, $result, "Deleted contacts should be excluded");
e4541c56 95 $this->assertEquals(count($result), count($contacts) - 1, "Only deleted contacts should be excluded");
134b2b64 96 }
97
ea8011f6 98
134b2b64 99 /**
c1ebd31f 100 * Test access based on relations
e4541c56 101 *
134b2b64 102 * There should be the following permission-relationship
103 * contact[0] -> contact[1] -> contact[2]
104 */
105 public function testPermissionByRelation() {
106 // create test scenario
c1ebd31f 107 $contacts = $this->createScenarioRelations();
134b2b64 108
109 // remove all permissions
110 $config = CRM_Core_Config::singleton();
111 $config->userPermissionClass->permissions = array();
112 $permissions_to_check = array(CRM_Core_Permission::VIEW => 'View', CRM_Core_Permission::EDIT => 'Edit');
113
114 // run this for SIMPLE relations
115 $config->secondDegRelPermissions = FALSE;
116 $this->assertFalse($config->secondDegRelPermissions);
117 foreach ($permissions_to_check as $permission => $permission_label) {
118 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts, $permission);
119 sort($result);
120
c1ebd31f 121 $this->assertNotContains($contacts[0], $result, "User[0] should NOT have $permission_label permission on contact[0].");
e4541c56 122 $this->assertContains($contacts[1], $result, "User[0] should have $permission_label permission on contact[1].");
c1ebd31f 123 $this->assertNotContains($contacts[2], $result, "User[0] should NOT have $permission_label permission on contact[2].");
124 $this->assertNotContains($contacts[3], $result, "User[0] should NOT have $permission_label permission on contact[3].");
125 $this->assertNotContains($contacts[4], $result, "User[0] should NOT have $permission_label permission on contact[4].");
aade61cd
AS
126 // view (b_a)
127 if ($permission == CRM_Core_Permission::VIEW) {
128 $this->assertContains($contacts[5], $result, "User[0] should have $permission_label permission on contact[5].");
129 }
130 else {
131 $this->assertNotContains($contacts[5], $result, "User[0] should NOT have $permission_label permission on contact[5].");
132 }
133 $this->assertNotContains($contacts[6], $result, "User[0] should NOT have $permission_label permission on contact[6].");
134 $this->assertNotContains($contacts[7], $result, "User[0] should NOT have $permission_label permission on contact[7].");
135 // edit (a_b)
136 $this->assertContains($contacts[8], $result, "User[0] should have $permission_label permission on contact[8].");
137 $this->assertNotContains($contacts[9], $result, "User[0] should NOT have $permission_label permission on contact[9].");
134b2b64 138 }
e4541c56 139
134b2b64 140 // run this for SECOND DEGREE relations
141 $config->secondDegRelPermissions = TRUE;
142 $this->assertTrue($config->secondDegRelPermissions);
143 foreach ($permissions_to_check as $permission => $permission_label) {
144 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts, $permission);
145 sort($result);
146
aade61cd
AS
147 $this->assertNotContains($contacts[0], $result, "User[0] should NOT have second degree $permission_label permission on contact[0].");
148 $this->assertContains($contacts[1], $result, "User[0] should have second degree $permission_label permission on contact[1].");
149 // Edit then edit -> edit
e4541c56 150 $this->assertContains($contacts[2], $result, "User[0] should have second degree $permission_label permission on contact[2].");
aade61cd
AS
151 $this->assertNotContains($contacts[3], $result, "User[0] should NOT have second degree $permission_label permission on contact[3].");
152 $this->assertNotContains($contacts[4], $result, "User[0] should NOT have second degree $permission_label permission on contact[4].");
153 // View then Edit -> View
154 if ($permission == CRM_Core_Permission::VIEW) {
155 $this->assertContains($contacts[5], $result, "User[0] should have second degree $permission_label permission on contact[5].");
156 $this->assertContains($contacts[6], $result, "User[0] should have second degree $permission_label permission on contact[6].");
157 }
158 else {
159 $this->assertNotContains($contacts[5], $result, "User[0] should NOT have second degree $permission_label permission on contact[5].");
160 $this->assertNotContains($contacts[6], $result, "User[0] should NOT have second degree $permission_label permission on contact[6].");
161 }
162 // View then Edit -> View
163 if ($permission == CRM_Core_Permission::VIEW) {
164 $this->assertContains($contacts[7], $result, "User[0] should have second degree $permission_label permission on contact[7].");
165 }
166 else {
167 $this->assertNotContains($contacts[7], $result, "User[0] should NOT have second degree $permission_label permission on contact[7].");
168 }
169 // Edit then View -> View
170 $this->assertContains($contacts[8], $result, "User[0] should have second degree $permission_label permission on contact[8].");
171 if ($permission == CRM_Core_Permission::VIEW) {
172 $this->assertContains($contacts[9], $result, "User[0] should have second degree $permission_label permission on contact[9].");
173 }
174 else {
175 $this->assertNotContains($contacts[9], $result, "User[0] should NOT have second degree $permission_label permission on contact[9].");
176 }
134b2b64 177 }
ea8011f6 178 }
179
180
134b2b64 181 /**
c1ebd31f 182 * Test access based on ACL
134b2b64 183 */
c1ebd31f 184 public function testPermissionByACL() {
185 $contacts = $this->createScenarioPlain();
186
187 // set custom hook
188 $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'hook_civicrm_aclWhereClause'));
189
190 // run simple test
191 $permissions_to_check = array(CRM_Core_Permission::VIEW => 'View', CRM_Core_Permission::EDIT => 'Edit');
192
193 $this->allowedContactsACL = array($contacts[0], $contacts[1], $contacts[4]);
194 CRM_Core_Config::singleton()->userPermissionClass->permissions = array();
195 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts);
196 sort($result);
197
e4541c56 198 $this->assertContains($contacts[0], $result, "User[0] should NOT have an ACL permission on contact[0].");
199 $this->assertContains($contacts[1], $result, "User[0] should have an ACL permission on contact[1].");
c1ebd31f 200 $this->assertNotContains($contacts[2], $result, "User[0] should NOT have an ACL permission on contact[2].");
201 $this->assertNotContains($contacts[3], $result, "User[0] should NOT have an RELATION permission on contact[3].");
e4541c56 202 $this->assertContains($contacts[4], $result, "User[0] should NOT have an ACL permission on contact[4].");
134b2b64 203 }
ea8011f6 204
c1ebd31f 205
134b2b64 206 /**
c1ebd31f 207 * Test access with a mix of ACL and relationship
134b2b64 208 */
c1ebd31f 209 public function testPermissionACLvsRelationship() {
210 $contacts = $this->createScenarioRelations();
211
212 // set custom hook
213 $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'hook_civicrm_aclWhereClause'));
214
215 $config = CRM_Core_Config::singleton();
216 $config->userPermissionClass->permissions = array();
217 $config->secondDegRelPermissions = TRUE;
218
219 $this->allowedContactsACL = array($contacts[0], $contacts[1], $contacts[4]);
220 CRM_Core_Config::singleton()->userPermissionClass->permissions = array();
221 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts);
222 sort($result);
223
e4541c56 224 $this->assertContains($contacts[0], $result, "User[0] should have an ACL permission on contact[0].");
225 $this->assertContains($contacts[1], $result, "User[0] should have an ACL permission on contact[1].");
226 $this->assertContains($contacts[2], $result, "User[0] should have second degree an relation permission on contact[2].");
c1ebd31f 227 $this->assertNotContains($contacts[3], $result, "User[0] should NOT have an ACL permission on contact[3].");
e4541c56 228 $this->assertContains($contacts[4], $result, "User[0] should have an ACL permission on contact[4].");
134b2b64 229 }
ea8011f6 230
134b2b64 231 /**
232 * Test access related to the 'access deleted contact' permission
233 */
3c645834 234 public function testPermissionCompare() {
235 $contacts = $this->createScenarioRelations();
236 $contact_index = array_flip($contacts);
237
238 // set custom hook
239 $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'hook_civicrm_aclWhereClause'));
240
241 $config = CRM_Core_Config::singleton();
242 $this->allowedContactsACL = array($contacts[0], $contacts[1], $contacts[4]);
243 $config->secondDegRelPermissions = TRUE;
244
245 // test configurations
246 $permissions_to_check = array(CRM_Core_Permission::VIEW => 'View', CRM_Core_Permission::EDIT => 'Edit');
247 $user_permission_options = array(/*ALL*/ NULL, /*NONE*/ array(), array('view all contacts'), array('edit all contacts'), array('view all contacts', 'edit all contacts'));
248
249 // run all combinations of those
250 foreach ($permissions_to_check as $permission_to_check => $permission_label) {
251 foreach ($user_permission_options as $user_permissions) {
252 // select the contact range
253 $contact_range = $contacts;
e4541c56 254 if (is_array($user_permissions) && count($user_permissions) == 0) {
3c645834 255 // slight (explainable) deviation on the own contact
256 unset($contact_range[0]);
257 }
258
259 $config->userPermissionClass->permissions = $user_permissions;
260 $user_permissions_label = json_encode($user_permissions);
261
262 // get the list result
263 $list_result = CRM_Contact_BAO_Contact_Permission::allowList($contact_range, $permission_to_check);
264 $this->assertTrue(count($list_result) <= count($contact_range), "Permission::allowList should return a subset of the contats.");
265 foreach ($list_result as $contact_id) {
266 $this->assertContains($contact_id, $contact_range, "Permission::allowList should return a subset of the contats.");
267 }
268
269 // now compare the results
270 foreach ($contact_range as $contact_id) {
271 $individual_result = CRM_Contact_BAO_Contact_Permission::allow($contact_id, $permission_to_check);
272
273 if (in_array($contact_id, $list_result)) {
274 // listPermission reports PERMISSION GRANTED
275 $this->assertTrue($individual_result, "Permission::allow denies {$permission_label} access for contact[{$contact_index[$contact_id]}], while Permission::allowList grants it. User permission: '{$user_permissions_label}'");
276
e4541c56 277 }
278 else {
3c645834 279 // listPermission reports PERMISSION DENIED
280 $this->assertFalse($individual_result, "Permission::allow grantes {$permission_label} access for contact[{$contact_index[$contact_id]}], while Permission::allowList denies it. User permission: '{$user_permissions_label}'");
281
282 }
283 }
284 }
285 }
134b2b64 286 }
ea8011f6 287
288
134b2b64 289 /****************************************************
290 * Scenario Builders *
291 ***************************************************/
ea8011f6 292
293 /**
134b2b64 294 * create plain test scenario, no relationships/ACLs
ea8011f6 295 */
134b2b64 296 protected function createScenarioPlain() {
ea8011f6 297 // get logged in user
298 $user_id = $this->createLoggedInUser();
299 $this->assertNotEmpty($user_id);
300
301 // create test contacts
aade61cd
AS
302 $bush_sr_id = $this->individualCreate(array('first_name' => 'George', 'middle_name' => 'H. W.', 'last_name' => 'Bush'));
303 $bush_jr_id = $this->individualCreate(array('first_name' => 'George', 'middle_name' => 'W.', 'last_name' => 'Bush'));
ea8011f6 304 $bush_laura_id = $this->individualCreate(array('first_name' => 'Laura Lane', 'last_name' => 'Bush'));
305 $bush_brbra_id = $this->individualCreate(array('first_name' => 'Barbara', 'last_name' => 'Bush'));
aade61cd
AS
306 $bush_brother_id = $this->individualCreate(array('first_name' => 'Brother', 'last_name' => 'Bush'));
307 $bush_nephew_id = $this->individualCreate(array('first_name' => 'Nephew', 'last_name' => 'Bush'));
308 $bush_nephew2_id = $this->individualCreate(array('first_name' => 'Nephew2', 'last_name' => 'Bush'));
309 $bush_otherbro_id = $this->individualCreate(array('first_name' => 'Other Brother', 'last_name' => 'Bush'));
310 $bush_otherneph_id = $this->individualCreate(array('first_name' => 'Other Nephew', 'last_name' => 'Bush'));
ea8011f6 311
aade61cd 312 $contacts = array($user_id, $bush_sr_id, $bush_jr_id, $bush_laura_id, $bush_brbra_id, $bush_brother_id, $bush_nephew_id, $bush_nephew2_id, $bush_otherbro_id, $bush_otherneph_id);
134b2b64 313 sort($contacts);
314 return $contacts;
315 }
316
317 /**
318 * create plain test scenario, no relationships/ACLs
319 */
c1ebd31f 320 protected function createScenarioRelations() {
134b2b64 321 $contacts = $this->createScenarioPlain();
322
ea8011f6 323 // create some relationships
324 $this->callAPISuccess('Relationship', 'create', array(
e4541c56 325 'relationship_type_id' => 1, // CHILD OF
134b2b64 326 'contact_id_a' => $contacts[1],
327 'contact_id_b' => $contacts[0],
328 'is_permission_b_a' => 1,
329 'is_active' => 1,
ea8011f6 330 ));
331
332 $this->callAPISuccess('Relationship', 'create', array(
e4541c56 333 'relationship_type_id' => 1, // CHILD OF
134b2b64 334 'contact_id_a' => $contacts[2],
335 'contact_id_b' => $contacts[1],
336 'is_permission_b_a' => 1,
337 'is_active' => 1,
ea8011f6 338 ));
339
ea8011f6 340 $this->callAPISuccess('Relationship', 'create', array(
e4541c56 341 'relationship_type_id' => 1, // CHILD OF
134b2b64 342 'contact_id_a' => $contacts[4],
343 'contact_id_b' => $contacts[2],
344 'is_permission_b_a' => 1,
345 'is_active' => 1,
ea8011f6 346 ));
347
aade61cd
AS
348 $this->callAPISuccess('Relationship', 'create', array(
349 'relationship_type_id' => 4, // SIBLING OF
350 'contact_id_a' => $contacts[5],
351 'contact_id_b' => $contacts[0],
352 'is_permission_b_a' => 2, // View
353 'is_active' => 1,
354 ));
355
356 $this->callAPISuccess('Relationship', 'create', array(
357 'relationship_type_id' => 1, // CHILD OF
358 'contact_id_a' => $contacts[6],
359 'contact_id_b' => $contacts[5],
360 'is_permission_b_a' => 1, // Edit
361 'is_active' => 1,
362 ));
363
364 $this->callAPISuccess('Relationship', 'create', array(
365 'relationship_type_id' => 1, // CHILD OF
366 'contact_id_a' => $contacts[7],
367 'contact_id_b' => $contacts[5],
368 'is_permission_b_a' => 2, // View
369 'is_active' => 1,
370 ));
371
372 $this->callAPISuccess('Relationship', 'create', array(
373 'relationship_type_id' => 4, // SIBLING OF
374 'contact_id_a' => $contacts[0],
375 'contact_id_b' => $contacts[8],
376 'is_permission_a_b' => 1, // edit (as a_b)
377 'is_active' => 1,
378 ));
379
380 $this->callAPISuccess('Relationship', 'create', array(
381 'relationship_type_id' => 1, // CHILD OF
382 'contact_id_a' => $contacts[9],
383 'contact_id_b' => $contacts[8],
384 'is_permission_b_a' => 2, // view
385 'is_active' => 1,
386 ));
387
134b2b64 388 return $contacts;
ea8011f6 389 }
c1ebd31f 390
e4541c56 391 /**
392 * ACL HOOK implementation for various tests
c1ebd31f 393 */
e4541c56 394 public function hook_civicrm_aclWhereClause($type, &$tables, &$whereTables, &$contactID, &$where) {
c1ebd31f 395 if (!empty($this->allowedContactsACL)) {
396 $contact_id_list = implode(',', $this->allowedContactsACL);
397 $where = " contact_a.id IN ($contact_id_list)";
398 }
399 }
e4541c56 400
ea8011f6 401}