CRM-13744 permissions allow 'or' permissions - prevents regression on event registrat...
authorEileen <eileen@fuzion.co.nz>
Tue, 19 Nov 2013 09:10:06 +0000 (09:10 +0000)
committerEileen <eileen@fuzion.co.nz>
Tue, 26 Nov 2013 23:54:23 +0000 (12:54 +1300)
----------------------------------------
* 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

CRM/Core/DAO/permissions.php
CRM/Core/Permission.php
api/v3/utils.php
tests/phpunit/api/v3/ContactTest.php
tests/phpunit/api/v3/EventTest.php
tests/phpunit/api/v3/UtilsTest.php

index 17e037472a446a26ad2d6a22dd55a994e7a53cf8..eac8d728f35ede38d40772cc43882b8419e6ecab 100644 (file)
@@ -83,7 +83,7 @@ function _civicrm_api3_permissions($entity, $action, &$params) {
       'edit all contacts',
     ),
     'getquick' => array(
-      'access CiviCRM',
+      array('access CiviCRM', 'access AJAX API'),
     ),
   );
 
index 78a47d87f485d92b6fcd048aa92566c5c8616e07..4f75345e49bfa11234b646f0703446899a045bb4 100644 (file)
@@ -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;
   }
 
   /**
index 36e84b908049ef1351e0822e90be48a1188d283b..d01eae4c5a574b95172b9c5ac6a36dc732210ced 100644 (file)
@@ -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;
 }
 
index 5f4b7b6a508781bde676199a09f47f19bbd9d7dc..4ef90f5fc1a8ccf83e41eefe471a74dbd0e7e8a8 100644 (file)
@@ -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));
+  }
 }
index 878aa5cfd02f17b2f40249bf9f5bbbd8a556557b..b8349e07ff1c71160462418fe473603139cf2172 100644 (file)
@@ -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);
index 8cbd5fa4488697844488e73be2dfc28f5ae5596c..d90566e1719df92bce037ad68885cac80b63b226 100644 (file)
@@ -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');