tududi/backend/modules/admin/service.js

259 lines
6.9 KiB
JavaScript

'use strict';
const adminRepository = require('./repository');
const {
validateUserId,
validateEmail,
validatePassword,
validateSetAdminRole,
validateCreateUser,
validateToggleRegistration,
} = require('./validation');
const {
NotFoundError,
ValidationError,
ForbiddenError,
UnauthorizedError,
ConflictError,
} = require('../../shared/errors');
const { isAdmin } = require('../../services/rolesService');
class AdminService {
/**
* Check if requester is admin or if bootstrapping (no roles yet).
*/
async verifyAdminOrBootstrap(requesterId) {
if (!requesterId) {
throw new UnauthorizedError('Authentication required');
}
const requester = await adminRepository.findUserUidById(requesterId);
if (!requester) {
throw new UnauthorizedError('Authentication required');
}
const requesterIsAdmin = await isAdmin(requester.uid);
const existingRolesCount = await adminRepository.countRoles();
if (!requesterIsAdmin && existingRolesCount > 0) {
throw new ForbiddenError('Forbidden');
}
return true;
}
/**
* Check if requester is admin.
*/
async verifyAdmin(requesterId) {
if (!requesterId) {
throw new UnauthorizedError('Authentication required');
}
const user = await adminRepository.findUserUidById(requesterId);
if (!user) {
throw new UnauthorizedError('Authentication required');
}
const admin = await isAdmin(user.uid);
if (!admin) {
throw new ForbiddenError('Forbidden');
}
return true;
}
/**
* Set admin role for a user.
*/
async setAdminRole(requesterId, body) {
await this.verifyAdminOrBootstrap(requesterId);
const { user_id, is_admin: makeAdmin } = validateSetAdminRole(body);
const user = await adminRepository.findUserById(user_id);
if (!user) {
throw new ValidationError('Invalid user_id');
}
const [role] = await adminRepository.findOrCreateRole(
user_id,
makeAdmin
);
if (role.is_admin !== makeAdmin) {
role.is_admin = makeAdmin;
await role.save();
}
return { user_id, is_admin: role.is_admin };
}
/**
* List all users with roles.
*/
async listUsers(requesterId) {
await this.verifyAdmin(requesterId);
const users = await adminRepository.findAllUsers();
const roles = await adminRepository.findAllRoles();
const userIdToRole = new Map(roles.map((r) => [r.user_id, r.is_admin]));
return users.map((u) => ({
id: u.id,
email: u.email,
name: u.name,
surname: u.surname,
created_at: u.created_at,
role: userIdToRole.get(u.id) ? 'admin' : 'user',
}));
}
/**
* Create a new user.
*/
async createUser(requesterId, body) {
await this.verifyAdmin(requesterId);
const { email, password, name, surname, role } =
validateCreateUser(body);
const userData = { email, password };
if (name) userData.name = name;
if (surname) userData.surname = surname;
let user;
try {
user = await adminRepository.createUser(userData);
} catch (err) {
if (err?.name === 'SequelizeUniqueConstraintError') {
throw new ConflictError('Email already exists');
}
throw err;
}
const makeAdmin = role === 'admin';
if (makeAdmin) {
const [userRole, roleCreated] =
await adminRepository.findOrCreateRole(user.id, true);
if (!roleCreated && !userRole.is_admin) {
userRole.is_admin = true;
await userRole.save();
}
}
return {
id: user.id,
email: user.email,
name: user.name,
surname: user.surname,
created_at: user.created_at,
role: makeAdmin ? 'admin' : 'user',
};
}
/**
* Update a user.
*/
async updateUser(requesterId, userId, body) {
await this.verifyAdmin(requesterId);
const id = validateUserId(userId);
const user = await adminRepository.findUserById(id);
if (!user) {
throw new NotFoundError('User not found');
}
const { email, password, name, surname, role } = body || {};
if (email !== undefined && email !== null) {
validateEmail(email);
user.email = email;
}
if (password && password.trim() !== '') {
validatePassword(password);
user.password = password;
user.changed('password_digest', true);
}
if (name !== undefined) user.name = name || null;
if (surname !== undefined) user.surname = surname || null;
try {
await user.save();
} catch (err) {
if (err?.name === 'SequelizeUniqueConstraintError') {
throw new ConflictError('Email already exists');
}
throw err;
}
if (role !== undefined) {
const makeAdmin = role === 'admin';
const [userRole] = await adminRepository.findOrCreateRole(
user.id,
makeAdmin
);
if (userRole.is_admin !== makeAdmin) {
userRole.is_admin = makeAdmin;
await userRole.save();
}
}
const userRole = await adminRepository.findRoleByUserId(user.id);
return {
id: user.id,
email: user.email,
name: user.name,
surname: user.surname,
created_at: user.created_at,
role: userRole?.is_admin ? 'admin' : 'user',
};
}
/**
* Delete a user.
*/
async deleteUser(requesterId, userId) {
await this.verifyAdmin(requesterId);
const id = validateUserId(userId);
if (id === requesterId) {
throw new ValidationError('Cannot delete your own account');
}
const result = await adminRepository.deleteUserWithData(
id,
requesterId
);
if (!result.success) {
if (result.status === 404) {
throw new NotFoundError(result.error);
}
throw new ValidationError(result.error);
}
return null;
}
/**
* Toggle registration setting.
*/
async toggleRegistration(requesterId, body) {
await this.verifyAdmin(requesterId);
const { enabled } = validateToggleRegistration(body);
const {
setRegistrationEnabled,
} = require('../auth/registrationService');
await setRegistrationEnabled(enabled);
return { enabled };
}
}
module.exports = new AdminService();