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