@sync-in/server
Version:
The secure, open-source platform for file storage, sharing, collaboration, and sync
376 lines (375 loc) • 18.3 kB
JavaScript
/*
* Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>
* This file is part of Sync-in | The open source file sync and share solution
* See the LICENSE file for licensing details
*/ "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "AdminUsersManager", {
enumerable: true,
get: function() {
return AdminUsersManager;
}
});
const _common = require("@nestjs/common");
const _authmanagerservice = require("../../../authentication/services/auth-manager.service");
const _functions = require("../../../common/functions");
const _files = require("../../files/utils/files");
const _group = require("../constants/group");
const _user = require("../constants/user");
const _usermodel = require("../models/user.model");
const _adminusersqueriesservice = require("./admin-users-queries.service");
function _ts_decorate(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
function _ts_metadata(k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
}
let AdminUsersManager = class AdminUsersManager {
listUsers() {
return this.adminQueries.listUsers();
}
async getUser(userId) {
const user = await this.adminQueries.listUsers(userId);
this.checkUser(user, true);
return user;
}
async getGuest(guestId) {
const user = await this.adminQueries.usersQueries.listGuests(guestId, 0, true);
this.checkUser(user, true);
return user;
}
async createUserOrGuest(createUserDto, userRole = _user.USER_ROLE.USER, asAdmin = false) {
await this.loginOrEmailAlreadyUsed(createUserDto.login, createUserDto.email);
try {
createUserDto.password = await (0, _functions.hashPassword)(createUserDto.password);
const userId = await this.adminQueries.usersQueries.createUserOrGuest(createUserDto, userRole);
const user = new _usermodel.UserModel({
...createUserDto,
id: userId,
role: userRole
});
this.logger.log(`${this.createUserOrGuest.name} - ${_user.USER_ROLE[userRole]} (${userId}) was created : ${JSON.stringify((0, _functions.anonymizePassword)(createUserDto))}`);
this.adminQueries.usersQueries.clearWhiteListCaches('*');
await user.makePaths();
if (userRole <= _user.USER_ROLE.USER) {
return asAdmin ? this.getUser(user.id) : user;
} else {
return asAdmin ? this.getGuest(user.id) : user;
}
} catch (e) {
this.logger.error(`${this.createUserOrGuest.name} - unable to create user *${createUserDto.login}* : ${e}`);
throw new _common.HttpException('Unable to create user', _common.HttpStatus.INTERNAL_SERVER_ERROR);
}
}
async updateUserOrGuest(userId, updateUserDto, userRole = _user.USER_ROLE.USER) {
const user = userRole === _user.USER_ROLE.USER ? await this.getUser(userId) : await this.getGuest(userId);
const updateUser = {};
const updateUserGroups = {
add: [],
delete: []
};
const updateGuestManagers = {
add: [],
delete: []
};
for (const [k, v] of Object.entries(updateUserDto)){
switch(k){
case 'login':
if (user.login === v) {
break;
}
if (await this.adminQueries.usersQueries.checkUserExists(v)) {
throw new _common.HttpException('Login already used', _common.HttpStatus.FORBIDDEN);
}
if (!await this.renameUserSpace(user.login, v)) {
throw new _common.HttpException('Unable to rename user space', _common.HttpStatus.INTERNAL_SERVER_ERROR);
}
updateUser.login = v;
break;
case 'email':
if (user.email === v) {
break;
}
if (await this.adminQueries.usersQueries.checkUserExists(null, v)) {
throw new _common.HttpException('Email already used', _common.HttpStatus.FORBIDDEN);
}
updateUser.email = v;
break;
case 'isActive':
updateUser.isActive = v;
if (v) {
updateUser.passwordAttempts = 0;
}
break;
case 'password':
updateUser.password = await (0, _functions.hashPassword)(updateUserDto.password);
break;
case 'groups':
if (userRole === _user.USER_ROLE.USER) {
const currentGroups = user.groups?.length ? user.groups.map((g)=>g.id) : [];
updateUserGroups.add = v.filter((id)=>currentGroups.indexOf(id) === -1);
updateUserGroups.delete = currentGroups.filter((id)=>v.indexOf(id) === -1);
}
break;
case 'managers':
if (userRole === _user.USER_ROLE.GUEST) {
const currentManagers = user.managers?.length ? user.managers.map((m)=>m.id) : [];
updateGuestManagers.add = v.filter((id)=>currentManagers.indexOf(id) === -1);
updateGuestManagers.delete = currentManagers.filter((id)=>v.indexOf(id) === -1);
}
break;
default:
updateUser[k] = v;
}
}
if (Object.keys(updateUser).length) {
// force the type for security reason
const forceRole = userRole === _user.USER_ROLE.GUEST ? _user.USER_ROLE.GUEST : undefined;
if (!await this.adminQueries.usersQueries.updateUserOrGuest(user.id, updateUser, forceRole)) {
throw new _common.HttpException('Unable to update user', _common.HttpStatus.INTERNAL_SERVER_ERROR);
}
}
if (userRole === _user.USER_ROLE.USER) {
if (updateUserGroups.add.length || updateUserGroups.delete.length) {
try {
await this.adminQueries.updateUserGroups(user.id, updateUserGroups);
} catch {
throw new _common.HttpException('Unable to update user groups', _common.HttpStatus.INTERNAL_SERVER_ERROR);
}
}
return this.getUser(userId);
} else {
if (updateGuestManagers.add.length || updateGuestManagers.delete.length) {
try {
await this.adminQueries.updateGuestManagers(user.id, updateGuestManagers);
} catch {
throw new _common.HttpException('Unable to update guest managers', _common.HttpStatus.INTERNAL_SERVER_ERROR);
}
}
return this.getGuest(userId);
}
}
async deleteUserOrGuest(userId, userLogin, deleteUserDto) {
try {
if (await this.adminQueries.deleteUser(userId, userLogin)) {
this.logger.log(`${this.deleteUserOrGuest.name} - *${userLogin}* (${userId}) was deleted`);
} else {
this.logger.error(`${this.deleteUserOrGuest.name} - *${userLogin}* (${userId}) was not deleted : not found`);
}
if (deleteUserDto.deleteSpace) {
await this.deleteUserSpace(userLogin, deleteUserDto.isGuest);
}
} catch (e) {
this.logger.error(`${this.deleteUserOrGuest.name} - unable to delete *${userLogin}* (${userId}) : ${e}`);
throw new _common.HttpException('Unable to delete user', _common.HttpStatus.INTERNAL_SERVER_ERROR);
}
}
async deleteUserOrGuestFromAdmin(userId, deleteUserDto) {
const userToDelete = this.checkUser(await this.adminQueries.usersQueries.from(userId));
if (userToDelete.isGuest !== deleteUserDto.isGuest) {
throw new _common.HttpException('User mismatch', _common.HttpStatus.BAD_REQUEST);
}
await this.deleteUserOrGuest(userToDelete.id, userToDelete.login, {
deleteSpace: deleteUserDto.isGuest ? true : deleteUserDto.deleteSpace,
isGuest: deleteUserDto.isGuest
});
}
listGuests() {
return this.adminQueries.usersQueries.listGuests(null, null, true);
}
createGuest(user, createGuestDto) {
if (!createGuestDto.managers.length) {
createGuestDto.managers.push(user.id);
}
return this.createUserOrGuest(createGuestDto, _user.USER_ROLE.GUEST, true);
}
updateGuest(guestId, updateGuestDto) {
if (!Object.keys(updateGuestDto).length) {
throw new _common.HttpException('No changes to update', _common.HttpStatus.BAD_REQUEST);
}
if (updateGuestDto.managers && !updateGuestDto.managers.length) {
throw new _common.HttpException('Guest must have at least one manager', _common.HttpStatus.BAD_REQUEST);
}
return this.updateUserOrGuest(guestId, updateGuestDto, _user.USER_ROLE.GUEST);
}
async browseGroups(name, type = _group.GROUP_TYPE.USER) {
if (name) {
const group = await this.adminQueries.groupFromName(name);
if (!group) {
throw new _common.HttpException('Group not found', _common.HttpStatus.NOT_FOUND);
}
return {
parentGroup: group,
members: await this.adminQueries.browseGroupMembers(group.id, type)
};
}
return {
parentGroup: undefined,
members: await this.adminQueries.browseRootGroupMembers(type)
};
}
async getGroup(groupId) {
const group = await this.adminQueries.groupFromId(groupId);
if (!group) {
throw new _common.HttpException('Group not found', _common.HttpStatus.NOT_FOUND);
}
return group;
}
async createGroup(createGroupDto) {
if (!createGroupDto.name) {
this.logger.error(`${this.createGroup.name} - missing group name : ${JSON.stringify(createGroupDto)}`);
throw new _common.HttpException('Group name is missing', _common.HttpStatus.BAD_REQUEST);
}
await this.checkGroupNameExists(createGroupDto.name);
try {
const groupId = await this.adminQueries.createGroup(createGroupDto);
this.logger.log(`${this.createGroup.name} - group (${groupId}) was created : ${JSON.stringify(createGroupDto)}`);
return this.adminQueries.groupFromId(groupId);
} catch (e) {
this.logger.error(`${this.createGroup.name} - group was not created : ${JSON.stringify(createGroupDto)} : ${e}`);
throw new _common.HttpException('Unable to create group', _common.HttpStatus.INTERNAL_SERVER_ERROR);
}
}
async updateGroup(groupId, updateGroupDto) {
if (updateGroupDto.name) {
await this.checkGroupNameExists(updateGroupDto.name);
}
if (!await this.adminQueries.updateGroup(groupId, updateGroupDto)) {
throw new _common.HttpException('Unable to update group', _common.HttpStatus.INTERNAL_SERVER_ERROR);
}
// Clear whitelist caches when the group’s visibility is changed
if (updateGroupDto.visibility !== undefined) {
this.adminQueries.usersQueries.clearWhiteListCaches('*');
}
return this.adminQueries.groupFromId(groupId);
}
async deleteGroup(groupId) {
if (await this.adminQueries.deleteGroup(groupId)) {
this.logger.log(`${this.deleteGroup.name} - group (${groupId}) was deleted`);
} else {
this.logger.warn(`${this.deleteGroup.name} - group (${groupId}) does not exist`);
throw new _common.HttpException('Unable to delete group', _common.HttpStatus.BAD_REQUEST);
}
}
async addUsersToGroup(groupId, userIds) {
const group = await this.adminQueries.groupFromId(groupId);
if (!group) {
throw new _common.HttpException('Group not found', _common.HttpStatus.NOT_FOUND);
}
try {
await this.adminQueries.addUsersToGroup(groupId, userIds, group.type === _group.GROUP_TYPE.USER ? _user.USER_ROLE.USER : undefined);
} catch (e) {
throw new _common.HttpException(e.message, _common.HttpStatus.BAD_REQUEST);
}
}
async updateUserFromGroup(groupId, userId, updateUserFromGroupDto) {
try {
await this.adminQueries.updateUserFromGroup(groupId, userId, updateUserFromGroupDto.role);
} catch (e) {
throw new _common.HttpException(e.message, _common.HttpStatus.BAD_REQUEST);
}
}
async removeUserFromGroup(groupId, userId) {
try {
await this.adminQueries.removeUserFromGroup(groupId, userId);
} catch (e) {
throw new _common.HttpException(e.message, _common.HttpStatus.BAD_REQUEST);
}
}
searchMembers(searchMembersDto) {
return this.adminQueries.usersQueries.searchUsersOrGroups(searchMembersDto);
}
async impersonateUser(admin, userId, res) {
if (admin.id === userId) {
throw new _common.HttpException('You are already logged in', _common.HttpStatus.BAD_REQUEST);
}
const user = this.checkUser(await this.adminQueries.usersQueries.from(userId));
user.impersonatedFromId = admin.id;
user.impersonatedClientId = admin.clientId;
return this.authManager.setCookies(user, res);
}
async logoutImpersonateUser(user, res) {
if (!user.impersonatedFromId) {
throw new _common.HttpException('You are not allowed to do this action', _common.HttpStatus.FORBIDDEN);
}
const admin = this.checkUser(await this.adminQueries.usersQueries.from(user.impersonatedFromId));
if (!admin.isAdmin) {
throw new _common.HttpException('You are not allowed to do this action', _common.HttpStatus.FORBIDDEN);
}
admin.clientId = user.impersonatedClientId;
return this.authManager.setCookies(admin, res);
}
async deleteUserSpace(userLogin, isGuest = false) {
const userSpace = _usermodel.UserModel.getHomePath(userLogin, isGuest);
try {
if (await (0, _files.isPathExists)(userSpace)) {
await (0, _files.removeFiles)(userSpace);
this.logger.log(`${this.deleteUserSpace.name} - user space *${userLogin}* was deleted`);
} else {
this.logger.warn(`${this.deleteUserSpace.name} - user space *${userLogin}* does not exist : ${userSpace}`);
}
} catch (e) {
this.logger.warn(`${this.deleteUserSpace.name} - user space *${userLogin}* (${userSpace}) was not deleted : ${e}`);
throw new _common.HttpException('Unable to delete user space', _common.HttpStatus.INTERNAL_SERVER_ERROR);
}
}
checkUser(user, checkOnly = false) {
if (!user) {
throw new _common.HttpException('User not found', _common.HttpStatus.NOT_FOUND);
}
if (!checkOnly) {
return new _usermodel.UserModel(user, true);
}
}
async renameUserSpace(oldLogin, newLogin) {
const currentUserSpace = _usermodel.UserModel.getHomePath(oldLogin);
if (!await (0, _files.isPathExists)(currentUserSpace)) {
this.logger.warn(`${this.renameUserSpace.name} - user space *${oldLogin}* does not exist : ${currentUserSpace}`);
return false;
}
const newUserSpace = _usermodel.UserModel.getHomePath(newLogin);
if (await (0, _files.isPathExists)(newUserSpace)) {
this.logger.warn(`${this.renameUserSpace.name} - user space *${newLogin}* already exists : ${newUserSpace}`);
return false;
}
try {
await (0, _files.moveFiles)(currentUserSpace, newUserSpace);
return true;
} catch (e) {
// try to restore
await (0, _files.moveFiles)(newUserSpace, currentUserSpace, true);
this.logger.error(`${this.renameUserSpace.name} - unable to rename user space from *${currentUserSpace}* to *${newUserSpace}* : ${e}`);
return false;
}
}
async checkGroupNameExists(groupName) {
if (await this.adminQueries.usersQueries.checkGroupNameExists(groupName)) {
throw new _common.HttpException('Name already used', _common.HttpStatus.BAD_REQUEST);
}
}
async loginOrEmailAlreadyUsed(login, email) {
const exists = await this.adminQueries.usersQueries.checkUserExists(login, email);
if (exists) {
throw new _common.HttpException(`${exists.login === login ? 'Login' : 'Email'} already used`, _common.HttpStatus.BAD_REQUEST);
}
}
constructor(authManager, adminQueries){
this.authManager = authManager;
this.adminQueries = adminQueries;
this.logger = new _common.Logger(AdminUsersManager.name);
}
};
AdminUsersManager = _ts_decorate([
(0, _common.Injectable)(),
_ts_metadata("design:type", Function),
_ts_metadata("design:paramtypes", [
typeof _authmanagerservice.AuthManager === "undefined" ? Object : _authmanagerservice.AuthManager,
typeof _adminusersqueriesservice.AdminUsersQueries === "undefined" ? Object : _adminusersqueriesservice.AdminUsersQueries
])
], AdminUsersManager);
//# sourceMappingURL=admin-users-manager.service.js.map