n8n
Version:
n8n Workflow Automation Tool
217 lines • 10 kB
JavaScript
;
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