Commit | Line | Data |
---|---|---|
6a488035 | 1 | <?php |
6a488035 TO |
2 | /* |
3 | +--------------------------------------------------------------------+ | |
fee14197 | 4 | | CiviCRM version 5 | |
6a488035 | 5 | +--------------------------------------------------------------------+ |
6b83d5bd | 6 | | Copyright CiviCRM LLC (c) 2004-2019 | |
6a488035 TO |
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 | +--------------------------------------------------------------------+ | |
d25dd0ee | 26 | */ |
6a488035 TO |
27 | |
28 | /** | |
29 | * | |
30 | * @package CRM | |
6b83d5bd | 31 | * @copyright CiviCRM LLC (c) 2004-2019 |
6a488035 TO |
32 | */ |
33 | ||
34 | /** | |
35 | * Access Control List | |
36 | */ | |
37 | class CRM_ACL_BAO_ACL extends CRM_ACL_DAO_ACL { | |
db01bf2f | 38 | /** |
39 | * @var string | |
40 | */ | |
683bf891 SL |
41 | public static $_entityTable = NULL; |
42 | public static $_objectTable = NULL; | |
43 | public static $_operation = NULL; | |
6a488035 | 44 | |
683bf891 | 45 | public static $_fieldKeys = NULL; |
6a488035 | 46 | |
28518c90 | 47 | /** |
3819f101 | 48 | * Get ACL entity table. |
49 | * | |
28518c90 EM |
50 | * @return array|null |
51 | */ | |
00be9182 | 52 | public static function entityTable() { |
6a488035 | 53 | if (!self::$_entityTable) { |
cf0d1c08 | 54 | self::$_entityTable = [ |
6a488035 TO |
55 | 'civicrm_contact' => ts('Contact'), |
56 | 'civicrm_acl_role' => ts('ACL Role'), | |
cf0d1c08 | 57 | ]; |
6a488035 TO |
58 | } |
59 | return self::$_entityTable; | |
60 | } | |
61 | ||
28518c90 EM |
62 | /** |
63 | * @return array|null | |
64 | */ | |
00be9182 | 65 | public static function objectTable() { |
6a488035 | 66 | if (!self::$_objectTable) { |
cf0d1c08 | 67 | self::$_objectTable = [ |
6a488035 TO |
68 | 'civicrm_contact' => ts('Contact'), |
69 | 'civicrm_group' => ts('Group'), | |
70 | 'civicrm_saved_search' => ts('Contact Group'), | |
6a488035 | 71 | 'civicrm_admin' => ts('Import'), |
cf0d1c08 | 72 | ]; |
6a488035 TO |
73 | } |
74 | return self::$_objectTable; | |
75 | } | |
76 | ||
28518c90 EM |
77 | /** |
78 | * @return array|null | |
79 | */ | |
00be9182 | 80 | public static function operation() { |
6a488035 | 81 | if (!self::$_operation) { |
cf0d1c08 | 82 | self::$_operation = [ |
6a488035 TO |
83 | 'View' => ts('View'), |
84 | 'Edit' => ts('Edit'), | |
85 | 'Create' => ts('Create'), | |
86 | 'Delete' => ts('Delete'), | |
87 | 'Search' => ts('Search'), | |
88 | 'All' => ts('All'), | |
cf0d1c08 | 89 | ]; |
6a488035 TO |
90 | } |
91 | return self::$_operation; | |
92 | } | |
93 | ||
94 | /** | |
95 | * Construct a WHERE clause to handle permissions to $object_* | |
96 | * | |
d6951adc | 97 | * @deprecated |
98 | * | |
b758c7d5 TO |
99 | * @param array $tables |
100 | * Any tables that may be needed in the FROM. | |
101 | * @param string $operation | |
102 | * The operation being attempted. | |
103 | * @param string $object_table | |
104 | * The table of the object in question. | |
105 | * @param int $object_id | |
106 | * The ID of the object in question. | |
107 | * @param int $acl_id | |
108 | * If it's a grant/revoke operation, the ACL ID. | |
109 | * @param bool $acl_role | |
110 | * For grant operations, this flag determines if we're granting a single acl (false) or an entire group. | |
6a488035 | 111 | * |
a6c01b45 CW |
112 | * @return string |
113 | * The WHERE clause, or 0 on failure | |
6a488035 | 114 | */ |
e6a83034 TO |
115 | public static function permissionClause( |
116 | &$tables, $operation, | |
6a488035 TO |
117 | $object_table = NULL, $object_id = NULL, |
118 | $acl_id = NULL, $acl_role = FALSE | |
119 | ) { | |
d6951adc | 120 | CRM_Core_Error::deprecatedFunctionWarning('unknown - this is really old & not used in core'); |
28a04ea9 | 121 | $dao = new CRM_ACL_DAO_ACL(); |
6a488035 | 122 | |
cf0d1c08 | 123 | $t = [ |
6a488035 TO |
124 | 'ACL' => self::getTableName(), |
125 | 'ACLRole' => 'civicrm_acl_role', | |
126 | 'ACLEntityRole' => CRM_ACL_DAO_EntityRole::getTableName(), | |
127 | 'Contact' => CRM_Contact_DAO_Contact::getTableName(), | |
128 | 'Group' => CRM_Contact_DAO_Group::getTableName(), | |
129 | 'GroupContact' => CRM_Contact_DAO_GroupContact::getTableName(), | |
cf0d1c08 | 130 | ]; |
6a488035 | 131 | |
3bdcd4ec | 132 | $contact_id = CRM_Core_Session::getLoggedInContactID(); |
6a488035 TO |
133 | |
134 | $where = " {$t['ACL']}.operation = '" . CRM_Utils_Type::escape($operation, 'String') . "'"; | |
135 | ||
136 | /* Include clause if we're looking for a specific table/id permission */ | |
137 | ||
6a488035 TO |
138 | if (!empty($object_table)) { |
139 | $where .= " AND ( {$t['ACL']}.object_table IS null | |
140 | OR ({$t['ACL']}.object_table = '" . CRM_Utils_Type::escape($object_table, 'String') . "'"; | |
141 | if (!empty($object_id)) { | |
142 | $where .= " AND ({$t['ACL']}.object_id IS null | |
143 | OR {$t['ACL']}.object_id = " . CRM_Utils_Type::escape($object_id, 'Integer') . ')'; | |
144 | } | |
145 | $where .= '))'; | |
146 | } | |
147 | ||
148 | /* Include clause if we're granting an ACL or ACL Role */ | |
149 | ||
6a488035 TO |
150 | if (!empty($acl_id)) { |
151 | $where .= " AND ({$t['ACL']}.acl_id IS null | |
152 | OR {$t['ACL']}.acl_id = " . CRM_Utils_Type::escape($acl_id, 'Integer') . ')'; | |
153 | if ($acl_role) { | |
154 | $where .= " AND {$t['ACL']}.acl_table = '{$t['ACLRole']}'"; | |
155 | } | |
156 | else { | |
157 | $where .= " AND {$t['ACL']}.acl_table = '{$t['ACL']}'"; | |
158 | } | |
159 | } | |
160 | ||
cf0d1c08 | 161 | $query = []; |
6a488035 TO |
162 | |
163 | /* Query for permissions granted to all contacts in the domain */ | |
164 | ||
6a488035 TO |
165 | $query[] = "SELECT {$t['ACL']}.*, 0 as override |
166 | FROM {$t['ACL']} | |
167 | ||
168 | WHERE {$t['ACL']}.entity_table = '{$t['Domain']}' | |
169 | AND ($where)"; | |
170 | ||
171 | /* Query for permissions granted to all contacts through an ACL group */ | |
172 | ||
6a488035 TO |
173 | $query[] = "SELECT {$t['ACL']}.*, 0 as override |
174 | FROM {$t['ACL']} | |
175 | ||
176 | INNER JOIN {$t['ACLEntityRole']} | |
177 | ON ({$t['ACL']}.entity_table = '{$t['ACLRole']}' | |
178 | AND {$t['ACL']}.entity_id = | |
179 | {$t['ACLEntityRole']}.acl_role_id) | |
180 | ||
181 | INNER JOIN {$t['ACLRole']} | |
182 | ON {$t['ACL']}.entity_id = | |
183 | {$t['ACLRole']}.id | |
184 | ||
185 | WHERE {$t['ACLEntityRole']}.entity_table = | |
186 | '{$t['Domain']}' | |
187 | AND {$t['ACLRole']}.is_active = 1 | |
188 | AND ($where)"; | |
189 | ||
190 | /* Query for permissions granted directly to the contact */ | |
191 | ||
6a488035 TO |
192 | $query[] = "SELECT {$t['ACL']}.*, 1 as override |
193 | FROM {$t['ACL']} | |
194 | ||
195 | INNER JOIN {$t['Contact']} | |
196 | ON ({$t['ACL']}.entity_table = '{$t['Contact']}' | |
197 | AND {$t['ACL']}.entity_id = {$t['Contact']}.id) | |
198 | ||
199 | WHERE {$t['Contact']}.id = $contact_id | |
200 | AND ($where)"; | |
201 | ||
202 | /* Query for permissions granted to the contact through an ACL group */ | |
203 | ||
6a488035 TO |
204 | $query[] = "SELECT {$t['ACL']}.*, 1 as override |
205 | FROM {$t['ACL']} | |
206 | ||
207 | INNER JOIN {$t['ACLEntityRole']} | |
208 | ON ({$t['ACL']}.entity_table = '{$t['ACLRole']}' | |
209 | AND {$t['ACL']}.entity_id = | |
210 | {$t['ACLEntityRole']}.acl_role_id) | |
211 | ||
212 | INNER JOIN {$t['ACLRole']} | |
213 | ON {$t['ACL']}.entity_id = {$t['ACLRole']}.id | |
214 | ||
215 | WHERE {$t['ACLEntityRole']}.entity_table = | |
216 | '{$t['Contact']}' | |
217 | AND {$t['ACLRole']}.is_active = 1 | |
218 | AND {$t['ACLEntityRole']}.entity_id = $contact_id | |
219 | AND ($where)"; | |
220 | ||
221 | /* Query for permissions granted to the contact through a group */ | |
222 | ||
6a488035 TO |
223 | $query[] = "SELECT {$t['ACL']}.*, 0 as override |
224 | FROM {$t['ACL']} | |
225 | ||
226 | INNER JOIN {$t['GroupContact']} | |
227 | ON ({$t['ACL']}.entity_table = '{$t['Group']}' | |
228 | AND {$t['ACL']}.entity_id = | |
229 | {$t['GroupContact']}.group_id) | |
230 | ||
231 | WHERE ($where) | |
232 | AND {$t['GroupContact']}.contact_id = $contact_id | |
233 | AND {$t['GroupContact']}.status = 'Added')"; | |
234 | ||
6a488035 | 235 | /* Query for permissions granted through an ACL group to a Contact |
e70a7fc0 | 236 | * group */ |
6a488035 | 237 | |
6a488035 TO |
238 | $query[] = "SELECT {$t['ACL']}.*, 0 as override |
239 | FROM {$t['ACL']} | |
240 | ||
241 | INNER JOIN {$t['ACLEntityRole']} | |
242 | ON ({$t['ACL']}.entity_table = '{$t['ACLRole']}' | |
243 | AND {$t['ACL']}.entity_id = | |
244 | {$t['ACLEntityRole']}.acl_role_id) | |
245 | ||
246 | INNER JOIN {$t['ACLRole']} | |
247 | ON {$t['ACL']}.entity_id = {$t['ACLRole']}.id | |
248 | ||
249 | INNER JOIN {$t['GroupContact']} | |
250 | ON ({$t['ACLEntityRole']}.entity_table = | |
251 | '{$t['Group']}' | |
252 | AND {$t['ACLEntityRole']}.entity_id = | |
253 | {$t['GroupContact']}.group_id) | |
254 | ||
255 | WHERE ($where) | |
256 | AND {$t['ACLRole']}.is_active = 1 | |
257 | AND {$t['GroupContact']}.contact_id = $contact_id | |
258 | AND {$t['GroupContact']}.status = 'Added'"; | |
259 | ||
260 | $union = '(' . implode(') UNION DISTINCT (', $query) . ')'; | |
261 | ||
262 | $dao->query($union); | |
263 | ||
cf0d1c08 | 264 | $allow = [0]; |
265 | $deny = [0]; | |
266 | $override = []; | |
6a488035 TO |
267 | |
268 | while ($dao->fetch()) { | |
269 | /* Instant bypass for the following cases: | |
e70a7fc0 TO |
270 | * 1) the rule governs all tables |
271 | * 2) the rule governs all objects in the table in question | |
272 | * 3) the rule governs the specific object we want | |
273 | */ | |
6a488035 | 274 | |
6a488035 TO |
275 | if (empty($dao->object_table) || |
276 | ($dao->object_table == $object_table | |
277 | && (empty($dao->object_id) | |
278 | || $dao->object_id == $object_id | |
279 | ) | |
280 | ) | |
281 | ) { | |
282 | $clause = 1; | |
283 | } | |
284 | else { | |
285 | /* Otherwise try to generate a clause for this rule */ | |
286 | ||
6a488035 TO |
287 | $clause = self::getClause( |
288 | $dao->object_table, $dao->object_id, $tables | |
289 | ); | |
290 | ||
291 | /* If the clause returned is null, then the rule is a blanket | |
e70a7fc0 TO |
292 | * (id is null) on a table other than the one we're interested |
293 | * in. So skip it. */ | |
6a488035 | 294 | |
6a488035 TO |
295 | if (empty($clause)) { |
296 | continue; | |
297 | } | |
298 | } | |
299 | ||
300 | /* Now we figure out if this is an allow or deny rule, and possibly | |
e70a7fc0 | 301 | * a contact-level override */ |
6a488035 | 302 | |
6a488035 TO |
303 | if ($dao->deny) { |
304 | $deny[] = $clause; | |
305 | } | |
306 | else { | |
307 | $allow[] = $clause; | |
308 | ||
309 | if ($dao->override) { | |
310 | $override[] = $clause; | |
311 | } | |
312 | } | |
313 | } | |
314 | ||
315 | $allows = '(' . implode(' OR ', $allow) . ')'; | |
316 | $denies = '(' . implode(' OR ', $deny) . ')'; | |
317 | if (!empty($override)) { | |
318 | $denies = '(NOT (' . implode(' OR ', $override) . ") AND $denies)"; | |
319 | } | |
320 | ||
321 | return "($allows AND NOT $denies)"; | |
322 | } | |
323 | ||
324 | /** | |
325 | * Given a table and id pair, return the filter clause | |
326 | * | |
b758c7d5 TO |
327 | * @param string $table |
328 | * The table owning the object. | |
329 | * @param int $id | |
330 | * The ID of the object. | |
331 | * @param array $tables | |
332 | * Tables that will be needed in the FROM. | |
6a488035 | 333 | * |
72b3a70c CW |
334 | * @return string|null |
335 | * WHERE-style clause to filter results, | |
16b10e64 | 336 | * or null if $table or $id is null |
6a488035 TO |
337 | */ |
338 | public static function getClause($table, $id, &$tables) { | |
353ffa53 TO |
339 | $table = CRM_Utils_Type::escape($table, 'String'); |
340 | $id = CRM_Utils_Type::escape($id, 'Integer'); | |
cf0d1c08 | 341 | $whereTables = []; |
6a488035 TO |
342 | |
343 | $ssTable = CRM_Contact_BAO_SavedSearch::getTableName(); | |
344 | ||
345 | if (empty($table)) { | |
346 | return NULL; | |
347 | } | |
348 | elseif ($table == $ssTable) { | |
349 | return CRM_Contact_BAO_SavedSearch::whereClause($id, $tables, $whereTables); | |
350 | } | |
351 | elseif (!empty($id)) { | |
352 | $tables[$table] = TRUE; | |
353 | return "$table.id = $id"; | |
354 | } | |
355 | return NULL; | |
356 | } | |
357 | ||
358 | /** | |
359 | * Construct an associative array of an ACL rule's properties | |
360 | * | |
b758c7d5 TO |
361 | * @param string $format |
362 | * Sprintf format for array. | |
363 | * @param bool $hideEmpty | |
364 | * Only return elements that have a value set. | |
6a488035 | 365 | * |
a6c01b45 CW |
366 | * @return array |
367 | * Assoc. array of the ACL rule's properties | |
6a488035 | 368 | */ |
100b0ec6 | 369 | public function toArray($format = '%s', $hideEmpty = FALSE) { |
cf0d1c08 | 370 | $result = []; |
6a488035 TO |
371 | |
372 | if (!self::$_fieldKeys) { | |
373 | $fields = CRM_ACL_DAO_ACL::fields(); | |
374 | self::$_fieldKeys = array_keys($fields); | |
375 | } | |
376 | ||
377 | foreach (self::$_fieldKeys as $field) { | |
378 | $result[$field] = $this->$field; | |
379 | } | |
380 | return $result; | |
381 | } | |
382 | ||
383 | /** | |
384 | * Retrieve ACLs for a contact or group. Note that including a contact id | |
385 | * without a group id will return those ACL rules which are granted | |
386 | * directly to the contact, but not those granted to the contact through | |
387 | * any/all of his group memberships. | |
388 | * | |
b758c7d5 TO |
389 | * @param int $contact_id |
390 | * ID of a contact to search for. | |
391 | * @param int $group_id | |
392 | * ID of a group to search for. | |
393 | * @param bool $aclRoles | |
394 | * Should we include ACL Roles. | |
6a488035 | 395 | * |
a6c01b45 CW |
396 | * @return array |
397 | * Array of assoc. arrays of ACL rules | |
6a488035 TO |
398 | */ |
399 | public static function &getACLs($contact_id = NULL, $group_id = NULL, $aclRoles = FALSE) { | |
cf0d1c08 | 400 | $results = []; |
6a488035 TO |
401 | |
402 | if (empty($contact_id)) { | |
403 | return $results; | |
404 | } | |
405 | ||
406 | $contact_id = CRM_Utils_Type::escape($contact_id, 'Integer'); | |
407 | if ($group_id) { | |
408 | $group_id = CRM_Utils_Type::escape($group_id, 'Integer'); | |
409 | } | |
410 | ||
411 | $rule = new CRM_ACL_BAO_ACL(); | |
412 | ||
353ffa53 | 413 | $acl = self::getTableName(); |
6a488035 | 414 | $contact = CRM_Contact_BAO_Contact::getTableName(); |
353ffa53 TO |
415 | $c2g = CRM_Contact_BAO_GroupContact::getTableName(); |
416 | $group = CRM_Contact_BAO_Group::getTableName(); | |
6a488035 TO |
417 | |
418 | $query = " SELECT $acl.* | |
419 | FROM $acl "; | |
420 | ||
421 | if (!empty($group_id)) { | |
422 | $query .= " INNER JOIN $c2g | |
423 | ON $acl.entity_id = $c2g.group_id | |
424 | WHERE $acl.entity_table = '$group' | |
425 | AND $acl.is_active = 1 | |
426 | AND $c2g.group_id = $group_id"; | |
427 | ||
428 | if (!empty($contact_id)) { | |
429 | $query .= " AND $c2g.contact_id = $contact_id | |
430 | AND $c2g.status = 'Added'"; | |
431 | } | |
432 | } | |
433 | else { | |
434 | if (!empty($contact_id)) { | |
435 | $query .= " WHERE $acl.entity_table = '$contact' | |
436 | AND $acl.entity_id = $contact_id"; | |
437 | } | |
438 | } | |
439 | ||
440 | $rule->query($query); | |
441 | ||
442 | while ($rule->fetch()) { | |
443 | $results[$rule->id] = $rule->toArray(); | |
444 | } | |
445 | ||
446 | if ($aclRoles) { | |
447 | $results += self::getACLRoles($contact_id, $group_id); | |
448 | } | |
449 | ||
450 | return $results; | |
451 | } | |
452 | ||
453 | /** | |
d2e5d2ce | 454 | * Get all of the ACLs through ACL groups. |
6a488035 | 455 | * |
b758c7d5 TO |
456 | * @param int $contact_id |
457 | * ID of a contact to search for. | |
458 | * @param int $group_id | |
459 | * ID of a group to search for. | |
6a488035 | 460 | * |
a6c01b45 CW |
461 | * @return array |
462 | * Array of assoc. arrays of ACL rules | |
6a488035 TO |
463 | */ |
464 | public static function &getACLRoles($contact_id = NULL, $group_id = NULL) { | |
465 | $contact_id = CRM_Utils_Type::escape($contact_id, 'Integer'); | |
466 | if ($group_id) { | |
467 | $group_id = CRM_Utils_Type::escape($group_id, 'Integer'); | |
468 | } | |
469 | ||
470 | $rule = new CRM_ACL_BAO_ACL(); | |
471 | ||
353ffa53 TO |
472 | $acl = self::getTableName(); |
473 | $aclRole = 'civicrm_acl_role'; | |
6a488035 | 474 | $aclRoleJoin = CRM_ACL_DAO_EntityRole::getTableName(); |
353ffa53 TO |
475 | $contact = CRM_Contact_BAO_Contact::getTableName(); |
476 | $c2g = CRM_Contact_BAO_GroupContact::getTableName(); | |
477 | $group = CRM_Contact_BAO_Group::getTableName(); | |
6a488035 TO |
478 | |
479 | $query = " SELECT $acl.* | |
480 | FROM $acl | |
481 | INNER JOIN civicrm_option_group og | |
482 | ON og.name = 'acl_role' | |
483 | INNER JOIN civicrm_option_value ov | |
484 | ON $acl.entity_table = '$aclRole' | |
485 | AND ov.option_group_id = og.id | |
486 | AND $acl.entity_id = ov.value"; | |
487 | ||
488 | if (!empty($group_id)) { | |
489 | $query .= " INNER JOIN $c2g | |
490 | ON $acl.entity_id = $c2g.group_id | |
491 | WHERE $acl.entity_table = '$group' | |
492 | AND $acl.is_active = 1 | |
493 | AND $c2g.group_id = $group_id"; | |
494 | ||
495 | if (!empty($contact_id)) { | |
496 | $query .= " AND $c2g.contact_id = $contact_id | |
497 | AND $c2g.status = 'Added'"; | |
498 | } | |
499 | } | |
500 | else { | |
501 | if (!empty($contact_id)) { | |
502 | $query .= " WHERE $acl.entity_table = '$contact' | |
503 | AND $acl.is_active = 1 | |
504 | AND $acl.entity_id = $contact_id"; | |
505 | } | |
506 | } | |
507 | ||
cf0d1c08 | 508 | $results = []; |
6a488035 TO |
509 | |
510 | $rule->query($query); | |
511 | ||
512 | while ($rule->fetch()) { | |
a5611c8e | 513 | $results[$rule->id] = $rule->toArray(); |
6a488035 TO |
514 | } |
515 | ||
516 | return $results; | |
517 | } | |
518 | ||
519 | /** | |
d2e5d2ce | 520 | * Get all ACLs granted to a contact through all group memberships. |
6a488035 | 521 | * |
b758c7d5 TO |
522 | * @param int $contact_id |
523 | * The contact's ID. | |
524 | * @param bool $aclRoles | |
525 | * Include ACL Roles?. | |
6a488035 | 526 | * |
a6c01b45 CW |
527 | * @return array |
528 | * Assoc array of ACL rules | |
6a488035 TO |
529 | */ |
530 | public static function &getGroupACLs($contact_id, $aclRoles = FALSE) { | |
531 | $contact_id = CRM_Utils_Type::escape($contact_id, 'Integer'); | |
532 | ||
533 | $rule = new CRM_ACL_BAO_ACL(); | |
534 | ||
353ffa53 TO |
535 | $acl = self::getTableName(); |
536 | $c2g = CRM_Contact_BAO_GroupContact::getTableName(); | |
537 | $group = CRM_Contact_BAO_Group::getTableName(); | |
cf0d1c08 | 538 | $results = []; |
6a488035 TO |
539 | |
540 | if ($contact_id) { | |
541 | $query = " | |
542 | SELECT $acl.* | |
543 | FROM $acl | |
544 | INNER JOIN $c2g | |
545 | ON $acl.entity_id = $c2g.group_id | |
546 | WHERE $acl.entity_table = '$group' | |
547 | AND $c2g.contact_id = $contact_id | |
548 | AND $c2g.status = 'Added'"; | |
549 | ||
550 | $rule->query($query); | |
551 | ||
552 | while ($rule->fetch()) { | |
79380078 | 553 | $results[$rule->id] = $rule->toArray(); |
6a488035 TO |
554 | } |
555 | } | |
556 | ||
557 | if ($aclRoles) { | |
558 | $results += self::getGroupACLRoles($contact_id); | |
559 | } | |
560 | ||
561 | return $results; | |
562 | } | |
563 | ||
564 | /** | |
d2e5d2ce | 565 | * Get all of the ACLs for a contact through ACL groups owned by Contact. |
6a488035 TO |
566 | * groups. |
567 | * | |
b758c7d5 TO |
568 | * @param int $contact_id |
569 | * ID of a contact to search for. | |
6a488035 | 570 | * |
a6c01b45 CW |
571 | * @return array |
572 | * Array of assoc. arrays of ACL rules | |
6a488035 TO |
573 | */ |
574 | public static function &getGroupACLRoles($contact_id) { | |
575 | $contact_id = CRM_Utils_Type::escape($contact_id, 'Integer'); | |
576 | ||
577 | $rule = new CRM_ACL_BAO_ACL(); | |
578 | ||
579 | $acl = self::getTableName(); | |
580 | $aclRole = 'civicrm_acl_role'; | |
581 | ||
6a488035 | 582 | $aclER = CRM_ACL_DAO_EntityRole::getTableName(); |
353ffa53 | 583 | $c2g = CRM_Contact_BAO_GroupContact::getTableName(); |
6a488035 TO |
584 | $group = CRM_Contact_BAO_Group::getTableName(); |
585 | ||
586 | $query = " SELECT $acl.* | |
587 | FROM $acl | |
588 | INNER JOIN civicrm_option_group og | |
589 | ON og.name = 'acl_role' | |
590 | INNER JOIN civicrm_option_value ov | |
591 | ON $acl.entity_table = '$aclRole' | |
592 | AND ov.option_group_id = og.id | |
593 | AND $acl.entity_id = ov.value | |
594 | AND ov.is_active = 1 | |
595 | INNER JOIN $aclER | |
596 | ON $aclER.acl_role_id = $acl.entity_id | |
597 | AND $aclER.is_active = 1 | |
598 | INNER JOIN $c2g | |
599 | ON $aclER.entity_id = $c2g.group_id | |
600 | AND $aclER.entity_table = 'civicrm_group' | |
601 | WHERE $acl.entity_table = '$aclRole' | |
602 | AND $acl.is_active = 1 | |
603 | AND $c2g.contact_id = $contact_id | |
604 | AND $c2g.status = 'Added'"; | |
605 | ||
cf0d1c08 | 606 | $results = []; |
6a488035 TO |
607 | |
608 | $rule->query($query); | |
609 | ||
610 | while ($rule->fetch()) { | |
39eb89f4 | 611 | $results[$rule->id] = $rule->toArray(); |
6a488035 TO |
612 | } |
613 | ||
614 | // also get all acls for "Any Role" case | |
615 | // and authenticated User Role if present | |
616 | $roles = "0"; | |
617 | $session = CRM_Core_Session::singleton(); | |
618 | if ($session->get('ufID') > 0) { | |
619 | $roles .= ",2"; | |
620 | } | |
621 | ||
622 | $query = " | |
623 | SELECT $acl.* | |
624 | FROM $acl | |
625 | WHERE $acl.entity_id IN ( $roles ) | |
626 | AND $acl.entity_table = 'civicrm_acl_role' | |
627 | "; | |
628 | ||
629 | $rule->query($query); | |
630 | while ($rule->fetch()) { | |
631 | $results[$rule->id] = $rule->toArray(); | |
632 | } | |
633 | ||
634 | return $results; | |
635 | } | |
636 | ||
637 | /** | |
638 | * Get all ACLs owned by a given contact, including domain and group-level. | |
639 | * | |
b758c7d5 TO |
640 | * @param int $contact_id |
641 | * The contact ID. | |
6a488035 | 642 | * |
a6c01b45 CW |
643 | * @return array |
644 | * Assoc array of ACL rules | |
6a488035 TO |
645 | */ |
646 | public static function &getAllByContact($contact_id) { | |
cf0d1c08 | 647 | $result = []; |
6a488035 TO |
648 | |
649 | /* First, the contact-specific ACLs, including ACL Roles */ | |
650 | $result += self::getACLs($contact_id, NULL, TRUE); | |
651 | ||
652 | /* Then, all ACLs granted through group membership */ | |
653 | $result += self::getGroupACLs($contact_id, TRUE); | |
654 | ||
655 | return $result; | |
656 | } | |
657 | ||
28518c90 | 658 | /** |
c490a46a | 659 | * @param array $params |
28518c90 EM |
660 | * |
661 | * @return CRM_ACL_DAO_ACL | |
662 | */ | |
00be9182 | 663 | public static function create(&$params) { |
6a488035 TO |
664 | $dao = new CRM_ACL_DAO_ACL(); |
665 | $dao->copyValues($params); | |
666 | $dao->save(); | |
1fe97a01 | 667 | return $dao; |
6a488035 TO |
668 | } |
669 | ||
28518c90 | 670 | /** |
c490a46a | 671 | * @param array $params |
28518c90 EM |
672 | * @param $defaults |
673 | */ | |
00be9182 | 674 | public static function retrieve(&$params, &$defaults) { |
6a488035 TO |
675 | CRM_Core_DAO::commonRetrieve('CRM_ACL_DAO_ACL', $params, $defaults); |
676 | } | |
677 | ||
678 | /** | |
fe482240 | 679 | * Update the is_active flag in the db. |
6a488035 | 680 | * |
b758c7d5 TO |
681 | * @param int $id |
682 | * Id of the database record. | |
683 | * @param bool $is_active | |
684 | * Value we want to set the is_active field. | |
6a488035 | 685 | * |
8a4fede3 | 686 | * @return bool |
687 | * true if we found and updated the object, else false | |
6a488035 | 688 | */ |
00be9182 | 689 | public static function setIsActive($id, $is_active) { |
9cdf85c1 | 690 | Civi::cache('fields')->flush(); |
5e601882 SL |
691 | // reset ACL and system caches. |
692 | CRM_Core_BAO_Cache::resetCaches(); | |
6a488035 TO |
693 | |
694 | return CRM_Core_DAO::setFieldValue('CRM_ACL_DAO_ACL', $id, 'is_active', $is_active); | |
695 | } | |
696 | ||
28518c90 EM |
697 | /** |
698 | * @param $str | |
100fef9d | 699 | * @param int $contactID |
28518c90 EM |
700 | * |
701 | * @return bool | |
702 | */ | |
00be9182 | 703 | public static function check($str, $contactID) { |
6a488035 TO |
704 | |
705 | $acls = CRM_ACL_BAO_Cache::build($contactID); | |
706 | ||
707 | $aclKeys = array_keys($acls); | |
708 | $aclKeys = implode(',', $aclKeys); | |
709 | ||
710 | if (empty($aclKeys)) { | |
711 | return FALSE; | |
712 | } | |
713 | ||
6a488035 TO |
714 | $query = " |
715 | SELECT count( a.id ) | |
716 | FROM civicrm_acl_cache c, civicrm_acl a | |
717 | WHERE c.acl_id = a.id | |
718 | AND a.is_active = 1 | |
719 | AND a.object_table = %1 | |
720 | AND a.id IN ( $aclKeys ) | |
721 | "; | |
cf0d1c08 | 722 | $params = [1 => [$str, 'String']]; |
6a488035 TO |
723 | |
724 | $count = CRM_Core_DAO::singleValueQuery($query, $params); | |
725 | return ($count) ? TRUE : FALSE; | |
726 | } | |
727 | ||
28518c90 EM |
728 | /** |
729 | * @param $type | |
730 | * @param $tables | |
731 | * @param $whereTables | |
100fef9d | 732 | * @param int $contactID |
28518c90 EM |
733 | * |
734 | * @return null|string | |
735 | */ | |
199761b4 | 736 | public static function whereClause($type, &$tables, &$whereTables, $contactID = NULL) { |
6a488035 | 737 | $acls = CRM_ACL_BAO_Cache::build($contactID); |
6a488035 TO |
738 | |
739 | $whereClause = NULL; | |
cf0d1c08 | 740 | $clauses = []; |
6a488035 TO |
741 | |
742 | if (!empty($acls)) { | |
743 | $aclKeys = array_keys($acls); | |
744 | $aclKeys = implode(',', $aclKeys); | |
745 | ||
746 | $query = " | |
747 | SELECT a.operation, a.object_id | |
748 | FROM civicrm_acl_cache c, civicrm_acl a | |
749 | WHERE c.acl_id = a.id | |
750 | AND a.is_active = 1 | |
751 | AND a.object_table = 'civicrm_saved_search' | |
752 | AND a.id IN ( $aclKeys ) | |
753 | ORDER BY a.object_id | |
754 | "; | |
755 | ||
756 | $dao = CRM_Core_DAO::executeQuery($query); | |
757 | ||
758 | // do an or of all the where clauses u see | |
cf0d1c08 | 759 | $ids = []; |
6a488035 TO |
760 | while ($dao->fetch()) { |
761 | // make sure operation matches the type TODO | |
762 | if (self::matchType($type, $dao->operation)) { | |
763 | if (!$dao->object_id) { | |
cf0d1c08 | 764 | $ids = []; |
6a488035 TO |
765 | $whereClause = ' ( 1 ) '; |
766 | break; | |
767 | } | |
768 | $ids[] = $dao->object_id; | |
769 | } | |
770 | } | |
771 | ||
772 | if (!empty($ids)) { | |
773 | $ids = implode(',', $ids); | |
774 | $query = " | |
775 | SELECT g.* | |
776 | FROM civicrm_group g | |
777 | WHERE g.id IN ( $ids ) | |
778 | AND g.is_active = 1 | |
779 | "; | |
353ffa53 | 780 | $dao = CRM_Core_DAO::executeQuery($query); |
1bcdee33 | 781 | $groupIDs = []; |
782 | $groupContactCacheClause = FALSE; | |
6a488035 | 783 | while ($dao->fetch()) { |
1bcdee33 | 784 | $groupIDs[] = $dao->id; |
6a488035 | 785 | |
1d902030 | 786 | if (($dao->saved_search_id || $dao->children || $dao->parents)) { |
787 | if ($dao->cache_date == NULL) { | |
788 | CRM_Contact_BAO_GroupContactCache::load($dao); | |
789 | } | |
1bcdee33 | 790 | $groupContactCacheClause = " UNION SELECT contact_id FROM civicrm_group_contact_cache WHERE group_id IN (" . implode(', ', $groupIDs) . ")"; |
6a488035 | 791 | } |
6a488035 | 792 | |
6a488035 TO |
793 | } |
794 | ||
1bcdee33 | 795 | if ($groupIDs) { |
796 | $clauses[] = "( | |
797 | `contact_a`.id IN ( | |
798 | SELECT contact_id FROM civicrm_group_contact WHERE group_id IN (" . implode(', ', $groupIDs) . ") AND status = 'Added' | |
799 | $groupContactCacheClause | |
800 | ) | |
801 | )"; | |
6a488035 TO |
802 | } |
803 | } | |
804 | } | |
805 | ||
806 | if (!empty($clauses)) { | |
807 | $whereClause = ' ( ' . implode(' OR ', $clauses) . ' ) '; | |
808 | } | |
809 | ||
810 | // call the hook to get additional whereClauses | |
811 | CRM_Utils_Hook::aclWhereClause($type, $tables, $whereTables, $contactID, $whereClause); | |
812 | ||
199761b4 | 813 | if (empty($whereClause)) { |
6a488035 TO |
814 | $whereClause = ' ( 0 ) '; |
815 | } | |
816 | ||
817 | return $whereClause; | |
818 | } | |
819 | ||
28518c90 | 820 | /** |
c490a46a | 821 | * @param int $type |
100fef9d | 822 | * @param int $contactID |
28518c90 EM |
823 | * @param string $tableName |
824 | * @param null $allGroups | |
825 | * @param null $includedGroups | |
826 | * | |
827 | * @return array | |
828 | */ | |
e6a83034 TO |
829 | public static function group( |
830 | $type, | |
100b0ec6 TO |
831 | $contactID = NULL, |
832 | $tableName = 'civicrm_saved_search', | |
833 | $allGroups = NULL, | |
6a488035 TO |
834 | $includedGroups = NULL |
835 | ) { | |
e3ad0182 | 836 | $userCacheKey = "{$contactID}_{$type}_{$tableName}_" . CRM_Core_Config::domainID() . '_' . md5(implode(',', array_merge((array) $allGroups, (array) $includedGroups))); |
837 | if (empty(Civi::$statics[__CLASS__]['permissioned_groups'])) { | |
cf0d1c08 | 838 | Civi::$statics[__CLASS__]['permissioned_groups'] = []; |
e3ad0182 | 839 | } |
840 | if (!empty(Civi::$statics[__CLASS__]['permissioned_groups'][$userCacheKey])) { | |
841 | return Civi::$statics[__CLASS__]['permissioned_groups'][$userCacheKey]; | |
842 | } | |
6a488035 | 843 | |
8aace6af | 844 | if ($allGroups == NULL) { |
887688b9 | 845 | $allGroups = CRM_Contact_BAO_Contact::buildOptions('group_id', NULL, ['onlyActive' => FALSE]); |
8aace6af JG |
846 | } |
847 | ||
6a488035 TO |
848 | $acls = CRM_ACL_BAO_Cache::build($contactID); |
849 | ||
cf0d1c08 | 850 | $ids = []; |
6a488035 TO |
851 | if (!empty($acls)) { |
852 | $aclKeys = array_keys($acls); | |
853 | $aclKeys = implode(',', $aclKeys); | |
854 | ||
77f080cb | 855 | $cacheKey = CRM_Utils_Cache::cleanKey("$tableName-$aclKeys"); |
557f8c17 ARW |
856 | $cache = CRM_Utils_Cache::singleton(); |
857 | $ids = $cache->get($cacheKey); | |
858 | if (!$ids) { | |
cf0d1c08 | 859 | $ids = []; |
557f8c17 | 860 | $query = " |
6a488035 TO |
861 | SELECT a.operation, a.object_id |
862 | FROM civicrm_acl_cache c, civicrm_acl a | |
863 | WHERE c.acl_id = a.id | |
864 | AND a.is_active = 1 | |
865 | AND a.object_table = %1 | |
866 | AND a.id IN ( $aclKeys ) | |
867 | GROUP BY a.operation,a.object_id | |
868 | ORDER BY a.object_id | |
869 | "; | |
cf0d1c08 | 870 | $params = [1 => [$tableName, 'String']]; |
557f8c17 ARW |
871 | $dao = CRM_Core_DAO::executeQuery($query, $params); |
872 | while ($dao->fetch()) { | |
873 | if ($dao->object_id) { | |
874 | if (self::matchType($type, $dao->operation)) { | |
875 | $ids[] = $dao->object_id; | |
876 | } | |
6a488035 | 877 | } |
557f8c17 ARW |
878 | else { |
879 | // this user has got the permission for all objects of this type | |
880 | // check if the type matches | |
881 | if (self::matchType($type, $dao->operation)) { | |
882 | foreach ($allGroups as $id => $dontCare) { | |
883 | $ids[] = $id; | |
884 | } | |
6a488035 | 885 | } |
557f8c17 | 886 | break; |
6a488035 | 887 | } |
6a488035 | 888 | } |
557f8c17 | 889 | $cache->set($cacheKey, $ids); |
6a488035 TO |
890 | } |
891 | } | |
892 | ||
b1f4e637 RN |
893 | if (empty($ids) && !empty($includedGroups) && |
894 | is_array($includedGroups) | |
895 | ) { | |
896 | $ids = $includedGroups; | |
897 | } | |
e3ad0182 | 898 | if ($contactID) { |
54d93c06 | 899 | $groupWhere = ''; |
900 | if (!empty($allGroups)) { | |
901 | $groupWhere = " AND id IN (" . implode(',', array_keys($allGroups)) . ")"; | |
902 | } | |
e3ad0182 | 903 | // Contacts create hidden groups from search results. They should be able to retrieve their own. |
904 | $ownHiddenGroupsList = CRM_Core_DAO::singleValueQuery(" | |
905 | SELECT GROUP_CONCAT(id) FROM civicrm_group WHERE is_hidden =1 AND created_id = $contactID | |
54d93c06 | 906 | $groupWhere |
e3ad0182 | 907 | "); |
908 | if ($ownHiddenGroupsList) { | |
909 | $ownHiddenGroups = explode(',', $ownHiddenGroupsList); | |
54d93c06 | 910 | $ids = array_merge((array) $ids, $ownHiddenGroups); |
e3ad0182 | 911 | } |
912 | ||
913 | } | |
b1f4e637 | 914 | |
6a488035 | 915 | CRM_Utils_Hook::aclGroup($type, $contactID, $tableName, $allGroups, $ids); |
e3ad0182 | 916 | Civi::$statics[__CLASS__]['permissioned_groups'][$userCacheKey] = $ids; |
6a488035 TO |
917 | return $ids; |
918 | } | |
919 | ||
28518c90 | 920 | /** |
c490a46a | 921 | * @param int $type |
28518c90 EM |
922 | * @param $operation |
923 | * | |
924 | * @return bool | |
925 | */ | |
00be9182 | 926 | public static function matchType($type, $operation) { |
6a488035 TO |
927 | $typeCheck = FALSE; |
928 | switch ($operation) { | |
929 | case 'All': | |
930 | $typeCheck = TRUE; | |
931 | break; | |
932 | ||
933 | case 'View': | |
934 | if ($type == CRM_ACL_API::VIEW) { | |
935 | $typeCheck = TRUE; | |
936 | } | |
937 | break; | |
938 | ||
939 | case 'Edit': | |
940 | if ($type == CRM_ACL_API::VIEW || $type == CRM_ACL_API::EDIT) { | |
941 | $typeCheck = TRUE; | |
942 | } | |
943 | break; | |
944 | ||
945 | case 'Create': | |
946 | if ($type == CRM_ACL_API::CREATE) { | |
947 | $typeCheck = TRUE; | |
948 | } | |
949 | break; | |
950 | ||
951 | case 'Delete': | |
952 | if ($type == CRM_ACL_API::DELETE) { | |
953 | $typeCheck = TRUE; | |
954 | } | |
955 | break; | |
956 | ||
957 | case 'Search': | |
958 | if ($type == CRM_ACL_API::SEARCH) { | |
959 | $typeCheck = TRUE; | |
960 | } | |
961 | break; | |
962 | } | |
963 | return $typeCheck; | |
964 | } | |
965 | ||
966 | /** | |
d2e5d2ce | 967 | * Delete ACL records. |
6a488035 | 968 | * |
b758c7d5 TO |
969 | * @param int $aclId |
970 | * ID of the ACL record to be deleted. | |
6a488035 | 971 | * |
6a488035 | 972 | */ |
00be9182 | 973 | public static function del($aclId) { |
6a488035 TO |
974 | // delete all entries from the acl cache |
975 | CRM_ACL_BAO_Cache::resetCache(); | |
976 | ||
977 | $acl = new CRM_ACL_DAO_ACL(); | |
978 | $acl->id = $aclId; | |
979 | $acl->delete(); | |
980 | } | |
96025800 | 981 | |
6a488035 | 982 | } |