Skip to content

Commit 5146fc8

Browse files
authored
fix(ui): undefined permissions passed in create-first-user view (#13671)
### 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
1 parent d9e1832 commit 5146fc8

File tree

4 files changed

+69
-10
lines changed

4 files changed

+69
-10
lines changed

packages/next/src/views/CreateFirstUser/index.tsx

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
import type { AdminViewServerProps } from 'payload'
1+
import type {
2+
AdminViewServerProps,
3+
SanitizedDocumentPermissions,
4+
SanitizedFieldsPermissions,
5+
} from 'payload'
26

37
import { buildFormState } from '@payloadcms/ui/utilities/buildFormState'
48
import React from 'react'
59

610
import { getDocPreferences } from '../Document/getDocPreferences.js'
711
import { getDocumentData } from '../Document/getDocumentData.js'
8-
import { getDocumentPermissions } from '../Document/getDocumentPermissions.js'
912
import { CreateFirstUserClient } from './index.client.js'
1013
import './index.scss'
1114

@@ -43,18 +46,27 @@ export async function CreateFirstUserView({ initPageResult }: AdminViewServerPro
4346
user: req.user,
4447
})
4548

46-
// Get permissions
47-
const { docPermissions } = await getDocumentPermissions({
48-
collectionConfig,
49-
data,
50-
req,
51-
})
49+
const baseFields: SanitizedFieldsPermissions = Object.fromEntries(
50+
collectionConfig.fields
51+
.filter((f): f is { name: string } & typeof f => 'name' in f && typeof f.name === 'string')
52+
.map((f) => [f.name, { create: true, read: true, update: true }]),
53+
)
54+
55+
// In create-first-user we should always allow all fields
56+
const docPermissionsForForm: SanitizedDocumentPermissions = {
57+
create: true,
58+
delete: true,
59+
fields: baseFields,
60+
read: true,
61+
readVersions: true,
62+
update: true,
63+
}
5264

5365
// Build initial form state from data
5466
const { state: formState } = await buildFormState({
5567
collectionSlug: collectionConfig.slug,
5668
data,
57-
docPermissions,
69+
docPermissions: docPermissionsForForm,
5870
docPreferences,
5971
locale: locale?.code,
6072
operation: 'create',
@@ -69,7 +81,7 @@ export async function CreateFirstUserView({ initPageResult }: AdminViewServerPro
6981
<h1>{req.t('general:welcome')}</h1>
7082
<p>{req.t('authentication:beginCreateFirstUser')}</p>
7183
<CreateFirstUserClient
72-
docPermissions={docPermissions}
84+
docPermissions={docPermissionsForForm}
7385
docPreferences={docPreferences}
7486
initialState={formState}
7587
loginWithUsername={loginWithUsername}

test/auth/config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ export default buildConfigWithDefaults({
7575
label: 'Named Save To JWT',
7676
saveToJWT: saveToJWTKey,
7777
},
78+
{
79+
name: 'richText',
80+
type: 'richText',
81+
},
7882
{
7983
name: 'group',
8084
type: 'group',

test/auth/e2e.spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,33 @@ describe('Auth', () => {
124124
.poll(() => page.url(), { timeout: POLL_TOPASS_TIMEOUT })
125125
.not.toContain('create-first-user')
126126
})
127+
128+
test('richText field should should not be readOnly in create first user view', async () => {
129+
const {
130+
admin: {
131+
routes: { createFirstUser: createFirstUserRoute },
132+
},
133+
routes: { admin: adminRoute },
134+
} = getRoutes({})
135+
136+
// wait for create first user route
137+
await page.goto(serverURL + `${adminRoute}${createFirstUserRoute}`)
138+
139+
await expect(page.locator('.create-first-user')).toBeVisible()
140+
141+
await waitForVisibleAuthFields()
142+
143+
const richTextRoot = page
144+
.locator('.rich-text-lexical .ContentEditable__root[data-lexical-editor="true"]')
145+
.first()
146+
147+
// ensure editor is present
148+
await expect(richTextRoot).toBeVisible()
149+
150+
// core read-only checks
151+
await expect(richTextRoot).toHaveAttribute('contenteditable', 'true')
152+
await expect(richTextRoot).not.toHaveAttribute('aria-readonly', 'true')
153+
})
127154
})
128155

129156
describe('non create first user', () => {

test/auth/payload-types.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,21 @@ export interface User {
243243
adminOnlyField?: string | null;
244244
roles: ('admin' | 'editor' | 'moderator' | 'user' | 'viewer')[];
245245
namedSaveToJWT?: string | null;
246+
richText?: {
247+
root: {
248+
type: string;
249+
children: {
250+
type: string;
251+
version: number;
252+
[k: string]: unknown;
253+
}[];
254+
direction: ('ltr' | 'rtl') | null;
255+
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
256+
indent: number;
257+
version: number;
258+
};
259+
[k: string]: unknown;
260+
} | null;
246261
group?: {
247262
liftedSaveToJWT?: string | null;
248263
};
@@ -503,6 +518,7 @@ export interface UsersSelect<T extends boolean = true> {
503518
adminOnlyField?: T;
504519
roles?: T;
505520
namedSaveToJWT?: T;
521+
richText?: T;
506522
group?:
507523
| T
508524
| {

0 commit comments

Comments
 (0)