feat(editor): Replace middleware for Role checks with Scope checks (#7847)

This commit is contained in:
Alex Grozav
2023-11-29 10:35:40 +02:00
committed by GitHub
parent d4970410e1
commit 72852a60eb
18 changed files with 267 additions and 111 deletions

View File

@@ -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', () => {

View File

@@ -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';

View File

@@ -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);
});
});
});

View File

@@ -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';

View 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;
};

View File

@@ -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,

View File

@@ -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();
});
});
});

View 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 });
}
};

View File

@@ -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,