SearchKit - Add "administer SearchKit" permission
authorColeman Watts <coleman@civicrm.org>
Thu, 2 Jun 2022 14:26:30 +0000 (10:26 -0400)
committerColeman Watts <coleman@civicrm.org>
Fri, 3 Jun 2022 03:19:12 +0000 (23:19 -0400)
This permission allows non-admins to use search kit.
Previously the user needed 'administer CiviCRM data' permission.

Fixes dev/core#3457

ext/search_kit/CRM/Search/BAO/SearchDisplay.php
ext/search_kit/Civi/Api4/Action/SearchDisplay/AbstractRunAction.php
ext/search_kit/Civi/Api4/Action/SearchDisplay/GetDefault.php
ext/search_kit/Civi/Api4/Event/Subscriber/SearchKitSubscriber.php [new file with mode: 0644]
ext/search_kit/Civi/Api4/SearchDisplay.php
ext/search_kit/Civi/Api4/SearchSegment.php
ext/search_kit/search_kit.php
ext/search_kit/tests/phpunit/api/v4/SearchDisplay/SearchRunTest.php
ext/search_kit/xml/Menu/search_kit.xml

index 48776d96ebba25c805332c25a3d95ba6785dfd46..c52b411427a81cc3fafdf694d19a3b7f28cca1ac 100644 (file)
@@ -25,8 +25,8 @@ class CRM_Search_BAO_SearchDisplay extends CRM_Search_DAO_SearchDisplay {
    */
   public static function _checkAccess(string $entityName, string $action, array $record, int $userCID) {
     // If we hit this function at all, the user is not a super-admin
-    // But they must be at least a regular administrator
-    if (!CRM_Core_Permission::check('administer CiviCRM data')) {
+    // But they must be at least a SearchKit administrator
+    if (!CRM_Core_Permission::check([['administer CiviCRM data', 'administer search_kit']])) {
       return FALSE;
     }
     if (in_array($action, ['create', 'update'], TRUE)) {
index 7a60d61ffe31c381bd992e1b7459eee53883d9f8..aaf693fb9d0068aa517ff3c043279d77a27b9b9e 100644 (file)
@@ -90,8 +90,11 @@ abstract class AbstractRunAction extends \Civi\Api4\Generic\AbstractAction {
    * @throws \API_Exception
    */
   public function _run(\Civi\Api4\Generic\Result $result) {
-    // Only administrators can use this in unsecured "preview mode"
-    if ((is_array($this->savedSearch) || is_array($this->display)) && $this->checkPermissions && !\CRM_Core_Permission::check('administer CiviCRM data')) {
+    // Only SearchKit admins can use this in unsecured "preview mode"
+    if (
+      (is_array($this->savedSearch) || is_array($this->display)) && $this->checkPermissions &&
+      !\CRM_Core_Permission::check([['administer CiviCRM data', 'administer search_kit']])
+    ) {
       throw new UnauthorizedException('Access denied');
     }
     $this->loadSavedSearch();
index 320831b7acf3aa0f7dcbb1f854a1b0330151203c..9ae2d8ad6dfd529edc60f69b1ffb0acae3c729c8 100644 (file)
@@ -42,8 +42,11 @@ class GetDefault extends \Civi\Api4\Generic\AbstractAction {
    * @throws \API_Exception
    */
   public function _run(\Civi\Api4\Generic\Result $result) {
-    // Only administrators can use this in unsecured "preview mode"
-    if (is_array($this->savedSearch) && $this->checkPermissions && !\CRM_Core_Permission::check('administer CiviCRM data')) {
+    // Only SearchKit admins can use this in unsecured "preview mode"
+    if (
+      is_array($this->savedSearch) && $this->checkPermissions &&
+      !\CRM_Core_Permission::check([['administer CiviCRM data', 'administer search_kit']])
+    ) {
       throw new UnauthorizedException('Access denied');
     }
     $this->loadSavedSearch();
diff --git a/ext/search_kit/Civi/Api4/Event/Subscriber/SearchKitSubscriber.php b/ext/search_kit/Civi/Api4/Event/Subscriber/SearchKitSubscriber.php
new file mode 100644 (file)
index 0000000..62e4815
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+namespace Civi\Api4\Event\Subscriber;
+
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Event subscriber to check extra permission for SavedSearches
+ */
+class SearchKitSubscriber implements EventSubscriberInterface {
+
+  /**
+   * @return array
+   */
+  public static function getSubscribedEvents() {
+    return [
+      'civi.api.authorize' => [
+        ['onApiAuthorize', -200],
+      ],
+    ];
+  }
+
+  /**
+   * Alters APIv4 permissions to allow users with 'administer search_kit' to create/delete a SavedSearch
+   *
+   * @param \Civi\API\Event\AuthorizeEvent $event
+   *   API authorization event.
+   */
+  public function onApiAuthorize(\Civi\API\Event\AuthorizeEvent $event) {
+    /* @var \Civi\Api4\Generic\AbstractAction $apiRequest */
+    $apiRequest = $event->getApiRequest();
+    if ($apiRequest['version'] == 4 && $apiRequest->getEntityName() === 'SavedSearch') {
+      if (\CRM_Core_Permission::check('administer search_kit')) {
+        $event->authorize();
+        $event->stopPropagation();
+      }
+    }
+  }
+
+}
index 58e37d483c32bf582fd455817cf411dfa02ce481..f748b72304ca7871187b2a0b13174eb0852627ac 100644 (file)
@@ -51,7 +51,7 @@ class SearchDisplay extends Generic\DAOEntity {
 
   public static function permissions() {
     $permissions = parent::permissions();
-    $permissions['default'] = ['administer CiviCRM data'];
+    $permissions['default'] = [['administer CiviCRM data', 'administer search_kit']];
     // Anyone with access to CiviCRM can view search displays (but not necessarily the results)
     $permissions['get'] = $permissions['getDefault'] = ['access CiviCRM'];
     // Anyone with access to CiviCRM can do search tasks (but not necessarily all of them)
index 15df9c4d8af5c3521b02db6a46c97cd2532f60de..3b44620e7a6e1ea6189b8d24a1dc06f889a80701 100644 (file)
@@ -9,4 +9,10 @@ namespace Civi\Api4;
 class SearchSegment extends Generic\DAOEntity {
   use \Civi\Api4\Generic\Traits\ManagedEntity;
 
+  public static function permissions() {
+    $permissions = parent::permissions();
+    $permissions['default'] = [['administer CiviCRM data', 'administer search_kit']];
+    return $permissions;
+  }
+
 }
index bf742583e3c294ec23ab2ccfd1f13f8dbbe3b74e..54e23a17f61dbfa35ba8dbc1e43e2e902e378a26 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 
 require_once 'search_kit.civix.php';
+use CRM_Search_ExtensionUtil as E;
 
 /**
  * Implements hook_civicrm_config().
@@ -10,6 +11,7 @@ require_once 'search_kit.civix.php';
 function search_kit_civicrm_config(&$config) {
   _search_kit_civix_civicrm_config($config);
   Civi::dispatcher()->addListener('hook_civicrm_alterAngular', ['\Civi\Search\AfformSearchMetadataInjector', 'preprocess'], 1000);
+  Civi::dispatcher()->addSubscriber(new Civi\Api4\Event\Subscriber\SearchKitSubscriber());
 }
 
 /**
@@ -23,6 +25,18 @@ function search_kit_civicrm_container($container) {
     ]);
 }
 
+/**
+ * Implements hook_civicrm_permission().
+ *
+ * Define SearchKit permissions.
+ */
+function search_kit_civicrm_permission(&$permissions) {
+  $permissions['administer search_kit'] = [
+    E::ts('Search Kit: edit and delete searches'),
+    E::ts('Gives non-admin users access to the Search Kit UI to create, update and delete searches and displays'),
+  ];
+}
+
 /**
  * Implements hook_civicrm_alterApiRoutePermissions().
  *
index 62e43010b08737c82f3d97edc2159da2b1995891..e0ce60a62e4192dd46d3ba5ab10d8b7f05f54c68 100644 (file)
@@ -712,7 +712,7 @@ class SearchRunTest extends \PHPUnit\Framework\TestCase implements HeadlessInter
     }
     $this->assertStringContainsString('failed', $error);
 
-    $config->userPermissionClass->permissions = ['access CiviCRM', 'administer CiviCRM data'];
+    $config->userPermissionClass->permissions = ['access CiviCRM', 'administer search_kit'];
 
     // Admins can edit the search and the display
     SavedSearch::update()->addWhere('name', '=', $searchName)
index eb53c327096eb0cdfb907ff013d65fe489108183..9a641dcf73e6c428be32a34b85ac1a37f3cfe588 100644 (file)
@@ -8,6 +8,6 @@
   <item>
     <path>civicrm/admin/search</path>
     <page_callback>CRM_Search_Page_Admin</page_callback>
-    <access_arguments>administer CiviCRM data</access_arguments>
+    <access_arguments>administer CiviCRM data;administer search_kit</access_arguments>
   </item>
 </menu>