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() { |
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 | } |