From e0e2084422e306aa2eab4051383ad3aee2cdecce Mon Sep 17 00:00:00 2001 From: Alexandre Bodin Date: Mon, 6 Mar 2023 21:46:45 +0100 Subject: [PATCH 1/8] Move RBAC into CE --- .../core/admin/ee/server/controllers/index.js | 6 +- .../admin/ee/server/controllers/permission.js | 21 - .../core/admin/ee/server/controllers/role.js | 39 - .../core/admin/ee/server/controllers/user.js | 36 +- packages/core/admin/ee/server/routes/index.js | 96 +- .../admin/ee/server/validation/permission.js | 8 - .../core/admin/ee/server/validation/role.js | 48 +- packages/core/admin/server/bootstrap.js | 1 - .../admin/server/controllers/permission.js | 6 +- .../core/admin/server/controllers/role.js | 93 +- packages/core/admin/server/domain/role.js | 29 - packages/core/admin/server/routes/roles.js | 48 + .../server/services/permission/queries.js | 75 +- .../admin/server/tests/admin-auth.test.api.js | 18 +- .../server/tests/admin-permission.test.api.js | 2120 +++++++---------- .../admin-permissions-conditions.test.api.js | 312 ++- .../admin/server/tests/admin-role.test.api.js | 1102 ++++----- .../admin/server/tests/admin-user.test.api.js | 13 +- .../admin/server/validation/permission.js | 84 +- packages/core/admin/server/validation/role.js | 44 + 20 files changed, 1723 insertions(+), 2476 deletions(-) delete mode 100644 packages/core/admin/ee/server/controllers/permission.js delete mode 100644 packages/core/admin/ee/server/validation/permission.js delete mode 100644 packages/core/admin/server/domain/role.js diff --git a/packages/core/admin/ee/server/controllers/index.js b/packages/core/admin/ee/server/controllers/index.js index 26a0bb79ad0..fd8d5d1e22e 100644 --- a/packages/core/admin/ee/server/controllers/index.js +++ b/packages/core/admin/ee/server/controllers/index.js @@ -2,9 +2,9 @@ module.exports = { authentication: require('./authentication'), - permission: require('./permission'), - role: require('./role'), - user: require('./user'), + // permission: require('./permission'), + // role: require('./role'), + // user: require('./user'), auditLogs: require('./audit-logs'), admin: require('./admin'), }; diff --git a/packages/core/admin/ee/server/controllers/permission.js b/packages/core/admin/ee/server/controllers/permission.js deleted file mode 100644 index f000e1d53e3..00000000000 --- a/packages/core/admin/ee/server/controllers/permission.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -const { getService } = require('../../../server/utils'); -const { formatConditions } = require('../../../server/controllers/formatters'); - -module.exports = { - async getAll(ctx) { - const { sectionsBuilder, actionProvider, conditionProvider } = getService('permission'); - - const actions = actionProvider.values(); - const conditions = conditionProvider.values(); - const sections = await sectionsBuilder.build(actions); - - ctx.body = { - data: { - conditions: formatConditions(conditions), - sections, - }, - }; - }, -}; diff --git a/packages/core/admin/ee/server/controllers/role.js b/packages/core/admin/ee/server/controllers/role.js index b025914dd02..67ace666ed2 100644 --- a/packages/core/admin/ee/server/controllers/role.js +++ b/packages/core/admin/ee/server/controllers/role.js @@ -1,14 +1,11 @@ 'use strict'; -const { ApplicationError } = require('@strapi/utils').errors; const { validateRoleCreateInput, validateRoleDeleteInput, validateRolesDeleteInput, } = require('../validation/role'); const { getService } = require('../../../server/utils'); -const { validatedUpdatePermissionsInput } = require('../validation/permission'); -const { SUPER_ADMIN_CODE } = require('../../../server/services/constants'); module.exports = { /** @@ -64,40 +61,4 @@ module.exports = { data: sanitizedRoles, }); }, - - /** - * Updates the permissions assigned to a role - * @param {KoaContext} ctx - koa context - */ - async updatePermissions(ctx) { - const { id } = ctx.params; - const { body: input } = ctx.request; - - const roleService = getService('role'); - const permissionService = getService('permission'); - - const role = await roleService.findOne({ id }); - - if (!role) { - return ctx.notFound('role.notFound'); - } - - if (role.code === SUPER_ADMIN_CODE) { - throw new ApplicationError("Super admin permissions can't be edited."); - } - - await validatedUpdatePermissionsInput(input); - - if (!role) { - return ctx.notFound('role.notFound'); - } - - const permissions = await roleService.assignPermissions(role.id, input.permissions); - - const sanitizedPermissions = permissions.map(permissionService.sanitizePermission); - - return ctx.send({ - data: sanitizedPermissions, - }); - }, }; diff --git a/packages/core/admin/ee/server/controllers/user.js b/packages/core/admin/ee/server/controllers/user.js index 902ff4cb42f..4bd2014951b 100644 --- a/packages/core/admin/ee/server/controllers/user.js +++ b/packages/core/admin/ee/server/controllers/user.js @@ -6,10 +6,7 @@ const _ = require('lodash'); const { pick, isNil } = require('lodash/fp'); const { ApplicationError, ForbiddenError } = require('@strapi/utils').errors; const { validateUserCreationInput } = require('../validation/user'); -const { - validateUserUpdateInput, - validateUsersDeleteInput, -} = require('../../../server/validation/user'); +const { validateUserUpdateInput } = require('../../../server/validation/user'); const { getService } = require('../../../server/utils'); const pickUserCreationAttributes = pick(['firstname', 'lastname', 'email', 'roles']); @@ -98,35 +95,4 @@ module.exports = { data: getService('user').sanitizeUser(updatedUser), }; }, - - async deleteOne(ctx) { - const { id } = ctx.params; - - const deletedUser = await getService('user').deleteById(id); - - if (!deletedUser) { - return ctx.notFound('User not found'); - } - - return ctx.deleted({ - data: getService('user').sanitizeUser(deletedUser), - }); - }, - - /** - * Delete several users - * @param {KoaContext} ctx - koa context - */ - async deleteMany(ctx) { - const { body } = ctx.request; - await validateUsersDeleteInput(body); - - const users = await getService('user').deleteByIds(body.ids); - - const sanitizedUsers = users.map(getService('user').sanitizeUser); - - return ctx.deleted({ - data: sanitizedUsers, - }); - }, }; diff --git a/packages/core/admin/ee/server/routes/index.js b/packages/core/admin/ee/server/routes/index.js index a684354b459..857423dd89b 100644 --- a/packages/core/admin/ee/server/routes/index.js +++ b/packages/core/admin/ee/server/routes/index.js @@ -11,54 +11,54 @@ const enableFeatureMiddleware = (featureName) => (ctx, next) => { }; module.exports = [ - { - method: 'POST', - path: '/roles', - handler: 'role.create', - config: { - policies: [ - 'admin::isAuthenticatedAdmin', - { - name: 'admin::hasPermissions', - config: { - actions: ['admin::roles.create'], - }, - }, - ], - }, - }, - { - method: 'DELETE', - path: '/roles/:id', - handler: 'role.deleteOne', - config: { - policies: [ - 'admin::isAuthenticatedAdmin', - { - name: 'admin::hasPermissions', - config: { - actions: ['admin::roles.delete'], - }, - }, - ], - }, - }, - { - method: 'POST', - path: '/roles/batch-delete', - handler: 'role.deleteMany', - config: { - policies: [ - 'admin::isAuthenticatedAdmin', - { - name: 'admin::hasPermissions', - config: { - actions: ['admin::roles.delete'], - }, - }, - ], - }, - }, + // { + // method: 'POST', + // path: '/roles', + // handler: 'role.create', + // config: { + // policies: [ + // 'admin::isAuthenticatedAdmin', + // { + // name: 'admin::hasPermissions', + // config: { + // actions: ['admin::roles.create'], + // }, + // }, + // ], + // }, + // }, + // { + // method: 'DELETE', + // path: '/roles/:id', + // handler: 'role.deleteOne', + // config: { + // policies: [ + // 'admin::isAuthenticatedAdmin', + // { + // name: 'admin::hasPermissions', + // config: { + // actions: ['admin::roles.delete'], + // }, + // }, + // ], + // }, + // }, + // { + // method: 'POST', + // path: '/roles/batch-delete', + // handler: 'role.deleteMany', + // config: { + // policies: [ + // 'admin::isAuthenticatedAdmin', + // { + // name: 'admin::hasPermissions', + // config: { + // actions: ['admin::roles.delete'], + // }, + // }, + // ], + // }, + // }, // SSO { diff --git a/packages/core/admin/ee/server/validation/permission.js b/packages/core/admin/ee/server/validation/permission.js deleted file mode 100644 index 4712a1ade29..00000000000 --- a/packages/core/admin/ee/server/validation/permission.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; - -const { validateYupSchema } = require('@strapi/utils'); -const validators = require('../../../server/validation/common-validators'); - -module.exports = { - validatedUpdatePermissionsInput: validateYupSchema(validators.updatePermissions), -}; diff --git a/packages/core/admin/ee/server/validation/role.js b/packages/core/admin/ee/server/validation/role.js index fa8a3f40185..4538c170b97 100644 --- a/packages/core/admin/ee/server/validation/role.js +++ b/packages/core/admin/ee/server/validation/role.js @@ -20,38 +20,46 @@ const rolesDeleteSchema = yup .of(yup.strapiID()) .min(1) .required() - .test('roles-deletion-checks', 'Roles deletion checks have failed', async function (ids) { - try { - await strapi.admin.services.role.checkRolesIdForDeletion(ids); + .test( + 'roles-deletion-checks', + 'Roles deletion checks have failed', + async function rolesDeletionChecks(ids) { + try { + await strapi.admin.services.role.checkRolesIdForDeletion(ids); - if (features.isEnabled('sso')) { - await strapi.admin.services.role.ssoCheckRolesIdForDeletion(ids); + if (features.isEnabled('sso')) { + await strapi.admin.services.role.ssoCheckRolesIdForDeletion(ids); + } + } catch (e) { + return this.createError({ path: 'ids', message: e.message }); } - } catch (e) { - return this.createError({ path: 'ids', message: e.message }); - } - return true; - }), + return true; + } + ), }) .noUnknown(); const roleDeleteSchema = yup .strapiID() .required() - .test('no-admin-single-delete', 'Role deletion checks have failed', async function (id) { - try { - await strapi.admin.services.role.checkRolesIdForDeletion([id]); + .test( + 'no-admin-single-delete', + 'Role deletion checks have failed', + async function noAdminSingleDelete(id) { + try { + await strapi.admin.services.role.checkRolesIdForDeletion([id]); - if (features.isEnabled('sso')) { - await strapi.admin.services.role.ssoCheckRolesIdForDeletion([id]); + if (features.isEnabled('sso')) { + await strapi.admin.services.role.ssoCheckRolesIdForDeletion([id]); + } + } catch (e) { + return this.createError({ path: 'id', message: e.message }); } - } catch (e) { - return this.createError({ path: 'id', message: e.message }); - } - return true; - }); + return true; + } + ); module.exports = { validateRoleCreateInput: validateYupSchema(roleCreateSchema), diff --git a/packages/core/admin/server/bootstrap.js b/packages/core/admin/server/bootstrap.js index e04c989bb0a..48f1f1ae304 100644 --- a/packages/core/admin/server/bootstrap.js +++ b/packages/core/admin/server/bootstrap.js @@ -85,7 +85,6 @@ module.exports = async () => { await roleService.resetSuperAdminPermissions(); await roleService.displayWarningIfNoSuperAdmin(); - await permissionService.ensureBoundPermissionsInDatabase(); await permissionService.cleanPermissionsInDatabase(); await userService.displayWarningIfUsersDontHaveRole(); diff --git a/packages/core/admin/server/controllers/permission.js b/packages/core/admin/server/controllers/permission.js index 481f5adb415..84b3c803798 100644 --- a/packages/core/admin/server/controllers/permission.js +++ b/packages/core/admin/server/controllers/permission.js @@ -29,11 +29,9 @@ module.exports = { * @param {KoaContext} ctx - koa context */ async getAll(ctx) { - const { role: roleId } = ctx.query; + const { sectionsBuilder, actionProvider, conditionProvider } = getService('permission'); - const { sectionsBuilder, conditionProvider } = getService('permission'); - - const actions = await getService('action').getAllowedActionsForRole(roleId); + const actions = actionProvider.values(); const conditions = conditionProvider.values(); const sections = await sectionsBuilder.build(actions); diff --git a/packages/core/admin/server/controllers/role.js b/packages/core/admin/server/controllers/role.js index 013c91cf47d..03c4b44644a 100644 --- a/packages/core/admin/server/controllers/role.js +++ b/packages/core/admin/server/controllers/role.js @@ -1,12 +1,32 @@ 'use strict'; const { ApplicationError } = require('@strapi/utils').errors; -const { validateRoleUpdateInput } = require('../validation/role'); +const { + validateRoleUpdateInput, + validateRoleCreateInput, + validateRoleDeleteInput, + validateRolesDeleteInput, +} = require('../validation/role'); const { validatedUpdatePermissionsInput } = require('../validation/permission'); -const { EDITOR_CODE, AUTHOR_CODE, SUPER_ADMIN_CODE } = require('../services/constants'); +const { SUPER_ADMIN_CODE } = require('../services/constants'); const { getService } = require('../utils'); module.exports = { + /** + * Create a new role + * @param {KoaContext} ctx - koa context + */ + async create(ctx) { + await validateRoleCreateInput(ctx.request.body); + + const roleService = getService('role'); + + const role = await roleService.create(ctx.request.body); + const sanitizedRole = roleService.sanitizeRole(role); + + ctx.created({ data: sanitizedRole }); + }, + /** * Returns on role by id * @param {KoaContext} ctx - koa context @@ -99,10 +119,10 @@ module.exports = { const { id } = ctx.params; const { body: input } = ctx.request; - const { findOne, assignPermissions } = getService('role'); - const { sanitizePermission, actionProvider } = getService('permission'); + const roleService = getService('role'); + const permissionService = getService('permission'); - const role = await findOne({ id }); + const role = await roleService.findOne({ id }); if (!role) { return ctx.notFound('role.notFound'); @@ -112,30 +132,57 @@ module.exports = { throw new ApplicationError("Super admin permissions can't be edited."); } - await validatedUpdatePermissionsInput(input, role); + await validatedUpdatePermissionsInput(input); - let permissionsToAssign; + if (!role) { + return ctx.notFound('role.notFound'); + } - if ([EDITOR_CODE, AUTHOR_CODE].includes(role.code)) { - permissionsToAssign = input.permissions.map((permission) => { - const action = actionProvider.get(permission.action); + const permissions = await roleService.assignPermissions(role.id, input.permissions); - if (action.section !== 'contentTypes') { - return permission; - } + const sanitizedPermissions = permissions.map(permissionService.sanitizePermission); - const conditions = role.code === AUTHOR_CODE ? ['admin::is-creator'] : []; + return ctx.send({ + data: sanitizedPermissions, + }); + }, - return { ...permission, conditions }; - }); - } else { - permissionsToAssign = input.permissions; - } + /** + * Delete a role + * @param {KoaContext} ctx - koa context + */ + async deleteOne(ctx) { + const { id } = ctx.params; - const permissions = await assignPermissions(role.id, permissionsToAssign); + await validateRoleDeleteInput(id); - ctx.body = { - data: permissions.map(sanitizePermission), - }; + const roleService = getService('role'); + + const roles = await roleService.deleteByIds([id]); + + const sanitizedRole = roles.map((role) => roleService.sanitizeRole(role))[0] || null; + + return ctx.deleted({ + data: sanitizedRole, + }); + }, + + /** + * delete several roles + * @param {KoaContext} ctx - koa context + */ + async deleteMany(ctx) { + const { body } = ctx.request; + + await validateRolesDeleteInput(body); + + const roleService = getService('role'); + + const roles = await roleService.deleteByIds(body.ids); + const sanitizedRoles = roles.map(roleService.sanitizeRole); + + return ctx.deleted({ + data: sanitizedRoles, + }); }, }; diff --git a/packages/core/admin/server/domain/role.js b/packages/core/admin/server/domain/role.js deleted file mode 100644 index 773558aac82..00000000000 --- a/packages/core/admin/server/domain/role.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -const { - contentTypes: { hasDraftAndPublish }, -} = require('@strapi/utils'); -const { - AUTHOR_CODE, - PUBLISH_ACTION, - DELETE_ACTION, - UPDATE_ACTION, - CREATE_ACTION, - READ_ACTION, -} = require('../services/constants'); - -const BOUND_ACTIONS = [READ_ACTION, CREATE_ACTION, UPDATE_ACTION, DELETE_ACTION, PUBLISH_ACTION]; - -const BOUND_ACTIONS_FOR_FIELDS = [READ_ACTION, CREATE_ACTION, UPDATE_ACTION]; - -const getBoundActionsBySubject = (role, subject) => { - const model = strapi.contentTypes[subject]; - - if (role.code === AUTHOR_CODE || !hasDraftAndPublish(model)) { - return [READ_ACTION, UPDATE_ACTION, CREATE_ACTION, DELETE_ACTION]; - } - - return BOUND_ACTIONS; -}; - -module.exports = { getBoundActionsBySubject, BOUND_ACTIONS, BOUND_ACTIONS_FOR_FIELDS }; diff --git a/packages/core/admin/server/routes/roles.js b/packages/core/admin/server/routes/roles.js index 991e0b77f06..3ff5b9b5932 100644 --- a/packages/core/admin/server/routes/roles.js +++ b/packages/core/admin/server/routes/roles.js @@ -45,6 +45,22 @@ module.exports = [ ], }, }, + { + method: 'POST', + path: '/roles', + handler: 'role.create', + config: { + policies: [ + 'admin::isAuthenticatedAdmin', + { + name: 'admin::hasPermissions', + config: { + actions: ['admin::roles.create'], + }, + }, + ], + }, + }, { method: 'PUT', path: '/roles/:id', @@ -56,4 +72,36 @@ module.exports = [ ], }, }, + { + method: 'DELETE', + path: '/roles/:id', + handler: 'role.deleteOne', + config: { + policies: [ + 'admin::isAuthenticatedAdmin', + { + name: 'admin::hasPermissions', + config: { + actions: ['admin::roles.delete'], + }, + }, + ], + }, + }, + { + method: 'POST', + path: '/roles/batch-delete', + handler: 'role.deleteMany', + config: { + policies: [ + 'admin::isAuthenticatedAdmin', + { + name: 'admin::hasPermissions', + config: { + actions: ['admin::roles.delete'], + }, + }, + ], + }, + }, ]; diff --git a/packages/core/admin/server/services/permission/queries.js b/packages/core/admin/server/services/permission/queries.js index 6b64367eb5a..58930a0541e 100644 --- a/packages/core/admin/server/services/permission/queries.js +++ b/packages/core/admin/server/services/permission/queries.js @@ -1,22 +1,7 @@ 'use strict'; -const { - flatMap, - reject, - isNil, - isArray, - prop, - xor, - eq, - uniq, - map, - difference, - differenceWith, - pipe, -} = require('lodash/fp'); +const { isNil, isArray, prop, xor, eq, map, differenceWith } = require('lodash/fp'); const pmap = require('p-map'); -const { EDITOR_CODE } = require('../constants'); -const { getBoundActionsBySubject, BOUND_ACTIONS_FOR_FIELDS } = require('../../domain/role'); const { getService } = require('../../utils'); const permissionDomain = require('../../domain/permission/index'); @@ -195,63 +180,6 @@ const cleanPermissionsInDatabase = async () => { } }; -const ensureBoundPermissionsInDatabase = async () => { - if (strapi.EE) { - return; - } - - const contentTypes = Object.values(strapi.contentTypes); - const editorRole = await strapi.query('admin::role').findOne({ - where: { code: EDITOR_CODE }, - }); - - if (isNil(editorRole)) { - return; - } - - for (const contentType of contentTypes) { - const boundActions = getBoundActionsBySubject(editorRole, contentType.uid); - - const permissions = await findMany({ - where: { - subject: contentType.uid, - action: boundActions, - role: { id: editorRole.id }, - }, - }); - - if (permissions.length === 0) { - return; - } - - const fields = pipe( - flatMap(permissionDomain.getProperty('fields')), - reject(isNil), - uniq - )(permissions); - - // Handle the scenario where permissions are missing - const missingActions = difference(map('action', permissions), boundActions); - - if (missingActions.length > 0) { - const permissions = pipe( - // Create a permission skeleton from the action id - map((action) => ({ action, subject: contentType.uid, role: editorRole.id })), - // Use the permission domain to create a clean permission from the given object - map(permissionDomain.create), - // Adds the fields property if the permission action is eligible - map((permission) => - BOUND_ACTIONS_FOR_FIELDS.includes(permission.action) - ? permissionDomain.setProperty('fields', fields, permission) - : permission - ) - )(missingActions); - - await createMany(permissions); - } - } -}; - module.exports = { createMany, findMany, @@ -259,5 +187,4 @@ module.exports = { deleteByIds, findUserPermissions, cleanPermissionsInDatabase, - ensureBoundPermissionsInDatabase, }; diff --git a/packages/core/admin/server/tests/admin-auth.test.api.js b/packages/core/admin/server/tests/admin-auth.test.api.js index 7a38beb353b..8855f02b262 100644 --- a/packages/core/admin/server/tests/admin-auth.test.api.js +++ b/packages/core/admin/server/tests/admin-auth.test.api.js @@ -5,8 +5,6 @@ const { createAuthRequest } = require('../../../../../test/helpers/request'); const { createStrapiInstance, superAdmin } = require('../../../../../test/helpers/strapi'); const { createUtils } = require('../../../../../test/helpers/utils'); -const edition = process.env.STRAPI_DISABLE_EE === 'true' ? 'CE' : 'EE'; - const internals = { role: null, }; @@ -21,20 +19,14 @@ describe('Admin Auth End to End', () => { rq = await createAuthRequest({ strapi }); utils = createUtils(strapi); - if (edition === 'EE') { - internals.role = await utils.createRole({ - name: 'auth_test_role', - description: 'Only used for auth crud test (api)', - }); - } else { - internals.role = await utils.getSuperAdminRole(); - } + internals.role = await utils.createRole({ + name: 'auth_test_role', + description: 'Only used for auth crud test (api)', + }); }); afterAll(async () => { - if (edition === 'EE') { - await utils.deleteRolesById([internals.role.id]); - } + await utils.deleteRolesById([internals.role.id]); await strapi.destroy(); }); diff --git a/packages/core/admin/server/tests/admin-permission.test.api.js b/packages/core/admin/server/tests/admin-permission.test.api.js index 8e2b03d7b7f..f41dcf2c412 100644 --- a/packages/core/admin/server/tests/admin-permission.test.api.js +++ b/packages/core/admin/server/tests/admin-permission.test.api.js @@ -5,8 +5,6 @@ const _ = require('lodash'); const { createAuthRequest } = require('../../../../../test/helpers/request'); const { createStrapiInstance } = require('../../../../../test/helpers/strapi'); -const edition = process.env.STRAPI_DISABLE_EE === 'true' ? 'CE' : 'EE'; - describe('Role CRUD End to End', () => { let rq; let strapi; @@ -35,1244 +33,169 @@ describe('Role CRUD End to End', () => { }); sortedData.conditions = sortedData.conditions.sort(); - if (edition === 'CE') { + // eslint-disable-next-line node/no-extraneous-require + const { features } = require('@strapi/strapi/lib/utils/ee'); + const hasSSO = features.isEnabled('sso'); + + if (hasSSO) { expect(sortedData).toMatchInlineSnapshot(` - { - "conditions": [ - { - "category": "default", - "displayName": "Is creator", - "id": "admin::is-creator", - }, - { - "category": "default", - "displayName": "Has same role as creator", - "id": "admin::has-same-role-as-creator", - }, - ], - "sections": { - "collectionTypes": [ - [ + { + "conditions": [ + { + "category": "default", + "displayName": "Is creator", + "id": "admin::is-creator", + }, + { + "category": "default", + "displayName": "Has same role as creator", + "id": "admin::has-same-role-as-creator", + }, + ], + "sections": { + "collectionTypes": [ + [ + { + "actionId": "plugin::content-manager.explorer.create", + "applyToProperties": [ + "fields", + "locales", + ], + "label": "Create", + "subjects": [ + "plugin::users-permissions.user", + ], + }, + { + "actionId": "plugin::content-manager.explorer.read", + "applyToProperties": [ + "fields", + "locales", + ], + "label": "Read", + "subjects": [ + "plugin::users-permissions.user", + ], + }, + { + "actionId": "plugin::content-manager.explorer.update", + "applyToProperties": [ + "fields", + "locales", + ], + "label": "Update", + "subjects": [ + "plugin::users-permissions.user", + ], + }, + { + "actionId": "plugin::content-manager.explorer.delete", + "applyToProperties": [ + "locales", + ], + "label": "Delete", + "subjects": [ + "plugin::users-permissions.user", + ], + }, + { + "actionId": "plugin::content-manager.explorer.publish", + "applyToProperties": [ + "locales", + ], + "label": "Publish", + "subjects": [], + }, + ], + [ + { + "label": "user", + "properties": [ + { + "children": [ + { + "label": "username", + "required": true, + "value": "username", + }, + { + "label": "email", + "required": true, + "value": "email", + }, + { + "label": "provider", + "value": "provider", + }, + { + "label": "password", + "value": "password", + }, + { + "label": "resetPasswordToken", + "value": "resetPasswordToken", + }, + { + "label": "confirmationToken", + "value": "confirmationToken", + }, + { + "label": "confirmed", + "value": "confirmed", + }, + { + "label": "blocked", + "value": "blocked", + }, + { + "label": "role", + "value": "role", + }, + ], + "label": "Fields", + "value": "fields", + }, + ], + "uid": "plugin::users-permissions.user", + }, + ], + ], + "plugins": [ { - "actionId": "plugin::content-manager.explorer.create", - "applyToProperties": [ - "fields", - "locales", - ], - "label": "Create", - "subjects": [ - "plugin::users-permissions.user", - ], + "action": "plugin::content-manager.collection-types.configure-view", + "displayName": "Configure view", + "plugin": "content-manager", + "subCategory": "collection types", }, { - "actionId": "plugin::content-manager.explorer.read", - "applyToProperties": [ - "fields", - "locales", - ], - "label": "Read", - "subjects": [ - "plugin::users-permissions.user", - ], + "action": "plugin::content-manager.components.configure-layout", + "displayName": "Configure Layout", + "plugin": "content-manager", + "subCategory": "components", }, { - "actionId": "plugin::content-manager.explorer.update", - "applyToProperties": [ - "fields", - "locales", - ], - "label": "Update", - "subjects": [ - "plugin::users-permissions.user", - ], + "action": "plugin::content-manager.single-types.configure-view", + "displayName": "Configure view", + "plugin": "content-manager", + "subCategory": "single types", }, { - "actionId": "plugin::content-manager.explorer.delete", - "applyToProperties": [ - "locales", - ], - "label": "Delete", - "subjects": [ - "plugin::users-permissions.user", - ], + "action": "plugin::content-type-builder.read", + "displayName": "Read", + "plugin": "content-type-builder", + "subCategory": "general", }, { - "actionId": "plugin::content-manager.explorer.publish", - "applyToProperties": [ - "locales", - ], - "label": "Publish", - "subjects": [], + "action": "plugin::documentation.read", + "displayName": "Access the Documentation", + "plugin": "documentation", + "subCategory": "general", }, - ], - [ { - "label": "user", - "properties": [ - { - "children": [ - { - "label": "username", - "required": true, - "value": "username", - }, - { - "label": "email", - "required": true, - "value": "email", - }, - { - "label": "provider", - "value": "provider", - }, - { - "label": "password", - "value": "password", - }, - { - "label": "resetPasswordToken", - "value": "resetPasswordToken", - }, - { - "label": "confirmationToken", - "value": "confirmationToken", - }, - { - "label": "confirmed", - "value": "confirmed", - }, - { - "label": "blocked", - "value": "blocked", - }, - { - "label": "role", - "value": "role", - }, - ], - "label": "Fields", - "value": "fields", - }, - ], - "uid": "plugin::users-permissions.user", - }, - ], - ], - "plugins": [ - { - "action": "plugin::content-manager.collection-types.configure-view", - "displayName": "Configure view", - "plugin": "content-manager", - "subCategory": "collection types", - }, - { - "action": "plugin::content-manager.components.configure-layout", - "displayName": "Configure Layout", - "plugin": "content-manager", - "subCategory": "components", - }, - { - "action": "plugin::content-manager.single-types.configure-view", - "displayName": "Configure view", - "plugin": "content-manager", - "subCategory": "single types", - }, - { - "action": "plugin::content-type-builder.read", - "displayName": "Read", - "plugin": "content-type-builder", - "subCategory": "general", - }, - { - "action": "plugin::documentation.read", - "displayName": "Access the Documentation", - "plugin": "documentation", - "subCategory": "general", - }, - { - "action": "plugin::documentation.settings.regenerate", - "displayName": "Regenerate", - "plugin": "documentation", - "subCategory": "general", - }, - { - "action": "plugin::documentation.settings.update", - "displayName": "Update and delete", - "plugin": "documentation", - "subCategory": "general", - }, - { - "action": "plugin::upload.assets.copy-link", - "displayName": "Copy link", - "plugin": "upload", - "subCategory": "assets", - }, - { - "action": "plugin::upload.assets.create", - "displayName": "Create (upload)", - "plugin": "upload", - "subCategory": "assets", - }, - { - "action": "plugin::upload.assets.download", - "displayName": "Download", - "plugin": "upload", - "subCategory": "assets", - }, - { - "action": "plugin::upload.assets.update", - "displayName": "Update (crop, details, replace) + delete", - "plugin": "upload", - "subCategory": "assets", - }, - { - "action": "plugin::upload.configure-view", - "displayName": "Configure view", - "plugin": "upload", - "subCategory": "general", - }, - { - "action": "plugin::upload.read", - "displayName": "Access the Media Library", - "plugin": "upload", - "subCategory": "general", - }, - { - "action": "plugin::users-permissions.advanced-settings.read", - "displayName": "Read", - "plugin": "users-permissions", - "subCategory": "advancedSettings", - }, - { - "action": "plugin::users-permissions.advanced-settings.update", - "displayName": "Edit", - "plugin": "users-permissions", - "subCategory": "advancedSettings", - }, - { - "action": "plugin::users-permissions.email-templates.read", - "displayName": "Read", - "plugin": "users-permissions", - "subCategory": "emailTemplates", - }, - { - "action": "plugin::users-permissions.email-templates.update", - "displayName": "Edit", - "plugin": "users-permissions", - "subCategory": "emailTemplates", - }, - { - "action": "plugin::users-permissions.providers.read", - "displayName": "Read", - "plugin": "users-permissions", - "subCategory": "providers", - }, - { - "action": "plugin::users-permissions.providers.update", - "displayName": "Edit", - "plugin": "users-permissions", - "subCategory": "providers", - }, - { - "action": "plugin::users-permissions.roles.create", - "displayName": "Create", - "plugin": "users-permissions", - "subCategory": "roles", - }, - { - "action": "plugin::users-permissions.roles.delete", - "displayName": "Delete", - "plugin": "users-permissions", - "subCategory": "roles", - }, - { - "action": "plugin::users-permissions.roles.read", - "displayName": "Read", - "plugin": "users-permissions", - "subCategory": "roles", - }, - { - "action": "plugin::users-permissions.roles.update", - "displayName": "Update", - "plugin": "users-permissions", - "subCategory": "roles", - }, - ], - "settings": [ - { - "action": "admin::api-tokens.access", - "category": "api tokens", - "displayName": "Access the API tokens settings page", - "subCategory": "api Tokens", - }, - { - "action": "admin::api-tokens.create", - "category": "api tokens", - "displayName": "Create (generate)", - "subCategory": "general", - }, - { - "action": "admin::api-tokens.delete", - "category": "api tokens", - "displayName": "Delete (revoke)", - "subCategory": "general", - }, - { - "action": "admin::api-tokens.read", - "category": "api tokens", - "displayName": "Read", - "subCategory": "general", - }, - { - "action": "admin::api-tokens.regenerate", - "category": "api tokens", - "displayName": "Regenerate", - "subCategory": "general", - }, - { - "action": "admin::api-tokens.update", - "category": "api tokens", - "displayName": "Update", - "subCategory": "general", - }, - { - "action": "admin::marketplace.read", - "category": "plugins and marketplace", - "displayName": "Access the marketplace", - "subCategory": "marketplace", - }, - { - "action": "admin::project-settings.read", - "category": "project", - "displayName": "Read the project level settings", - "subCategory": "general", - }, - { - "action": "admin::project-settings.update", - "category": "project", - "displayName": "Update the project level settings", - "subCategory": "general", - }, - { - "action": "admin::roles.create", - "category": "users and roles", - "displayName": "Create", - "subCategory": "roles", - }, - { - "action": "admin::roles.delete", - "category": "users and roles", - "displayName": "Delete", - "subCategory": "roles", - }, - { - "action": "admin::roles.read", - "category": "users and roles", - "displayName": "Read", - "subCategory": "roles", - }, - { - "action": "admin::roles.update", - "category": "users and roles", - "displayName": "Update", - "subCategory": "roles", - }, - { - "action": "admin::transfer.tokens.access", - "category": "transfer tokens", - "displayName": "Access the transfer tokens settings page", - "subCategory": "transfer tokens", - }, - { - "action": "admin::transfer.tokens.create", - "category": "transfer tokens", - "displayName": "Create (generate)", - "subCategory": "general", - }, - { - "action": "admin::transfer.tokens.delete", - "category": "transfer tokens", - "displayName": "Delete (revoke)", - "subCategory": "general", - }, - { - "action": "admin::transfer.tokens.read", - "category": "transfer tokens", - "displayName": "Read", - "subCategory": "general", - }, - { - "action": "admin::transfer.tokens.regenerate", - "category": "transfer tokens", - "displayName": "Regenerate", - "subCategory": "general", - }, - { - "action": "admin::transfer.tokens.update", - "category": "transfer tokens", - "displayName": "Update", - "subCategory": "general", - }, - { - "action": "admin::users.create", - "category": "users and roles", - "displayName": "Create (invite)", - "subCategory": "users", - }, - { - "action": "admin::users.delete", - "category": "users and roles", - "displayName": "Delete", - "subCategory": "users", - }, - { - "action": "admin::users.read", - "category": "users and roles", - "displayName": "Read", - "subCategory": "users", - }, - { - "action": "admin::users.update", - "category": "users and roles", - "displayName": "Update", - "subCategory": "users", - }, - { - "action": "admin::webhooks.create", - "category": "webhooks", - "displayName": "Create", - "subCategory": "general", - }, - { - "action": "admin::webhooks.delete", - "category": "webhooks", - "displayName": "Delete", - "subCategory": "general", - }, - { - "action": "admin::webhooks.read", - "category": "webhooks", - "displayName": "Read", - "subCategory": "general", - }, - { - "action": "admin::webhooks.update", - "category": "webhooks", - "displayName": "Update", - "subCategory": "general", - }, - { - "action": "plugin::documentation.settings.read", - "category": "documentation", - "displayName": "Access the documentation settings page", - "subCategory": "general", - }, - { - "action": "plugin::email.settings.read", - "category": "email", - "displayName": "Access the Email Settings page", - "subCategory": "general", - }, - { - "action": "plugin::i18n.locale.create", - "category": "Internationalization", - "displayName": "Create", - "subCategory": "Locales", - }, - { - "action": "plugin::i18n.locale.delete", - "category": "Internationalization", - "displayName": "Delete", - "subCategory": "Locales", - }, - { - "action": "plugin::i18n.locale.read", - "category": "Internationalization", - "displayName": "Read", - "subCategory": "Locales", - }, - { - "action": "plugin::i18n.locale.update", - "category": "Internationalization", - "displayName": "Update", - "subCategory": "Locales", - }, - { - "action": "plugin::upload.settings.read", - "category": "media library", - "displayName": "Access the Media Library settings page", - "subCategory": "general", - }, - ], - "singleTypes": [ - [ - { - "actionId": "plugin::content-manager.explorer.create", - "applyToProperties": [ - "fields", - "locales", - ], - "label": "Create", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.read", - "applyToProperties": [ - "fields", - "locales", - ], - "label": "Read", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.update", - "applyToProperties": [ - "fields", - "locales", - ], - "label": "Update", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.delete", - "applyToProperties": [ - "locales", - ], - "label": "Delete", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.publish", - "applyToProperties": [ - "locales", - ], - "label": "Publish", - "subjects": [], - }, - ], - [], - ], - }, - } - `); - } else { - // eslint-disable-next-line node/no-extraneous-require - const { features } = require('@strapi/strapi/lib/utils/ee'); - const hasSSO = features.isEnabled('sso'); - - if (hasSSO) { - expect(sortedData).toMatchInlineSnapshot(` - { - "conditions": [ - { - "category": "default", - "displayName": "Is creator", - "id": "admin::is-creator", - }, - { - "category": "default", - "displayName": "Has same role as creator", - "id": "admin::has-same-role-as-creator", - }, - ], - "sections": { - "collectionTypes": [ - [ - { - "actionId": "plugin::content-manager.explorer.create", - "applyToProperties": [ - "fields", - "locales", - ], - "label": "Create", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.read", - "applyToProperties": [ - "fields", - "locales", - ], - "label": "Read", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.update", - "applyToProperties": [ - "fields", - "locales", - ], - "label": "Update", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.delete", - "applyToProperties": [ - "locales", - ], - "label": "Delete", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.publish", - "applyToProperties": [ - "locales", - ], - "label": "Publish", - "subjects": [], - }, - ], - [ - { - "label": "user", - "properties": [ - { - "children": [ - { - "label": "username", - "required": true, - "value": "username", - }, - { - "label": "email", - "required": true, - "value": "email", - }, - { - "label": "provider", - "value": "provider", - }, - { - "label": "password", - "value": "password", - }, - { - "label": "resetPasswordToken", - "value": "resetPasswordToken", - }, - { - "label": "confirmationToken", - "value": "confirmationToken", - }, - { - "label": "confirmed", - "value": "confirmed", - }, - { - "label": "blocked", - "value": "blocked", - }, - { - "label": "role", - "value": "role", - }, - ], - "label": "Fields", - "value": "fields", - }, - ], - "uid": "plugin::users-permissions.user", - }, - ], - ], - "plugins": [ - { - "action": "plugin::content-manager.collection-types.configure-view", - "displayName": "Configure view", - "plugin": "content-manager", - "subCategory": "collection types", - }, - { - "action": "plugin::content-manager.components.configure-layout", - "displayName": "Configure Layout", - "plugin": "content-manager", - "subCategory": "components", - }, - { - "action": "plugin::content-manager.single-types.configure-view", - "displayName": "Configure view", - "plugin": "content-manager", - "subCategory": "single types", - }, - { - "action": "plugin::content-type-builder.read", - "displayName": "Read", - "plugin": "content-type-builder", - "subCategory": "general", - }, - { - "action": "plugin::documentation.read", - "displayName": "Access the Documentation", - "plugin": "documentation", - "subCategory": "general", - }, - { - "action": "plugin::documentation.settings.regenerate", - "displayName": "Regenerate", - "plugin": "documentation", - "subCategory": "general", - }, - { - "action": "plugin::documentation.settings.update", - "displayName": "Update and delete", - "plugin": "documentation", - "subCategory": "general", - }, - { - "action": "plugin::upload.assets.copy-link", - "displayName": "Copy link", - "plugin": "upload", - "subCategory": "assets", - }, - { - "action": "plugin::upload.assets.create", - "displayName": "Create (upload)", - "plugin": "upload", - "subCategory": "assets", - }, - { - "action": "plugin::upload.assets.download", - "displayName": "Download", - "plugin": "upload", - "subCategory": "assets", - }, - { - "action": "plugin::upload.assets.update", - "displayName": "Update (crop, details, replace) + delete", - "plugin": "upload", - "subCategory": "assets", - }, - { - "action": "plugin::upload.configure-view", - "displayName": "Configure view", - "plugin": "upload", - "subCategory": "general", - }, - { - "action": "plugin::upload.read", - "displayName": "Access the Media Library", - "plugin": "upload", - "subCategory": "general", - }, - { - "action": "plugin::users-permissions.advanced-settings.read", - "displayName": "Read", - "plugin": "users-permissions", - "subCategory": "advancedSettings", - }, - { - "action": "plugin::users-permissions.advanced-settings.update", - "displayName": "Edit", - "plugin": "users-permissions", - "subCategory": "advancedSettings", - }, - { - "action": "plugin::users-permissions.email-templates.read", - "displayName": "Read", - "plugin": "users-permissions", - "subCategory": "emailTemplates", - }, - { - "action": "plugin::users-permissions.email-templates.update", - "displayName": "Edit", - "plugin": "users-permissions", - "subCategory": "emailTemplates", - }, - { - "action": "plugin::users-permissions.providers.read", - "displayName": "Read", - "plugin": "users-permissions", - "subCategory": "providers", - }, - { - "action": "plugin::users-permissions.providers.update", - "displayName": "Edit", - "plugin": "users-permissions", - "subCategory": "providers", - }, - { - "action": "plugin::users-permissions.roles.create", - "displayName": "Create", - "plugin": "users-permissions", - "subCategory": "roles", - }, - { - "action": "plugin::users-permissions.roles.delete", - "displayName": "Delete", - "plugin": "users-permissions", - "subCategory": "roles", - }, - { - "action": "plugin::users-permissions.roles.read", - "displayName": "Read", - "plugin": "users-permissions", - "subCategory": "roles", - }, - { - "action": "plugin::users-permissions.roles.update", - "displayName": "Update", - "plugin": "users-permissions", - "subCategory": "roles", - }, - ], - "settings": [ - { - "action": "admin::api-tokens.access", - "category": "api tokens", - "displayName": "Access the API tokens settings page", - "subCategory": "api Tokens", - }, - { - "action": "admin::api-tokens.create", - "category": "api tokens", - "displayName": "Create (generate)", - "subCategory": "general", - }, - { - "action": "admin::api-tokens.delete", - "category": "api tokens", - "displayName": "Delete (revoke)", - "subCategory": "general", - }, - { - "action": "admin::api-tokens.read", - "category": "api tokens", - "displayName": "Read", - "subCategory": "general", - }, - { - "action": "admin::api-tokens.regenerate", - "category": "api tokens", - "displayName": "Regenerate", - "subCategory": "general", - }, - { - "action": "admin::api-tokens.update", - "category": "api tokens", - "displayName": "Update", - "subCategory": "general", - }, - { - "action": "admin::audit-logs.read", - "category": "audit logs", - "displayName": "Read", - "subCategory": "options", - }, - { - "action": "admin::marketplace.read", - "category": "plugins and marketplace", - "displayName": "Access the marketplace", - "subCategory": "marketplace", - }, - { - "action": "admin::project-settings.read", - "category": "project", - "displayName": "Read the project level settings", - "subCategory": "general", - }, - { - "action": "admin::project-settings.update", - "category": "project", - "displayName": "Update the project level settings", - "subCategory": "general", - }, - { - "action": "admin::provider-login.read", - "category": "single sign on", - "displayName": "Read", - "subCategory": "options", - }, - { - "action": "admin::provider-login.update", - "category": "single sign on", - "displayName": "Update", - "subCategory": "options", - }, - { - "action": "admin::roles.create", - "category": "users and roles", - "displayName": "Create", - "subCategory": "roles", - }, - { - "action": "admin::roles.delete", - "category": "users and roles", - "displayName": "Delete", - "subCategory": "roles", - }, - { - "action": "admin::roles.read", - "category": "users and roles", - "displayName": "Read", - "subCategory": "roles", - }, - { - "action": "admin::roles.update", - "category": "users and roles", - "displayName": "Update", - "subCategory": "roles", - }, - { - "action": "admin::transfer.tokens.access", - "category": "transfer tokens", - "displayName": "Access the transfer tokens settings page", - "subCategory": "transfer tokens", - }, - { - "action": "admin::transfer.tokens.create", - "category": "transfer tokens", - "displayName": "Create (generate)", - "subCategory": "general", - }, - { - "action": "admin::transfer.tokens.delete", - "category": "transfer tokens", - "displayName": "Delete (revoke)", - "subCategory": "general", - }, - { - "action": "admin::transfer.tokens.read", - "category": "transfer tokens", - "displayName": "Read", - "subCategory": "general", - }, - { - "action": "admin::transfer.tokens.regenerate", - "category": "transfer tokens", - "displayName": "Regenerate", - "subCategory": "general", - }, - { - "action": "admin::transfer.tokens.update", - "category": "transfer tokens", - "displayName": "Update", - "subCategory": "general", - }, - { - "action": "admin::users.create", - "category": "users and roles", - "displayName": "Create (invite)", - "subCategory": "users", - }, - { - "action": "admin::users.delete", - "category": "users and roles", - "displayName": "Delete", - "subCategory": "users", - }, - { - "action": "admin::users.read", - "category": "users and roles", - "displayName": "Read", - "subCategory": "users", - }, - { - "action": "admin::users.update", - "category": "users and roles", - "displayName": "Update", - "subCategory": "users", - }, - { - "action": "admin::webhooks.create", - "category": "webhooks", - "displayName": "Create", - "subCategory": "general", - }, - { - "action": "admin::webhooks.delete", - "category": "webhooks", - "displayName": "Delete", - "subCategory": "general", - }, - { - "action": "admin::webhooks.read", - "category": "webhooks", - "displayName": "Read", - "subCategory": "general", - }, - { - "action": "admin::webhooks.update", - "category": "webhooks", - "displayName": "Update", - "subCategory": "general", - }, - { - "action": "plugin::documentation.settings.read", - "category": "documentation", - "displayName": "Access the documentation settings page", - "subCategory": "general", - }, - { - "action": "plugin::email.settings.read", - "category": "email", - "displayName": "Access the Email Settings page", - "subCategory": "general", - }, - { - "action": "plugin::i18n.locale.create", - "category": "Internationalization", - "displayName": "Create", - "subCategory": "Locales", - }, - { - "action": "plugin::i18n.locale.delete", - "category": "Internationalization", - "displayName": "Delete", - "subCategory": "Locales", - }, - { - "action": "plugin::i18n.locale.read", - "category": "Internationalization", - "displayName": "Read", - "subCategory": "Locales", - }, - { - "action": "plugin::i18n.locale.update", - "category": "Internationalization", - "displayName": "Update", - "subCategory": "Locales", - }, - { - "action": "plugin::upload.settings.read", - "category": "media library", - "displayName": "Access the Media Library settings page", - "subCategory": "general", - }, - ], - "singleTypes": [ - [ - { - "actionId": "plugin::content-manager.explorer.create", - "applyToProperties": [ - "fields", - "locales", - ], - "label": "Create", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.read", - "applyToProperties": [ - "fields", - "locales", - ], - "label": "Read", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.update", - "applyToProperties": [ - "fields", - "locales", - ], - "label": "Update", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.delete", - "applyToProperties": [ - "locales", - ], - "label": "Delete", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.publish", - "applyToProperties": [ - "locales", - ], - "label": "Publish", - "subjects": [], - }, - ], - [], - ], - }, - } - `); - } else { - expect(sortedData).toMatchInlineSnapshot(` - { - "conditions": [ - { - "category": "default", - "displayName": "Is creator", - "id": "admin::is-creator", - }, - { - "category": "default", - "displayName": "Has same role as creator", - "id": "admin::has-same-role-as-creator", - }, - ], - "sections": { - "collectionTypes": [ - [ - { - "actionId": "plugin::content-manager.explorer.create", - "applyToProperties": [ - "fields", - "locales", - ], - "label": "Create", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.read", - "applyToProperties": [ - "fields", - "locales", - ], - "label": "Read", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.update", - "applyToProperties": [ - "fields", - "locales", - ], - "label": "Update", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.delete", - "applyToProperties": [ - "locales", - ], - "label": "Delete", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.publish", - "applyToProperties": [ - "locales", - ], - "label": "Publish", - "subjects": [], - }, - ], - [ - { - "label": "user", - "properties": [ - { - "children": [ - { - "label": "username", - "required": true, - "value": "username", - }, - { - "label": "email", - "required": true, - "value": "email", - }, - { - "label": "provider", - "value": "provider", - }, - { - "label": "password", - "value": "password", - }, - { - "label": "resetPasswordToken", - "value": "resetPasswordToken", - }, - { - "label": "confirmationToken", - "value": "confirmationToken", - }, - { - "label": "confirmed", - "value": "confirmed", - }, - { - "label": "blocked", - "value": "blocked", - }, - { - "label": "role", - "value": "role", - }, - ], - "label": "Fields", - "value": "fields", - }, - ], - "uid": "plugin::users-permissions.user", - }, - ], - ], - "plugins": [ - { - "action": "plugin::content-manager.collection-types.configure-view", - "displayName": "Configure view", - "plugin": "content-manager", - "subCategory": "collection types", - }, - { - "action": "plugin::content-manager.components.configure-layout", - "displayName": "Configure Layout", - "plugin": "content-manager", - "subCategory": "components", - }, - { - "action": "plugin::content-manager.single-types.configure-view", - "displayName": "Configure view", - "plugin": "content-manager", - "subCategory": "single types", - }, - { - "action": "plugin::content-type-builder.read", - "displayName": "Read", - "plugin": "content-type-builder", - "subCategory": "general", - }, - { - "action": "plugin::documentation.read", - "displayName": "Access the Documentation", - "plugin": "documentation", - "subCategory": "general", - }, - { - "action": "plugin::documentation.settings.regenerate", - "displayName": "Regenerate", - "plugin": "documentation", - "subCategory": "general", + "action": "plugin::documentation.settings.regenerate", + "displayName": "Regenerate", + "plugin": "documentation", + "subCategory": "general", }, { "action": "plugin::documentation.settings.update", @@ -1414,6 +337,12 @@ describe('Role CRUD End to End', () => { "displayName": "Update", "subCategory": "general", }, + { + "action": "admin::audit-logs.read", + "category": "audit logs", + "displayName": "Read", + "subCategory": "options", + }, { "action": "admin::marketplace.read", "category": "plugins and marketplace", @@ -1432,6 +361,18 @@ describe('Role CRUD End to End', () => { "displayName": "Update the project level settings", "subCategory": "general", }, + { + "action": "admin::provider-login.read", + "category": "single sign on", + "displayName": "Read", + "subCategory": "options", + }, + { + "action": "admin::provider-login.update", + "category": "single sign on", + "displayName": "Update", + "subCategory": "options", + }, { "action": "admin::roles.create", "category": "users and roles", @@ -1456,6 +397,42 @@ describe('Role CRUD End to End', () => { "displayName": "Update", "subCategory": "roles", }, + { + "action": "admin::transfer.tokens.access", + "category": "transfer tokens", + "displayName": "Access the transfer tokens settings page", + "subCategory": "transfer tokens", + }, + { + "action": "admin::transfer.tokens.create", + "category": "transfer tokens", + "displayName": "Create (generate)", + "subCategory": "general", + }, + { + "action": "admin::transfer.tokens.delete", + "category": "transfer tokens", + "displayName": "Delete (revoke)", + "subCategory": "general", + }, + { + "action": "admin::transfer.tokens.read", + "category": "transfer tokens", + "displayName": "Read", + "subCategory": "general", + }, + { + "action": "admin::transfer.tokens.regenerate", + "category": "transfer tokens", + "displayName": "Regenerate", + "subCategory": "general", + }, + { + "action": "admin::transfer.tokens.update", + "category": "transfer tokens", + "displayName": "Update", + "subCategory": "general", + }, { "action": "admin::users.create", "category": "users and roles", @@ -1517,96 +494,623 @@ describe('Role CRUD End to End', () => { "subCategory": "general", }, { - "action": "plugin::i18n.locale.create", - "category": "Internationalization", - "displayName": "Create", - "subCategory": "Locales", + "action": "plugin::i18n.locale.create", + "category": "Internationalization", + "displayName": "Create", + "subCategory": "Locales", + }, + { + "action": "plugin::i18n.locale.delete", + "category": "Internationalization", + "displayName": "Delete", + "subCategory": "Locales", + }, + { + "action": "plugin::i18n.locale.read", + "category": "Internationalization", + "displayName": "Read", + "subCategory": "Locales", + }, + { + "action": "plugin::i18n.locale.update", + "category": "Internationalization", + "displayName": "Update", + "subCategory": "Locales", + }, + { + "action": "plugin::upload.settings.read", + "category": "media library", + "displayName": "Access the Media Library settings page", + "subCategory": "general", + }, + ], + "singleTypes": [ + [ + { + "actionId": "plugin::content-manager.explorer.create", + "applyToProperties": [ + "fields", + "locales", + ], + "label": "Create", + "subjects": [ + "plugin::users-permissions.user", + ], + }, + { + "actionId": "plugin::content-manager.explorer.read", + "applyToProperties": [ + "fields", + "locales", + ], + "label": "Read", + "subjects": [ + "plugin::users-permissions.user", + ], + }, + { + "actionId": "plugin::content-manager.explorer.update", + "applyToProperties": [ + "fields", + "locales", + ], + "label": "Update", + "subjects": [ + "plugin::users-permissions.user", + ], + }, + { + "actionId": "plugin::content-manager.explorer.delete", + "applyToProperties": [ + "locales", + ], + "label": "Delete", + "subjects": [ + "plugin::users-permissions.user", + ], + }, + { + "actionId": "plugin::content-manager.explorer.publish", + "applyToProperties": [ + "locales", + ], + "label": "Publish", + "subjects": [], + }, + ], + [], + ], + }, + } + `); + } else { + expect(sortedData).toMatchInlineSnapshot(` + { + "conditions": [ + { + "category": "default", + "displayName": "Is creator", + "id": "admin::is-creator", + }, + { + "category": "default", + "displayName": "Has same role as creator", + "id": "admin::has-same-role-as-creator", + }, + ], + "sections": { + "collectionTypes": [ + [ + { + "actionId": "plugin::content-manager.explorer.create", + "applyToProperties": [ + "fields", + "locales", + ], + "label": "Create", + "subjects": [ + "plugin::users-permissions.user", + ], + }, + { + "actionId": "plugin::content-manager.explorer.read", + "applyToProperties": [ + "fields", + "locales", + ], + "label": "Read", + "subjects": [ + "plugin::users-permissions.user", + ], + }, + { + "actionId": "plugin::content-manager.explorer.update", + "applyToProperties": [ + "fields", + "locales", + ], + "label": "Update", + "subjects": [ + "plugin::users-permissions.user", + ], + }, + { + "actionId": "plugin::content-manager.explorer.delete", + "applyToProperties": [ + "locales", + ], + "label": "Delete", + "subjects": [ + "plugin::users-permissions.user", + ], + }, + { + "actionId": "plugin::content-manager.explorer.publish", + "applyToProperties": [ + "locales", + ], + "label": "Publish", + "subjects": [], + }, + ], + [ + { + "label": "user", + "properties": [ + { + "children": [ + { + "label": "username", + "required": true, + "value": "username", + }, + { + "label": "email", + "required": true, + "value": "email", + }, + { + "label": "provider", + "value": "provider", + }, + { + "label": "password", + "value": "password", + }, + { + "label": "resetPasswordToken", + "value": "resetPasswordToken", + }, + { + "label": "confirmationToken", + "value": "confirmationToken", + }, + { + "label": "confirmed", + "value": "confirmed", + }, + { + "label": "blocked", + "value": "blocked", + }, + { + "label": "role", + "value": "role", + }, + ], + "label": "Fields", + "value": "fields", + }, + ], + "uid": "plugin::users-permissions.user", + }, + ], + ], + "plugins": [ + { + "action": "plugin::content-manager.collection-types.configure-view", + "displayName": "Configure view", + "plugin": "content-manager", + "subCategory": "collection types", + }, + { + "action": "plugin::content-manager.components.configure-layout", + "displayName": "Configure Layout", + "plugin": "content-manager", + "subCategory": "components", + }, + { + "action": "plugin::content-manager.single-types.configure-view", + "displayName": "Configure view", + "plugin": "content-manager", + "subCategory": "single types", + }, + { + "action": "plugin::content-type-builder.read", + "displayName": "Read", + "plugin": "content-type-builder", + "subCategory": "general", + }, + { + "action": "plugin::documentation.read", + "displayName": "Access the Documentation", + "plugin": "documentation", + "subCategory": "general", + }, + { + "action": "plugin::documentation.settings.regenerate", + "displayName": "Regenerate", + "plugin": "documentation", + "subCategory": "general", + }, + { + "action": "plugin::documentation.settings.update", + "displayName": "Update and delete", + "plugin": "documentation", + "subCategory": "general", + }, + { + "action": "plugin::upload.assets.copy-link", + "displayName": "Copy link", + "plugin": "upload", + "subCategory": "assets", + }, + { + "action": "plugin::upload.assets.create", + "displayName": "Create (upload)", + "plugin": "upload", + "subCategory": "assets", + }, + { + "action": "plugin::upload.assets.download", + "displayName": "Download", + "plugin": "upload", + "subCategory": "assets", + }, + { + "action": "plugin::upload.assets.update", + "displayName": "Update (crop, details, replace) + delete", + "plugin": "upload", + "subCategory": "assets", + }, + { + "action": "plugin::upload.configure-view", + "displayName": "Configure view", + "plugin": "upload", + "subCategory": "general", + }, + { + "action": "plugin::upload.read", + "displayName": "Access the Media Library", + "plugin": "upload", + "subCategory": "general", + }, + { + "action": "plugin::users-permissions.advanced-settings.read", + "displayName": "Read", + "plugin": "users-permissions", + "subCategory": "advancedSettings", + }, + { + "action": "plugin::users-permissions.advanced-settings.update", + "displayName": "Edit", + "plugin": "users-permissions", + "subCategory": "advancedSettings", + }, + { + "action": "plugin::users-permissions.email-templates.read", + "displayName": "Read", + "plugin": "users-permissions", + "subCategory": "emailTemplates", + }, + { + "action": "plugin::users-permissions.email-templates.update", + "displayName": "Edit", + "plugin": "users-permissions", + "subCategory": "emailTemplates", + }, + { + "action": "plugin::users-permissions.providers.read", + "displayName": "Read", + "plugin": "users-permissions", + "subCategory": "providers", + }, + { + "action": "plugin::users-permissions.providers.update", + "displayName": "Edit", + "plugin": "users-permissions", + "subCategory": "providers", + }, + { + "action": "plugin::users-permissions.roles.create", + "displayName": "Create", + "plugin": "users-permissions", + "subCategory": "roles", + }, + { + "action": "plugin::users-permissions.roles.delete", + "displayName": "Delete", + "plugin": "users-permissions", + "subCategory": "roles", + }, + { + "action": "plugin::users-permissions.roles.read", + "displayName": "Read", + "plugin": "users-permissions", + "subCategory": "roles", + }, + { + "action": "plugin::users-permissions.roles.update", + "displayName": "Update", + "plugin": "users-permissions", + "subCategory": "roles", + }, + ], + "settings": [ + { + "action": "admin::api-tokens.access", + "category": "api tokens", + "displayName": "Access the API tokens settings page", + "subCategory": "api Tokens", + }, + { + "action": "admin::api-tokens.create", + "category": "api tokens", + "displayName": "Create (generate)", + "subCategory": "general", + }, + { + "action": "admin::api-tokens.delete", + "category": "api tokens", + "displayName": "Delete (revoke)", + "subCategory": "general", + }, + { + "action": "admin::api-tokens.read", + "category": "api tokens", + "displayName": "Read", + "subCategory": "general", + }, + { + "action": "admin::api-tokens.regenerate", + "category": "api tokens", + "displayName": "Regenerate", + "subCategory": "general", + }, + { + "action": "admin::api-tokens.update", + "category": "api tokens", + "displayName": "Update", + "subCategory": "general", + }, + { + "action": "admin::marketplace.read", + "category": "plugins and marketplace", + "displayName": "Access the marketplace", + "subCategory": "marketplace", + }, + { + "action": "admin::project-settings.read", + "category": "project", + "displayName": "Read the project level settings", + "subCategory": "general", + }, + { + "action": "admin::project-settings.update", + "category": "project", + "displayName": "Update the project level settings", + "subCategory": "general", + }, + { + "action": "admin::roles.create", + "category": "users and roles", + "displayName": "Create", + "subCategory": "roles", + }, + { + "action": "admin::roles.delete", + "category": "users and roles", + "displayName": "Delete", + "subCategory": "roles", + }, + { + "action": "admin::roles.read", + "category": "users and roles", + "displayName": "Read", + "subCategory": "roles", + }, + { + "action": "admin::roles.update", + "category": "users and roles", + "displayName": "Update", + "subCategory": "roles", + }, + { + "action": "admin::transfer.tokens.access", + "category": "transfer tokens", + "displayName": "Access the transfer tokens settings page", + "subCategory": "transfer tokens", + }, + { + "action": "admin::transfer.tokens.create", + "category": "transfer tokens", + "displayName": "Create (generate)", + "subCategory": "general", + }, + { + "action": "admin::transfer.tokens.delete", + "category": "transfer tokens", + "displayName": "Delete (revoke)", + "subCategory": "general", + }, + { + "action": "admin::transfer.tokens.read", + "category": "transfer tokens", + "displayName": "Read", + "subCategory": "general", + }, + { + "action": "admin::transfer.tokens.regenerate", + "category": "transfer tokens", + "displayName": "Regenerate", + "subCategory": "general", + }, + { + "action": "admin::transfer.tokens.update", + "category": "transfer tokens", + "displayName": "Update", + "subCategory": "general", + }, + { + "action": "admin::users.create", + "category": "users and roles", + "displayName": "Create (invite)", + "subCategory": "users", + }, + { + "action": "admin::users.delete", + "category": "users and roles", + "displayName": "Delete", + "subCategory": "users", + }, + { + "action": "admin::users.read", + "category": "users and roles", + "displayName": "Read", + "subCategory": "users", + }, + { + "action": "admin::users.update", + "category": "users and roles", + "displayName": "Update", + "subCategory": "users", + }, + { + "action": "admin::webhooks.create", + "category": "webhooks", + "displayName": "Create", + "subCategory": "general", + }, + { + "action": "admin::webhooks.delete", + "category": "webhooks", + "displayName": "Delete", + "subCategory": "general", + }, + { + "action": "admin::webhooks.read", + "category": "webhooks", + "displayName": "Read", + "subCategory": "general", + }, + { + "action": "admin::webhooks.update", + "category": "webhooks", + "displayName": "Update", + "subCategory": "general", + }, + { + "action": "plugin::documentation.settings.read", + "category": "documentation", + "displayName": "Access the documentation settings page", + "subCategory": "general", + }, + { + "action": "plugin::email.settings.read", + "category": "email", + "displayName": "Access the Email Settings page", + "subCategory": "general", + }, + { + "action": "plugin::i18n.locale.create", + "category": "Internationalization", + "displayName": "Create", + "subCategory": "Locales", + }, + { + "action": "plugin::i18n.locale.delete", + "category": "Internationalization", + "displayName": "Delete", + "subCategory": "Locales", + }, + { + "action": "plugin::i18n.locale.read", + "category": "Internationalization", + "displayName": "Read", + "subCategory": "Locales", + }, + { + "action": "plugin::i18n.locale.update", + "category": "Internationalization", + "displayName": "Update", + "subCategory": "Locales", + }, + { + "action": "plugin::upload.settings.read", + "category": "media library", + "displayName": "Access the Media Library settings page", + "subCategory": "general", + }, + ], + "singleTypes": [ + [ + { + "actionId": "plugin::content-manager.explorer.create", + "applyToProperties": [ + "fields", + "locales", + ], + "label": "Create", + "subjects": [ + "plugin::users-permissions.user", + ], }, { - "action": "plugin::i18n.locale.delete", - "category": "Internationalization", - "displayName": "Delete", - "subCategory": "Locales", + "actionId": "plugin::content-manager.explorer.read", + "applyToProperties": [ + "fields", + "locales", + ], + "label": "Read", + "subjects": [ + "plugin::users-permissions.user", + ], }, { - "action": "plugin::i18n.locale.read", - "category": "Internationalization", - "displayName": "Read", - "subCategory": "Locales", + "actionId": "plugin::content-manager.explorer.update", + "applyToProperties": [ + "fields", + "locales", + ], + "label": "Update", + "subjects": [ + "plugin::users-permissions.user", + ], }, { - "action": "plugin::i18n.locale.update", - "category": "Internationalization", - "displayName": "Update", - "subCategory": "Locales", + "actionId": "plugin::content-manager.explorer.delete", + "applyToProperties": [ + "locales", + ], + "label": "Delete", + "subjects": [ + "plugin::users-permissions.user", + ], }, { - "action": "plugin::upload.settings.read", - "category": "media library", - "displayName": "Access the Media Library settings page", - "subCategory": "general", + "actionId": "plugin::content-manager.explorer.publish", + "applyToProperties": [ + "locales", + ], + "label": "Publish", + "subjects": [], }, ], - "singleTypes": [ - [ - { - "actionId": "plugin::content-manager.explorer.create", - "applyToProperties": [ - "fields", - "locales", - ], - "label": "Create", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.read", - "applyToProperties": [ - "fields", - "locales", - ], - "label": "Read", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.update", - "applyToProperties": [ - "fields", - "locales", - ], - "label": "Update", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.delete", - "applyToProperties": [ - "locales", - ], - "label": "Delete", - "subjects": [ - "plugin::users-permissions.user", - ], - }, - { - "actionId": "plugin::content-manager.explorer.publish", - "applyToProperties": [ - "locales", - ], - "label": "Publish", - "subjects": [], - }, - ], - [], - ], - }, - } - `); - } + [], + ], + }, + } + `); } }); }); diff --git a/packages/core/admin/server/tests/admin-permissions-conditions.test.api.js b/packages/core/admin/server/tests/admin-permissions-conditions.test.api.js index fcdfd125602..6da010f7214 100644 --- a/packages/core/admin/server/tests/admin-permissions-conditions.test.api.js +++ b/packages/core/admin/server/tests/admin-permissions-conditions.test.api.js @@ -6,197 +6,189 @@ const { createStrapiInstance } = require('../../../../../test/helpers/strapi'); const { createRequest, createAuthRequest } = require('../../../../../test/helpers/request'); const { createUtils } = require('../../../../../test/helpers/utils'); -const edition = process.env.STRAPI_DISABLE_EE === 'true' ? 'CE' : 'EE'; - -if (edition === 'EE') { - describe('Admin Permissions - Conditions', () => { - let strapi; - let utils; - const builder = createTestBuilder(); - const requests = { - public: null, - admin: null, - }; - - const localTestData = { - models: { - article: { - singularName: 'article', - pluralName: 'articles', - displayName: 'Article', - attributes: { - title: { - type: 'string', - }, - price: { - type: 'integer', - }, +describe('Admin Permissions - Conditions', () => { + let strapi; + let utils; + const builder = createTestBuilder(); + const requests = { + public: null, + admin: null, + }; + + const localTestData = { + models: { + article: { + singularName: 'article', + pluralName: 'articles', + displayName: 'Article', + attributes: { + title: { + type: 'string', + }, + price: { + type: 'integer', }, }, }, - entry: { - name: 'Test Article', - price: 999, + }, + entry: { + name: 'Test Article', + price: 999, + }, + role: { + name: 'foobar', + description: 'A dummy test role', + }, + permissions: [ + { + action: 'plugin::content-manager.explorer.create', + subject: 'api::article.article', + fields: null, + conditions: [], }, - role: { - name: 'foobar', - description: 'A dummy test role', + { + action: 'plugin::content-manager.explorer.read', + subject: 'api::article.article', + fields: null, + conditions: ['admin::has-same-role-as-creator'], }, - permissions: [ - { - action: 'plugin::content-manager.explorer.create', - subject: 'api::article.article', - fields: null, - conditions: [], - }, - { - action: 'plugin::content-manager.explorer.read', - subject: 'api::article.article', - fields: null, - conditions: ['admin::has-same-role-as-creator'], - }, - { - action: 'plugin::content-manager.explorer.delete', - subject: 'api::article.article', - fields: null, - conditions: ['admin::is-creator'], - }, - ], - userPassword: 'fooBar42', - users: [ - { firstname: 'Alice', lastname: 'Foo', email: 'alice.foo@test.com' }, - { firstname: 'Bob', lastname: 'Bar', email: 'bob.bar@test.com' }, - ], - }; + { + action: 'plugin::content-manager.explorer.delete', + subject: 'api::article.article', + fields: null, + conditions: ['admin::is-creator'], + }, + ], + userPassword: 'fooBar42', + users: [ + { firstname: 'Alice', lastname: 'Foo', email: 'alice.foo@test.com' }, + { firstname: 'Bob', lastname: 'Bar', email: 'bob.bar@test.com' }, + ], + }; - const createFixtures = async () => { - // Login with admin and init admin tools - requests.admin = await createAuthRequest({ strapi }); - requests.public = createRequest({ strapi }); + const createFixtures = async () => { + // Login with admin and init admin tools + requests.admin = await createAuthRequest({ strapi }); + requests.public = createRequest({ strapi }); - // Create the foobar role - const role = await utils.createRole(localTestData.role); + // Create the foobar role + const role = await utils.createRole(localTestData.role); - // Assign permissions to the foobar role - const permissions = await utils.assignPermissionsToRole(role.id, localTestData.permissions); - Object.assign(role, { permissions }); + // Assign permissions to the foobar role + const permissions = await utils.assignPermissionsToRole(role.id, localTestData.permissions); + Object.assign(role, { permissions }); - // Create users with the new role & create associated auth requests - const users = []; + // Create users with the new role & create associated auth requests + const users = []; - for (let i = 0; i < localTestData.users.length; i += 1) { - const userFixture = localTestData.users[i]; - const userAttributes = { - ...userFixture, - password: localTestData.userPassword, - roles: [role.id], - }; + for (let i = 0; i < localTestData.users.length; i += 1) { + const userFixture = localTestData.users[i]; + const userAttributes = { + ...userFixture, + password: localTestData.userPassword, + roles: [role.id], + }; - const createdUser = await utils.createUser(userAttributes); + const createdUser = await utils.createUser(userAttributes); - requests[createdUser.id] = await createAuthRequest({ strapi, userInfo: createdUser }); + requests[createdUser.id] = await createAuthRequest({ strapi, userInfo: createdUser }); - users.push(createdUser); - } + users.push(createdUser); + } - // Update the local data store - Object.assign(localTestData, { role, permissions, users }); - }; + // Update the local data store + Object.assign(localTestData, { role, permissions, users }); + }; - const getUserRequest = (idx) => requests[localTestData.users[idx].id]; - const getModelName = () => localTestData.models.article.singularName; + const getUserRequest = (idx) => requests[localTestData.users[idx].id]; + const getModelName = () => localTestData.models.article.singularName; - const deleteFixtures = async () => { - // Delete users - const usersId = localTestData.users.map(prop('id')); - await utils.deleteUsersById(usersId); + const deleteFixtures = async () => { + // Delete users + const usersId = localTestData.users.map(prop('id')); + await utils.deleteUsersById(usersId); - // Delete the foobar role - await utils.deleteRolesById([localTestData.role.id]); - }; + // Delete the foobar role + await utils.deleteRolesById([localTestData.role.id]); + }; - beforeAll(async () => { - await builder.addContentType(localTestData.models.article).build(); + beforeAll(async () => { + await builder.addContentType(localTestData.models.article).build(); - strapi = await createStrapiInstance(); - utils = createUtils(strapi); + strapi = await createStrapiInstance(); + utils = createUtils(strapi); - await createFixtures(); - }); + await createFixtures(); + }); - afterAll(async () => { - await deleteFixtures(); + afterAll(async () => { + await deleteFixtures(); - await strapi.destroy(); - await builder.cleanup(); - }); + await strapi.destroy(); + await builder.cleanup(); + }); - test('User A can create an entry', async () => { - const rq = getUserRequest(0); - const modelName = getModelName(); - const res = await rq({ - method: 'POST', - url: `/content-manager/collection-types/api::${modelName}.${modelName}`, - body: localTestData.entry, - }); - - expect(res.statusCode).toBe(200); - localTestData.entry = res.body; + test('User A can create an entry', async () => { + const rq = getUserRequest(0); + const modelName = getModelName(); + const res = await rq({ + method: 'POST', + url: `/content-manager/collection-types/api::${modelName}.${modelName}`, + body: localTestData.entry, }); - test('User A can read its entry', async () => { - const { id } = localTestData.entry; - const modelName = getModelName(); - const rq = getUserRequest(0); - const res = await rq({ - method: 'GET', - url: `/content-manager/collection-types/api::${modelName}.${modelName}/${id}`, - }); - - expect(res.statusCode).toBe(200); - expect(res.body).toMatchObject(localTestData.entry); - }); + expect(res.statusCode).toBe(200); + localTestData.entry = res.body; + }); - test('User B can read the entry created by user A', async () => { - const { id } = localTestData.entry; - const modelName = getModelName(); - const rq = getUserRequest(1); - const res = await rq({ - method: 'GET', - url: `/content-manager/collection-types/api::${modelName}.${modelName}/${id}`, - }); - - expect(res.statusCode).toBe(200); - expect(res.body).toMatchObject(localTestData.entry); + test('User A can read its entry', async () => { + const { id } = localTestData.entry; + const modelName = getModelName(); + const rq = getUserRequest(0); + const res = await rq({ + method: 'GET', + url: `/content-manager/collection-types/api::${modelName}.${modelName}/${id}`, }); - test('User B cannot delete the entry created by user A', async () => { - const { id } = localTestData.entry; - const modelName = getModelName(); - const rq = getUserRequest(1); - const res = await rq({ - method: 'DELETE', - url: `/content-manager/collection-types/api::${modelName}.${modelName}/${id}`, - }); + expect(res.statusCode).toBe(200); + expect(res.body).toMatchObject(localTestData.entry); + }); - expect(res.statusCode).toBe(403); + test('User B can read the entry created by user A', async () => { + const { id } = localTestData.entry; + const modelName = getModelName(); + const rq = getUserRequest(1); + const res = await rq({ + method: 'GET', + url: `/content-manager/collection-types/api::${modelName}.${modelName}/${id}`, }); - test('User A can delete its entry', async () => { - const { id } = localTestData.entry; - const modelName = getModelName(); - const rq = getUserRequest(0); - const res = await rq({ - method: 'DELETE', - url: `/content-manager/collection-types/api::${modelName}.${modelName}/${id}`, - }); - - expect(res.statusCode).toBe(200); - expect(res.body).toMatchObject(localTestData.entry); + expect(res.statusCode).toBe(200); + expect(res.body).toMatchObject(localTestData.entry); + }); + + test('User B cannot delete the entry created by user A', async () => { + const { id } = localTestData.entry; + const modelName = getModelName(); + const rq = getUserRequest(1); + const res = await rq({ + method: 'DELETE', + url: `/content-manager/collection-types/api::${modelName}.${modelName}/${id}`, }); + + expect(res.statusCode).toBe(403); }); -} else { - describe('Admin Permissions - Conditions ', () => { - test.skip('Only in EE', () => {}); + + test('User A can delete its entry', async () => { + const { id } = localTestData.entry; + const modelName = getModelName(); + const rq = getUserRequest(0); + const res = await rq({ + method: 'DELETE', + url: `/content-manager/collection-types/api::${modelName}.${modelName}/${id}`, + }); + + expect(res.statusCode).toBe(200); + expect(res.body).toMatchObject(localTestData.entry); }); -} +}); diff --git a/packages/core/admin/server/tests/admin-role.test.api.js b/packages/core/admin/server/tests/admin-role.test.api.js index e8ea1e564df..97870746670 100644 --- a/packages/core/admin/server/tests/admin-role.test.api.js +++ b/packages/core/admin/server/tests/admin-role.test.api.js @@ -5,8 +5,6 @@ const _ = require('lodash'); const { createStrapiInstance } = require('../../../../../test/helpers/strapi'); const { createAuthRequest } = require('../../../../../test/helpers/request'); -const edition = process.env.STRAPI_DISABLE_EE === 'true' ? 'CE' : 'EE'; - const data = { rolesWithUsers: [], rolesWithoutUsers: [], @@ -106,644 +104,404 @@ describe('Role CRUD End to End', () => { }); }); - if (edition === 'EE') { - const newPermissions = [ - { - action: 'plugin::users-permissions.roles.update', - }, - { - action: 'plugin::content-manager.explorer.create', - subject: 'plugin::users-permissions.user', - properties: { fields: ['username'], locales: [] }, - conditions: ['admin::is-creator'], - }, - ]; - - test('Conditions of editors and author can be modified', async () => { - let res = await rq({ - url: `/admin/roles/${data.editorRole.id}/permissions`, - method: 'PUT', - body: { permissions: newPermissions }, - }); + const newPermissions = [ + { + action: 'plugin::users-permissions.roles.update', + }, + { + action: 'plugin::content-manager.explorer.create', + subject: 'plugin::users-permissions.user', + properties: { fields: ['username'], locales: [] }, + conditions: ['admin::is-creator'], + }, + ]; + + test('Conditions of editors and author can be modified', async () => { + let res = await rq({ + url: `/admin/roles/${data.editorRole.id}/permissions`, + method: 'PUT', + body: { permissions: newPermissions }, + }); - expect(res.statusCode).toBe(200); - expect(res.body.data).toHaveLength(2); - expect(res.body).toEqual({ - data: expect.arrayContaining([ - expect.objectContaining({ - action: 'plugin::users-permissions.roles.update', - properties: {}, - conditions: [], - subject: null, - }), - expect.objectContaining({ - action: 'plugin::content-manager.explorer.create', - subject: 'plugin::users-permissions.user', - properties: { fields: ['username'], locales: [] }, - conditions: ['admin::is-creator'], - }), - ]), - }); + expect(res.statusCode).toBe(200); + expect(res.body.data).toHaveLength(2); + expect(res.body).toEqual({ + data: expect.arrayContaining([ + expect.objectContaining({ + action: 'plugin::users-permissions.roles.update', + properties: {}, + conditions: [], + subject: null, + }), + expect.objectContaining({ + action: 'plugin::content-manager.explorer.create', + subject: 'plugin::users-permissions.user', + properties: { fields: ['username'], locales: [] }, + conditions: ['admin::is-creator'], + }), + ]), + }); - res = await rq({ - url: `/admin/roles/${data.authorRole.id}/permissions`, - method: 'PUT', - body: { permissions: newPermissions }, - }); + res = await rq({ + url: `/admin/roles/${data.authorRole.id}/permissions`, + method: 'PUT', + body: { permissions: newPermissions }, + }); - expect(res.statusCode).toBe(200); - expect(res.body.data).toHaveLength(2); - expect(res.body).toEqual({ - data: expect.arrayContaining([ - expect.objectContaining({ - action: 'plugin::users-permissions.roles.update', - properties: {}, - conditions: [], - subject: null, - }), - expect.objectContaining({ - action: 'plugin::content-manager.explorer.create', - subject: 'plugin::users-permissions.user', - properties: { fields: ['username'], locales: [] }, - conditions: ['admin::is-creator'], - }), - ]), - }); + expect(res.statusCode).toBe(200); + expect(res.body.data).toHaveLength(2); + expect(res.body).toEqual({ + data: expect.arrayContaining([ + expect.objectContaining({ + action: 'plugin::users-permissions.roles.update', + properties: {}, + conditions: [], + subject: null, + }), + expect.objectContaining({ + action: 'plugin::content-manager.explorer.create', + subject: 'plugin::users-permissions.user', + properties: { fields: ['username'], locales: [] }, + conditions: ['admin::is-creator'], + }), + ]), }); - } else if (edition === 'CE') { - const newPermissions = [ - { - action: 'plugin::users-permissions.roles.update', - }, - { - action: 'plugin::users-permissions.roles.read', - conditions: ['admin::is-creator'], - }, - { - action: 'plugin::content-manager.explorer.create', - subject: 'plugin::users-permissions.user', - properties: { fields: ['username'], locales: [] }, - conditions: ['admin::is-creator'], - }, - { - action: 'plugin::content-manager.explorer.update', - subject: 'plugin::users-permissions.user', - properties: { fields: ['username'], locales: [] }, - conditions: ['admin::is-creator'], - }, - { - action: 'plugin::content-manager.explorer.delete', - subject: 'plugin::users-permissions.user', - properties: { locales: [] }, - conditions: ['admin::is-creator'], - }, - { - action: 'plugin::content-manager.explorer.read', - subject: 'plugin::users-permissions.user', - properties: { fields: ['username'], locales: [] }, - conditions: ['admin::is-creator'], - }, - ]; + }); + }); - test("Conditions of editors and author can't be modified", async () => { - let res = await rq({ - url: `/admin/roles/${data.editorRole.id}/permissions`, - method: 'PUT', - body: { permissions: newPermissions }, - }); + describe('Create some roles', () => { + const rolesToCreate = [ + [{ name: 'new role 0', description: 'description' }], + [{ name: 'new role 1', description: 'description' }], + [{ name: 'new role 2', description: 'description' }], + [{ name: 'new role 3', description: 'description' }], + [{ name: 'new role 4', description: 'description' }], + [{ name: 'new role 5', description: 'description' }], + ]; + + test.each(rolesToCreate)('can create %p', async (role) => { + const res = await rq({ + url: '/admin/roles', + method: 'POST', + body: role, + }); - expect(res.statusCode).toBe(200); - expect(res.body.data).toHaveLength(6); - expect(res.body).toEqual({ - data: expect.arrayContaining( - newPermissions - .slice(3, 6) - .map((p) => ({ ...p, conditions: [] })) - .map((perm) => expect.objectContaining(perm)) - ), - }); + expect(res.statusCode).toBe(201); + expect(res.body.data).toMatchObject({ + id: expect.anything(), + name: role.name, + description: role.description, + }); + data.rolesWithoutUsers.push(res.body.data); + }); + test('Cannot create a role already existing', async () => { + const role = _.pick(data.rolesWithoutUsers[0], ['name', 'description']); + const res = await rq({ + url: '/admin/roles', + method: 'POST', + body: role, + }); - res = await rq({ - url: `/admin/roles/${data.authorRole.id}/permissions`, - method: 'PUT', - body: { permissions: newPermissions }, - }); + expect(res.statusCode).toBe(400); + expect(res.body).toMatchObject({ + data: null, + error: { + details: {}, + message: 'The name must be unique and a role with name `new role 0` already exists.', + name: 'ApplicationError', + status: 400, + }, + }); + }); + test('Can create a user with a role', async () => { + const user = { + email: 'new-user@strapi.io', + firstname: 'New', + lastname: 'User', + roles: [data.rolesWithoutUsers[5].id], + }; - expect(res.statusCode).toBe(200); - expect(res.body.data).toHaveLength(6); - expect(res.body).toEqual({ - data: expect.arrayContaining( - newPermissions - .slice(3, 6) - .map((p) => ({ ...p, conditions: ['admin::is-creator'] })) - .map(expect.objectContaining) - ), - }); + const res = await rq({ + url: '/admin/users', + method: 'POST', + body: user, }); - } - }); - if (edition === 'EE') { - describe('Create some roles', () => { - const rolesToCreate = [ - [{ name: 'new role 0', description: 'description' }], - [{ name: 'new role 1', description: 'description' }], - [{ name: 'new role 2', description: 'description' }], - [{ name: 'new role 3', description: 'description' }], - [{ name: 'new role 4', description: 'description' }], - [{ name: 'new role 5', description: 'description' }], - ]; + expect(res.statusCode).toBe(201); - test.each(rolesToCreate)('can create %p', async (role) => { - const res = await rq({ - url: '/admin/roles', - method: 'POST', - body: role, - }); + data.users.push(res.body.data); + data.rolesWithUsers.push(data.rolesWithoutUsers[5]); + data.rolesWithoutUsers.splice(5, 1); + }); + }); - expect(res.statusCode).toBe(201); - expect(res.body.data).toMatchObject({ - id: expect.anything(), - name: role.name, - description: role.description, - }); - data.rolesWithoutUsers.push(res.body.data); + describe('Find a role', () => { + test('Can find a role successfully', async () => { + const res = await rq({ + url: `/admin/roles/${data.rolesWithoutUsers[0].id}`, + method: 'GET', }); - test('Cannot create a role already existing', async () => { - const role = _.pick(data.rolesWithoutUsers[0], ['name', 'description']); - const res = await rq({ - url: '/admin/roles', - method: 'POST', - body: role, - }); - expect(res.statusCode).toBe(400); - expect(res.body).toMatchObject({ - data: null, - error: { - details: {}, - message: 'The name must be unique and a role with name `new role 0` already exists.', - name: 'ApplicationError', - status: 400, - }, - }); + expect(res.statusCode).toBe(200); + expect(res.body.data).toMatchObject({ + id: data.rolesWithoutUsers[0].id, + name: data.rolesWithoutUsers[0].name, + description: data.rolesWithoutUsers[0].description, + usersCount: 0, + code: expect.anything(), }); - test('Can create a user with a role', async () => { - const user = { - email: 'new-user@strapi.io', - firstname: 'New', - lastname: 'User', - roles: [data.rolesWithoutUsers[5].id], - }; + expect(res.body.data.code.startsWith('new-role-0')).toBe(true); + }); + }); - const res = await rq({ - url: '/admin/users', - method: 'POST', - body: user, - }); + describe('Find all roles', () => { + test('Can find all roles successfully', async () => { + const expectedRolesWithoutUser = data.rolesWithoutUsers.map((r) => ({ + ...r, + usersCount: 0, + })); + const expectedRolesWithUser = data.rolesWithUsers.map((r) => ({ ...r, usersCount: 1 })); + const expectedRoles = expectedRolesWithoutUser.concat(expectedRolesWithUser); - expect(res.statusCode).toBe(201); + const res = await rq({ + url: '/admin/roles', + method: 'GET', + }); - data.users.push(res.body.data); - data.rolesWithUsers.push(data.rolesWithoutUsers[5]); - data.rolesWithoutUsers.splice(5, 1); + expect(res.statusCode).toBe(200); + expectedRoles.forEach((role) => { + expect(res.body.data).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: role.id, + name: role.name, + description: role.description, + usersCount: role.usersCount, + code: expect.anything(), + }), + ]) + ); }); }); + }); - describe('Find a role', () => { - test('Can find a role successfully', async () => { - const res = await rq({ - url: `/admin/roles/${data.rolesWithoutUsers[0].id}`, - method: 'GET', - }); + describe('Update a role', () => { + test('Can update name and description of a role successfully', async () => { + const updates = { + name: 'new name - Cannot update the name of a role', + description: 'new description - Can update a role successfully', + }; + const res = await rq({ + url: `/admin/roles/${data.rolesWithoutUsers[0].id}`, + method: 'PUT', + body: updates, + }); - expect(res.statusCode).toBe(200); - expect(res.body.data).toMatchObject({ - id: data.rolesWithoutUsers[0].id, - name: data.rolesWithoutUsers[0].name, - description: data.rolesWithoutUsers[0].description, - usersCount: 0, - code: expect.anything(), - }); - expect(res.body.data.code.startsWith('new-role-0')).toBe(true); + expect(res.statusCode).toBe(200); + expect(omitTimestamps(res.body.data)).toMatchObject({ + ...omitTimestamps(data.rolesWithoutUsers[0]), + ...updates, }); + data.rolesWithoutUsers[0] = res.body.data; }); - describe('Find all roles', () => { - test('Can find all roles successfully', async () => { - const expectedRolesWithoutUser = data.rolesWithoutUsers.map((r) => ({ - ...r, - usersCount: 0, - })); - const expectedRolesWithUser = data.rolesWithUsers.map((r) => ({ ...r, usersCount: 1 })); - const expectedRoles = expectedRolesWithoutUser.concat(expectedRolesWithUser); - - const res = await rq({ - url: '/admin/roles', - method: 'GET', - }); + test('Can update description of a role successfully', async () => { + const updates = { + name: 'new name - Cannot update the name of a role', + description: 'new description - Can update description of a role successfully', + }; + const res = await rq({ + url: `/admin/roles/${data.rolesWithoutUsers[0].id}`, + method: 'PUT', + body: updates, + }); - expect(res.statusCode).toBe(200); - expectedRoles.forEach((role) => { - expect(res.body.data).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: role.id, - name: role.name, - description: role.description, - usersCount: role.usersCount, - code: expect.anything(), - }), - ]) - ); - }); + expect(res.statusCode).toBe(200); + expect(omitTimestamps(res.body.data)).toMatchObject({ + ...omitTimestamps(data.rolesWithoutUsers[0]), + ...updates, }); + data.rolesWithoutUsers[0] = res.body.data; }); - describe('Update a role', () => { - test('Can update name and description of a role successfully', async () => { - const updates = { - name: 'new name - Cannot update the name of a role', - description: 'new description - Can update a role successfully', - }; - const res = await rq({ - url: `/admin/roles/${data.rolesWithoutUsers[0].id}`, - method: 'PUT', - body: updates, - }); + test('Cannot update the name of a role if already exists', async () => { + const updates = { + name: data.rolesWithoutUsers[0].name, + description: 'new description - Cannot update the name of a role if already exists', + }; + const res = await rq({ + url: `/admin/roles/${data.rolesWithoutUsers[1].id}`, + method: 'PUT', + body: updates, + }); - expect(res.statusCode).toBe(200); - expect(omitTimestamps(res.body.data)).toMatchObject({ - ...omitTimestamps(data.rolesWithoutUsers[0]), - ...updates, - }); - data.rolesWithoutUsers[0] = res.body.data; + expect(res.statusCode).toBe(400); + expect(res.body).toMatchObject({ + data: null, + error: { + details: {}, + message: + 'The name must be unique and a role with name `new name - Cannot update the name of a role` already exists.', + name: 'ApplicationError', + status: 400, + }, }); + }); - test('Can update description of a role successfully', async () => { - const updates = { - name: 'new name - Cannot update the name of a role', - description: 'new description - Can update description of a role successfully', - }; - const res = await rq({ - url: `/admin/roles/${data.rolesWithoutUsers[0].id}`, - method: 'PUT', - body: updates, - }); + test('Cannot update super admin role', async () => { + const updates = { + name: 'new name - Cannot update the name of a role', + description: 'new description - Can update a role successfully', + }; + const res = await rq({ + url: `/admin/roles/${data.superAdminRole.id}`, + method: 'PUT', + body: updates, + }); - expect(res.statusCode).toBe(200); - expect(omitTimestamps(res.body.data)).toMatchObject({ - ...omitTimestamps(data.rolesWithoutUsers[0]), - ...updates, - }); - data.rolesWithoutUsers[0] = res.body.data; - }); - - test('Cannot update the name of a role if already exists', async () => { - const updates = { - name: data.rolesWithoutUsers[0].name, - description: 'new description - Cannot update the name of a role if already exists', - }; - const res = await rq({ - url: `/admin/roles/${data.rolesWithoutUsers[1].id}`, - method: 'PUT', - body: updates, + expect(res.statusCode).toBe(400); + }); + }); + + describe('Delete roles', () => { + describe('batch-delete', () => { + test("Don't delete the roles if some still have assigned users", async () => { + const roles = [data.rolesWithUsers[0], data.rolesWithUsers[0]]; + const rolesIds = roles.map((r) => r.id); + let res = await rq({ + url: '/admin/roles/batch-delete', + method: 'POST', + body: { ids: rolesIds }, }); expect(res.statusCode).toBe(400); expect(res.body).toMatchObject({ data: null, error: { - details: {}, - message: - 'The name must be unique and a role with name `new name - Cannot update the name of a role` already exists.', - name: 'ApplicationError', + details: { + errors: [ + { + message: 'Some roles are still assigned to some users', + name: 'ValidationError', + path: ['ids'], + }, + ], + }, + message: 'Some roles are still assigned to some users', + name: 'ValidationError', status: 400, }, }); - }); - - test('Cannot update super admin role', async () => { - const updates = { - name: 'new name - Cannot update the name of a role', - description: 'new description - Can update a role successfully', - }; - const res = await rq({ - url: `/admin/roles/${data.superAdminRole.id}`, - method: 'PUT', - body: updates, - }); - - expect(res.statusCode).toBe(400); - }); - }); - - describe('Delete roles', () => { - describe('batch-delete', () => { - test("Don't delete the roles if some still have assigned users", async () => { - const roles = [data.rolesWithUsers[0], data.rolesWithUsers[0]]; - const rolesIds = roles.map((r) => r.id); - let res = await rq({ - url: '/admin/roles/batch-delete', - method: 'POST', - body: { ids: rolesIds }, - }); - - expect(res.statusCode).toBe(400); - expect(res.body).toMatchObject({ - data: null, - error: { - details: { - errors: [ - { - message: 'Some roles are still assigned to some users', - name: 'ValidationError', - path: ['ids'], - }, - ], - }, - message: 'Some roles are still assigned to some users', - name: 'ValidationError', - status: 400, - }, - }); - - for (const role of roles) { - res = await rq({ - url: `/admin/roles/${role.id}`, - method: 'GET', - }); - expect(res.statusCode).toBe(200); - expect(res.body.data).toMatchObject(role); - } - }); - - test('Can delete a role', async () => { - let res = await rq({ - url: '/admin/roles/batch-delete', - method: 'POST', - body: { ids: [data.rolesWithoutUsers[0].id] }, - }); - expect(res.statusCode).toBe(200); - expect(res.body.data).toMatchObject([data.rolesWithoutUsers[0]]); + for (const role of roles) { res = await rq({ - url: `/admin/roles/${data.rolesWithoutUsers[0].id}`, + url: `/admin/roles/${role.id}`, method: 'GET', }); - expect(res.statusCode).toBe(404); - - data.deleteRolesIds.push(data.rolesWithoutUsers[0].id); - data.rolesWithoutUsers.shift(); - }); - - test('Can delete two roles', async () => { - const roles = data.rolesWithoutUsers.slice(0, 2); - const rolesIds = roles.map((r) => r.id); - - let res = await rq({ - url: '/admin/roles/batch-delete', - method: 'POST', - body: { ids: rolesIds }, - }); expect(res.statusCode).toBe(200); - expect(res.body.data).toMatchObject(roles); - - for (const roleId of rolesIds) { - res = await rq({ - url: `/admin/roles/${roleId}`, - method: 'GET', - }); - expect(res.statusCode).toBe(404); - data.deleteRolesIds.push(data.rolesWithoutUsers[0].id); - data.rolesWithoutUsers.shift(); - } - }); + expect(res.body.data).toMatchObject(role); + } }); - describe('simple delete', () => { - test('Can delete a role', async () => { - let res = await rq({ - url: `/admin/roles/${data.rolesWithoutUsers[0].id}`, - method: 'DELETE', - }); - expect(res.statusCode).toBe(200); - expect(res.body.data).toMatchObject(data.rolesWithoutUsers[0]); - - res = await rq({ - url: `/admin/roles/${data.rolesWithoutUsers[0].id}`, - method: 'GET', - }); - expect(res.statusCode).toBe(404); + test('Can delete a role', async () => { + let res = await rq({ + url: '/admin/roles/batch-delete', + method: 'POST', + body: { ids: [data.rolesWithoutUsers[0].id] }, + }); + expect(res.statusCode).toBe(200); + expect(res.body.data).toMatchObject([data.rolesWithoutUsers[0]]); - data.deleteRolesIds.push(data.rolesWithoutUsers[0].id); - data.rolesWithoutUsers.shift(); + res = await rq({ + url: `/admin/roles/${data.rolesWithoutUsers[0].id}`, + method: 'GET', }); + expect(res.statusCode).toBe(404); - test("Don't delete a role if it still has assigned users", async () => { - let res = await rq({ - url: `/admin/roles/${data.rolesWithUsers[0].id}`, - method: 'DELETE', - }); + data.deleteRolesIds.push(data.rolesWithoutUsers[0].id); + data.rolesWithoutUsers.shift(); + }); - expect(res.statusCode).toBe(400); - expect(res.body).toMatchObject({ - data: null, - error: { - details: { - errors: [ - { - message: 'Some roles are still assigned to some users', - name: 'ValidationError', - path: ['id'], - }, - ], - }, - message: 'Some roles are still assigned to some users', - name: 'ValidationError', - status: 400, - }, - }); + test('Can delete two roles', async () => { + const roles = data.rolesWithoutUsers.slice(0, 2); + const rolesIds = roles.map((r) => r.id); - res = await rq({ - url: `/admin/roles/${data.rolesWithUsers[0].id}`, - method: 'GET', - }); - expect(res.statusCode).toBe(200); - expect(res.body.data).toMatchObject(data.rolesWithUsers[0]); + let res = await rq({ + url: '/admin/roles/batch-delete', + method: 'POST', + body: { ids: rolesIds }, }); + expect(res.statusCode).toBe(200); + expect(res.body.data).toMatchObject(roles); - test("Can't delete super admin role", async () => { - let res = await rq({ - url: `/admin/roles/${data.superAdminRole.id}`, - method: 'DELETE', - }); - - expect(res.statusCode).toBe(400); - expect(res.body).toMatchObject({ - data: null, - error: { - details: { - errors: [ - { - message: 'You cannot delete the super admin role', - name: 'ValidationError', - path: ['id'], - }, - ], - }, - message: 'You cannot delete the super admin role', - name: 'ValidationError', - status: 400, - }, - }); - + for (const roleId of rolesIds) { res = await rq({ - url: `/admin/roles/${data.rolesWithUsers[0].id}`, + url: `/admin/roles/${roleId}`, method: 'GET', }); - expect(res.statusCode).toBe(200); - expect(res.body.data).toMatchObject(data.rolesWithUsers[0]); - }); + expect(res.statusCode).toBe(404); + data.deleteRolesIds.push(data.rolesWithoutUsers[0].id); + data.rolesWithoutUsers.shift(); + } }); }); - describe("Roles don't exist", () => { - test("Cannot update a role if it doesn't exist", async () => { - const updates = { - name: "new name - Cannot update a role if it doesn't exist", - description: "new description - Cannot update a role if it doesn't exist", - }; - const res = await rq({ - url: `/admin/roles/${data.deleteRolesIds[0]}`, - method: 'PUT', - body: updates, - }); - - expect(res.statusCode).toBe(404); - expect(res.body).toMatchObject({ - error: { - details: {}, - message: 'role.notFound', - name: 'NotFoundError', - status: 404, - }, - }); - }); - - test("Simple delete - No error if deleting a role that doesn't exist", async () => { - const res = await rq({ - url: `/admin/roles/${data.deleteRolesIds[0]}`, + describe('simple delete', () => { + test('Can delete a role', async () => { + let res = await rq({ + url: `/admin/roles/${data.rolesWithoutUsers[0].id}`, method: 'DELETE', }); - expect(res.statusCode).toBe(200); - expect(res.body.data).toEqual(null); - }); - test("Batch Delete - No error if deleting a role that doesn't exist", async () => { - const res = await rq({ - url: '/admin/roles/batch-delete', - method: 'POST', - body: { ids: [data.deleteRolesIds[0]] }, - }); - - expect(res.statusCode).toBe(200); - expect(res.body.data).toEqual([]); - }); - }); + expect(res.body.data).toMatchObject(data.rolesWithoutUsers[0]); - describe('get & update Permissions', () => { - test('get permissions on empty role', async () => { - const res = await rq({ - url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`, + res = await rq({ + url: `/admin/roles/${data.rolesWithoutUsers[0].id}`, method: 'GET', }); + expect(res.statusCode).toBe(404); - expect(res.statusCode).toBe(200); - expect(res.body).toEqual({ - data: [], - }); + data.deleteRolesIds.push(data.rolesWithoutUsers[0].id); + data.rolesWithoutUsers.shift(); }); - test('assign permissions on role', async () => { - const res = await rq({ - url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`, - method: 'PUT', - body: { - permissions: [ - { - action: 'plugin::users-permissions.roles.update', - }, - { - action: 'plugin::content-manager.explorer.create', - subject: 'plugin::users-permissions.user', - properties: { fields: ['username'], locales: [] }, - conditions: ['admin::is-creator'], - }, - ], - }, - }); - - expect(res.statusCode).toBe(200); - expect(res.body.data.length > 0).toBe(true); - res.body.data.forEach((permission) => { - expect(permission).toMatchObject({ - id: expect.anything(), - action: expect.any(String), - subject: expect.stringOrNull(), - }); - - if (permission.conditions.length > 0) { - expect(permission.conditions).toEqual(expect.arrayContaining([expect.any(String)])); - } - if (permission.fields && permission.fields.length > 0) { - expect(permission.fields).toEqual(expect.arrayContaining([expect.any(String)])); - } + test("Don't delete a role if it still has assigned users", async () => { + let res = await rq({ + url: `/admin/roles/${data.rolesWithUsers[0].id}`, + method: 'DELETE', }); - }); - test('assign permissions on role with an unknown condition', async () => { - const permissions = [ - { - action: 'plugin::users-permissions.roles.update', - }, - { - action: 'plugin::content-manager.explorer.create', - subject: 'plugin::users-permissions.user', - properties: { fields: ['username'], locales: [] }, - conditions: ['admin::is-creator'], - }, - ]; - const res = await rq({ - url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`, - method: 'PUT', - body: { - permissions: [ - permissions[0], - { - ...permissions[1], - conditions: [...permissions[1].conditions, 'unknown-condition'], - }, - ], + expect(res.statusCode).toBe(400); + expect(res.body).toMatchObject({ + data: null, + error: { + details: { + errors: [ + { + message: 'Some roles are still assigned to some users', + name: 'ValidationError', + path: ['id'], + }, + ], + }, + message: 'Some roles are still assigned to some users', + name: 'ValidationError', + status: 400, }, }); + res = await rq({ + url: `/admin/roles/${data.rolesWithUsers[0].id}`, + method: 'GET', + }); expect(res.statusCode).toBe(200); - expect(res.body.data).toHaveLength(1); - expect(res.body.data[0]).toMatchObject(permissions[1]); + expect(res.body.data).toMatchObject(data.rolesWithUsers[0]); }); - test("can't assign non-existing permissions on role", async () => { - const res = await rq({ - url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`, - method: 'PUT', - body: { - permissions: [ - { - action: 'non.existing.action', - }, - ], - }, + test("Can't delete super admin role", async () => { + let res = await rq({ + url: `/admin/roles/${data.superAdminRole.id}`, + method: 'DELETE', }); expect(res.statusCode).toBe(400); @@ -753,61 +511,209 @@ describe('Role CRUD End to End', () => { details: { errors: [ { - message: 'action is not an existing permission action', + message: 'You cannot delete the super admin role', name: 'ValidationError', - path: ['permissions', '0', 'action'], + path: ['id'], }, ], }, - message: 'action is not an existing permission action', + message: 'You cannot delete the super admin role', name: 'ValidationError', status: 400, }, }); - }); - test('get permissions role', async () => { - const res = await rq({ - url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`, + res = await rq({ + url: `/admin/roles/${data.rolesWithUsers[0].id}`, method: 'GET', }); - expect(res.statusCode).toBe(200); - expect(res.body.data.length > 0).toBe(true); - res.body.data.forEach((permission) => { - expect(permission).toMatchObject({ - id: expect.anything(), - action: expect.any(String), - subject: expect.stringOrNull(), - }); + expect(res.body.data).toMatchObject(data.rolesWithUsers[0]); + }); + }); + }); + + describe("Roles don't exist", () => { + test("Cannot update a role if it doesn't exist", async () => { + const updates = { + name: "new name - Cannot update a role if it doesn't exist", + description: "new description - Cannot update a role if it doesn't exist", + }; + const res = await rq({ + url: `/admin/roles/${data.deleteRolesIds[0]}`, + method: 'PUT', + body: updates, + }); + + expect(res.statusCode).toBe(404); + expect(res.body).toMatchObject({ + error: { + details: {}, + message: 'role.notFound', + name: 'NotFoundError', + status: 404, + }, + }); + }); + + test("Simple delete - No error if deleting a role that doesn't exist", async () => { + const res = await rq({ + url: `/admin/roles/${data.deleteRolesIds[0]}`, + method: 'DELETE', + }); + + expect(res.statusCode).toBe(200); + expect(res.body.data).toEqual(null); + }); + test("Batch Delete - No error if deleting a role that doesn't exist", async () => { + const res = await rq({ + url: '/admin/roles/batch-delete', + method: 'POST', + body: { ids: [data.deleteRolesIds[0]] }, + }); + + expect(res.statusCode).toBe(200); + expect(res.body.data).toEqual([]); + }); + }); - if (permission.conditions.length > 0) { - expect(permission.conditions).toEqual(expect.arrayContaining([expect.any(String)])); - } - if (permission.fields && permission.fields.length > 0) { - expect(permission.fields).toEqual(expect.arrayContaining([expect.any(String)])); - } + describe('get & update Permissions', () => { + test('get permissions on empty role', async () => { + const res = await rq({ + url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`, + method: 'GET', + }); + + expect(res.statusCode).toBe(200); + expect(res.body).toEqual({ + data: [], + }); + }); + + test('assign permissions on role', async () => { + const res = await rq({ + url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`, + method: 'PUT', + body: { + permissions: [ + { + action: 'plugin::users-permissions.roles.update', + }, + { + action: 'plugin::content-manager.explorer.create', + subject: 'plugin::users-permissions.user', + properties: { fields: ['username'], locales: [] }, + conditions: ['admin::is-creator'], + }, + ], + }, + }); + + expect(res.statusCode).toBe(200); + expect(res.body.data.length > 0).toBe(true); + res.body.data.forEach((permission) => { + expect(permission).toMatchObject({ + id: expect.anything(), + action: expect.any(String), + subject: expect.stringOrNull(), }); + + if (permission.conditions.length > 0) { + expect(permission.conditions).toEqual(expect.arrayContaining([expect.any(String)])); + } + if (permission.fields && permission.fields.length > 0) { + expect(permission.fields).toEqual(expect.arrayContaining([expect.any(String)])); + } }); }); - } - - if (edition === 'CE') { - describe('Cannot create a new role', () => { - test('Cannot create a role successfully', async () => { - const role = { - name: 'new role', - description: 'Description of new role', - }; - - const res = await rq({ - url: '/admin/roles', - method: 'POST', - body: role, + + test('assign permissions on role with an unknown condition', async () => { + const permissions = [ + { + action: 'plugin::users-permissions.roles.update', + }, + { + action: 'plugin::content-manager.explorer.create', + subject: 'plugin::users-permissions.user', + properties: { fields: ['username'], locales: [] }, + conditions: ['admin::is-creator'], + }, + ]; + const res = await rq({ + url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`, + method: 'PUT', + body: { + permissions: [ + permissions[0], + { + ...permissions[1], + conditions: [...permissions[1].conditions, 'unknown-condition'], + }, + ], + }, + }); + + expect(res.statusCode).toBe(200); + expect(res.body.data).toHaveLength(1); + expect(res.body.data[0]).toMatchObject(permissions[1]); + }); + + test("can't assign non-existing permissions on role", async () => { + const res = await rq({ + url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`, + method: 'PUT', + body: { + permissions: [ + { + action: 'non.existing.action', + }, + ], + }, + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toMatchObject({ + data: null, + error: { + details: { + errors: [ + { + message: 'action is not an existing permission action', + name: 'ValidationError', + path: ['permissions', '0', 'action'], + }, + ], + }, + message: 'action is not an existing permission action', + name: 'ValidationError', + status: 400, + }, + }); + }); + + test('get permissions role', async () => { + const res = await rq({ + url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`, + method: 'GET', + }); + + expect(res.statusCode).toBe(200); + expect(res.body.data.length > 0).toBe(true); + + res.body.data.forEach((permission) => { + expect(permission).toMatchObject({ + id: expect.anything(), + action: expect.any(String), + subject: expect.stringOrNull(), }); - expect(res.statusCode).toBe(405); + if (permission.conditions.length > 0) { + expect(permission.conditions).toEqual(expect.arrayContaining([expect.any(String)])); + } + if (permission.fields && permission.fields.length > 0) { + expect(permission.fields).toEqual(expect.arrayContaining([expect.any(String)])); + } }); }); - } + }); }); diff --git a/packages/core/admin/server/tests/admin-user.test.api.js b/packages/core/admin/server/tests/admin-user.test.api.js index 7a04853aa21..2cf6de4d876 100644 --- a/packages/core/admin/server/tests/admin-user.test.api.js +++ b/packages/core/admin/server/tests/admin-user.test.api.js @@ -5,8 +5,6 @@ const { createStrapiInstance } = require('../../../../../test/helpers/strapi'); const { createAuthRequest } = require('../../../../../test/helpers/request'); const { createUtils } = require('../../../../../test/helpers/utils'); -const edition = process.env.STRAPI_DISABLE_EE === 'true' ? 'CE' : 'EE'; - const omitTimestamps = omit(['updatedAt', 'createdAt']); const omitRegistrationToken = omit(['registrationToken']); @@ -59,11 +57,7 @@ describe('Admin User CRUD (api)', () => { rq = await createAuthRequest({ strapi }); utils = createUtils(strapi); - if (edition === 'EE') { - testData.role = await createUserRole(); - } else { - testData.role = await utils.getSuperAdminRole(); - } + testData.role = await createUserRole(); testData.firstSuperAdminUser = rq.getLoggedUser(); testData.superAdminRole = await utils.getSuperAdminRole(); @@ -71,9 +65,8 @@ describe('Admin User CRUD (api)', () => { // Cleanup actions afterAll(async () => { - if (edition === 'EE') { - await utils.deleteRolesById([testData.role.id]); - } + await utils.deleteRolesById([testData.role.id]); + await strapi.destroy(); }); diff --git a/packages/core/admin/server/validation/permission.js b/packages/core/admin/server/validation/permission.js index 7fea301f86c..11cf98640b3 100644 --- a/packages/core/admin/server/validation/permission.js +++ b/packages/core/admin/server/validation/permission.js @@ -1,83 +1,9 @@ 'use strict'; -const _ = require('lodash'); const { yup, validateYupSchema } = require('@strapi/utils'); const { getService } = require('../utils'); -const { AUTHOR_CODE, PUBLISH_ACTION } = require('../services/constants'); -const { - BOUND_ACTIONS_FOR_FIELDS, - BOUND_ACTIONS, - getBoundActionsBySubject, -} = require('../domain/role'); const validators = require('./common-validators'); -// validatedUpdatePermissionsInput - -const actionFieldsAreEqual = (a, b) => { - const aFields = a.properties.fields || []; - const bFields = b.properties.fields || []; - - return _.isEqual(aFields.sort(), bFields.sort()); -}; - -const haveSameFieldsAsOtherActions = (a, i, allActions) => - allActions.slice(i + 1).every((b) => actionFieldsAreEqual(a, b)); - -const checkPermissionsAreBound = (role) => - function (permissions) { - const permsBySubject = _.groupBy( - permissions.filter((perm) => BOUND_ACTIONS.includes(perm.action)), - 'subject' - ); - - for (const [subject, perms] of Object.entries(permsBySubject)) { - const boundActions = getBoundActionsBySubject(role, subject); - const missingActions = - _.xor( - perms.map((p) => p.action), - boundActions - ).length !== 0; - if (missingActions) return false; - - const permsBoundByFields = perms.filter((p) => BOUND_ACTIONS_FOR_FIELDS.includes(p.action)); - const everyActionsHaveSameFields = _.every(permsBoundByFields, haveSameFieldsAsOtherActions); - if (!everyActionsHaveSameFields) return false; - } - - return true; - }; - -const noPublishPermissionForAuthorRole = (role) => - function (permissions) { - const isAuthor = role.code === AUTHOR_CODE; - const hasPublishPermission = permissions.some((perm) => perm.action === PUBLISH_ACTION); - - return !(isAuthor && hasPublishPermission); - }; - -const getUpdatePermissionsSchemas = (role) => [ - validators.updatePermissions, - yup.object().shape({ permissions: actionsExistSchema.clone() }), - yup.object().shape({ - permissions: yup - .array() - .test( - 'author-no-publish', - 'The author role cannot have the publish permission.', - noPublishPermissionForAuthorRole(role) - ), - }), - yup.object().shape({ - permissions: yup - .array() - .test( - 'are-bond', - 'Permissions have to be defined all together for a subject field or not at all', - checkPermissionsAreBound(role) - ), - }), -]; - const checkPermissionsSchema = yup.object().shape({ permissions: yup.array().of( yup @@ -91,13 +17,6 @@ const checkPermissionsSchema = yup.object().shape({ ), }); -const validatedUpdatePermissionsInput = async (permissions, role) => { - const schemas = getUpdatePermissionsSchemas(role); - for (const schema of schemas) { - await validateYupSchema(schema)(permissions); - } -}; - // validatePermissionsExist const checkPermissionsExist = function (permissions) { @@ -131,7 +50,8 @@ const actionsExistSchema = yup // exports module.exports = { - validatedUpdatePermissionsInput, + // validatedUpdatePermissionsInput, + validatedUpdatePermissionsInput: validateYupSchema(validators.updatePermissions), validatePermissionsExist: validateYupSchema(actionsExistSchema), validateCheckPermissionsInput: validateYupSchema(checkPermissionsSchema), }; diff --git a/packages/core/admin/server/validation/role.js b/packages/core/admin/server/validation/role.js index 63fc5c27192..5e943ed1773 100644 --- a/packages/core/admin/server/validation/role.js +++ b/packages/core/admin/server/validation/role.js @@ -2,6 +2,47 @@ const { yup, validateYupSchema } = require('@strapi/utils'); +const roleCreateSchema = yup + .object() + .shape({ + name: yup.string().min(1).required(), + description: yup.string().nullable(), + }) + .noUnknown(); + +const rolesDeleteSchema = yup + .object() + .shape({ + ids: yup + .array() + .of(yup.strapiID()) + .min(1) + .required() + .test('roles-deletion-checks', 'Roles deletion checks have failed', async function (ids) { + try { + await strapi.admin.services.role.checkRolesIdForDeletion(ids); + } catch (e) { + return this.createError({ path: 'ids', message: e.message }); + } + + return true; + }), + }) + .noUnknown(); + +const roleDeleteSchema = yup + .strapiID() + .required() + .test('no-admin-single-delete', 'Role deletion checks have failed', async function (id) { + try { + await strapi.admin.services.role.checkRolesIdForDeletion([id]); + } catch (e) { + return this.createError({ path: 'id', message: e.message }); + } + + return true; + }); + const roleUpdateSchema = yup .object() .shape({ @@ -12,4 +53,7 @@ const roleUpdateSchema = yup module.exports = { validateRoleUpdateInput: validateYupSchema(roleUpdateSchema), + validateRoleCreateInput: validateYupSchema(roleCreateSchema), + validateRolesDeleteInput: validateYupSchema(rolesDeleteSchema), + validateRoleDeleteInput: validateYupSchema(roleDeleteSchema), }; From 6a003d738c2af89ed0c56dffdec5371fc756d271 Mon Sep 17 00:00:00 2001 From: Alexandre Bodin Date: Mon, 6 Mar 2023 22:15:57 +0100 Subject: [PATCH 2/8] Move frontend --- .../src/components/UpgradePlanModal/index.js | 123 ----- .../UpgradePlanModal/tests/index.test.js | 66 --- .../pages/Roles/CreatePage/index.js | 270 ++++++++++- .../tests/__snapshots__/index.test.js.snap | 0 .../Roles/CreatePage/tests/index.test.js | 4 +- .../pages/Roles/CreatePage/utils/schema.js | 0 .../ConditionsModal/ActionRow/index.js | 3 +- .../ActionRow/utils/constants.js | 3 - .../ContentTypeCollapse/Collapse/index.js | 5 +- .../Collapse/utils/constants.js | 3 - .../CollapsePropertyMatrix/ActionRow/index.js | 5 +- .../ActionRow/utils/constants.js | 3 - .../SubActionRow/index.js | 5 +- .../SubActionRow/utils/constants.js | 3 - .../components/GlobalActions/index.js | 3 +- .../GlobalActions/utils/constants.js | 3 - .../PluginsAndSettings/SubCategory/index.js | 10 +- .../SubCategory/utils/constants.js | 3 - .../pages/Roles/ListPage/index.js | 427 +++++++++++++----- .../pages/Roles/ListPage/reducer.js | 0 .../Roles/ListPage/tests/reducer.test.js | 0 .../pages/SettingsPage/utils/defaultRoutes.js | 28 +- .../core/admin/admin/src/translations/ca.json | 20 +- .../core/admin/admin/src/translations/de.json | 6 - .../core/admin/admin/src/translations/dk.json | 6 - .../core/admin/admin/src/translations/en.json | 6 - .../core/admin/admin/src/translations/es.json | 6 - .../core/admin/admin/src/translations/eu.json | 6 - .../core/admin/admin/src/translations/fr.json | 6 - .../core/admin/admin/src/translations/gu.json | 6 - .../core/admin/admin/src/translations/he.json | 6 - .../core/admin/admin/src/translations/hi.json | 6 - .../core/admin/admin/src/translations/hu.json | 6 - .../core/admin/admin/src/translations/id.json | 6 - .../core/admin/admin/src/translations/it.json | 6 - .../core/admin/admin/src/translations/ja.json | 6 - .../core/admin/admin/src/translations/ko.json | 6 - .../core/admin/admin/src/translations/ml.json | 6 - .../core/admin/admin/src/translations/nl.json | 6 - .../core/admin/admin/src/translations/no.json | 6 - .../core/admin/admin/src/translations/pl.json | 6 - .../admin/admin/src/translations/pt-BR.json | 6 - .../core/admin/admin/src/translations/ru.json | 6 - .../core/admin/admin/src/translations/sa.json | 6 - .../core/admin/admin/src/translations/sk.json | 6 - .../core/admin/admin/src/translations/sv.json | 6 - .../core/admin/admin/src/translations/th.json | 6 - .../core/admin/admin/src/translations/tr.json | 6 - .../admin/admin/src/translations/zh-Hans.json | 6 - .../core/admin/admin/src/translations/zh.json | 6 - .../pages/Roles/CreatePage/index.js | 270 ----------- .../ActionRow/utils/constants.js | 3 - .../MenuList/utils/constants.js | 3 - .../Collapse/utils/constants.js | 3 - .../ActionRow/utils/constants.js | 3 - .../SubActionRow/utils/constants.js | 3 - .../GlobalActions/utils/constants.js | 3 - .../SubCategory/utils/constants.js | 3 - .../pages/Roles/ListPage/index.js | 376 --------------- .../pages/Roles/ListPage/tests/index.test.js | 165 ------- .../pages/Roles/ProtectedListPage/index.js | 12 - 61 files changed, 607 insertions(+), 1386 deletions(-) delete mode 100644 packages/core/admin/admin/src/components/UpgradePlanModal/index.js delete mode 100644 packages/core/admin/admin/src/components/UpgradePlanModal/tests/index.test.js rename packages/core/admin/{ee/admin => admin/src}/pages/SettingsPage/pages/Roles/CreatePage/tests/__snapshots__/index.test.js.snap (100%) rename packages/core/admin/{ee/admin => admin/src}/pages/SettingsPage/pages/Roles/CreatePage/tests/index.test.js (90%) rename packages/core/admin/{ee/admin => admin/src}/pages/SettingsPage/pages/Roles/CreatePage/utils/schema.js (100%) delete mode 100644 packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/utils/constants.js delete mode 100644 packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/utils/constants.js delete mode 100644 packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/ActionRow/utils/constants.js delete mode 100644 packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/SubActionRow/utils/constants.js delete mode 100644 packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/GlobalActions/utils/constants.js delete mode 100644 packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/utils/constants.js rename packages/core/admin/{ee/admin => admin/src}/pages/SettingsPage/pages/Roles/ListPage/reducer.js (100%) rename packages/core/admin/{ee/admin => admin/src}/pages/SettingsPage/pages/Roles/ListPage/tests/reducer.test.js (100%) delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/index.js delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/utils/constants.js delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ConditionsSelect/MenuList/utils/constants.js delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/utils/constants.js delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/ActionRow/utils/constants.js delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/SubActionRow/utils/constants.js delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/GlobalActions/utils/constants.js delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/utils/constants.js delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/index.js delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/tests/index.test.js delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ProtectedListPage/index.js diff --git a/packages/core/admin/admin/src/components/UpgradePlanModal/index.js b/packages/core/admin/admin/src/components/UpgradePlanModal/index.js deleted file mode 100644 index 0338d53492a..00000000000 --- a/packages/core/admin/admin/src/components/UpgradePlanModal/index.js +++ /dev/null @@ -1,123 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import styled from 'styled-components'; -import { useIntl } from 'react-intl'; -import { Portal, FocusTrap, IconButton, Box, Flex, Typography } from '@strapi/design-system'; -import { LinkButton } from '@strapi/design-system/v2'; -import { ExternalLink, Cross } from '@strapi/icons'; -import { setHexOpacity, useLockScroll } from '@strapi/helper-plugin'; -import AirBalloon from '../../assets/images/hot-air-balloon.png'; -import BigArrow from '../../assets/images/upgrade-details.png'; - -const UpgradeWrapper = styled.div` - position: absolute; - z-index: 3; - inset: 0; - background: ${({ theme }) => setHexOpacity(theme.colors.neutral800, 0.2)}; - padding: 0 ${({ theme }) => theme.spaces[8]}; -`; - -const UpgradeContainer = styled(Flex)` - position: relative; - max-width: ${830 / 16}rem; - height: ${415 / 16}rem; - margin: 0 auto; - overflow: hidden; - margin-top: 10%; - padding-left: ${64 / 16}rem; - - img:first-of-type { - position: absolute; - right: 0; - top: 0; - max-width: ${360 / 16}rem; - } - - img:not(:first-of-type) { - width: ${130 / 16}rem; - margin-left: 12%; - margin-right: ${20 / 16}rem; - z-index: 0; - } -`; - -const FlexStart = styled(Flex)` - max-width: ${400 / 16}rem; - z-index: 0; -`; - -const CloseButtonContainer = styled(Box)` - position: absolute; - right: ${({ theme }) => theme.spaces[4]}; - top: ${({ theme }) => theme.spaces[4]}; -`; - -const UpgradePlanModal = ({ onClose, isOpen }) => { - useLockScroll(isOpen); - const { formatMessage } = useIntl(); - - if (!isOpen) { - return null; - } - - return ( - - - - e.stopPropagation()} - aria-labelledby="upgrade-plan" - background="neutral0" - hasRadius - > - air-balloon - - } /> - - - - {formatMessage({ - id: 'app.components.UpgradePlanModal.text-ce', - defaultMessage: 'COMMUNITY EDITION', - })} - - - - {formatMessage({ - id: 'app.components.UpgradePlanModal.limit-reached', - defaultMessage: 'You have reached the limit', - })} - - - {formatMessage({ - id: 'app.components.UpgradePlanModal.text-power', - defaultMessage: - 'Unlock the full power of Strapi by upgrading your plan to the Enterprise Edition', - })} - - - } - > - {formatMessage({ - id: 'app.components.UpgradePlanModal.button', - defaultMessage: 'Learn more', - })} - - - upgrade-arrow - - - - - ); -}; - -UpgradePlanModal.propTypes = { - onClose: PropTypes.func.isRequired, - isOpen: PropTypes.bool.isRequired, -}; - -export default UpgradePlanModal; diff --git a/packages/core/admin/admin/src/components/UpgradePlanModal/tests/index.test.js b/packages/core/admin/admin/src/components/UpgradePlanModal/tests/index.test.js deleted file mode 100644 index f77343b998c..00000000000 --- a/packages/core/admin/admin/src/components/UpgradePlanModal/tests/index.test.js +++ /dev/null @@ -1,66 +0,0 @@ -import React from 'react'; -import { render, screen, waitFor } from '@testing-library/react'; -import { IntlProvider } from 'react-intl'; -import { ThemeProvider, lightTheme } from '@strapi/design-system'; -import UpgradePlanModal from '../index'; - -const App = ( - - - - - -); - -describe('UpgradePlanModal', () => { - it('renders and matches the snapshot', async () => { - const { - container: { firstChild }, - } = render(App); - - expect(firstChild).toMatchInlineSnapshot(` - .c0 { - border: 0; - -webkit-clip: rect(0 0 0 0); - clip: rect(0 0 0 0); - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px; - } - -
-

-

-

- `); - }); - - it('renders and matches the snapshot', async () => { - render(App); - - await waitFor(() => { - expect(screen.getByText('You have reached the limit')).toBeInTheDocument(); - }); - }); -}); diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/index.js index 01f1a7a2e71..3f3db74d443 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/index.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/index.js @@ -1,8 +1,270 @@ -import React from 'react'; -import { Redirect } from 'react-router-dom'; +import React, { useRef, useState } from 'react'; +import { format } from 'date-fns'; +import { + CheckPagePermissions, + Form, + LoadingIndicatorPage, + SettingsPageTitle, + request, + useNotification, + useOverlayBlocker, + useTracking, + Link, +} from '@strapi/helper-plugin'; +import { + Box, + Button, + ContentLayout, + HeaderLayout, + Grid, + GridItem, + Main, + Flex, + Typography, + TextInput, + Textarea, +} from '@strapi/design-system'; +import { ArrowLeft } from '@strapi/icons'; +import { Formik } from 'formik'; +import get from 'lodash/get'; +import isEmpty from 'lodash/isEmpty'; +import { useIntl } from 'react-intl'; +import { useHistory, useRouteMatch } from 'react-router-dom'; +import styled from 'styled-components'; +import Permissions from '../EditPage/components/Permissions'; +import { useFetchPermissionsLayout, useFetchRole } from '../../../../../hooks'; +import adminPermissions from '../../../../../permissions'; +import schema from './utils/schema'; + +const UsersRoleNumber = styled.div` + border: 1px solid ${({ theme }) => theme.colors.primary200}; + background: ${({ theme }) => theme.colors.primary100}; + padding: ${({ theme }) => `${theme.spaces[2]} ${theme.spaces[4]}`}; + color: ${({ theme }) => theme.colors.primary600}; + border-radius: ${({ theme }) => theme.borderRadius}; + font-size: ${12 / 16}rem; + font-weight: bold; +`; const CreatePage = () => { - return ; + const toggleNotification = useNotification(); + const { lockApp, unlockApp } = useOverlayBlocker(); + const { formatMessage } = useIntl(); + const [isSubmitting, setIsSubmiting] = useState(false); + const { replace } = useHistory(); + const permissionsRef = useRef(); + const { trackUsage } = useTracking(); + const params = useRouteMatch('/settings/roles/duplicate/:id'); + const id = get(params, 'params.id', null); + const { isLoading: isLayoutLoading, data: permissionsLayout } = useFetchPermissionsLayout(); + const { permissions: rolePermissions, isLoading: isRoleLoading } = useFetchRole(id); + + const handleCreateRoleSubmit = (data) => { + lockApp(); + setIsSubmiting(true); + + if (id) { + trackUsage('willDuplicateRole'); + } else { + trackUsage('willCreateNewRole'); + } + + Promise.resolve( + request('/admin/roles', { + method: 'POST', + body: data, + }) + ) + .then(async (res) => { + const { permissionsToSend } = permissionsRef.current.getPermissions(); + + if (id) { + trackUsage('didDuplicateRole'); + } else { + trackUsage('didCreateNewRole'); + } + + if (res.data.id && !isEmpty(permissionsToSend)) { + await request(`/admin/roles/${res.data.id}/permissions`, { + method: 'PUT', + body: { permissions: permissionsToSend }, + }); + } + + return res; + }) + .then((res) => { + setIsSubmiting(false); + toggleNotification({ + type: 'success', + message: { id: 'Settings.roles.created', defaultMessage: 'created' }, + }); + replace(`/settings/roles/${res.data.id}`); + }) + .catch((err) => { + console.error(err); + setIsSubmiting(false); + toggleNotification({ + type: 'warning', + message: { id: 'notification.error' }, + }); + }) + .finally(() => { + unlockApp(); + }); + }; + + const defaultDescription = `${formatMessage({ + id: 'Settings.roles.form.created', + defaultMessage: 'Created', + })} ${format(new Date(), 'PPP')}`; + + return ( +
+ + + {({ handleSubmit, values, errors, handleReset, handleChange }) => ( +
+ <> + + + + + } + title={formatMessage({ + id: 'Settings.roles.create.title', + defaultMessage: 'Create a role', + })} + subtitle={formatMessage({ + id: 'Settings.roles.create.description', + defaultMessage: 'Define the rights given to the role', + })} + navigationAction={ + } to="/settings/roles"> + {formatMessage({ + id: 'global.back', + defaultMessage: 'Back', + })} + + } + /> + + + + + + + + + {formatMessage({ + id: 'global.details', + defaultMessage: 'Details', + })} + + + + + {formatMessage({ + id: 'Settings.roles.form.description', + defaultMessage: 'Name and description of the role', + })} + + + + + {formatMessage( + { + id: 'Settings.roles.form.button.users-with-role', + defaultMessage: + '{number, plural, =0 {# users} one {# user} other {# users}} with this role', + }, + { number: 0 } + )} + + + + + + + + + + + + + {!isLayoutLoading && !isRoleLoading ? ( + + + + ) : ( + + + + )} + + + + + )} +
+
+ ); }; -export default CreatePage; +export default function () { + return ( + + + + ); +} + +export { CreatePage }; diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/tests/__snapshots__/index.test.js.snap b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/tests/__snapshots__/index.test.js.snap similarity index 100% rename from packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/tests/__snapshots__/index.test.js.snap rename to packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/tests/__snapshots__/index.test.js.snap diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/tests/index.test.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/tests/index.test.js similarity index 90% rename from packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/tests/index.test.js rename to packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/tests/index.test.js index 4f194a00923..d3a718a2016 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/tests/index.test.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/tests/index.test.js @@ -10,8 +10,8 @@ import { IntlProvider } from 'react-intl'; import { Router, Switch, Route } from 'react-router-dom'; import { createMemoryHistory } from 'history'; import { lightTheme, darkTheme } from '@strapi/design-system'; -import Theme from '../../../../../../../../admin/src/components/Theme'; -import ThemeToggleProvider from '../../../../../../../../admin/src/components/ThemeToggleProvider'; +import Theme from '../../../../../../components/Theme'; +import ThemeToggleProvider from '../../../../../../components/ThemeToggleProvider'; import { CreatePage } from '../index'; diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/utils/schema.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/utils/schema.js similarity index 100% rename from packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/utils/schema.js rename to packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/utils/schema.js diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/index.js index 41a8f7a184c..697304e51d6 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/index.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/index.js @@ -1,6 +1,5 @@ import React from 'react'; import PropTypes from 'prop-types'; -import IS_DISABLED from 'ee_else_ce/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/utils/constants'; import { Box, Flex, Typography, MultiSelectNested } from '@strapi/design-system'; import { useIntl } from 'react-intl'; @@ -52,7 +51,7 @@ const ActionRow = ({ onChange={handleChange} value={getSelectedValues(value)} options={getNestedOptions(arrayOfOptionsGroupedByCategory)} - disabled={isFormDisabled || IS_DISABLED} + disabled={isFormDisabled} /> diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/utils/constants.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/utils/constants.js deleted file mode 100644 index 7cb5eb2dac0..00000000000 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/utils/constants.js +++ /dev/null @@ -1,3 +0,0 @@ -const IS_DISABLED = true; - -export default IS_DISABLED; diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/index.js index 447d20f41ad..93db5b5a3c1 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/index.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/index.js @@ -6,7 +6,6 @@ import get from 'lodash/get'; import omit from 'lodash/omit'; import { useIntl } from 'react-intl'; import styled from 'styled-components'; -import IS_DISABLED from 'ee_else_ce/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/utils/constants'; import { usePermissionsDataManager } from '../../../../../../../../hooks'; import ConditionsButton from '../../ConditionsButton'; import ConditionsModal from '../../ConditionsModal'; @@ -179,7 +178,7 @@ const Collapse = ({ {hasConditions && } {hasConditions && } { diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/ActionRow/utils/constants.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/ActionRow/utils/constants.js deleted file mode 100644 index 7cb5eb2dac0..00000000000 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/ActionRow/utils/constants.js +++ /dev/null @@ -1,3 +0,0 @@ -const IS_DISABLED = true; - -export default IS_DISABLED; diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/SubActionRow/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/SubActionRow/index.js index dfe24614d22..bc7e456d120 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/SubActionRow/index.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/SubActionRow/index.js @@ -5,7 +5,6 @@ import upperFirst from 'lodash/upperFirst'; import { useIntl } from 'react-intl'; import styled from 'styled-components'; import { BaseCheckbox, Box, Flex, Typography } from '@strapi/design-system'; -import IS_DISABLED from 'ee_else_ce/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/SubActionRow/utils/constants'; import { usePermissionsDataManager } from '../../../../../../../../../hooks'; import CollapseLabel from '../../../CollapseLabel'; import Curve from '../../../Curve'; @@ -150,7 +149,7 @@ const SubActionRow = ({ return ( { })} { onChangeCollectionTypeGlobalActionCheckbox(kind, actionId, value); }} diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/GlobalActions/utils/constants.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/GlobalActions/utils/constants.js deleted file mode 100644 index 7cb5eb2dac0..00000000000 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/GlobalActions/utils/constants.js +++ /dev/null @@ -1,3 +0,0 @@ -const IS_DISABLED = true; - -export default IS_DISABLED; diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/index.js index 91527ff0827..9a81fff6d15 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/index.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/index.js @@ -4,7 +4,6 @@ import PropTypes from 'prop-types'; import { Grid, GridItem, Box, Checkbox, Flex, Typography } from '@strapi/design-system'; import { useIntl } from 'react-intl'; import get from 'lodash/get'; -import IS_DISABLED from 'ee_else_ce/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/utils/constants'; import { usePermissionsDataManager } from '../../../../../../../../hooks'; import { getCheckboxState, removeConditionKeyFromData } from '../../utils'; import ConditionsButton from '../../ConditionsButton'; @@ -77,7 +76,7 @@ const SubCategory = ({ categoryName, isFormDisabled, subCategoryName, actions, p { onChangeParentCheckbox({ @@ -99,13 +98,10 @@ const SubCategory = ({ categoryName, isFormDisabled, subCategoryName, actions, p {formattedActions.map(({ checkboxName, value, action, displayName, hasConditions }) => { return ( - + { onChangeSimpleCheckbox({ diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/utils/constants.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/utils/constants.js deleted file mode 100644 index 7cb5eb2dac0..00000000000 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/utils/constants.js +++ /dev/null @@ -1,3 +0,0 @@ -const IS_DISABLED = true; - -export default IS_DISABLED; diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/index.js index 98769f28203..ee1f34eb17f 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/index.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/index.js @@ -1,49 +1,138 @@ -import React, { useCallback, useState } from 'react'; -import matchSorter from 'match-sorter'; +import React, { useCallback, useEffect, useReducer, useState } from 'react'; import { + ConfirmDialog, + LoadingIndicatorPage, + SearchURLQuery, SettingsPageTitle, - useQuery, - useTracking, + getFetchClient, + useNotification, + useQueryParams, + useRBAC, useFocusWhenNavigate, } from '@strapi/helper-plugin'; -import { Plus, Trash, Pencil, Duplicate } from '@strapi/icons'; +import { Plus, Trash, Duplicate, Pencil } from '@strapi/icons'; import { Button, + ActionLayout, ContentLayout, HeaderLayout, + Main, Table, Tbody, - Th, + TFooter, Thead, + Th, Tr, - TFooter, Typography, - Main, VisuallyHidden, } from '@strapi/design-system'; - +import { get } from 'lodash'; +import matchSorter from 'match-sorter'; import { useIntl } from 'react-intl'; import { useHistory } from 'react-router-dom'; -import RoleRow from './components/RoleRow'; -import EmptyRole from './components/EmptyRole'; -import UpgradePlanModal from '../../../../../components/UpgradePlanModal'; import { useRolesList } from '../../../../../hooks'; +import adminPermissions from '../../../../../permissions'; +import EmptyRole from './components/EmptyRole'; +import BaseRoleRow from './components/RoleRow'; +import reducer, { initialState } from './reducer'; const useSortedRoles = () => { - const { roles, isLoading } = useRolesList(); + useFocusWhenNavigate(); + const { + isLoading: isLoadingForPermissions, + allowedActions: { canCreate, canDelete, canRead, canUpdate }, + } = useRBAC(adminPermissions.settings.roles); - const query = useQuery(); - const _q = decodeURIComponent(query.get('_q') || ''); + const { getData, roles, isLoading } = useRolesList(false); + const [{ query }] = useQueryParams(); + const _q = query?._q || ''; const sortedRoles = matchSorter(roles, _q, { keys: ['name', 'description'] }); - return { isLoading, sortedRoles }; + useEffect(() => { + if (!isLoadingForPermissions && canRead) { + getData(); + } + }, [isLoadingForPermissions, canRead, getData]); + + return { + isLoadingForPermissions, + canCreate, + canDelete, + canRead, + canUpdate, + isLoading, + getData, + sortedRoles, + roles, + }; }; -const useRoleActions = () => { +const useRoleActions = ({ getData, canCreate, canDelete, canUpdate }) => { const { formatMessage } = useIntl(); - const [isModalOpen, setIsModalOpen] = useState(false); - const { trackUsage } = useTracking(); + + const toggleNotification = useNotification(); + const [isWarningDeleteAllOpened, setIsWarningDeleteAllOpenend] = useState(false); const { push } = useHistory(); + const [{ selectedRoles, showModalConfirmButtonLoading, roleToDelete }, dispatch] = useReducer( + reducer, + initialState + ); + + const { post } = getFetchClient(); + + const handleDeleteData = async () => { + try { + dispatch({ + type: 'ON_REMOVE_ROLES', + }); + + await post('/admin/roles/batch-delete', { + ids: [roleToDelete], + }); + + await getData(); + + dispatch({ + type: 'RESET_DATA_TO_DELETE', + }); + } catch (err) { + const errorIds = get(err, ['response', 'payload', 'data', 'ids'], null); + + if (errorIds && Array.isArray(errorIds)) { + const errorsMsg = errorIds.join('\n'); + toggleNotification({ + type: 'warning', + message: errorsMsg, + }); + } else { + toggleNotification({ + type: 'warning', + message: { id: 'notification.error' }, + }); + } + } + handleToggleModal(); + }; + + const onRoleDuplicate = useCallback( + (id) => { + push(`/settings/roles/duplicate/${id}`); + }, + [push] + ); + + const handleNewRoleClick = () => push('/settings/roles/new'); + + const onRoleRemove = useCallback((roleId) => { + dispatch({ + type: 'SET_ROLE_TO_DELETE', + id: roleId, + }); + + handleToggleModal(); + }, []); + + const handleToggleModal = () => setIsWarningDeleteAllOpenend((prev) => !prev); const handleGoTo = useCallback( (id) => { @@ -52,144 +141,234 @@ const useRoleActions = () => { [push] ); - const handleToggle = useCallback(() => { - setIsModalOpen((prev) => !prev); - }, []); + const handleClickDelete = useCallback( + (e, role) => { + e.preventDefault(); + e.stopPropagation(); - const handleToggleModalForCreatingRole = useCallback(() => { - trackUsage('didShowRBACUpgradeModal'); - setIsModalOpen(true); - }, [trackUsage]); + if (role.usersCount) { + toggleNotification({ + type: 'info', + message: { id: 'Roles.ListPage.notification.delete-not-allowed' }, + }); + } else { + onRoleRemove(role.id); + } + }, + [toggleNotification, onRoleRemove] + ); + + const handleClickDuplicate = useCallback( + (e, role) => { + e.preventDefault(); + e.stopPropagation(); + onRoleDuplicate(role.id); + }, + [onRoleDuplicate] + ); const getIcons = useCallback( (role) => [ - { - onClick: handleToggle, - label: formatMessage({ id: 'app.utils.duplicate', defaultMessage: 'Duplicate' }), - icon: , - }, - { - onClick: () => handleGoTo(role.id), - label: formatMessage({ id: 'app.utils.edit', defaultMessage: 'Edit' }), - icon: , - }, - { - onClick: handleToggle, - label: formatMessage({ id: 'global.delete', defaultMessage: 'Delete' }), - icon: , - }, + ...(canCreate + ? [ + { + onClick: (e) => handleClickDuplicate(e, role), + label: formatMessage({ id: 'app.utils.duplicate', defaultMessage: 'Duplicate' }), + icon: , + }, + ] + : []), + ...(canUpdate + ? [ + { + onClick: () => handleGoTo(role.id), + label: formatMessage({ id: 'app.utils.edit', defaultMessage: 'Edit' }), + icon: , + }, + ] + : []), + ...(canDelete + ? [ + { + onClick: (e) => handleClickDelete(e, role), + label: formatMessage({ id: 'global.delete', defaultMessage: 'Delete' }), + icon: , + }, + ] + : []), ], - [formatMessage, handleToggle, handleGoTo] + [ + formatMessage, + handleClickDelete, + handleClickDuplicate, + handleGoTo, + canCreate, + canUpdate, + canDelete, + ] ); return { - isModalOpen, - handleToggleModalForCreatingRole, - handleToggle, + handleNewRoleClick, getIcons, + selectedRoles, + isWarningDeleteAllOpened, + showModalConfirmButtonLoading, + handleToggleModal, + handleDeleteData, }; }; const RoleListPage = () => { const { formatMessage } = useIntl(); - useFocusWhenNavigate(); - const { sortedRoles, isLoading } = useSortedRoles(); - const { isModalOpen, handleToggle, handleToggleModalForCreatingRole, getIcons } = - useRoleActions(); + const { + isLoadingForPermissions, + canCreate, + canRead, + canDelete, + canUpdate, + isLoading, + getData, + sortedRoles, + } = useSortedRoles(); + + const { + handleNewRoleClick, + getIcons, + isWarningDeleteAllOpened, + showModalConfirmButtonLoading, + handleToggleModal, + handleDeleteData, + } = useRoleActions({ getData, canCreate, canDelete, canUpdate }); + + // ! TODO - Show the search bar only if the user is allowed to read - add the search input + // canRead const rowCount = sortedRoles.length + 1; - const colCount = 5; + const colCount = 6; + + if (isLoadingForPermissions) { + return ( +
+ +
+ ); + } - // ! TODO - Add the search input + const title = formatMessage({ + id: 'global.roles', + defaultMessage: 'roles', + }); return (
} size="S"> - {formatMessage({ - id: 'Settings.roles.list.button.add', - defaultMessage: 'Add new role', - })} - + canCreate ? ( + + ) : null } - title={formatMessage({ - id: 'global.roles', - defaultMessage: 'roles', - })} + title={title} subtitle={formatMessage({ id: 'Settings.roles.list.description', defaultMessage: 'List of roles', })} + as="h2" /> - - }> - {formatMessage({ - id: 'Settings.roles.list.button.add', - defaultMessage: 'Add new role', - })} - + {canRead && ( + } - > - - - + + + + + + + + + {sortedRoles?.map((role, index) => ( + + ))} + +
- + /> + )} + {canRead && ( + + }> {formatMessage({ - id: 'global.name', - defaultMessage: 'Name', + id: 'Settings.roles.list.button.add', + defaultMessage: 'Add new role', })} - - - - - - - - - {sortedRoles?.map((role, rowIndex) => ( - - ))} - -
- - {formatMessage({ - id: 'global.description', - defaultMessage: 'Description', - })} - - - - {formatMessage({ - id: 'global.users', - defaultMessage: 'Users', - })} - - - - {formatMessage({ - id: 'global.actions', - defaultMessage: 'Actions', - })} - -
- {!rowCount && !isLoading && } -
- + + ) : null + } + > +
+ + {formatMessage({ + id: 'global.name', + defaultMessage: 'Name', + })} + + + + {formatMessage({ + id: 'global.description', + defaultMessage: 'Description', + })} + + + + {formatMessage({ + id: 'global.users', + defaultMessage: 'Users', + })} + + + + {formatMessage({ + id: 'global.actions', + defaultMessage: 'Actions', + })} + +
+ {!rowCount && !isLoading && } +
+ )} +
); }; diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/reducer.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/reducer.js similarity index 100% rename from packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/reducer.js rename to packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/reducer.js diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/tests/reducer.test.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/tests/reducer.test.js similarity index 100% rename from packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/tests/reducer.test.js rename to packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/tests/reducer.test.js diff --git a/packages/core/admin/admin/src/pages/SettingsPage/utils/defaultRoutes.js b/packages/core/admin/admin/src/pages/SettingsPage/utils/defaultRoutes.js index 266e7e43a16..97dd3cc4b35 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/utils/defaultRoutes.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/utils/defaultRoutes.js @@ -1,25 +1,33 @@ -import RolesCreatePage from 'ee_else_ce/pages/SettingsPage/pages/Roles/CreatePage'; -import ProtectedRolesListPage from 'ee_else_ce/pages/SettingsPage/pages/Roles/ProtectedListPage'; - const defaultRoutes = [ { - Component() { - return { default: ProtectedRolesListPage }; - }, + async Component() { + const component = await import( + /* webpackChunkName: "admin-edit-roles-page" */ '../pages/Roles/ProtectedListPage' + ); + return component; + }, to: '/settings/roles', exact: true, }, { - Component() { - return { default: RolesCreatePage }; + async Component() { + const component = await import( + /* webpackChunkName: "admin-edit-roles-page" */ '../pages/Roles/CreatePage' + ); + + return component; }, to: '/settings/roles/duplicate/:id', exact: true, }, { - Component() { - return { default: RolesCreatePage }; + async Component() { + const component = await import( + /* webpackChunkName: "admin-edit-roles-page" */ '../pages/Roles/CreatePage' + ); + + return component; }, to: '/settings/roles/new', exact: true, diff --git a/packages/core/admin/admin/src/translations/ca.json b/packages/core/admin/admin/src/translations/ca.json index f1c0510a4f0..f1322d95bdb 100644 --- a/packages/core/admin/admin/src/translations/ca.json +++ b/packages/core/admin/admin/src/translations/ca.json @@ -162,7 +162,7 @@ "Settings.webhooks.create": "Crea un webhook", "Settings.webhooks.create.header": "Crea una nova capçalera", "Settings.webhooks.created": "Webhook creat", - "Settings.webhooks.event.publish-tooltip": "Aquest esdeveniment només existeix per a continguts amb el sistema Esborrany\/Publicació habilitat", + "Settings.webhooks.event.publish-tooltip": "Aquest esdeveniment només existeix per a continguts amb el sistema Esborrany/Publicació habilitat", "Settings.webhooks.events.create": "Crear", "Settings.webhooks.events.update": "Actualitzar", "Settings.webhooks.form.events": "Esdeveniments", @@ -245,20 +245,20 @@ "app.components.EmptyAttributes.title": "Encara no hi ha camps", "app.components.EmptyStateLayout.content-document": "No s'ha trobat contingut", "app.components.EmptyStateLayout.content-permissions": "No tens els permisos per accedir a aquest contingut.", - "app.components.GuidedTour.CM.create.content": "

Creeu i gestioneu tot el contingut aquí al Gestor de continguts.<\/p>

Ex.: Si fem l'exemple del lloc web del bloc més enllà, es pot escriure un article, desar-lo i publicar-lo com vulgui.<\/p>< p>💡 Consell ràpid: no us oblideu de fer clic a Publicar al contingut que creeu.<\/p>", + "app.components.GuidedTour.CM.create.content": "

Creeu i gestioneu tot el contingut aquí al Gestor de continguts.

Ex.: Si fem l'exemple del lloc web del bloc més enllà, es pot escriure un article, desar-lo i publicar-lo com vulgui.

< p>💡 Consell ràpid: no us oblideu de fer clic a Publicar al contingut que creeu.

", "app.components.GuidedTour.CM.create.title": "⚡️ Crea contingut", - "app.components.GuidedTour.CM.success.content": "

Increïble, un últim pas per fer!<\/p>🚀 Veure contingut en acció<\/b>", + "app.components.GuidedTour.CM.success.content": "

Increïble, un últim pas per fer!

🚀 Veure contingut en acció", "app.components.GuidedTour.CM.success.cta.title": "Prova l'API", "app.components.GuidedTour.CM.success.title": "Pas 2: completat ✅", - "app.components.GuidedTour.CTB.create.content": "

Els tipus de col·lecció us ajuden a gestionar diverses entrades, els tipus únics són adequats per gestionar només una entrada.<\/p>

Ex: per a un lloc web de bloc, els articles serien un tipus de col·lecció mentre que una pàgina d'inici seria un tipus únic. <\/p>", + "app.components.GuidedTour.CTB.create.content": "

Els tipus de col·lecció us ajuden a gestionar diverses entrades, els tipus únics són adequats per gestionar només una entrada.

Ex: per a un lloc web de bloc, els articles serien un tipus de col·lecció mentre que una pàgina d'inici seria un tipus únic.

", "app.components.GuidedTour.CTB.create.cta.title": "Creeu un tipus de col·lecció", "app.components.GuidedTour.CTB.create.title": "🧠 Crea un primer tipus de col·lecció", - "app.components.GuidedTour.CTB.success.content": "

Que vagi bé!<\/p>⚡️ Què t'agradaria compartir amb el món?<\/b>", + "app.components.GuidedTour.CTB.success.content": "

Que vagi bé!

⚡️ Què t'agradaria compartir amb el món?", "app.components.GuidedTour.CTB.success.title": "Pas 1: Completat ✅", - "app.components.GuidedTour.apiTokens.create.content": "

Genereu aquí un testimoni d'autenticació i recupereu el contingut que acabeu de crear.<\/p>", + "app.components.GuidedTour.apiTokens.create.content": "

Genereu aquí un testimoni d'autenticació i recupereu el contingut que acabeu de crear.

", "app.components.GuidedTour.apiTokens.create.cta.title": "Genereu un testimoni API", "app.components.GuidedTour.apiTokens.create.title": "🚀 Veure contingut en acció", - "app.components.GuidedTour.apiTokens.success.content": "

Consulteu el contingut en acció fent una sol·licitud HTTP:<\/p>