mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
This commit is contained in:
@@ -95,8 +95,6 @@ exports[`Scope Information ensure scopes are defined correctly 1`] = `
|
||||
"workflow:share",
|
||||
"workflow:execute",
|
||||
"workflow:move",
|
||||
"workflow:activate",
|
||||
"workflow:deactivate",
|
||||
"workflow:create",
|
||||
"workflow:read",
|
||||
"workflow:update",
|
||||
@@ -123,14 +121,6 @@ exports[`Scope Information ensure scopes are defined correctly 1`] = `
|
||||
"dataStore:writeRow",
|
||||
"dataStore:listProject",
|
||||
"dataStore:*",
|
||||
"execution:delete",
|
||||
"execution:read",
|
||||
"execution:list",
|
||||
"execution:get",
|
||||
"execution:*",
|
||||
"workflowTags:update",
|
||||
"workflowTags:list",
|
||||
"workflowTags:*",
|
||||
"*",
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -22,13 +22,11 @@ export const RESOURCES = {
|
||||
user: ['resetPassword', 'changeRole', 'enforceMfa', ...DEFAULT_OPERATIONS] as const,
|
||||
variable: [...DEFAULT_OPERATIONS] as const,
|
||||
workersView: ['manage'] as const,
|
||||
workflow: ['share', 'execute', 'move', 'activate', 'deactivate', ...DEFAULT_OPERATIONS] as const,
|
||||
workflow: ['share', 'execute', 'move', ...DEFAULT_OPERATIONS] as const,
|
||||
folder: [...DEFAULT_OPERATIONS, 'move'] as const,
|
||||
insights: ['list'] as const,
|
||||
oidc: ['manage'] as const,
|
||||
dataStore: [...DEFAULT_OPERATIONS, 'readRow', 'writeRow', 'listProject'] as const,
|
||||
execution: ['delete', 'read', 'list', 'get'] as const,
|
||||
workflowTags: ['update', 'list'] as const,
|
||||
} as const;
|
||||
|
||||
export const API_KEY_RESOURCES = {
|
||||
|
||||
@@ -13,7 +13,7 @@ export { hasGlobalScope } from './utilities/has-global-scope.ee';
|
||||
export { combineScopes } from './utilities/combine-scopes.ee';
|
||||
export { rolesWithScope } from './utilities/roles-with-scope.ee';
|
||||
export { getGlobalScopes } from './utilities/get-global-scopes.ee';
|
||||
export { getRoleScopes, getAuthPrincipalScopes } from './utilities/get-role-scopes.ee';
|
||||
export { getRoleScopes } from './utilities/get-role-scopes.ee';
|
||||
export { getResourcePermissions } from './utilities/get-resource-permissions.ee';
|
||||
export type { PermissionsRecord } from './utilities/get-resource-permissions.ee';
|
||||
export * from './public-api-permissions.ee';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { isApiKeyScope, type ApiKeyScope, type AuthPrincipal, type GlobalRole } from './types.ee';
|
||||
import type { ApiKeyScope, GlobalRole } from './types.ee';
|
||||
|
||||
export const OWNER_API_KEY_SCOPES: ApiKeyScope[] = [
|
||||
'user:read',
|
||||
@@ -65,46 +65,14 @@ export const MEMBER_API_KEY_SCOPES: ApiKeyScope[] = [
|
||||
'credential:delete',
|
||||
];
|
||||
|
||||
/**
|
||||
* This is a bit of a mess, because we are handing out scopes in API keys that are only
|
||||
* valid for the personal project, which is enforced in the public API, because the workflows,
|
||||
* execution endpoints are limited to the personal project.
|
||||
* This is a temporary solution until we have a better way to handle personal projects and API key scopes!
|
||||
*/
|
||||
export const API_KEY_SCOPES_FOR_IMPLICIT_PERSONAL_PROJECT: ApiKeyScope[] = [
|
||||
'workflowTags:update',
|
||||
'workflowTags:list',
|
||||
'workflow:create',
|
||||
'workflow:read',
|
||||
'workflow:update',
|
||||
'workflow:delete',
|
||||
'workflow:list',
|
||||
'workflow:move',
|
||||
'workflow:activate',
|
||||
'workflow:deactivate',
|
||||
'execution:delete',
|
||||
'execution:read',
|
||||
'execution:list',
|
||||
'credential:create',
|
||||
'credential:move',
|
||||
'credential:delete',
|
||||
];
|
||||
|
||||
const MAP_ROLE_SCOPES: Record<GlobalRole, ApiKeyScope[]> = {
|
||||
'global:owner': OWNER_API_KEY_SCOPES,
|
||||
'global:admin': ADMIN_API_KEY_SCOPES,
|
||||
'global:member': MEMBER_API_KEY_SCOPES,
|
||||
};
|
||||
|
||||
export const getApiKeyScopesForRole = (user: AuthPrincipal) => {
|
||||
return [
|
||||
...new Set(
|
||||
user.role.scopes
|
||||
.map((scope) => scope.slug)
|
||||
.concat(API_KEY_SCOPES_FOR_IMPLICIT_PERSONAL_PROJECT)
|
||||
.filter(isApiKeyScope),
|
||||
),
|
||||
];
|
||||
export const getApiKeyScopesForRole = (role: GlobalRole) => {
|
||||
return MAP_ROLE_SCOPES[role];
|
||||
};
|
||||
|
||||
export const getOwnerOnlyApiKeyScopes = () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { API_KEY_RESOURCES, RESOURCES } from './constants.ee';
|
||||
import type { ApiKeyScope, Scope, ScopeInformation } from './types.ee';
|
||||
import { RESOURCES } from './constants.ee';
|
||||
import type { Scope, ScopeInformation } from './types.ee';
|
||||
|
||||
function buildResourceScopes() {
|
||||
const resourceScopes = Object.entries(RESOURCES).flatMap(([resource, operations]) => [
|
||||
@@ -11,19 +11,8 @@ function buildResourceScopes() {
|
||||
return resourceScopes;
|
||||
}
|
||||
|
||||
function buildApiKeyScopes() {
|
||||
const apiKeyScopes = Object.entries(API_KEY_RESOURCES).flatMap(([resource, operations]) => [
|
||||
...operations.map((op) => `${resource}:${op}` as const),
|
||||
]) as ApiKeyScope[];
|
||||
|
||||
return new Set(apiKeyScopes);
|
||||
}
|
||||
|
||||
export const ALL_SCOPES = buildResourceScopes();
|
||||
|
||||
// Keep the type of Scope[] to ensure that ApiKeyScopes are a subset of Scopes!
|
||||
export const ALL_API_KEY_SCOPES = buildApiKeyScopes();
|
||||
|
||||
export const scopeInformation: Partial<Record<Scope, ScopeInformation>> = {
|
||||
'annotationTag:create': {
|
||||
displayName: 'Create Annotation Tag',
|
||||
|
||||
@@ -10,7 +10,6 @@ import type {
|
||||
teamRoleSchema,
|
||||
workflowSharingRoleSchema,
|
||||
} from './schemas.ee';
|
||||
import { ALL_API_KEY_SCOPES } from './scope-information';
|
||||
|
||||
export type ScopeInformation = {
|
||||
displayName: string;
|
||||
@@ -77,21 +76,12 @@ export type AllRolesMap = {
|
||||
workflow: Array<RoleObject<WorkflowSharingRole>>;
|
||||
};
|
||||
|
||||
export type DbScope = {
|
||||
slug: Scope;
|
||||
};
|
||||
|
||||
export type DbRole = {
|
||||
slug: string;
|
||||
scopes: DbScope[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents an authenticated entity in the system that can have specific permissions via a role.
|
||||
* @property role - The global role this principal has
|
||||
*/
|
||||
export type AuthPrincipal = {
|
||||
role: DbRole;
|
||||
role: GlobalRole;
|
||||
};
|
||||
|
||||
// #region Public API
|
||||
@@ -111,9 +101,4 @@ type AllApiKeyScopesObject = {
|
||||
|
||||
export type ApiKeyScope = AllApiKeyScopesObject[PublicApiKeyResources];
|
||||
|
||||
export function isApiKeyScope(scope: Scope): scope is ApiKeyScope {
|
||||
// We are casting with as for runtime type checking
|
||||
return ALL_API_KEY_SCOPES.has(scope as ApiKeyScope);
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { GLOBAL_SCOPE_MAP } from '../../roles/role-maps.ee';
|
||||
import type { GlobalRole } from '../../types.ee';
|
||||
import { getGlobalScopes } from '../get-global-scopes.ee';
|
||||
import { createAuthPrinicipal } from './utils';
|
||||
|
||||
describe('getGlobalScopes', () => {
|
||||
test.each(['global:owner', 'global:admin', 'global:member'] as const)(
|
||||
'should return correct scopes for %s',
|
||||
(role) => {
|
||||
const scopes = getGlobalScopes(createAuthPrinicipal(role));
|
||||
const scopes = getGlobalScopes({ role });
|
||||
|
||||
expect(scopes).toEqual(GLOBAL_SCOPE_MAP[role]);
|
||||
},
|
||||
);
|
||||
|
||||
test('should return empty array for non-existent role', () => {
|
||||
const scopes = getGlobalScopes(createAuthPrinicipal('non:existent'));
|
||||
const scopes = getGlobalScopes({ role: 'non:existent' as GlobalRole });
|
||||
|
||||
expect(scopes).toEqual([]);
|
||||
});
|
||||
|
||||
@@ -15,7 +15,6 @@ describe('permissions', () => {
|
||||
externalSecretsProvider: {},
|
||||
externalSecret: {},
|
||||
eventBusDestination: {},
|
||||
execution: {},
|
||||
ldap: {},
|
||||
license: {},
|
||||
logStreaming: {},
|
||||
@@ -30,7 +29,6 @@ describe('permissions', () => {
|
||||
variable: {},
|
||||
workersView: {},
|
||||
workflow: {},
|
||||
workflowTags: {},
|
||||
folder: {},
|
||||
insights: {},
|
||||
dataStore: {},
|
||||
@@ -132,8 +130,6 @@ describe('permissions', () => {
|
||||
list: true,
|
||||
},
|
||||
dataStore: {},
|
||||
execution: {},
|
||||
workflowTags: {},
|
||||
};
|
||||
|
||||
expect(getResourcePermissions(scopes)).toEqual(permissionRecord);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { GlobalRole, Scope } from '../../types.ee';
|
||||
import { hasGlobalScope } from '../has-global-scope.ee';
|
||||
import { createAuthPrinicipal } from './utils';
|
||||
|
||||
describe('hasGlobalScope', () => {
|
||||
describe('single scope checks', () => {
|
||||
@@ -12,7 +11,7 @@ describe('hasGlobalScope', () => {
|
||||
] as Array<{ role: GlobalRole; scope: Scope; expected: boolean }>)(
|
||||
'$role with $scope -> $expected',
|
||||
({ role, scope, expected }) => {
|
||||
expect(hasGlobalScope(createAuthPrinicipal(role), scope)).toBe(expected);
|
||||
expect(hasGlobalScope({ role }, scope)).toBe(expected);
|
||||
},
|
||||
);
|
||||
});
|
||||
@@ -20,7 +19,7 @@ describe('hasGlobalScope', () => {
|
||||
describe('multiple scopes', () => {
|
||||
test('oneOf mode (default)', () => {
|
||||
expect(
|
||||
hasGlobalScope(createAuthPrinicipal('global:member'), [
|
||||
hasGlobalScope({ role: 'global:member' }, [
|
||||
'tag:create',
|
||||
'user:list',
|
||||
// a member cannot create users
|
||||
@@ -32,7 +31,7 @@ describe('hasGlobalScope', () => {
|
||||
test('allOf mode', () => {
|
||||
expect(
|
||||
hasGlobalScope(
|
||||
createAuthPrinicipal('global:member'),
|
||||
{ role: 'global:member' },
|
||||
[
|
||||
'tag:create',
|
||||
'user:list',
|
||||
@@ -46,6 +45,6 @@ describe('hasGlobalScope', () => {
|
||||
});
|
||||
|
||||
test('edge cases', () => {
|
||||
expect(hasGlobalScope(createAuthPrinicipal('global:owner'), [])).toBe(false);
|
||||
expect(hasGlobalScope({ role: 'global:owner' }, [])).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import { GLOBAL_SCOPE_MAP } from '@/roles/role-maps.ee';
|
||||
import { globalRoleSchema } from '@/schemas.ee';
|
||||
import type { AuthPrincipal, GlobalRole, Scope } from '@/types.ee';
|
||||
|
||||
function createBuildInAuthPrinicipal(role: GlobalRole): AuthPrincipal {
|
||||
return {
|
||||
role: {
|
||||
slug: role,
|
||||
scopes:
|
||||
GLOBAL_SCOPE_MAP[role].map((scope) => {
|
||||
return {
|
||||
slug: scope,
|
||||
};
|
||||
}) || [],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createAuthPrinicipal(role: string, scopes: Scope[] = []): AuthPrincipal {
|
||||
try {
|
||||
const isGlobalRole = globalRoleSchema.parse(role);
|
||||
if (isGlobalRole) {
|
||||
return createBuildInAuthPrinicipal(isGlobalRole);
|
||||
}
|
||||
} catch (error) {
|
||||
// If the role is not a valid global role, we proceed
|
||||
// to create a custom role with the provided scopes.
|
||||
}
|
||||
return {
|
||||
role: {
|
||||
slug: role,
|
||||
scopes:
|
||||
scopes.map((scope) => {
|
||||
return {
|
||||
slug: scope,
|
||||
};
|
||||
}) || [],
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { GLOBAL_SCOPE_MAP } from '../roles/role-maps.ee';
|
||||
import type { AuthPrincipal } from '../types.ee';
|
||||
|
||||
/**
|
||||
@@ -5,5 +6,4 @@ import type { AuthPrincipal } from '../types.ee';
|
||||
* @param principal - Contains the role to look up
|
||||
* @returns Array of scopes for the role, or empty array if not found
|
||||
*/
|
||||
export const getGlobalScopes = (principal: AuthPrincipal) =>
|
||||
principal.role.scopes.map((scope) => scope.slug) ?? [];
|
||||
export const getGlobalScopes = (principal: AuthPrincipal) => GLOBAL_SCOPE_MAP[principal.role] ?? [];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ALL_ROLE_MAPS } from '../roles/role-maps.ee';
|
||||
import type { AllRoleTypes, AuthPrincipal, Resource, Scope } from '../types.ee';
|
||||
import type { AllRoleTypes, Resource, Scope } from '../types.ee';
|
||||
|
||||
export const COMBINED_ROLE_MAP = Object.fromEntries(
|
||||
Object.values(ALL_ROLE_MAPS).flatMap((o: Record<string, Scope[]>) => Object.entries(o)),
|
||||
@@ -10,8 +10,6 @@ export const COMBINED_ROLE_MAP = Object.fromEntries(
|
||||
* @param role - The role to look up
|
||||
* @param filters - Optional resources to filter scopes by
|
||||
* @returns Array of matching scopes
|
||||
*
|
||||
* @deprecated Use the 'getRoleScopes' from the AuthRolesService instead.
|
||||
*/
|
||||
export function getRoleScopes(role: AllRoleTypes, filters?: Resource[]): Scope[] {
|
||||
let scopes = COMBINED_ROLE_MAP[role];
|
||||
@@ -20,22 +18,3 @@ export function getRoleScopes(role: AllRoleTypes, filters?: Resource[]): Scope[]
|
||||
}
|
||||
return scopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets scopes for an auth principal, optionally filtered by resource types.
|
||||
* @param user - The auth principal to search scopes for
|
||||
* @param filters - Optional resources to filter scopes by
|
||||
* @returns Array of matching scopes
|
||||
*/
|
||||
export function getAuthPrincipalScopes(user: AuthPrincipal, filters?: Resource[]): Scope[] {
|
||||
if (!user.role) {
|
||||
const e = new Error('AuthPrincipal does not have a role defined');
|
||||
console.error('AuthPrincipal does not have a role defined', e);
|
||||
throw e;
|
||||
}
|
||||
let scopes = user.role.scopes.map((s) => s.slug);
|
||||
if (filters) {
|
||||
scopes = scopes.filter((s) => filters.includes(s.split(':')[0] as Resource));
|
||||
}
|
||||
return scopes;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getGlobalScopes } from './get-global-scopes.ee';
|
||||
import { hasScope } from './has-scope.ee';
|
||||
import type { AuthPrincipal, Scope, ScopeOptions } from '../types.ee';
|
||||
import { getAuthPrincipalScopes } from './get-role-scopes.ee';
|
||||
|
||||
/**
|
||||
* Checks if an auth-principal has specified global scope(s).
|
||||
@@ -12,6 +12,6 @@ export const hasGlobalScope = (
|
||||
scope: Scope | Scope[],
|
||||
scopeOptions?: ScopeOptions,
|
||||
): boolean => {
|
||||
const global = getAuthPrincipalScopes(principal);
|
||||
const global = getGlobalScopes(principal);
|
||||
return hasScope(scope, { global }, undefined, scopeOptions);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user