feat(editor): Replace middleware for Role checks with Scope checks (#7847)
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user