Commit | Line | Data |
---|---|---|
6a488035 | 1 | <?php |
6a488035 TO |
2 | /* |
3 | +--------------------------------------------------------------------+ | |
2fe49090 | 4 | | CiviCRM version 5 | |
6a488035 | 5 | +--------------------------------------------------------------------+ |
6b83d5bd | 6 | | Copyright CiviCRM LLC (c) 2004-2019 | |
6a488035 TO |
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 | ||
6a488035 TO |
28 | /** |
29 | * This class is intended to test ACL permission using the multisite module | |
30 | * | |
7884d958 | 31 | * @package CiviCRM_APIv3 |
32 | * @subpackage API_Contact | |
acb109b7 | 33 | * @group headless |
6a488035 | 34 | */ |
6a488035 | 35 | class api_v3_ACLPermissionTest extends CiviUnitTestCase { |
2af6f0c0 | 36 | |
37 | use CRMTraits_ACL_PermissionTrait; | |
38 | ||
4e420887 | 39 | protected $_apiversion = 3; |
4e420887 | 40 | public $DBResetRequired = FALSE; |
430ae6dd TO |
41 | protected $_entity; |
42 | ||
00be9182 | 43 | public function setUp() { |
6a488035 TO |
44 | parent::setUp(); |
45 | $baoObj = new CRM_Core_DAO(); | |
ff3833b0 | 46 | $baoObj->createTestObject('CRM_Pledge_BAO_Pledge', [], 1, 0); |
47 | $baoObj->createTestObject('CRM_Core_BAO_Phone', [], 1, 0); | |
5e8daa54 | 48 | $this->prepareForACLs(); |
6a488035 | 49 | } |
7884d958 | 50 | |
51 | /** | |
52 | * (non-PHPdoc) | |
53 | * @see CiviUnitTestCase::tearDown() | |
54 | */ | |
00be9182 | 55 | public function tearDown() { |
5e8daa54 | 56 | $this->cleanUpAfterACLs(); |
ff3833b0 | 57 | $tablesToTruncate = [ |
7884d958 | 58 | 'civicrm_contact', |
ae4bb4c9 EM |
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', | |
225d474b | 67 | 'civicrm_uf_match', |
bbd2743b | 68 | 'civicrm_activity', |
69 | 'civicrm_activity_contact', | |
c6835264 CW |
70 | 'civicrm_note', |
71 | 'civicrm_entity_tag', | |
72 | 'civicrm_tag', | |
ff3833b0 | 73 | ]; |
6a488035 | 74 | $this->quickCleanup($tablesToTruncate); |
6a488035 | 75 | } |
7884d958 | 76 | |
77 | /** | |
eceb18cc | 78 | * Function tests that an empty where hook returns no results. |
7884d958 | 79 | */ |
00be9182 | 80 | public function testContactGetNoResultsHook() { |
ff3833b0 | 81 | $this->hookClass->setHook('civicrm_aclWhereClause', [ |
82 | $this, | |
83 | 'aclWhereHookNoResults', | |
84 | ]); | |
85 | $result = $this->callAPISuccess('contact', 'get', [ | |
6a488035 TO |
86 | 'check_permissions' => 1, |
87 | 'return' => 'display_name', | |
ff3833b0 | 88 | ]); |
6a488035 TO |
89 | $this->assertEquals(0, $result['count']); |
90 | } | |
91 | ||
1028f75e | 92 | /** |
1a4651ba | 93 | * Function tests that an empty where hook returns exactly 1 result with "view my contact". |
1028f75e | 94 | * |
95 | * CRM-16512 caused contacts with Edit my contact to be able to view all records. | |
96 | */ | |
1a4651ba | 97 | public function testContactGetOneResultHookWithViewMyContact() { |
1028f75e | 98 | $this->createLoggedInUser(); |
ff3833b0 | 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', [ | |
1028f75e | 108 | 'check_permissions' => 1, |
109 | 'return' => 'display_name', | |
ff3833b0 | 110 | ]); |
1a4651ba CW |
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() { | |
1a4651ba | 118 | $cid = $this->createLoggedInUser(); |
ff3833b0 | 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', [ | |
1a4651ba CW |
128 | 'check_permissions' => 1, |
129 | 'id' => $cid, | |
ff3833b0 | 130 | ]); |
1028f75e | 131 | } |
132 | ||
52ed95a8 | 133 | /** |
134 | * Ensure contact permissions do not block contact-less location entities. | |
135 | */ | |
136 | public function testAddressWithoutContactIDAccess() { | |
137 | $ownID = $this->createLoggedInUser(); | |
ff3833b0 | 138 | CRM_Core_Config::singleton()->userPermissionClass->permissions = [ |
139 | 'access CiviCRM', | |
140 | 'view all contacts', | |
141 | ]; | |
142 | $this->callAPISuccess('Address', 'create', [ | |
52ed95a8 | 143 | 'city' => 'Mouseville', |
144 | 'location_type_id' => 'Main', | |
145 | 'api.LocBlock.create' => 1, | |
146 | 'contact_id' => $ownID, | |
ff3833b0 | 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 | ]); | |
52ed95a8 | 162 | } |
163 | ||
c16ed19b CW |
164 | /** |
165 | * Ensure contact permissions extend to related entities like email | |
166 | */ | |
167 | public function testRelatedEntityPermissions() { | |
0a61b6e2 | 168 | $this->createLoggedInUser(); |
ff3833b0 | 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 | ]; | |
c16ed19b | 186 | foreach ($testEntities as $entity => $params) { |
ff3833b0 | 187 | $params += [ |
c16ed19b CW |
188 | 'contact_id' => $disallowedContact, |
189 | 'check_permissions' => 1, | |
ff3833b0 | 190 | ]; |
c16ed19b CW |
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); | |
ff3833b0 | 193 | $this->callAPISuccess($entity, 'create', ['check_permissions' => 0] + $params); |
194 | $results = $this->callAPISuccess($entity, 'get', [ | |
195 | 'contact_id' => $disallowedContact, | |
196 | 'check_permissions' => 1, | |
197 | ]); | |
c16ed19b CW |
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); | |
ff3833b0 | 203 | $results = $this->callAPISuccess($entity, 'get', [ |
204 | 'contact_id' => $this->allowedContactId, | |
205 | 'check_permissions' => 1, | |
206 | ]); | |
c16ed19b CW |
207 | $this->assertGreaterThan(0, $results['count']); |
208 | } | |
ff3833b0 | 209 | $newTag = civicrm_api3('Tag', 'create', [ |
c6835264 | 210 | 'name' => 'Foo123', |
ff3833b0 | 211 | ]); |
212 | $relatedEntities = [ | |
213 | 'Note' => ['note' => 'abc'], | |
214 | 'EntityTag' => ['tag_id' => $newTag['id']], | |
215 | ]; | |
c6835264 | 216 | foreach ($relatedEntities as $entity => $params) { |
ff3833b0 | 217 | $params += [ |
c6835264 CW |
218 | 'entity_id' => $disallowedContact, |
219 | 'entity_table' => 'civicrm_contact', | |
220 | 'check_permissions' => 1, | |
ff3833b0 | 221 | ]; |
c6835264 CW |
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); | |
ff3833b0 | 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 | ]); | |
c6835264 CW |
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); | |
ff3833b0 | 235 | $results = $this->callAPISuccess($entity, 'get', [ |
236 | 'entity_id' => $this->allowedContactId, | |
237 | 'entity_table' => 'civicrm_contact', | |
238 | 'check_permissions' => 1, | |
239 | ]); | |
c6835264 CW |
240 | $this->assertGreaterThan(0, $results['count']); |
241 | } | |
c16ed19b CW |
242 | } |
243 | ||
6a488035 | 244 | /** |
eceb18cc | 245 | * Function tests all results are returned. |
7884d958 | 246 | */ |
00be9182 | 247 | public function testContactGetAllResultsHook() { |
ff3833b0 | 248 | $this->hookClass->setHook('civicrm_aclWhereClause', [ |
249 | $this, | |
250 | 'aclWhereHookAllResults', | |
251 | ]); | |
252 | $result = $this->callAPISuccess('contact', 'get', [ | |
7884d958 | 253 | 'check_permissions' => 1, |
254 | 'return' => 'display_name', | |
ff3833b0 | 255 | ]); |
6a488035 | 256 | |
6a488035 TO |
257 | $this->assertEquals(2, $result['count']); |
258 | } | |
7884d958 | 259 | |
6a488035 | 260 | /** |
eceb18cc | 261 | * Function tests that deleted contacts are not returned. |
7884d958 | 262 | */ |
00be9182 | 263 | public function testContactGetPermissionHookNoDeleted() { |
ff3833b0 | 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', [ | |
7884d958 | 270 | 'check_permissions' => 1, |
271 | 'return' => 'display_name', | |
ff3833b0 | 272 | ]); |
6a488035 TO |
273 | $this->assertEquals(1, $result['count']); |
274 | } | |
275 | ||
276 | /** | |
eceb18cc | 277 | * Test permissions limited by hook. |
6a488035 | 278 | */ |
00be9182 | 279 | public function testContactGetHookLimitingHook() { |
ff3833b0 | 280 | $this->hookClass->setHook('civicrm_aclWhereClause', [ |
281 | $this, | |
282 | 'aclWhereOnlySecond', | |
283 | ]); | |
6a488035 | 284 | |
ff3833b0 | 285 | $result = $this->callAPISuccess('contact', 'get', [ |
6a488035 TO |
286 | 'check_permissions' => 1, |
287 | 'return' => 'display_name', | |
ff3833b0 | 288 | ]); |
6a488035 TO |
289 | $this->assertEquals(1, $result['count']); |
290 | } | |
291 | ||
7884d958 | 292 | /** |
1028f75e | 293 | * Confirm that without check permissions we still get 2 contacts returned. |
7884d958 | 294 | */ |
00be9182 | 295 | public function testContactGetHookLimitingHookDontCheck() { |
ff3833b0 | 296 | $result = $this->callAPISuccess('contact', 'get', [ |
4e420887 | 297 | 'check_permissions' => 0, |
298 | 'return' => 'display_name', | |
ff3833b0 | 299 | ]); |
6a488035 TO |
300 | $this->assertEquals(2, $result['count']); |
301 | } | |
7884d958 | 302 | |
6a488035 | 303 | /** |
eceb18cc | 304 | * Check that id works as a filter. |
6a488035 | 305 | */ |
00be9182 | 306 | public function testContactGetIDFilter() { |
ff3833b0 | 307 | $this->hookClass->setHook('civicrm_aclWhereClause', [ |
308 | $this, | |
309 | 'aclWhereHookAllResults', | |
310 | ]); | |
311 | $result = $this->callAPISuccess('contact', 'get', [ | |
6a488035 TO |
312 | 'sequential' => 1, |
313 | 'id' => 2, | |
314 | 'check_permissions' => 1, | |
ff3833b0 | 315 | ]); |
6a488035 | 316 | |
6a488035 TO |
317 | $this->assertEquals(1, $result['count']); |
318 | $this->assertEquals(2, $result['id']); | |
319 | } | |
320 | ||
7884d958 | 321 | /** |
eceb18cc | 322 | * Check that address IS returned. |
7884d958 | 323 | */ |
00be9182 | 324 | public function testContactGetAddressReturned() { |
ff3833b0 | 325 | $this->hookClass->setHook('civicrm_aclWhereClause', [ |
326 | $this, | |
327 | 'aclWhereOnlySecond', | |
328 | ]); | |
329 | $fullresult = $this->callAPISuccess('contact', 'get', [ | |
7884d958 | 330 | 'sequential' => 1, |
ff3833b0 | 331 | ]); |
7884d958 | 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 | |
ff3833b0 | 335 | $elementsReturnDoesntSupport = [ |
7884d958 | 336 | 'prefix', |
7884d958 | 337 | 'suffix', |
7884d958 | 338 | 'gender', |
339 | 'current_employer', | |
340 | 'phone_id', | |
341 | 'phone_type_id', | |
342 | 'phone', | |
343 | 'worldregion_id', | |
21dfd5f5 | 344 | 'world_region', |
ff3833b0 | 345 | ]; |
7884d958 | 346 | $expectedReturnElements = array_diff(array_keys($fullresult['values'][0]), $elementsReturnDoesntSupport); |
ff3833b0 | 347 | $result = $this->callAPISuccess('contact', 'get', [ |
7884d958 | 348 | 'check_permissions' => 1, |
349 | 'return' => $expectedReturnElements, | |
350 | 'sequential' => 1, | |
ff3833b0 | 351 | ]); |
7884d958 | 352 | $this->assertEquals(1, $result['count']); |
353 | foreach ($expectedReturnElements as $element) { | |
354 | $this->assertArrayHasKey($element, $result['values'][0]); | |
6a488035 | 355 | } |
7884d958 | 356 | } |
357 | ||
358 | /** | |
eceb18cc | 359 | * Check that pledge IS not returned. |
7884d958 | 360 | */ |
00be9182 | 361 | public function testContactGetPledgeIDNotReturned() { |
ff3833b0 | 362 | $this->hookClass->setHook('civicrm_aclWhereClause', [ |
363 | $this, | |
364 | 'aclWhereHookAllResults', | |
365 | ]); | |
366 | $this->callAPISuccess('contact', 'get', [ | |
7884d958 | 367 | 'sequential' => 1, |
ff3833b0 | 368 | ]); |
369 | $result = $this->callAPISuccess('contact', 'get', [ | |
7884d958 | 370 | 'check_permissions' => 1, |
371 | 'return' => 'pledge_id', | |
372 | 'sequential' => 1, | |
ff3833b0 | 373 | ]); |
7884d958 | 374 | $this->assertArrayNotHasKey('pledge_id', $result['values'][0]); |
375 | } | |
6a488035 | 376 | |
7884d958 | 377 | /** |
eceb18cc | 378 | * Check that pledge IS not an allowable filter. |
7884d958 | 379 | */ |
00be9182 | 380 | public function testContactGetPledgeIDNotFiltered() { |
ff3833b0 | 381 | $this->hookClass->setHook('civicrm_aclWhereClause', [ |
382 | $this, | |
383 | 'aclWhereHookAllResults', | |
384 | ]); | |
385 | $this->callAPISuccess('contact', 'get', [ | |
7884d958 | 386 | 'sequential' => 1, |
ff3833b0 | 387 | ]); |
388 | $result = $this->callAPISuccess('contact', 'get', [ | |
7884d958 | 389 | 'check_permissions' => 1, |
390 | 'pledge_id' => 1, | |
391 | 'sequential' => 1, | |
ff3833b0 | 392 | ]); |
7884d958 | 393 | $this->assertEquals(2, $result['count']); |
394 | } | |
395 | ||
396 | /** | |
397 | * Check that chaining doesn't bypass permissions | |
398 | */ | |
00be9182 | 399 | public function testContactGetPledgeNotChainable() { |
ff3833b0 | 400 | $this->hookClass->setHook('civicrm_aclWhereClause', [ |
401 | $this, | |
402 | 'aclWhereOnlySecond', | |
403 | ]); | |
404 | $this->callAPISuccess('contact', 'get', [ | |
7884d958 | 405 | 'sequential' => 1, |
ff3833b0 | 406 | ]); |
407 | $this->callAPIFailure('contact', 'get', [ | |
408 | 'check_permissions' => 1, | |
409 | 'api.pledge.get' => 1, | |
410 | 'sequential' => 1, | |
411 | ], | |
d235daf6 | 412 | 'Error in call to Pledge_get : API permission check failed for Pledge/get call; insufficient permission: require access CiviCRM and access CiviPledge' |
7884d958 | 413 | ); |
414 | } | |
6a488035 | 415 | |
00be9182 | 416 | public function setupCoreACL() { |
ae4bb4c9 | 417 | $this->createLoggedInUser(); |
ff3833b0 | 418 | $this->_permissionedDisabledGroup = $this->groupCreate([ |
92915c55 TO |
419 | 'title' => 'pick-me-disabled', |
420 | 'is_active' => 0, | |
421 | 'name' => 'pick-me-disabled', | |
ff3833b0 | 422 | ]); |
423 | $this->_permissionedGroup = $this->groupCreate([ | |
92915c55 TO |
424 | 'title' => 'pick-me-active', |
425 | 'is_active' => 1, | |
426 | 'name' => 'pick-me-active', | |
ff3833b0 | 427 | ]); |
ae4bb4c9 EM |
428 | $this->setupACL(); |
429 | } | |
5896d037 | 430 | |
ae4bb4c9 EM |
431 | /** |
432 | * @dataProvider entities | |
433 | * confirm that without check permissions we still get 2 contacts returned | |
1e1fdcf6 | 434 | * @param $entity |
ae4bb4c9 | 435 | */ |
00be9182 | 436 | public function testEntitiesGetHookLimitingHookNoCheck($entity) { |
ff3833b0 | 437 | CRM_Core_Config::singleton()->userPermissionClass->permissions = []; |
ae4bb4c9 | 438 | $this->setUpEntities($entity); |
ff3833b0 | 439 | $this->hookClass->setHook('civicrm_aclWhereClause', [ |
440 | $this, | |
441 | 'aclWhereHookNoResults', | |
442 | ]); | |
443 | $result = $this->callAPISuccess($entity, 'get', [ | |
ae4bb4c9 EM |
444 | 'check_permissions' => 0, |
445 | 'return' => 'contact_id', | |
ff3833b0 | 446 | ]); |
ae4bb4c9 EM |
447 | $this->assertEquals(2, $result['count']); |
448 | } | |
449 | ||
450 | /** | |
451 | * @dataProvider entities | |
452 | * confirm that without check permissions we still get 2 entities returned | |
1e1fdcf6 | 453 | * @param $entity |
ae4bb4c9 | 454 | */ |
00be9182 | 455 | public function testEntitiesGetCoreACLLimitingHookNoCheck($entity) { |
ae4bb4c9 EM |
456 | $this->setupCoreACL(); |
457 | //CRM_Core_Config::singleton()->userPermissionClass->permissions = array(); | |
458 | $this->setUpEntities($entity); | |
ff3833b0 | 459 | $this->hookClass->setHook('civicrm_aclWhereClause', [ |
460 | $this, | |
461 | 'aclWhereHookNoResults', | |
462 | ]); | |
463 | $result = $this->callAPISuccess($entity, 'get', [ | |
ae4bb4c9 EM |
464 | 'check_permissions' => 0, |
465 | 'return' => 'contact_id', | |
ff3833b0 | 466 | ]); |
ae4bb4c9 EM |
467 | $this->assertEquals(2, $result['count']); |
468 | } | |
5896d037 | 469 | |
ae4bb4c9 EM |
470 | /** |
471 | * @dataProvider entities | |
472 | * confirm that with check permissions we don't get entities | |
1e1fdcf6 EM |
473 | * @param $entity |
474 | * @throws \PHPUnit_Framework_IncompleteTestError | |
ae4bb4c9 | 475 | */ |
00be9182 | 476 | public function testEntitiesGetCoreACLLimitingCheck($entity) { |
ae4bb4c9 EM |
477 | $this->setupCoreACL(); |
478 | $this->setUpEntities($entity); | |
ff3833b0 | 479 | $result = $this->callAPISuccess($entity, 'get', [ |
ae4bb4c9 EM |
480 | 'check_permissions' => 1, |
481 | 'return' => 'contact_id', | |
ff3833b0 | 482 | ]); |
ae4bb4c9 EM |
483 | $this->assertEquals(0, $result['count']); |
484 | } | |
485 | ||
ae4bb4c9 EM |
486 | /** |
487 | * @dataProvider entities | |
488 | * Function tests that an empty where hook returns no results | |
1028f75e | 489 | * @param string $entity |
1e1fdcf6 | 490 | * @throws \PHPUnit_Framework_IncompleteTestError |
ae4bb4c9 | 491 | */ |
00be9182 | 492 | public function testEntityGetNoResultsHook($entity) { |
ae4bb4c9 | 493 | $this->markTestIncomplete('hook acls only work with contacts so far'); |
ff3833b0 | 494 | CRM_Core_Config::singleton()->userPermissionClass->permissions = []; |
ae4bb4c9 | 495 | $this->setUpEntities($entity); |
ff3833b0 | 496 | $this->hookClass->setHook('civicrm_aclWhereClause', [ |
497 | $this, | |
498 | 'aclWhereHookNoResults', | |
499 | ]); | |
500 | $result = $this->callAPISuccess($entity, 'get', [ | |
ae4bb4c9 | 501 | 'check_permission' => 1, |
ff3833b0 | 502 | ]); |
ae4bb4c9 EM |
503 | $this->assertEquals(0, $result['count']); |
504 | } | |
505 | ||
506 | /** | |
507 | * @return array | |
508 | */ | |
509 | public static function entities() { | |
ff3833b0 | 510 | return [ |
511 | ['contribution'], | |
512 | ['participant'], | |
513 | ];// @todo array('pledge' => 'pledge') | |
ae4bb4c9 EM |
514 | } |
515 | ||
516 | /** | |
517 | * Create 2 entities | |
1e1fdcf6 | 518 | * @param $entity |
ae4bb4c9 EM |
519 | */ |
520 | public function setUpEntities($entity) { | |
521 | $baoObj = new CRM_Core_DAO(); | |
ff3833b0 | 522 | $baoObj->createTestObject(_civicrm_api3_get_BAO($entity), [], 2, 0); |
523 | CRM_Core_Config::singleton()->userPermissionClass->permissions = [ | |
ae4bb4c9 EM |
524 | 'access CiviCRM', |
525 | 'access CiviContribute', | |
526 | 'access CiviEvent', | |
527 | 'view event participants', | |
ff3833b0 | 528 | ]; |
ae4bb4c9 EM |
529 | } |
530 | ||
bbd2743b | 531 | /** |
532 | * Basic check that an unpermissioned call keeps working and permissioned call fails. | |
533 | */ | |
534 | public function testGetActivityNoPermissions() { | |
ff3833b0 | 535 | $this->setPermissions([]); |
536 | $this->callAPISuccess('Activity', 'get', []); | |
537 | $this->callAPIFailure('Activity', 'get', ['check_permissions' => 1]); | |
bbd2743b | 538 | } |
539 | ||
540 | /** | |
541 | * View all activities is enough regardless of contact ACLs. | |
542 | */ | |
cdacd6ab | 543 | public function testGetActivityViewAllActivitiesDoesntCutItAnymore() { |
bbd2743b | 544 | $activity = $this->activityCreate(); |
ff3833b0 | 545 | $this->setPermissions(['view all activities', 'access CiviCRM']); |
546 | $this->callAPISuccessGetCount('Activity', [ | |
547 | 'check_permissions' => 1, | |
548 | 'id' => $activity['id'], | |
549 | ], 0); | |
bbd2743b | 550 | } |
551 | ||
552 | /** | |
553 | * View all activities is required unless id is passed in. | |
554 | */ | |
cdacd6ab | 555 | public function testGetActivityViewAllContactsEnoughWithoutID() { |
ff3833b0 | 556 | $this->setPermissions(['view all contacts', 'access CiviCRM']); |
557 | $this->callAPISuccess('Activity', 'get', ['check_permissions' => 1]); | |
bbd2743b | 558 | } |
559 | ||
560 | /** | |
3af8de9f | 561 | * Without view all activities contact level acls are used. |
bbd2743b | 562 | */ |
563 | public function testGetActivityViewAllContactsEnoughWIthID() { | |
564 | $activity = $this->activityCreate(); | |
ff3833b0 | 565 | $this->setPermissions(['view all contacts', 'access CiviCRM']); |
566 | $this->callAPISuccess('Activity', 'getsingle', [ | |
567 | 'check_permissions' => 1, | |
568 | 'id' => $activity['id'], | |
569 | ]); | |
bbd2743b | 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(); | |
ff3833b0 | 577 | $this->setPermissions(['access CiviCRM']); |
578 | $this->callAPIFailure('Activity', 'getsingle', [ | |
579 | 'check_permissions' => 1, | |
580 | 'id' => $activity['id'], | |
581 | ]); | |
bbd2743b | 582 | } |
583 | ||
3af8de9f | 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']); | |
ff3833b0 | 596 | $this->hookClass->setHook('civicrm_aclWhereClause', [ |
597 | $this, | |
598 | 'aclWhereHookAllResults', | |
599 | ]); | |
3af8de9f | 600 | $this->setPermissions(['access CiviCRM', 'access CiviContribute']); |
ff3833b0 | 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); | |
ff2a3553 | 609 | |
3af8de9f | 610 | } |
611 | ||
c4937fe9 | 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']); | |
ff3833b0 | 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); | |
c4937fe9 | 636 | } |
637 | ||
bbd2743b | 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() { | |
ff3833b0 | 645 | $this->setPermissions(['access CiviCRM']); |
bbd2743b | 646 | $activity = $this->activityCreate(); |
647 | ||
ff3833b0 | 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 | ]); | |
bbd2743b | 660 | } |
661 | ||
662 | /** | |
cdacd6ab | 663 | * To leverage ACL permission to view an activity you must be able to see any of the contacts. |
bbd2743b | 664 | */ |
665 | public function testGetActivityByAclCannotViewAllContacts() { | |
cdacd6ab | 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; | |
ff3833b0 | 672 | $this->hookClass->setHook('civicrm_aclWhereClause', [ |
673 | $this, | |
674 | 'aclWhereOnlyOne', | |
675 | ]); | |
cdacd6ab | 676 | $this->cleanupCachedPermissions(); |
677 | $result = $this->callAPISuccessGetSingle('Activity', [ | |
678 | 'check_permissions' => 1, | |
679 | 'id' => $activity['id'], | |
ff3833b0 | 680 | 'return' => [ |
681 | 'source_contact_id', | |
682 | 'target_contact_id', | |
683 | 'assignee_contact_id', | |
684 | ], | |
cdacd6ab | 685 | ]); |
ff3833b0 | 686 | foreach ([ |
687 | 'source_contact', | |
688 | 'target_contact', | |
689 | 'assignee_contact', | |
690 | ] as $roleName) { | |
cdacd6ab | 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() { | |
bbd2743b | 707 | $activity = $this->activityCreate(); |
708 | $contacts = $this->getActivityContacts($activity); | |
ff3833b0 | 709 | $this->setPermissions(['access CiviCRM']); |
bbd2743b | 710 | |
711 | foreach ($contacts as $contact_id) { | |
ff3833b0 | 712 | $this->callAPIFailure('Activity', 'getsingle', [ |
713 | 'check_permissions' => 1, | |
714 | 'id' => $activity['id'], | |
715 | ]); | |
bbd2743b | 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() { | |
ff3833b0 | 727 | $this->setPermissions(['access CiviCRM', 'delete contacts']); |
bbd2743b | 728 | $activity = $this->activityCreate(); |
729 | $contacts = $this->getActivityContacts($activity); | |
730 | ||
ff3833b0 | 731 | $this->hookClass->setHook('civicrm_aclWhereClause', [ |
732 | $this, | |
733 | 'aclWhereHookAllResults', | |
734 | ]); | |
bbd2743b | 735 | $this->contactDelete($contacts['source_contact_id']); |
ff3833b0 | 736 | $this->callAPISuccess('Activity', 'getsingle', [ |
737 | 'check_permissions' => 1, | |
738 | 'id' => $activity['id'], | |
739 | ]); | |
bbd2743b | 740 | } |
741 | ||
f404486e SL |
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(); | |
ff3833b0 | 750 | $this->setPermissions(['access CiviCRM']); |
751 | $this->hookClass->setHook('civicrm_aclWhereClause', [ | |
752 | $this, | |
753 | 'aclWhereHookAllResults', | |
754 | ]); | |
f404486e | 755 | // Get activities associated with contact $this->_contactID. |
ff3833b0 | 756 | $params = [ |
757 | 'id' => ['IN' => [$activity['id'], $activity2['id']]], | |
f404486e | 758 | 'check_permissions' => TRUE, |
ff3833b0 | 759 | ]; |
f404486e SL |
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); | |
ff3833b0 | 773 | $this->setPermissions(['access CiviCRM']); |
f404486e SL |
774 | foreach ($contacts as $contact_id) { |
775 | $this->allowedContacts[] = $contact_id; | |
776 | } | |
ff3833b0 | 777 | $this->hookClass->setHook('civicrm_aclWhereClause', [ |
778 | $this, | |
779 | 'aclWhereMultipleContacts', | |
780 | ]); | |
f404486e | 781 | $contact2 = $this->individualCreate(); |
ff3833b0 | 782 | $activity2 = $this->activityCreate(['source_contact_id' => $contact2]); |
f404486e | 783 | // Get activities associated with contact $this->_contactID. |
ff3833b0 | 784 | $params = [ |
785 | 'id' => ['IN' => [$activity['id']]], | |
f404486e | 786 | 'check_permissions' => TRUE, |
ff3833b0 | 787 | ]; |
f404486e SL |
788 | $result = $this->callAPISuccess('activity', 'get', $params); |
789 | $this->assertEquals(1, $result['count']); | |
ff3833b0 | 790 | $this->callAPIFailure('activity', 'get', array_merge($params, [ |
791 | 'id' => [ | |
792 | 'IN', | |
793 | [$activity2['id']], | |
794 | ], | |
795 | ])); | |
f404486e SL |
796 | } |
797 | ||
dfe0e2e1 SL |
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(); | |
ff3833b0 | 806 | $this->setPermissions(['access CiviCRM']); |
807 | $this->hookClass->setHook('civicrm_aclWhereClause', [ | |
808 | $this, | |
809 | 'aclWhereHookAllResults', | |
810 | ]); | |
dfe0e2e1 | 811 | // Get activities associated with contact $this->_contactID. |
ff3833b0 | 812 | $params = [ |
813 | 'id' => ['NOT IN' => [$activity['id'], $activity2['id']]], | |
dfe0e2e1 | 814 | 'check_permissions' => TRUE, |
ff3833b0 | 815 | ]; |
3c9d67b0 | 816 | $result = $this->callAPISuccess('activity', 'get', $params); |
817 | $this->assertEquals(0, $result['count']); | |
dfe0e2e1 SL |
818 | } |
819 | ||
bbd2743b | 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) { | |
ff3833b0 | 829 | $contacts = []; |
bbd2743b | 830 | |
ff3833b0 | 831 | $activityContacts = $this->callAPISuccess('ActivityContact', 'get', [ |
bbd2743b | 832 | 'activity_id' => $activity['id'], |
ff3833b0 | 833 | ] |
bbd2743b | 834 | ); |
835 | ||
ff3833b0 | 836 | $activityRecordTypes = $this->callAPISuccess('ActivityContact', 'getoptions', ['field' => 'record_type_id']); |
bbd2743b | 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 | ||
8e12938a | 857 | /** |
858 | * Test that the 'everyone' group can be given access to a contact. | |
859 | */ | |
860 | public function testGetACLEveryonePermittedEntity() { | |
861 | $this->setupScenarioCoreACLEveryonePermittedToGroup(); | |
1bcdee33 | 862 | $this->callAPISuccessGetCount('Contact', [ |
8e12938a | 863 | 'id' => $this->scenarioIDs['Contact']['permitted_contact'], |
864 | 'check_permissions' => 1, | |
1bcdee33 | 865 | ], 1); |
866 | ||
867 | $this->callAPISuccessGetCount('Contact', [ | |
868 | 'id' => $this->scenarioIDs['Contact']['non_permitted_contact'], | |
869 | 'check_permissions' => 1, | |
870 | ], 0); | |
1876e376 | 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 | ]); | |
a99b82c5 | 885 | $this->assertEquals(2, $dupes['count']); |
678bd58a | 886 | CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM']; |
1876e376 | 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 | ||
8e12938a | 899 | } |
900 | ||
6a488035 | 901 | } |