diff --git a/packages/strapi-admin/domain/user.js b/packages/strapi-admin/domain/user.js index ce247b1092..aea98a9a4c 100644 --- a/packages/strapi-admin/domain/user.js +++ b/packages/strapi-admin/domain/user.js @@ -1,5 +1,7 @@ 'use strict'; +const { SUPER_ADMIN_CODE } = require('../services/constants'); + /** * Create a new user model by merging default and specified attributes * @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 = { createUser, + hasSuperAdminRole, }; diff --git a/packages/strapi-admin/services/__tests__/user.test.js b/packages/strapi-admin/services/__tests__/user.test.js index f7fde51709..fab3ccc9c6 100644 --- a/packages/strapi-admin/services/__tests__/user.test.js +++ b/packages/strapi-admin/services/__tests__/user.test.js @@ -132,12 +132,13 @@ describe('User', () => { const id = 1; 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 hashPassword = jest.fn(() => Promise.resolve(hash)); global.strapi = { query() { - return { update }; + return { update, findOne }; }, admin: { services: { @@ -160,11 +161,13 @@ describe('User', () => { const user = { email: 'test@strapi.io', }; + + const findOne = jest.fn(() => Promise.resolve(user)); const update = jest.fn(() => Promise.resolve(user)); global.strapi = { query() { - return { update }; + return { update, findOne }; }, }; const id = 1; diff --git a/packages/strapi-admin/services/user.js b/packages/strapi-admin/services/user.js index 3c2c3cc482..8019f54a18 100644 --- a/packages/strapi-admin/services/user.js +++ b/packages/strapi-admin/services/user.js @@ -1,8 +1,8 @@ 'use strict'; const _ = require('lodash'); -const { stringIncludes, stringEquals } = require('strapi-utils'); -const { createUser } = require('../domain/user'); +const { stringIncludes } = require('strapi-utils'); +const { createUser, hasSuperAdminRole } = require('../domain/user'); const { SUPER_ADMIN_CODE } = require('./constants'); const sanitizeUserRoles = role => _.pick(role, ['id', 'name', 'description', 'code']); @@ -51,20 +51,26 @@ const create = async attributes => { const updateById = async (id, attributes) => { // Check at least one super admin remains if (_.has(attributes, 'roles')) { + const lastAdminUser = await isLastSuperAdminUser(id); const superAdminRole = await strapi.admin.services.role.getSuperAdminWithUsersCount(); - const nbOfSuperAdminUsers = _.get(superAdminRole, 'usersCount'); - const mayRemoveSuperAdmins = !stringIncludes(attributes.roles, superAdminRole.id); + const willRemoveSuperAdminRole = !stringIncludes(attributes.roles, superAdminRole.id); - if (nbOfSuperAdminUsers === 1 && mayRemoveSuperAdmins) { - const userWithAdminRole = await strapi - .query('user', 'admin') - .findOne({ roles: [superAdminRole.id] }); - if (stringEquals(userWithAdminRole.id, id)) { - throw strapi.errors.badRequest( - 'ValidationError', - 'You must have at least one user with super admin role.' - ); - } + if (lastAdminUser && willRemoveSuperAdminRole) { + throw strapi.errors.badRequest( + 'ValidationError', + '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.' + ); } } @@ -84,6 +90,17 @@ const updateById = async (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 * @param attributes A partial user object @@ -133,8 +150,8 @@ const register = async ({ registrationToken, userInfo }) => { /** * Find one user */ -const findOne = async params => { - return strapi.query('user', 'admin').findOne(params); +const findOne = async (params, populate) => { + return strapi.query('user', 'admin').findOne(params, populate); }; /** Find many users (paginated)