mirror of
https://github.com/strapi/strapi.git
synced 2025-12-26 14:44:31 +00:00
create role at startup + warning
Signed-off-by: Pierre Noël <petersg83@gmail.com>
This commit is contained in:
parent
84c4591816
commit
711c58b05e
108
packages/strapi-admin/config/functions/bootstrap.js
vendored
108
packages/strapi-admin/config/functions/bootstrap.js
vendored
@ -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();
|
||||
};
|
||||
|
||||
3
packages/strapi-admin/config/settings.json
Normal file
3
packages/strapi-admin/config/settings.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"superAdminCode": "strapi-super-admin"
|
||||
}
|
||||
@ -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 = {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -15,6 +15,12 @@
|
||||
"configurable": false,
|
||||
"required": true
|
||||
},
|
||||
"code": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"unique": true,
|
||||
"configurable": false
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"configurable": false
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user