diff --git a/packages/strapi-admin/config/routes.json b/packages/strapi-admin/config/routes.json index 3516bb7569..84d6ed952c 100644 --- a/packages/strapi-admin/config/routes.json +++ b/packages/strapi-admin/config/routes.json @@ -194,6 +194,14 @@ "config": { "policies": [] } + }, + { + "method": "GET", + "path": "/permissions", + "handler": "permission.getAll", + "config": { + "policies": [] + } } ] } diff --git a/packages/strapi-admin/controllers/permission.js b/packages/strapi-admin/controllers/permission.js new file mode 100644 index 0000000000..806605e88b --- /dev/null +++ b/packages/strapi-admin/controllers/permission.js @@ -0,0 +1,15 @@ +'use strict'; + +module.exports = { + /** + * Returns every permissions, in nested format + * @param {KoaContext} ctx - koa context + */ + async getAll(ctx) { + const allWithNestedFormat = await strapi.admin.permissionProvider.getAllWithNestedFormat(); + + ctx.body = { + data: allWithNestedFormat, + }; + }, +}; diff --git a/packages/strapi-admin/ee/validation/role.js b/packages/strapi-admin/ee/validation/role.js index 042b98195d..3f5bf753cc 100644 --- a/packages/strapi-admin/ee/validation/role.js +++ b/packages/strapi-admin/ee/validation/role.js @@ -4,7 +4,7 @@ const { yup, formatYupErrors } = require('strapi-utils'); const handleReject = error => Promise.reject(formatYupErrors(error)); -const roleCreateUpdateSchema = yup +const roleCreateSchema = yup .object() .shape({ name: yup @@ -15,6 +15,14 @@ const roleCreateUpdateSchema = yup }) .noUnknown(); +const roleUpdateSchema = yup + .object() + .shape({ + name: yup.string().min(1), + description: yup.string().nullable(), + }) + .noUnknown(); + const roleDeleteSchema = yup .object() .shape({ @@ -27,15 +35,11 @@ const roleDeleteSchema = yup .noUnknown(); const validateRoleCreateInput = async data => { - return roleCreateUpdateSchema - .validate(data, { strict: true, abortEarly: false }) - .catch(handleReject); + return roleCreateSchema.validate(data, { strict: true, abortEarly: false }).catch(handleReject); }; const validateRoleUpdateInput = async data => { - return roleCreateUpdateSchema - .validate(data, { strict: true, abortEarly: false }) - .catch(handleReject); + return roleUpdateSchema.validate(data, { strict: true, abortEarly: false }).catch(handleReject); }; const validateRoleDeleteInput = async data => { diff --git a/packages/strapi-admin/services/__tests__/permission-provider.test.js b/packages/strapi-admin/services/__tests__/permission-provider.test.js index 807c706fc2..12470551d9 100644 --- a/packages/strapi-admin/services/__tests__/permission-provider.test.js +++ b/packages/strapi-admin/services/__tests__/permission-provider.test.js @@ -25,7 +25,7 @@ describe('Permission Provider Service', () => { }; await permissionProvider.register([permission]); - const permissions = permissionProvider.getPermissions(); + const permissions = permissionProvider.getAll(); expect(permissions).toMatchObject([ { @@ -46,7 +46,7 @@ describe('Permission Provider Service', () => { }; await permissionProvider.register([permission]); - const permissions = permissionProvider.getPermissions(); + const permissions = permissionProvider.getAll(); expect(permissions).toMatchObject([ { @@ -68,7 +68,7 @@ describe('Permission Provider Service', () => { }; await permissionProvider.register([permission]); - const permissions = permissionProvider.getPermissions(); + const permissions = permissionProvider.getAll(); expect(permissions).toMatchObject([ { @@ -89,7 +89,7 @@ describe('Permission Provider Service', () => { }; await permissionProvider.register([permission]); - const permissions = permissionProvider.getPermissions(); + const permissions = permissionProvider.getAll(); expect(permissions).toMatchObject([ { @@ -136,13 +136,8 @@ describe('Permission Provider Service', () => { category: 'plugins and marketplace', }; - await permissionProvider.register([permission]); - - expect(global.strapi.stopWithError).toHaveBeenCalledWith( - expect.objectContaining({ - name: 'ValidationError', - message: '[0].pluginName is not an existing plugin', - }) + expect(() => permissionProvider.register([permission])).toThrow( + '[0].pluginName is not an existing plugin' ); }); @@ -154,13 +149,8 @@ describe('Permission Provider Service', () => { section: 'settings', }; - await permissionProvider.register([permission]); - - expect(global.strapi.stopWithError).toHaveBeenCalledWith( - expect.objectContaining({ - name: 'ValidationError', - message: '[0].category is a required field', - }) + expect(() => permissionProvider.register([permission])).toThrow( + '[0].category is a required field' ); }); }); diff --git a/packages/strapi-admin/services/__tests__/permission.test.js b/packages/strapi-admin/services/__tests__/permission.test.js index d4a709ca24..d1309d3c73 100644 --- a/packages/strapi-admin/services/__tests__/permission.test.js +++ b/packages/strapi-admin/services/__tests__/permission.test.js @@ -22,8 +22,10 @@ describe('Permission Service', () => { test('Delete previous permissions', async () => { const deleteFn = jest.fn(() => Promise.resolve([])); const create = jest.fn(() => Promise.resolve({})); + const getAll = jest.fn(() => []); global.strapi = { + admin: { permissionProvider: { getAll } }, query() { return { delete: deleteFn, create }; }, @@ -37,8 +39,14 @@ describe('Permission Service', () => { test('Create new permissions', async () => { const deleteFn = jest.fn(() => Promise.resolve([])); const create = jest.fn(() => Promise.resolve({})); + const getAll = jest.fn(() => + Array(5) + .fill(0) + .map((v, i) => ({ permissionId: `action-${i}` })) + ); global.strapi = { + admin: { permissionProvider: { getAll } }, query() { return { delete: deleteFn, create }; }, @@ -46,13 +54,13 @@ describe('Permission Service', () => { const permissions = Array(5) .fill(0) - .map(() => ({ action: 'test' })); + .map((v, i) => ({ action: `action-${i}` })); await permissionService.assign(1, permissions); expect(create).toHaveBeenCalledTimes(5); - expect(create).toHaveBeenCalledWith({ - action: 'test', + expect(create).toHaveBeenNthCalledWith(1, { + action: 'action-0', role: 1, conditions: [], fields: [], diff --git a/packages/strapi-admin/services/permission-provider.js b/packages/strapi-admin/services/permission-provider.js index b1ae9e6891..3f20f7f018 100644 --- a/packages/strapi-admin/services/permission-provider.js +++ b/packages/strapi-admin/services/permission-provider.js @@ -4,7 +4,7 @@ const { validateRegisterProviderPermission } = require('../validation/permission const calculateId = permission => { let id = ''; - const sanitizedname = _.snakeCase(permission.name).replace('_', '.'); + const sanitizedname = _.snakeCase(permission.name).replace(/_/g, '.'); if (permission.pluginName === 'admin') { id = `admin::${sanitizedname}`; } else { @@ -13,12 +13,12 @@ const calculateId = permission => { return id; }; -const formatPermission = permission => { +const formatPermissionToBeRegistered = permission => { const formattedPermission = _.clone(permission); formattedPermission.permissionId = calculateId(permission); formattedPermission.conditions = permission.conditions || []; - if (permission.section === 'settings') { + if (['settings', 'plugins'].includes(permission.section)) { formattedPermission.subCategory = permission.subCategory || 'general'; } @@ -39,18 +39,58 @@ const getDuplicatedIds = permissions => { return duplicatedIds; }; +const formatPermissionsToNestedFormat = formattedPermissions => { + const sections = formattedPermissions.reduce((result, p) => { + const checkboxItem = { + displayName: p.displayName, + action: p.permissionId, + }; + + switch (p.section) { + case 'contentTypes': + checkboxItem.subjects = p.subjects; + break; + case 'plugins': + checkboxItem.subCategory = p.subCategory; + checkboxItem.plugin = `plugin::${p.pluginName}`; + break; + case 'settings': + checkboxItem.category = p.category; + checkboxItem.subCategory = p.subCategory; + break; + case 'default': + throw new Error(`Unknown section ${p.section}`); + } + + result[p.section] = result[p.section] || []; + result[p.section].push(checkboxItem); + + return result; + }, {}); + + return { + sections, + conditions: [], + }; +}; + class PermissionProvider { constructor() { this.permissions = []; + this.permissionsWithNestedFormat = {}; } - getPermissions() { + getAll() { return _.cloneDeep(this.permissions); } + getAllWithNestedFormat() { + return _.cloneDeep(this.permissionsWithNestedFormat); + } + register(newPermissions) { validateRegisterProviderPermission(newPermissions); - const newPermissionsWithIds = newPermissions.map(formatPermission); + const newPermissionsWithIds = newPermissions.map(formatPermissionToBeRegistered); const mergedPermissions = [...this.permissions, ...newPermissionsWithIds]; const duplicatedIds = getDuplicatedIds(mergedPermissions); @@ -65,6 +105,7 @@ class PermissionProvider { } this.permissions = mergedPermissions; + this.permissionsWithNestedFormat = formatPermissionsToNestedFormat(mergedPermissions); } } diff --git a/packages/strapi-admin/services/permission.js b/packages/strapi-admin/services/permission.js index fe016fd459..e1c938c0e8 100644 --- a/packages/strapi-admin/services/permission.js +++ b/packages/strapi-admin/services/permission.js @@ -26,9 +26,20 @@ const find = (params = {}) => { * @param {Array} permissions - permissions to assign to the role */ const assign = async (roleID, permissions = []) => { - // TODO: verify the data once we have the permissions registry + const existingPermissions = strapi.admin.permissionProvider.getAll(); + for (let permission of permissions) { + const permissionExists = existingPermissions.find( + ep => + ep.permissionId === permission.action && + (ep.section !== 'contentTypes' || ep.subjects.includes(permission.subject)) + ); + if (!permissionExists) { + throw strapi.errors.badRequest( + `ValidationError', 'This permission doesn't exist: ${JSON.stringify(permission)}` + ); + } + } - // clear previous permissions await strapi.query('permission', 'admin').delete({ role: roleID }); const permissionsWithRole = permissions.map(permission => { diff --git a/packages/strapi-admin/test/admin-permission.test.e2e.js b/packages/strapi-admin/test/admin-permission.test.e2e.js new file mode 100644 index 0000000000..c7599fa63e --- /dev/null +++ b/packages/strapi-admin/test/admin-permission.test.e2e.js @@ -0,0 +1,22 @@ +const { registerAndLogin } = require('../../../test/helpers/auth'); +const { createAuthRequest } = require('../../../test/helpers/request'); +const expectedPermissions = require('./permissions'); + +let rq; + +describe('Role CRUD End to End', () => { + beforeAll(async () => { + const token = await registerAndLogin(); + rq = createAuthRequest(token); + }, 60000); + + test('Can get the existing permissions', async () => { + let res = await rq({ + url: '/admin/permissions', + method: 'GET', + }); + + expect(res.statusCode).toBe(200); + expect(res.body.data).toMatchObject(expectedPermissions); + }); +}); diff --git a/packages/strapi-admin/test/admin-role.test.e2e.js b/packages/strapi-admin/test/admin-role.test.e2e.js index b9360988bb..671362cd89 100644 --- a/packages/strapi-admin/test/admin-role.test.e2e.js +++ b/packages/strapi-admin/test/admin-role.test.e2e.js @@ -355,11 +355,11 @@ describe('Role CRUD End to End', () => { body: { permissions: [ { - action: 'test.action', + action: 'plugins::users-permissions.roles.update', }, { - action: 'test.action2', - subject: 'model1', + action: 'plugins::content-manager.create', + subject: 'plugins::upload.file', conditions: ['isOwner'], }, ], @@ -384,6 +384,29 @@ describe('Role CRUD End to End', () => { }); }); + 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); + console.log('res.body', res.body); + expect(res.body).toMatchObject({ + statusCode: 400, + error: 'Bad Request', + message: + 'ValidationError\', \'This permission doesn\'t exist: {"action":"non.existing.action"}', + }); + }); + test('get permissions role', async () => { const res = await rq({ url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`, diff --git a/packages/strapi-admin/test/permissions.json b/packages/strapi-admin/test/permissions.json new file mode 100644 index 0000000000..a3b436a3ff --- /dev/null +++ b/packages/strapi-admin/test/permissions.json @@ -0,0 +1,285 @@ +{ + "sections": { + "settings": [ + { + "displayName": "Can access to the marketplace", + "action": "admin::marketplace.read", + "category": "plugins and marketplace", + "subCategory": "marketplace" + }, + { + "displayName": "Install (only for dev env)", + "action": "admin::marketplace.plugins.install", + "category": "plugins and marketplace", + "subCategory": "plugins" + }, + { + "displayName": "Uninstall (only for dev env)", + "action": "admin::marketplace.plugins.uninstall", + "category": "plugins and marketplace", + "subCategory": "plugins" + }, + { + "displayName": "Create", + "action": "admin::webhooks.create", + "category": "webhooks", + "subCategory": "general" + }, + { + "displayName": "Read", + "action": "admin::webhooks.read", + "category": "webhooks", + "subCategory": "general" + }, + { + "displayName": "Update", + "action": "admin::webhooks.update", + "category": "webhooks", + "subCategory": "general" + }, + { + "displayName": "Delete", + "action": "admin::webhooks.delete", + "category": "webhooks", + "subCategory": "general" + }, + { + "displayName": "Create (invite)", + "action": "admin::users.create", + "category": "users and roles", + "subCategory": "users" + }, + { + "displayName": "Read", + "action": "admin::users.read", + "category": "users and roles", + "subCategory": "users" + }, + { + "displayName": "Update", + "action": "admin::users.update", + "category": "users and roles", + "subCategory": "users" + }, + { + "displayName": "Delete", + "action": "admin::users.delete", + "category": "users and roles", + "subCategory": "users" + }, + { + "displayName": "Create", + "action": "admin::roles.create", + "category": "users and roles", + "subCategory": "roles" + }, + { + "displayName": "Read", + "action": "admin::roles.read", + "category": "users and roles", + "subCategory": "roles" + }, + { + "displayName": "Update", + "action": "admin::roles.update", + "category": "users and roles", + "subCategory": "roles" + }, + { + "displayName": "Delete", + "action": "admin::roles.delete", + "category": "users and roles", + "subCategory": "roles" + } + ], + "plugins": [ + { + "displayName": "Read", + "action": "plugins::content-type-builder.read", + "subCategory": "general", + "plugin": "plugin::content-type-builder" + }, + { + "displayName": "Can access to the Documentation", + "action": "plugins::documentation.read", + "subCategory": "general", + "plugin": "plugin::documentation" + }, + { + "displayName": "Update and delete", + "action": "plugins::documentation.settings.update", + "subCategory": "settings", + "plugin": "plugin::documentation" + }, + { + "displayName": "Regenerate", + "action": "plugins::documentation.settings.regenerate", + "subCategory": "settings", + "plugin": "plugin::documentation" + }, + { + "displayName": "Can access to the Media Library", + "action": "plugins::upload.read", + "subCategory": "general", + "plugin": "plugin::upload" + }, + { + "displayName": "Create (upload)", + "action": "plugins::upload.assets.create", + "subCategory": "assets", + "plugin": "plugin::upload" + }, + { + "displayName": "Update (crop, details, replace)", + "action": "plugins::upload.assets.update", + "subCategory": "assets", + "plugin": "plugin::upload" + }, + { + "displayName": "Download", + "action": "plugins::upload.assets.download", + "subCategory": "assets", + "plugin": "plugin::upload" + }, + { + "displayName": "Copy link", + "action": "plugins::upload.assets.copy.link", + "subCategory": "assets", + "plugin": "plugin::upload" + }, + { + "displayName": "Configure view", + "action": "plugins::content-manager.single.types.configure.view", + "subCategory": "single types", + "plugin": "plugin::content-manager" + }, + { + "displayName": "Configure view", + "action": "plugins::content-manager.collection.types.configure.view", + "subCategory": "collection types", + "plugin": "plugin::content-manager" + }, + { + "displayName": "Configure Layout", + "action": "plugins::content-manager.components.configure.layout", + "subCategory": "components", + "plugin": "plugin::content-manager" + }, + { + "displayName": "Create", + "action": "plugins::users-permissions.roles.create", + "subCategory": "roles", + "plugin": "plugin::users-permissions" + }, + { + "displayName": "Read", + "action": "plugins::users-permissions.roles.read", + "subCategory": "roles", + "plugin": "plugin::users-permissions" + }, + { + "displayName": "Update", + "action": "plugins::users-permissions.roles.update", + "subCategory": "roles", + "plugin": "plugin::users-permissions" + }, + { + "displayName": "Delete", + "action": "plugins::users-permissions.roles.delete", + "subCategory": "roles", + "plugin": "plugin::users-permissions" + }, + { + "displayName": "Read", + "action": "plugins::users-permissions.providers.read", + "subCategory": "providers", + "plugin": "plugin::users-permissions" + }, + { + "displayName": "Edit", + "action": "plugins::users-permissions.providers.update", + "subCategory": "providers", + "plugin": "plugin::users-permissions" + }, + { + "displayName": "Read", + "action": "plugins::users-permissions.email.templates.read", + "subCategory": "emailTemplates", + "plugin": "plugin::users-permissions" + }, + { + "displayName": "Edit", + "action": "plugins::users-permissions.email.templates.update", + "subCategory": "emailTemplates", + "plugin": "plugin::users-permissions" + }, + { + "displayName": "Read", + "action": "plugins::users-permissions.advanced.settings.read", + "subCategory": "advancedSettings", + "plugin": "plugin::users-permissions" + }, + { + "displayName": "Edit", + "action": "plugins::users-permissions.advanced.settings.update", + "subCategory": "advancedSettings", + "plugin": "plugin::users-permissions" + } + ], + "contentTypes": [ + { + "displayName": "Create", + "action": "plugins::content-manager.create", + "subjects": [ + "strapi::permission", + "strapi::role", + "strapi::user", + "plugins::users-permissions.permission", + "plugins::users-permissions.role", + "plugins::users-permissions.user", + "plugins::upload.file" + ] + }, + { + "displayName": "Read", + "action": "plugins::content-manager.read", + "subjects": [ + "strapi::permission", + "strapi::role", + "strapi::user", + "plugins::users-permissions.permission", + "plugins::users-permissions.role", + "plugins::users-permissions.user", + "plugins::upload.file" + ] + }, + { + "displayName": "Update", + "action": "plugins::content-manager.update", + "subjects": [ + "strapi::permission", + "strapi::role", + "strapi::user", + "plugins::users-permissions.permission", + "plugins::users-permissions.role", + "plugins::users-permissions.user", + "plugins::upload.file" + ] + }, + { + "displayName": "Delete", + "action": "plugins::content-manager.delete", + "subjects": [ + "strapi::permission", + "strapi::role", + "strapi::user", + "plugins::users-permissions.permission", + "plugins::users-permissions.role", + "plugins::users-permissions.user", + "plugins::upload.file" + ] + } + ] + }, + "conditions": [] +} diff --git a/packages/strapi-admin/validation/permission-provider.js b/packages/strapi-admin/validation/permission-provider.js index 7aa464a570..0b11174169 100644 --- a/packages/strapi-admin/validation/permission-provider.js +++ b/packages/strapi-admin/validation/permission-provider.js @@ -15,29 +15,29 @@ const registerProviderPermissionSchema = yup .string() .oneOf(['contentTypes', 'plugins', 'settings']) .required(), - pluginName: yup.mixed().when('section', { + pluginName: yup + .string() + .required() + .isAPluginName(), + subjects: yup.mixed().when('section', { is: 'contentTypes', - then: yup.mixed().oneOf([undefined]), - otherwise: yup - .string() - .required() - .isAPluginName(), + then: yup + .array() + .of(yup.string().isAContentTypeId()) + .required(), + otherwise: yup.mixed().oneOf([undefined]), }), displayName: yup.string().required(), - category: yup - .mixed() - .when('section', { - is: val => ['plugins', 'contentTypes'].includes(val), - then: yup.mixed().oneOf([undefined]), - otherwise: yup.string().required(), - }), - subCategory: yup - .mixed() - .when('section', { - is: 'contentTypes', - then: yup.mixed().oneOf([undefined]), - otherwise: yup.string(), - }), + category: yup.mixed().when('section', { + is: val => ['plugins', 'contentTypes'].includes(val), + then: yup.mixed().oneOf([undefined]), + otherwise: yup.string().required(), + }), + subCategory: yup.mixed().when('section', { + is: 'contentTypes', + then: yup.mixed().oneOf([undefined]), + otherwise: yup.string(), + }), conditions: yup.array().of(yup.string()), }) .noUnknown() @@ -47,7 +47,11 @@ const validateRegisterProviderPermission = data => { try { registerProviderPermissionSchema.validateSync(data, { strict: true, abortEarly: false }); } catch (e) { - strapi.stopWithError(e); + if (e.errors.length > 0) { + throw new yup.ValidationError(e.errors.join(', ')); + } else { + throw e; + } } }; diff --git a/packages/strapi-plugin-content-manager/config/functions/bootstrap.js b/packages/strapi-plugin-content-manager/config/functions/bootstrap.js index be6e1fde02..a41f7eb26d 100644 --- a/packages/strapi-plugin-content-manager/config/functions/bootstrap.js +++ b/packages/strapi-plugin-content-manager/config/functions/bootstrap.js @@ -20,6 +20,7 @@ module.exports = () => { async function syncSchemas() { await syncContentTypesSchemas(); await syncComponentsSchemas(); + registerPermissions(); } /** @@ -58,14 +59,10 @@ async function updateContentTypes(configurations) { const contentTypesToDelete = _.difference(DBUIDs, currentUIDS); // delette old schemas - await Promise.all( - contentTypesToDelete.map(uid => contentTypeService.deleteConfiguration(uid)) - ); + await Promise.all(contentTypesToDelete.map(uid => contentTypeService.deleteConfiguration(uid))); // create new schemas - await Promise.all( - contentTypesToAdd.map(uid => generateNewConfiguration(uid)) - ); + await Promise.all(contentTypesToAdd.map(uid => generateNewConfiguration(uid))); // update current schemas await Promise.all(contentTypesToUpdate.map(uid => updateConfiguration(uid))); @@ -102,9 +99,7 @@ async function syncComponentsSchemas() { const componentsToDelete = _.difference(DBUIDs, realUIDs); // delette old schemas - await Promise.all( - componentsToDelete.map(uid => componentService.deleteConfiguration(uid)) - ); + await Promise.all(componentsToDelete.map(uid => componentService.deleteConfiguration(uid))); // create new schemas await Promise.all(componentsToAdd.map(uid => generateNewConfiguration(uid))); @@ -112,3 +107,61 @@ async function syncComponentsSchemas() { // update current schemas await Promise.all(componentsToUpdate.map(uid => updateConfiguration(uid))); } + +function registerPermissions() { + const contentTypesUids = Object.keys(strapi.contentTypes); // TODO: filter to not have internal contentTypes + + const permissions = [ + { + section: 'contentTypes', + displayName: 'Create', + name: 'create', + pluginName: 'content-manager', + subjects: contentTypesUids, + }, + { + section: 'contentTypes', + displayName: 'Read', + name: 'read', + pluginName: 'content-manager', + subjects: contentTypesUids, + }, + { + section: 'contentTypes', + displayName: 'Update', + name: 'update', + pluginName: 'content-manager', + subjects: contentTypesUids, + }, + { + section: 'contentTypes', + displayName: 'Delete', + name: 'delete', + pluginName: 'content-manager', + subjects: contentTypesUids, + }, + { + section: 'plugins', + displayName: 'Configure view', + name: 'single-types.configure-view', + subCategory: 'single types', + pluginName: 'content-manager', + }, + { + section: 'plugins', + displayName: 'Configure view', + name: 'collection-types.configure-view', + subCategory: 'collection types', + pluginName: 'content-manager', + }, + { + section: 'plugins', + displayName: 'Configure Layout', + name: 'components.configure-layout', + subCategory: 'components', + pluginName: 'content-manager', + }, + ]; + + strapi.admin.permissionProvider.register(permissions); +} diff --git a/packages/strapi-plugin-content-type-builder/config/functions/bootstrap.js b/packages/strapi-plugin-content-type-builder/config/functions/bootstrap.js new file mode 100644 index 0000000000..7c47738142 --- /dev/null +++ b/packages/strapi-plugin-content-type-builder/config/functions/bootstrap.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = () => { + const permissions = [ + { + section: 'plugins', + displayName: 'Read', + name: 'read', + pluginName: 'content-type-builder', + }, + ]; + + strapi.admin.permissionProvider.register(permissions); +}; diff --git a/packages/strapi-plugin-documentation/config/functions/bootstrap.js b/packages/strapi-plugin-documentation/config/functions/bootstrap.js index adff3f5bfc..7616048f60 100755 --- a/packages/strapi-plugin-documentation/config/functions/bootstrap.js +++ b/packages/strapi-plugin-documentation/config/functions/bootstrap.js @@ -107,4 +107,30 @@ module.exports = async () => { 'utf8' ); } + + // Add permissions + const permissions = [ + { + section: 'plugins', + displayName: 'Can access to the Documentation', + name: 'read', + pluginName: 'documentation', + }, + { + section: 'plugins', + displayName: 'Update and delete', + name: 'settings.update', + subCategory: 'settings', + pluginName: 'documentation', + }, + { + section: 'plugins', + displayName: 'Regenerate', + name: 'settings.regenerate', + subCategory: 'settings', + pluginName: 'documentation', + }, + ]; + + strapi.admin.permissionProvider.register(permissions); }; diff --git a/packages/strapi-plugin-upload/config/functions/bootstrap.js b/packages/strapi-plugin-upload/config/functions/bootstrap.js index 68f8652b4f..14ff4a97ac 100644 --- a/packages/strapi-plugin-upload/config/functions/bootstrap.js +++ b/packages/strapi-plugin-upload/config/functions/bootstrap.js @@ -28,6 +28,7 @@ module.exports = async () => { } await pruneObsoleteRelations(); + registerPermissions(); }; const createProvider = ({ provider, providerOptions }) => { @@ -79,3 +80,44 @@ const pruneObsoleteRelationsQuery = ({ model }) => { { $pull: { related: { kind: { $nin: modelsId } } } } ); }; + +const registerPermissions = () => { + const permissions = [ + { + section: 'plugins', + displayName: 'Can access to the Media Library', + name: 'read', + pluginName: 'upload', + }, + { + section: 'plugins', + displayName: 'Create (upload)', + name: 'assets.create', + subCategory: 'assets', + pluginName: 'upload', + }, + { + section: 'plugins', + displayName: 'Update (crop, details, replace)', + name: 'assets.update', + subCategory: 'assets', + pluginName: 'upload', + }, + { + section: 'plugins', + displayName: 'Download', + name: 'assets.download', + subCategory: 'assets', + pluginName: 'upload', + }, + { + section: 'plugins', + displayName: 'Copy link', + name: 'assets.copy-link', + subCategory: 'assets', + pluginName: 'upload', + }, + ]; + + strapi.admin.permissionProvider.register(permissions); +}; diff --git a/packages/strapi-plugin-upload/test/__tests__/bootstrap.test.js b/packages/strapi-plugin-upload/test/__tests__/bootstrap.test.js index 6faf36135b..0160466ac0 100644 --- a/packages/strapi-plugin-upload/test/__tests__/bootstrap.test.js +++ b/packages/strapi-plugin-upload/test/__tests__/bootstrap.test.js @@ -3,8 +3,14 @@ const bootstrap = require('../../config/functions/bootstrap'); describe('Upload plugin bootstrap function', () => { test('Sets default config if id does not exist', async () => { const setStore = jest.fn(() => {}); + const register = jest.fn(() => {}); global.strapi = { + admin: { + permissionProvider: { + register, + }, + }, log: { error() {}, }, diff --git a/packages/strapi-plugin-users-permissions/config/functions/bootstrap.js b/packages/strapi-plugin-users-permissions/config/functions/bootstrap.js index f6d5bb58b6..8de2965b91 100644 --- a/packages/strapi-plugin-users-permissions/config/functions/bootstrap.js +++ b/packages/strapi-plugin-users-permissions/config/functions/bootstrap.js @@ -10,6 +10,8 @@ const _ = require('lodash'); const uuid = require('uuid/v4'); +const usersPermissionsPermissions = require('../users-permissions-permissions'); + module.exports = async () => { const pluginStore = strapi.store({ environment: '', @@ -180,4 +182,6 @@ module.exports = async () => { strapi.reload.isWatching = true; } + + strapi.admin.permissionProvider.register(usersPermissionsPermissions.permissions); }; diff --git a/packages/strapi-plugin-users-permissions/config/users-permissions-permissions.js b/packages/strapi-plugin-users-permissions/config/users-permissions-permissions.js new file mode 100644 index 0000000000..7455d0d75c --- /dev/null +++ b/packages/strapi-plugin-users-permissions/config/users-permissions-permissions.js @@ -0,0 +1,78 @@ +module.exports = { + permissions: [ + { + // Roles + section: 'plugins', + displayName: 'Create', + name: 'roles.create', + subCategory: 'roles', + pluginName: 'users-permissions', + }, + { + section: 'plugins', + displayName: 'Read', + name: 'roles.read', + subCategory: 'roles', + pluginName: 'users-permissions', + }, + { + section: 'plugins', + displayName: 'Update', + name: 'roles.update', + subCategory: 'roles', + pluginName: 'users-permissions', + }, + { + section: 'plugins', + displayName: 'Delete', + name: 'roles.delete', + subCategory: 'roles', + pluginName: 'users-permissions', + }, + { + // providers + section: 'plugins', + displayName: 'Read', + name: 'providers.read', + subCategory: 'providers', + pluginName: 'users-permissions', + }, + { + section: 'plugins', + displayName: 'Edit', + name: 'providers.update', + subCategory: 'providers', + pluginName: 'users-permissions', + }, + { + // emailTemplates + section: 'plugins', + displayName: 'Read', + name: 'emailTemplates.read', + subCategory: 'emailTemplates', + pluginName: 'users-permissions', + }, + { + section: 'plugins', + displayName: 'Edit', + name: 'emailTemplates.update', + subCategory: 'emailTemplates', + pluginName: 'users-permissions', + }, + { + // advancedSettings + section: 'plugins', + displayName: 'Read', + name: 'advancedSettings.read', + subCategory: 'advancedSettings', + pluginName: 'users-permissions', + }, + { + section: 'plugins', + displayName: 'Edit', + name: 'advancedSettings.update', + subCategory: 'advancedSettings', + pluginName: 'users-permissions', + }, + ], +}; diff --git a/packages/strapi-utils/lib/validators.js b/packages/strapi-utils/lib/validators.js index 24120ec809..2a3f9468e3 100644 --- a/packages/strapi-utils/lib/validators.js +++ b/packages/strapi-utils/lib/validators.js @@ -28,10 +28,22 @@ function isAPluginName(message) { }); } +function isAContentTypeId(message) { + return this.test('is not a content-type id', message, function(value) { + return Object.keys(strapi.contentTypes).includes(value) + ? true + : this.createError({ + path: this.path, + message: `${this.path} is not an existing content-type id`, + }); + }); +} + yup.addMethod(yup.mixed, 'notNil', isNotNill); yup.addMethod(yup.mixed, 'notNull', isNotNull); yup.addMethod(yup.array, 'requiredAllowEmpty', arrayRequiredAllowEmpty); yup.addMethod(yup.string, 'isAPluginName', isAPluginName); +yup.addMethod(yup.string, 'isAContentTypeId', isAContentTypeId); class StrapiIDSchema extends MixedSchemaType { constructor() {