Enforce customizable `permission` for server routes. Add E2E test for server-routes.
authorTim Otten <totten@civicrm.org>
Thu, 19 Dec 2019 04:11:52 +0000 (20:11 -0800)
committerCiviCRM <info@civicrm.org>
Wed, 16 Sep 2020 02:13:20 +0000 (19:13 -0700)
ext/afform/bin/test-all.sh
ext/afform/core/afform.php
ext/afform/mock/tests/phpunit/api/v4/AfformRoutingTest.php [new file with mode: 0644]

index c1a7cf77abde29be502bced0142ad67f29972b1c..fdb01887e11b06adfa2c050c08887021ae0d7b3f 100755 (executable)
@@ -18,7 +18,13 @@ pushd "$AFF_CORE" >> /dev/null
 popd >> /dev/null
 
 pushd "$AFF_MOCK" >> /dev/null
-  if ! phpunit6 "$@" ; then
+  if ! phpunit6 --group e2e "$@" ; then
+    EXIT=1
+  fi
+popd >> /dev/null
+
+pushd "$AFF_MOCK" >> /dev/null
+  if ! phpunit6 --group headless "$@" ; then
     EXIT=1
   fi
 popd >> /dev/null
index 3ec7664af04b899f0ad679f592d071a0d9db5c8e..b103781824263cf4f42324affcd5486e2ac3ee89 100644 (file)
@@ -452,13 +452,34 @@ function afform_civicrm_alterMenu(&$items) {
         'page_callback' => 'CRM_Afform_Page_AfformBase',
         'page_arguments' => 'afform=' . urlencode($name),
         'title' => $meta['title'] ?? '',
-        'access_arguments' => [['access CiviCRM'], 'and'], // FIXME
+        'access_arguments' => [["@afform:$name"], 'and'],
         'is_public' => $meta['is_public'],
       ];
     }
   }
 }
 
+/**
+ * Implements hook_civicrm_permission_check().
+ *
+ * @see CRM_Utils_Hook::permission_check()
+ */
+function afform_civicrm_permission_check($permission, &$granted, $contactId) {
+  if ($permission{0} !== '@') {
+    // Micro-optimization - this function may get hit a lot.
+    return;
+  }
+
+  if (preg_match('/^@afform:(.*)/', $permission, $m)) {
+    $name = $m[1];
+
+    /** @var CRM_Afform_AfformScanner $scanner */
+    $scanner = \Civi::container()->get('afform_scanner');
+    $meta = $scanner->getMeta($name);
+    $granted = CRM_Core_Permission::check($meta['permission'], $contactId);
+  }
+}
+
 /**
  * Clear any local/in-memory caches based on afform data.
  */
diff --git a/ext/afform/mock/tests/phpunit/api/v4/AfformRoutingTest.php b/ext/afform/mock/tests/phpunit/api/v4/AfformRoutingTest.php
new file mode 100644 (file)
index 0000000..3fd74d3
--- /dev/null
@@ -0,0 +1,99 @@
+<?php
+
+/**
+ * Ensure that the routes created by Afform are working.
+ * @group e2e
+ */
+class api_v4_AfformRoutingTest extends \PHPUnit\Framework\TestCase implements \Civi\Test\EndToEndInterface {
+
+  protected $formName = 'mockPage';
+
+  public static function setUpBeforeClass() {
+    \Civi\Test::e2e()
+      ->install(['org.civicrm.afform', 'org.civicrm.afform-mock'])
+      ->apply();
+  }
+
+  public function setUp() {
+    parent::setUp();
+    Civi\Api4\Afform::revert()
+      ->setCheckPermissions(FALSE)
+      ->addWhere('name', '=', $this->formName)
+      ->execute();
+  }
+
+  public function tearDown() {
+    parent::tearDown();
+    Civi\Api4\Afform::revert()
+      ->setCheckPermissions(FALSE)
+      ->addWhere('name', '=', $this->formName)
+      ->execute();
+  }
+
+  public function testChangingPermissions() {
+    $http = new \GuzzleHttp\Client(['http_errors' => FALSE]);
+    $url = function ($path, $query = NULL) {
+      return CRM_Utils_System::url($path, $query, TRUE, NULL, FALSE);
+    };
+
+    $result = $http->get($url('civicrm/mock-page'));
+    $this->assertNotAuthorized($result);
+
+    Civi\Api4\Afform::update()
+      ->setCheckPermissions(FALSE)
+      ->addWhere('name', '=', $this->formName)
+      ->addValue('permission', CRM_Core_Permission::ALWAYS_ALLOW_PERMISSION)
+      ->execute();
+
+    $result = $http->get($url('civicrm/mock-page'));
+    $this->assertOpensPage($result, 'mock-page');
+  }
+
+  public function testChangingPath() {
+    $http = new \GuzzleHttp\Client(['http_errors' => FALSE]);
+    $url = function ($path, $query = NULL) {
+      return CRM_Utils_System::url($path, $query, TRUE, NULL, FALSE);
+    };
+
+    Civi\Api4\Afform::update()
+      ->setCheckPermissions(FALSE)
+      ->addWhere('name', '=', $this->formName)
+      ->addValue('permission', CRM_Core_Permission::ALWAYS_ALLOW_PERMISSION)
+      ->execute();
+
+    $this->assertOpensPage($http->get($url('civicrm/mock-page')), 'mock-page');
+    $this->assertNotAuthorized($http->get($url('civicrm/mock-page-renamed')));
+
+    Civi\Api4\Afform::update()
+      ->setCheckPermissions(FALSE)
+      ->addWhere('name', '=', $this->formName)
+      ->addValue('server_route', 'civicrm/mock-page-renamed')
+      ->execute();
+
+    $this->assertNotAuthorized($http->get($url('civicrm/mock-page')));
+    $this->assertOpensPage($http->get($url('civicrm/mock-page-renamed')), 'mock-page');
+  }
+
+  /**
+   * @param $result
+   */
+  private function assertNotAuthorized(Psr\Http\Message\ResponseInterface $result) {
+    $contents = $result->getBody()->getContents();
+    $this->assertEquals(403, $result->getStatusCode());
+    $this->assertRegExp(';You are not authorized to access;', $contents);
+    $this->assertNotRegExp(';afform":\{"open":".*"\};', $contents);
+  }
+
+  /**
+   * @param $result
+   * @param string $directive
+   *   The name of the directive which auto-opens.
+   */
+  private function assertOpensPage(Psr\Http\Message\ResponseInterface $result, $directive) {
+    $contents = $result->getBody()->getContents();
+    $this->assertEquals(200, $result->getStatusCode());
+    $this->assertNotRegExp(';You are not authorized to access;', $contents);
+    $this->assertRegExp(';afform":\{"open":"' . preg_quote($directive, ';') . '"\};', $contents);
+  }
+
+}