mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-09 22:24:05 -08:00
feat(editor): Replace middleware for Role checks with Scope checks (#7847)
This commit is contained in:
parent
d4970410e1
commit
72852a60eb
|
@ -63,13 +63,23 @@ describe('User Management', { disableAutoLogin: true }, () => {
|
|||
personalSettingsPage.actions.changeTheme('Dark');
|
||||
cy.get('body').should('have.attr', 'data-theme', 'dark');
|
||||
settingsSidebar.actions.back();
|
||||
mainSidebar.getters.logo().should('have.attr', 'src', '/n8n-dev-logo-dark-mode.svg');
|
||||
mainSidebar.getters
|
||||
.logo()
|
||||
.should('have.attr', 'src')
|
||||
.then((src) => {
|
||||
expect(src).to.include('/n8n-dev-logo-dark-mode.svg');
|
||||
});
|
||||
|
||||
cy.visit(personalSettingsPage.url);
|
||||
personalSettingsPage.actions.changeTheme('Light');
|
||||
cy.get('body').should('have.attr', 'data-theme', 'light');
|
||||
settingsSidebar.actions.back();
|
||||
mainSidebar.getters.logo().should('have.attr', 'src', '/n8n-dev-logo.svg');
|
||||
mainSidebar.getters
|
||||
.logo()
|
||||
.should('have.attr', 'src')
|
||||
.then((src) => {
|
||||
expect(src).to.include('/n8n-dev-logo.svg');
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete user and their data', () => {
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
export type DefaultOperations = 'create' | 'read' | 'update' | 'delete' | 'list';
|
||||
export type Resource =
|
||||
| 'workflow'
|
||||
| 'tag'
|
||||
| 'user'
|
||||
| 'auditLogs'
|
||||
| 'communityPackage'
|
||||
| 'credential'
|
||||
| 'variable'
|
||||
| 'sourceControl'
|
||||
| 'externalSecretsProvider'
|
||||
| 'externalSecret'
|
||||
| 'eventBusEvent'
|
||||
| 'eventBusDestination'
|
||||
| 'orchestration'
|
||||
| 'communityPackage'
|
||||
| 'ldap'
|
||||
| 'saml';
|
||||
| 'logStreaming'
|
||||
| 'orchestration'
|
||||
| 'sourceControl'
|
||||
| 'saml'
|
||||
| 'tag'
|
||||
| 'user'
|
||||
| 'variable'
|
||||
| 'workflow';
|
||||
|
||||
export type ResourceScope<
|
||||
R extends Resource,
|
||||
|
@ -22,45 +24,49 @@ export type ResourceScope<
|
|||
|
||||
export type WildcardScope = `${Resource}:*` | '*';
|
||||
|
||||
export type WorkflowScope = ResourceScope<'workflow', DefaultOperations | 'share'>;
|
||||
export type TagScope = ResourceScope<'tag'>;
|
||||
export type UserScope = ResourceScope<'user', DefaultOperations | 'resetPassword'>;
|
||||
export type AuditLogsScope = ResourceScope<'auditLogs', 'manage'>;
|
||||
export type CommunityPackageScope = ResourceScope<
|
||||
'communityPackage',
|
||||
'install' | 'uninstall' | 'update' | 'list' | 'manage'
|
||||
>;
|
||||
export type CredentialScope = ResourceScope<'credential', DefaultOperations | 'share'>;
|
||||
export type VariableScope = ResourceScope<'variable'>;
|
||||
export type SourceControlScope = ResourceScope<'sourceControl', 'pull' | 'push' | 'manage'>;
|
||||
export type ExternalSecretScope = ResourceScope<'externalSecret', 'list'>;
|
||||
export type ExternalSecretProviderScope = ResourceScope<
|
||||
'externalSecretsProvider',
|
||||
DefaultOperations | 'sync'
|
||||
>;
|
||||
export type ExternalSecretScope = ResourceScope<'externalSecret', 'list'>;
|
||||
export type EventBusEventScope = ResourceScope<'eventBusEvent', DefaultOperations | 'query'>;
|
||||
export type EventBusDestinationScope = ResourceScope<
|
||||
'eventBusDestination',
|
||||
DefaultOperations | 'test'
|
||||
>;
|
||||
export type OrchestrationScope = ResourceScope<'orchestration', 'read' | 'list'>;
|
||||
export type CommunityPackageScope = ResourceScope<
|
||||
'communityPackage',
|
||||
'install' | 'uninstall' | 'update' | 'list'
|
||||
>;
|
||||
export type EventBusEventScope = ResourceScope<'eventBusEvent', DefaultOperations | 'query'>;
|
||||
export type LdapScope = ResourceScope<'ldap', 'manage' | 'sync'>;
|
||||
export type LogStreamingScope = ResourceScope<'logStreaming', 'manage'>;
|
||||
export type OrchestrationScope = ResourceScope<'orchestration', 'read' | 'list'>;
|
||||
export type SamlScope = ResourceScope<'saml', 'manage'>;
|
||||
export type SourceControlScope = ResourceScope<'sourceControl', 'pull' | 'push' | 'manage'>;
|
||||
export type TagScope = ResourceScope<'tag'>;
|
||||
export type UserScope = ResourceScope<'user', DefaultOperations | 'resetPassword'>;
|
||||
export type VariableScope = ResourceScope<'variable'>;
|
||||
export type WorkflowScope = ResourceScope<'workflow', DefaultOperations | 'share'>;
|
||||
|
||||
export type Scope =
|
||||
| WorkflowScope
|
||||
| TagScope
|
||||
| UserScope
|
||||
| AuditLogsScope
|
||||
| CommunityPackageScope
|
||||
| CredentialScope
|
||||
| VariableScope
|
||||
| SourceControlScope
|
||||
| ExternalSecretProviderScope
|
||||
| ExternalSecretScope
|
||||
| EventBusEventScope
|
||||
| EventBusDestinationScope
|
||||
| OrchestrationScope
|
||||
| CommunityPackageScope
|
||||
| LdapScope
|
||||
| SamlScope;
|
||||
| LogStreamingScope
|
||||
| OrchestrationScope
|
||||
| SamlScope
|
||||
| SourceControlScope
|
||||
| TagScope
|
||||
| UserScope
|
||||
| VariableScope
|
||||
| WorkflowScope;
|
||||
|
||||
export type ScopeLevel = 'global' | 'project' | 'resource';
|
||||
export type GetScopeLevel<T extends ScopeLevel> = Record<T, Scope[]>;
|
||||
|
|
|
@ -1,53 +1,17 @@
|
|||
import type { Scope } from '@n8n/permissions';
|
||||
|
||||
export const ownerPermissions: Scope[] = [
|
||||
'workflow:create',
|
||||
'workflow:read',
|
||||
'workflow:update',
|
||||
'workflow:delete',
|
||||
'workflow:list',
|
||||
'workflow:share',
|
||||
'tag:create',
|
||||
'tag:read',
|
||||
'tag:update',
|
||||
'tag:delete',
|
||||
'tag:list',
|
||||
'user:create',
|
||||
'user:read',
|
||||
'user:update',
|
||||
'user:delete',
|
||||
'user:list',
|
||||
'user:resetPassword',
|
||||
'auditLogs:manage',
|
||||
'credential:create',
|
||||
'credential:read',
|
||||
'credential:update',
|
||||
'credential:delete',
|
||||
'credential:list',
|
||||
'credential:share',
|
||||
'variable:create',
|
||||
'variable:read',
|
||||
'variable:update',
|
||||
'variable:delete',
|
||||
'variable:list',
|
||||
'sourceControl:pull',
|
||||
'sourceControl:push',
|
||||
'sourceControl:manage',
|
||||
'externalSecretsProvider:create',
|
||||
'externalSecretsProvider:read',
|
||||
'externalSecretsProvider:update',
|
||||
'externalSecretsProvider:delete',
|
||||
'externalSecretsProvider:list',
|
||||
'externalSecretsProvider:sync',
|
||||
'externalSecret:list',
|
||||
'orchestration:read',
|
||||
'orchestration:list',
|
||||
'communityPackage:install',
|
||||
'communityPackage:uninstall',
|
||||
'communityPackage:update',
|
||||
'communityPackage:list',
|
||||
'ldap:manage',
|
||||
'ldap:sync',
|
||||
'saml:manage',
|
||||
'eventBusEvent:create',
|
||||
'eventBusEvent:read',
|
||||
'eventBusEvent:update',
|
||||
|
@ -61,18 +25,56 @@ export const ownerPermissions: Scope[] = [
|
|||
'eventBusDestination:delete',
|
||||
'eventBusDestination:list',
|
||||
'eventBusDestination:test',
|
||||
];
|
||||
export const adminPermissions: Scope[] = ownerPermissions.concat();
|
||||
export const memberPermissions: Scope[] = [
|
||||
'user:list',
|
||||
'variable:list',
|
||||
'variable:read',
|
||||
'externalSecretsProvider:create',
|
||||
'externalSecretsProvider:read',
|
||||
'externalSecretsProvider:update',
|
||||
'externalSecretsProvider:delete',
|
||||
'externalSecretsProvider:list',
|
||||
'externalSecretsProvider:sync',
|
||||
'externalSecret:list',
|
||||
'ldap:manage',
|
||||
'ldap:sync',
|
||||
'logStreaming:manage',
|
||||
'orchestration:read',
|
||||
'orchestration:list',
|
||||
'saml:manage',
|
||||
'sourceControl:pull',
|
||||
'sourceControl:push',
|
||||
'sourceControl:manage',
|
||||
'tag:create',
|
||||
'tag:read',
|
||||
'tag:update',
|
||||
'tag:delete',
|
||||
'tag:list',
|
||||
'user:create',
|
||||
'user:read',
|
||||
'user:update',
|
||||
'user:delete',
|
||||
'user:list',
|
||||
'user:resetPassword',
|
||||
'variable:create',
|
||||
'variable:read',
|
||||
'variable:update',
|
||||
'variable:delete',
|
||||
'variable:list',
|
||||
'workflow:create',
|
||||
'workflow:read',
|
||||
'workflow:update',
|
||||
'workflow:delete',
|
||||
'workflow:list',
|
||||
'workflow:share',
|
||||
];
|
||||
export const adminPermissions: Scope[] = ownerPermissions.concat();
|
||||
export const memberPermissions: Scope[] = [
|
||||
'eventBusEvent:list',
|
||||
'eventBusEvent:read',
|
||||
'eventBusDestination:list',
|
||||
'eventBusDestination:test',
|
||||
'tag:create',
|
||||
'tag:read',
|
||||
'tag:update',
|
||||
'tag:list',
|
||||
'user:list',
|
||||
'variable:list',
|
||||
'variable:read',
|
||||
];
|
||||
|
|
|
@ -120,7 +120,6 @@ import { useWorkflowsStore } from '@/stores/workflows.store';
|
|||
import { isNavigationFailure } from 'vue-router';
|
||||
import ExecutionsUsage from '@/components/ExecutionsUsage.vue';
|
||||
import MainSidebarSourceControl from '@/components/MainSidebarSourceControl.vue';
|
||||
import { ROLE } from '@/utils/userUtils';
|
||||
import { hasPermission } from '@/rbac/permissions';
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -177,9 +176,7 @@ export default defineComponent({
|
|||
return accessibleRoute !== null;
|
||||
},
|
||||
showUserArea(): boolean {
|
||||
return hasPermission(['role'], {
|
||||
role: [ROLE.Member, ROLE.Owner],
|
||||
});
|
||||
return hasPermission(['authenticated']);
|
||||
},
|
||||
workflowExecution(): IExecutionResponse | null {
|
||||
return this.workflowsStore.getWorkflowExecution;
|
||||
|
|
|
@ -5,6 +5,7 @@ vi.mock('@/rbac/checks', () => ({
|
|||
hasRole: vi.fn(),
|
||||
hasScope: vi.fn(),
|
||||
isGuest: vi.fn(),
|
||||
isDefaultUser: vi.fn(),
|
||||
isAuthenticated: vi.fn(),
|
||||
isEnterpriseFeatureEnabled: vi.fn(),
|
||||
isValid: vi.fn(),
|
||||
|
@ -15,13 +16,22 @@ describe('hasPermission()', () => {
|
|||
vi.mocked(checks.hasRole).mockReturnValue(true);
|
||||
vi.mocked(checks.hasScope).mockReturnValue(true);
|
||||
vi.mocked(checks.isGuest).mockReturnValue(true);
|
||||
vi.mocked(checks.isDefaultUser).mockReturnValue(true);
|
||||
vi.mocked(checks.isAuthenticated).mockReturnValue(true);
|
||||
vi.mocked(checks.isEnterpriseFeatureEnabled).mockReturnValue(true);
|
||||
vi.mocked(checks.isValid).mockReturnValue(true);
|
||||
|
||||
expect(hasPermission(['authenticated', 'custom', 'enterprise', 'guest', 'rbac', 'role'])).toBe(
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
hasPermission([
|
||||
'authenticated',
|
||||
'custom',
|
||||
'enterprise',
|
||||
'guest',
|
||||
'rbac',
|
||||
'role',
|
||||
'defaultUser',
|
||||
]),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if any permission is invalid', () => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useRBACStore } from '@/stores/rbac.store';
|
||||
import { hasScope } from '@/rbac/checks/hasScope';
|
||||
import type { HasScopeOptions } from '@n8n/permissions';
|
||||
import type { ScopeOptions } from '@n8n/permissions';
|
||||
|
||||
vi.mock('@/stores/rbac.store', () => ({
|
||||
useRBACStore: vi.fn(),
|
||||
|
@ -19,7 +19,7 @@ describe('Checks', () => {
|
|||
} as unknown as ReturnType<typeof useRBACStore>);
|
||||
|
||||
const scope = 'workflow:read';
|
||||
const options: HasScopeOptions = { mode: 'allOf' };
|
||||
const options: ScopeOptions = { mode: 'allOf' };
|
||||
const projectId = 'proj123';
|
||||
const resourceType = 'workflow';
|
||||
const resourceId = 'res123';
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import { useUsersStore } from '@/stores/users.store';
|
||||
import { isDefaultUser } from '@/rbac/checks/isDefaultUser';
|
||||
|
||||
vi.mock('@/stores/users.store', () => ({
|
||||
useUsersStore: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('Checks', () => {
|
||||
describe('isDefaultUser()', () => {
|
||||
it('should return false if user not logged in', () => {
|
||||
vi.mocked(useUsersStore).mockReturnValue({ currentUser: null } as ReturnType<
|
||||
typeof useUsersStore
|
||||
>);
|
||||
|
||||
expect(isDefaultUser()).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true if user is default user', () => {
|
||||
const mockUser = { id: 'user123', name: 'Test User', isDefaultUser: true };
|
||||
vi.mocked(useUsersStore).mockReturnValue({ currentUser: mockUser } as unknown as ReturnType<
|
||||
typeof useUsersStore
|
||||
>);
|
||||
|
||||
expect(isDefaultUser()).toBe(mockUser.isDefaultUser);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,6 +1,7 @@
|
|||
export * from './hasRole';
|
||||
export * from './hasScope';
|
||||
export * from './isAuthenticated';
|
||||
export * from './isDefaultUser';
|
||||
export * from './isEnterpriseFeatureEnabled';
|
||||
export * from './isGuest';
|
||||
export * from './isValid';
|
||||
|
|
12
packages/editor-ui/src/rbac/checks/isDefaultUser.ts
Normal file
12
packages/editor-ui/src/rbac/checks/isDefaultUser.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { useUsersStore } from '@/stores/users.store';
|
||||
import type { DefaultUserMiddlewareOptions, RBACPermissionCheck } from '@/types/rbac';
|
||||
|
||||
export const isDefaultUser: RBACPermissionCheck<DefaultUserMiddlewareOptions> = () => {
|
||||
const usersStore = useUsersStore();
|
||||
const currentUser = usersStore.currentUser;
|
||||
|
||||
if (currentUser) {
|
||||
return currentUser.isDefaultUser;
|
||||
}
|
||||
return false;
|
||||
};
|
|
@ -5,6 +5,7 @@ import { guestMiddleware } from '@/rbac/middleware/guest';
|
|||
import { rbacMiddleware } from '@/rbac/middleware/rbac';
|
||||
import { roleMiddleware } from '@/rbac/middleware/role';
|
||||
import { customMiddleware } from '@/rbac/middleware/custom';
|
||||
import { defaultUserMiddleware } from '@/rbac/middleware/defaultUser';
|
||||
|
||||
type Middleware = {
|
||||
[key in RouterMiddlewareType]: RouterMiddleware<MiddlewareOptions[key]>;
|
||||
|
@ -13,6 +14,7 @@ type Middleware = {
|
|||
export const middleware: Middleware = {
|
||||
authenticated: authenticatedMiddleware,
|
||||
custom: customMiddleware,
|
||||
defaultUser: defaultUserMiddleware,
|
||||
enterprise: enterpriseMiddleware,
|
||||
guest: guestMiddleware,
|
||||
rbac: rbacMiddleware,
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
import { useUsersStore } from '@/stores/users.store';
|
||||
import { VIEWS } from '@/constants';
|
||||
import { defaultUserMiddleware } from '@/rbac/middleware/defaultUser';
|
||||
import type { RouteLocationNormalized } from 'vue-router';
|
||||
|
||||
vi.mock('@/stores/users.store', () => ({
|
||||
useUsersStore: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('Middleware', () => {
|
||||
describe('defaultUser', () => {
|
||||
it('should redirect to homepage if user not logged in', async () => {
|
||||
vi.mocked(useUsersStore).mockReturnValue({
|
||||
currentUser: null,
|
||||
} as ReturnType<typeof useUsersStore>);
|
||||
|
||||
const nextMock = vi.fn();
|
||||
const toMock = { query: {} } as RouteLocationNormalized;
|
||||
const fromMock = {} as RouteLocationNormalized;
|
||||
|
||||
await defaultUserMiddleware(toMock, fromMock, nextMock, {});
|
||||
|
||||
expect(nextMock).toHaveBeenCalledWith({ name: VIEWS.HOMEPAGE });
|
||||
});
|
||||
|
||||
it('should redirect to homepage if user is not default user', async () => {
|
||||
vi.mocked(useUsersStore).mockReturnValue({
|
||||
currentUser: { id: '123', isDefaultUser: false },
|
||||
} as ReturnType<typeof useUsersStore>);
|
||||
|
||||
const nextMock = vi.fn();
|
||||
const toMock = { query: {} } as RouteLocationNormalized;
|
||||
const fromMock = {} as RouteLocationNormalized;
|
||||
|
||||
await defaultUserMiddleware(toMock, fromMock, nextMock, {});
|
||||
|
||||
expect(nextMock).toHaveBeenCalledWith({ name: VIEWS.HOMEPAGE });
|
||||
});
|
||||
|
||||
it('should allow navigation if a current user is present', async () => {
|
||||
vi.mocked(useUsersStore).mockReturnValue({
|
||||
currentUser: { id: '123', isDefaultUser: true },
|
||||
} as ReturnType<typeof useUsersStore>);
|
||||
|
||||
const nextMock = vi.fn();
|
||||
const toMock = { query: {} } as RouteLocationNormalized;
|
||||
const fromMock = {} as RouteLocationNormalized;
|
||||
|
||||
await defaultUserMiddleware(toMock, fromMock, nextMock, {});
|
||||
|
||||
expect(nextMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
15
packages/editor-ui/src/rbac/middleware/defaultUser.ts
Normal file
15
packages/editor-ui/src/rbac/middleware/defaultUser.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import type { RouterMiddleware } from '@/types/router';
|
||||
import { VIEWS } from '@/constants';
|
||||
import type { DefaultUserMiddlewareOptions } from '@/types/rbac';
|
||||
import { isDefaultUser } from '@/rbac/checks';
|
||||
|
||||
export const defaultUserMiddleware: RouterMiddleware<DefaultUserMiddlewareOptions> = async (
|
||||
to,
|
||||
from,
|
||||
next,
|
||||
) => {
|
||||
const valid = isDefaultUser();
|
||||
if (!valid) {
|
||||
return next({ name: VIEWS.HOMEPAGE });
|
||||
}
|
||||
};
|
|
@ -2,6 +2,7 @@ import {
|
|||
hasRole,
|
||||
hasScope,
|
||||
isAuthenticated,
|
||||
isDefaultUser,
|
||||
isEnterpriseFeatureEnabled,
|
||||
isGuest,
|
||||
isValid,
|
||||
|
@ -15,6 +16,7 @@ type Permissions = {
|
|||
export const permissions: Permissions = {
|
||||
authenticated: isAuthenticated,
|
||||
custom: isValid,
|
||||
defaultUser: isDefaultUser,
|
||||
enterprise: isEnterpriseFeatureEnabled,
|
||||
guest: isGuest,
|
||||
rbac: hasScope,
|
||||
|
|
|
@ -9,7 +9,6 @@ import type {
|
|||
} from 'vue-router';
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
import { runExternalHook } from '@/utils/externalHooks';
|
||||
import { ROLE } from '@/utils/userUtils';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { useTemplatesStore } from '@/stores/templates.store';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
|
@ -406,10 +405,7 @@ export const routes = [
|
|||
default: SetupView,
|
||||
},
|
||||
meta: {
|
||||
middleware: ['role'],
|
||||
middlewareOptions: {
|
||||
role: [ROLE.Default],
|
||||
},
|
||||
middleware: ['defaultUser'],
|
||||
telemetry: {
|
||||
pageCategory: 'auth',
|
||||
},
|
||||
|
@ -480,10 +476,7 @@ export const routes = [
|
|||
settingsView: SettingsPersonalView,
|
||||
},
|
||||
meta: {
|
||||
middleware: ['authenticated', 'role'],
|
||||
middlewareOptions: {
|
||||
role: [ROLE.Owner, ROLE.Member],
|
||||
},
|
||||
middleware: ['authenticated'],
|
||||
telemetry: {
|
||||
pageCategory: 'settings',
|
||||
getProperties(route: RouteLocation) {
|
||||
|
@ -548,9 +541,11 @@ export const routes = [
|
|||
settingsView: SettingsSourceControl,
|
||||
},
|
||||
meta: {
|
||||
middleware: ['authenticated', 'role'],
|
||||
middleware: ['authenticated', 'rbac'],
|
||||
middlewareOptions: {
|
||||
role: [ROLE.Owner],
|
||||
rbac: {
|
||||
scope: 'sourceControl:manage',
|
||||
},
|
||||
},
|
||||
telemetry: {
|
||||
pageCategory: 'settings',
|
||||
|
@ -569,9 +564,11 @@ export const routes = [
|
|||
settingsView: SettingsExternalSecrets,
|
||||
},
|
||||
meta: {
|
||||
middleware: ['authenticated', 'role'],
|
||||
middleware: ['authenticated', 'rbac'],
|
||||
middlewareOptions: {
|
||||
role: [ROLE.Owner],
|
||||
rbac: {
|
||||
scope: ['externalSecretsProvider:list', 'externalSecretsProvider:update'],
|
||||
},
|
||||
},
|
||||
telemetry: {
|
||||
pageCategory: 'settings',
|
||||
|
@ -590,13 +587,15 @@ export const routes = [
|
|||
settingsView: SettingsSso,
|
||||
},
|
||||
meta: {
|
||||
middleware: ['authenticated', 'role', 'custom'],
|
||||
middleware: ['authenticated', 'rbac', 'custom'],
|
||||
middlewareOptions: {
|
||||
custom: () => {
|
||||
const settingsStore = useSettingsStore();
|
||||
return !settingsStore.isDesktopDeployment;
|
||||
},
|
||||
role: [ROLE.Owner],
|
||||
rbac: {
|
||||
scope: 'saml:manage',
|
||||
},
|
||||
},
|
||||
telemetry: {
|
||||
pageCategory: 'settings',
|
||||
|
@ -615,9 +614,11 @@ export const routes = [
|
|||
settingsView: SettingsLogStreamingView,
|
||||
},
|
||||
meta: {
|
||||
middleware: ['authenticated', 'role'],
|
||||
middleware: ['authenticated', 'rbac'],
|
||||
middlewareOptions: {
|
||||
role: [ROLE.Owner],
|
||||
rbac: {
|
||||
scope: 'logStreaming:manage',
|
||||
},
|
||||
},
|
||||
telemetry: {
|
||||
pageCategory: 'settings',
|
||||
|
@ -641,9 +642,11 @@ export const routes = [
|
|||
settingsView: SettingsCommunityNodesView,
|
||||
},
|
||||
meta: {
|
||||
middleware: ['authenticated', 'role', 'custom'],
|
||||
middleware: ['authenticated', 'rbac', 'custom'],
|
||||
middlewareOptions: {
|
||||
role: [ROLE.Owner],
|
||||
rbac: {
|
||||
scope: ['communityPackage:list', 'communityPackage:update'],
|
||||
},
|
||||
custom: () => {
|
||||
const settingsStore = useSettingsStore();
|
||||
return settingsStore.isCommunityNodesFeatureEnabled;
|
||||
|
@ -679,9 +682,11 @@ export const routes = [
|
|||
settingsView: SettingsLdapView,
|
||||
},
|
||||
meta: {
|
||||
middleware: ['authenticated', 'role'],
|
||||
middleware: ['authenticated', 'rbac'],
|
||||
middlewareOptions: {
|
||||
role: [ROLE.Owner],
|
||||
rbac: {
|
||||
scope: 'ldap:manage',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -692,12 +697,14 @@ export const routes = [
|
|||
settingsView: SettingsAuditLogs,
|
||||
},
|
||||
meta: {
|
||||
middleware: ['authenticated', 'role', 'custom'],
|
||||
middleware: ['authenticated', 'rbac', 'custom'],
|
||||
middlewareOptions: {
|
||||
custom: () => {
|
||||
return !!useStorage('audit-logs').value;
|
||||
},
|
||||
role: [ROLE.Owner],
|
||||
rbac: {
|
||||
scope: 'auditLogs:manage',
|
||||
},
|
||||
},
|
||||
telemetry: {
|
||||
pageCategory: 'settings',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { hasScope as genericHasScope } from '@n8n/permissions';
|
||||
import type { HasScopeOptions, Scope, Resource } from '@n8n/permissions';
|
||||
import type { ScopeOptions, Scope, Resource } from '@n8n/permissions';
|
||||
import { ref } from 'vue';
|
||||
import { STORES } from '@/constants';
|
||||
import type { IRole } from '@/Interface';
|
||||
|
@ -80,7 +80,7 @@ export const useRBACStore = defineStore(STORES.RBAC, () => {
|
|||
resourceId?: string;
|
||||
projectId?: string;
|
||||
},
|
||||
options?: HasScopeOptions,
|
||||
options?: ScopeOptions,
|
||||
): boolean {
|
||||
return genericHasScope(
|
||||
scope,
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import type { EnterpriseEditionFeature } from '@/constants';
|
||||
import type { Resource, HasScopeOptions, Scope } from '@n8n/permissions';
|
||||
import type { Resource, ScopeOptions, Scope } from '@n8n/permissions';
|
||||
import type { IRole } from '@/Interface';
|
||||
|
||||
export type AuthenticatedPermissionOptions = {};
|
||||
export type CustomPermissionOptions<C = {}> = RBACPermissionCheck<C>;
|
||||
export type DefaultUserMiddlewareOptions = {};
|
||||
export type EnterprisePermissionOptions = {
|
||||
feature?: EnterpriseEditionFeature | EnterpriseEditionFeature[];
|
||||
mode?: 'oneOf' | 'allOf';
|
||||
|
@ -14,14 +15,22 @@ export type RBACPermissionOptions = {
|
|||
projectId?: string;
|
||||
resourceType?: Resource;
|
||||
resourceId?: string;
|
||||
options?: HasScopeOptions;
|
||||
options?: ScopeOptions;
|
||||
};
|
||||
export type RolePermissionOptions = IRole[];
|
||||
|
||||
export type PermissionType = 'authenticated' | 'custom' | 'enterprise' | 'guest' | 'rbac' | 'role';
|
||||
export type PermissionType =
|
||||
| 'authenticated'
|
||||
| 'custom'
|
||||
| 'defaultUser'
|
||||
| 'enterprise'
|
||||
| 'guest'
|
||||
| 'rbac'
|
||||
| 'role';
|
||||
export type PermissionTypeOptions = {
|
||||
authenticated: AuthenticatedPermissionOptions;
|
||||
custom: CustomPermissionOptions;
|
||||
defaultUser: DefaultUserMiddlewareOptions;
|
||||
enterprise: EnterprisePermissionOptions;
|
||||
guest: GuestPermissionOptions;
|
||||
rbac: RBACPermissionOptions;
|
||||
|
|
|
@ -13,6 +13,7 @@ import type {
|
|||
RBACPermissionOptions,
|
||||
RolePermissionOptions,
|
||||
PermissionType,
|
||||
DefaultUserMiddlewareOptions,
|
||||
} from '@/types/rbac';
|
||||
|
||||
export type RouterMiddlewareType = PermissionType;
|
||||
|
@ -24,6 +25,7 @@ export type CustomMiddlewareOptions = CustomPermissionOptions<{
|
|||
export type MiddlewareOptions = {
|
||||
authenticated: AuthenticatedPermissionOptions;
|
||||
custom: CustomMiddlewareOptions;
|
||||
defaultUser: DefaultUserMiddlewareOptions;
|
||||
enterprise: EnterprisePermissionOptions;
|
||||
guest: GuestPermissionOptions;
|
||||
rbac: RBACPermissionOptions;
|
||||
|
|
|
@ -92,7 +92,7 @@ export default defineComponent({
|
|||
return this.settingsStore.isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing);
|
||||
},
|
||||
showUMSetupWarning() {
|
||||
return hasPermission(['role'], { role: [ROLE.Default] });
|
||||
return hasPermission(['defaultUser']);
|
||||
},
|
||||
usersListActions(): IUserListAction[] {
|
||||
return [
|
||||
|
|
Loading…
Reference in a new issue