3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
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 +--------------------------------------------------------------------+
12 use Civi\Api4\Contact
;
13 use Civi\Api4\CustomField
;
14 use Civi\Api4\CustomGroup
;
15 use Civi\Api4\CustomValue
;
18 * This class is intended to test ACL permission using the multisite module
20 * @package CiviCRM_APIv3
21 * @subpackage API_Contact
24 class api_v3_ACLPermissionTest
extends CiviUnitTestCase
{
26 use CRMTraits_ACL_PermissionTrait
;
28 public $DBResetRequired = FALSE;
31 public function setUp() {
33 CRM_Core_DAO
::createTestObject('CRM_Pledge_BAO_Pledge', [], 1, 0);
34 $this->callAPISuccess('Phone', 'create', ['id' => $this->individualCreate(['email' => '']), 'phone' => '911', 'location_type_id' => 'Home']);
35 $this->prepareForACLs();
40 * @see CiviUnitTestCase::tearDown()
42 public function tearDown() {
43 $this->cleanUpAfterACLs();
47 'civicrm_group_contact',
51 'civicrm_acl_entity_role',
52 'civicrm_acl_contact_cache',
53 'civicrm_contribution',
55 'civicrm_participant',
58 'civicrm_activity_contact',
63 $this->quickCleanup($tablesToTruncate);
67 * Function tests that an empty where hook returns no results.
71 * @dataProvider versionThreeAndFour
72 * @throws \CRM_Core_Exception
74 public function testContactGetNoResultsHook($version) {
75 $this->_apiversion
= $version;
76 $this->hookClass
->setHook('civicrm_aclWhereClause', [
78 'aclWhereHookNoResults',
80 $result = $this->callAPISuccess('contact', 'get', [
81 'check_permissions' => 1,
82 'return' => 'display_name',
84 $this->assertEquals(0, $result['count']);
88 * Function tests that an empty where hook returns exactly 1 result with "view my contact".
90 * CRM-16512 caused contacts with Edit my contact to be able to view all records.
94 * @dataProvider versionThreeAndFour
95 * @throws \CRM_Core_Exception
97 public function testContactGetOneResultHookWithViewMyContact($version) {
98 $this->_apiversion
= $version;
99 $this->createLoggedInUser();
100 $this->hookClass
->setHook('civicrm_aclWhereClause', [
102 'aclWhereHookNoResults',
104 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= [
108 $result = $this->callAPISuccess('contact', 'get', [
109 'check_permissions' => 1,
110 'return' => 'display_name',
112 $this->assertEquals(1, $result['count']);
116 * Function tests that a user with "edit my contact" can edit themselves.
118 * @param int $version
120 * @dataProvider versionThreeAndFour
121 * @throws \CRM_Core_Exception
123 public function testContactEditHookWithEditMyContact($version) {
124 $this->_apiversion
= $version;
125 $cid = $this->createLoggedInUser();
126 $this->hookClass
->setHook('civicrm_aclWhereClause', [
128 'aclWhereHookNoResults',
130 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= [
134 $this->callAPISuccess('contact', 'create', [
135 'check_permissions' => 1,
137 'first_name' => 'NewName',
142 * Ensure contact permissions do not block contact-less location entities.
144 * @param int $version
146 * @dataProvider versionThreeAndFour
147 * @throws \CRM_Core_Exception
149 public function testAddressWithoutContactIDAccess($version) {
150 $this->_apiversion
= $version;
151 $ownID = $this->createLoggedInUser();
152 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= [
156 $this->callAPISuccess('Address', 'create', [
157 'city' => 'Mouseville',
158 'location_type_id' => 'Main',
159 'api.LocBlock.create' => 1,
160 'contact_id' => $ownID,
162 $this->callAPISuccessGetSingle('Address', [
163 'city' => 'Mouseville',
164 'check_permissions' => 1,
166 CRM_Core_DAO
::executeQuery('UPDATE civicrm_address SET contact_id = NULL WHERE contact_id = %1', [
172 $this->callAPISuccessGetSingle('Address', [
173 'city' => 'Mouseville',
174 'check_permissions' => 1,
179 * Ensure contact permissions extend to related entities like email
181 * @param int $version
183 * @throws \CRM_Core_Exception
184 * @throws \CiviCRM_API3_Exception
185 * @dataProvider versionThreeAndFour
186 * FIXME: Finish api4 part
188 public function testRelatedEntityPermissions($version) {
189 $this->_apiversion
= $version;
190 $this->createLoggedInUser();
191 $disallowedContact = $this->individualCreate([], 0);
192 $this->allowedContactId
= $this->individualCreate([], 1);
193 $this->hookClass
->setHook('civicrm_aclWhereClause', [
197 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= ['access CiviCRM'];
199 'Email' => ['email' => 'null@nothing', 'location_type_id' => 1],
200 'Phone' => ['phone' => '123456', 'location_type_id' => 1],
201 'IM' => ['name' => 'hello', 'location_type_id' => 1],
202 'Website' => ['url' => 'http://test'],
204 'street_address' => '123 Sesame St.',
205 'location_type_id' => 1,
208 foreach ($testEntities as $entity => $params) {
210 'contact_id' => $disallowedContact,
211 'check_permissions' => 1,
213 // We should be prevented from getting or creating entities for a contact we don't have permission for
214 $this->callAPIFailure($entity, 'create', $params);
215 $this->callAPISuccess($entity, 'create', ['check_permissions' => 0] +
$params);
216 $results = $this->callAPISuccess($entity, 'get', [
217 'contact_id' => $disallowedContact,
218 'check_permissions' => 1,
220 $this->assertEquals(0, $results['count']);
222 // We should be allowed to create and get for contacts we do have permission on
223 $params['contact_id'] = $this->allowedContactId
;
224 $this->callAPISuccess($entity, 'create', $params);
225 $results = $this->callAPISuccess($entity, 'get', [
226 'contact_id' => $this->allowedContactId
,
227 'check_permissions' => 1,
229 $this->assertGreaterThan(0, $results['count']);
232 $this->markTestIncomplete('Skipping entity_id related perms in api4 for now.');
234 $newTag = civicrm_api3('Tag', 'create', [
238 'Note' => ['note' => 'abc'],
239 'EntityTag' => ['tag_id' => $newTag['id']],
241 foreach ($relatedEntities as $entity => $params) {
243 'entity_id' => $disallowedContact,
244 'entity_table' => 'civicrm_contact',
245 'check_permissions' => 1,
247 // We should be prevented from getting or creating entities for a contact we don't have permission for
248 $this->callAPIFailure($entity, 'create', $params);
249 $this->callAPISuccess($entity, 'create', ['check_permissions' => 0] +
$params);
250 $results = $this->callAPISuccess($entity, 'get', [
251 'entity_id' => $disallowedContact,
252 'entity_table' => 'civicrm_contact',
253 'check_permissions' => 1,
255 $this->assertEquals(0, $results['count']);
257 // We should be allowed to create and get for entities we do have permission on
258 $params['entity_id'] = $this->allowedContactId
;
259 $this->callAPISuccess($entity, 'create', $params);
260 $results = $this->callAPISuccess($entity, 'get', [
261 'entity_id' => $this->allowedContactId
,
262 'entity_table' => 'civicrm_contact',
263 'check_permissions' => 1,
265 $this->assertGreaterThan(0, $results['count']);
270 * Function tests all results are returned.
272 * @param int $version
274 * @dataProvider versionThreeAndFour
275 * @throws \CRM_Core_Exception
277 public function testContactGetAllResultsHook($version) {
278 $this->_apiversion
= $version;
279 $this->hookClass
->setHook('civicrm_aclWhereClause', [
281 'aclWhereHookAllResults',
283 $result = $this->callAPISuccess('contact', 'get', [
284 'check_permissions' => 1,
285 'return' => 'display_name',
288 $this->assertEquals(2, $result['count']);
292 * Function tests that deleted contacts are not returned.
294 * @param int $version
296 * @dataProvider versionThreeAndFour
297 * @throws \CRM_Core_Exception
299 public function testContactGetPermissionHookNoDeleted($version) {
300 $this->_apiversion
= $version;
301 $this->callAPISuccess('contact', 'create', ['id' => 2, 'is_deleted' => 1]);
302 $this->hookClass
->setHook('civicrm_aclWhereClause', [
304 'aclWhereHookAllResults',
306 $result = $this->callAPISuccess('contact', 'get', [
307 'check_permissions' => 1,
308 'return' => 'display_name',
310 $this->assertEquals(1, $result['count']);
314 * Test permissions limited by hook.
316 * @param int $version
318 * @dataProvider versionThreeAndFour
319 * @throws \CRM_Core_Exception
321 public function testContactGetHookLimitingHook($version) {
322 $this->_apiversion
= $version;
323 $this->hookClass
->setHook('civicrm_aclWhereClause', [
325 'aclWhereOnlySecond',
328 $result = $this->callAPISuccess('contact', 'get', [
329 'check_permissions' => 1,
330 'return' => 'display_name',
332 $this->assertEquals(1, $result['count']);
336 * Confirm that without check permissions we still get 2 contacts returned.
338 * @param int $version
340 * @dataProvider versionThreeAndFour
341 * @throws \CRM_Core_Exception
343 public function testContactGetHookLimitingHookDontCheck($version) {
344 $this->_apiversion
= $version;
345 $result = $this->callAPISuccess('contact', 'get', [
346 'check_permissions' => 0,
347 'return' => 'display_name',
349 $this->assertEquals(2, $result['count']);
353 * Check that id works as a filter.
354 * @param int $version
355 * @dataProvider versionThreeAndFour
357 public function testContactGetIDFilter($version) {
358 $this->_apiversion
= $version;
359 $this->hookClass
->setHook('civicrm_aclWhereClause', [
361 'aclWhereHookAllResults',
363 $result = $this->callAPISuccess('contact', 'get', [
366 'check_permissions' => 1,
369 $this->assertEquals(1, $result['count']);
370 $this->assertEquals(2, $result['id']);
374 * Check that address IS returned.
376 public function testContactGetAddressReturned() {
377 $this->hookClass
->setHook('civicrm_aclWhereClause', [
379 'aclWhereOnlySecond',
381 $fullresult = $this->callAPISuccess('contact', 'get', [
384 //return doesn't work for all keys - can't fix that here so let's skip ...
385 //prefix & suffix are inconsistent due to CRM-7929
386 // unsure about others but return doesn't work on them
387 $elementsReturnDoesntSupport = [
398 $expectedReturnElements = array_diff(array_keys($fullresult['values'][0]), $elementsReturnDoesntSupport);
399 $result = $this->callAPISuccess('contact', 'get', [
400 'check_permissions' => 1,
401 'return' => $expectedReturnElements,
404 $this->assertEquals(1, $result['count']);
405 foreach ($expectedReturnElements as $element) {
406 $this->assertArrayHasKey($element, $result['values'][0]);
411 * Check that pledge IS not returned.
412 * @param int $version
413 * @dataProvider versionThreeAndFour
415 public function testContactGetPledgeIDNotReturned($version) {
416 $this->_apiversion
= $version;
417 $this->hookClass
->setHook('civicrm_aclWhereClause', [
419 'aclWhereHookAllResults',
421 $this->callAPISuccess('contact', 'get', [
424 $result = $this->callAPISuccess('contact', 'get', [
425 'check_permissions' => 1,
426 'return' => 'pledge_id',
429 $this->assertArrayNotHasKey('pledge_id', $result['values'][0]);
433 * Check that pledge IS not an allowable filter.
435 public function testContactGetPledgeIDNotFiltered() {
436 $this->hookClass
->setHook('civicrm_aclWhereClause', [
438 'aclWhereHookAllResults',
440 $this->callAPISuccess('contact', 'get', [
443 $result = $this->callAPISuccess('contact', 'get', [
444 'check_permissions' => 1,
448 $this->assertEquals(2, $result['count']);
452 * Check that chaining doesn't bypass permissions
454 * @param int $version
456 * @dataProvider versionThreeAndFour
457 * @throws \CRM_Core_Exception
459 public function testContactGetPledgeNotChainable($version) {
460 $this->_apiversion
= $version;
461 $this->hookClass
->setHook('civicrm_aclWhereClause', [
463 'aclWhereOnlySecond',
465 $this->callAPISuccess('contact', 'get', [
468 $this->callAPIFailure('contact', 'get', [
469 'check_permissions' => 1,
470 'api.pledge.get' => 1,
473 'Error in call to Pledge_get : API permission check failed for Pledge/get call; insufficient permission: require access CiviCRM and access CiviPledge'
477 public function setupCoreACL() {
478 $this->createLoggedInUser();
479 $this->_permissionedDisabledGroup
= $this->groupCreate([
480 'title' => 'pick-me-disabled',
482 'name' => 'pick-me-disabled',
484 $this->_permissionedGroup
= $this->groupCreate([
485 'title' => 'pick-me-active',
487 'name' => 'pick-me-active',
493 * @dataProvider entities
494 * confirm that without check permissions we still get 2 contacts returned
496 * @param string $entity
498 * @throws \CRM_Core_Exception
500 public function testEntitiesGetHookLimitingHookNoCheck($entity) {
501 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= [];
502 $this->setUpEntities($entity);
503 $this->hookClass
->setHook('civicrm_aclWhereClause', [
505 'aclWhereHookNoResults',
507 $result = $this->callAPISuccess($entity, 'get', [
508 'check_permissions' => 0,
509 'return' => 'contact_id',
511 $this->assertEquals(2, $result['count']);
515 * @dataProvider entities
516 * confirm that without check permissions we still get 2 entities returned
519 public function testEntitiesGetCoreACLLimitingHookNoCheck($entity) {
520 $this->setupCoreACL();
521 //CRM_Core_Config::singleton()->userPermissionClass->permissions = array();
522 $this->setUpEntities($entity);
523 $this->hookClass
->setHook('civicrm_aclWhereClause', [
525 'aclWhereHookNoResults',
527 $result = $this->callAPISuccess($entity, 'get', [
528 'check_permissions' => 0,
529 'return' => 'contact_id',
531 $this->assertEquals(2, $result['count']);
535 * @dataProvider entities
536 * confirm that with check permissions we don't get entities
540 * @throws \PHPUnit\Framework\IncompleteTestError
541 * @throws \CRM_Core_Exception
543 public function testEntitiesGetCoreACLLimitingCheck($entity) {
544 $this->setupCoreACL();
545 $this->setUpEntities($entity);
546 $result = $this->callAPISuccess($entity, 'get', [
547 'check_permissions' => 1,
548 'return' => 'contact_id',
550 $this->assertEquals(0, $result['count']);
554 * @dataProvider entities
555 * Function tests that an empty where hook returns no results
557 * @param string $entity
559 * @throws \PHPUnit\Framework\IncompleteTestError
560 * @throws \CRM_Core_Exception
562 public function testEntityGetNoResultsHook($entity) {
563 $this->markTestIncomplete('hook acls only work with contacts so far');
564 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= [];
565 $this->setUpEntities($entity);
566 $this->hookClass
->setHook('civicrm_aclWhereClause', [
568 'aclWhereHookNoResults',
570 $result = $this->callAPISuccess($entity, 'get', [
571 'check_permission' => 1,
573 $this->assertEquals(0, $result['count']);
579 public static function entities() {
583 // @todo array('pledge' => 'pledge')
590 * @param string $entity
592 public function setUpEntities($entity) {
593 CRM_Core_DAO
::createTestObject(_civicrm_api3_get_BAO($entity), [], 2, 0);
594 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= [
596 'access CiviContribute',
598 'view event participants',
603 * Basic check that an un-permissioned call keeps working and permissioned call fails.
605 * @param int $version
607 * @dataProvider versionThreeAndFour
608 * @throws \CRM_Core_Exception
610 public function testGetActivityNoPermissions($version) {
611 $this->_apiversion
= $version;
612 $this->setPermissions([]);
613 $this->callAPISuccess('Activity', 'get');
614 $this->callAPIFailure('Activity', 'get', ['check_permissions' => 1]);
618 * View all activities is enough regardless of contact ACLs.
620 * @param int $version
622 * @throws \CRM_Core_Exception
623 * @throws \CiviCRM_API3_Exception
624 * @dataProvider versionThreeAndFour
626 public function testGetActivityViewAllActivitiesDoesntCutItAnymore($version) {
627 $this->_apiversion
= $version;
628 $activity = $this->activityCreate();
629 $this->setPermissions(['view all activities', 'access CiviCRM']);
630 $this->callAPISuccessGetCount('Activity', [
631 'check_permissions' => 1,
632 'id' => $activity['id'],
637 * View all activities is required unless id is passed in.
639 * @param int $version
641 * @dataProvider versionThreeAndFour
642 * @throws \CRM_Core_Exception
644 public function testGetActivityViewAllContactsEnoughWithoutID($version) {
645 $this->_apiversion
= $version;
646 $this->setPermissions(['view all contacts', 'access CiviCRM']);
647 $this->callAPISuccess('Activity', 'get', ['check_permissions' => 1]);
651 * Without view all activities contact level acls are used.
653 * @param int $version
655 * @throws \CRM_Core_Exception
656 * @throws \CiviCRM_API3_Exception
657 * @dataProvider versionThreeAndFour
659 public function testGetActivityViewAllContactsEnoughWIthID($version) {
660 $this->_apiversion
= $version;
661 $activity = $this->activityCreate();
662 $this->setPermissions(['view all contacts', 'access CiviCRM']);
663 $this->callAPISuccess('Activity', 'getsingle', [
664 'check_permissions' => 1,
665 'id' => $activity['id'],
670 * Check the error message is not a permission error.
672 * @param int $version
674 * @throws \CRM_Core_Exception
675 * @throws \CiviCRM_API3_Exception
676 * @dataProvider versionThreeAndFour
678 public function testGetActivityAccessCiviCRMEnough($version) {
679 $this->_apiversion
= $version;
680 $activity = $this->activityCreate();
681 $this->setPermissions(['access CiviCRM']);
682 $this->callAPIFailure('Activity', 'getsingle', [
683 'check_permissions' => 1,
684 'id' => $activity['id'],
685 ], 'Expected one Activity but found 0');
686 $this->callAPISuccessGetCount('Activity', [
687 'check_permissions' => 1,
688 'id' => $activity['id'],
693 * Check that component related activity filtering.
695 * If the contact does NOT have permission to 'view all contacts' but they DO have permission
696 * to view the contact in question they will only see the activities of components they have access too.
698 * (logically the same component limit should apply when they have access to view all too but....
699 * adding test for 'how it is at the moment.)
701 * @param int $version
703 * @throws \CRM_Core_Exception
704 * @throws \CiviCRM_API3_Exception
705 * @dataProvider versionThreeAndFour
707 public function testGetActivityCheckPermissionsByComponent($version) {
708 $this->_apiversion
= $version;
709 $activity = $this->activityCreate(['activity_type_id' => 'Contribution']);
710 $activity2 = $this->activityCreate(['activity_type_id' => 'Pledge Reminder']);
711 $this->hookClass
->setHook('civicrm_aclWhereClause', [
713 'aclWhereHookAllResults',
715 $this->setPermissions(['access CiviCRM', 'access CiviContribute']);
716 $this->callAPISuccessGetSingle('Activity', [
717 'check_permissions' => 1,
718 'id' => ['IN' => [$activity['id'], $activity2['id']]],
720 $this->callAPISuccessGetCount('Activity', [
721 'check_permissions' => 1,
722 'id' => ['IN' => [$activity['id'], $activity2['id']]],
728 * Check that component related activity filtering works for CiviCase.
730 * @param int $version
732 * @throws \CRM_Core_Exception
733 * @throws \CiviCRM_API3_Exception
734 * @dataProvider versionThreeAndFour
736 public function testGetActivityCheckPermissionsByCaseComponent($version) {
737 $this->_apiversion
= $version;
738 CRM_Core_BAO_ConfigSetting
::enableComponent('CiviCase');
739 $activity = $this->activityCreate(['activity_type_id' => 'Open Case']);
740 $activity2 = $this->activityCreate(['activity_type_id' => 'Pledge Reminder']);
741 $this->hookClass
->setHook('civicrm_aclWhereClause', [
743 'aclWhereHookAllResults',
745 $this->setPermissions([
747 'access CiviContribute',
748 'access all cases and activities',
750 $this->callAPISuccessGetSingle('Activity', [
751 'check_permissions' => 1,
752 'id' => ['IN' => [$activity['id'], $activity2['id']]],
754 $this->callAPISuccessGetCount('Activity', [
755 'check_permissions' => 1,
756 'id' => ['IN' => [$activity['id'], $activity2['id']]],
761 * Check that activities can be retrieved by ACL.
763 * The activities api applies ACLs in a very limited circumstance, if id is passed in.
764 * Otherwise it sticks with the blunt original permissions.
766 * @param int $version
768 * @throws \CRM_Core_Exception
769 * @throws \CiviCRM_API3_Exception
770 * @dataProvider versionThreeAndFour
772 public function testGetActivityByACL($version) {
773 $this->_apiversion
= $version;
774 $this->setPermissions(['access CiviCRM']);
775 $activity = $this->activityCreate();
777 $this->hookClass
->setHook('civicrm_aclWhereClause', [
779 'aclWhereHookAllResults',
781 $this->callAPISuccessGetSingle('Activity', [
782 'check_permissions' => 1,
783 'id' => $activity['id'],
785 $this->callAPISuccessGetCount('Activity', [
786 'check_permissions' => 1,
787 'id' => $activity['id'],
792 * To leverage ACL permission to view an activity you must be able to see any of the contacts.
795 public function testGetActivityByAclCannotViewAllContacts() {
796 $activity = $this->activityCreate(['assignee_contact_id' => $this->individualCreate()]);
797 $contacts = $this->getActivityContacts($activity);
798 $this->setPermissions(['access CiviCRM']);
800 foreach ($contacts as $role => $contact_id) {
801 $this->allowedContactId
= $contact_id;
802 $this->hookClass
->setHook('civicrm_aclWhereClause', [
806 $this->cleanupCachedPermissions();
807 $result = $this->callAPISuccessGetSingle('Activity', [
808 'check_permissions' => 1,
809 'id' => $activity['id'],
813 'assignee_contact_id',
821 $roleKey = $roleName . '_id';
822 if ($role !== $roleKey) {
823 $this->assertTrue(empty($result[$roleKey]), "Only contact in $role is permissioned to be returned, not $roleKey");
826 $this->assertEquals([$contact_id], (array) $result[$roleKey]);
827 $this->assertNotEmpty($result[$roleName . '_name']);
834 * To leverage ACL permission to view an activity you must be able to see any of the contacts.
836 * @param int $version
838 * @throws \CRM_Core_Exception
839 * @throws \CiviCRM_API3_Exception
840 * @dataProvider versionThreeAndFour
842 public function testGetActivityByAclCannotViewAnyContacts($version) {
843 $this->_apiversion
= $version;
844 $activity = $this->activityCreate();
845 $contacts = $this->getActivityContacts($activity);
846 $this->setPermissions(['access CiviCRM']);
848 foreach ($contacts as $contact_id) {
849 $this->callAPIFailure('Activity', 'getsingle', [
850 'check_permissions' => 1,
851 'id' => $activity['id'],
857 * Check that if the source contact is deleted but we can view the others we can see the activity.
861 * @param int $version
863 * @dataProvider versionThreeAndFour
864 * @throws \CiviCRM_API3_Exception
865 * @throws \CRM_Core_Exception
867 public function testGetActivityACLSourceContactDeleted($version) {
868 $this->_apiversion
= $version;
869 $this->setPermissions(['access CiviCRM', 'delete contacts']);
870 $activity = $this->activityCreate();
871 $contacts = $this->getActivityContacts($activity);
873 $this->hookClass
->setHook('civicrm_aclWhereClause', [
875 'aclWhereHookAllResults',
877 $this->contactDelete($contacts['source_contact_id']);
878 $this->callAPISuccess('Activity', 'getsingle', [
879 'check_permissions' => 1,
880 'id' => $activity['id'],
885 * Test get activities multiple ids with check permissions
887 * @see https://issues.civicrm.org/jira/browse/CRM-20441
889 * @param int $version
891 * @throws \CRM_Core_Exception
892 * @throws \CiviCRM_API3_Exception
893 * @dataProvider versionThreeAndFour
895 public function testActivitiesGetMultipleIdsCheckPermissions($version) {
896 $this->_apiversion
= $version;
897 $this->createLoggedInUser();
898 $activity = $this->activityCreate();
899 $activity2 = $this->activityCreate();
900 $this->setPermissions(['access CiviCRM']);
901 $this->hookClass
->setHook('civicrm_aclWhereClause', [
903 'aclWhereHookAllResults',
905 // Get activities associated with contact $this->_contactID.
907 'id' => ['IN' => [$activity['id'], $activity2['id']]],
908 'check_permissions' => TRUE,
910 $result = $this->callAPISuccess('activity', 'get', $params);
911 $this->assertEquals(2, $result['count']);
915 * Test get activities multiple ids with check permissions
916 * Limit access to One contact
918 * @see https://issues.civicrm.org/jira/browse/CRM-20441
920 * @param int $version
922 * @throws \CRM_Core_Exception
923 * @throws \CiviCRM_API3_Exception
924 * @dataProvider versionThreeAndFour
926 public function testActivitiesGetMultipleIdsCheckPermissionsLimitedACL($version) {
927 $this->_apiversion
= $version;
928 $this->createLoggedInUser();
929 $activity = $this->activityCreate();
930 $contacts = $this->getActivityContacts($activity);
931 $this->setPermissions(['access CiviCRM']);
932 foreach ($contacts as $contact_id) {
933 $this->allowedContacts
[] = $contact_id;
935 $this->hookClass
->setHook('civicrm_aclWhereClause', [
937 'aclWhereMultipleContacts',
939 $contact2 = $this->individualCreate();
940 $activity2 = $this->activityCreate(['source_contact_id' => $contact2]);
941 // Get activities associated with contact $this->_contactID.
943 'id' => ['IN' => [$activity['id']]],
944 'check_permissions' => TRUE,
946 $result = $this->callAPISuccess('activity', 'get', $params);
947 $this->assertEquals(1, $result['count']);
948 $this->callAPIFailure('activity', 'getsingle', array_merge($params, [
957 * Test get activities multiple ids with check permissions
959 * @see https://issues.civicrm.org/jira/browse/CRM-20441
961 * @param int $version
963 * @throws \CRM_Core_Exception
964 * @throws \CiviCRM_API3_Exception
965 * @dataProvider versionThreeAndFour
967 public function testActivitiesGetMultipleIdsCheckPermissionsNotIN($version) {
968 $this->_apiversion
= $version;
969 $this->createLoggedInUser();
970 $activity = $this->activityCreate();
971 $activity2 = $this->activityCreate();
972 $this->setPermissions(['access CiviCRM']);
973 $this->hookClass
->setHook('civicrm_aclWhereClause', [
975 'aclWhereHookAllResults',
977 // Get activities associated with contact $this->_contactID.
979 'id' => ['NOT IN' => [$activity['id'], $activity2['id']]],
980 'check_permissions' => TRUE,
982 $result = $this->callAPISuccess('activity', 'get', $params);
983 $this->assertEquals(0, $result['count']);
987 * Get the contacts for the activity.
992 * @throws \CRM_Core_Exception
994 protected function getActivityContacts($activity) {
997 $activityContacts = $this->callAPISuccess('ActivityContact', 'get', [
998 'activity_id' => $activity['id'],
1001 $activityRecordTypes = $this->callAPISuccess('ActivityContact', 'getoptions', ['field' => 'record_type_id']);
1002 foreach ($activityContacts['values'] as $activityContact) {
1003 $type = $activityRecordTypes['values'][$activityContact['record_type_id']];
1005 case 'Activity Source':
1006 $contacts['source_contact_id'] = $activityContact['contact_id'];
1009 case 'Activity Targets':
1010 $contacts['target_contact_id'] = $activityContact['contact_id'];
1013 case 'Activity Assignees':
1014 $contacts['assignee_contact_id'] = $activityContact['contact_id'];
1023 * Test that the 'everyone' group can be given access to a contact.
1026 public function testGetACLEveryonePermittedEntity() {
1027 $this->setupScenarioCoreACLEveryonePermittedToGroup();
1028 $this->callAPISuccessGetCount('Contact', [
1029 'id' => $this->scenarioIDs
['Contact']['permitted_contact'],
1030 'check_permissions' => 1,
1033 $this->callAPISuccessGetCount('Contact', [
1034 'id' => $this->scenarioIDs
['Contact']['non_permitted_contact'],
1035 'check_permissions' => 1,
1038 // Also check that we can access ACLs through a path that uses the acl_contact_cache table.
1039 // historically this has caused errors due to the key_constraint on that table.
1040 // This is a bit of an artificial check as we have to amp up permissions to access this api.
1041 // However, the lower level function is more directly accessed through the Contribution & Event & Profile
1042 $dupes = $this->callAPISuccess('Contact', 'duplicatecheck', [
1044 'first_name' => 'Anthony',
1045 'last_name' => 'Anderson',
1046 'contact_type' => 'Individual',
1047 'email' => 'anthony_anderson@civicrm.org',
1049 'check_permissions' => 0,
1051 $this->assertEquals(2, $dupes['count']);
1052 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= ['access CiviCRM'];
1054 $dupes = $this->callAPISuccess('Contact', 'duplicatecheck', [
1056 'first_name' => 'Anthony',
1057 'last_name' => 'Anderson',
1058 'contact_type' => 'Individual',
1059 'email' => 'anthony_anderson@civicrm.org',
1061 'check_permissions' => 1,
1063 $this->assertEquals(1, $dupes['count']);
1068 * @param int $version
1070 * @dataProvider versionThreeAndFour
1071 * @throws \CRM_Core_Exception
1073 public function testContactGetViaJoin($version) {
1074 $this->_apiversion
= $version;
1075 $this->createLoggedInUser();
1076 $main = $this->individualCreate(['first_name' => 'Main']);
1077 $other = $this->individualCreate(['first_name' => 'Other'], 1);
1078 $tag1 = $this->tagCreate(['name' => uniqid('created'), 'created_id' => $main])['id'];
1079 $tag2 = $this->tagCreate(['name' => uniqid('other'), 'created_id' => $other])['id'];
1080 $this->setPermissions(['access CiviCRM']);
1081 $this->hookClass
->setHook('civicrm_aclWhereClause', [$this, 'aclWhereHookAllResults']);
1082 $createdFirstName = $version === 4 ?
'created.first_name' : 'created_id.first_name';
1083 $result = $this->callAPISuccess('Tag', 'get', [
1084 'check_permissions' => 1,
1085 'return' => ['id', $createdFirstName],
1086 'id' => ['IN' => [$tag1, $tag2]],
1088 $this->assertEquals('Main', $result['values'][$tag1][$createdFirstName]);
1089 $this->assertEquals('Other', $result['values'][$tag2][$createdFirstName]);
1090 $this->allowedContactId
= $main;
1091 $this->hookClass
->setHook('civicrm_aclWhereClause', [$this, 'aclWhereOnlyOne']);
1092 $this->cleanupCachedPermissions();
1093 $result = $this->callAPISuccess('Tag', 'get', [
1094 'check_permissions' => 1,
1095 'return' => ['id', $createdFirstName],
1096 'id' => ['IN' => [$tag1, $tag2]],
1098 $this->assertEquals('Main', $result['values'][$tag1][$createdFirstName]);
1099 $this->assertEquals($tag2, $result['values'][$tag2]['id']);
1100 $this->assertFalse(isset($result['values'][$tag2][$createdFirstName]));
1103 public function testApi4CustomEntityACL() {
1104 $group = uniqid('mg');
1105 $textField = uniqid('tx');
1107 CustomGroup
::create(FALSE)
1108 ->addValue('name', $group)
1109 ->addValue('extends', 'Contact')
1110 ->addValue('is_multiple', TRUE)
1111 ->addChain('field', CustomField
::create()
1112 ->addValue('label', $textField)
1113 ->addValue('custom_group_id', '$id')
1114 ->addValue('html_type', 'Text')
1115 ->addValue('data_type', 'String')
1119 $this->createLoggedInUser();
1120 $c1 = $this->individualCreate(['first_name' => 'C1']);
1121 $c2 = $this->individualCreate(['first_name' => 'C2', 'is_deleted' => 1], 1);
1123 CustomValue
::save($group)->setCheckPermissions(FALSE)
1124 ->addRecord(['entity_id' => $c1, $textField => '1'])
1125 ->addRecord(['entity_id' => $c2, $textField => '2'])
1128 $this->setPermissions(['access CiviCRM', 'view debug output']);
1129 $this->hookClass
->setHook('civicrm_aclWhereClause', [$this, 'aclWhereHookAllResults']);
1131 // Without "access deleted contacts" we won't see C2
1132 $vals = CustomValue
::get($group)->setDebug(TRUE)->execute();
1133 $this->assertCount(1, $vals);
1134 $this->assertEquals($c1, $vals[0]['entity_id']);
1136 $this->setPermissions(['access CiviCRM', 'access deleted contacts', 'view debug output']);
1137 $this->hookClass
->setHook('civicrm_aclWhereClause', [$this, 'aclWhereHookAllResults']);
1138 $this->cleanupCachedPermissions();
1140 $vals = CustomValue
::get($group)->execute();
1141 $this->assertCount(2, $vals);
1143 $this->allowedContactId
= $c2;
1144 $this->hookClass
->setHook('civicrm_aclWhereClause', [$this, 'aclWhereOnlyOne']);
1145 $this->cleanupCachedPermissions();
1147 $vals = CustomValue
::get($group)->addSelect('*', 'contact.first_name')->execute();
1148 $this->assertCount(1, $vals);
1149 $this->assertEquals($c2, $vals[0]['entity_id']);
1150 $this->assertEquals('C2', $vals[0]['contact.first_name']);
1152 $vals = Contact
::get()
1153 ->addJoin('Custom_' . $group . ' AS cf')
1154 ->addSelect('first_name', 'cf.' . $textField)
1155 ->addWhere('is_deleted', '=', TRUE)
1157 $this->assertCount(1, $vals);
1158 $this->assertEquals('C2', $vals[0]['first_name']);
1159 $this->assertEquals('2', $vals[0]['cf.' . $textField]);