From: Eileen Date: Tue, 19 Nov 2013 09:10:06 +0000 (+0000) Subject: CRM-13744 permissions allow 'or' permissions - prevents regression on event registrat... X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=60ec9f43970da556243e3da70e9dd662fa08ebfc;p=civicrm-core.git CRM-13744 permissions allow 'or' permissions - prevents regression on event registration under some circumstances ---------------------------------------- * CRM-13744: cid=0 contribution & event form autocompletes not working for people with *only* access ajax api http://issues.civicrm.org/jira/browse/CRM-13744 --- diff --git a/CRM/Core/DAO/permissions.php b/CRM/Core/DAO/permissions.php index 17e037472a..eac8d728f3 100644 --- a/CRM/Core/DAO/permissions.php +++ b/CRM/Core/DAO/permissions.php @@ -83,7 +83,7 @@ function _civicrm_api3_permissions($entity, $action, &$params) { 'edit all contacts', ), 'getquick' => array( - 'access CiviCRM', + array('access CiviCRM', 'access AJAX API'), ), ); diff --git a/CRM/Core/Permission.php b/CRM/Core/Permission.php index 78a47d87f4..4f75345e49 100644 --- a/CRM/Core/Permission.php +++ b/CRM/Core/Permission.php @@ -81,17 +81,63 @@ class CRM_Core_Permission { } /** - * given a permission string, check for access requirements + * given a permission string or array, check for access requirements + * @param mixed $permissions the permission to check as an array or string -see examples + * arrays * - * @param string $str the permission to check + * Ex 1 + * + * Must have 'access CiviCRM' + * (string) 'access CiviCRM' + * + * + * Ex 2 Must have 'access CiviCRM' and 'access Ajax API' + * array('access CiviCRM', 'access Ajax API') + * + * Ex 3 Must have 'access CiviCRM' or 'access Ajax API' + * array( + * array('access CiviCRM', 'access Ajax API'), + * ), + * + * Ex 4 Must have 'access CiviCRM' or 'access Ajax API' AND 'access CiviEvent' + * array( + * array('access CiviCRM', 'access Ajax API'), + * 'access CiviEvent', + * ), + * + * Note that in permissions.php this is keyed by the action eg. + * (access Civi || access AJAX) && (access CiviEvent || access CiviContribute) + * 'myaction' => array( + * array('access CiviCRM', 'access Ajax API'), + * array('access CiviEvent', 'access CiviContribute') + * ), * * @return boolean true if yes, else false * @static * @access public */ - static function check($str) { - $config = CRM_Core_Config::singleton(); - return $config->userPermissionClass->check($str); + static function check($permissions) { + $permissions = (array) $permissions; + + foreach ($permissions as $permission) { + if(is_array($permission)) { + foreach ($permission as $orPerm) { + if(self::check($orPerm)) { + //one of our 'or' permissions has succeeded - stop checking this permission + return TRUE;; + } + } + //none of our our conditions was met + return FALSE; + } + else { + if(!CRM_Core_Config::singleton()->userPermissionClass->check($permission)) { + //one of our 'and' conditions has not been met + return FALSE; + } + } + } + return TRUE; } /** diff --git a/api/v3/utils.php b/api/v3/utils.php index 36e84b9080..d01eae4c5a 100644 --- a/api/v3/utils.php +++ b/api/v3/utils.php @@ -910,7 +910,7 @@ function _civicrm_api3_check_required_fields($params, $daoName, $return = FALSE) * @param $entity string API entity being accessed * @param $action string API action being performed * @param $params array params of the API call - * @param $throw bool whether to throw exception instead of returning false + * @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 @@ -929,16 +929,20 @@ function _civicrm_api3_api_check_permission($entity, $action, &$params, $throw = return TRUE; } - foreach ($permissions as $perm) { - if (!CRM_Core_Permission::check($perm)) { - if ($throw) { - throw new Exception("API permission check failed for $entity/$action call; missing permission: $perm."); - } - else { - return FALSE; + 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; } diff --git a/tests/phpunit/api/v3/ContactTest.php b/tests/phpunit/api/v3/ContactTest.php index 5f4b7b6a50..4ef90f5fc1 100644 --- a/tests/phpunit/api/v3/ContactTest.php +++ b/tests/phpunit/api/v3/ContactTest.php @@ -1582,7 +1582,7 @@ class api_v3_ContactTest extends CiviUnitTestCase { $config = CRM_Core_Config::singleton(); $config->userPermissionClass->permissions = array('access CiviCRM'); $result = $this->callAPIFailure('contact', 'create', $params); - $this->assertEquals('API permission check failed for contact/create call; missing permission: add contacts.', $result['error_message'], 'lacking permissions should not be enough to create a contact'); + $this->assertEquals('API permission check failed for contact/create call; insufficient permission: require access CiviCRM and add contacts', $result['error_message'], 'lacking permissions should not be enough to create a contact'); $config->userPermissionClass->permissions = array('access CiviCRM', 'add contacts', 'import contacts'); $result = $this->callAPISuccess('contact', 'create', $params, NULL, 'overfluous permissions should be enough to create a contact'); @@ -1596,7 +1596,7 @@ class api_v3_ContactTest extends CiviUnitTestCase { $config->userPermissionClass->permissions = array('access CiviCRM'); $result = $this->callAPIFailure('contact', 'update', $params); - $this->assertEquals('API permission check failed for contact/update call; missing permission: edit all contacts.', $result['error_message'], 'lacking permissions should not be enough to update a contact'); + $this->assertEquals('API permission check failed for contact/update call; insufficient permission: require access CiviCRM and edit all contacts', $result['error_message'], 'lacking permissions should not be enough to update a contact'); $config->userPermissionClass->permissions = array('access CiviCRM', 'add contacts', 'view all contacts', 'edit all contacts', 'import contacts'); $result = $this->callAPISuccess('contact', 'update', $params, NULL, 'overfluous permissions should be enough to update a contact'); @@ -1644,4 +1644,17 @@ class api_v3_ContactTest extends CiviUnitTestCase { $result = $this->callAPISuccess('contact', 'proximity', $proxParams); $this->assertEquals(1, $result['count'], 'In line ' . __LINE__); } + + /** + * Test that Ajax API permission is suffient to access quicksearch api + * (note that quicksearch api is required for autocomplete & has ACL permissions applied) + */ + function testQuickSearchPermission_CRM_13744() { + CRM_Core_Config::singleton()->userPermissionClass->permissions = array('access CiviEvent'); + $result = $this->callAPIFailure('contact', 'getquick', array('name' => 'b', 'check_permissions' => TRUE)); + CRM_Core_Config::singleton()->userPermissionClass->permissions = array('access CiviCRM'); + $result = $this->callAPISuccess('contact', 'getquick', array('name' => 'b', 'check_permissions' => TRUE)); + CRM_Core_Config::singleton()->userPermissionClass->permissions = array('access AJAX API'); + $result = $this->callAPISuccess('contact', 'getquick', array('name' => 'b', 'check_permissions' => TRUE)); + } } diff --git a/tests/phpunit/api/v3/EventTest.php b/tests/phpunit/api/v3/EventTest.php index 878aa5cfd0..b8349e07ff 100644 --- a/tests/phpunit/api/v3/EventTest.php +++ b/tests/phpunit/api/v3/EventTest.php @@ -475,7 +475,7 @@ class api_v3_EventTest extends CiviUnitTestCase { $config = &CRM_Core_Config::singleton(); $config->userPermissionClass->permissions = array('access CiviCRM'); $result = $this->callAPIFailure('event', 'create', $params); - $this->assertEquals('API permission check failed for event/create call; missing permission: access CiviEvent.', $result['error_message'], 'lacking permissions should not be enough to create an event'); + $this->assertEquals('API permission check failed for event/create call; insufficient permission: require access CiviCRM and access CiviEvent and edit all events', $result['error_message'], 'lacking permissions should not be enough to create an event'); $config->userPermissionClass->permissions = array('access CiviEvent', 'edit all events', 'access CiviCRM'); $result = $this->callAPISuccess('event', 'create', $params); diff --git a/tests/phpunit/api/v3/UtilsTest.php b/tests/phpunit/api/v3/UtilsTest.php index 8cbd5fa448..d90566e171 100644 --- a/tests/phpunit/api/v3/UtilsTest.php +++ b/tests/phpunit/api/v3/UtilsTest.php @@ -91,7 +91,7 @@ class api_v3_UtilsTest extends CiviUnitTestCase { catch(Exception $e) { $message = $e->getMessage(); } - $this->assertEquals($message, 'API permission check failed for contact/create call; missing permission: add contacts.', 'lacking permissions should throw an exception'); + $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');