X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=Civi%2FAPI%2FSubscriber%2FDynamicFKAuthorization.php;h=1d7a0dd3835826bd1afcc70b3966fa9760d8fc2b;hb=HEAD;hp=4aeef4c314cd40b88525b394b59dd900e5be4520;hpb=ff7da3a6508d0a7059c6527216920b2efb896b93;p=civicrm-core.git diff --git a/Civi/API/Subscriber/DynamicFKAuthorization.php b/Civi/API/Subscriber/DynamicFKAuthorization.php index 4aeef4c314..1d7a0dd383 100644 --- a/Civi/API/Subscriber/DynamicFKAuthorization.php +++ b/Civi/API/Subscriber/DynamicFKAuthorization.php @@ -1,9 +1,9 @@ $, table_name => $, extends => $) + */ + protected $lookupCustomFieldCache; + /** * @var array list of related tables for which FKs are allowed */ @@ -104,14 +122,17 @@ class DynamicFKAuthorization implements EventSubscriberInterface { * "get", "delete"). * @param string $lookupDelegateSql * See docblock in DynamicFKAuthorization::$lookupDelegateSql. + * @param string $lookupCustomFieldSql + * See docblock in DynamicFKAuthorization::$lookupCustomFieldSql. * @param array|NULL $allowedDelegates * e.g. "civicrm_mailing","civicrm_activity"; NULL to allow any. */ - public function __construct($kernel, $entityName, $actions, $lookupDelegateSql, $allowedDelegates = NULL) { + public function __construct($kernel, $entityName, $actions, $lookupDelegateSql, $lookupCustomFieldSql, $allowedDelegates = NULL) { $this->kernel = $kernel; - $this->entityName = $entityName; + $this->entityName = \CRM_Utils_String::convertStringToCamel($entityName); $this->actions = $actions; $this->lookupDelegateSql = $lookupDelegateSql; + $this->lookupCustomFieldSql = $lookupCustomFieldSql; $this->allowedDelegates = $allowedDelegates; } @@ -123,7 +144,16 @@ class DynamicFKAuthorization implements EventSubscriberInterface { */ public function onApiAuthorize(\Civi\API\Event\AuthorizeEvent $event) { $apiRequest = $event->getApiRequest(); - if ($apiRequest['version'] == 3 && $apiRequest['entity'] == $this->entityName && in_array(strtolower($apiRequest['action']), $this->actions)) { + if ($apiRequest['version'] == 3 && \CRM_Utils_String::convertStringToCamel($apiRequest['entity']) == $this->entityName && in_array(strtolower($apiRequest['action']), $this->actions)) { + if (isset($apiRequest['params']['field_name'])) { + $fldIdx = \CRM_Utils_Array::index(array('field_name'), $this->getCustomFields()); + if (empty($fldIdx[$apiRequest['params']['field_name']])) { + throw new \Exception("Failed to map custom field to entity table"); + } + $apiRequest['params']['entity_table'] = $fldIdx[$apiRequest['params']['field_name']]['entity_table']; + unset($apiRequest['params']['field_name']); + } + if (/*!$isTrusted */ empty($apiRequest['params']['id']) && empty($apiRequest['params']['entity_table']) ) { @@ -177,20 +207,40 @@ class DynamicFKAuthorization implements EventSubscriberInterface { public function authorizeDelegate($action, $entityTable, $entityId, $apiRequest) { $entity = $this->getDelegatedEntityName($entityTable); if (!$entity) { - throw new \API_Exception("Failed to run permission check: Unrecognized target entity ($entityTable)"); + throw new \API_Exception("Failed to run permission check: Unrecognized target entity table ($entityTable)"); + } + if (!$entityId) { + throw new \Civi\API\Exception\UnauthorizedException("Authorization failed on ($entity): Missing entity_id"); } if ($this->isTrusted($apiRequest)) { return; } - $params = array('check_permissions' => 1); - if ($entityId) { - $params['id'] = $entityId; - } + /** + * @var \Exception $exception + */ + $exception = NULL; + $self = $this; + \CRM_Core_Transaction::create(TRUE)->run(function($tx) use ($entity, $action, $entityId, &$exception, $self) { + $tx->rollback(); // Just to be safe. + + $params = array( + 'version' => 3, + 'check_permissions' => 1, + 'id' => $entityId, + ); + + $result = $self->kernel->run($entity, $self->getDelegatedAction($action), $params); + if ($result['is_error'] || empty($result['values'])) { + $exception = new \Civi\API\Exception\UnauthorizedException("Authorization failed on ($entity,$entityId)", array( + 'cause' => $result, + )); + } + }); - if (!$this->kernel->runAuthorize($entity, $this->getDelegatedAction($action), $params)) { - throw new \Civi\API\Exception\UnauthorizedException("Authorization failed on ($entity,$entityId)"); + if ($exception) { + throw $exception; } } @@ -210,6 +260,7 @@ class DynamicFKAuthorization implements EventSubscriberInterface { */ public function preventReassignment($fileId, $entityTable, $entityId, $apiRequest) { if (strtolower($apiRequest['action']) == 'create' && $fileId && !$this->isTrusted($apiRequest)) { + // TODO: no change in field_name? if (isset($apiRequest['params']['entity_table']) && $entityTable != $apiRequest['params']['entity_table']) { throw new \API_Exception("Cannot modify entity_table"); } @@ -267,13 +318,25 @@ class DynamicFKAuthorization implements EventSubscriberInterface { * e.g. file ID. * @return array * (0 => bool $isValid, 1 => string $entityTable, 2 => int $entityId) + * @throws \Exception */ public function getDelegate($id) { $query = \CRM_Core_DAO::executeQuery($this->lookupDelegateSql, array( 1 => array($id, 'Positive'), )); if ($query->fetch()) { - return array($query->is_valid, $query->entity_table, $query->entity_id); + if (!preg_match('/^civicrm_value_/', $query->entity_table)) { + // A normal attachment directly on its entity. + return array($query->is_valid, $query->entity_table, $query->entity_id); + } + + // Ex: Translate custom-field table ("civicrm_value_foo_4") to + // entity table ("civicrm_activity"). + $tblIdx = \CRM_Utils_Array::index(array('table_name'), $this->getCustomFields()); + if (isset($tblIdx[$query->entity_table])) { + return array($query->is_valid, $tblIdx[$query->entity_table]['entity_table'], $query->entity_id); + } + throw new \Exception('Failed to lookup entity table for custom field.'); } else { return array(FALSE, NULL, NULL); @@ -290,4 +353,22 @@ class DynamicFKAuthorization implements EventSubscriberInterface { return empty($apiRequest['params']['check_permissions']) or $apiRequest['params']['check_permissions'] == FALSE; } + /** + * @return array + * Each item has keys 'field_name', 'table_name', 'extends', 'entity_table' + */ + public function getCustomFields() { + $query = \CRM_Core_DAO::executeQuery($this->lookupCustomFieldSql); + $rows = array(); + while ($query->fetch()) { + $rows[] = array( + 'field_name' => $query->field_name, + 'table_name' => $query->table_name, + 'extends' => $query->extends, + 'entity_table' => \CRM_Core_BAO_CustomGroup::getTableNameByEntityName($query->extends), + ); + } + return $rows; + } + }