From f16b2aeed0866c654a8d418fc913cc6ed21be061 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Wed, 18 Dec 2019 20:11:52 -0800 Subject: [PATCH] Enforce customizable `permission` for server routes. Add E2E test for server-routes. --- ext/afform/bin/test-all.sh | 8 +- ext/afform/core/afform.php | 23 ++++- .../phpunit/api/v4/AfformRoutingTest.php | 99 +++++++++++++++++++ 3 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 ext/afform/mock/tests/phpunit/api/v4/AfformRoutingTest.php diff --git a/ext/afform/bin/test-all.sh b/ext/afform/bin/test-all.sh index c1a7cf77ab..fdb01887e1 100755 --- a/ext/afform/bin/test-all.sh +++ b/ext/afform/bin/test-all.sh @@ -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 diff --git a/ext/afform/core/afform.php b/ext/afform/core/afform.php index 3ec7664af0..b103781824 100644 --- a/ext/afform/core/afform.php +++ b/ext/afform/core/afform.php @@ -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 index 0000000000..3fd74d34e1 --- /dev/null +++ b/ext/afform/mock/tests/phpunit/api/v4/AfformRoutingTest.php @@ -0,0 +1,99 @@ +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); + } + +} -- 2.25.1