--- /dev/null
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.4 |
+ +--------------------------------------------------------------------+
+ | 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 |
+ +--------------------------------------------------------------------+
+*/
+
+namespace Civi\API\Subscriber;
+use Civi\API\Events;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * For any API requests that correspond to a Doctrine entity ($apiRequest['doctrineClass']), check
+ * permissions specified in Civi\API\Annotation\Permission.
+ */
+class PermissionCheck implements EventSubscriberInterface {
+ public static function getSubscribedEvents() {
+ return array(
+ Events::AUTHORIZE => array(
+ array('onApiAuthorize', Events::W_LATE),
+ ),
+ );
+ }
+
+ public function onApiAuthorize(\Civi\API\Event\AuthorizeEvent $event) {
+ $apiRequest = $event->getApiRequest();
+ if ($apiRequest['version'] < 4) {
+ // return early unless we’re told explicitly to do the permission check
+ if (empty($apiRequest['params']['check_permissions']) or $apiRequest['params']['check_permissions'] == FALSE) {
+ $event->authorize();
+ $event->stopPropagation();
+ return;
+ }
+
+ require_once 'CRM/Core/DAO/permissions.php';
+ $permissions = _civicrm_api3_permissions($apiRequest['entity'], $apiRequest['action'], $apiRequest['params']);
+
+ // $params might’ve been reset by the alterAPIPermissions() hook
+ if (isset($apiRequest['params']['check_permissions']) and $apiRequest['params']['check_permissions'] == FALSE) {
+ $event->authorize();
+ $event->stopPropagation();
+ return;
+ }
+
+ if (!\CRM_Core_Permission::check($permissions)) {
+ if (is_array($permissions)) {
+ $permissions = implode(' and ', $permissions);
+ }
+ // FIXME: Generating the exception ourselves allows for detailed error but doesn't play well with multiple authz subscribers.
+ throw new \API_Exception("API permission check failed for {$apiRequest['entity']}/{$apiRequest['action']} call; insufficient permission: require $permissions", \API_Exception::UNAUTHORIZED);
+ }
+
+ $event->authorize();
+ $event->stopPropagation();
+ }
+ }
+}
\ No newline at end of file
$dispatcher->addSubscriber(new \Civi\API\Subscriber\TransactionSubscriber());
$dispatcher->addSubscriber(new \Civi\API\Subscriber\I18nSubscriber());
$dispatcher->addSubscriber(new \Civi\API\Provider\MagicFunctionProvider());
+ $dispatcher->addSubscriber(new \Civi\API\Subscriber\PermissionCheck());
$dispatcher->addSubscriber(new \Civi\API\Subscriber\APIv3SchemaAdapter());
$dispatcher->addSubscriber(new \Civi\API\Subscriber\WrapperAdapter(array(
\CRM_Utils_API_HTMLInputCoder::singleton(),
\CRM_Utils_API_MatchOption::singleton(),
)));
$dispatcher->addSubscriber(new \Civi\API\Subscriber\XDebugSubscriber());
- $dispatcher->addListener(\Civi\API\Events::AUTHORIZE, function(\Civi\API\Event\AuthorizeEvent $event) {
- $apiRequest = $event->getApiRequest();
- // At time of writing, _civicrm_api3_api_check_permission generates an exception on failure
- _civicrm_api3_api_check_permission($apiRequest['entity'], $apiRequest['action'], $apiRequest['params']);
- $event->authorize();
- });
$kernel = new \Civi\API\Kernel($dispatcher, array());
return $kernel;
}
return TRUE;
}
-/**
- * Check permissions for a given API call.
- *
- * @param $entity string API entity being accessed
- * @param $action string API action being performed
- * @param $params array params of the API call
- * @param $throw deprecated bool whether to throw exception instead of returning false
- *
- * @throws Exception
- * @return bool whether the current API user has the permission to make the call
- */
-function _civicrm_api3_api_check_permission($entity, $action, &$params, $throw = TRUE) {
- // return early unless we’re told explicitly to do the permission check
- if (empty($params['check_permissions']) or $params['check_permissions'] == FALSE) {
- return TRUE;
- }
-
- require_once 'CRM/Core/DAO/permissions.php';
- $permissions = _civicrm_api3_permissions($entity, $action, $params);
-
- // $params might’ve been reset by the alterAPIPermissions() hook
- if (isset($params['check_permissions']) and $params['check_permissions'] == FALSE) {
- return TRUE;
- }
-
- if (!CRM_Core_Permission::check($permissions)) {
- if ($throw) {
- if(is_array($permissions)) {
- $permissions = implode(' and ', $permissions);
- }
- throw new Exception("API permission check failed for $entity/$action call; insufficient permission: require $permissions");
- }
- else {
- //@todo remove this - this is an internal api function called with $throw set to TRUE. It is only called with false
- // in tests & that should be tidied up
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
/**
* Function to do a 'standard' api get - when the api is only doing a $bao->find then use this
*
$check = array('check_permissions' => TRUE);
$config = CRM_Core_Config::singleton();
$config->userPermissionClass->permissions = array();
- $this->assertFalse(_civicrm_api3_api_check_permission('contact', 'create', $check, FALSE), 'empty permissions should not be enough');
+ $this->assertFalse($this->runPermissionCheck('contact', 'create', $check), 'empty permissions should not be enough');
$config->userPermissionClass->permissions = array('access CiviCRM');
- $this->assertFalse(_civicrm_api3_api_check_permission('contact', 'create', $check, FALSE), 'lacking permissions should not be enough');
+ $this->assertFalse($this->runPermissionCheck('contact', 'create', $check), 'lacking permissions should not be enough');
$config->userPermissionClass->permissions = array('add contacts');
- $this->assertFalse(_civicrm_api3_api_check_permission('contact', 'create', $check, FALSE), 'lacking permissions should not be enough');
+ $this->assertFalse($this->runPermissionCheck('contact', 'create', $check), 'lacking permissions should not be enough');
$config->userPermissionClass->permissions = array('access CiviCRM', 'add contacts');
- $this->assertTrue(_civicrm_api3_api_check_permission('contact', 'create', $check, FALSE), 'exact permissions should be enough');
+ $this->assertTrue($this->runPermissionCheck('contact', 'create', $check), 'exact permissions should be enough');
$config->userPermissionClass->permissions = array('access CiviCRM', 'add contacts', 'import contacts');
- $this->assertTrue(_civicrm_api3_api_check_permission('contact', 'create', $check, FALSE), 'overfluous permissions should be enough');
+ $this->assertTrue($this->runPermissionCheck('contact', 'create', $check), 'overfluous permissions should be enough');
}
function testCheckPermissionThrow() {
$config = CRM_Core_Config::singleton();
try {
$config->userPermissionClass->permissions = array('access CiviCRM');
- _civicrm_api3_api_check_permission('contact', 'create', $check);
+ $this->runPermissionCheck('contact', 'create', $check, TRUE);
}
catch(Exception $e) {
$message = $e->getMessage();
$this->assertEquals($message, 'API permission check failed for contact/create call; insufficient permission: require access CiviCRM and add contacts', 'lacking permissions should throw an exception');
$config->userPermissionClass->permissions = array('access CiviCRM', 'add contacts', 'import contacts');
- $this->assertTrue(_civicrm_api3_api_check_permission('contact', 'create', $check), 'overfluous permissions should return true');
+ $this->assertTrue($this->runPermissionCheck('contact', 'create', $check), 'overfluous permissions should return true');
}
function testCheckPermissionSkip() {
$config = CRM_Core_Config::singleton();
$config->userPermissionClass->permissions = array('access CiviCRM');
$params = array('check_permissions' => TRUE);
- $this->assertFalse(_civicrm_api3_api_check_permission('contact', 'create', $params, FALSE), 'lacking permissions should not be enough');
+ $this->assertFalse($this->runPermissionCheck('contact', 'create', $params), 'lacking permissions should not be enough');
$params = array('check_permissions' => FALSE);
- $this->assertTrue(_civicrm_api3_api_check_permission('contact', 'create', $params, FALSE), 'permission check should be skippable');
+ $this->assertTrue($this->runPermissionCheck('contact', 'create', $params), 'permission check should be skippable');
+ }
+
+ /**
+ * @param string $entity
+ * @param string $action
+ * @param array $params
+ * @param bool $throws whether we should pass any exceptions for authorization failures
+ * @return bool TRUE or FALSE depending on the outcome of the authorization check
+ */
+ function runPermissionCheck($entity, $action, $params, $throws = FALSE) {
+ $dispatcher = new \Symfony\Component\EventDispatcher\EventDispatcher();
+ $dispatcher->addSubscriber(new \Civi\API\Subscriber\PermissionCheck());
+ $kernel = new \Civi\API\Kernel($dispatcher);
+ $apiRequest = $kernel->createRequest($entity, $action, $params, NULL);
+ try {
+ $kernel->authorize(NULL, $apiRequest);
+ return TRUE;
+ } catch (\API_Exception $e) {
+ $extra = $e->getExtraParams();
+ if (!$throws && $extra['error_code'] == API_Exception::UNAUTHORIZED) {
+ return FALSE;
+ } else {
+ throw $e;
+ }
+ }
}
/*