APIv4 - Add test to ensure gatekeeper permissions are checked across joins
authorColeman Watts <coleman@civicrm.org>
Tue, 13 Apr 2021 12:14:59 +0000 (08:14 -0400)
committerSeamus Lee <seamuslee001@gmail.com>
Mon, 19 Apr 2021 23:21:21 +0000 (09:21 +1000)
tests/phpunit/api/v4/Query/PermissionCheckTest.php [new file with mode: 0644]

diff --git a/tests/phpunit/api/v4/Query/PermissionCheckTest.php b/tests/phpunit/api/v4/Query/PermissionCheckTest.php
new file mode 100644 (file)
index 0000000..790e08c
--- /dev/null
@@ -0,0 +1,178 @@
+<?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       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+
+namespace api\v4\Query;
+
+use api\v4\UnitTestCase;
+use Civi\API\Exception\UnauthorizedException;
+use Civi\Api4\Contact;
+use Civi\Api4\Event;
+use Civi\Api4\Participant;
+
+/**
+ * @group headless
+ */
+class PermissionCheckTest extends UnitTestCase {
+
+  /**
+   * Clean up after test.
+   *
+   * @throws \Exception
+   */
+  public function tearDown(): void {
+    \CRM_Utils_Hook::singleton()->reset();
+    $config = \CRM_Core_Config::singleton();
+    unset($config->userPermissionClass->permissions);
+    parent::tearDown();
+  }
+
+  /**
+   */
+  public function testGatekeeperPermissions() {
+    $config = \CRM_Core_Config::singleton();
+    $config->userPermissionClass->permissions = [
+      'access CiviCRM',
+      'access CiviEvent',
+      'view event info',
+    ];
+    // Above permissions should be sufficient to perform Event::get
+    Event::get()->execute();
+
+    $config->userPermissionClass->permissions = [];
+    // Ensure error is thrown if permissions are not sufficient
+    try {
+      Event::get()->execute();
+    }
+    catch (UnauthorizedException $e) {
+      $err = $e->getMessage();
+    }
+    $this->assertContains('Authorization failed', $err);
+  }
+
+  /**
+   * Tests that gatekeeper permissions are enforced for implicit joins
+   */
+  public function testImplicitJoinPermissions() {
+    $config = \CRM_Core_Config::singleton();
+    $config->userPermissionClass->permissions = [
+      'access CiviCRM',
+      'access CiviEvent',
+      'view all contacts',
+      'view event info',
+      'view event participants',
+    ];
+    $name = uniqid(__FUNCTION__);
+    $event = Event::create(FALSE)
+      ->addValue('title', 'ABC123 Event')
+      ->addValue('event_type_id', 1)
+      ->addValue('start_date', 'now')
+      ->execute()->first();
+    $contact = Contact::create(FALSE)
+      ->addValue('first_name', $name)
+      ->addChain('participant', Participant::create()
+        ->addValue('contact_id', '$id')
+        ->addValue('event_id', $event['id']),
+      0)
+      ->execute()->first();
+    $participant = Participant::get()
+      ->addSelect('contact.first_name', 'event.title')
+      ->addWhere('event.id', '=', $event['id'])
+      ->execute()
+      ->first();
+
+    $this->assertEquals('ABC123 Event', $participant['event.title']);
+    $this->assertEquals($name, $participant['contact.first_name']);
+
+    // Remove access to view events
+    $config->userPermissionClass->permissions = [
+      'access CiviCRM',
+      'access CiviEvent',
+      'view all contacts',
+      'view event participants',
+    ];
+    $participant = Participant::get()
+      ->addSelect('contact.first_name')
+      ->addSelect('event.title')
+      ->addWhere('id', '=', $contact['participant']['id'])
+      ->execute()
+      ->first();
+
+    $this->assertTrue(empty($participant['event.title']));
+    $this->assertEquals($name, $participant['contact.first_name']);
+
+  }
+
+  /**
+   * Tests that gatekeeper permissions are enforced for explicit joins
+   */
+  public function testExplicitJoinPermissions() {
+    $config = \CRM_Core_Config::singleton();
+    $config->userPermissionClass->permissions = [
+      'access CiviCRM',
+      'access CiviEvent',
+      'view all contacts',
+      'view event info',
+      'view event participants',
+    ];
+    $name = uniqid(__FUNCTION__);
+    $event = Event::create(FALSE)
+      ->addValue('title', 'ABC321 Event')
+      ->addValue('event_type_id', 1)
+      ->addValue('start_date', 'now')
+      ->execute()->first();
+    $contact = Contact::create(FALSE)
+      ->addValue('first_name', $name)
+      ->addChain('participant', Participant::create()
+        ->addValue('contact_id', '$id')
+        ->addValue('event_id', $event['id']),
+      0)
+      ->execute()->first();
+    $participant = Participant::get()
+      ->addJoin('Contact AS contact1', 'INNER', ['contact1.id', '=', 'contact_id'])
+      ->addJoin('Event AS event1', 'INNER')
+      ->addSelect('contact1.first_name', 'event1.title')
+      ->addWhere('event1.id', '=', $event['id'])
+      ->execute()
+      ->first();
+
+    $this->assertEquals('ABC321 Event', $participant['event1.title']);
+    $this->assertEquals($name, $participant['contact1.first_name']);
+
+    // Remove access to view events
+    $config->userPermissionClass->permissions = [
+      'access CiviCRM',
+      'access CiviEvent',
+      'view all contacts',
+      'view event participants',
+    ];
+    $participant = Participant::get()
+      ->addJoin('Contact AS contact1', 'INNER', ['contact1.id', '=', 'contact_id'])
+      ->addJoin('Event AS event1', 'INNER')
+      ->addSelect('contact1.first_name')
+      ->addSelect('event1.title')
+      ->addWhere('id', '=', $contact['participant']['id'])
+      ->execute()
+      ->first();
+
+    $this->assertTrue(empty($participant['event1.title']));
+    $this->assertEquals($name, $participant['contact1.first_name']);
+
+  }
+
+}