Commit 6a488035 authored by totten's avatar totten
Browse files

Import from SVN (r45945, r596)

parent 39330a6d
<?php
// $Id$
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.3 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2013 |
+--------------------------------------------------------------------+
| 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-2013
* $Id$
*
*/
class CRM_ACL_API {
/**
* The various type of permissions
*
* @var int
*/
CONST EDIT = 1;
CONST VIEW = 2;
CONST DELETE = 3;
CONST CREATE = 4;
CONST SEARCH = 5;
CONST ALL = 6;
/**
* given a permission string, check for access requirements
*
* @param string $str the permission to check
* @param int $contactID the contactID for whom the check is made
*
* @return boolean true if yes, else false
* @static
* @access public
*/
static function check($str, $contactID = NULL) {
if ($contactID == NULL) {
$session = CRM_Core_Session::singleton();
$contactID = $session->get('userID');
}
if (!$contactID) {
// anonymous user
$contactID = 0;
}
return CRM_ACL_BAO_ACL::check($str, $contactID);
}
/**
* Get the permissioned where clause for the user
*
* @param int $type the type of permission needed
* @param array $tables (reference ) add the tables that are needed for the select clause
* @param array $whereTables (reference ) add the tables that are needed for the where clause
* @param int $contactID the contactID for whom the check is made
* @param bool $onlyDeleted whether to include only deleted contacts
* @param bool $skipDeleteClause don't add delete clause if this is true,
* this means it is handled by generating query
*
* @return string the group where clause for this user
* @access public
*/
public static function whereClause($type,
&$tables,
&$whereTables,
$contactID = NULL,
$onlyDeleted = FALSE,
$skipDeleteClause = FALSE
) {
// the default value which is valid for rhe final AND
$deleteClause = ' ( 1 ) ';
if (!$skipDeleteClause) {
if (CRM_Core_Permission::check('access deleted contacts') and $onlyDeleted) {
$deleteClause = '(contact_a.is_deleted)';
}
else {
// CRM-6181
$deleteClause = '(contact_a.is_deleted = 0)';
}
}
// first see if the contact has edit / view all contacts
if (CRM_Core_Permission::check('edit all contacts') ||
($type == self::VIEW &&
CRM_Core_Permission::check('view all contacts')
)
) {
return $skipDeleteClause ? ' ( 1 ) ' : $deleteClause;
}
if ($contactID == NULL) {
$session = CRM_Core_Session::singleton();
$contactID = $session->get('userID');
}
if (!$contactID) {
// anonymous user
$contactID = 0;
}
return implode(' AND ',
array(
CRM_ACL_BAO_ACL::whereClause($type,
$tables,
$whereTables,
$contactID
),
$deleteClause,
)
);
}
/**
* get all the groups the user has access to for the given operation
*
* @param int $type the type of permission needed
* @param int $contactID the contactID for whom the check is made
*
* @return array the ids of the groups for which the user has permissions
* @access public
*/
public static function group(
$type,
$contactID = NULL,
$tableName = 'civicrm_saved_search',
$allGroups = NULL,
$includedGroups = NULL
) {
if ($contactID == NULL) {
$session = CRM_Core_Session::singleton();
$contactID = $session->get('userID');
}
if (!$contactID) {
// anonymous user
$contactID = 0;
}
return CRM_ACL_BAO_ACL::group($type, $contactID, $tableName, $allGroups, $includedGroups);
}
/**
* check if the user has access to this group for operation $type
*
* @param int $type the type of permission needed
* @param int $contactID the contactID for whom the check is made
*
* @return array the ids of the groups for which the user has permissions
* @access public
*/
public static function groupPermission(
$type,
$groupID,
$contactID = NULL,
$tableName = 'civicrm_saved_search',
$allGroups = NULL,
$includedGroups = NULL
) {
static $cache = array();
if (!$contactID) {
$session = CRM_Core_Session::singleton();
$contactID = NULL;
if ($session->get('userID')) {
$contactID = $session->get('userID');
}
}
$key = "{$tableName}_{$type}_{$contactID}";
if (array_key_exists($key, $cache)) {
$groups = &$cache[$key];
}
else {
$groups = self::group($type, $contactID, $tableName, $allGroups, $includedGroups);
$cache[$key] = $groups;
}
return in_array($groupID, $groups) ? TRUE : FALSE;
}
}
<?php
// $Id$
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.3 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2013 |
+--------------------------------------------------------------------+
| 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-2013
* $Id$
*
*/
/**
* Access Control List
*/
class CRM_ACL_BAO_ACL extends CRM_ACL_DAO_ACL {
static $_entityTable = NULL;
static $_objectTable = NULL;
static $_operation = NULL;
static $_fieldKeys = NULL;
static function entityTable() {
if (!self::$_entityTable) {
self::$_entityTable = array(
'civicrm_contact' => ts('Contact'),
'civicrm_acl_role' => ts('ACL Role'),
);
}
return self::$_entityTable;
}
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('Administer'),
'civicrm_admin' => ts('Import'),
);
}
return self::$_objectTable;
}
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 ref $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 boolean $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
* @access public
* @static
*/
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(),
);
$session = CRM_Core_Session::singleton();
$contact_id = $session->get('userID');
$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 ref $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
* @access public
* @static
*/
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 sprintf format for array
* @param bool empty only return elemnts that have a value set.
*
* @return array - Assoc. array of the ACL rule's properties
* @access 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 boolean $aclRoles - Should we include ACL Roles
*
* @return array - Array of assoc. arrays of ACL rules
* @access public
* @static
*/
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'";