Merge branch 'CRM-14696-v2' of https://github.com/JKingsnorth/civicrm-core into CRM...
[civicrm-core.git] / tests / phpunit / api / v3 / ACLPermissionTest.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
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 require_once 'CiviTest/CiviUnitTestCase.php';
29
30 /**
31 * This class is intended to test ACL permission using the multisite module
32 *
33 * @package CiviCRM_APIv3
34 * @subpackage API_Contact
35 */
36
37 class api_v3_ACLPermissionTest extends CiviUnitTestCase {
38 protected $_apiversion = 3;
39 protected $_params;
40
41 /**
42 * @var CRM_Utils_Hook_UnitTests
43 */
44 protected $hookClass = NULL;
45 public $DBResetRequired = FALSE;
46
47
48
49 protected $_entity;
50
51 function setUp() {
52 parent::setUp();
53 $baoObj = new CRM_Core_DAO();
54 $baoObj->createTestObject('CRM_Pledge_BAO_Pledge', array(), 1, 0);
55 $baoObj->createTestObject('CRM_Core_BAO_Phone', array(), 1, 0);
56 $this->hookClass = CRM_Utils_Hook::singleton();
57 $config = CRM_Core_Config::singleton();
58 $config->userPermissionClass->permissions = array();
59 }
60
61 /**
62 * (non-PHPdoc)
63 * @see CiviUnitTestCase::tearDown()
64 */
65 function tearDown() {
66 CRM_Utils_Hook::singleton()->reset();
67 $tablesToTruncate = array(
68 'civicrm_contact',
69 'civicrm_group_contact',
70 'civicrm_group',
71 'civicrm_acl',
72 'civicrm_acl_cache',
73 'civicrm_acl_entity_role',
74 'civicrm_acl_contact_cache',
75 'civicrm_contribution',
76 'civicrm_participant',
77 );
78 $this->quickCleanup($tablesToTruncate);
79 $config = CRM_Core_Config::singleton();
80 unset($config->userPermissionClass->permissions);
81 }
82
83 /**
84 * Function tests that an empty where hook returns no results
85 */
86 function testContactGetNoResultsHook() {
87 $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookNoResults'));
88 $result = $this->callAPISuccess('contact', 'get', array(
89 'check_permissions' => 1,
90 'return' => 'display_name',
91 ));
92 $this->assertEquals(0, $result['count']);
93 }
94
95 /**
96 * Function tests all results are returned
97 */
98 function testContactGetAllResultsHook() {
99 $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookAllResults'));
100 $result = $this->callAPISuccess('contact', 'get', array(
101 'check_permissions' => 1,
102 'return' => 'display_name',
103 ));
104
105 $this->assertEquals(2, $result['count']);
106 }
107
108 /**
109 * Function tests that deleted contacts are not returned
110 */
111 function testContactGetPermissionHookNoDeleted() {
112 $this->callAPISuccess('contact', 'create', array('id' => 2, 'is_deleted' => 1));
113 $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookAllResults'));
114 $result = $this->callAPISuccess('contact', 'get', array(
115 'check_permissions' => 1,
116 'return' => 'display_name',
117 ));
118 $this->assertEquals(1, $result['count']);
119 }
120
121 /**
122 * Test permissions limited by hook
123 */
124 function testContactGetHookLimitingHook() {
125 $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'aclWhereOnlySecond'));
126
127 $result = $this->callAPISuccess('contact', 'get', array(
128 'check_permissions' => 1,
129 'return' => 'display_name',
130 ));
131 $this->assertEquals(1, $result['count']);
132 }
133
134 /**
135 * Confirm that without check permissions we still get 2 contacts returned
136 */
137 function testContactGetHookLimitingHookDontCheck() {
138 //
139 $result = $this->callAPISuccess('contact', 'get', array(
140 'check_permissions' => 0,
141 'return' => 'display_name',
142 ));
143 $this->assertEquals(2, $result['count']);
144 }
145
146 /**
147 * Check that id works as a filter
148 */
149 function testContactGetIDFilter() {
150 $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookAllResults'));
151 $result = $this->callAPISuccess('contact', 'get', array(
152 'sequential' => 1,
153 'id' => 2,
154 'check_permissions' => 1,
155 ));
156
157 $this->assertEquals(1, $result['count']);
158 $this->assertEquals(2, $result['id']);
159 }
160
161 /**
162 * Check that address IS returned
163 */
164 function testContactGetAddressReturned() {
165 $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'aclWhereOnlySecond'));
166 $fullresult = $this->callAPISuccess('contact', 'get', array(
167 'sequential' => 1,
168 ));
169 //return doesn't work for all keys - can't fix that here so let's skip ...
170 //prefix & suffix are inconsistent due to CRM-7929
171 // unsure about others but return doesn't work on them
172 $elementsReturnDoesntSupport = array(
173 'prefix',
174 'suffix',
175 'gender',
176 'current_employer',
177 'phone_id',
178 'phone_type_id',
179 'phone',
180 'worldregion_id',
181 'world_region'
182 );
183 $expectedReturnElements = array_diff(array_keys($fullresult['values'][0]), $elementsReturnDoesntSupport);
184 $result = $this->callAPISuccess('contact', 'get', array(
185 'check_permissions' => 1,
186 'return' => $expectedReturnElements,
187 'sequential' => 1,
188 ));
189 $this->assertEquals(1, $result['count']);
190 foreach ($expectedReturnElements as $element) {
191 $this->assertArrayHasKey($element, $result['values'][0]);
192 }
193 }
194
195 /**
196 * Check that pledge IS not returned
197 */
198 function testContactGetPledgeIDNotReturned() {
199 $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookAllResults'));
200 $this->callAPISuccess('contact', 'get', array(
201 'sequential' => 1,
202 ));
203 $result = $this->callAPISuccess('contact', 'get', array(
204 'check_permissions' => 1,
205 'return' => 'pledge_id',
206 'sequential' => 1,
207 ));
208 $this->assertArrayNotHasKey('pledge_id', $result['values'][0]);
209 }
210
211 /**
212 * Check that pledge IS not an allowable filter
213 */
214 function testContactGetPledgeIDNotFiltered() {
215 $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookAllResults'));
216 $this->callAPISuccess('contact', 'get', array(
217 'sequential' => 1,
218 ));
219 $result = $this->callAPISuccess('contact', 'get', array(
220 'check_permissions' => 1,
221 'pledge_id' => 1,
222 'sequential' => 1,
223 ));
224 $this->assertEquals(2, $result['count']);
225 }
226
227 /**
228 * Check that chaining doesn't bypass permissions
229 */
230 function testContactGetPledgeNotChainable() {
231 $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'aclWhereOnlySecond'));
232 $this->callAPISuccess('contact', 'get', array(
233 'sequential' => 1,
234 ));
235 $this->callAPIFailure('contact', 'get', array(
236 'check_permissions' => 1,
237 'api.pledge.get' => 1,
238 'sequential' => 1,
239 ),
240 'Error in call to pledge_get : API permission check failed for pledge/get call; missing permission: access CiviCRM.'
241 );
242 }
243
244 function setupCoreACL() {
245 $this->createLoggedInUser();
246 $this->_permissionedDisabledGroup = $this->groupCreate(array('title' => 'pick-me-disabled', 'is_active' => 0, 'name' => 'pick-me-disabled'));
247 $this->_permissionedGroup = $this->groupCreate(array('title' => 'pick-me-active', 'is_active' => 1, 'name' => 'pick-me-active'));
248 $this->setupACL();
249 }
250 /**
251 * @dataProvider entities
252 * confirm that without check permissions we still get 2 contacts returned
253 */
254 function testEntitiesGetHookLimitingHookNoCheck($entity) {
255 CRM_Core_Config::singleton()->userPermissionClass->permissions = array();
256 $this->setUpEntities($entity);
257 $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookNoResults'));
258 $result = $this->callAPISuccess($entity, 'get', array(
259 'check_permissions' => 0,
260 'return' => 'contact_id',
261 ));
262 $this->assertEquals(2, $result['count']);
263 }
264
265 /**
266 * @dataProvider entities
267 * confirm that without check permissions we still get 2 entities returned
268 */
269 function testEntitiesGetCoreACLLimitingHookNoCheck($entity) {
270 $this->setupCoreACL();
271 //CRM_Core_Config::singleton()->userPermissionClass->permissions = array();
272 $this->setUpEntities($entity);
273 $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookNoResults'));
274 $result = $this->callAPISuccess($entity, 'get', array(
275 'check_permissions' => 0,
276 'return' => 'contact_id',
277 ));
278 $this->assertEquals(2, $result['count']);
279 }
280 /**
281 * @dataProvider entities
282 * confirm that with check permissions we don't get entities
283 */
284 function testEntitiesGetCoreACLLimitingCheck($entity) {
285 $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');
286 $this->setupCoreACL();
287 $this->setUpEntities($entity);
288 $result = $this->callAPISuccess($entity, 'get', array(
289 'check_permissions' => 1,
290 'return' => 'contact_id',
291 ));
292 $this->assertEquals(0, $result['count']);
293 }
294
295
296 /**
297 * @dataProvider entities
298 * Function tests that an empty where hook returns no results
299 */
300 function testEntityGetNoResultsHook($entity) {
301 $this->markTestIncomplete('hook acls only work with contacts so far');
302 CRM_Core_Config::singleton()->userPermissionClass->permissions = array();
303 $this->setUpEntities($entity);
304 $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'aclWhereHookNoResults'));
305 $result = $this->callAPISuccess($entity, 'get', array(
306 'check_permission' => 1,
307 ));
308 $this->assertEquals(0, $result['count']);
309 }
310
311 /**
312 * @return array
313 */
314 public static function entities() {
315 return array(array('contribution'), array('participant'),);// @todo array('pledge' => 'pledge')
316 }
317
318 /**
319 * Create 2 entities
320 */
321 public function setUpEntities($entity) {
322 $baoObj = new CRM_Core_DAO();
323 $baoObj->createTestObject( _civicrm_api3_get_BAO($entity), array(), 2, 0);
324 CRM_Core_Config::singleton()->userPermissionClass->permissions = array(
325 'access CiviCRM',
326 'access CiviContribute',
327 'access CiviEvent',
328 'view event participants',
329 );
330 }
331
332 /**
333 * No results returned
334 */
335 function aclWhereHookNoResults($type, &$tables, &$whereTables, &$contactID, &$where) {
336 }
337
338 /**
339 * All results returned
340 * @implements CRM_Utils_Hook::aclWhereClause
341 */
342 function aclWhereHookAllResults($type, &$tables, &$whereTables, &$contactID, &$where) {
343 $where = " (1) ";
344 }
345
346 /**
347 * Full results returned
348 * @implements CRM_Utils_Hook::aclWhereClause
349 */
350 function aclWhereOnlySecond($type, &$tables, &$whereTables, &$contactID, &$where) {
351 $where = " contact_a.id > 1";
352 }
353 }