From c8849b89be0a383ae6076dc716c05aede097816d Mon Sep 17 00:00:00 2001 From: Rich Lott / Artful Robot Date: Fri, 30 Jun 2023 11:31:23 +0100 Subject: [PATCH] standalone: Sack off Role, UserRole entities; use Pseudoconstant on User for roles; update tests The role table only held a string; using OptionValue enables us to have translatable values etc. Also it enables us to use a bookend multi value in user roles, which should be fine as it's rarely searched. --- .../CRM/Standaloneusers/BAO/Role.php | 26 -- .../Standaloneusers/BAO/RolePermission.php | 26 -- .../CRM/Standaloneusers/BAO/UserRole.php | 26 -- .../CRM/Standaloneusers/DAO/Role.php | 202 -------------- .../Standaloneusers/DAO/RolePermission.php | 26 +- .../CRM/Standaloneusers/DAO/User.php | 39 ++- .../CRM/Standaloneusers/DAO/UserRole.php | 247 ------------------ ext/standaloneusers/Civi/Api4/Role.php | 13 - ext/standaloneusers/Civi/Api4/UserRole.php | 13 - .../Civi/Standalone/Security.php | 11 +- .../ang/afformEditUserAccount.aff.html | 1 + .../afsearchAdministerUserAccounts.aff.html | 17 ++ .../afsearchAdministerUserAccounts.aff.json | 23 ++ .../ang/afsearchUsers.aff.html | 8 - .../ang/afsearchUsers.aff.json | 9 - .../managed/OptionGroup_role.mgd.php | 64 +++++ ext/standaloneusers/sql/auto_install.sql | 41 +-- ext/standaloneusers/sql/auto_uninstall.sql | 2 - .../phpunit/Civi/Standalone/SecurityTest.php | 29 +- .../CRM/Standaloneusers/Role.entityType.php | 10 - .../xml/schema/CRM/Standaloneusers/Role.xml | 37 --- .../CRM/Standaloneusers/RolePermission.xml | 11 +- .../xml/schema/CRM/Standaloneusers/User.xml | 15 ++ .../Standaloneusers/UserRole.entityType.php | 10 - .../schema/CRM/Standaloneusers/UserRole.xml | 54 ---- .../init/StandaloneUsers.civi-setup.php | 20 +- 26 files changed, 214 insertions(+), 766 deletions(-) delete mode 100644 ext/standaloneusers/CRM/Standaloneusers/BAO/Role.php delete mode 100644 ext/standaloneusers/CRM/Standaloneusers/BAO/RolePermission.php delete mode 100644 ext/standaloneusers/CRM/Standaloneusers/BAO/UserRole.php delete mode 100644 ext/standaloneusers/CRM/Standaloneusers/DAO/Role.php delete mode 100644 ext/standaloneusers/CRM/Standaloneusers/DAO/UserRole.php delete mode 100644 ext/standaloneusers/Civi/Api4/Role.php delete mode 100644 ext/standaloneusers/Civi/Api4/UserRole.php create mode 100644 ext/standaloneusers/ang/afsearchAdministerUserAccounts.aff.html create mode 100644 ext/standaloneusers/ang/afsearchAdministerUserAccounts.aff.json delete mode 100644 ext/standaloneusers/ang/afsearchUsers.aff.html delete mode 100644 ext/standaloneusers/ang/afsearchUsers.aff.json create mode 100644 ext/standaloneusers/managed/OptionGroup_role.mgd.php delete mode 100644 ext/standaloneusers/xml/schema/CRM/Standaloneusers/Role.entityType.php delete mode 100644 ext/standaloneusers/xml/schema/CRM/Standaloneusers/Role.xml delete mode 100644 ext/standaloneusers/xml/schema/CRM/Standaloneusers/UserRole.entityType.php delete mode 100644 ext/standaloneusers/xml/schema/CRM/Standaloneusers/UserRole.xml diff --git a/ext/standaloneusers/CRM/Standaloneusers/BAO/Role.php b/ext/standaloneusers/CRM/Standaloneusers/BAO/Role.php deleted file mode 100644 index b1072181b2..0000000000 --- a/ext/standaloneusers/CRM/Standaloneusers/BAO/Role.php +++ /dev/null @@ -1,26 +0,0 @@ -copyValues($params); - * $instance->save(); - * CRM_Utils_Hook::post($hook, $entityName, $instance->id, $instance); - * - * return $instance; - * } */ - -} diff --git a/ext/standaloneusers/CRM/Standaloneusers/BAO/RolePermission.php b/ext/standaloneusers/CRM/Standaloneusers/BAO/RolePermission.php deleted file mode 100644 index 9f5c382f09..0000000000 --- a/ext/standaloneusers/CRM/Standaloneusers/BAO/RolePermission.php +++ /dev/null @@ -1,26 +0,0 @@ -copyValues($params); - * $instance->save(); - * CRM_Utils_Hook::post($hook, $entityName, $instance->id, $instance); - * - * return $instance; - * } */ - -} diff --git a/ext/standaloneusers/CRM/Standaloneusers/BAO/UserRole.php b/ext/standaloneusers/CRM/Standaloneusers/BAO/UserRole.php deleted file mode 100644 index 27aa0fd7de..0000000000 --- a/ext/standaloneusers/CRM/Standaloneusers/BAO/UserRole.php +++ /dev/null @@ -1,26 +0,0 @@ -copyValues($params); - * $instance->save(); - * CRM_Utils_Hook::post($hook, $entityName, $instance->id, $instance); - * - * return $instance; - * } */ - -} diff --git a/ext/standaloneusers/CRM/Standaloneusers/DAO/Role.php b/ext/standaloneusers/CRM/Standaloneusers/DAO/Role.php deleted file mode 100644 index 7d6cfbcc42..0000000000 --- a/ext/standaloneusers/CRM/Standaloneusers/DAO/Role.php +++ /dev/null @@ -1,202 +0,0 @@ -__table = 'civicrm_role'; - parent::__construct(); - } - - /** - * Returns localized title of this entity. - * - * @param bool $plural - * Whether to return the plural version of the title. - */ - public static function getEntityTitle($plural = FALSE) { - return $plural ? E::ts('Roles') : E::ts('Role'); - } - - /** - * Returns all the column names of this table - * - * @return array - */ - public static function &fields() { - if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = [ - 'id' => [ - 'name' => 'id', - 'type' => CRM_Utils_Type::T_INT, - 'title' => E::ts('ID'), - 'description' => E::ts('Unique Role ID'), - 'required' => TRUE, - 'usage' => [ - 'import' => FALSE, - 'export' => FALSE, - 'duplicate_matching' => FALSE, - 'token' => FALSE, - ], - 'where' => 'civicrm_role.id', - 'table_name' => 'civicrm_role', - 'entity' => 'Role', - 'bao' => 'CRM_Standaloneusers_DAO_Role', - 'localizable' => 0, - 'html' => [ - 'type' => 'Number', - ], - 'readonly' => TRUE, - 'add' => NULL, - ], - 'name' => [ - 'name' => 'name', - 'type' => CRM_Utils_Type::T_STRING, - 'title' => E::ts('Name'), - 'required' => TRUE, - 'maxlength' => 64, - 'size' => CRM_Utils_Type::BIG, - 'usage' => [ - 'import' => FALSE, - 'export' => FALSE, - 'duplicate_matching' => FALSE, - 'token' => FALSE, - ], - 'where' => 'civicrm_role.name', - 'table_name' => 'civicrm_role', - 'entity' => 'Role', - 'bao' => 'CRM_Standaloneusers_DAO_Role', - 'localizable' => 0, - 'add' => NULL, - ], - ]; - CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); - } - return Civi::$statics[__CLASS__]['fields']; - } - - /** - * Return a mapping from field-name to the corresponding key (as used in fields()). - * - * @return array - * Array(string $name => string $uniqueName). - */ - public static function &fieldKeys() { - if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { - Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); - } - return Civi::$statics[__CLASS__]['fieldKeys']; - } - - /** - * Returns the names of this table - * - * @return string - */ - public static function getTableName() { - return self::$_tableName; - } - - /** - * Returns if this table needs to be logged - * - * @return bool - */ - public function getLog() { - return self::$_log; - } - - /** - * Returns the list of fields that can be imported - * - * @param bool $prefix - * - * @return array - */ - public static function &import($prefix = FALSE) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'role', $prefix, []); - return $r; - } - - /** - * Returns the list of fields that can be exported - * - * @param bool $prefix - * - * @return array - */ - public static function &export($prefix = FALSE) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'role', $prefix, []); - return $r; - } - - /** - * Returns the list of indices - * - * @param bool $localize - * - * @return array - */ - public static function indices($localize = TRUE) { - $indices = [ - 'UI_name' => [ - 'name' => 'UI_name', - 'field' => [ - 0 => 'name', - ], - 'localizable' => FALSE, - 'unique' => TRUE, - 'sig' => 'civicrm_role::1::name', - ], - ]; - return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; - } - -} diff --git a/ext/standaloneusers/CRM/Standaloneusers/DAO/RolePermission.php b/ext/standaloneusers/CRM/Standaloneusers/DAO/RolePermission.php index cac905623b..fc800fb637 100644 --- a/ext/standaloneusers/CRM/Standaloneusers/DAO/RolePermission.php +++ b/ext/standaloneusers/CRM/Standaloneusers/DAO/RolePermission.php @@ -6,7 +6,7 @@ * * Generated from standaloneusers/xml/schema/CRM/Standaloneusers/RolePermission.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:84bc2b8af9d957f50af8bf53a806f92f) + * (GenCodeChecksum:9bd09dba43f645696426e1ef15f776d9) */ use CRM_Standaloneusers_ExtensionUtil as E; @@ -41,7 +41,7 @@ class CRM_Standaloneusers_DAO_RolePermission extends CRM_Core_DAO { public $id; /** - * FK to Role + * FK to a role option value * * @var int|string|null * (SQL type: int unsigned) @@ -76,21 +76,6 @@ class CRM_Standaloneusers_DAO_RolePermission extends CRM_Core_DAO { return $plural ? E::ts('Role Permissions') : E::ts('Role Permission'); } - /** - * Returns foreign keys and entity references. - * - * @return array - * [CRM_Core_Reference_Interface] - */ - public static function getReferenceColumns() { - if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'role_id', 'civicrm_role', 'id'); - CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); - } - return Civi::$statics[__CLASS__]['links']; - } - /** * Returns all the column names of this table * @@ -126,7 +111,7 @@ class CRM_Standaloneusers_DAO_RolePermission extends CRM_Core_DAO { 'name' => 'role_id', 'type' => CRM_Utils_Type::T_INT, 'title' => E::ts('Role ID'), - 'description' => E::ts('FK to Role'), + 'description' => E::ts('FK to a role option value'), 'usage' => [ 'import' => FALSE, 'export' => FALSE, @@ -138,7 +123,10 @@ class CRM_Standaloneusers_DAO_RolePermission extends CRM_Core_DAO { 'entity' => 'RolePermission', 'bao' => 'CRM_Standaloneusers_DAO_RolePermission', 'localizable' => 0, - 'FKClassName' => 'CRM_Standaloneusers_DAO_Role', + 'pseudoconstant' => [ + 'optionGroupName' => 'role', + 'optionEditPath' => 'civicrm/admin/options/role', + ], 'add' => NULL, ], 'permission' => [ diff --git a/ext/standaloneusers/CRM/Standaloneusers/DAO/User.php b/ext/standaloneusers/CRM/Standaloneusers/DAO/User.php index a06885f5ab..eaf0b65fee 100644 --- a/ext/standaloneusers/CRM/Standaloneusers/DAO/User.php +++ b/ext/standaloneusers/CRM/Standaloneusers/DAO/User.php @@ -6,7 +6,7 @@ * * Generated from standaloneusers/xml/schema/CRM/Standaloneusers/User.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:7138e29e351cc35c36eb2647ff1b291a) + * (GenCodeChecksum:7f2d1d449d263061c7db7a6c5d57c27d) */ use CRM_Standaloneusers_ExtensionUtil as E; @@ -91,6 +91,15 @@ class CRM_Standaloneusers_DAO_User extends CRM_Core_DAO { */ public $email; + /** + * FK to values from OptionGroup role + * + * @var string|null + * (SQL type: varchar(128)) + * Note that values will be retrieved from the database as a string. + */ + public $roles; + /** * @var string|null * (SQL type: timestamp) @@ -288,6 +297,34 @@ class CRM_Standaloneusers_DAO_User extends CRM_Core_DAO { ], 'add' => NULL, ], + 'roles' => [ + 'name' => 'roles', + 'type' => CRM_Utils_Type::T_STRING, + 'title' => E::ts('Roles'), + 'description' => E::ts('FK to values from OptionGroup role'), + 'maxlength' => 128, + 'size' => CRM_Utils_Type::HUGE, + 'usage' => [ + 'import' => FALSE, + 'export' => FALSE, + 'duplicate_matching' => FALSE, + 'token' => FALSE, + ], + 'where' => 'civicrm_user.roles', + 'table_name' => 'civicrm_user', + 'entity' => 'User', + 'bao' => 'CRM_Standaloneusers_DAO_User', + 'localizable' => 0, + 'serialize' => self::SERIALIZE_SEPARATOR_BOOKEND, + 'html' => [ + 'type' => 'Select', + ], + 'pseudoconstant' => [ + 'optionGroupName' => 'role', + 'optionEditPath' => 'civicrm/admin/options/role', + ], + 'add' => NULL, + ], 'when_created' => [ 'name' => 'when_created', 'type' => CRM_Utils_Type::T_TIMESTAMP, diff --git a/ext/standaloneusers/CRM/Standaloneusers/DAO/UserRole.php b/ext/standaloneusers/CRM/Standaloneusers/DAO/UserRole.php deleted file mode 100644 index ca5806cee1..0000000000 --- a/ext/standaloneusers/CRM/Standaloneusers/DAO/UserRole.php +++ /dev/null @@ -1,247 +0,0 @@ -__table = 'civicrm_user_role'; - parent::__construct(); - } - - /** - * Returns localized title of this entity. - * - * @param bool $plural - * Whether to return the plural version of the title. - */ - public static function getEntityTitle($plural = FALSE) { - return $plural ? E::ts('User Roles') : E::ts('User Role'); - } - - /** - * Returns foreign keys and entity references. - * - * @return array - * [CRM_Core_Reference_Interface] - */ - public static function getReferenceColumns() { - if (!isset(Civi::$statics[__CLASS__]['links'])) { - Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'user_id', 'civicrm_user', 'id'); - Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'role_id', 'civicrm_role', 'id'); - CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']); - } - return Civi::$statics[__CLASS__]['links']; - } - - /** - * Returns all the column names of this table - * - * @return array - */ - public static function &fields() { - if (!isset(Civi::$statics[__CLASS__]['fields'])) { - Civi::$statics[__CLASS__]['fields'] = [ - 'id' => [ - 'name' => 'id', - 'type' => CRM_Utils_Type::T_INT, - 'title' => E::ts('ID'), - 'description' => E::ts('Unique UserRole ID'), - 'required' => TRUE, - 'usage' => [ - 'import' => FALSE, - 'export' => FALSE, - 'duplicate_matching' => FALSE, - 'token' => FALSE, - ], - 'where' => 'civicrm_user_role.id', - 'table_name' => 'civicrm_user_role', - 'entity' => 'UserRole', - 'bao' => 'CRM_Standaloneusers_DAO_UserRole', - 'localizable' => 0, - 'html' => [ - 'type' => 'Number', - ], - 'readonly' => TRUE, - 'add' => NULL, - ], - 'user_id' => [ - 'name' => 'user_id', - 'type' => CRM_Utils_Type::T_INT, - 'title' => E::ts('User ID'), - 'description' => E::ts('FK to User'), - 'usage' => [ - 'import' => FALSE, - 'export' => FALSE, - 'duplicate_matching' => FALSE, - 'token' => FALSE, - ], - 'where' => 'civicrm_user_role.user_id', - 'table_name' => 'civicrm_user_role', - 'entity' => 'UserRole', - 'bao' => 'CRM_Standaloneusers_DAO_UserRole', - 'localizable' => 0, - 'FKClassName' => 'CRM_Standaloneusers_DAO_User', - 'add' => NULL, - ], - 'role_id' => [ - 'name' => 'role_id', - 'type' => CRM_Utils_Type::T_INT, - 'title' => E::ts('Role ID'), - 'description' => E::ts('FK to role'), - 'usage' => [ - 'import' => FALSE, - 'export' => FALSE, - 'duplicate_matching' => FALSE, - 'token' => FALSE, - ], - 'where' => 'civicrm_user_role.role_id', - 'table_name' => 'civicrm_user_role', - 'entity' => 'UserRole', - 'bao' => 'CRM_Standaloneusers_DAO_UserRole', - 'localizable' => 0, - 'FKClassName' => 'CRM_Standaloneusers_DAO_Role', - 'add' => NULL, - ], - ]; - CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); - } - return Civi::$statics[__CLASS__]['fields']; - } - - /** - * Return a mapping from field-name to the corresponding key (as used in fields()). - * - * @return array - * Array(string $name => string $uniqueName). - */ - public static function &fieldKeys() { - if (!isset(Civi::$statics[__CLASS__]['fieldKeys'])) { - Civi::$statics[__CLASS__]['fieldKeys'] = array_flip(CRM_Utils_Array::collect('name', self::fields())); - } - return Civi::$statics[__CLASS__]['fieldKeys']; - } - - /** - * Returns the names of this table - * - * @return string - */ - public static function getTableName() { - return self::$_tableName; - } - - /** - * Returns if this table needs to be logged - * - * @return bool - */ - public function getLog() { - return self::$_log; - } - - /** - * Returns the list of fields that can be imported - * - * @param bool $prefix - * - * @return array - */ - public static function &import($prefix = FALSE) { - $r = CRM_Core_DAO_AllCoreTables::getImports(__CLASS__, 'user_role', $prefix, []); - return $r; - } - - /** - * Returns the list of fields that can be exported - * - * @param bool $prefix - * - * @return array - */ - public static function &export($prefix = FALSE) { - $r = CRM_Core_DAO_AllCoreTables::getExports(__CLASS__, 'user_role', $prefix, []); - return $r; - } - - /** - * Returns the list of indices - * - * @param bool $localize - * - * @return array - */ - public static function indices($localize = TRUE) { - $indices = [ - 'index_user_role' => [ - 'name' => 'index_user_role', - 'field' => [ - 0 => 'user_id', - 1 => 'role_id', - ], - 'localizable' => FALSE, - 'sig' => 'civicrm_user_role::0::user_id::role_id', - ], - ]; - return ($localize && !empty($indices)) ? CRM_Core_DAO_AllCoreTables::multilingualize(__CLASS__, $indices) : $indices; - } - -} diff --git a/ext/standaloneusers/Civi/Api4/Role.php b/ext/standaloneusers/Civi/Api4/Role.php deleted file mode 100644 index 678779fa3a..0000000000 --- a/ext/standaloneusers/Civi/Api4/Role.php +++ /dev/null @@ -1,13 +0,0 @@ -addWhere('id', '=', $userID) + ->addSelect('roles')->execute()->first()['roles']; + + // artfulrobot: I think we should cache these per request, e.g. Civi::$statics? + // except in testing permissions shouldn't change during a request. @todo $found = \Civi\Api4\RolePermission::get(FALSE) ->selectRowCount() - ->addJoin('UserRole AS user_role', 'INNER', - ['role_id', '=', 'user_role.role_id'], - ['user_role.user_id', '=', $userID]) + ->addWhere('role_id', 'IN', $roleIDs) ->addWhere('permission', '=', $permissionName) ->execute()->countMatched(); return (bool) $found; diff --git a/ext/standaloneusers/ang/afformEditUserAccount.aff.html b/ext/standaloneusers/ang/afformEditUserAccount.aff.html index 4bde1ee768..739d072112 100644 --- a/ext/standaloneusers/ang/afformEditUserAccount.aff.html +++ b/ext/standaloneusers/ang/afformEditUserAccount.aff.html @@ -1,6 +1,7 @@
+ diff --git a/ext/standaloneusers/ang/afsearchAdministerUserAccounts.aff.html b/ext/standaloneusers/ang/afsearchAdministerUserAccounts.aff.html new file mode 100644 index 0000000000..fd9a7e7c9d --- /dev/null +++ b/ext/standaloneusers/ang/afsearchAdministerUserAccounts.aff.html @@ -0,0 +1,17 @@ +
+
+ + +

{{:: ts('User accounts allow people to access CiviCRM. What they can access is determined by which roles the users have, and what permissions are granted to those roles.') }}

+
+ + +
+
+ + + + +
+ +
diff --git a/ext/standaloneusers/ang/afsearchAdministerUserAccounts.aff.json b/ext/standaloneusers/ang/afsearchAdministerUserAccounts.aff.json new file mode 100644 index 0000000000..30cf8bf663 --- /dev/null +++ b/ext/standaloneusers/ang/afsearchAdministerUserAccounts.aff.json @@ -0,0 +1,23 @@ +{ + "type": "search", + "requires": [], + "entity_type": null, + "join_entity": null, + "title": "Administer User Accounts", + "description": "", + "is_dashlet": false, + "is_public": false, + "is_token": false, + "contact_summary": null, + "summary_contact_type": null, + "icon": "fa-users", + "server_route": "civicrm/admin/users", + "permission": "cms:administer users", + "redirect": null, + "create_submission": false, + "navigation": { + "parent": "Administer", + "label": "Administer User Accounts", + "weight": 0 + } +} diff --git a/ext/standaloneusers/ang/afsearchUsers.aff.html b/ext/standaloneusers/ang/afsearchUsers.aff.html deleted file mode 100644 index 8c24030e81..0000000000 --- a/ext/standaloneusers/ang/afsearchUsers.aff.html +++ /dev/null @@ -1,8 +0,0 @@ -
-
-
-
-
- -
- diff --git a/ext/standaloneusers/ang/afsearchUsers.aff.json b/ext/standaloneusers/ang/afsearchUsers.aff.json deleted file mode 100644 index f9c40c3bfb..0000000000 --- a/ext/standaloneusers/ang/afsearchUsers.aff.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "type": "search", - "title": "Users", - "description": "Administer users", - "icon": "fa-list-alt", - "server_route": "civicrm/admin/users", - "permission": "access CiviCRM" -} - diff --git a/ext/standaloneusers/managed/OptionGroup_role.mgd.php b/ext/standaloneusers/managed/OptionGroup_role.mgd.php new file mode 100644 index 0000000000..6cde7cb77b --- /dev/null +++ b/ext/standaloneusers/managed/OptionGroup_role.mgd.php @@ -0,0 +1,64 @@ + 'OptionGroup_role', + 'entity' => 'OptionGroup', + 'cleanup' => 'unused', + 'update' => 'unmodified', + 'params' => [ + 'version' => 4, + 'values' => [ + 'name' => 'role', + 'title' => E::ts('Role'), + 'description' => E::ts('A role is grants permissions to users in CiviCRM Standalone.'), + 'data_type' => 'Integer', + 'is_reserved' => TRUE, + 'is_active' => TRUE, + 'is_locked' => FALSE, + 'option_value_fields' => [ + 'label', + 'description', + 'icon', + 'color', + 'name', + ], + ], + 'match' => [ + 'name', + ], + ], + ], + [ + 'name' => 'OptionGroup_role_OptionValue_admin', + 'entity' => 'OptionValue', + 'cleanup' => 'unused', + 'update' => 'unmodified', + 'params' => [ + 'version' => 4, + 'values' => [ + 'option_group_id.name' => 'role', + 'label' => E::ts('Administrator'), + 'value' => '1', + 'name' => 'admin', + 'grouping' => NULL, + 'filter' => 0, + 'is_default' => FALSE, + 'description' => NULL, + 'is_optgroup' => FALSE, + 'is_reserved' => TRUE, + 'is_active' => TRUE, + 'component_id' => NULL, + 'domain_id' => NULL, + 'visibility_id' => NULL, + 'icon' => NULL, + 'color' => NULL, + ], + 'match' => [ + 'name', + 'option_group_id', + ], + ], + ], +]; diff --git a/ext/standaloneusers/sql/auto_install.sql b/ext/standaloneusers/sql/auto_install.sql index 8240223b45..c03740accf 100644 --- a/ext/standaloneusers/sql/auto_install.sql +++ b/ext/standaloneusers/sql/auto_install.sql @@ -17,10 +17,8 @@ SET FOREIGN_KEY_CHECKS=0; -DROP TABLE IF EXISTS `civicrm_user_role`; DROP TABLE IF EXISTS `civicrm_user`; DROP TABLE IF EXISTS `civicrm_role_permission`; -DROP TABLE IF EXISTS `civicrm_role`; SET FOREIGN_KEY_CHECKS=1; -- /******************************************************* @@ -29,21 +27,6 @@ SET FOREIGN_KEY_CHECKS=1; -- * -- *******************************************************/ --- /******************************************************* --- * --- * civicrm_role --- * --- * Permissions are assigned to roles which are assigned to users --- * --- *******************************************************/ -CREATE TABLE `civicrm_role` ( - `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'Unique Role ID', - `name` varchar(64) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE INDEX `UI_name`(name) -) -ENGINE=InnoDB; - -- /******************************************************* -- * -- * civicrm_role_permission @@ -53,10 +36,9 @@ ENGINE=InnoDB; -- *******************************************************/ CREATE TABLE `civicrm_role_permission` ( `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'Unique RolePermission ID', - `role_id` int unsigned COMMENT 'FK to Role', + `role_id` int unsigned COMMENT 'FK to a role option value', `permission` varchar(60) NOT NULL COMMENT 'A single permission granted to this role', - PRIMARY KEY (`id`), - CONSTRAINT FK_civicrm_role_permission_role_id FOREIGN KEY (`role_id`) REFERENCES `civicrm_role`(`id`) ON DELETE CASCADE + PRIMARY KEY (`id`) ) ENGINE=InnoDB; @@ -73,6 +55,7 @@ CREATE TABLE `civicrm_user` ( `username` varchar(60) NOT NULL, `password` varchar(128) NOT NULL COMMENT 'Hashed password', `email` varchar(255) NOT NULL COMMENT 'Email (e.g. for password resets)', + `roles` varchar(128) COMMENT 'FK to values from OptionGroup role', `when_created` timestamp DEFAULT CURRENT_TIMESTAMP, `when_last_accessed` timestamp NULL, `when_updated` timestamp NULL, @@ -84,21 +67,3 @@ CREATE TABLE `civicrm_user` ( CONSTRAINT FK_civicrm_user_contact_id FOREIGN KEY (`contact_id`) REFERENCES `civicrm_contact`(`id`) ON DELETE CASCADE ) ENGINE=InnoDB; - --- /******************************************************* --- * --- * civicrm_user_role --- * --- * Assigns Roles to Users --- * --- *******************************************************/ -CREATE TABLE `civicrm_user_role` ( - `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'Unique UserRole ID', - `user_id` int unsigned COMMENT 'FK to User', - `role_id` int unsigned COMMENT 'FK to role', - PRIMARY KEY (`id`), - INDEX `index_user_role`(user_id, role_id), - CONSTRAINT FK_civicrm_user_role_user_id FOREIGN KEY (`user_id`) REFERENCES `civicrm_user`(`id`) ON DELETE CASCADE, - CONSTRAINT FK_civicrm_user_role_role_id FOREIGN KEY (`role_id`) REFERENCES `civicrm_role`(`id`) ON DELETE CASCADE -) -ENGINE=InnoDB; diff --git a/ext/standaloneusers/sql/auto_uninstall.sql b/ext/standaloneusers/sql/auto_uninstall.sql index 8426d37129..6485a0f1ef 100644 --- a/ext/standaloneusers/sql/auto_uninstall.sql +++ b/ext/standaloneusers/sql/auto_uninstall.sql @@ -15,9 +15,7 @@ SET FOREIGN_KEY_CHECKS=0; -DROP TABLE IF EXISTS `civicrm_user_role`; DROP TABLE IF EXISTS `civicrm_user`; DROP TABLE IF EXISTS `civicrm_role_permission`; -DROP TABLE IF EXISTS `civicrm_role`; SET FOREIGN_KEY_CHECKS=1; \ No newline at end of file diff --git a/ext/standaloneusers/tests/phpunit/Civi/Standalone/SecurityTest.php b/ext/standaloneusers/tests/phpunit/Civi/Standalone/SecurityTest.php index 303a88a706..b37f5b0c97 100644 --- a/ext/standaloneusers/tests/phpunit/Civi/Standalone/SecurityTest.php +++ b/ext/standaloneusers/tests/phpunit/Civi/Standalone/SecurityTest.php @@ -30,6 +30,8 @@ class SecurityTest extends \PHPUnit\Framework\TestCase implements EndToEndInterf protected $contactID; protected $userID; + const ADMIN_ROLE_ID = 1; + public static function setUpBeforeClass(): void { parent::setUpBeforeClass(); \Civi\Test::e2e() @@ -70,14 +72,26 @@ class SecurityTest extends \PHPUnit\Framework\TestCase implements EndToEndInterf public function testPerms() { [$contactID, $userID, $security] = $this->createFixtureContactAndUser(); - // Create role, - $roleID = \Civi\Api4\Role::create(FALSE) - ->setValues(['name' => 'staff'])->execute()->first()['id']; - $this->assertGreaterThan(0, $roleID); - // Assign role to user - \Civi\Api4\UserRole::create(FALSE) - ->setValues(['user_id' => $userID, 'role_id' => $roleID])->execute(); + // Create a custom role + $roleID = \Civi\Api4\OptionValue::create(FALSE) + ->setValues([ + 'option_group_id.name' => 'role', + 'name' => 'demo_role', + 'label' => 'demo_role', + ])->execute()->first()['value']; + + // Give our user this role only. + \Civi\Api4\User::update(FALSE) + ->addValue('roles', [$roleID]) + ->addWhere('id', '=', $userID) + ->execute(); + + $existingPermissions = \Civi\Api4\RolePermission::get(FALSE) + ->selectRowCount() + ->addWhere('role_id', '=', $demoRoleID) + ->execute()->count(); + $this->assertEquals(0, $existingPermissions); // Assign some permissions to the role. \Civi\Api4\RolePermission::save(FALSE) @@ -140,6 +154,7 @@ class SecurityTest extends \PHPUnit\Framework\TestCase implements EndToEndInterf $this->assertGreaterThan(0, $userID); $this->contactID = $contactID; $this->userID = $userID; + return [$contactID, $userID, $security]; } diff --git a/ext/standaloneusers/xml/schema/CRM/Standaloneusers/Role.entityType.php b/ext/standaloneusers/xml/schema/CRM/Standaloneusers/Role.entityType.php deleted file mode 100644 index 881922985e..0000000000 --- a/ext/standaloneusers/xml/schema/CRM/Standaloneusers/Role.entityType.php +++ /dev/null @@ -1,10 +0,0 @@ - 'Role', - 'class' => 'CRM_Standaloneusers_DAO_Role', - 'table' => 'civicrm_role', - ], -]; diff --git a/ext/standaloneusers/xml/schema/CRM/Standaloneusers/Role.xml b/ext/standaloneusers/xml/schema/CRM/Standaloneusers/Role.xml deleted file mode 100644 index 4e3c532799..0000000000 --- a/ext/standaloneusers/xml/schema/CRM/Standaloneusers/Role.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - CRM/Standaloneusers - Role - civicrm_role - Permissions are assigned to roles which are assigned to users - true - name - - - id - int unsigned - true - Unique Role ID - - Number - - - - id - true - - - - name - varchar - true - 64 - - - UI_name - true - name - - -
diff --git a/ext/standaloneusers/xml/schema/CRM/Standaloneusers/RolePermission.xml b/ext/standaloneusers/xml/schema/CRM/Standaloneusers/RolePermission.xml index 3a95370f02..7d6abf6487 100644 --- a/ext/standaloneusers/xml/schema/CRM/Standaloneusers/RolePermission.xml +++ b/ext/standaloneusers/xml/schema/CRM/Standaloneusers/RolePermission.xml @@ -24,14 +24,11 @@ role_id int unsigned - FK to Role + FK to a role option value + + role + - - role_id - civicrm_role
- id - CASCADE -
permission diff --git a/ext/standaloneusers/xml/schema/CRM/Standaloneusers/User.xml b/ext/standaloneusers/xml/schema/CRM/Standaloneusers/User.xml index 45d396ffef..db7b91cde0 100644 --- a/ext/standaloneusers/xml/schema/CRM/Standaloneusers/User.xml +++ b/ext/standaloneusers/xml/schema/CRM/Standaloneusers/User.xml @@ -75,6 +75,21 @@ + + roles + varchar + Roles + 128 + FK to values from OptionGroup role + + role + + + Select + + SEPARATOR_BOOKEND + + when_created timestamp diff --git a/ext/standaloneusers/xml/schema/CRM/Standaloneusers/UserRole.entityType.php b/ext/standaloneusers/xml/schema/CRM/Standaloneusers/UserRole.entityType.php deleted file mode 100644 index 3ef64043b2..0000000000 --- a/ext/standaloneusers/xml/schema/CRM/Standaloneusers/UserRole.entityType.php +++ /dev/null @@ -1,10 +0,0 @@ - 'UserRole', - 'class' => 'CRM_Standaloneusers_DAO_UserRole', - 'table' => 'civicrm_user_role', - ], -]; diff --git a/ext/standaloneusers/xml/schema/CRM/Standaloneusers/UserRole.xml b/ext/standaloneusers/xml/schema/CRM/Standaloneusers/UserRole.xml deleted file mode 100644 index 0ebb663db8..0000000000 --- a/ext/standaloneusers/xml/schema/CRM/Standaloneusers/UserRole.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - CRM/Standaloneusers - UserRole - civicrm_user_role - Assigns Roles to Users - true - - - id - int unsigned - true - Unique UserRole ID - - Number - - - - id - true - - - - user_id - int unsigned - FK to User - - - user_id -
civicrm_user
- id - CASCADE - - - - role_id - int unsigned - FK to role - - - role_id - civicrm_role
- id - CASCADE -
- - - index_user_role - user_id - role_id - - - diff --git a/setup/plugins/init/StandaloneUsers.civi-setup.php b/setup/plugins/init/StandaloneUsers.civi-setup.php index 74507d99e5..8c2e1395a2 100644 --- a/setup/plugins/init/StandaloneUsers.civi-setup.php +++ b/setup/plugins/init/StandaloneUsers.civi-setup.php @@ -2,7 +2,7 @@ /** * @file * - * On "Standalone" UF, default policy is to enable `standaloneusers` and create user+role. + * On "Standalone" UF, default policy is to enable `standaloneusers` and create user with admin role. */ if (!defined('CIVI_SETUP')) { @@ -28,6 +28,7 @@ if (!defined('CIVI_SETUP')) { 'adminPass' => $toAlphanum(random_bytes(8)), 'adminEmail' => 'admin@localhost.localdomain', ]; + $e->getModel()->extras['adminPassWasSpecified'] = !empty($e->getModel()->extras['adminPass']); $e->getModel()->extras = array_merge($defaults, $e->getModel()->extras); }); @@ -39,8 +40,8 @@ if (!defined('CIVI_SETUP')) { \Civi\Setup::log()->info(sprintf('[%s] Handle %s', basename(__FILE__), 'installDatabase')); - // Create role with permissions - $roleID = \Civi\Api4\Role::create(FALSE)->setValues(['name' => 'Administrator'])->execute()->first()['id']; + $roleID = 1; // admin should always be role 1 on install. + // @todo I expect there's a better way than this; this doesn't even bring in all the permissions. $records = [['permission' => 'authenticate with password']]; foreach (array_keys(\CRM_Core_Permission::getCorePermissions()) as $permission) { @@ -69,11 +70,16 @@ if (!defined('CIVI_SETUP')) { ]; $userID = \CRM_Core_BAO_CMSUser::create($params, $adminEmail); - // Assign role to user - \Civi\Api4\UserRole::create(FALSE)->setValues(['role_id' => $roleID, 'user_id' => $userID])->execute(); + // Assign 'admin' role to user + \Civi\Api4\User::update(FALSE) + ->addWhere('id', '=', $userID) + ->addValue('roles', [$roleID]) + ->execute(); - // TODO - If admin specified an explicit password, then we don't really need to log it. - $message = "Created new user \"{$e->getModel()->extras['adminUser']}\" (user ID #$userID, contact ID #$contactID) with password \"" . ($e->getModel()->extras['adminPass']) . "\" and ALL permissions."; + $message = "Created new user \"{$e->getModel()->extras['adminUser']}\" (user ID #$userID, contact ID #$contactID) with 'admin' role and "; + $message .= empty($e->getModel()->extras['adminPassWasSpecified']) + ? "random password \"" . ($e->getModel()->extras['adminPass']) + : "specified password"; \Civi::log()->notice($message); }, \Civi\Setup::PRIORITY_LATE); -- 2.25.1