From 69ce577a7758d10e3de6dbf008713eeeffbc983b Mon Sep 17 00:00:00 2001 From: Rich Lott / Artful Robot Date: Tue, 12 Dec 2023 17:15:16 +0000 Subject: [PATCH] standalone: implement cms-prefixed permissions as concrete ones --- CRM/Core/Permission/Standalone.php | 61 +++++++++++++++++-- ext/standaloneusers/Civi/Api4/Role.php | 9 +++ .../Civi/Standalone/Security.php | 4 +- ext/standaloneusers/standaloneusers.php | 5 +- .../init/StandaloneUsers.civi-setup.php | 3 + 5 files changed, 73 insertions(+), 9 deletions(-) diff --git a/CRM/Core/Permission/Standalone.php b/CRM/Core/Permission/Standalone.php index bcc62bb2d6..deca76aba8 100644 --- a/CRM/Core/Permission/Standalone.php +++ b/CRM/Core/Permission/Standalone.php @@ -15,6 +15,8 @@ * @copyright CiviCRM LLC https://civicrm.org/licensing */ +use Civi\Api4\Role; + /** * Permissions class for Standalone. * @@ -35,10 +37,8 @@ class CRM_Core_Permission_Standalone extends CRM_Core_Permission_Base { * Given a permission string, check for access requirements. * * Note this differs from CRM_Core_Permission::check() which handles - * composite permissions (ORs etc) and Contacts. - * - * Some codepaths assume to be able to check a permission through this class; - * others through CRM_Core_Permission::check(). + * composite permissions (ORs etc), implied permission hierarchies, + * and Contacts. * * @param string $str * The permission to check. @@ -48,7 +48,58 @@ class CRM_Core_Permission_Standalone extends CRM_Core_Permission_Base { * true if yes, else false */ public function check($str, $userId = NULL) { - return \Civi\Standalone\Security::singleton()->checkPermission($this, $str, $userId); + // These core-defined synthetic permissions (which cannot be applied by our Role UI): + // cms:administer users + // cms:view user account + // need mapping to our concrete permissions (which can be applied to Roles) with the same names: + $str = $this->translatePermission($str, 'Standalone', [ + 'view user account' => 'view user account', + 'administer users' => 'administer users', + ]); + return \Civi\Standalone\Security::singleton()->checkPermission($str, $userId); + } + + /** + * Determine whether the permission store allows us to store + * a list of permissions generated dynamically (eg by + * hook_civicrm_permissions.) + * + * @return bool + */ + public function isModulePermissionSupported() { + return TRUE; + } + + /** + * Ensure that the CMS supports all the permissions defined by CiviCRM + * and its extensions. If there are stale permissions, they should be + * deleted. This is useful during module upgrade when the newer module + * version has removed permission that were defined in the older version. + * + * @param array $permissions + * Same format as CRM_Core_Permission::getCorePermissions(). + * + * @throws CRM_Core_Exception + * @see CRM_Core_Permission::getCorePermissions + */ + public function upgradePermissions($permissions) { + if (empty($permissions)) { + throw new CRM_Core_Exception("Cannot upgrade permissions: permission list missing"); + } + if (class_exists(Role::class)) { + $roles = Role::get(FALSE)->addSelect('permissions')->execute(); + $records = []; + $definedPermissions = array_keys($permissions); + foreach ($roles as $role) { + $newPermissions = array_intersect($role['permissions'], $definedPermissions); + if (count($newPermissions) < count($role['permissions'])) { + $records[] = ['id' => $role['id'], 'permissions' => $newPermissions]; + } + } + if ($records) { + Role::save(FALSE)->setRecords($records)->execute(); + } + } } } diff --git a/ext/standaloneusers/Civi/Api4/Role.php b/ext/standaloneusers/Civi/Api4/Role.php index 678779fa3a..19c5d562cb 100644 --- a/ext/standaloneusers/Civi/Api4/Role.php +++ b/ext/standaloneusers/Civi/Api4/Role.php @@ -10,4 +10,13 @@ namespace Civi\Api4; */ class Role extends Generic\DAOEntity { + /** + * Declare permissions needed to access this entity. + */ + public static function permissions() { + return [ + 'default' => ['cms:administer users'], + ]; + } + } diff --git a/ext/standaloneusers/Civi/Standalone/Security.php b/ext/standaloneusers/Civi/Standalone/Security.php index f9fca9b68b..64c0ab7105 100644 --- a/ext/standaloneusers/Civi/Standalone/Security.php +++ b/ext/standaloneusers/Civi/Standalone/Security.php @@ -29,8 +29,6 @@ class Security { /** * CRM_Core_Permission_Standalone::check() delegates here. * - * @param \CRM_Core_Permission_Standalone $permissionObject - * * @param string $permissionName * The permission to check. * @@ -40,7 +38,7 @@ class Security { * @return bool * true if yes, else false */ - public function checkPermission(\CRM_Core_Permission_Standalone $permissionObject, string $permissionName, ?int $userID = NULL) { + public function checkPermission(string $permissionName, ?int $userID = NULL) { if ($permissionName == \CRM_Core_Permission::ALWAYS_DENY_PERMISSION) { return FALSE; } diff --git a/ext/standaloneusers/standaloneusers.php b/ext/standaloneusers/standaloneusers.php index a463b09f8d..7960ae2f5c 100644 --- a/ext/standaloneusers/standaloneusers.php +++ b/ext/standaloneusers/standaloneusers.php @@ -75,6 +75,9 @@ function standaloneusers_civicrm_enable() { /** * Implements hook_civicrm_permission(). */ -function standalone_civicrm_permission(&$permissions) { +function standaloneusers_civicrm_permission(&$permissions) { $permissions['access password resets'] = ts('Allow users to access the reset password system'); + // Concrete implementations of synthetic cms: permissions. + $permissions['administer users'] = ts('Administer user accounts'); + $permissions['view user account'] = ts('View user accounts'); } diff --git a/setup/plugins/init/StandaloneUsers.civi-setup.php b/setup/plugins/init/StandaloneUsers.civi-setup.php index 51db2076e9..8e5dcb32e9 100644 --- a/setup/plugins/init/StandaloneUsers.civi-setup.php +++ b/setup/plugins/init/StandaloneUsers.civi-setup.php @@ -141,6 +141,9 @@ if (!defined('CIVI_SETUP')) { 'view own manual batches', 'access all custom data', 'access contact reference fields', + // Standalone-defined permissions that have the same name as the cms: prefixed synthetic ones + 'administer users', + 'view user account', // The admninister CiviCRM data implicitly sets other permissions as well. // Such as, edit message templates and admnister dedupe rules. 'administer CiviCRM Data', -- 2.25.1