"use strict";
var __decorate = (this && this.__decorate) || function (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;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var UsersController_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.UsersController = void 0;
const api_types_1 = require("@n8n/api-types");
const backend_common_1 = require("@n8n/backend-common");
const db_1 = require("@n8n/db");
const decorators_1 = require("@n8n/decorators");
const permissions_1 = require("@n8n/permissions");
const auth_service_1 = require("../auth/auth.service");
const credentials_service_1 = require("../credentials/credentials.service");
const bad_request_error_1 = require("../errors/response-errors/bad-request.error");
const forbidden_error_1 = require("../errors/response-errors/forbidden.error");
const not_found_error_1 = require("../errors/response-errors/not-found.error");
const event_service_1 = require("../events/event.service");
const external_hooks_1 = require("../external-hooks");
const provisioning_service_ee_1 = require("../modules/provisioning.ee/provisioning.service.ee");
const folder_service_1 = require("../services/folder.service");
const project_service_ee_1 = require("../services/project.service.ee");
const user_service_1 = require("../services/user.service");
const workflow_service_1 = require("../workflows/workflow.service");
const jwt_service_1 = require("../services/jwt.service");
const url_service_1 = require("../services/url.service");
let UsersController = UsersController_1 = class UsersController {
    constructor(logger, externalHooks, sharedCredentialsRepository, sharedWorkflowRepository, userRepository, authService, userService, projectRepository, workflowService, credentialsService, eventService, folderService, jwtService, urlService, projectService, provisioningService) {
        this.logger = logger;
        this.externalHooks = externalHooks;
        this.sharedCredentialsRepository = sharedCredentialsRepository;
        this.sharedWorkflowRepository = sharedWorkflowRepository;
        this.userRepository = userRepository;
        this.authService = authService;
        this.userService = userService;
        this.projectRepository = projectRepository;
        this.workflowService = workflowService;
        this.credentialsService = credentialsService;
        this.eventService = eventService;
        this.folderService = folderService;
        this.jwtService = jwtService;
        this.urlService = urlService;
        this.projectService = projectService;
        this.provisioningService = provisioningService;
    }
    removeSupplementaryFields(publicUsers, listQueryOptions, currentUser) {
        const { select } = listQueryOptions;
        if (select !== undefined && !select.includes('id')) {
            for (const user of publicUsers)
                delete user.id;
        }
        if (select) {
            for (const user of publicUsers) {
                delete user.isOwner;
                delete user.isPending;
                delete user.signInType;
            }
        }
        const usersSeesAllDetails = (0, permissions_1.hasGlobalScope)(currentUser, 'user:create');
        return publicUsers.map((user) => {
            return usersSeesAllDetails || user.id === currentUser.id
                ? api_types_1.userDetailSchema.parse(user)
                : api_types_1.userBaseSchema.parse(user);
        });
    }
    async listUsers(req, _res, listQueryOptions) {
        if (listQueryOptions.filter?.projectId) {
            const project = await this.projectService.getProjectWithScope(req.user, listQueryOptions.filter.projectId, ['project:list']);
            if (!project) {
                throw new not_found_error_1.NotFoundError('Project not found');
            }
        }
        else if (!['global:owner', 'global:admin'].includes(req.user.role.slug)) {
            const isProjectAdmin = (await this.projectService.getProjectIdsWithScope(req.user, ['project:update'])).length > 0;
            if (!isProjectAdmin) {
                throw new forbidden_error_1.ForbiddenError('Listing all users is limited to instance administrators and project admins. Filter by project to list project members.');
            }
        }
        const userQuery = this.userRepository.buildUserQuery(listQueryOptions);
        const response = await userQuery.getManyAndCount();
        const [users, count] = response;
        const publicUsers = await Promise.all(users.map(async (u) => {
            const user = await this.userService.toPublic(u);
            if (listQueryOptions.select && !listQueryOptions.select?.includes('role')) {
                delete user.role;
            }
            return {
                ...user,
                projectRelations: u.projectRelations?.map((pr) => ({
                    id: pr.projectId,
                    role: pr.role.slug,
                    name: pr.project.name,
                })),
            };
        }));
        return api_types_1.usersListSchema.parse({
            count,
            items: this.removeSupplementaryFields(publicUsers, listQueryOptions, req.user),
        });
    }
    async getUserPasswordResetLink(req) {
        const user = await this.userRepository.findOneOrFail({
            where: { id: req.params.id },
            relations: ['role'],
        });
        if (!user) {
            throw new not_found_error_1.NotFoundError('User not found');
        }
        if (req.user.role.slug === db_1.GLOBAL_ADMIN_ROLE.slug &&
            user.role.slug === db_1.GLOBAL_OWNER_ROLE.slug) {
            throw new forbidden_error_1.ForbiddenError('Admin cannot reset password of global owner');
        }
        const link = this.authService.generatePasswordResetUrl(user);
        return { link };
    }
    async generateInviteLink(req, _res) {
        const inviterId = req.user.id;
        const inviteeId = req.params.id;
        const targetUser = await this.userRepository.findOne({ where: { id: inviteeId } });
        if (!targetUser) {
            throw new not_found_error_1.NotFoundError('User to generate invite link for not found');
        }
        const token = this.jwtService.sign({
            inviterId,
            inviteeId,
        }, {
            expiresIn: '90d',
        });
        const baseUrl = this.urlService.getInstanceBaseUrl();
        const inviteLink = `${baseUrl}/signup?token=${token}`;
        return { link: inviteLink };
    }
    async updateUserSettings(_req, _res, payload, id) {
        await this.userService.updateSettings(id, payload);
        const user = await this.userRepository.findOneOrFail({
            select: ['settings'],
            where: { id },
        });
        return user.settings;
    }
    async deleteUser(req) {
        const { id: idToDelete } = req.params;
        if (req.user.id === idToDelete) {
            this.logger.debug('Request to delete a user failed because it attempted to delete the requesting user', { userId: req.user.id });
            throw new bad_request_error_1.BadRequestError('Cannot delete your own user');
        }
        const { transferId } = req.query;
        const userToDelete = await this.userRepository.findOne({
            where: { id: idToDelete },
            relations: ['role'],
        });
        if (!userToDelete) {
            throw new not_found_error_1.NotFoundError('Request to delete a user failed because the user to delete was not found in DB');
        }
        if (userToDelete.role.slug === db_1.GLOBAL_OWNER_ROLE.slug) {
            throw new forbidden_error_1.ForbiddenError('Instance owner cannot be deleted.');
        }
        const personalProjectToDelete = await this.projectRepository.getPersonalProjectForUserOrFail(userToDelete.id);
        if (transferId === personalProjectToDelete.id) {
            throw new bad_request_error_1.BadRequestError('Request to delete a user failed because the user to delete and the transferee are the same user');
        }
        let transfereeId;
        if (transferId) {
            const transfereeProject = await this.projectRepository.findOneBy({ id: transferId });
            if (!transfereeProject) {
                throw new not_found_error_1.NotFoundError('Request to delete a user failed because the transferee project was not found in DB');
            }
            const transferee = await this.userRepository.findOneByOrFail({
                projectRelations: {
                    projectId: transfereeProject.id,
                },
            });
            transfereeId = transferee.id;
            await this.userService.getManager().transaction(async (trx) => {
                await this.workflowService.transferAll(personalProjectToDelete.id, transfereeProject.id, trx);
                await this.credentialsService.transferAll(personalProjectToDelete.id, transfereeProject.id, trx);
                await this.folderService.transferAllFoldersToProject(personalProjectToDelete.id, transfereeProject.id, trx);
            });
        }
        const [ownedSharedWorkflows, ownedSharedCredentials] = await Promise.all([
            this.sharedWorkflowRepository.find({
                select: { workflowId: true },
                where: { projectId: personalProjectToDelete.id, role: 'workflow:owner' },
            }),
            this.sharedCredentialsRepository.find({
                relations: { credentials: true },
                where: { projectId: personalProjectToDelete.id, role: 'credential:owner' },
            }),
        ]);
        const ownedCredentials = ownedSharedCredentials.map(({ credentials }) => credentials);
        for (const { workflowId } of ownedSharedWorkflows) {
            await this.workflowService.delete(userToDelete, workflowId, true);
        }
        for (const credential of ownedCredentials) {
            await this.credentialsService.delete(userToDelete, credential.id);
        }
        await this.userService.getManager().transaction(async (trx) => {
            await trx.delete(db_1.AuthIdentity, { userId: userToDelete.id });
            await trx.delete(db_1.Project, { id: personalProjectToDelete.id });
            await trx.delete(db_1.User, { id: userToDelete.id });
        });
        this.eventService.emit('user-deleted', {
            user: req.user,
            publicApi: false,
            targetUserOldStatus: userToDelete.isPending ? 'invited' : 'active',
            targetUserId: idToDelete,
            migrationStrategy: transferId ? 'transfer_data' : 'delete_data',
            migrationUserId: transfereeId,
        });
        await this.externalHooks.run('user.deleted', [await this.userService.toPublic(userToDelete)]);
        return { success: true };
    }
    async changeGlobalRole(req, _, payload, id) {
        if (await this.provisioningService.isInstanceRoleManaged()) {
            throw new forbidden_error_1.ForbiddenError('Instance roles are managed automatically and cannot be changed manually');
        }
        const { NO_ADMIN_ON_OWNER, NO_USER, NO_OWNER_ON_OWNER } = UsersController_1.ERROR_MESSAGES.CHANGE_ROLE;
        const targetUser = await this.userRepository.findOne({
            where: { id },
            relations: ['role'],
        });
        if (targetUser === null) {
            throw new not_found_error_1.NotFoundError(NO_USER);
        }
        if (req.user.role.slug === db_1.GLOBAL_ADMIN_ROLE.slug &&
            targetUser.role.slug === db_1.GLOBAL_OWNER_ROLE.slug) {
            throw new forbidden_error_1.ForbiddenError(NO_ADMIN_ON_OWNER);
        }
        if (req.user.role.slug === db_1.GLOBAL_OWNER_ROLE.slug &&
            targetUser.role.slug === db_1.GLOBAL_OWNER_ROLE.slug) {
            throw new forbidden_error_1.ForbiddenError(NO_OWNER_ON_OWNER);
        }
        await this.userService.changeUserRole(targetUser, payload);
        this.eventService.emit('user-changed-role', {
            userId: req.user.id,
            targetUserId: targetUser.id,
            targetUserNewRole: payload.newRoleName,
            publicApi: false,
        });
        return { success: true };
    }
};
exports.UsersController = UsersController;
UsersController.ERROR_MESSAGES = {
    CHANGE_ROLE: {
        NO_USER: 'Target user not found',
        NO_ADMIN_ON_OWNER: 'Admin cannot change role on global owner',
        NO_OWNER_ON_OWNER: 'Owner cannot change role on global owner',
    },
};
__decorate([
    (0, decorators_1.Get)('/'),
    (0, decorators_1.GlobalScope)('user:list'),
    __param(2, decorators_1.Query),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, Object, api_types_1.UsersListFilterDto]),
    __metadata("design:returntype", Promise)
], UsersController.prototype, "listUsers", null);
__decorate([
    (0, decorators_1.Get)('/:id/password-reset-link'),
    (0, decorators_1.GlobalScope)('user:resetPassword'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], UsersController.prototype, "getUserPasswordResetLink", null);
__decorate([
    (0, decorators_1.Post)('/:id/invite-link'),
    (0, decorators_1.GlobalScope)('user:generateInviteLink'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, Object]),
    __metadata("design:returntype", Promise)
], UsersController.prototype, "generateInviteLink", null);
__decorate([
    (0, decorators_1.Patch)('/:id/settings'),
    (0, decorators_1.GlobalScope)('user:update'),
    __param(2, decorators_1.Body),
    __param(3, (0, decorators_1.Param)('id')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, Object, api_types_1.SettingsUpdateRequestDto, String]),
    __metadata("design:returntype", Promise)
], UsersController.prototype, "updateUserSettings", null);
__decorate([
    (0, decorators_1.Delete)('/:id'),
    (0, decorators_1.GlobalScope)('user:delete'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], UsersController.prototype, "deleteUser", null);
__decorate([
    (0, decorators_1.Patch)('/:id/role'),
    (0, decorators_1.GlobalScope)('user:changeRole'),
    (0, decorators_1.Licensed)('feat:advancedPermissions'),
    __param(2, decorators_1.Body),
    __param(3, (0, decorators_1.Param)('id')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, Object, api_types_1.RoleChangeRequestDto, String]),
    __metadata("design:returntype", Promise)
], UsersController.prototype, "changeGlobalRole", null);
exports.UsersController = UsersController = UsersController_1 = __decorate([
    (0, decorators_1.RestController)('/users'),
    __metadata("design:paramtypes", [backend_common_1.Logger,
        external_hooks_1.ExternalHooks,
        db_1.SharedCredentialsRepository,
        db_1.SharedWorkflowRepository,
        db_1.UserRepository,
        auth_service_1.AuthService,
        user_service_1.UserService,
        db_1.ProjectRepository,
        workflow_service_1.WorkflowService,
        credentials_service_1.CredentialsService,
        event_service_1.EventService,
        folder_service_1.FolderService,
        jwt_service_1.JwtService,
        url_service_1.UrlService,
        project_service_ee_1.ProjectService,
        provisioning_service_ee_1.ProvisioningService])
], UsersController);
//# sourceMappingURL=users.controller.js.map