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