using new Permission::allowList to fix CRM-12645
[civicrm-core.git] / tests / phpunit / CRM / ACL / ListTest.php
CommitLineData
ea8011f6 1<?php
2
3/**
4 * Class CRM_ACL_Test
5 *
6 * This test focuses on testing the (new) ID list-based functions:
7 * CRM_Contact_BAO_Contact_Permission::allowList()
8 * CRM_Contact_BAO_Contact_Permission::relationshipList()
9 * @group headless
10 */
11class CRM_ACL_ListTest extends CiviUnitTestCase {
12
13 /**
14 * Set up function.
15 */
16 public function setUp() {
17 parent::setUp();
3c645834 18 // $this->quickCleanup(array('civicrm_acl_contact_cache'), TRUE);
ea8011f6 19 $this->useTransaction(TRUE);
c1ebd31f 20 $this->allowedContactsACL = array();
ea8011f6 21 }
22
23 /**
24 * general test for the 'view all contacts' permission
25 */
26 public function testViewAllPermission() {
27 // create test contacts
134b2b64 28 $contacts = $this->createScenarioPlain();
ea8011f6 29
134b2b64 30 // test WITH all permissions
31 CRM_Core_Config::singleton()->userPermissionClass->permissions = NULL;
ea8011f6 32 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts);
134b2b64 33 sort($result);
34 $this->assertEquals($result, $contacts, "Contacts should be viewable when 'view all contacts'");
ea8011f6 35
36
37 // test WITH explicit permission
38 CRM_Core_Config::singleton()->userPermissionClass->permissions = array('view all contacts');
39 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts, CRM_Core_Permission::VIEW);
134b2b64 40 sort($result);
41 $this->assertEquals($result, $contacts, "Contacts should be viewable when 'view all contacts'");
ea8011f6 42
43
44 // test WITHOUT permission
45 CRM_Core_Config::singleton()->userPermissionClass->permissions = array();
46 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts);
134b2b64 47 sort($result);
ea8011f6 48 $this->assertEmpty($result, "Contacts should NOT be viewable when 'view all contacts' is not set");
49 }
50
51
52 /**
53 * general test for the 'view all contacts' permission
54 */
55 public function testEditAllPermission() {
56 // create test contacts
134b2b64 57 $contacts = $this->createScenarioPlain();
ea8011f6 58
59 // test WITH explicit permission
60 CRM_Core_Config::singleton()->userPermissionClass->permissions = array('edit all contacts');
61 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts, CRM_Core_Permission::EDIT);
134b2b64 62 sort($result);
63 $this->assertEquals($result, $contacts, "Contacts should be viewable when 'edit all contacts'");
ea8011f6 64
65
66 // test WITHOUT permission
67 CRM_Core_Config::singleton()->userPermissionClass->permissions = array();
68 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts);
134b2b64 69 sort($result);
ea8011f6 70 $this->assertEmpty($result, "Contacts should NOT be viewable when 'edit all contacts' is not set");
71 }
72
73
74 /**
134b2b64 75 * Test access related to the 'access deleted contact' permission
ea8011f6 76 */
77 public function testViewEditDeleted() {
134b2b64 78 // create test contacts
79 $contacts = $this->createScenarioPlain();
80
81 // delete one contact
82 $deleted_contact_id = $contacts[2];
83 $this->callAPISuccess('Contact', 'create', array('id' => $deleted_contact_id, 'contact_is_deleted' => 1));
84 $deleted_contact = $this->callAPISuccess('Contact', 'getsingle', array('id' => $deleted_contact_id));
85 $this->assertEquals($deleted_contact['contact_is_deleted'], 1, "Contact should've been deleted");
86
87 // test WITH explicit permission
ea8011f6 88 CRM_Core_Config::singleton()->userPermissionClass->permissions = array('edit all contacts', 'view all contacts');
134b2b64 89 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts, CRM_Core_Permission::EDIT);
90 sort($result);
91 $this->assertNotContains($deleted_contact_id, $result, "Deleted contacts should be excluded");
92 $this->assertEquals(count($result), count($contacts)-1, "Only deleted contacts should be excluded");
93
94 }
95
ea8011f6 96
134b2b64 97 /**
c1ebd31f 98 * Test access based on relations
134b2b64 99 *
100 * There should be the following permission-relationship
101 * contact[0] -> contact[1] -> contact[2]
102 */
103 public function testPermissionByRelation() {
104 // create test scenario
c1ebd31f 105 $contacts = $this->createScenarioRelations();
134b2b64 106
107 // remove all permissions
108 $config = CRM_Core_Config::singleton();
109 $config->userPermissionClass->permissions = array();
110 $permissions_to_check = array(CRM_Core_Permission::VIEW => 'View', CRM_Core_Permission::EDIT => 'Edit');
111
112 // run this for SIMPLE relations
113 $config->secondDegRelPermissions = FALSE;
114 $this->assertFalse($config->secondDegRelPermissions);
115 foreach ($permissions_to_check as $permission => $permission_label) {
116 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts, $permission);
117 sort($result);
118
119
c1ebd31f 120 $this->assertNotContains($contacts[0], $result, "User[0] should NOT have $permission_label permission on contact[0].");
121 $this->assertContains( $contacts[1], $result, "User[0] should have $permission_label permission on contact[1].");
122 $this->assertNotContains($contacts[2], $result, "User[0] should NOT have $permission_label permission on contact[2].");
123 $this->assertNotContains($contacts[3], $result, "User[0] should NOT have $permission_label permission on contact[3].");
124 $this->assertNotContains($contacts[4], $result, "User[0] should NOT have $permission_label permission on contact[4].");
134b2b64 125 }
ea8011f6 126
134b2b64 127 // run this for SECOND DEGREE relations
128 $config->secondDegRelPermissions = TRUE;
129 $this->assertTrue($config->secondDegRelPermissions);
130 foreach ($permissions_to_check as $permission => $permission_label) {
131 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts, $permission);
132 sort($result);
133
c1ebd31f 134 $this->assertNotContains($contacts[0], $result, "User[0] should NOT have $permission_label permission on contact[0].");
135 $this->assertContains( $contacts[1], $result, "User[0] should have $permission_label permission on contact[1].");
136 $this->assertContains( $contacts[2], $result, "User[0] should have second degree $permission_label permission on contact[2].");
137 $this->assertNotContains($contacts[3], $result, "User[0] should NOT have $permission_label permission on contact[3].");
138 $this->assertNotContains($contacts[4], $result, "User[0] should NOT have $permission_label permission on contact[4].");
134b2b64 139 }
ea8011f6 140 }
141
142
134b2b64 143 /**
c1ebd31f 144 * Test access based on ACL
134b2b64 145 */
c1ebd31f 146 public function testPermissionByACL() {
147 $contacts = $this->createScenarioPlain();
148
149 // set custom hook
150 $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'hook_civicrm_aclWhereClause'));
151
152 // run simple test
153 $permissions_to_check = array(CRM_Core_Permission::VIEW => 'View', CRM_Core_Permission::EDIT => 'Edit');
154
155 $this->allowedContactsACL = array($contacts[0], $contacts[1], $contacts[4]);
156 CRM_Core_Config::singleton()->userPermissionClass->permissions = array();
157 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts);
158 sort($result);
159
160 $this->assertContains( $contacts[0], $result, "User[0] should NOT have an ACL permission on contact[0].");
161 $this->assertContains( $contacts[1], $result, "User[0] should have an ACL permission on contact[1].");
162 $this->assertNotContains($contacts[2], $result, "User[0] should NOT have an ACL permission on contact[2].");
163 $this->assertNotContains($contacts[3], $result, "User[0] should NOT have an RELATION permission on contact[3].");
164 $this->assertContains( $contacts[4], $result, "User[0] should NOT have an ACL permission on contact[4].");
134b2b64 165 }
ea8011f6 166
c1ebd31f 167
134b2b64 168 /**
c1ebd31f 169 * Test access with a mix of ACL and relationship
134b2b64 170 */
c1ebd31f 171 public function testPermissionACLvsRelationship() {
172 $contacts = $this->createScenarioRelations();
173
174 // set custom hook
175 $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'hook_civicrm_aclWhereClause'));
176
177 $config = CRM_Core_Config::singleton();
178 $config->userPermissionClass->permissions = array();
179 $config->secondDegRelPermissions = TRUE;
180
181 $this->allowedContactsACL = array($contacts[0], $contacts[1], $contacts[4]);
182 CRM_Core_Config::singleton()->userPermissionClass->permissions = array();
183 $result = CRM_Contact_BAO_Contact_Permission::allowList($contacts);
184 sort($result);
185
186 $this->assertContains( $contacts[0], $result, "User[0] should have an ACL permission on contact[0].");
187 $this->assertContains( $contacts[1], $result, "User[0] should have an ACL permission on contact[1].");
188 $this->assertContains( $contacts[2], $result, "User[0] should have second degree an relation permission on contact[2].");
189 $this->assertNotContains($contacts[3], $result, "User[0] should NOT have an ACL permission on contact[3].");
190 $this->assertContains( $contacts[4], $result, "User[0] should have an ACL permission on contact[4].");
134b2b64 191 }
ea8011f6 192
134b2b64 193 /**
194 * Test access related to the 'access deleted contact' permission
195 */
3c645834 196 public function testPermissionCompare() {
197 $contacts = $this->createScenarioRelations();
198 $contact_index = array_flip($contacts);
199
200 // set custom hook
201 $this->hookClass->setHook('civicrm_aclWhereClause', array($this, 'hook_civicrm_aclWhereClause'));
202
203 $config = CRM_Core_Config::singleton();
204 $this->allowedContactsACL = array($contacts[0], $contacts[1], $contacts[4]);
205 $config->secondDegRelPermissions = TRUE;
206
207 // test configurations
208 $permissions_to_check = array(CRM_Core_Permission::VIEW => 'View', CRM_Core_Permission::EDIT => 'Edit');
209 $user_permission_options = array(/*ALL*/ NULL, /*NONE*/ array(), array('view all contacts'), array('edit all contacts'), array('view all contacts', 'edit all contacts'));
210
211 // run all combinations of those
212 foreach ($permissions_to_check as $permission_to_check => $permission_label) {
213 foreach ($user_permission_options as $user_permissions) {
214 // select the contact range
215 $contact_range = $contacts;
216 if (is_array($user_permissions) && count($user_permissions)==0) {
217 // slight (explainable) deviation on the own contact
218 unset($contact_range[0]);
219 }
220
221 $config->userPermissionClass->permissions = $user_permissions;
222 $user_permissions_label = json_encode($user_permissions);
223
224 // get the list result
225 $list_result = CRM_Contact_BAO_Contact_Permission::allowList($contact_range, $permission_to_check);
226 $this->assertTrue(count($list_result) <= count($contact_range), "Permission::allowList should return a subset of the contats.");
227 foreach ($list_result as $contact_id) {
228 $this->assertContains($contact_id, $contact_range, "Permission::allowList should return a subset of the contats.");
229 }
230
231 // now compare the results
232 foreach ($contact_range as $contact_id) {
233 $individual_result = CRM_Contact_BAO_Contact_Permission::allow($contact_id, $permission_to_check);
234
235 if (in_array($contact_id, $list_result)) {
236 // listPermission reports PERMISSION GRANTED
237 $this->assertTrue($individual_result, "Permission::allow denies {$permission_label} access for contact[{$contact_index[$contact_id]}], while Permission::allowList grants it. User permission: '{$user_permissions_label}'");
238
239 } else {
240 // listPermission reports PERMISSION DENIED
241 $this->assertFalse($individual_result, "Permission::allow grantes {$permission_label} access for contact[{$contact_index[$contact_id]}], while Permission::allowList denies it. User permission: '{$user_permissions_label}'");
242
243 }
244 }
245 }
246 }
134b2b64 247 }
ea8011f6 248
249
134b2b64 250 /****************************************************
251 * Scenario Builders *
252 ***************************************************/
ea8011f6 253
254 /**
134b2b64 255 * create plain test scenario, no relationships/ACLs
ea8011f6 256 */
134b2b64 257 protected function createScenarioPlain() {
ea8011f6 258 // get logged in user
259 $user_id = $this->createLoggedInUser();
260 $this->assertNotEmpty($user_id);
261
262 // create test contacts
263 $bush_sr_id = $this->individualCreate(array('first_name' => 'George', 'middle_name' => 'W.', 'last_name' => 'Bush'));
264 $bush_jr_id = $this->individualCreate(array('first_name' => 'George', 'middle_name' => 'H. W.', 'last_name' => 'Bush'));
265 $bush_laura_id = $this->individualCreate(array('first_name' => 'Laura Lane', 'last_name' => 'Bush'));
266 $bush_brbra_id = $this->individualCreate(array('first_name' => 'Barbara', 'last_name' => 'Bush'));
267
134b2b64 268 $contacts = array($user_id, $bush_sr_id, $bush_jr_id, $bush_laura_id, $bush_brbra_id);
269 sort($contacts);
270 return $contacts;
271 }
272
273 /**
274 * create plain test scenario, no relationships/ACLs
275 */
c1ebd31f 276 protected function createScenarioRelations() {
134b2b64 277 $contacts = $this->createScenarioPlain();
278
ea8011f6 279 // create some relationships
280 $this->callAPISuccess('Relationship', 'create', array(
281 'relationship_type_id' => 1, // CHILD OF
134b2b64 282 'contact_id_a' => $contacts[1],
283 'contact_id_b' => $contacts[0],
284 'is_permission_b_a' => 1,
285 'is_active' => 1,
ea8011f6 286 ));
287
288 $this->callAPISuccess('Relationship', 'create', array(
289 'relationship_type_id' => 1, // CHILD OF
134b2b64 290 'contact_id_a' => $contacts[2],
291 'contact_id_b' => $contacts[1],
292 'is_permission_b_a' => 1,
293 'is_active' => 1,
ea8011f6 294 ));
295
296 // create some relationships
297 $this->callAPISuccess('Relationship', 'create', array(
298 'relationship_type_id' => 1, // CHILD OF
134b2b64 299 'contact_id_a' => $contacts[4],
300 'contact_id_b' => $contacts[2],
301 'is_permission_b_a' => 1,
302 'is_active' => 1,
ea8011f6 303 ));
304
134b2b64 305 return $contacts;
ea8011f6 306 }
c1ebd31f 307
308 /**
309 * ACL HOOK implementation for various tests
310 */
311 public function hook_civicrm_aclWhereClause($type, &$tables, &$whereTables, &$contactID, &$where ) {
312 if (!empty($this->allowedContactsACL)) {
313 $contact_id_list = implode(',', $this->allowedContactsACL);
314 $where = " contact_a.id IN ($contact_id_list)";
315 }
316 }
ea8011f6 317}