Merge pull request #107 from strapi/chore/disallow-disabling-last-super-admin

Chore/disallow disabling last super admin
This commit is contained in:
Alexandre BODIN 2020-07-08 17:20:06 +02:00 committed by GitHub
commit f2d449a965
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 18 deletions

View File

@ -1,5 +1,7 @@
'use strict'; 'use strict';
const { SUPER_ADMIN_CODE } = require('../services/constants');
/** /**
* Create a new user model by merging default and specified attributes * Create a new user model by merging default and specified attributes
* @param attributes A partial user object * @param attributes A partial user object
@ -13,6 +15,11 @@ function createUser(attributes) {
}; };
} }
const hasSuperAdminRole = user => {
return user.roles.filter(role => role.code === SUPER_ADMIN_CODE).length > 0;
};
module.exports = { module.exports = {
createUser, createUser,
hasSuperAdminRole,
}; };

View File

@ -132,12 +132,13 @@ describe('User', () => {
const id = 1; const id = 1;
const input = { email: 'test@strapi.io', password: '123' }; const input = { email: 'test@strapi.io', password: '123' };
const findOne = jest.fn((_, user) => Promise.resolve(user));
const update = jest.fn((_, user) => Promise.resolve(user)); const update = jest.fn((_, user) => Promise.resolve(user));
const hashPassword = jest.fn(() => Promise.resolve(hash)); const hashPassword = jest.fn(() => Promise.resolve(hash));
global.strapi = { global.strapi = {
query() { query() {
return { update }; return { update, findOne };
}, },
admin: { admin: {
services: { services: {
@ -160,11 +161,13 @@ describe('User', () => {
const user = { const user = {
email: 'test@strapi.io', email: 'test@strapi.io',
}; };
const findOne = jest.fn(() => Promise.resolve(user));
const update = jest.fn(() => Promise.resolve(user)); const update = jest.fn(() => Promise.resolve(user));
global.strapi = { global.strapi = {
query() { query() {
return { update }; return { update, findOne };
}, },
}; };
const id = 1; const id = 1;

View File

@ -1,8 +1,8 @@
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const { stringIncludes, stringEquals } = require('strapi-utils'); const { stringIncludes } = require('strapi-utils');
const { createUser } = require('../domain/user'); const { createUser, hasSuperAdminRole } = require('../domain/user');
const { SUPER_ADMIN_CODE } = require('./constants'); const { SUPER_ADMIN_CODE } = require('./constants');
const sanitizeUserRoles = role => _.pick(role, ['id', 'name', 'description', 'code']); const sanitizeUserRoles = role => _.pick(role, ['id', 'name', 'description', 'code']);
@ -51,21 +51,27 @@ const create = async attributes => {
const updateById = async (id, attributes) => { const updateById = async (id, attributes) => {
// Check at least one super admin remains // Check at least one super admin remains
if (_.has(attributes, 'roles')) { if (_.has(attributes, 'roles')) {
const lastAdminUser = await isLastSuperAdminUser(id);
const superAdminRole = await strapi.admin.services.role.getSuperAdminWithUsersCount(); const superAdminRole = await strapi.admin.services.role.getSuperAdminWithUsersCount();
const nbOfSuperAdminUsers = _.get(superAdminRole, 'usersCount'); const willRemoveSuperAdminRole = !stringIncludes(attributes.roles, superAdminRole.id);
const mayRemoveSuperAdmins = !stringIncludes(attributes.roles, superAdminRole.id);
if (nbOfSuperAdminUsers === 1 && mayRemoveSuperAdmins) { if (lastAdminUser && willRemoveSuperAdminRole) {
const userWithAdminRole = await strapi
.query('user', 'admin')
.findOne({ roles: [superAdminRole.id] });
if (stringEquals(userWithAdminRole.id, id)) {
throw strapi.errors.badRequest( throw strapi.errors.badRequest(
'ValidationError', 'ValidationError',
'You must have at least one user with super admin role.' 'You must have at least one user with super admin role.'
); );
} }
} }
// cannot disable last super admin
if (attributes.isActive === false) {
const lastAdminUser = await isLastSuperAdminUser(id);
if (lastAdminUser) {
throw strapi.errors.badRequest(
'ValidationError',
'You must have at least one active user with super admin role.'
);
}
} }
// hash password if a new one is sent // hash password if a new one is sent
@ -84,6 +90,17 @@ const updateById = async (id, attributes) => {
return strapi.query('user', 'admin').update({ id }, attributes); return strapi.query('user', 'admin').update({ id }, attributes);
}; };
/**
* Check if a user is the last super admin
* @param {int|string} userId user's id to look for
*/
const isLastSuperAdminUser = async userId => {
const user = await findOne({ id: userId }, ['roles']);
const superAdminRole = await strapi.admin.services.role.getSuperAdminWithUsersCount();
return superAdminRole.usersCount === 1 && hasSuperAdminRole(user);
};
/** /**
* Check if a user with specific attributes exists in the database * Check if a user with specific attributes exists in the database
* @param attributes A partial user object * @param attributes A partial user object
@ -133,8 +150,8 @@ const register = async ({ registrationToken, userInfo }) => {
/** /**
* Find one user * Find one user
*/ */
const findOne = async params => { const findOne = async (params, populate) => {
return strapi.query('user', 'admin').findOne(params); return strapi.query('user', 'admin').findOne(params, populate);
}; };
/** Find many users (paginated) /** Find many users (paginated)