3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2015 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
29 * This class is intended to test ACL permission using the multisite module
31 * @package CiviCRM_APIv3
32 * @subpackage API_Contact
34 class api_v3_ACLPermissionTest
extends CiviUnitTestCase
{
35 protected $_apiversion = 3;
36 public $DBResetRequired = FALSE;
38 protected $allowedContactId = 0;
40 public function setUp() {
42 $baoObj = new CRM_Core_DAO();
43 $baoObj->createTestObject('CRM_Pledge_BAO_Pledge', array(), 1, 0);
44 $baoObj->createTestObject('CRM_Core_BAO_Phone', array(), 1, 0);
45 $config = CRM_Core_Config
::singleton();
46 $config->userPermissionClass
->permissions
= array();
51 * @see CiviUnitTestCase::tearDown()
53 public function tearDown() {
54 CRM_Utils_Hook
::singleton()->reset();
55 $tablesToTruncate = array(
57 'civicrm_group_contact',
61 'civicrm_acl_entity_role',
62 'civicrm_acl_contact_cache',
63 'civicrm_contribution',
64 'civicrm_participant',
67 $this->quickCleanup($tablesToTruncate);
68 $config = CRM_Core_Config
::singleton();
69 unset($config->userPermissionClass
->permissions
);
73 * Function tests that an empty where hook returns no results.
75 public function testContactGetNoResultsHook() {
76 $this->hookClass
->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookNoResults'));
77 $result = $this->callAPISuccess('contact', 'get', array(
78 'check_permissions' => 1,
79 'return' => 'display_name',
81 $this->assertEquals(0, $result['count']);
85 * Function tests that an empty where hook returns exactly 1 result with "view my contact".
87 * CRM-16512 caused contacts with Edit my contact to be able to view all records.
89 public function testContactGetOneResultHookWithViewMyContact() {
90 $this->createLoggedInUser();
91 $this->hookClass
->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookNoResults'));
92 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= array('access CiviCRM', 'view my contact');
93 $result = $this->callAPISuccess('contact', 'get', array(
94 'check_permissions' => 1,
95 'return' => 'display_name',
97 $this->assertEquals(1, $result['count']);
101 * Function tests that a user with "edit my contact" can edit themselves.
103 public function testContactEditHookWithEditMyContact() {
104 $cid = $this->createLoggedInUser();
105 $this->hookClass
->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookNoResults'));
106 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= array('access CiviCRM', 'edit my contact');
107 $this->callAPISuccess('contact', 'create', array(
108 'check_permissions' => 1,
114 * Ensure contact permissions extend to related entities like email
116 public function testRelatedEntityPermissions() {
117 $this->createLoggedInUser();
118 $disallowedContact = $this->individualCreate(array(), 0);
119 $this->allowedContactId
= $this->individualCreate(array(), 1);
120 $this->hookClass
->setHook('civicrm_aclWhereClause', array($this, 'aclWhereOnlyOne'));
121 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= array('access CiviCRM');
122 $testEntities = array(
123 'Email' => array('email' => 'null@nothing', 'location_type_id' => 1),
124 'Phone' => array('phone' => '123456', 'location_type_id' => 1),
125 'IM' => array('name' => 'hello', 'location_type_id' => 1),
126 'Website' => array('url' => 'http://test'),
127 'Address' => array('street_address' => '123 Sesame St.', 'location_type_id' => 1),
129 foreach ($testEntities as $entity => $params) {
131 'contact_id' => $disallowedContact,
132 'check_permissions' => 1,
134 // We should be prevented from getting or creating entities for a contact we don't have permission for
135 $this->callAPIFailure($entity, 'create', $params);
136 $results = $this->callAPISuccess($entity, 'get', array('contact_id' => $disallowedContact, 'check_permissions' => 1));
137 $this->assertEquals(0, $results['count']);
139 // We should be allowed to create and get for contacts we do have permission on
140 $params['contact_id'] = $this->allowedContactId
;
141 $this->callAPISuccess($entity, 'create', $params);
142 $results = $this->callAPISuccess($entity, 'get', array('contact_id' => $this->allowedContactId
, 'check_permissions' => 1));
143 $this->assertGreaterThan(0, $results['count']);
148 * Function tests all results are returned.
150 public function testContactGetAllResultsHook() {
151 $this->hookClass
->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookAllResults'));
152 $result = $this->callAPISuccess('contact', 'get', array(
153 'check_permissions' => 1,
154 'return' => 'display_name',
157 $this->assertEquals(2, $result['count']);
161 * Function tests that deleted contacts are not returned.
163 public function testContactGetPermissionHookNoDeleted() {
164 $this->callAPISuccess('contact', 'create', array('id' => 2, 'is_deleted' => 1));
165 $this->hookClass
->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookAllResults'));
166 $result = $this->callAPISuccess('contact', 'get', array(
167 'check_permissions' => 1,
168 'return' => 'display_name',
170 $this->assertEquals(1, $result['count']);
174 * Test permissions limited by hook.
176 public function testContactGetHookLimitingHook() {
177 $this->hookClass
->setHook('civicrm_aclWhereClause', array($this, 'aclWhereOnlySecond'));
179 $result = $this->callAPISuccess('contact', 'get', array(
180 'check_permissions' => 1,
181 'return' => 'display_name',
183 $this->assertEquals(1, $result['count']);
187 * Confirm that without check permissions we still get 2 contacts returned.
189 public function testContactGetHookLimitingHookDontCheck() {
190 $result = $this->callAPISuccess('contact', 'get', array(
191 'check_permissions' => 0,
192 'return' => 'display_name',
194 $this->assertEquals(2, $result['count']);
198 * Check that id works as a filter.
200 public function testContactGetIDFilter() {
201 $this->hookClass
->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookAllResults'));
202 $result = $this->callAPISuccess('contact', 'get', array(
205 'check_permissions' => 1,
208 $this->assertEquals(1, $result['count']);
209 $this->assertEquals(2, $result['id']);
213 * Check that address IS returned.
215 public function testContactGetAddressReturned() {
216 $this->hookClass
->setHook('civicrm_aclWhereClause', array($this, 'aclWhereOnlySecond'));
217 $fullresult = $this->callAPISuccess('contact', 'get', array(
220 //return doesn't work for all keys - can't fix that here so let's skip ...
221 //prefix & suffix are inconsistent due to CRM-7929
222 // unsure about others but return doesn't work on them
223 $elementsReturnDoesntSupport = array(
234 $expectedReturnElements = array_diff(array_keys($fullresult['values'][0]), $elementsReturnDoesntSupport);
235 $result = $this->callAPISuccess('contact', 'get', array(
236 'check_permissions' => 1,
237 'return' => $expectedReturnElements,
240 $this->assertEquals(1, $result['count']);
241 foreach ($expectedReturnElements as $element) {
242 $this->assertArrayHasKey($element, $result['values'][0]);
247 * Check that pledge IS not returned.
249 public function testContactGetPledgeIDNotReturned() {
250 $this->hookClass
->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookAllResults'));
251 $this->callAPISuccess('contact', 'get', array(
254 $result = $this->callAPISuccess('contact', 'get', array(
255 'check_permissions' => 1,
256 'return' => 'pledge_id',
259 $this->assertArrayNotHasKey('pledge_id', $result['values'][0]);
263 * Check that pledge IS not an allowable filter.
265 public function testContactGetPledgeIDNotFiltered() {
266 $this->hookClass
->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookAllResults'));
267 $this->callAPISuccess('contact', 'get', array(
270 $result = $this->callAPISuccess('contact', 'get', array(
271 'check_permissions' => 1,
275 $this->assertEquals(2, $result['count']);
279 * Check that chaining doesn't bypass permissions
281 public function testContactGetPledgeNotChainable() {
282 $this->hookClass
->setHook('civicrm_aclWhereClause', array($this, 'aclWhereOnlySecond'));
283 $this->callAPISuccess('contact', 'get', array(
286 $this->callAPIFailure('contact', 'get', array(
287 'check_permissions' => 1,
288 'api.pledge.get' => 1,
291 'Error in call to pledge_get : API permission check failed for pledge/get call; missing permission: access CiviCRM.'
295 public function setupCoreACL() {
296 $this->createLoggedInUser();
297 $this->_permissionedDisabledGroup
= $this->groupCreate(array(
298 'title' => 'pick-me-disabled',
300 'name' => 'pick-me-disabled',
302 $this->_permissionedGroup
= $this->groupCreate(array(
303 'title' => 'pick-me-active',
305 'name' => 'pick-me-active',
311 * @dataProvider entities
312 * confirm that without check permissions we still get 2 contacts returned
315 public function testEntitiesGetHookLimitingHookNoCheck($entity) {
316 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= array();
317 $this->setUpEntities($entity);
318 $this->hookClass
->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookNoResults'));
319 $result = $this->callAPISuccess($entity, 'get', array(
320 'check_permissions' => 0,
321 'return' => 'contact_id',
323 $this->assertEquals(2, $result['count']);
327 * @dataProvider entities
328 * confirm that without check permissions we still get 2 entities returned
331 public function testEntitiesGetCoreACLLimitingHookNoCheck($entity) {
332 $this->setupCoreACL();
333 //CRM_Core_Config::singleton()->userPermissionClass->permissions = array();
334 $this->setUpEntities($entity);
335 $this->hookClass
->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookNoResults'));
336 $result = $this->callAPISuccess($entity, 'get', array(
337 'check_permissions' => 0,
338 'return' => 'contact_id',
340 $this->assertEquals(2, $result['count']);
344 * @dataProvider entities
345 * confirm that with check permissions we don't get entities
347 * @throws \PHPUnit_Framework_IncompleteTestError
349 public function testEntitiesGetCoreACLLimitingCheck($entity) {
350 $this->markTestIncomplete('this does not work in 4.4 but can be enabled in 4.5 or a security release of 4.4 including the important security fix CRM-14877');
351 $this->setupCoreACL();
352 $this->setUpEntities($entity);
353 $result = $this->callAPISuccess($entity, 'get', array(
354 'check_permissions' => 1,
355 'return' => 'contact_id',
357 $this->assertEquals(0, $result['count']);
361 * @dataProvider entities
362 * Function tests that an empty where hook returns no results
363 * @param string $entity
364 * @throws \PHPUnit_Framework_IncompleteTestError
366 public function testEntityGetNoResultsHook($entity) {
367 $this->markTestIncomplete('hook acls only work with contacts so far');
368 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= array();
369 $this->setUpEntities($entity);
370 $this->hookClass
->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookNoResults'));
371 $result = $this->callAPISuccess($entity, 'get', array(
372 'check_permission' => 1,
374 $this->assertEquals(0, $result['count']);
380 public static function entities() {
381 return array(array('contribution'), array('participant'));// @todo array('pledge' => 'pledge')
388 public function setUpEntities($entity) {
389 $baoObj = new CRM_Core_DAO();
390 $baoObj->createTestObject(_civicrm_api3_get_BAO($entity), array(), 2, 0);
391 CRM_Core_Config
::singleton()->userPermissionClass
->permissions
= array(
393 'access CiviContribute',
395 'view event participants',
400 * No results returned.
402 * @implements CRM_Utils_Hook::aclWhereClause
404 * @param string $type
405 * @param array $tables
406 * @param array $whereTables
407 * @param int $contactID
408 * @param string $where
410 public function aclWhereHookNoResults($type, &$tables, &$whereTables, &$contactID, &$where) {
414 * All results returned.
416 * @implements CRM_Utils_Hook::aclWhereClause
418 * @param string $type
419 * @param array $tables
420 * @param array $whereTables
421 * @param int $contactID
422 * @param string $where
424 public function aclWhereHookAllResults($type, &$tables, &$whereTables, &$contactID, &$where) {
429 * All but first results returned.
430 * @implements CRM_Utils_Hook::aclWhereClause
433 * @param $whereTables
437 public function aclWhereOnlySecond($type, &$tables, &$whereTables, &$contactID, &$where) {
438 $where = " contact_a.id > 1";
442 * Only specified contact returned.
443 * @implements CRM_Utils_Hook::aclWhereClause
446 * @param $whereTables
450 public function aclWhereOnlyOne($type, &$tables, &$whereTables, &$contactID, &$where) {
451 $where = " contact_a.id = " . $this->allowedContactId
;