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