Remove getACLRoles
[civicrm-core.git] / CRM / ACL / BAO / ACL.php
CommitLineData
6a488035 1<?php
6a488035
TO
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
16 */
17
18/**
19 * Access Control List
20 */
21class CRM_ACL_BAO_ACL extends CRM_ACL_DAO_ACL {
db01bf2f 22 /**
23 * @var string
24 */
683bf891
SL
25 public static $_entityTable = NULL;
26 public static $_objectTable = NULL;
27 public static $_operation = NULL;
6a488035 28
683bf891 29 public static $_fieldKeys = NULL;
6a488035 30
28518c90 31 /**
9bd90abd 32 * Available operations for pseudoconstant.
33 *
34 * @return array
28518c90 35 */
00be9182 36 public static function operation() {
6a488035 37 if (!self::$_operation) {
cf0d1c08 38 self::$_operation = [
6a488035
TO
39 'View' => ts('View'),
40 'Edit' => ts('Edit'),
41 'Create' => ts('Create'),
42 'Delete' => ts('Delete'),
43 'Search' => ts('Search'),
44 'All' => ts('All'),
cf0d1c08 45 ];
6a488035
TO
46 }
47 return self::$_operation;
48 }
49
6a488035
TO
50 /**
51 * Construct an associative array of an ACL rule's properties
52 *
b758c7d5
TO
53 * @param string $format
54 * Sprintf format for array.
55 * @param bool $hideEmpty
56 * Only return elements that have a value set.
6a488035 57 *
a6c01b45
CW
58 * @return array
59 * Assoc. array of the ACL rule's properties
6a488035 60 */
100b0ec6 61 public function toArray($format = '%s', $hideEmpty = FALSE) {
cf0d1c08 62 $result = [];
6a488035
TO
63
64 if (!self::$_fieldKeys) {
65 $fields = CRM_ACL_DAO_ACL::fields();
66 self::$_fieldKeys = array_keys($fields);
67 }
68
69 foreach (self::$_fieldKeys as $field) {
70 $result[$field] = $this->$field;
71 }
72 return $result;
73 }
74
6a488035 75 /**
d2e5d2ce 76 * Get all ACLs granted to a contact through all group memberships.
6a488035 77 *
b758c7d5
TO
78 * @param int $contact_id
79 * The contact's ID.
80 * @param bool $aclRoles
81 * Include ACL Roles?.
6a488035 82 *
a6c01b45
CW
83 * @return array
84 * Assoc array of ACL rules
03149bb2 85 * @throws \CRM_Core_Exception
6a488035 86 */
9bd90abd 87 protected static function getGroupACLs($contact_id, $aclRoles = FALSE) {
6a488035
TO
88 $contact_id = CRM_Utils_Type::escape($contact_id, 'Integer');
89
cf0d1c08 90 $results = [];
6a488035
TO
91
92 if ($contact_id) {
93 $query = "
20095057 94SELECT acl.*
d340e675 95 FROM civicrm_acl acl
51f5878d 96 INNER JOIN civicrm_group_contact group_contact
20095057 97 ON acl.entity_id = group_contact.group_id
51f5878d 98 WHERE acl.entity_table = 'civicrm_group'
20095057 99 AND group_contact.contact_id = $contact_id
100 AND group_contact.status = 'Added'";
6a488035 101
51f5878d 102 $rule = CRM_Core_DAO::executeQuery($query);
6a488035
TO
103
104 while ($rule->fetch()) {
79380078 105 $results[$rule->id] = $rule->toArray();
6a488035
TO
106 }
107 }
108
109 if ($aclRoles) {
110 $results += self::getGroupACLRoles($contact_id);
111 }
112
113 return $results;
114 }
115
116 /**
d2e5d2ce 117 * Get all of the ACLs for a contact through ACL groups owned by Contact.
6a488035
TO
118 * groups.
119 *
b758c7d5
TO
120 * @param int $contact_id
121 * ID of a contact to search for.
6a488035 122 *
a6c01b45
CW
123 * @return array
124 * Array of assoc. arrays of ACL rules
03149bb2 125 * @throws \CRM_Core_Exception
6a488035 126 */
9bd90abd 127 protected static function getGroupACLRoles($contact_id) {
6a488035
TO
128 $contact_id = CRM_Utils_Type::escape($contact_id, 'Integer');
129
20095057 130 $query = " SELECT acl.*
d340e675 131 FROM civicrm_acl acl
6a488035
TO
132 INNER JOIN civicrm_option_group og
133 ON og.name = 'acl_role'
134 INNER JOIN civicrm_option_value ov
a4757413 135 ON acl.entity_table = 'civicrm_acl_role'
6a488035 136 AND ov.option_group_id = og.id
20095057 137 AND acl.entity_id = ov.value
6a488035 138 AND ov.is_active = 1
a4757413 139 INNER JOIN civicrm_acl_entity_role acl_entity_role
140 ON acl_entity_role.acl_role_id = acl.entity_id
141 AND acl_entity_role.is_active = 1
142 INNER JOIN civicrm_group_contact group_contact
143 ON acl_entity_role.entity_id = group_contact.group_id
144 AND acl_entity_role.entity_table = 'civicrm_group'
145 WHERE acl.entity_table = 'civicrm_acl_role'
20095057 146 AND acl.is_active = 1
a4757413 147 AND group_contact.contact_id = $contact_id
148 AND group_contact.status = 'Added'";
6a488035 149
cf0d1c08 150 $results = [];
6a488035 151
3e8437f3 152 $rule = CRM_Core_DAO::executeQuery($query);
6a488035
TO
153
154 while ($rule->fetch()) {
39eb89f4 155 $results[$rule->id] = $rule->toArray();
6a488035
TO
156 }
157
158 // also get all acls for "Any Role" case
159 // and authenticated User Role if present
160 $roles = "0";
161 $session = CRM_Core_Session::singleton();
162 if ($session->get('ufID') > 0) {
163 $roles .= ",2";
164 }
165
166 $query = "
20095057 167SELECT acl.*
d340e675 168 FROM civicrm_acl acl
20095057 169 WHERE acl.entity_id IN ( $roles )
170 AND acl.entity_table = 'civicrm_acl_role'
6a488035
TO
171";
172
3e8437f3 173 $rule = CRM_Core_DAO::executeQuery($query);
6a488035
TO
174 while ($rule->fetch()) {
175 $results[$rule->id] = $rule->toArray();
176 }
177
178 return $results;
179 }
180
181 /**
182 * Get all ACLs owned by a given contact, including domain and group-level.
183 *
b758c7d5
TO
184 * @param int $contact_id
185 * The contact ID.
6a488035 186 *
a6c01b45
CW
187 * @return array
188 * Assoc array of ACL rules
03149bb2 189 *
190 * @throws \CRM_Core_Exception
6a488035 191 */
03149bb2 192 public static function getAllByContact($contact_id) {
cf0d1c08 193 $result = [];
6a488035
TO
194
195 /* First, the contact-specific ACLs, including ACL Roles */
2ee636aa 196 if ($contact_id) {
02e01272 197 $query = " SELECT acl.*
198 FROM civicrm_acl acl
199 WHERE acl.entity_table = 'civicrm_contact'
200 AND acl.entity_id = $contact_id";
201
202 $rule = CRM_Core_DAO::executeQuery($query);
203
204 while ($rule->fetch()) {
205 $result[$rule->id] = $rule->toArray();
206 }
2ee636aa 207 }
6a488035
TO
208
209 /* Then, all ACLs granted through group membership */
210 $result += self::getGroupACLs($contact_id, TRUE);
211
212 return $result;
213 }
214
28518c90 215 /**
c490a46a 216 * @param array $params
28518c90
EM
217 *
218 * @return CRM_ACL_DAO_ACL
219 */
03149bb2 220 public static function create($params) {
6a488035
TO
221 $dao = new CRM_ACL_DAO_ACL();
222 $dao->copyValues($params);
223 $dao->save();
1fe97a01 224 return $dao;
6a488035
TO
225 }
226
28518c90 227 /**
c490a46a 228 * @param array $params
03149bb2 229 * @param array $defaults
28518c90 230 */
00be9182 231 public static function retrieve(&$params, &$defaults) {
6a488035
TO
232 CRM_Core_DAO::commonRetrieve('CRM_ACL_DAO_ACL', $params, $defaults);
233 }
234
235 /**
fe482240 236 * Update the is_active flag in the db.
6a488035 237 *
b758c7d5
TO
238 * @param int $id
239 * Id of the database record.
240 * @param bool $is_active
241 * Value we want to set the is_active field.
6a488035 242 *
8a4fede3 243 * @return bool
244 * true if we found and updated the object, else false
6a488035 245 */
00be9182 246 public static function setIsActive($id, $is_active) {
9cdf85c1 247 Civi::cache('fields')->flush();
5e601882
SL
248 // reset ACL and system caches.
249 CRM_Core_BAO_Cache::resetCaches();
6a488035
TO
250
251 return CRM_Core_DAO::setFieldValue('CRM_ACL_DAO_ACL', $id, 'is_active', $is_active);
252 }
253
28518c90
EM
254 /**
255 * @param $str
100fef9d 256 * @param int $contactID
28518c90
EM
257 *
258 * @return bool
259 */
00be9182 260 public static function check($str, $contactID) {
6a488035
TO
261
262 $acls = CRM_ACL_BAO_Cache::build($contactID);
263
264 $aclKeys = array_keys($acls);
265 $aclKeys = implode(',', $aclKeys);
266
267 if (empty($aclKeys)) {
268 return FALSE;
269 }
270
6a488035
TO
271 $query = "
272SELECT count( a.id )
273 FROM civicrm_acl_cache c, civicrm_acl a
274 WHERE c.acl_id = a.id
275 AND a.is_active = 1
276 AND a.object_table = %1
277 AND a.id IN ( $aclKeys )
278";
cf0d1c08 279 $params = [1 => [$str, 'String']];
6a488035
TO
280
281 $count = CRM_Core_DAO::singleValueQuery($query, $params);
1699214f 282 return (bool) $count;
6a488035
TO
283 }
284
28518c90
EM
285 /**
286 * @param $type
287 * @param $tables
288 * @param $whereTables
100fef9d 289 * @param int $contactID
28518c90
EM
290 *
291 * @return null|string
292 */
199761b4 293 public static function whereClause($type, &$tables, &$whereTables, $contactID = NULL) {
6a488035 294 $acls = CRM_ACL_BAO_Cache::build($contactID);
6a488035
TO
295
296 $whereClause = NULL;
cf0d1c08 297 $clauses = [];
6a488035
TO
298
299 if (!empty($acls)) {
300 $aclKeys = array_keys($acls);
301 $aclKeys = implode(',', $aclKeys);
302
303 $query = "
304SELECT a.operation, a.object_id
305 FROM civicrm_acl_cache c, civicrm_acl a
306 WHERE c.acl_id = a.id
307 AND a.is_active = 1
308 AND a.object_table = 'civicrm_saved_search'
309 AND a.id IN ( $aclKeys )
310ORDER BY a.object_id
311";
312
313 $dao = CRM_Core_DAO::executeQuery($query);
314
315 // do an or of all the where clauses u see
cf0d1c08 316 $ids = [];
6a488035
TO
317 while ($dao->fetch()) {
318 // make sure operation matches the type TODO
319 if (self::matchType($type, $dao->operation)) {
320 if (!$dao->object_id) {
cf0d1c08 321 $ids = [];
6a488035
TO
322 $whereClause = ' ( 1 ) ';
323 break;
324 }
325 $ids[] = $dao->object_id;
326 }
327 }
328
329 if (!empty($ids)) {
330 $ids = implode(',', $ids);
331 $query = "
332SELECT g.*
333 FROM civicrm_group g
334 WHERE g.id IN ( $ids )
335 AND g.is_active = 1
336";
353ffa53 337 $dao = CRM_Core_DAO::executeQuery($query);
1bcdee33 338 $groupIDs = [];
339 $groupContactCacheClause = FALSE;
6a488035 340 while ($dao->fetch()) {
1bcdee33 341 $groupIDs[] = $dao->id;
6a488035 342
1d902030 343 if (($dao->saved_search_id || $dao->children || $dao->parents)) {
344 if ($dao->cache_date == NULL) {
345 CRM_Contact_BAO_GroupContactCache::load($dao);
346 }
1bcdee33 347 $groupContactCacheClause = " UNION SELECT contact_id FROM civicrm_group_contact_cache WHERE group_id IN (" . implode(', ', $groupIDs) . ")";
6a488035 348 }
6a488035 349
6a488035
TO
350 }
351
1bcdee33 352 if ($groupIDs) {
353 $clauses[] = "(
354 `contact_a`.id IN (
355 SELECT contact_id FROM civicrm_group_contact WHERE group_id IN (" . implode(', ', $groupIDs) . ") AND status = 'Added'
356 $groupContactCacheClause
357 )
358 )";
6a488035
TO
359 }
360 }
361 }
362
363 if (!empty($clauses)) {
364 $whereClause = ' ( ' . implode(' OR ', $clauses) . ' ) ';
365 }
366
367 // call the hook to get additional whereClauses
368 CRM_Utils_Hook::aclWhereClause($type, $tables, $whereTables, $contactID, $whereClause);
369
199761b4 370 if (empty($whereClause)) {
6a488035
TO
371 $whereClause = ' ( 0 ) ';
372 }
373
374 return $whereClause;
375 }
376
28518c90 377 /**
c490a46a 378 * @param int $type
100fef9d 379 * @param int $contactID
28518c90
EM
380 * @param string $tableName
381 * @param null $allGroups
382 * @param null $includedGroups
383 *
384 * @return array
385 */
e6a83034
TO
386 public static function group(
387 $type,
100b0ec6
TO
388 $contactID = NULL,
389 $tableName = 'civicrm_saved_search',
390 $allGroups = NULL,
6a488035
TO
391 $includedGroups = NULL
392 ) {
e3ad0182 393 $userCacheKey = "{$contactID}_{$type}_{$tableName}_" . CRM_Core_Config::domainID() . '_' . md5(implode(',', array_merge((array) $allGroups, (array) $includedGroups)));
394 if (empty(Civi::$statics[__CLASS__]['permissioned_groups'])) {
cf0d1c08 395 Civi::$statics[__CLASS__]['permissioned_groups'] = [];
e3ad0182 396 }
397 if (!empty(Civi::$statics[__CLASS__]['permissioned_groups'][$userCacheKey])) {
398 return Civi::$statics[__CLASS__]['permissioned_groups'][$userCacheKey];
399 }
6a488035 400
8aace6af 401 if ($allGroups == NULL) {
39868387 402 $allGroups = CRM_Contact_BAO_Contact::buildOptions('group_id', 'get');
8aace6af
JG
403 }
404
6a488035
TO
405 $acls = CRM_ACL_BAO_Cache::build($contactID);
406
cf0d1c08 407 $ids = [];
6a488035
TO
408 if (!empty($acls)) {
409 $aclKeys = array_keys($acls);
410 $aclKeys = implode(',', $aclKeys);
411
d4761b7f 412 $cacheKey = CRM_Utils_Cache::cleanKey("$type-$tableName-$aclKeys");
557f8c17
ARW
413 $cache = CRM_Utils_Cache::singleton();
414 $ids = $cache->get($cacheKey);
69fb41da 415 if (!is_array($ids)) {
cf0d1c08 416 $ids = [];
557f8c17 417 $query = "
6a488035
TO
418SELECT a.operation, a.object_id
419 FROM civicrm_acl_cache c, civicrm_acl a
420 WHERE c.acl_id = a.id
421 AND a.is_active = 1
422 AND a.object_table = %1
423 AND a.id IN ( $aclKeys )
424GROUP BY a.operation,a.object_id
425ORDER BY a.object_id
426";
cf0d1c08 427 $params = [1 => [$tableName, 'String']];
557f8c17
ARW
428 $dao = CRM_Core_DAO::executeQuery($query, $params);
429 while ($dao->fetch()) {
430 if ($dao->object_id) {
431 if (self::matchType($type, $dao->operation)) {
432 $ids[] = $dao->object_id;
433 }
6a488035 434 }
557f8c17
ARW
435 else {
436 // this user has got the permission for all objects of this type
437 // check if the type matches
438 if (self::matchType($type, $dao->operation)) {
439 foreach ($allGroups as $id => $dontCare) {
440 $ids[] = $id;
441 }
6a488035 442 }
557f8c17 443 break;
6a488035 444 }
6a488035 445 }
557f8c17 446 $cache->set($cacheKey, $ids);
6a488035
TO
447 }
448 }
449
b1f4e637
RN
450 if (empty($ids) && !empty($includedGroups) &&
451 is_array($includedGroups)
452 ) {
7ad3182b 453 // This is pretty alarming - we 'sometimes' include all included groups
454 // seems problematic per https://lab.civicrm.org/dev/core/-/issues/1879
b1f4e637
RN
455 $ids = $includedGroups;
456 }
e3ad0182 457 if ($contactID) {
54d93c06 458 $groupWhere = '';
459 if (!empty($allGroups)) {
460 $groupWhere = " AND id IN (" . implode(',', array_keys($allGroups)) . ")";
461 }
e3ad0182 462 // Contacts create hidden groups from search results. They should be able to retrieve their own.
463 $ownHiddenGroupsList = CRM_Core_DAO::singleValueQuery("
464 SELECT GROUP_CONCAT(id) FROM civicrm_group WHERE is_hidden =1 AND created_id = $contactID
54d93c06 465 $groupWhere
e3ad0182 466 ");
467 if ($ownHiddenGroupsList) {
468 $ownHiddenGroups = explode(',', $ownHiddenGroupsList);
54d93c06 469 $ids = array_merge((array) $ids, $ownHiddenGroups);
e3ad0182 470 }
471
472 }
b1f4e637 473
6a488035 474 CRM_Utils_Hook::aclGroup($type, $contactID, $tableName, $allGroups, $ids);
e3ad0182 475 Civi::$statics[__CLASS__]['permissioned_groups'][$userCacheKey] = $ids;
6a488035
TO
476 return $ids;
477 }
478
28518c90 479 /**
c490a46a 480 * @param int $type
28518c90
EM
481 * @param $operation
482 *
483 * @return bool
484 */
9bd90abd 485 protected static function matchType($type, $operation) {
6a488035
TO
486 $typeCheck = FALSE;
487 switch ($operation) {
488 case 'All':
489 $typeCheck = TRUE;
490 break;
491
492 case 'View':
493 if ($type == CRM_ACL_API::VIEW) {
494 $typeCheck = TRUE;
495 }
496 break;
497
498 case 'Edit':
499 if ($type == CRM_ACL_API::VIEW || $type == CRM_ACL_API::EDIT) {
500 $typeCheck = TRUE;
501 }
502 break;
503
504 case 'Create':
505 if ($type == CRM_ACL_API::CREATE) {
506 $typeCheck = TRUE;
507 }
508 break;
509
510 case 'Delete':
511 if ($type == CRM_ACL_API::DELETE) {
512 $typeCheck = TRUE;
513 }
514 break;
515
516 case 'Search':
517 if ($type == CRM_ACL_API::SEARCH) {
518 $typeCheck = TRUE;
519 }
520 break;
521 }
522 return $typeCheck;
523 }
524
525 /**
d2e5d2ce 526 * Delete ACL records.
6a488035 527 *
b758c7d5
TO
528 * @param int $aclId
529 * ID of the ACL record to be deleted.
6a488035 530 *
6a488035 531 */
00be9182 532 public static function del($aclId) {
6a488035
TO
533 // delete all entries from the acl cache
534 CRM_ACL_BAO_Cache::resetCache();
535
536 $acl = new CRM_ACL_DAO_ACL();
537 $acl->id = $aclId;
538 $acl->delete();
539 }
96025800 540
6a488035 541}