UNPKG

n8n

Version:

n8n Workflow Automation Tool

217 lines 10 kB
"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); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RoleService = void 0; const backend_common_1 = require("@n8n/backend-common"); const db_1 = require("@n8n/db"); const di_1 = require("@n8n/di"); const permissions_1 = require("@n8n/permissions"); const n8n_workflow_1 = require("n8n-workflow"); const bad_request_error_1 = require("../errors/response-errors/bad-request.error"); const not_found_error_1 = require("../errors/response-errors/not-found.error"); const role_cache_service_1 = require("./role-cache.service"); const response_helper_1 = require("../response-helper"); let RoleService = class RoleService { constructor(license, roleRepository, scopeRepository, roleCacheService) { this.license = license; this.roleRepository = roleRepository; this.scopeRepository = scopeRepository; this.roleCacheService = roleCacheService; } dbRoleToRoleDTO(role, usedByUsers) { return { ...role, scopes: role.scopes.map((s) => s.slug), licensed: this.isRoleLicensed(role.slug), usedByUsers, }; } async getAllRoles(withCount = false) { const roles = await this.roleRepository.findAll(); if (!withCount) { return roles.map((r) => this.dbRoleToRoleDTO(r)); } const roleCounts = await this.roleRepository.findAllRoleCounts(); return roles.map((role) => { const usedByUsers = roleCounts[role.slug] ?? 0; return this.dbRoleToRoleDTO(role, usedByUsers); }); } async getRole(slug, withCount = false) { const role = await this.roleRepository.findBySlug(slug); if (role) { const usedByUsers = withCount ? await this.roleRepository.countUsersWithRole(role) : undefined; return this.dbRoleToRoleDTO(role, usedByUsers); } throw new not_found_error_1.NotFoundError('Role not found'); } async removeCustomRole(slug) { const role = await this.roleRepository.findBySlug(slug); if (!role) { throw new not_found_error_1.NotFoundError('Role not found'); } if (role.systemRole) { throw new bad_request_error_1.BadRequestError('Cannot delete system roles'); } const usersWithRole = await this.roleRepository.countUsersWithRole(role); if (usersWithRole > 0) { throw new bad_request_error_1.BadRequestError('Cannot delete role assigned to users'); } await this.roleRepository.removeBySlug(slug); await this.roleCacheService.invalidateCache(); return this.dbRoleToRoleDTO(role); } async resolveScopes(scopeSlugs) { if (!scopeSlugs) { return undefined; } if (scopeSlugs.length === 0) { return []; } const scopes = await this.scopeRepository.findByList(scopeSlugs); if (scopes.length !== scopeSlugs.length) { const invalidScopes = scopeSlugs.filter((slug) => !scopes.some((s) => s.slug === slug)); throw new Error(`The following scopes are invalid: ${invalidScopes.join(', ')}`); } return scopes; } async updateCustomRole(slug, newData) { const { displayName, description, scopes: scopeSlugs } = newData; try { const updatedRole = await this.roleRepository.updateRole(slug, { displayName, description, scopes: await this.resolveScopes(scopeSlugs), }); await this.roleCacheService.invalidateCache(); return this.dbRoleToRoleDTO(updatedRole); } catch (error) { if (error instanceof n8n_workflow_1.UserError && error.message === 'Role not found') { throw new not_found_error_1.NotFoundError('Role not found'); } if (error instanceof n8n_workflow_1.UserError && error.message === 'Cannot update system roles') { throw new bad_request_error_1.BadRequestError('Cannot update system roles'); } if (error instanceof Error && (0, response_helper_1.isUniqueConstraintError)(error)) { throw new bad_request_error_1.BadRequestError(`A role with the name "${displayName}" already exists`); } throw error; } } async createCustomRole(newRole) { const role = new db_1.Role(); role.displayName = newRole.displayName; if (newRole.description) { role.description = newRole.description; } const scopes = await this.resolveScopes(newRole.scopes); if (scopes === undefined) throw new bad_request_error_1.BadRequestError('Scopes are required'); role.scopes = scopes; role.systemRole = false; role.roleType = newRole.roleType; role.slug = `${newRole.roleType}:${newRole.displayName.toLowerCase().replace(/[^a-z0-9]+/g, '-')}-${Math.random().toString(36).substring(2, 8)}`; try { const createdRole = await this.roleRepository.save(role); await this.roleCacheService.invalidateCache(); return this.dbRoleToRoleDTO(createdRole); } catch (error) { if (error instanceof Error && (0, response_helper_1.isUniqueConstraintError)(error)) { throw new bad_request_error_1.BadRequestError(`A role with the name "${newRole.displayName}" already exists`); } throw error; } } async checkRolesExist(roleSlugs, roleType) { const uniqueRoleSlugs = new Set(roleSlugs); const roles = await this.roleRepository.findBySlugs(Array.from(uniqueRoleSlugs), roleType); if (roles.length < uniqueRoleSlugs.size) { const nonExistentRoles = Array.from(uniqueRoleSlugs).filter((slug) => !roles.find((role) => role.slug === slug)); throw new bad_request_error_1.BadRequestError(nonExistentRoles.length === 1 ? `Role ${nonExistentRoles[0]} does not exist` : `Roles ${nonExistentRoles.join(', ')} do not exist`); } } addScopes(rawEntity, user, userProjectRelations) { const shared = rawEntity.shared; const entity = rawEntity; entity.scopes = []; if (shared === undefined) { return entity; } if (!('active' in entity) && !('type' in entity)) { throw new n8n_workflow_1.UnexpectedError('Cannot detect if entity is a workflow or credential.'); } const entityType = 'active' in entity ? 'workflow' : 'credential'; entity.scopes = this.combineResourceScopes(entityType, user, shared, userProjectRelations); if (entityType === 'credential' && 'isGlobal' in entity && entity.isGlobal && !entity.scopes.includes('credential:read')) { entity.scopes.push('credential:read'); } return entity; } combineResourceScopes(type, user, shared, userProjectRelations) { const globalScopes = (0, permissions_1.getAuthPrincipalScopes)(user, [type]); const scopesSet = new Set(globalScopes); for (const sharedEntity of shared) { const pr = userProjectRelations.find((p) => p.projectId === (sharedEntity.projectId ?? sharedEntity.project.id)); let projectScopes = []; if (pr) { projectScopes = pr.role.scopes.map((s) => s.slug); } const resourceMask = (0, permissions_1.getRoleScopes)(sharedEntity.role); const mergedScopes = (0, permissions_1.combineScopes)({ global: globalScopes, project: projectScopes, }, { sharing: resourceMask }); mergedScopes.forEach((s) => scopesSet.add(s)); } return [...scopesSet].sort(); } async rolesWithScope(namespace, scopes) { if (!Array.isArray(scopes)) { scopes = [scopes]; } return await this.roleCacheService.getRolesWithAllScopes(namespace, scopes); } isRoleLicensed(role) { if (!(0, permissions_1.isBuiltInRole)(role)) { return this.license.isCustomRolesLicensed(); } switch (role) { case permissions_1.PROJECT_ADMIN_ROLE_SLUG: return this.license.isProjectRoleAdminLicensed(); case permissions_1.PROJECT_EDITOR_ROLE_SLUG: return this.license.isProjectRoleEditorLicensed(); case permissions_1.PROJECT_VIEWER_ROLE_SLUG: return this.license.isProjectRoleViewerLicensed(); case db_1.GLOBAL_ADMIN_ROLE.slug: return this.license.isAdvancedPermissionsLicensed(); default: return true; } } }; exports.RoleService = RoleService; exports.RoleService = RoleService = __decorate([ (0, di_1.Service)(), __metadata("design:paramtypes", [backend_common_1.LicenseState, db_1.RoleRepository, db_1.ScopeRepository, role_cache_service_1.RoleCacheService]) ], RoleService); //# sourceMappingURL=role.service.js.map