3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
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 |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
21 class CRM_ACL_BAO_ACL
extends CRM_ACL_DAO_ACL
{
25 public static $_entityTable = NULL;
26 public static $_objectTable = NULL;
27 public static $_operation = NULL;
29 public static $_fieldKeys = NULL;
32 * Available operations for pseudoconstant.
36 public static function operation() {
37 if (!self
::$_operation) {
41 'Create' => ts('Create'),
42 'Delete' => ts('Delete'),
43 'Search' => ts('Search'),
47 return self
::$_operation;
51 * Construct an associative array of an ACL rule's properties
53 * @param string $format
54 * Sprintf format for array.
55 * @param bool $hideEmpty
56 * Only return elements that have a value set.
59 * Assoc. array of the ACL rule's properties
61 public function toArray($format = '%s', $hideEmpty = FALSE) {
64 if (!self
::$_fieldKeys) {
65 $fields = CRM_ACL_DAO_ACL
::fields();
66 self
::$_fieldKeys = array_keys($fields);
69 foreach (self
::$_fieldKeys as $field) {
70 $result[$field] = $this->$field;
76 * Retrieve ACLs for a contact or group. Note that including a contact id
77 * without a group id will return those ACL rules which are granted
78 * directly to the contact, but not those granted to the contact through
79 * any/all of his group memberships.
81 * @param int $contact_id
82 * ID of a contact to search for.
85 * Array of assoc. arrays of ACL rules
87 * @throws \CRM_Core_Exception
89 protected static function getACLs(int $contact_id) {
92 $rule = new CRM_ACL_BAO_ACL();
94 $contact = CRM_Contact_BAO_Contact
::getTableName();
96 $query = " SELECT acl.*
98 WHERE acl.entity_table = '$contact'
99 AND acl.entity_id = $contact_id";
101 $rule->query($query);
103 while ($rule->fetch()) {
104 $results[$rule->id
] = $rule->toArray();
107 $results +
= self
::getACLRoles($contact_id);
113 * Get all of the ACLs through ACL groups.
115 * @param int $contact_id
116 * ID of a contact to search for.
119 * Array of assoc. arrays of ACL rules
121 * @throws \CRM_Core_Exception
123 protected static function getACLRoles($contact_id = NULL) {
124 $contact_id = CRM_Utils_Type
::escape($contact_id, 'Integer');
126 $rule = new CRM_ACL_BAO_ACL();
128 $contact = CRM_Contact_BAO_Contact
::getTableName();
130 $query = 'SELECT acl.* FROM civicrm_acl acl';
131 $where = ['acl.entity_table = "civicrm_acl_role" AND acl.entity_id IN (' . implode(',', array_keys(CRM_Core_OptionGroup
::values('acl_role'))) . ')'];
133 if (!empty($contact_id)) {
134 $where[] = " acl.entity_table = '$contact' AND acl.is_active = 1 AND acl.entity_id = $contact_id";
139 $rule->query($query . ' WHERE ' . implode(' AND ', $where));
141 while ($rule->fetch()) {
142 $results[$rule->id
] = $rule->toArray();
149 * Get all ACLs granted to a contact through all group memberships.
151 * @param int $contact_id
153 * @param bool $aclRoles
154 * Include ACL Roles?.
157 * Assoc array of ACL rules
158 * @throws \CRM_Core_Exception
160 protected static function getGroupACLs($contact_id, $aclRoles = FALSE) {
161 $contact_id = CRM_Utils_Type
::escape($contact_id, 'Integer');
163 $rule = new CRM_ACL_BAO_ACL();
165 $c2g = CRM_Contact_BAO_GroupContact
::getTableName();
166 $group = CRM_Contact_BAO_Group
::getTableName();
173 INNER JOIN $c2g group_contact
174 ON acl.entity_id = group_contact.group_id
175 WHERE acl.entity_table = '$group'
176 AND group_contact.contact_id = $contact_id
177 AND group_contact.status = 'Added'";
179 $rule->query($query);
181 while ($rule->fetch()) {
182 $results[$rule->id
] = $rule->toArray();
187 $results +
= self
::getGroupACLRoles($contact_id);
194 * Get all of the ACLs for a contact through ACL groups owned by Contact.
197 * @param int $contact_id
198 * ID of a contact to search for.
201 * Array of assoc. arrays of ACL rules
202 * @throws \CRM_Core_Exception
204 protected static function getGroupACLRoles($contact_id) {
205 $contact_id = CRM_Utils_Type
::escape($contact_id, 'Integer');
207 $rule = new CRM_ACL_BAO_ACL();
209 $aclRole = 'civicrm_acl_role';
211 $aclER = CRM_ACL_DAO_EntityRole
::getTableName();
212 $c2g = CRM_Contact_BAO_GroupContact
::getTableName();
214 $query = " SELECT acl.*
216 INNER JOIN civicrm_option_group og
217 ON og.name = 'acl_role'
218 INNER JOIN civicrm_option_value ov
219 ON acl.entity_table = '$aclRole'
220 AND ov.option_group_id = og.id
221 AND acl.entity_id = ov.value
224 ON $aclER.acl_role_id = acl.entity_id
225 AND $aclER.is_active = 1
227 ON $aclER.entity_id = $c2g.group_id
228 AND $aclER.entity_table = 'civicrm_group'
229 WHERE acl.entity_table = '$aclRole'
230 AND acl.is_active = 1
231 AND $c2g.contact_id = $contact_id
232 AND $c2g.status = 'Added'";
236 $rule->query($query);
238 while ($rule->fetch()) {
239 $results[$rule->id
] = $rule->toArray();
242 // also get all acls for "Any Role" case
243 // and authenticated User Role if present
245 $session = CRM_Core_Session
::singleton();
246 if ($session->get('ufID') > 0) {
253 WHERE acl.entity_id IN ( $roles )
254 AND acl.entity_table = 'civicrm_acl_role'
257 $rule->query($query);
258 while ($rule->fetch()) {
259 $results[$rule->id
] = $rule->toArray();
266 * Get all ACLs owned by a given contact, including domain and group-level.
268 * @param int $contact_id
272 * Assoc array of ACL rules
274 * @throws \CRM_Core_Exception
276 public static function getAllByContact($contact_id) {
279 /* First, the contact-specific ACLs, including ACL Roles */
281 $result +
= self
::getACLs((int) $contact_id);
284 /* Then, all ACLs granted through group membership */
285 $result +
= self
::getGroupACLs($contact_id, TRUE);
291 * @param array $params
293 * @return CRM_ACL_DAO_ACL
295 public static function create($params) {
296 $dao = new CRM_ACL_DAO_ACL();
297 $dao->copyValues($params);
303 * @param array $params
304 * @param array $defaults
306 public static function retrieve(&$params, &$defaults) {
307 CRM_Core_DAO
::commonRetrieve('CRM_ACL_DAO_ACL', $params, $defaults);
311 * Update the is_active flag in the db.
314 * Id of the database record.
315 * @param bool $is_active
316 * Value we want to set the is_active field.
319 * true if we found and updated the object, else false
321 public static function setIsActive($id, $is_active) {
322 Civi
::cache('fields')->flush();
323 // reset ACL and system caches.
324 CRM_Core_BAO_Cache
::resetCaches();
326 return CRM_Core_DAO
::setFieldValue('CRM_ACL_DAO_ACL', $id, 'is_active', $is_active);
331 * @param int $contactID
335 public static function check($str, $contactID) {
337 $acls = CRM_ACL_BAO_Cache
::build($contactID);
339 $aclKeys = array_keys($acls);
340 $aclKeys = implode(',', $aclKeys);
342 if (empty($aclKeys)) {
348 FROM civicrm_acl_cache c, civicrm_acl a
349 WHERE c.acl_id = a.id
351 AND a.object_table = %1
352 AND a.id IN ( $aclKeys )
354 $params = [1 => [$str, 'String']];
356 $count = CRM_Core_DAO
::singleValueQuery($query, $params);
357 return (bool) $count;
363 * @param $whereTables
364 * @param int $contactID
366 * @return null|string
368 public static function whereClause($type, &$tables, &$whereTables, $contactID = NULL) {
369 $acls = CRM_ACL_BAO_Cache
::build($contactID);
375 $aclKeys = array_keys($acls);
376 $aclKeys = implode(',', $aclKeys);
379 SELECT a.operation, a.object_id
380 FROM civicrm_acl_cache c, civicrm_acl a
381 WHERE c.acl_id = a.id
383 AND a.object_table = 'civicrm_saved_search'
384 AND a.id IN ( $aclKeys )
388 $dao = CRM_Core_DAO
::executeQuery($query);
390 // do an or of all the where clauses u see
392 while ($dao->fetch()) {
393 // make sure operation matches the type TODO
394 if (self
::matchType($type, $dao->operation
)) {
395 if (!$dao->object_id
) {
397 $whereClause = ' ( 1 ) ';
400 $ids[] = $dao->object_id
;
405 $ids = implode(',', $ids);
409 WHERE g.id IN ( $ids )
412 $dao = CRM_Core_DAO
::executeQuery($query);
414 $groupContactCacheClause = FALSE;
415 while ($dao->fetch()) {
416 $groupIDs[] = $dao->id
;
418 if (($dao->saved_search_id ||
$dao->children ||
$dao->parents
)) {
419 if ($dao->cache_date
== NULL) {
420 CRM_Contact_BAO_GroupContactCache
::load($dao);
422 $groupContactCacheClause = " UNION SELECT contact_id FROM civicrm_group_contact_cache WHERE group_id IN (" . implode(', ', $groupIDs) . ")";
430 SELECT contact_id FROM civicrm_group_contact WHERE group_id IN (" . implode(', ', $groupIDs) . ") AND status = 'Added'
431 $groupContactCacheClause
438 if (!empty($clauses)) {
439 $whereClause = ' ( ' . implode(' OR ', $clauses) . ' ) ';
442 // call the hook to get additional whereClauses
443 CRM_Utils_Hook
::aclWhereClause($type, $tables, $whereTables, $contactID, $whereClause);
445 if (empty($whereClause)) {
446 $whereClause = ' ( 0 ) ';
454 * @param int $contactID
455 * @param string $tableName
456 * @param null $allGroups
457 * @param null $includedGroups
461 public static function group(
464 $tableName = 'civicrm_saved_search',
466 $includedGroups = NULL
468 $userCacheKey = "{$contactID}_{$type}_{$tableName}_" . CRM_Core_Config
::domainID() . '_' . md5(implode(',', array_merge((array) $allGroups, (array) $includedGroups)));
469 if (empty(Civi
::$statics[__CLASS__
]['permissioned_groups'])) {
470 Civi
::$statics[__CLASS__
]['permissioned_groups'] = [];
472 if (!empty(Civi
::$statics[__CLASS__
]['permissioned_groups'][$userCacheKey])) {
473 return Civi
::$statics[__CLASS__
]['permissioned_groups'][$userCacheKey];
476 if ($allGroups == NULL) {
477 $allGroups = CRM_Contact_BAO_Contact
::buildOptions('group_id', 'get');
480 $acls = CRM_ACL_BAO_Cache
::build($contactID);
484 $aclKeys = array_keys($acls);
485 $aclKeys = implode(',', $aclKeys);
487 $cacheKey = CRM_Utils_Cache
::cleanKey("$tableName-$aclKeys");
488 $cache = CRM_Utils_Cache
::singleton();
489 $ids = $cache->get($cacheKey);
493 SELECT a.operation, a.object_id
494 FROM civicrm_acl_cache c, civicrm_acl a
495 WHERE c.acl_id = a.id
497 AND a.object_table = %1
498 AND a.id IN ( $aclKeys )
499 GROUP BY a.operation,a.object_id
502 $params = [1 => [$tableName, 'String']];
503 $dao = CRM_Core_DAO
::executeQuery($query, $params);
504 while ($dao->fetch()) {
505 if ($dao->object_id
) {
506 if (self
::matchType($type, $dao->operation
)) {
507 $ids[] = $dao->object_id
;
511 // this user has got the permission for all objects of this type
512 // check if the type matches
513 if (self
::matchType($type, $dao->operation
)) {
514 foreach ($allGroups as $id => $dontCare) {
521 $cache->set($cacheKey, $ids);
525 if (empty($ids) && !empty($includedGroups) &&
526 is_array($includedGroups)
528 $ids = $includedGroups;
532 if (!empty($allGroups)) {
533 $groupWhere = " AND id IN (" . implode(',', array_keys($allGroups)) . ")";
535 // Contacts create hidden groups from search results. They should be able to retrieve their own.
536 $ownHiddenGroupsList = CRM_Core_DAO
::singleValueQuery("
537 SELECT GROUP_CONCAT(id) FROM civicrm_group WHERE is_hidden =1 AND created_id = $contactID
540 if ($ownHiddenGroupsList) {
541 $ownHiddenGroups = explode(',', $ownHiddenGroupsList);
542 $ids = array_merge((array) $ids, $ownHiddenGroups);
547 CRM_Utils_Hook
::aclGroup($type, $contactID, $tableName, $allGroups, $ids);
548 Civi
::$statics[__CLASS__
]['permissioned_groups'][$userCacheKey] = $ids;
558 protected static function matchType($type, $operation) {
560 switch ($operation) {
566 if ($type == CRM_ACL_API
::VIEW
) {
572 if ($type == CRM_ACL_API
::VIEW ||
$type == CRM_ACL_API
::EDIT
) {
578 if ($type == CRM_ACL_API
::CREATE
) {
584 if ($type == CRM_ACL_API
::DELETE
) {
590 if ($type == CRM_ACL_API
::SEARCH
) {
599 * Delete ACL records.
602 * ID of the ACL record to be deleted.
605 public static function del($aclId) {
606 // delete all entries from the acl cache
607 CRM_ACL_BAO_Cache
::resetCache();
609 $acl = new CRM_ACL_DAO_ACL();