Merge pull request #16469 from civicrm/5.22
[civicrm-core.git] / tests / phpunit / api / v3 / ACLPermissionTest.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
10 */
11
12 /**
13 * This class is intended to test ACL permission using the multisite module
14 *
15 * @package CiviCRM_APIv3
16 * @subpackage API_Contact
17 * @group headless
18 */
19 class api_v3_ACLPermissionTest extends CiviUnitTestCase {
20
21 use CRMTraits_ACL_PermissionTrait;
22
23 public $DBResetRequired = FALSE;
24 protected $_entity;
25
26 public function setUp() {
27 parent::setUp();
28 $baoObj = new CRM_Core_DAO();
29 $baoObj->createTestObject('CRM_Pledge_BAO_Pledge', [], 1, 0);
30 $baoObj->createTestObject('CRM_Core_BAO_Phone', [], 1, 0);
31 $this->prepareForACLs();
32 }
33
34 /**
35 * (non-PHPdoc)
36 * @see CiviUnitTestCase::tearDown()
37 */
38 public function tearDown() {
39 $this->cleanUpAfterACLs();
40 $tablesToTruncate = [
41 'civicrm_contact',
42 'civicrm_group_contact',
43 'civicrm_group',
44 'civicrm_acl',
45 'civicrm_acl_cache',
46 'civicrm_acl_entity_role',
47 'civicrm_acl_contact_cache',
48 'civicrm_contribution',
49 'civicrm_participant',
50 'civicrm_uf_match',
51 'civicrm_activity',
52 'civicrm_activity_contact',
53 'civicrm_note',
54 'civicrm_entity_tag',
55 'civicrm_tag',
56 ];
57 $this->quickCleanup($tablesToTruncate);
58 }
59
60 /**
61 * Function tests that an empty where hook returns no results.
62 * @param int $version
63 * @dataProvider versionThreeAndFour
64 */
65 public function testContactGetNoResultsHook($version) {
66 $this->_apiversion = $version;
67 $this->hookClass->setHook('civicrm_aclWhereClause', [
68 $this,
69 'aclWhereHookNoResults',
70 ]);
71 $result = $this->callAPISuccess('contact', 'get', [
72 'check_permissions' => 1,
73 'return' => 'display_name',
74 ]);
75 $this->assertEquals(0, $result['count']);
76 }
77
78 /**
79 * Function tests that an empty where hook returns exactly 1 result with "view my contact".
80 *
81 * CRM-16512 caused contacts with Edit my contact to be able to view all records.
82 * @param int $version
83 * @dataProvider versionThreeAndFour
84 */
85 public function testContactGetOneResultHookWithViewMyContact($version) {
86 $this->_apiversion = $version;
87 $this->createLoggedInUser();
88 $this->hookClass->setHook('civicrm_aclWhereClause', [
89 $this,
90 'aclWhereHookNoResults',
91 ]);
92 CRM_Core_Config::singleton()->userPermissionClass->permissions = [
93 'access CiviCRM',
94 'view my contact',
95 ];
96 $result = $this->callAPISuccess('contact', 'get', [
97 'check_permissions' => 1,
98 'return' => 'display_name',
99 ]);
100 $this->assertEquals(1, $result['count']);
101 }
102
103 /**
104 * Function tests that a user with "edit my contact" can edit themselves.
105 * @param int $version
106 * @dataProvider versionThreeAndFour
107 */
108 public function testContactEditHookWithEditMyContact($version) {
109 $this->_apiversion = $version;
110 $cid = $this->createLoggedInUser();
111 $this->hookClass->setHook('civicrm_aclWhereClause', [
112 $this,
113 'aclWhereHookNoResults',
114 ]);
115 CRM_Core_Config::singleton()->userPermissionClass->permissions = [
116 'access CiviCRM',
117 'edit my contact',
118 ];
119 $this->callAPISuccess('contact', 'create', [
120 'check_permissions' => 1,
121 'id' => $cid,
122 'first_name' => 'NewName',
123 ]);
124 }
125
126 /**
127 * Ensure contact permissions do not block contact-less location entities.
128 * @param int $version
129 * @dataProvider versionThreeAndFour
130 */
131 public function testAddressWithoutContactIDAccess($version) {
132 $this->_apiversion = $version;
133 $ownID = $this->createLoggedInUser();
134 CRM_Core_Config::singleton()->userPermissionClass->permissions = [
135 'access CiviCRM',
136 'view all contacts',
137 ];
138 $this->callAPISuccess('Address', 'create', [
139 'city' => 'Mouseville',
140 'location_type_id' => 'Main',
141 'api.LocBlock.create' => 1,
142 'contact_id' => $ownID,
143 ]);
144 $this->callAPISuccessGetSingle('Address', [
145 'city' => 'Mouseville',
146 'check_permissions' => 1,
147 ]);
148 CRM_Core_DAO::executeQuery('UPDATE civicrm_address SET contact_id = NULL WHERE contact_id = %1', [
149 1 => [
150 $ownID,
151 'Integer',
152 ],
153 ]);
154 $this->callAPISuccessGetSingle('Address', [
155 'city' => 'Mouseville',
156 'check_permissions' => 1,
157 ]);
158 }
159
160 /**
161 * Ensure contact permissions extend to related entities like email
162 * @param int $version
163 * @dataProvider versionThreeAndFour
164 * FIXME: Finish api4 part
165 */
166 public function testRelatedEntityPermissions($version) {
167 $this->_apiversion = $version;
168 $this->createLoggedInUser();
169 $disallowedContact = $this->individualCreate([], 0);
170 $this->allowedContactId = $this->individualCreate([], 1);
171 $this->hookClass->setHook('civicrm_aclWhereClause', [
172 $this,
173 'aclWhereOnlyOne',
174 ]);
175 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM'];
176 $testEntities = [
177 'Email' => ['email' => 'null@nothing', 'location_type_id' => 1],
178 'Phone' => ['phone' => '123456', 'location_type_id' => 1],
179 'IM' => ['name' => 'hello', 'location_type_id' => 1],
180 'Website' => ['url' => 'http://test'],
181 'Address' => [
182 'street_address' => '123 Sesame St.',
183 'location_type_id' => 1,
184 ],
185 ];
186 foreach ($testEntities as $entity => $params) {
187 $params += [
188 'contact_id' => $disallowedContact,
189 'check_permissions' => 1,
190 ];
191 // We should be prevented from getting or creating entities for a contact we don't have permission for
192 $this->callAPIFailure($entity, 'create', $params);
193 $this->callAPISuccess($entity, 'create', ['check_permissions' => 0] + $params);
194 $results = $this->callAPISuccess($entity, 'get', [
195 'contact_id' => $disallowedContact,
196 'check_permissions' => 1,
197 ]);
198 $this->assertEquals(0, $results['count']);
199
200 // We should be allowed to create and get for contacts we do have permission on
201 $params['contact_id'] = $this->allowedContactId;
202 $this->callAPISuccess($entity, 'create', $params);
203 $results = $this->callAPISuccess($entity, 'get', [
204 'contact_id' => $this->allowedContactId,
205 'check_permissions' => 1,
206 ]);
207 $this->assertGreaterThan(0, $results['count']);
208 }
209 if ($version == 4) {
210 $this->markTestIncomplete('Skipping entity_id related perms in api4 for now.');
211 }
212 $newTag = civicrm_api3('Tag', 'create', [
213 'name' => 'Foo123',
214 ]);
215 $relatedEntities = [
216 'Note' => ['note' => 'abc'],
217 'EntityTag' => ['tag_id' => $newTag['id']],
218 ];
219 foreach ($relatedEntities as $entity => $params) {
220 $params += [
221 'entity_id' => $disallowedContact,
222 'entity_table' => 'civicrm_contact',
223 'check_permissions' => 1,
224 ];
225 // We should be prevented from getting or creating entities for a contact we don't have permission for
226 $this->callAPIFailure($entity, 'create', $params);
227 $this->callAPISuccess($entity, 'create', ['check_permissions' => 0] + $params);
228 $results = $this->callAPISuccess($entity, 'get', [
229 'entity_id' => $disallowedContact,
230 'entity_table' => 'civicrm_contact',
231 'check_permissions' => 1,
232 ]);
233 $this->assertEquals(0, $results['count']);
234
235 // We should be allowed to create and get for entities we do have permission on
236 $params['entity_id'] = $this->allowedContactId;
237 $this->callAPISuccess($entity, 'create', $params);
238 $results = $this->callAPISuccess($entity, 'get', [
239 'entity_id' => $this->allowedContactId,
240 'entity_table' => 'civicrm_contact',
241 'check_permissions' => 1,
242 ]);
243 $this->assertGreaterThan(0, $results['count']);
244 }
245 }
246
247 /**
248 * Function tests all results are returned.
249 * @param int $version
250 * @dataProvider versionThreeAndFour
251 */
252 public function testContactGetAllResultsHook($version) {
253 $this->_apiversion = $version;
254 $this->hookClass->setHook('civicrm_aclWhereClause', [
255 $this,
256 'aclWhereHookAllResults',
257 ]);
258 $result = $this->callAPISuccess('contact', 'get', [
259 'check_permissions' => 1,
260 'return' => 'display_name',
261 ]);
262
263 $this->assertEquals(2, $result['count']);
264 }
265
266 /**
267 * Function tests that deleted contacts are not returned.
268 * @param int $version
269 * @dataProvider versionThreeAndFour
270 */
271 public function testContactGetPermissionHookNoDeleted($version) {
272 $this->_apiversion = $version;
273 $this->callAPISuccess('contact', 'create', ['id' => 2, 'is_deleted' => 1]);
274 $this->hookClass->setHook('civicrm_aclWhereClause', [
275 $this,
276 'aclWhereHookAllResults',
277 ]);
278 $result = $this->callAPISuccess('contact', 'get', [
279 'check_permissions' => 1,
280 'return' => 'display_name',
281 ]);
282 $this->assertEquals(1, $result['count']);
283 }
284
285 /**
286 * Test permissions limited by hook.
287 * @param int $version
288 * @dataProvider versionThreeAndFour
289 */
290 public function testContactGetHookLimitingHook($version) {
291 $this->_apiversion = $version;
292 $this->hookClass->setHook('civicrm_aclWhereClause', [
293 $this,
294 'aclWhereOnlySecond',
295 ]);
296
297 $result = $this->callAPISuccess('contact', 'get', [
298 'check_permissions' => 1,
299 'return' => 'display_name',
300 ]);
301 $this->assertEquals(1, $result['count']);
302 }
303
304 /**
305 * Confirm that without check permissions we still get 2 contacts returned.
306 * @param int $version
307 * @dataProvider versionThreeAndFour
308 */
309 public function testContactGetHookLimitingHookDontCheck($version) {
310 $this->_apiversion = $version;
311 $result = $this->callAPISuccess('contact', 'get', [
312 'check_permissions' => 0,
313 'return' => 'display_name',
314 ]);
315 $this->assertEquals(2, $result['count']);
316 }
317
318 /**
319 * Check that id works as a filter.
320 * @param int $version
321 * @dataProvider versionThreeAndFour
322 */
323 public function testContactGetIDFilter($version) {
324 $this->_apiversion = $version;
325 $this->hookClass->setHook('civicrm_aclWhereClause', [
326 $this,
327 'aclWhereHookAllResults',
328 ]);
329 $result = $this->callAPISuccess('contact', 'get', [
330 'sequential' => 1,
331 'id' => 2,
332 'check_permissions' => 1,
333 ]);
334
335 $this->assertEquals(1, $result['count']);
336 $this->assertEquals(2, $result['id']);
337 }
338
339 /**
340 * Check that address IS returned.
341 */
342 public function testContactGetAddressReturned() {
343 $this->hookClass->setHook('civicrm_aclWhereClause', [
344 $this,
345 'aclWhereOnlySecond',
346 ]);
347 $fullresult = $this->callAPISuccess('contact', 'get', [
348 'sequential' => 1,
349 ]);
350 //return doesn't work for all keys - can't fix that here so let's skip ...
351 //prefix & suffix are inconsistent due to CRM-7929
352 // unsure about others but return doesn't work on them
353 $elementsReturnDoesntSupport = [
354 'prefix',
355 'suffix',
356 'gender',
357 'current_employer',
358 'phone_id',
359 'phone_type_id',
360 'phone',
361 'worldregion_id',
362 'world_region',
363 ];
364 $expectedReturnElements = array_diff(array_keys($fullresult['values'][0]), $elementsReturnDoesntSupport);
365 $result = $this->callAPISuccess('contact', 'get', [
366 'check_permissions' => 1,
367 'return' => $expectedReturnElements,
368 'sequential' => 1,
369 ]);
370 $this->assertEquals(1, $result['count']);
371 foreach ($expectedReturnElements as $element) {
372 $this->assertArrayHasKey($element, $result['values'][0]);
373 }
374 }
375
376 /**
377 * Check that pledge IS not returned.
378 * @param int $version
379 * @dataProvider versionThreeAndFour
380 */
381 public function testContactGetPledgeIDNotReturned($version) {
382 $this->_apiversion = $version;
383 $this->hookClass->setHook('civicrm_aclWhereClause', [
384 $this,
385 'aclWhereHookAllResults',
386 ]);
387 $this->callAPISuccess('contact', 'get', [
388 'sequential' => 1,
389 ]);
390 $result = $this->callAPISuccess('contact', 'get', [
391 'check_permissions' => 1,
392 'return' => 'pledge_id',
393 'sequential' => 1,
394 ]);
395 $this->assertArrayNotHasKey('pledge_id', $result['values'][0]);
396 }
397
398 /**
399 * Check that pledge IS not an allowable filter.
400 */
401 public function testContactGetPledgeIDNotFiltered() {
402 $this->hookClass->setHook('civicrm_aclWhereClause', [
403 $this,
404 'aclWhereHookAllResults',
405 ]);
406 $this->callAPISuccess('contact', 'get', [
407 'sequential' => 1,
408 ]);
409 $result = $this->callAPISuccess('contact', 'get', [
410 'check_permissions' => 1,
411 'pledge_id' => 1,
412 'sequential' => 1,
413 ]);
414 $this->assertEquals(2, $result['count']);
415 }
416
417 /**
418 * Check that chaining doesn't bypass permissions
419 * @param int $version
420 * @dataProvider versionThreeAndFour
421 */
422 public function testContactGetPledgeNotChainable($version) {
423 $this->_apiversion = $version;
424 $this->hookClass->setHook('civicrm_aclWhereClause', [
425 $this,
426 'aclWhereOnlySecond',
427 ]);
428 $this->callAPISuccess('contact', 'get', [
429 'sequential' => 1,
430 ]);
431 $this->callAPIFailure('contact', 'get', [
432 'check_permissions' => 1,
433 'api.pledge.get' => 1,
434 'sequential' => 1,
435 ],
436 'Error in call to Pledge_get : API permission check failed for Pledge/get call; insufficient permission: require access CiviCRM and access CiviPledge'
437 );
438 }
439
440 public function setupCoreACL() {
441 $this->createLoggedInUser();
442 $this->_permissionedDisabledGroup = $this->groupCreate([
443 'title' => 'pick-me-disabled',
444 'is_active' => 0,
445 'name' => 'pick-me-disabled',
446 ]);
447 $this->_permissionedGroup = $this->groupCreate([
448 'title' => 'pick-me-active',
449 'is_active' => 1,
450 'name' => 'pick-me-active',
451 ]);
452 $this->setupACL();
453 }
454
455 /**
456 * @dataProvider entities
457 * confirm that without check permissions we still get 2 contacts returned
458 * @param $entity
459 */
460 public function testEntitiesGetHookLimitingHookNoCheck($entity) {
461 CRM_Core_Config::singleton()->userPermissionClass->permissions = [];
462 $this->setUpEntities($entity);
463 $this->hookClass->setHook('civicrm_aclWhereClause', [
464 $this,
465 'aclWhereHookNoResults',
466 ]);
467 $result = $this->callAPISuccess($entity, 'get', [
468 'check_permissions' => 0,
469 'return' => 'contact_id',
470 ]);
471 $this->assertEquals(2, $result['count']);
472 }
473
474 /**
475 * @dataProvider entities
476 * confirm that without check permissions we still get 2 entities returned
477 * @param $entity
478 */
479 public function testEntitiesGetCoreACLLimitingHookNoCheck($entity) {
480 $this->setupCoreACL();
481 //CRM_Core_Config::singleton()->userPermissionClass->permissions = array();
482 $this->setUpEntities($entity);
483 $this->hookClass->setHook('civicrm_aclWhereClause', [
484 $this,
485 'aclWhereHookNoResults',
486 ]);
487 $result = $this->callAPISuccess($entity, 'get', [
488 'check_permissions' => 0,
489 'return' => 'contact_id',
490 ]);
491 $this->assertEquals(2, $result['count']);
492 }
493
494 /**
495 * @dataProvider entities
496 * confirm that with check permissions we don't get entities
497 * @param $entity
498 * @throws \PHPUnit\Framework\IncompleteTestError
499 */
500 public function testEntitiesGetCoreACLLimitingCheck($entity) {
501 $this->setupCoreACL();
502 $this->setUpEntities($entity);
503 $result = $this->callAPISuccess($entity, 'get', [
504 'check_permissions' => 1,
505 'return' => 'contact_id',
506 ]);
507 $this->assertEquals(0, $result['count']);
508 }
509
510 /**
511 * @dataProvider entities
512 * Function tests that an empty where hook returns no results
513 * @param string $entity
514 * @throws \PHPUnit\Framework\IncompleteTestError
515 */
516 public function testEntityGetNoResultsHook($entity) {
517 $this->markTestIncomplete('hook acls only work with contacts so far');
518 CRM_Core_Config::singleton()->userPermissionClass->permissions = [];
519 $this->setUpEntities($entity);
520 $this->hookClass->setHook('civicrm_aclWhereClause', [
521 $this,
522 'aclWhereHookNoResults',
523 ]);
524 $result = $this->callAPISuccess($entity, 'get', [
525 'check_permission' => 1,
526 ]);
527 $this->assertEquals(0, $result['count']);
528 }
529
530 /**
531 * @return array
532 */
533 public static function entities() {
534 return [
535 ['contribution'],
536 ['participant'],
537 // @todo array('pledge' => 'pledge')
538 ];
539 }
540
541 /**
542 * Create 2 entities
543 * @param $entity
544 */
545 public function setUpEntities($entity) {
546 $baoObj = new CRM_Core_DAO();
547 $baoObj->createTestObject(_civicrm_api3_get_BAO($entity), [], 2, 0);
548 CRM_Core_Config::singleton()->userPermissionClass->permissions = [
549 'access CiviCRM',
550 'access CiviContribute',
551 'access CiviEvent',
552 'view event participants',
553 ];
554 }
555
556 /**
557 * Basic check that an unpermissioned call keeps working and permissioned call fails.
558 * @param int $version
559 * @dataProvider versionThreeAndFour
560 */
561 public function testGetActivityNoPermissions($version) {
562 $this->_apiversion = $version;
563 $this->setPermissions([]);
564 $this->callAPISuccess('Activity', 'get', []);
565 $this->callAPIFailure('Activity', 'get', ['check_permissions' => 1]);
566 }
567
568 /**
569 * View all activities is enough regardless of contact ACLs.
570 * @param int $version
571 * @dataProvider versionThreeAndFour
572 */
573 public function testGetActivityViewAllActivitiesDoesntCutItAnymore($version) {
574 $this->_apiversion = $version;
575 $activity = $this->activityCreate();
576 $this->setPermissions(['view all activities', 'access CiviCRM']);
577 $this->callAPISuccessGetCount('Activity', [
578 'check_permissions' => 1,
579 'id' => $activity['id'],
580 ], 0);
581 }
582
583 /**
584 * View all activities is required unless id is passed in.
585 * @param int $version
586 * @dataProvider versionThreeAndFour
587 */
588 public function testGetActivityViewAllContactsEnoughWithoutID($version) {
589 $this->_apiversion = $version;
590 $this->setPermissions(['view all contacts', 'access CiviCRM']);
591 $this->callAPISuccess('Activity', 'get', ['check_permissions' => 1]);
592 }
593
594 /**
595 * Without view all activities contact level acls are used.
596 * @param int $version
597 * @dataProvider versionThreeAndFour
598 */
599 public function testGetActivityViewAllContactsEnoughWIthID($version) {
600 $this->_apiversion = $version;
601 $activity = $this->activityCreate();
602 $this->setPermissions(['view all contacts', 'access CiviCRM']);
603 $this->callAPISuccess('Activity', 'getsingle', [
604 'check_permissions' => 1,
605 'id' => $activity['id'],
606 ]);
607 }
608
609 /**
610 * Check the error message is not a permission error.
611 * @param int $version
612 * @dataProvider versionThreeAndFour
613 */
614 public function testGetActivityAccessCiviCRMEnough($version) {
615 $this->_apiversion = $version;
616 $activity = $this->activityCreate();
617 $this->setPermissions(['access CiviCRM']);
618 $this->callAPIFailure('Activity', 'getsingle', [
619 'check_permissions' => 1,
620 'id' => $activity['id'],
621 ], 'Expected one Activity but found 0');
622 $this->callAPISuccessGetCount('Activity', [
623 'check_permissions' => 1,
624 'id' => $activity['id'],
625 ], 0);
626 }
627
628 /**
629 * Check that component related activity filtering.
630 *
631 * If the contact does NOT have permission to 'view all contacts' but they DO have permission
632 * to view the contact in question they will only see the activities of components they have access too.
633 *
634 * (logically the same component limit should apply when they have access to view all too but....
635 * adding test for 'how it is at the moment.)
636 * @param int $version
637 * @dataProvider versionThreeAndFour
638 */
639 public function testGetActivityCheckPermissionsByComponent($version) {
640 $this->_apiversion = $version;
641 $activity = $this->activityCreate(['activity_type_id' => 'Contribution']);
642 $activity2 = $this->activityCreate(['activity_type_id' => 'Pledge Reminder']);
643 $this->hookClass->setHook('civicrm_aclWhereClause', [
644 $this,
645 'aclWhereHookAllResults',
646 ]);
647 $this->setPermissions(['access CiviCRM', 'access CiviContribute']);
648 $this->callAPISuccessGetSingle('Activity', [
649 'check_permissions' => 1,
650 'id' => ['IN' => [$activity['id'], $activity2['id']]],
651 ]);
652 $this->callAPISuccessGetCount('Activity', [
653 'check_permissions' => 1,
654 'id' => ['IN' => [$activity['id'], $activity2['id']]],
655 ], 1);
656
657 }
658
659 /**
660 * Check that component related activity filtering works for CiviCase.
661 * @param int $version
662 * @dataProvider versionThreeAndFour
663 */
664 public function testGetActivityCheckPermissionsByCaseComponent($version) {
665 $this->_apiversion = $version;
666 CRM_Core_BAO_ConfigSetting::enableComponent('CiviCase');
667 $activity = $this->activityCreate(['activity_type_id' => 'Open Case']);
668 $activity2 = $this->activityCreate(['activity_type_id' => 'Pledge Reminder']);
669 $this->hookClass->setHook('civicrm_aclWhereClause', [
670 $this,
671 'aclWhereHookAllResults',
672 ]);
673 $this->setPermissions([
674 'access CiviCRM',
675 'access CiviContribute',
676 'access all cases and activities',
677 ]);
678 $this->callAPISuccessGetSingle('Activity', [
679 'check_permissions' => 1,
680 'id' => ['IN' => [$activity['id'], $activity2['id']]],
681 ]);
682 $this->callAPISuccessGetCount('Activity', [
683 'check_permissions' => 1,
684 'id' => ['IN' => [$activity['id'], $activity2['id']]],
685 ], 1);
686 }
687
688 /**
689 * Check that activities can be retrieved by ACL.
690 *
691 * The activities api applies ACLs in a very limited circumstance, if id is passed in.
692 * Otherwise it sticks with the blunt original permissions.
693 * @param int $version
694 * @dataProvider versionThreeAndFour
695 */
696 public function testGetActivityByACL($version) {
697 $this->_apiversion = $version;
698 $this->setPermissions(['access CiviCRM']);
699 $activity = $this->activityCreate();
700
701 $this->hookClass->setHook('civicrm_aclWhereClause', [
702 $this,
703 'aclWhereHookAllResults',
704 ]);
705 $this->callAPISuccessGetSingle('Activity', [
706 'check_permissions' => 1,
707 'id' => $activity['id'],
708 ]);
709 $this->callAPISuccessGetCount('Activity', [
710 'check_permissions' => 1,
711 'id' => $activity['id'],
712 ]);
713 }
714
715 /**
716 * To leverage ACL permission to view an activity you must be able to see any of the contacts.
717 * FIXME: Api4
718 */
719 public function testGetActivityByAclCannotViewAllContacts() {
720 $activity = $this->activityCreate(['assignee_contact_id' => $this->individualCreate()]);
721 $contacts = $this->getActivityContacts($activity);
722 $this->setPermissions(['access CiviCRM']);
723
724 foreach ($contacts as $role => $contact_id) {
725 $this->allowedContactId = $contact_id;
726 $this->hookClass->setHook('civicrm_aclWhereClause', [
727 $this,
728 'aclWhereOnlyOne',
729 ]);
730 $this->cleanupCachedPermissions();
731 $result = $this->callAPISuccessGetSingle('Activity', [
732 'check_permissions' => 1,
733 'id' => $activity['id'],
734 'return' => [
735 'source_contact_id',
736 'target_contact_id',
737 'assignee_contact_id',
738 ],
739 ]);
740 foreach ([
741 'source_contact',
742 'target_contact',
743 'assignee_contact',
744 ] as $roleName) {
745 $roleKey = $roleName . '_id';
746 if ($role !== $roleKey) {
747 $this->assertTrue(empty($result[$roleKey]), "Only contact in $role is permissioned to be returned, not $roleKey");
748 }
749 else {
750 $this->assertEquals([$contact_id], (array) $result[$roleKey]);
751 $this->assertTrue(!empty($result[$roleName . '_name']));
752 }
753 }
754 }
755 }
756
757 /**
758 * To leverage ACL permission to view an activity you must be able to see any of the contacts.
759 * @param int $version
760 * @dataProvider versionThreeAndFour
761 */
762 public function testGetActivityByAclCannotViewAnyContacts($version) {
763 $this->_apiversion = $version;
764 $activity = $this->activityCreate();
765 $contacts = $this->getActivityContacts($activity);
766 $this->setPermissions(['access CiviCRM']);
767
768 foreach ($contacts as $contact_id) {
769 $this->callAPIFailure('Activity', 'getsingle', [
770 'check_permissions' => 1,
771 'id' => $activity['id'],
772 ]);
773 }
774 }
775
776 /**
777 * Check that if the source contact is deleted but we can view the others we can see the activity.
778 *
779 * CRM-18409.
780 *
781 * @throws \CRM_Core_Exception
782 * @param int $version
783 * @dataProvider versionThreeAndFour
784 */
785 public function testGetActivityACLSourceContactDeleted($version) {
786 $this->_apiversion = $version;
787 $this->setPermissions(['access CiviCRM', 'delete contacts']);
788 $activity = $this->activityCreate();
789 $contacts = $this->getActivityContacts($activity);
790
791 $this->hookClass->setHook('civicrm_aclWhereClause', [
792 $this,
793 'aclWhereHookAllResults',
794 ]);
795 $this->contactDelete($contacts['source_contact_id']);
796 $this->callAPISuccess('Activity', 'getsingle', [
797 'check_permissions' => 1,
798 'id' => $activity['id'],
799 ]);
800 }
801
802 /**
803 * Test get activities multiple ids with check permissions
804 * CRM-20441
805 * @param int $version
806 * @dataProvider versionThreeAndFour
807 */
808 public function testActivitiesGetMultipleIdsCheckPermissions($version) {
809 $this->_apiversion = $version;
810 $this->createLoggedInUser();
811 $activity = $this->activityCreate();
812 $activity2 = $this->activityCreate();
813 $this->setPermissions(['access CiviCRM']);
814 $this->hookClass->setHook('civicrm_aclWhereClause', [
815 $this,
816 'aclWhereHookAllResults',
817 ]);
818 // Get activities associated with contact $this->_contactID.
819 $params = [
820 'id' => ['IN' => [$activity['id'], $activity2['id']]],
821 'check_permissions' => TRUE,
822 ];
823 $result = $this->callAPISuccess('activity', 'get', $params);
824 $this->assertEquals(2, $result['count']);
825 }
826
827 /**
828 * Test get activities multiple ids with check permissions
829 * Limit access to One contact
830 * CRM-20441
831 * @param int $version
832 * @dataProvider versionThreeAndFour
833 */
834 public function testActivitiesGetMultipleIdsCheckPermissionsLimitedACL($version) {
835 $this->_apiversion = $version;
836 $this->createLoggedInUser();
837 $activity = $this->activityCreate();
838 $contacts = $this->getActivityContacts($activity);
839 $this->setPermissions(['access CiviCRM']);
840 foreach ($contacts as $contact_id) {
841 $this->allowedContacts[] = $contact_id;
842 }
843 $this->hookClass->setHook('civicrm_aclWhereClause', [
844 $this,
845 'aclWhereMultipleContacts',
846 ]);
847 $contact2 = $this->individualCreate();
848 $activity2 = $this->activityCreate(['source_contact_id' => $contact2]);
849 // Get activities associated with contact $this->_contactID.
850 $params = [
851 'id' => ['IN' => [$activity['id']]],
852 'check_permissions' => TRUE,
853 ];
854 $result = $this->callAPISuccess('activity', 'get', $params);
855 $this->assertEquals(1, $result['count']);
856 $this->callAPIFailure('activity', 'getsingle', array_merge($params, [
857 'id' => [
858 'IN',
859 [$activity2['id']],
860 ],
861 ]));
862 }
863
864 /**
865 * Test get activities multiple ids with check permissions
866 * CRM-20441
867 * @param int $version
868 * @dataProvider versionThreeAndFour
869 */
870 public function testActivitiesGetMultipleIdsCheckPermissionsNotIN($version) {
871 $this->_apiversion = $version;
872 $this->createLoggedInUser();
873 $activity = $this->activityCreate();
874 $activity2 = $this->activityCreate();
875 $this->setPermissions(['access CiviCRM']);
876 $this->hookClass->setHook('civicrm_aclWhereClause', [
877 $this,
878 'aclWhereHookAllResults',
879 ]);
880 // Get activities associated with contact $this->_contactID.
881 $params = [
882 'id' => ['NOT IN' => [$activity['id'], $activity2['id']]],
883 'check_permissions' => TRUE,
884 ];
885 $result = $this->callAPISuccess('activity', 'get', $params);
886 $this->assertEquals(0, $result['count']);
887 }
888
889 /**
890 * Get the contacts for the activity.
891 *
892 * @param $activity
893 *
894 * @return array
895 * @throws \CRM_Core_Exception
896 */
897 protected function getActivityContacts($activity) {
898 $contacts = [];
899
900 $activityContacts = $this->callAPISuccess('ActivityContact', 'get', [
901 'activity_id' => $activity['id'],
902 ]);
903
904 $activityRecordTypes = $this->callAPISuccess('ActivityContact', 'getoptions', ['field' => 'record_type_id']);
905 foreach ($activityContacts['values'] as $activityContact) {
906 $type = $activityRecordTypes['values'][$activityContact['record_type_id']];
907 switch ($type) {
908 case 'Activity Source':
909 $contacts['source_contact_id'] = $activityContact['contact_id'];
910 break;
911
912 case 'Activity Targets':
913 $contacts['target_contact_id'] = $activityContact['contact_id'];
914 break;
915
916 case 'Activity Assignees':
917 $contacts['assignee_contact_id'] = $activityContact['contact_id'];
918 break;
919
920 }
921 }
922 return $contacts;
923 }
924
925 /**
926 * Test that the 'everyone' group can be given access to a contact.
927 * FIXME: Api4
928 */
929 public function testGetACLEveryonePermittedEntity() {
930 $this->setupScenarioCoreACLEveryonePermittedToGroup();
931 $this->callAPISuccessGetCount('Contact', [
932 'id' => $this->scenarioIDs['Contact']['permitted_contact'],
933 'check_permissions' => 1,
934 ], 1);
935
936 $this->callAPISuccessGetCount('Contact', [
937 'id' => $this->scenarioIDs['Contact']['non_permitted_contact'],
938 'check_permissions' => 1,
939 ], 0);
940
941 // Also check that we can access ACLs through a path that uses the acl_contact_cache table.
942 // historically this has caused errors due to the key_constraint on that table.
943 // This is a bit of an artificial check as we have to amp up permissions to access this api.
944 // However, the lower level function is more directly accessed through the Contribution & Event & Profile
945 $dupes = $this->callAPISuccess('Contact', 'duplicatecheck', [
946 'match' => [
947 'first_name' => 'Anthony',
948 'last_name' => 'Anderson',
949 'contact_type' => 'Individual',
950 'email' => 'anthony_anderson@civicrm.org',
951 ],
952 'check_permissions' => 0,
953 ]);
954 $this->assertEquals(2, $dupes['count']);
955 CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM'];
956
957 $dupes = $this->callAPISuccess('Contact', 'duplicatecheck', [
958 'match' => [
959 'first_name' => 'Anthony',
960 'last_name' => 'Anderson',
961 'contact_type' => 'Individual',
962 'email' => 'anthony_anderson@civicrm.org',
963 ],
964 'check_permissions' => 1,
965 ]);
966 $this->assertEquals(1, $dupes['count']);
967
968 }
969
970 }