From 5146fc865f8f677e1baf18e2777582e64470a2d9 Mon Sep 17 00:00:00 2001 From: Patrik <35232443+PatrikKozak@users.noreply.github.com> Date: Wed, 3 Sep 2025 17:16:49 -0400 Subject: [PATCH] fix(ui): undefined permissions passed in create-first-user view (#13671) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### What? In the create-first-user view, fields like `richText` were being marked as `readOnly: true` because they had no permissions entry in the permissions map. ### Why? The view was passing an incomplete `docPermissions` object. When a field had no entry in `docPermissions.fields`, `renderField` received `permissions: undefined`, which was interpreted as denied access. This caused fields (notably `richText`) to default to read-only even though the user should have full access when creating the first user. ### How? - Updated the create-first-user view to always pass a complete `docPermissions` object. - Default all fields in the user collection to `{ create: true, read: true, update: true }`. - Ensures every field is explicitly granted full access during the first-user flow. - Keeps the `renderField` logic unchanged and aligned with Payload’s permission model. Fixes #13612 --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211211792037939 --- .../next/src/views/CreateFirstUser/index.tsx | 32 +++++++++++++------ test/auth/config.ts | 4 +++ test/auth/e2e.spec.ts | 27 ++++++++++++++++ test/auth/payload-types.ts | 16 ++++++++++ 4 files changed, 69 insertions(+), 10 deletions(-) diff --git a/packages/next/src/views/CreateFirstUser/index.tsx b/packages/next/src/views/CreateFirstUser/index.tsx index d6c3c7c66..dd83082c9 100644 --- a/packages/next/src/views/CreateFirstUser/index.tsx +++ b/packages/next/src/views/CreateFirstUser/index.tsx @@ -1,11 +1,14 @@ -import type { AdminViewServerProps } from 'payload' +import type { + AdminViewServerProps, + SanitizedDocumentPermissions, + SanitizedFieldsPermissions, +} from 'payload' import { buildFormState } from '@payloadcms/ui/utilities/buildFormState' import React from 'react' import { getDocPreferences } from '../Document/getDocPreferences.js' import { getDocumentData } from '../Document/getDocumentData.js' -import { getDocumentPermissions } from '../Document/getDocumentPermissions.js' import { CreateFirstUserClient } from './index.client.js' import './index.scss' @@ -43,18 +46,27 @@ export async function CreateFirstUserView({ initPageResult }: AdminViewServerPro user: req.user, }) - // Get permissions - const { docPermissions } = await getDocumentPermissions({ - collectionConfig, - data, - req, - }) + const baseFields: SanitizedFieldsPermissions = Object.fromEntries( + collectionConfig.fields + .filter((f): f is { name: string } & typeof f => 'name' in f && typeof f.name === 'string') + .map((f) => [f.name, { create: true, read: true, update: true }]), + ) + + // In create-first-user we should always allow all fields + const docPermissionsForForm: SanitizedDocumentPermissions = { + create: true, + delete: true, + fields: baseFields, + read: true, + readVersions: true, + update: true, + } // Build initial form state from data const { state: formState } = await buildFormState({ collectionSlug: collectionConfig.slug, data, - docPermissions, + docPermissions: docPermissionsForForm, docPreferences, locale: locale?.code, operation: 'create', @@ -69,7 +81,7 @@ export async function CreateFirstUserView({ initPageResult }: AdminViewServerPro

{req.t('general:welcome')}

{req.t('authentication:beginCreateFirstUser')}

{ .poll(() => page.url(), { timeout: POLL_TOPASS_TIMEOUT }) .not.toContain('create-first-user') }) + + test('richText field should should not be readOnly in create first user view', async () => { + const { + admin: { + routes: { createFirstUser: createFirstUserRoute }, + }, + routes: { admin: adminRoute }, + } = getRoutes({}) + + // wait for create first user route + await page.goto(serverURL + `${adminRoute}${createFirstUserRoute}`) + + await expect(page.locator('.create-first-user')).toBeVisible() + + await waitForVisibleAuthFields() + + const richTextRoot = page + .locator('.rich-text-lexical .ContentEditable__root[data-lexical-editor="true"]') + .first() + + // ensure editor is present + await expect(richTextRoot).toBeVisible() + + // core read-only checks + await expect(richTextRoot).toHaveAttribute('contenteditable', 'true') + await expect(richTextRoot).not.toHaveAttribute('aria-readonly', 'true') + }) }) describe('non create first user', () => { diff --git a/test/auth/payload-types.ts b/test/auth/payload-types.ts index 8b432b5a9..97937fca4 100644 --- a/test/auth/payload-types.ts +++ b/test/auth/payload-types.ts @@ -243,6 +243,21 @@ export interface User { adminOnlyField?: string | null; roles: ('admin' | 'editor' | 'moderator' | 'user' | 'viewer')[]; namedSaveToJWT?: string | null; + richText?: { + root: { + type: string; + children: { + type: string; + version: number; + [k: string]: unknown; + }[]; + direction: ('ltr' | 'rtl') | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + [k: string]: unknown; + } | null; group?: { liftedSaveToJWT?: string | null; }; @@ -503,6 +518,7 @@ export interface UsersSelect { adminOnlyField?: T; roles?: T; namedSaveToJWT?: T; + richText?: T; group?: | T | {