create role at startup + warning

Signed-off-by: Pierre Noël <petersg83@gmail.com>
This commit is contained in:
Pierre Noël 2020-06-12 18:42:07 +02:00 committed by Alexandre Bodin
parent 84c4591816
commit 711c58b05e
8 changed files with 186 additions and 12 deletions

View File

@ -1,5 +1,6 @@
'use strict';
const _ = require('lodash');
const adminActions = require('../admin-actions');
const registerPermissionActions = () => {
@ -7,6 +8,17 @@ const registerPermissionActions = () => {
actionProvider.register(adminActions.actions);
};
const registerAdminConditions = () => {
const { conditionProvider } = strapi.admin.services.permission;
conditionProvider.register({
displayName: 'Is Creator',
name: 'is-creator',
plugin: 'admin',
handler: user => ({ 'created_by.id': user.id }),
});
};
const cleanPermissionInDatabase = async () => {
const { actionProvider } = strapi.admin.services.permission;
const dbPermissions = await strapi.admin.services.permission.find();
@ -26,19 +38,101 @@ const cleanPermissionInDatabase = async () => {
await strapi.admin.services.permission.deleteByIds(permissionsToRemoveIds);
};
const registerAdminConditions = () => {
const { conditionProvider } = strapi.admin.services.permission;
const getNestedFields = (attributes, fieldPath = '', nestingLevel = 3) => {
if (nestingLevel === 0) {
return fieldPath ? [fieldPath] : [];
}
conditionProvider.register({
displayName: 'Is Creator',
name: 'is-creator',
plugin: 'admin',
handler: user => ({ 'created_by.id': user.id }),
const fields = [];
_.forIn(attributes, (attribute, attributeName) => {
const newFieldPath = fieldPath ? `${fieldPath}.${attributeName}` : attributeName;
if (attribute.type === 'component') {
const component = strapi.components[attribute.component];
const componentFields = getNestedFields(component.attributes, newFieldPath, nestingLevel - 1);
fields.push(...componentFields);
} else {
fields.push(newFieldPath);
}
});
return fields;
};
const createRolesIfNeeded = async () => {
const someRolesExist = await strapi.admin.services.role.exists();
if (someRolesExist) {
return;
}
const defaultActionsIds = [
'plugins::content-manager.read',
'plugins::content-manager.create',
'plugins::content-manager.update',
'plugins::content-manager.delete',
];
const allActions = strapi.admin.services.permission.provider.getAll();
const contentTypesActions = allActions.filter(a => defaultActionsIds.includes(a.actionId));
await strapi.admin.services.role.create({
name: 'Super Admin',
code: 'strapi-super-admin',
description: 'Super Admins can access and manage all features and settings.',
});
const editorRole = await strapi.admin.services.role.create({
name: 'Editor',
code: 'strapi-editor',
description: 'Editors can manage and publish contents including those of other users.',
});
const authorRole = await strapi.admin.services.role.create({
name: 'Author',
code: 'strapi-author',
description: 'Authors can manage and publish their own content.',
});
const editorPermissions = [];
contentTypesActions.forEach(action => {
_.forIn(strapi.contentTypes, contentType => {
if (action.subjects.includes(contentType.uid)) {
const fields = getNestedFields(contentType.attributes);
editorPermissions.push({
action: action.actionId,
subject: contentType.uid,
fields,
});
}
});
});
await strapi.admin.services.permission.assign(editorRole.id, editorPermissions);
await strapi.admin.services.permission.assign(authorRole.id, editorPermissions);
};
const displayWarningIfNoSuperAdmin = async () => {
const adminRole = await strapi.admin.services.role.getAdminWithUsersCount();
const someUsersExists = await strapi.admin.services.user.exists();
if (!adminRole) {
return strapi.log.warn("Your application doesn't have a super admin role.");
} else if (someUsersExists && adminRole.usersCount === 0) {
return strapi.log.warn("Your application doesn't have a super admin user.");
}
};
const displayWarningIfUsersDontHaveRole = async () => {
const count = await strapi.admin.services.user.countUsersWithoutRole();
if (count > 0) {
strapi.log.warn(`You have ${count} user${count === 1 ? '' : 's'} without any role.`);
}
};
module.exports = async () => {
registerAdminConditions();
registerPermissionActions();
await cleanPermissionInDatabase();
await createRolesIfNeeded();
await displayWarningIfNoSuperAdmin();
await displayWarningIfUsersDontHaveRole();
};

View File

@ -0,0 +1,3 @@
{
"superAdminCode": "strapi-super-admin"
}

View File

@ -113,11 +113,13 @@ module.exports = {
return ctx.badRequest('You cannot register a new super admin');
}
// TODO: assign super admin role
const adminRole = await strapi.admin.services.role.getAdmin();
const user = await strapi.admin.services.user.create({
...input,
registrationToken: null,
isActive: true,
roles: adminRole ? [adminRole.id] : [],
});
ctx.body = {

View File

@ -3,6 +3,7 @@
const {
validateRoleCreateInput,
validateRoleUpdateInput,
validateRolesDeleteInput,
validateRoleDeleteInput,
} = require('../validation/role');
const { validatedUpdatePermissionsInput } = require('../validation/permission');
@ -57,6 +58,12 @@ module.exports = {
async deleteOne(ctx) {
const { id } = ctx.params;
try {
await validateRoleDeleteInput(id);
} catch (err) {
return ctx.badRequest('ValidationError', err);
}
const roles = await strapi.admin.services.role.deleteByIds([id]);
const sanitizedRole = roles.map(strapi.admin.services.role.sanitizeRole)[0] || null;
@ -73,7 +80,7 @@ module.exports = {
async deleteMany(ctx) {
const { body } = ctx.request;
try {
await validateRoleDeleteInput(body);
await validateRolesDeleteInput(body);
} catch (err) {
return ctx.badRequest('ValidationError', err);
}

View File

@ -23,17 +23,31 @@ const roleUpdateSchema = yup
})
.noUnknown();
const roleDeleteSchema = yup
const rolesDeleteSchema = yup
.object()
.shape({
ids: yup
.array()
.of(yup.strapiID())
.min(1)
.required(),
.required()
.test('no-admin-many-delete', 'you cannot delete the super admin role', async ids => {
const adminRole = await strapi.admin.services.role.getAdmin();
return !ids.map(String).includes(String(adminRole.id));
}),
})
.noUnknown();
const roleDeleteSchema = yup
.strapiID()
.required()
.test('no-admin-single-delete', 'you cannot delete the super admin role', async function(id) {
const adminRole = await strapi.admin.services.role.getAdmin();
return String(id) !== String(adminRole.id)
? true
: this.createError({ path: 'id', message: `you cannot delete the super admin role` });
});
const validateRoleCreateInput = async data => {
return roleCreateSchema.validate(data, { strict: true, abortEarly: false }).catch(handleReject);
};
@ -42,6 +56,10 @@ const validateRoleUpdateInput = async data => {
return roleUpdateSchema.validate(data, { strict: true, abortEarly: false }).catch(handleReject);
};
const validateRolesDeleteInput = async data => {
return rolesDeleteSchema.validate(data, { strict: true, abortEarly: false }).catch(handleReject);
};
const validateRoleDeleteInput = async data => {
return roleDeleteSchema.validate(data, { strict: true, abortEarly: false }).catch(handleReject);
};
@ -49,5 +67,6 @@ const validateRoleDeleteInput = async data => {
module.exports = {
validateRoleCreateInput,
validateRoleUpdateInput,
validateRolesDeleteInput,
validateRoleDeleteInput,
};

View File

@ -15,6 +15,12 @@
"configurable": false,
"required": true
},
"code": {
"type": "string",
"minLength": 1,
"unique": true,
"configurable": false
},
"description": {
"type": "string",
"configurable": false

View File

@ -133,9 +133,20 @@ const deleteByIds = async (ids = []) => {
* @returns {Promise<integer>}
*/
const getUsersCount = async roleId => {
return strapi.query('user', 'admin').count({ 'roles.id': roleId });
return strapi.query('user', 'admin').count({ roles: [roleId] });
};
/** Returns admin role
* @returns {Promise<role>}
*/
const getAdmin = () => findOne({ code: strapi.admin.config.superAdminCode });
/** Returns admin role with userCount
* @returns {Promise<role>}
*/
const getAdminWithUsersCount = () =>
findOneWithUsersCount({ code: strapi.admin.config.superAdminCode });
module.exports = {
sanitizeRole,
create,
@ -147,4 +158,6 @@ module.exports = {
exists,
deleteByIds,
getUsersCount,
getAdmin,
getAdminWithUsersCount,
};

View File

@ -140,6 +140,35 @@ const deleteOne = async query => {
return strapi.query('user', 'admin').delete(query);
};
/** Count the users that don't have any associated roles
* @returns {Promise<number>}
*/
const countUsersWithoutRole = async () => {
const userModel = strapi.query('user', 'admin').model;
const assocTable = userModel.associations.find(a => a.alias === 'roles').tableCollectionName;
let count;
if (userModel.orm === 'bookshelf') {
const result = await userModel
.query(qb => {
qb.count()
.leftJoin(assocTable, `${userModel.collectionName}.id`, `${assocTable}.user_id`)
.where(`${assocTable}.role_id`, null);
})
.fetch();
count = result.toJSON()['count(*)'];
} else if (userModel.orm === 'mongoose') {
count = await strapi.query('user', 'admin').model.countDocuments({ roles: { $size: 0 } });
} else {
const allRoles = await strapi.query('role', 'admin').find();
count = await strapi.query('user', 'admin').count({
roles_nin: allRoles.map(r => r.id),
});
}
return count;
};
module.exports = {
create,
update,
@ -151,4 +180,5 @@ module.exports = {
findPage,
searchPage,
deleteOne,
countUsersWithoutRole,
};