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