From 5ecb9965b7fe1d338fca874a7d64a8ae4bec50a4 Mon Sep 17 00:00:00 2001 From: Brainslug Date: Fri, 24 Feb 2023 18:44:16 +0100 Subject: [PATCH] Hide related fields without read permissions (#16832) * remove related O2M fields without read permissions * Added basic test for usePermissions composable * updated test --------- Co-authored-by: Rijk van Zanten --- app/src/composables/use-permissions.test.ts | 123 ++++++++++++++++++++ app/src/composables/use-permissions.ts | 6 + 2 files changed, 129 insertions(+) create mode 100644 app/src/composables/use-permissions.test.ts diff --git a/app/src/composables/use-permissions.test.ts b/app/src/composables/use-permissions.test.ts new file mode 100644 index 0000000000..9067206b73 --- /dev/null +++ b/app/src/composables/use-permissions.test.ts @@ -0,0 +1,123 @@ +import { createTestingPinia } from '@pinia/testing'; +import { setActivePinia } from 'pinia'; +import { ref } from 'vue'; +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; + +beforeEach(() => { + setActivePinia( + createTestingPinia({ + createSpy: vi.fn, + stubActions: false, + }) + ); +}); + +import { useUserStore } from '@/stores/user'; +import { usePermissionsStore } from '@/stores/permissions'; +import { usePermissions } from './use-permissions'; +import { useCollection } from '@directus/shared/composables'; +import { Field } from '@directus/shared/types'; + +vi.mock('@directus/shared/composables'); + +const mockUser = { + id: '00000000-0000-0000-0000-000000000000', + role: { + admin_access: false, + id: '00000000-0000-0000-0000-000000000000', + }, +}; +const mockReadPermissions = { + role: '00000000-0000-0000-0000-000000000000', + permissions: { + _and: [ + { + field_a: { + _null: true, + }, + }, + { + field_b: { + _null: true, + }, + }, + ], + }, + validation: null, + presets: null, + fields: ['id', 'start_date', 'end_date'], + collection: 'test', + action: 'read', +}; +const mockFields: Field[] = [ + { + collection: 'test', + field: 'id', + name: 'id', + type: 'integer', + schema: null, + meta: null, + }, + { + collection: 'test', + field: 'name', + name: 'name', + type: 'string', + schema: null, + meta: null, + }, + { + collection: 'test', + field: 'start_date', + name: 'start_date', + type: 'timestamp', + schema: null, + meta: null, + }, + { + collection: 'test', + field: 'end_date', + name: 'end_date', + type: 'timestamp', + schema: null, + meta: null, + }, +]; + +vi.mock('@/api', () => { + return { + default: { + get: (path: string) => { + if (path === '/permissions') { + return Promise.resolve({ + data: { data: [mockReadPermissions] }, + }); + } + + return Promise.reject(new Error(`GET "${path}" is not mocked in this test`)); + }, + }, + }; +}); + +afterEach(() => { + vi.restoreAllMocks(); +}); + +describe('usePermissions', () => { + test('Remove fields without read permissions #16732', async () => { + const userStore = useUserStore(); + userStore.currentUser = mockUser as any; + + const permissionsStore = usePermissionsStore(); + await permissionsStore.hydrate(); + + vi.mocked(useCollection).mockReturnValue({ info: ref(null), fields: ref(mockFields) } as any); + + const { fields } = usePermissions(ref('test'), ref(null), ref(false)); + expect(fields.value.length).toBeGreaterThan(0); + for (const field of fields.value) { + expect(mockReadPermissions.fields.includes(field.field)).toBe(true); + } + }); +}); diff --git a/app/src/composables/use-permissions.ts b/app/src/composables/use-permissions.ts index 8f9b5fa885..1a029ca730 100644 --- a/app/src/composables/use-permissions.ts +++ b/app/src/composables/use-permissions.ts @@ -59,6 +59,12 @@ export function usePermissions(collection: Ref, item: Ref, isNew: R const permissions = permissionsStore.getPermissionsForUser(collection.value, isNew.value ? 'create' : 'update'); + // remove fields without read permissions so they don't show up in the DOM + const readableFields = permissionsStore.getPermissionsForUser(collection.value, 'read')?.fields; + if (readableFields && readableFields.includes('*') === false) { + fields = fields.filter((field) => readableFields.includes(field.field)); + } + if (!permissions) return fields; if (permissions.fields?.includes('*') === false) {