diff --git a/packages/payload/src/admin/components/forms/RenderFields/filterFields.tsx b/packages/payload/src/admin/components/forms/RenderFields/filterFields.tsx index 472927002c..243d618763 100644 --- a/packages/payload/src/admin/components/forms/RenderFields/filterFields.tsx +++ b/packages/payload/src/admin/components/forms/RenderFields/filterFields.tsx @@ -48,23 +48,24 @@ export const filterFields = (args: { } const isFieldAffectingData = fieldAffectsData(field) - const fieldPermissions = isFieldAffectingData ? permissions?.[field.name] : permissions + // if the user cannot read the field, then filter it out + if (fieldPermissions?.read?.permission === false) { + return acc + } + + // readOnly from field config let readOnly = field.admin && 'readOnly' in field.admin ? field.admin.readOnly : undefined + // if parent field is readOnly + // but this field is `readOnly: false` + // the field should be editable if (readOnlyOverride && readOnly !== false) readOnly = true - if ( - (isFieldAffectingData && permissions?.[field?.name]?.read?.permission !== false) || - !isFieldAffectingData - ) { - if ( - isFieldAffectingData && - permissions?.[field?.name]?.[operation]?.permission === false - ) { - readOnly = true - } + // unless the user does not pass access control + if (fieldPermissions?.[operation]?.permission === false) { + readOnly = true } if (FieldComponent) { diff --git a/test/access-control/config.ts b/test/access-control/config.ts index be38197429..8858f76157 100644 --- a/test/access-control/config.ts +++ b/test/access-control/config.ts @@ -52,7 +52,22 @@ export default buildConfigWithDefaults({ setTimeout(resolve, 50, true) // set to 'true' or 'false' here to simulate the response }), }, - fields: [], + fields: [ + { + name: 'roles', + type: 'select', + hasMany: true, + options: ['admin', 'user'], + defaultValue: ['user'], + access: { + create: ({ req }) => req.user?.roles?.includes('admin'), + read: () => false, + update: ({ req }) => { + return req.user?.roles?.includes('admin') + }, + }, + }, + ], }, { slug, diff --git a/test/access-control/e2e.spec.ts b/test/access-control/e2e.spec.ts index 6e8225ea83..5b43293a75 100644 --- a/test/access-control/e2e.spec.ts +++ b/test/access-control/e2e.spec.ts @@ -125,6 +125,14 @@ describe('access control', () => { }) }) + describe('restricted fields', () => { + test('should not show field without permission', async () => { + await page.goto(url.account) + await wait(500) + await expect(page.locator('#field-roles')).toBeHidden() + }) + }) + describe('read-only collection', () => { let existingDoc: ReadOnlyCollection diff --git a/test/access-control/payload-types.ts b/test/access-control/payload-types.ts index 6395621fde..aca75e6703 100644 --- a/test/access-control/payload-types.ts +++ b/test/access-control/payload-types.ts @@ -1,4 +1,5 @@ /* tslint:disable */ +/* eslint-disable */ /** * This file was automatically generated by Payload. * DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config, @@ -9,92 +10,156 @@ export interface Config { collections: { users: User posts: Post + unrestricted: Unrestricted restricted: Restricted 'read-only-collection': ReadOnlyCollection + 'user-restricted': UserRestricted 'restricted-versions': RestrictedVersion 'sibling-data': SiblingDatum 'rely-on-request-headers': RelyOnRequestHeader 'doc-level-access': DocLevelAccess 'hidden-fields': HiddenField + 'hidden-access': HiddenAccess + 'payload-preferences': PayloadPreference + 'payload-migrations': PayloadMigration } globals: {} } export interface User { id: string - email?: string - resetPasswordToken?: string - resetPasswordExpiration?: string - loginAttempts?: number - lockUntil?: string - createdAt: string + roles?: ('admin' | 'user')[] | null updatedAt: string - password?: string + createdAt: string + email: string + resetPasswordToken?: string | null + resetPasswordExpiration?: string | null + salt?: string | null + hash?: string | null + loginAttempts?: number | null + lockUntil?: string | null + password: string | null } export interface Post { id: string - restrictedField?: string + restrictedField?: string | null group?: { - restrictedGroupText?: string + restrictedGroupText?: string | null } - restrictedRowText?: string - restrictedCollapsibleText?: string - createdAt: string + restrictedRowText?: string | null + restrictedCollapsibleText?: string | null updatedAt: string + createdAt: string +} +export interface Unrestricted { + id: string + name?: string | null + userRestrictedDocs?: (string | UserRestricted)[] | null + updatedAt: string + createdAt: string +} +export interface UserRestricted { + id: string + name?: string | null + updatedAt: string + createdAt: string } export interface Restricted { id: string - name?: string - createdAt: string + name?: string | null updatedAt: string + createdAt: string } export interface ReadOnlyCollection { id: string - name?: string - createdAt: string + name?: string | null updatedAt: string + createdAt: string } export interface RestrictedVersion { id: string - name?: string - createdAt: string + name?: string | null + hidden?: boolean | null updatedAt: string + createdAt: string } export interface SiblingDatum { id: string - array?: { - allowPublicReadability?: boolean - text?: string - id?: string - }[] - createdAt: string + array?: + | { + allowPublicReadability?: boolean | null + text?: string | null + id?: string | null + }[] + | null updatedAt: string + createdAt: string } export interface RelyOnRequestHeader { id: string - name?: string - createdAt: string + name?: string | null updatedAt: string + createdAt: string } export interface DocLevelAccess { id: string - approvedForRemoval?: boolean - approvedTitle?: string - lockTitle?: boolean - createdAt: string + approvedForRemoval?: boolean | null + approvedTitle?: string | null + lockTitle?: boolean | null updatedAt: string + createdAt: string } export interface HiddenField { id: string - title?: string + title?: string | null partiallyHiddenGroup?: { - name?: string - value?: string + name?: string | null + value?: string | null } - partiallyHiddenArray?: { - name?: string - value?: string - id?: string - }[] - createdAt: string + partiallyHiddenArray?: + | { + name?: string | null + value?: string | null + id?: string | null + }[] + | null + hidden?: boolean | null updatedAt: string + createdAt: string +} +export interface HiddenAccess { + id: string + title: string + hidden?: boolean | null + updatedAt: string + createdAt: string +} +export interface PayloadPreference { + id: string + user: { + relationTo: 'users' + value: string | User + } + key?: string | null + value?: + | { + [k: string]: unknown + } + | unknown[] + | string + | number + | boolean + | null + updatedAt: string + createdAt: string +} +export interface PayloadMigration { + id: string + name?: string | null + batch?: number | null + updatedAt: string + createdAt: string +} + +declare module 'payload' { + export interface GeneratedTypes extends Config {} }