1007 lines
31 KiB
PHP
1007 lines
31 KiB
PHP
|
<?php
|
||
|
/*
|
||
|
+--------------------------------------------------------------------+
|
||
|
| CiviCRM version 4.7 |
|
||
|
+--------------------------------------------------------------------+
|
||
|
| Copyright CiviCRM LLC (c) 2004-2017 |
|
||
|
+--------------------------------------------------------------------+
|
||
|
| This file is a part of CiviCRM. |
|
||
|
| |
|
||
|
| CiviCRM is free software; you can copy, modify, and distribute it |
|
||
|
| under the terms of the GNU Affero General Public License |
|
||
|
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
|
||
|
| |
|
||
|
| CiviCRM is distributed in the hope that it will be useful, but |
|
||
|
| WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||
|
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
|
||
|
| See the GNU Affero General Public License for more details. |
|
||
|
| |
|
||
|
| You should have received a copy of the GNU Affero General Public |
|
||
|
| License and the CiviCRM Licensing Exception along |
|
||
|
| with this program; if not, contact CiviCRM LLC |
|
||
|
| at info[AT]civicrm[DOT]org. If you have questions about the |
|
||
|
| GNU Affero General Public License or the licensing of CiviCRM, |
|
||
|
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
|
||
|
+--------------------------------------------------------------------+
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @package CRM
|
||
|
* @copyright CiviCRM LLC (c) 2004-2017
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Access Control List
|
||
|
*/
|
||
|
class CRM_ACL_BAO_ACL extends CRM_ACL_DAO_ACL {
|
||
|
/**
|
||
|
* @var string
|
||
|
*/
|
||
|
static $_entityTable = NULL;
|
||
|
static $_objectTable = NULL;
|
||
|
static $_operation = NULL;
|
||
|
|
||
|
static $_fieldKeys = NULL;
|
||
|
|
||
|
/**
|
||
|
* Get ACL entity table.
|
||
|
*
|
||
|
* @return array|null
|
||
|
*/
|
||
|
public static function entityTable() {
|
||
|
if (!self::$_entityTable) {
|
||
|
self::$_entityTable = array(
|
||
|
'civicrm_contact' => ts('Contact'),
|
||
|
'civicrm_acl_role' => ts('ACL Role'),
|
||
|
);
|
||
|
}
|
||
|
return self::$_entityTable;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return array|null
|
||
|
*/
|
||
|
public static function objectTable() {
|
||
|
if (!self::$_objectTable) {
|
||
|
self::$_objectTable = array(
|
||
|
'civicrm_contact' => ts('Contact'),
|
||
|
'civicrm_group' => ts('Group'),
|
||
|
'civicrm_saved_search' => ts('Contact Group'),
|
||
|
'civicrm_admin' => ts('Import'),
|
||
|
);
|
||
|
}
|
||
|
return self::$_objectTable;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return array|null
|
||
|
*/
|
||
|
public static function operation() {
|
||
|
if (!self::$_operation) {
|
||
|
self::$_operation = array(
|
||
|
'View' => ts('View'),
|
||
|
'Edit' => ts('Edit'),
|
||
|
'Create' => ts('Create'),
|
||
|
'Delete' => ts('Delete'),
|
||
|
'Search' => ts('Search'),
|
||
|
'All' => ts('All'),
|
||
|
);
|
||
|
}
|
||
|
return self::$_operation;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Construct a WHERE clause to handle permissions to $object_*
|
||
|
*
|
||
|
* @param array $tables
|
||
|
* Any tables that may be needed in the FROM.
|
||
|
* @param string $operation
|
||
|
* The operation being attempted.
|
||
|
* @param string $object_table
|
||
|
* The table of the object in question.
|
||
|
* @param int $object_id
|
||
|
* The ID of the object in question.
|
||
|
* @param int $acl_id
|
||
|
* If it's a grant/revoke operation, the ACL ID.
|
||
|
* @param bool $acl_role
|
||
|
* For grant operations, this flag determines if we're granting a single acl (false) or an entire group.
|
||
|
*
|
||
|
* @return string
|
||
|
* The WHERE clause, or 0 on failure
|
||
|
*/
|
||
|
public static function permissionClause(
|
||
|
&$tables, $operation,
|
||
|
$object_table = NULL, $object_id = NULL,
|
||
|
$acl_id = NULL, $acl_role = FALSE
|
||
|
) {
|
||
|
$dao = new CRM_ACL_DAO_ACL();
|
||
|
|
||
|
$t = array(
|
||
|
'ACL' => self::getTableName(),
|
||
|
'ACLRole' => 'civicrm_acl_role',
|
||
|
'ACLEntityRole' => CRM_ACL_DAO_EntityRole::getTableName(),
|
||
|
'Contact' => CRM_Contact_DAO_Contact::getTableName(),
|
||
|
'Group' => CRM_Contact_DAO_Group::getTableName(),
|
||
|
'GroupContact' => CRM_Contact_DAO_GroupContact::getTableName(),
|
||
|
);
|
||
|
|
||
|
$contact_id = CRM_Core_Session::getLoggedInContactID();
|
||
|
|
||
|
$where = " {$t['ACL']}.operation = '" . CRM_Utils_Type::escape($operation, 'String') . "'";
|
||
|
|
||
|
/* Include clause if we're looking for a specific table/id permission */
|
||
|
|
||
|
if (!empty($object_table)) {
|
||
|
$where .= " AND ( {$t['ACL']}.object_table IS null
|
||
|
OR ({$t['ACL']}.object_table = '" . CRM_Utils_Type::escape($object_table, 'String') . "'";
|
||
|
if (!empty($object_id)) {
|
||
|
$where .= " AND ({$t['ACL']}.object_id IS null
|
||
|
OR {$t['ACL']}.object_id = " . CRM_Utils_Type::escape($object_id, 'Integer') . ')';
|
||
|
}
|
||
|
$where .= '))';
|
||
|
}
|
||
|
|
||
|
/* Include clause if we're granting an ACL or ACL Role */
|
||
|
|
||
|
if (!empty($acl_id)) {
|
||
|
$where .= " AND ({$t['ACL']}.acl_id IS null
|
||
|
OR {$t['ACL']}.acl_id = " . CRM_Utils_Type::escape($acl_id, 'Integer') . ')';
|
||
|
if ($acl_role) {
|
||
|
$where .= " AND {$t['ACL']}.acl_table = '{$t['ACLRole']}'";
|
||
|
}
|
||
|
else {
|
||
|
$where .= " AND {$t['ACL']}.acl_table = '{$t['ACL']}'";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$query = array();
|
||
|
|
||
|
/* Query for permissions granted to all contacts in the domain */
|
||
|
|
||
|
$query[] = "SELECT {$t['ACL']}.*, 0 as override
|
||
|
FROM {$t['ACL']}
|
||
|
|
||
|
WHERE {$t['ACL']}.entity_table = '{$t['Domain']}'
|
||
|
AND ($where)";
|
||
|
|
||
|
/* Query for permissions granted to all contacts through an ACL group */
|
||
|
|
||
|
$query[] = "SELECT {$t['ACL']}.*, 0 as override
|
||
|
FROM {$t['ACL']}
|
||
|
|
||
|
INNER JOIN {$t['ACLEntityRole']}
|
||
|
ON ({$t['ACL']}.entity_table = '{$t['ACLRole']}'
|
||
|
AND {$t['ACL']}.entity_id =
|
||
|
{$t['ACLEntityRole']}.acl_role_id)
|
||
|
|
||
|
INNER JOIN {$t['ACLRole']}
|
||
|
ON {$t['ACL']}.entity_id =
|
||
|
{$t['ACLRole']}.id
|
||
|
|
||
|
WHERE {$t['ACLEntityRole']}.entity_table =
|
||
|
'{$t['Domain']}'
|
||
|
AND {$t['ACLRole']}.is_active = 1
|
||
|
AND ($where)";
|
||
|
|
||
|
/* Query for permissions granted directly to the contact */
|
||
|
|
||
|
$query[] = "SELECT {$t['ACL']}.*, 1 as override
|
||
|
FROM {$t['ACL']}
|
||
|
|
||
|
INNER JOIN {$t['Contact']}
|
||
|
ON ({$t['ACL']}.entity_table = '{$t['Contact']}'
|
||
|
AND {$t['ACL']}.entity_id = {$t['Contact']}.id)
|
||
|
|
||
|
WHERE {$t['Contact']}.id = $contact_id
|
||
|
AND ($where)";
|
||
|
|
||
|
/* Query for permissions granted to the contact through an ACL group */
|
||
|
|
||
|
$query[] = "SELECT {$t['ACL']}.*, 1 as override
|
||
|
FROM {$t['ACL']}
|
||
|
|
||
|
INNER JOIN {$t['ACLEntityRole']}
|
||
|
ON ({$t['ACL']}.entity_table = '{$t['ACLRole']}'
|
||
|
AND {$t['ACL']}.entity_id =
|
||
|
{$t['ACLEntityRole']}.acl_role_id)
|
||
|
|
||
|
INNER JOIN {$t['ACLRole']}
|
||
|
ON {$t['ACL']}.entity_id = {$t['ACLRole']}.id
|
||
|
|
||
|
WHERE {$t['ACLEntityRole']}.entity_table =
|
||
|
'{$t['Contact']}'
|
||
|
AND {$t['ACLRole']}.is_active = 1
|
||
|
AND {$t['ACLEntityRole']}.entity_id = $contact_id
|
||
|
AND ($where)";
|
||
|
|
||
|
/* Query for permissions granted to the contact through a group */
|
||
|
|
||
|
$query[] = "SELECT {$t['ACL']}.*, 0 as override
|
||
|
FROM {$t['ACL']}
|
||
|
|
||
|
INNER JOIN {$t['GroupContact']}
|
||
|
ON ({$t['ACL']}.entity_table = '{$t['Group']}'
|
||
|
AND {$t['ACL']}.entity_id =
|
||
|
{$t['GroupContact']}.group_id)
|
||
|
|
||
|
WHERE ($where)
|
||
|
AND {$t['GroupContact']}.contact_id = $contact_id
|
||
|
AND {$t['GroupContact']}.status = 'Added')";
|
||
|
|
||
|
/* Query for permissions granted through an ACL group to a Contact
|
||
|
* group */
|
||
|
|
||
|
$query[] = "SELECT {$t['ACL']}.*, 0 as override
|
||
|
FROM {$t['ACL']}
|
||
|
|
||
|
INNER JOIN {$t['ACLEntityRole']}
|
||
|
ON ({$t['ACL']}.entity_table = '{$t['ACLRole']}'
|
||
|
AND {$t['ACL']}.entity_id =
|
||
|
{$t['ACLEntityRole']}.acl_role_id)
|
||
|
|
||
|
INNER JOIN {$t['ACLRole']}
|
||
|
ON {$t['ACL']}.entity_id = {$t['ACLRole']}.id
|
||
|
|
||
|
INNER JOIN {$t['GroupContact']}
|
||
|
ON ({$t['ACLEntityRole']}.entity_table =
|
||
|
'{$t['Group']}'
|
||
|
AND {$t['ACLEntityRole']}.entity_id =
|
||
|
{$t['GroupContact']}.group_id)
|
||
|
|
||
|
WHERE ($where)
|
||
|
AND {$t['ACLRole']}.is_active = 1
|
||
|
AND {$t['GroupContact']}.contact_id = $contact_id
|
||
|
AND {$t['GroupContact']}.status = 'Added'";
|
||
|
|
||
|
$union = '(' . implode(') UNION DISTINCT (', $query) . ')';
|
||
|
|
||
|
$dao->query($union);
|
||
|
|
||
|
$allow = array(0);
|
||
|
$deny = array(0);
|
||
|
$override = array();
|
||
|
|
||
|
while ($dao->fetch()) {
|
||
|
/* Instant bypass for the following cases:
|
||
|
* 1) the rule governs all tables
|
||
|
* 2) the rule governs all objects in the table in question
|
||
|
* 3) the rule governs the specific object we want
|
||
|
*/
|
||
|
|
||
|
if (empty($dao->object_table) ||
|
||
|
($dao->object_table == $object_table
|
||
|
&& (empty($dao->object_id)
|
||
|
|| $dao->object_id == $object_id
|
||
|
)
|
||
|
)
|
||
|
) {
|
||
|
$clause = 1;
|
||
|
}
|
||
|
else {
|
||
|
/* Otherwise try to generate a clause for this rule */
|
||
|
|
||
|
$clause = self::getClause(
|
||
|
$dao->object_table, $dao->object_id, $tables
|
||
|
);
|
||
|
|
||
|
/* If the clause returned is null, then the rule is a blanket
|
||
|
* (id is null) on a table other than the one we're interested
|
||
|
* in. So skip it. */
|
||
|
|
||
|
if (empty($clause)) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Now we figure out if this is an allow or deny rule, and possibly
|
||
|
* a contact-level override */
|
||
|
|
||
|
if ($dao->deny) {
|
||
|
$deny[] = $clause;
|
||
|
}
|
||
|
else {
|
||
|
$allow[] = $clause;
|
||
|
|
||
|
if ($dao->override) {
|
||
|
$override[] = $clause;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$allows = '(' . implode(' OR ', $allow) . ')';
|
||
|
$denies = '(' . implode(' OR ', $deny) . ')';
|
||
|
if (!empty($override)) {
|
||
|
$denies = '(NOT (' . implode(' OR ', $override) . ") AND $denies)";
|
||
|
}
|
||
|
|
||
|
return "($allows AND NOT $denies)";
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Given a table and id pair, return the filter clause
|
||
|
*
|
||
|
* @param string $table
|
||
|
* The table owning the object.
|
||
|
* @param int $id
|
||
|
* The ID of the object.
|
||
|
* @param array $tables
|
||
|
* Tables that will be needed in the FROM.
|
||
|
*
|
||
|
* @return string|null
|
||
|
* WHERE-style clause to filter results,
|
||
|
* or null if $table or $id is null
|
||
|
*/
|
||
|
public static function getClause($table, $id, &$tables) {
|
||
|
$table = CRM_Utils_Type::escape($table, 'String');
|
||
|
$id = CRM_Utils_Type::escape($id, 'Integer');
|
||
|
$whereTables = array();
|
||
|
|
||
|
$ssTable = CRM_Contact_BAO_SavedSearch::getTableName();
|
||
|
|
||
|
if (empty($table)) {
|
||
|
return NULL;
|
||
|
}
|
||
|
elseif ($table == $ssTable) {
|
||
|
return CRM_Contact_BAO_SavedSearch::whereClause($id, $tables, $whereTables);
|
||
|
}
|
||
|
elseif (!empty($id)) {
|
||
|
$tables[$table] = TRUE;
|
||
|
return "$table.id = $id";
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Construct an associative array of an ACL rule's properties
|
||
|
*
|
||
|
* @param string $format
|
||
|
* Sprintf format for array.
|
||
|
* @param bool $hideEmpty
|
||
|
* Only return elements that have a value set.
|
||
|
*
|
||
|
* @return array
|
||
|
* Assoc. array of the ACL rule's properties
|
||
|
*/
|
||
|
public function toArray($format = '%s', $hideEmpty = FALSE) {
|
||
|
$result = array();
|
||
|
|
||
|
if (!self::$_fieldKeys) {
|
||
|
$fields = CRM_ACL_DAO_ACL::fields();
|
||
|
self::$_fieldKeys = array_keys($fields);
|
||
|
}
|
||
|
|
||
|
foreach (self::$_fieldKeys as $field) {
|
||
|
$result[$field] = $this->$field;
|
||
|
}
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve ACLs for a contact or group. Note that including a contact id
|
||
|
* without a group id will return those ACL rules which are granted
|
||
|
* directly to the contact, but not those granted to the contact through
|
||
|
* any/all of his group memberships.
|
||
|
*
|
||
|
* @param int $contact_id
|
||
|
* ID of a contact to search for.
|
||
|
* @param int $group_id
|
||
|
* ID of a group to search for.
|
||
|
* @param bool $aclRoles
|
||
|
* Should we include ACL Roles.
|
||
|
*
|
||
|
* @return array
|
||
|
* Array of assoc. arrays of ACL rules
|
||
|
*/
|
||
|
public static function &getACLs($contact_id = NULL, $group_id = NULL, $aclRoles = FALSE) {
|
||
|
$results = array();
|
||
|
|
||
|
if (empty($contact_id)) {
|
||
|
return $results;
|
||
|
}
|
||
|
|
||
|
$contact_id = CRM_Utils_Type::escape($contact_id, 'Integer');
|
||
|
if ($group_id) {
|
||
|
$group_id = CRM_Utils_Type::escape($group_id, 'Integer');
|
||
|
}
|
||
|
|
||
|
$rule = new CRM_ACL_BAO_ACL();
|
||
|
|
||
|
$acl = self::getTableName();
|
||
|
$contact = CRM_Contact_BAO_Contact::getTableName();
|
||
|
$c2g = CRM_Contact_BAO_GroupContact::getTableName();
|
||
|
$group = CRM_Contact_BAO_Group::getTableName();
|
||
|
|
||
|
$query = " SELECT $acl.*
|
||
|
FROM $acl ";
|
||
|
|
||
|
if (!empty($group_id)) {
|
||
|
$query .= " INNER JOIN $c2g
|
||
|
ON $acl.entity_id = $c2g.group_id
|
||
|
WHERE $acl.entity_table = '$group'
|
||
|
AND $acl.is_active = 1
|
||
|
AND $c2g.group_id = $group_id";
|
||
|
|
||
|
if (!empty($contact_id)) {
|
||
|
$query .= " AND $c2g.contact_id = $contact_id
|
||
|
AND $c2g.status = 'Added'";
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (!empty($contact_id)) {
|
||
|
$query .= " WHERE $acl.entity_table = '$contact'
|
||
|
AND $acl.entity_id = $contact_id";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$rule->query($query);
|
||
|
|
||
|
while ($rule->fetch()) {
|
||
|
$results[$rule->id] = $rule->toArray();
|
||
|
}
|
||
|
|
||
|
if ($aclRoles) {
|
||
|
$results += self::getACLRoles($contact_id, $group_id);
|
||
|
}
|
||
|
|
||
|
return $results;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get all of the ACLs through ACL groups.
|
||
|
*
|
||
|
* @param int $contact_id
|
||
|
* ID of a contact to search for.
|
||
|
* @param int $group_id
|
||
|
* ID of a group to search for.
|
||
|
*
|
||
|
* @return array
|
||
|
* Array of assoc. arrays of ACL rules
|
||
|
*/
|
||
|
public static function &getACLRoles($contact_id = NULL, $group_id = NULL) {
|
||
|
$contact_id = CRM_Utils_Type::escape($contact_id, 'Integer');
|
||
|
if ($group_id) {
|
||
|
$group_id = CRM_Utils_Type::escape($group_id, 'Integer');
|
||
|
}
|
||
|
|
||
|
$rule = new CRM_ACL_BAO_ACL();
|
||
|
|
||
|
$acl = self::getTableName();
|
||
|
$aclRole = 'civicrm_acl_role';
|
||
|
$aclRoleJoin = CRM_ACL_DAO_EntityRole::getTableName();
|
||
|
$contact = CRM_Contact_BAO_Contact::getTableName();
|
||
|
$c2g = CRM_Contact_BAO_GroupContact::getTableName();
|
||
|
$group = CRM_Contact_BAO_Group::getTableName();
|
||
|
|
||
|
$query = " SELECT $acl.*
|
||
|
FROM $acl
|
||
|
INNER JOIN civicrm_option_group og
|
||
|
ON og.name = 'acl_role'
|
||
|
INNER JOIN civicrm_option_value ov
|
||
|
ON $acl.entity_table = '$aclRole'
|
||
|
AND ov.option_group_id = og.id
|
||
|
AND $acl.entity_id = ov.value";
|
||
|
|
||
|
if (!empty($group_id)) {
|
||
|
$query .= " INNER JOIN $c2g
|
||
|
ON $acl.entity_id = $c2g.group_id
|
||
|
WHERE $acl.entity_table = '$group'
|
||
|
AND $acl.is_active = 1
|
||
|
AND $c2g.group_id = $group_id";
|
||
|
|
||
|
if (!empty($contact_id)) {
|
||
|
$query .= " AND $c2g.contact_id = $contact_id
|
||
|
AND $c2g.status = 'Added'";
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (!empty($contact_id)) {
|
||
|
$query .= " WHERE $acl.entity_table = '$contact'
|
||
|
AND $acl.is_active = 1
|
||
|
AND $acl.entity_id = $contact_id";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$results = array();
|
||
|
|
||
|
$rule->query($query);
|
||
|
|
||
|
while ($rule->fetch()) {
|
||
|
$results[$rule->id] = $rule->toArray();
|
||
|
}
|
||
|
|
||
|
return $results;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get all ACLs granted to a contact through all group memberships.
|
||
|
*
|
||
|
* @param int $contact_id
|
||
|
* The contact's ID.
|
||
|
* @param bool $aclRoles
|
||
|
* Include ACL Roles?.
|
||
|
*
|
||
|
* @return array
|
||
|
* Assoc array of ACL rules
|
||
|
*/
|
||
|
public static function &getGroupACLs($contact_id, $aclRoles = FALSE) {
|
||
|
$contact_id = CRM_Utils_Type::escape($contact_id, 'Integer');
|
||
|
|
||
|
$rule = new CRM_ACL_BAO_ACL();
|
||
|
|
||
|
$acl = self::getTableName();
|
||
|
$c2g = CRM_Contact_BAO_GroupContact::getTableName();
|
||
|
$group = CRM_Contact_BAO_Group::getTableName();
|
||
|
$results = array();
|
||
|
|
||
|
if ($contact_id) {
|
||
|
$query = "
|
||
|
SELECT $acl.*
|
||
|
FROM $acl
|
||
|
INNER JOIN $c2g
|
||
|
ON $acl.entity_id = $c2g.group_id
|
||
|
WHERE $acl.entity_table = '$group'
|
||
|
AND $c2g.contact_id = $contact_id
|
||
|
AND $c2g.status = 'Added'";
|
||
|
|
||
|
$rule->query($query);
|
||
|
|
||
|
while ($rule->fetch()) {
|
||
|
$results[$rule->id] = $rule->toArray();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($aclRoles) {
|
||
|
$results += self::getGroupACLRoles($contact_id);
|
||
|
}
|
||
|
|
||
|
return $results;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get all of the ACLs for a contact through ACL groups owned by Contact.
|
||
|
* groups.
|
||
|
*
|
||
|
* @param int $contact_id
|
||
|
* ID of a contact to search for.
|
||
|
*
|
||
|
* @return array
|
||
|
* Array of assoc. arrays of ACL rules
|
||
|
*/
|
||
|
public static function &getGroupACLRoles($contact_id) {
|
||
|
$contact_id = CRM_Utils_Type::escape($contact_id, 'Integer');
|
||
|
|
||
|
$rule = new CRM_ACL_BAO_ACL();
|
||
|
|
||
|
$acl = self::getTableName();
|
||
|
$aclRole = 'civicrm_acl_role';
|
||
|
|
||
|
$aclER = CRM_ACL_DAO_EntityRole::getTableName();
|
||
|
$c2g = CRM_Contact_BAO_GroupContact::getTableName();
|
||
|
$group = CRM_Contact_BAO_Group::getTableName();
|
||
|
|
||
|
$query = " SELECT $acl.*
|
||
|
FROM $acl
|
||
|
INNER JOIN civicrm_option_group og
|
||
|
ON og.name = 'acl_role'
|
||
|
INNER JOIN civicrm_option_value ov
|
||
|
ON $acl.entity_table = '$aclRole'
|
||
|
AND ov.option_group_id = og.id
|
||
|
AND $acl.entity_id = ov.value
|
||
|
AND ov.is_active = 1
|
||
|
INNER JOIN $aclER
|
||
|
ON $aclER.acl_role_id = $acl.entity_id
|
||
|
AND $aclER.is_active = 1
|
||
|
INNER JOIN $c2g
|
||
|
ON $aclER.entity_id = $c2g.group_id
|
||
|
AND $aclER.entity_table = 'civicrm_group'
|
||
|
WHERE $acl.entity_table = '$aclRole'
|
||
|
AND $acl.is_active = 1
|
||
|
AND $c2g.contact_id = $contact_id
|
||
|
AND $c2g.status = 'Added'";
|
||
|
|
||
|
$results = array();
|
||
|
|
||
|
$rule->query($query);
|
||
|
|
||
|
while ($rule->fetch()) {
|
||
|
$results[$rule->id] = $rule->toArray();
|
||
|
}
|
||
|
|
||
|
// also get all acls for "Any Role" case
|
||
|
// and authenticated User Role if present
|
||
|
$roles = "0";
|
||
|
$session = CRM_Core_Session::singleton();
|
||
|
if ($session->get('ufID') > 0) {
|
||
|
$roles .= ",2";
|
||
|
}
|
||
|
|
||
|
$query = "
|
||
|
SELECT $acl.*
|
||
|
FROM $acl
|
||
|
WHERE $acl.entity_id IN ( $roles )
|
||
|
AND $acl.entity_table = 'civicrm_acl_role'
|
||
|
";
|
||
|
|
||
|
$rule->query($query);
|
||
|
while ($rule->fetch()) {
|
||
|
$results[$rule->id] = $rule->toArray();
|
||
|
}
|
||
|
|
||
|
return $results;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get all ACLs owned by a given contact, including domain and group-level.
|
||
|
*
|
||
|
* @param int $contact_id
|
||
|
* The contact ID.
|
||
|
*
|
||
|
* @return array
|
||
|
* Assoc array of ACL rules
|
||
|
*/
|
||
|
public static function &getAllByContact($contact_id) {
|
||
|
$result = array();
|
||
|
|
||
|
/* First, the contact-specific ACLs, including ACL Roles */
|
||
|
$result += self::getACLs($contact_id, NULL, TRUE);
|
||
|
|
||
|
/* Then, all ACLs granted through group membership */
|
||
|
$result += self::getGroupACLs($contact_id, TRUE);
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param array $params
|
||
|
*
|
||
|
* @return CRM_ACL_DAO_ACL
|
||
|
*/
|
||
|
public static function create(&$params) {
|
||
|
$dao = new CRM_ACL_DAO_ACL();
|
||
|
$dao->copyValues($params);
|
||
|
$dao->save();
|
||
|
return $dao;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param array $params
|
||
|
* @param $defaults
|
||
|
*/
|
||
|
public static function retrieve(&$params, &$defaults) {
|
||
|
CRM_Core_DAO::commonRetrieve('CRM_ACL_DAO_ACL', $params, $defaults);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update the is_active flag in the db.
|
||
|
*
|
||
|
* @param int $id
|
||
|
* Id of the database record.
|
||
|
* @param bool $is_active
|
||
|
* Value we want to set the is_active field.
|
||
|
*
|
||
|
* @return Object
|
||
|
* DAO object on success, null otherwise
|
||
|
*/
|
||
|
public static function setIsActive($id, $is_active) {
|
||
|
// note this also resets any ACL cache
|
||
|
CRM_Core_BAO_Cache::deleteGroup('contact fields');
|
||
|
|
||
|
return CRM_Core_DAO::setFieldValue('CRM_ACL_DAO_ACL', $id, 'is_active', $is_active);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $str
|
||
|
* @param int $contactID
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
public static function check($str, $contactID) {
|
||
|
|
||
|
$acls = CRM_ACL_BAO_Cache::build($contactID);
|
||
|
|
||
|
$aclKeys = array_keys($acls);
|
||
|
$aclKeys = implode(',', $aclKeys);
|
||
|
|
||
|
if (empty($aclKeys)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
$query = "
|
||
|
SELECT count( a.id )
|
||
|
FROM civicrm_acl_cache c, civicrm_acl a
|
||
|
WHERE c.acl_id = a.id
|
||
|
AND a.is_active = 1
|
||
|
AND a.object_table = %1
|
||
|
AND a.id IN ( $aclKeys )
|
||
|
";
|
||
|
$params = array(1 => array($str, 'String'));
|
||
|
|
||
|
$count = CRM_Core_DAO::singleValueQuery($query, $params);
|
||
|
return ($count) ? TRUE : FALSE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $type
|
||
|
* @param $tables
|
||
|
* @param $whereTables
|
||
|
* @param int $contactID
|
||
|
*
|
||
|
* @return null|string
|
||
|
*/
|
||
|
public static function whereClause($type, &$tables, &$whereTables, $contactID = NULL) {
|
||
|
$acls = CRM_ACL_BAO_Cache::build($contactID);
|
||
|
|
||
|
$whereClause = NULL;
|
||
|
$clauses = array();
|
||
|
|
||
|
if (!empty($acls)) {
|
||
|
$aclKeys = array_keys($acls);
|
||
|
$aclKeys = implode(',', $aclKeys);
|
||
|
|
||
|
$query = "
|
||
|
SELECT a.operation, a.object_id
|
||
|
FROM civicrm_acl_cache c, civicrm_acl a
|
||
|
WHERE c.acl_id = a.id
|
||
|
AND a.is_active = 1
|
||
|
AND a.object_table = 'civicrm_saved_search'
|
||
|
AND a.id IN ( $aclKeys )
|
||
|
ORDER BY a.object_id
|
||
|
";
|
||
|
|
||
|
$dao = CRM_Core_DAO::executeQuery($query);
|
||
|
|
||
|
// do an or of all the where clauses u see
|
||
|
$ids = array();
|
||
|
while ($dao->fetch()) {
|
||
|
// make sure operation matches the type TODO
|
||
|
if (self::matchType($type, $dao->operation)) {
|
||
|
if (!$dao->object_id) {
|
||
|
$ids = array();
|
||
|
$whereClause = ' ( 1 ) ';
|
||
|
break;
|
||
|
}
|
||
|
$ids[] = $dao->object_id;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!empty($ids)) {
|
||
|
$ids = implode(',', $ids);
|
||
|
$query = "
|
||
|
SELECT g.*
|
||
|
FROM civicrm_group g
|
||
|
WHERE g.id IN ( $ids )
|
||
|
AND g.is_active = 1
|
||
|
";
|
||
|
$dao = CRM_Core_DAO::executeQuery($query);
|
||
|
$staticGroupIDs = array();
|
||
|
$cachedGroupIDs = array();
|
||
|
while ($dao->fetch()) {
|
||
|
// currently operation is restrcited to VIEW/EDIT
|
||
|
if ($dao->where_clause) {
|
||
|
if ($dao->select_tables) {
|
||
|
$tmpTables = array();
|
||
|
foreach (unserialize($dao->select_tables) as $tmpName => $tmpInfo) {
|
||
|
if ($tmpName == '`civicrm_group_contact-' . $dao->id . '`') {
|
||
|
$tmpName = '`civicrm_group_contact-ACL`';
|
||
|
$tmpInfo = str_replace('civicrm_group_contact-' . $dao->id, 'civicrm_group_contact-ACL', $tmpInfo);
|
||
|
}
|
||
|
elseif ($tmpName == '`civicrm_group_contact_cache_' . $dao->id . '`') {
|
||
|
$tmpName = '`civicrm_group_contact_cache-ACL`';
|
||
|
$tmpInfo = str_replace('civicrm_group_contact_cache_' . $dao->id, 'civicrm_group_contact_cache-ACL', $tmpInfo);
|
||
|
}
|
||
|
$tmpTables[$tmpName] = $tmpInfo;
|
||
|
}
|
||
|
$tables = array_merge($tables,
|
||
|
$tmpTables
|
||
|
);
|
||
|
}
|
||
|
if ($dao->where_tables) {
|
||
|
$tmpTables = array();
|
||
|
foreach (unserialize($dao->where_tables) as $tmpName => $tmpInfo) {
|
||
|
if ($tmpName == '`civicrm_group_contact-' . $dao->id . '`') {
|
||
|
$tmpName = '`civicrm_group_contact-ACL`';
|
||
|
$tmpInfo = str_replace('civicrm_group_contact-' . $dao->id, 'civicrm_group_contact-ACL', $tmpInfo);
|
||
|
$staticGroupIDs[] = $dao->id;
|
||
|
}
|
||
|
elseif ($tmpName == '`civicrm_group_contact_cache_' . $dao->id . '`') {
|
||
|
$tmpName = '`civicrm_group_contact_cache-ACL`';
|
||
|
$tmpInfo = str_replace('civicrm_group_contact_cache_' . $dao->id, 'civicrm_group_contact_cache-ACL', $tmpInfo);
|
||
|
$cachedGroupIDs[] = $dao->id;
|
||
|
}
|
||
|
$tmpTables[$tmpName] = $tmpInfo;
|
||
|
}
|
||
|
$whereTables = array_merge($whereTables, $tmpTables);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (($dao->saved_search_id || $dao->children || $dao->parents) &&
|
||
|
$dao->cache_date == NULL
|
||
|
) {
|
||
|
CRM_Contact_BAO_GroupContactCache::load($dao);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($staticGroupIDs) {
|
||
|
$clauses[] = '( `civicrm_group_contact-ACL`.group_id IN (' . implode(', ', $staticGroupIDs) . ') AND `civicrm_group_contact-ACL`.status IN ("Added") )';
|
||
|
}
|
||
|
|
||
|
if ($cachedGroupIDs) {
|
||
|
$clauses[] = '`civicrm_group_contact_cache-ACL`.group_id IN (' . implode(', ', $cachedGroupIDs) . ')';
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!empty($clauses)) {
|
||
|
$whereClause = ' ( ' . implode(' OR ', $clauses) . ' ) ';
|
||
|
}
|
||
|
|
||
|
// call the hook to get additional whereClauses
|
||
|
CRM_Utils_Hook::aclWhereClause($type, $tables, $whereTables, $contactID, $whereClause);
|
||
|
|
||
|
if (empty($whereClause)) {
|
||
|
$whereClause = ' ( 0 ) ';
|
||
|
}
|
||
|
|
||
|
return $whereClause;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param int $type
|
||
|
* @param int $contactID
|
||
|
* @param string $tableName
|
||
|
* @param null $allGroups
|
||
|
* @param null $includedGroups
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public static function group(
|
||
|
$type,
|
||
|
$contactID = NULL,
|
||
|
$tableName = 'civicrm_saved_search',
|
||
|
$allGroups = NULL,
|
||
|
$includedGroups = NULL
|
||
|
) {
|
||
|
$userCacheKey = "{$contactID}_{$type}_{$tableName}_" . CRM_Core_Config::domainID() . '_' . md5(implode(',', array_merge((array) $allGroups, (array) $includedGroups)));
|
||
|
if (empty(Civi::$statics[__CLASS__]['permissioned_groups'])) {
|
||
|
Civi::$statics[__CLASS__]['permissioned_groups'] = array();
|
||
|
}
|
||
|
if (!empty(Civi::$statics[__CLASS__]['permissioned_groups'][$userCacheKey])) {
|
||
|
return Civi::$statics[__CLASS__]['permissioned_groups'][$userCacheKey];
|
||
|
}
|
||
|
|
||
|
$acls = CRM_ACL_BAO_Cache::build($contactID);
|
||
|
|
||
|
$ids = array();
|
||
|
if (!empty($acls)) {
|
||
|
$aclKeys = array_keys($acls);
|
||
|
$aclKeys = implode(',', $aclKeys);
|
||
|
|
||
|
$cacheKey = "$tableName-$aclKeys";
|
||
|
$cache = CRM_Utils_Cache::singleton();
|
||
|
$ids = $cache->get($cacheKey);
|
||
|
if (!$ids) {
|
||
|
$query = "
|
||
|
SELECT a.operation, a.object_id
|
||
|
FROM civicrm_acl_cache c, civicrm_acl a
|
||
|
WHERE c.acl_id = a.id
|
||
|
AND a.is_active = 1
|
||
|
AND a.object_table = %1
|
||
|
AND a.id IN ( $aclKeys )
|
||
|
GROUP BY a.operation,a.object_id
|
||
|
ORDER BY a.object_id
|
||
|
";
|
||
|
$params = array(1 => array($tableName, 'String'));
|
||
|
$dao = CRM_Core_DAO::executeQuery($query, $params);
|
||
|
while ($dao->fetch()) {
|
||
|
if ($dao->object_id) {
|
||
|
if (self::matchType($type, $dao->operation)) {
|
||
|
$ids[] = $dao->object_id;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// this user has got the permission for all objects of this type
|
||
|
// check if the type matches
|
||
|
if (self::matchType($type, $dao->operation)) {
|
||
|
foreach ($allGroups as $id => $dontCare) {
|
||
|
$ids[] = $id;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
$cache->set($cacheKey, $ids);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (empty($ids) && !empty($includedGroups) &&
|
||
|
is_array($includedGroups)
|
||
|
) {
|
||
|
$ids = $includedGroups;
|
||
|
}
|
||
|
if ($contactID) {
|
||
|
$groupWhere = '';
|
||
|
if (!empty($allGroups)) {
|
||
|
$groupWhere = " AND id IN (" . implode(',', array_keys($allGroups)) . ")";
|
||
|
}
|
||
|
// Contacts create hidden groups from search results. They should be able to retrieve their own.
|
||
|
$ownHiddenGroupsList = CRM_Core_DAO::singleValueQuery("
|
||
|
SELECT GROUP_CONCAT(id) FROM civicrm_group WHERE is_hidden =1 AND created_id = $contactID
|
||
|
$groupWhere
|
||
|
");
|
||
|
if ($ownHiddenGroupsList) {
|
||
|
$ownHiddenGroups = explode(',', $ownHiddenGroupsList);
|
||
|
$ids = array_merge((array) $ids, $ownHiddenGroups);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
CRM_Utils_Hook::aclGroup($type, $contactID, $tableName, $allGroups, $ids);
|
||
|
Civi::$statics[__CLASS__]['permissioned_groups'][$userCacheKey] = $ids;
|
||
|
return $ids;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param int $type
|
||
|
* @param $operation
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
public static function matchType($type, $operation) {
|
||
|
$typeCheck = FALSE;
|
||
|
switch ($operation) {
|
||
|
case 'All':
|
||
|
$typeCheck = TRUE;
|
||
|
break;
|
||
|
|
||
|
case 'View':
|
||
|
if ($type == CRM_ACL_API::VIEW) {
|
||
|
$typeCheck = TRUE;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'Edit':
|
||
|
if ($type == CRM_ACL_API::VIEW || $type == CRM_ACL_API::EDIT) {
|
||
|
$typeCheck = TRUE;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'Create':
|
||
|
if ($type == CRM_ACL_API::CREATE) {
|
||
|
$typeCheck = TRUE;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'Delete':
|
||
|
if ($type == CRM_ACL_API::DELETE) {
|
||
|
$typeCheck = TRUE;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'Search':
|
||
|
if ($type == CRM_ACL_API::SEARCH) {
|
||
|
$typeCheck = TRUE;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
return $typeCheck;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Delete ACL records.
|
||
|
*
|
||
|
* @param int $aclId
|
||
|
* ID of the ACL record to be deleted.
|
||
|
*
|
||
|
*/
|
||
|
public static function del($aclId) {
|
||
|
// delete all entries from the acl cache
|
||
|
CRM_ACL_BAO_Cache::resetCache();
|
||
|
|
||
|
$acl = new CRM_ACL_DAO_ACL();
|
||
|
$acl->id = $aclId;
|
||
|
$acl->delete();
|
||
|
}
|
||
|
|
||
|
}
|