Add Publish permission & action

Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu>
This commit is contained in:
Convly 2020-08-11 16:39:05 +02:00 committed by Pierre Noël
parent 38d896f0ea
commit 310a0d16f3
7 changed files with 67 additions and 42 deletions

View File

@ -10,8 +10,15 @@ const actionFields = [
'pluginName', 'pluginName',
'subjects', 'subjects',
'conditions', 'conditions',
'options',
]; ];
const defaultAction = {
options: {
fieldsGranularity: true,
},
};
/** /**
* Return a prefixed id that depends on the pluginName * Return a prefixed id that depends on the pluginName
* @param {Object} params * @param {Object} params
@ -19,15 +26,13 @@ const actionFields = [
* @param {Object} params.uid - uid defined by the developer * @param {Object} params.uid - uid defined by the developer
*/ */
const getActionId = ({ pluginName, uid }) => { const getActionId = ({ pluginName, uid }) => {
let id = '';
if (pluginName === 'admin') { if (pluginName === 'admin') {
id = `admin::${uid}`; return `admin::${uid}`;
} else if (pluginName) { } else if (pluginName) {
id = `plugins::${pluginName}.${uid}`; return `plugins::${pluginName}.${uid}`;
} else {
id = `application::${uid}`;
} }
return id;
return `application::${uid}`;
}; };
/** /**
@ -42,7 +47,7 @@ function createAction(attributes) {
action.subCategory = attributes.subCategory || 'general'; action.subCategory = attributes.subCategory || 'general';
} }
return action; return _.merge(action, defaultAction);
} }
module.exports = { module.exports = {

View File

@ -1,10 +1,9 @@
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const domain = require('../../domain/action');
const actionProviderService = require('../permission/action-provider'); const actionProviderService = require('../permission/action-provider');
describe('Action Provider Service', () => { describe('Action Provider Service', () => {
const createdActions = [];
beforeEach(() => { beforeEach(() => {
global.strapi = { global.strapi = {
plugins: { aPlugin: {} }, plugins: { aPlugin: {} },
@ -37,8 +36,6 @@ describe('Action Provider Service', () => {
..._.omit(readAction, ['uid']), ..._.omit(readAction, ['uid']),
actionId: 'admin::marketplace.read', actionId: 'admin::marketplace.read',
}); });
createdActions.push(createdAction);
}); });
test('Can register a settings action without subCategory', async () => { test('Can register a settings action without subCategory', async () => {
@ -50,7 +47,6 @@ describe('Action Provider Service', () => {
actionId: 'admin::marketplace.create', actionId: 'admin::marketplace.create',
subCategory: 'general', subCategory: 'general',
}); });
createdActions.push(createdAction);
}); });
test('Can get all registered entries (array)', () => { test('Can get all registered entries (array)', () => {
@ -61,6 +57,12 @@ describe('Action Provider Service', () => {
expect(actionProviderService.getAllByMap().size).toBe(2); expect(actionProviderService.getAllByMap().size).toBe(2);
}); });
test('Can get an action by its actionId', () => {
const actionId = 'admin::marketplace.create';
const expected = domain.createAction(createAction);
expect(actionProviderService.getByActionId(actionId)).toStrictEqual(expected);
});
test('Can register a settings action with a pluginName other than "admin"', async () => { test('Can register a settings action with a pluginName other than "admin"', async () => {
const action = { const action = {
uid: 'marketplace.update', uid: 'marketplace.update',

View File

@ -110,24 +110,20 @@ const getNestedFieldsWithIntermediate = (
* @param {array} actions array of actions * @param {array} actions array of actions
* @param {object} options * @param {object} options
* @param {number} options.nestingLevel level of nesting * @param {number} options.nestingLevel level of nesting
* @param {array} options.fieldsNullFor actionIds where the fields should be null
* @param {array} options.restrictedSubjects subjectsId to ignore * @param {array} options.restrictedSubjects subjectsId to ignore
* @returns {array<permissions>} * @returns {array<permissions>}
*/ */
const getPermissionsWithNestedFields = ( const getPermissionsWithNestedFields = (actions, { nestingLevel, restrictedSubjects = [] } = {}) =>
actions,
{ nestingLevel, fieldsNullFor = [], restrictedSubjects = [] } = {}
) =>
actions.reduce((perms, action) => { actions.reduce((perms, action) => {
action.subjects action.subjects
.filter(subject => !restrictedSubjects.includes(subject)) .filter(subject => !restrictedSubjects.includes(subject))
.forEach(contentTypeUid => { .forEach(contentTypeUid => {
const fields = fieldsNullFor.includes(action.actionId) const fields = action.options.fieldsGranularity
? null ? getNestedFields(strapi.contentTypes[contentTypeUid], {
: getNestedFields(strapi.contentTypes[contentTypeUid], {
components: strapi.components, components: strapi.components,
nestingLevel, nestingLevel,
}); })
: null;
perms.push({ perms.push({
action: action.actionId, action: action.actionId,
subject: contentTypeUid, subject: contentTypeUid,
@ -143,28 +139,30 @@ const getPermissionsWithNestedFields = (
* @param {object} permissions array of existing permissions in db * @param {object} permissions array of existing permissions in db
* @param {object} options * @param {object} options
* @param {number} options.nestingLevel level of nesting * @param {number} options.nestingLevel level of nesting
* @param {array} options.fieldsNullFor actionIds where the fields should be null
* @returns {array<permissions>} * @returns {array<permissions>}
*/ */
const cleanPermissionFields = (permissions, { nestingLevel, fieldsNullFor = [] }) => const cleanPermissionFields = (permissions, { nestingLevel }) =>
permissions.map(perm => { permissions.map(perm => {
let newFields = perm.fields; const { action: actionId, fields, subject } = perm;
if (fieldsNullFor.includes(perm.action)) { const action = strapi.admin.services.permission.actionProvider.getByActionId(actionId);
let newFields = fields;
if (!action.options.fieldsGranularity) {
newFields = null; newFields = null;
} else if (perm.subject && strapi.contentTypes[perm.subject]) { } else if (subject && strapi.contentTypes[subject]) {
const possiblefields = getNestedFieldsWithIntermediate(strapi.contentTypes[perm.subject], { const possibleFields = getNestedFieldsWithIntermediate(strapi.contentTypes[subject], {
components: strapi.components, components: strapi.components,
nestingLevel, nestingLevel,
}); });
const requiredFields = getNestedFields(strapi.contentTypes[perm.subject], { const requiredFields = getNestedFields(strapi.contentTypes[subject], {
components: strapi.components, components: strapi.components,
requiredOnly: true, requiredOnly: true,
nestingLevel, nestingLevel,
existingFields: perm.fields, existingFields: fields,
}); });
const badNestedFields = _.uniq([ const badNestedFields = _.uniq([
..._.intersection(perm.fields, possiblefields), ..._.intersection(fields, possibleFields),
...requiredFields, ...requiredFields,
]); ]);
newFields = badNestedFields.filter( newFields = badNestedFields.filter(

View File

@ -28,7 +28,7 @@ const arePermissionsEqual = (perm1, perm2) =>
/** /**
* Removes unwanted fields from a permission * Removes unwanted fields from a permission
* @param permission * @param perm
* @returns {*} * @returns {*}
*/ */
const sanitizePermission = perm => ({ const sanitizePermission = perm => ({
@ -157,10 +157,7 @@ const cleanPermissionInDatabase = async () => {
// Second, clean fields of permissions (add required ones, remove the non-existing anymore ones) // Second, clean fields of permissions (add required ones, remove the non-existing anymore ones)
const permissionsInDb = dbPermissions.filter(perm => !permissionsToRemoveIds.includes(perm.id)); const permissionsInDb = dbPermissions.filter(perm => !permissionsToRemoveIds.includes(perm.id));
const permissionsWithCleanFields = strapi.admin.services['content-type'].cleanPermissionFields( const permissionsWithCleanFields = strapi.admin.services['content-type'].cleanPermissionFields(
permissionsInDb, permissionsInDb
{
fieldsNullFor: ['plugins::content-manager.explorer.delete'],
}
); );
// Update only the ones that need to be updated // Update only the ones that need to be updated
@ -197,10 +194,7 @@ const resetSuperAdminPermissions = async () => {
const contentTypesActions = allActions.filter(a => a.section === 'contentTypes'); const contentTypesActions = allActions.filter(a => a.section === 'contentTypes');
const permissions = strapi.admin.services['content-type'].getPermissionsWithNestedFields( const permissions = strapi.admin.services['content-type'].getPermissionsWithNestedFields(
contentTypesActions, contentTypesActions
{
fieldsNullFor: ['plugins::content-manager.explorer.delete'],
}
); );
const otherActions = allActions.filter(a => a.section !== 'contentTypes'); const otherActions = allActions.filter(a => a.section !== 'contentTypes');

View File

@ -16,8 +16,16 @@ const createActionProvider = () => {
*/ */
get(uid, pluginName) { get(uid, pluginName) {
const actionId = getActionId({ pluginName, uid }); const actionId = getActionId({ pluginName, uid });
const action = actions.get(actionId); return actions.get(actionId);
return action; },
/**
* Get an action by its actionId
* @param {string} actionId
* @returns {Action}
*/
getByActionId(actionId) {
return actions.get(actionId);
}, },
/** /**

View File

@ -32,7 +32,7 @@ const registerProviderActionSchema = yup
then: yup then: yup
.array() .array()
.of(yup.string()) .of(yup.string())
.required(), .requiredAllowEmpty(),
otherwise: yup otherwise: yup
.mixed() .mixed()
.oneOf([undefined], 'subjects should only be defined for the "contentTypes" section'), .oneOf([undefined], 'subjects should only be defined for the "contentTypes" section'),
@ -62,6 +62,9 @@ const registerProviderActionSchema = yup
} }
), ),
}), }),
options: yup.object({
fieldsGranularity: yup.boolean(),
}),
}) })
.noUnknown() .noUnknown()
); );

View File

@ -94,6 +94,8 @@ const registerPermissions = () => {
'content-manager' 'content-manager'
].services.contenttypes.getDisplayedContentTypesUids(); ].services.contenttypes.getDisplayedContentTypesUids();
const hasDraftAndPublish = uid => strapi.contentTypes[uid].options.draftAndPublish;
const actions = [ const actions = [
{ {
section: 'contentTypes', section: 'contentTypes',
@ -122,6 +124,19 @@ const registerPermissions = () => {
uid: 'explorer.delete', uid: 'explorer.delete',
pluginName: 'content-manager', pluginName: 'content-manager',
subjects: contentTypesUids, subjects: contentTypesUids,
options: {
fieldsGranularity: false,
},
},
{
section: 'contentTypes',
displayName: 'Publish',
uid: 'explorer.publish',
pluginName: 'content-manager',
subjects: contentTypesUids.filter(hasDraftAndPublish),
options: {
fieldsGranularity: false,
},
}, },
{ {
section: 'plugins', section: 'plugins',