367 lines
9.7 KiB
JavaScript
Raw Normal View History

'use strict';
const _ = require('lodash');
2021-09-27 16:06:43 +02:00
const { defaults } = require('lodash/fp');
2021-04-29 13:51:12 +02:00
const { stringIncludes } = require('@strapi/utils');
2021-10-20 17:30:05 +02:00
const { ValidationError } = require('@strapi/utils').errors;
const { createUser, hasSuperAdminRole } = require('../domain/user');
const { password: passwordValidator } = require('../validation/common-validators');
2021-07-28 15:32:21 +02:00
const { getService } = require('../utils');
const { SUPER_ADMIN_CODE } = require('./constants');
2022-08-08 23:33:39 +02:00
const sanitizeUserRoles = (role) => _.pick(role, ['id', 'name', 'description', 'code']);
/**
* Remove private user fields
* @param {Object} user - user to sanitize
*/
2022-08-08 23:33:39 +02:00
const sanitizeUser = (user) => {
2020-05-27 16:06:15 +02:00
return {
..._.omit(user, ['password', 'resetPasswordToken', 'registrationToken', 'roles']),
roles: user.roles && user.roles.map(sanitizeUserRoles),
2020-05-27 16:06:15 +02:00
};
};
/**
* Create and save a user in database
* @param attributes A partial user object
* @returns {Promise<user>}
*/
2022-08-08 23:33:39 +02:00
const create = async (attributes) => {
const userInfo = {
2021-07-28 15:32:21 +02:00
registrationToken: getService('token').createToken(),
...attributes,
};
if (_.has(attributes, 'password')) {
2021-07-28 15:32:21 +02:00
userInfo.password = await getService('auth').hashPassword(attributes.password);
}
const user = createUser(userInfo);
2021-06-22 17:13:11 +02:00
const createdUser = await strapi.query('admin::user').create({ data: user, populate: ['roles'] });
2021-08-06 10:51:34 +02:00
getService('metrics').sendDidInviteUser();
2022-12-23 11:52:31 +01:00
strapi.eventHub.emit('user.create', { user: sanitizeUser(createdUser) });
2022-12-16 09:52:52 -05:00
return createdUser;
};
/**
* Update a user in database
* @param id query params to find the user to update
* @param attributes A partial user object
* @returns {Promise<user>}
*/
const updateById = async (id, attributes) => {
// Check at least one super admin remains
if (_.has(attributes, 'roles')) {
const lastAdminUser = await isLastSuperAdminUser(id);
2021-07-28 15:32:21 +02:00
const superAdminRole = await getService('role').getSuperAdminWithUsersCount();
const willRemoveSuperAdminRole = !stringIncludes(attributes.roles, superAdminRole.id);
if (lastAdminUser && willRemoveSuperAdminRole) {
2021-10-20 17:30:05 +02:00
throw new 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) {
2021-10-20 17:30:05 +02:00
throw new ValidationError('You must have at least one user with super admin role.');
}
}
// hash password if a new one is sent
if (_.has(attributes, 'password')) {
2021-07-28 15:32:21 +02:00
const hashedPassword = await getService('auth').hashPassword(attributes.password);
2022-12-16 10:25:00 -05:00
const updatedUser = await strapi.query('admin::user').update({
2021-06-22 17:13:11 +02:00
where: { id },
data: {
...attributes,
password: hashedPassword,
2021-06-22 17:13:11 +02:00
},
2021-07-28 15:32:21 +02:00
populate: ['roles'],
2021-06-22 17:13:11 +02:00
});
2022-12-16 10:25:00 -05:00
2022-12-23 11:52:31 +01:00
strapi.eventHub.emit('user.update', { user: sanitizeUser(updatedUser) });
2022-12-16 10:25:00 -05:00
return updatedUser;
}
2022-12-16 10:25:00 -05:00
const updatedUser = await strapi.query('admin::user').update({
2021-06-22 17:13:11 +02:00
where: { id },
data: attributes,
2021-07-28 15:32:21 +02:00
populate: ['roles'],
2021-06-22 17:13:11 +02:00
});
2022-12-16 10:07:04 -05:00
2023-01-25 16:58:37 +01:00
if (updatedUser) {
strapi.eventHub.emit('user.update', { user: sanitizeUser(updatedUser) });
}
2022-12-16 10:07:04 -05:00
return updatedUser;
};
/**
* Reset a user password by email. (Used in admin:reset CLI)
* @param {string} email - user email
* @param {string} password - new password
*/
const resetPasswordByEmail = async (email, password) => {
2021-09-27 16:06:43 +02:00
const user = await strapi.query('admin::user').findOne({ where: { email }, populate: ['roles'] });
if (!user) {
throw new Error(`User not found for email: ${email}`);
}
try {
await passwordValidator.validate(password);
} catch (error) {
2021-10-27 18:54:58 +02:00
throw new ValidationError(
'Invalid password. Expected a minimum of 8 characters with at least one number and one uppercase letter'
);
}
await updateById(user.id, { password });
};
/**
* Check if a user is the last super admin
* @param {int|string} userId user's id to look for
*/
2022-08-08 23:33:39 +02:00
const isLastSuperAdminUser = async (userId) => {
2021-09-27 16:44:05 +02:00
const user = await findOne(userId);
2021-07-28 15:32:21 +02:00
const superAdminRole = await getService('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
* @returns {Promise<boolean>}
*/
const exists = async (attributes = {}) => {
return (await strapi.query('admin::user').count({ where: attributes })) > 0;
};
/**
* Returns a user registration info
* @param {string} registrationToken - a user registration token
* @returns {Promise<registrationInfo>} - Returns user email, firstname and lastname
*/
2022-08-08 23:33:39 +02:00
const findRegistrationInfo = async (registrationToken) => {
const user = await strapi.query('admin::user').findOne({ where: { registrationToken } });
if (!user) {
return undefined;
}
return _.pick(user, ['email', 'firstname', 'lastname']);
};
/**
* Registers a user based on a registrationToken and some informations to update
* @param {Object} params
* @param {Object} params.registrationToken registration token
* @param {Object} params.userInfo user info
*/
const register = async ({ registrationToken, userInfo }) => {
const matchingUser = await strapi.query('admin::user').findOne({ where: { registrationToken } });
if (!matchingUser) {
2021-10-20 17:30:05 +02:00
throw new ValidationError('Invalid registration info');
}
2021-07-28 15:32:21 +02:00
return getService('user').updateById(matchingUser.id, {
password: userInfo.password,
firstname: userInfo.firstname,
lastname: userInfo.lastname,
registrationToken: null,
isActive: true,
});
};
2020-05-28 14:03:48 +02:00
/**
* Find one user
*/
2021-09-27 16:06:43 +02:00
const findOne = async (id, populate = ['roles']) => {
2022-12-16 10:33:51 -05:00
return strapi.entityService.findOne('admin::user', id, { populate });
2020-05-28 14:03:48 +02:00
};
/**
* Find one user by its email
* @param {string} id email
* @param {string || string[] || object} populate
* @returns
*/
const findOneByEmail = async (email, populate = []) => {
return strapi.query('admin::user').findOne({
where: { email },
populate,
});
};
/** Find many users (paginated)
* @param query
* @returns {Promise<user>}
*/
2021-07-05 21:20:24 +02:00
const findPage = async (query = {}) => {
2021-09-27 16:06:43 +02:00
const enrichedQuery = defaults({ populate: ['roles'] }, query);
return strapi.entityService.findPage('admin::user', enrichedQuery);
};
/** Delete a user
* @param id id of the user to delete
* @returns {Promise<user>}
*/
2022-08-08 23:33:39 +02:00
const deleteById = async (id) => {
// Check at least one super admin remains
const userToDelete = await strapi.query('admin::user').findOne({
2021-06-22 17:13:11 +02:00
where: { id },
populate: ['roles'],
});
2021-06-29 16:27:35 +02:00
if (!userToDelete) {
return null;
}
if (userToDelete) {
2022-08-08 23:33:39 +02:00
if (userToDelete.roles.some((r) => r.code === SUPER_ADMIN_CODE)) {
2021-07-28 15:32:21 +02:00
const superAdminRole = await getService('role').getSuperAdminWithUsersCount();
if (superAdminRole.usersCount === 1) {
2021-10-20 17:30:05 +02:00
throw new ValidationError('You must have at least one user with super admin role.');
}
}
}
2022-12-16 09:59:52 -05:00
const deletedUser = await strapi
.query('admin::user')
.delete({ where: { id }, populate: ['roles'] });
2022-12-23 11:52:31 +01:00
strapi.eventHub.emit('user.delete', { user: sanitizeUser(deletedUser) });
2022-12-16 09:59:52 -05:00
return deletedUser;
};
/** Delete a user
* @param ids ids of the users to delete
* @returns {Promise<user>}
*/
2022-08-08 23:33:39 +02:00
const deleteByIds = async (ids) => {
// Check at least one super admin remains
2021-07-28 15:32:21 +02:00
const superAdminRole = await getService('role').getSuperAdminWithUsersCount();
const nbOfSuperAdminToDelete = await strapi.query('admin::user').count({
2021-06-22 17:13:11 +02:00
where: {
id: ids,
roles: { id: superAdminRole.id },
},
});
if (superAdminRole.usersCount === nbOfSuperAdminToDelete) {
2021-10-20 17:30:05 +02:00
throw new ValidationError('You must have at least one user with super admin role.');
}
2021-06-29 16:27:35 +02:00
const deletedUsers = [];
for (const id of ids) {
const deletedUser = await strapi.query('admin::user').delete({
2021-06-29 16:27:35 +02:00
where: { id },
2021-07-28 15:32:21 +02:00
populate: ['roles'],
2021-06-29 16:27:35 +02:00
});
deletedUsers.push(deletedUser);
}
strapi.eventHub.emit('user.delete', {
users: deletedUsers.map((deletedUser) => sanitizeUser(deletedUser)),
});
2022-12-16 09:59:52 -05:00
2021-06-29 16:27:35 +02:00
return deletedUsers;
};
/** Count the users that don't have any associated roles
* @returns {Promise<number>}
*/
const countUsersWithoutRole = async () => {
return strapi.query('admin::user').count({
2021-06-25 12:07:32 +02:00
where: {
roles: {
id: { $null: true },
},
},
});
};
/**
* Count the number of users based on search params
* @param params params used for the query
* @returns {Promise<number>}
*/
2021-06-22 17:13:11 +02:00
const count = async (where = {}) => {
return strapi.query('admin::user').count({ where });
};
/** Assign some roles to several users
* @returns {undefined}
*/
2022-08-08 23:33:39 +02:00
const assignARoleToAll = async (roleId) => {
const users = await strapi.query('admin::user').findMany({
2021-06-24 18:28:36 +02:00
select: ['id'],
where: {
roles: { id: { $null: true } },
},
2021-06-22 17:13:11 +02:00
});
2021-06-28 12:34:29 +02:00
await Promise.all(
2022-08-08 23:33:39 +02:00
users.map((user) => {
return strapi.query('admin::user').update({
2021-06-28 12:34:29 +02:00
where: { id: user.id },
data: { roles: [roleId] },
});
})
);
};
/** Display a warning if some users don't have at least one role
* @returns {Promise<>}
*/
const displayWarningIfUsersDontHaveRole = async () => {
const count = await countUsersWithoutRole();
if (count > 0) {
strapi.log.warn(`Some users (${count}) don't have any role.`);
}
};
/** Returns an array of interface languages currently used by users
* @returns {Promise<Array<string>>}
*/
const getLanguagesInUse = async () => {
const users = await strapi.query('admin::user').findMany({ select: ['preferedLanguage'] });
2022-08-08 23:33:39 +02:00
return users.map((user) => user.preferedLanguage || 'en');
};
module.exports = {
create,
updateById,
exists,
findRegistrationInfo,
register,
sanitizeUser,
2020-05-28 14:03:48 +02:00
findOne,
findOneByEmail,
findPage,
deleteById,
deleteByIds,
countUsersWithoutRole,
count,
assignARoleToAll,
displayWarningIfUsersDontHaveRole,
resetPasswordByEmail,
getLanguagesInUse,
};