Files
payloadcms/packages/ui/src/providers/DocumentInfo/useGetDocPermissions.tsx
Paul 26ffbca914 feat: sanitise access endpoint (#7335)
Protects the `/api/access` endpoint behind authentication and sanitizes
the result, making it more secure and significantly smaller. To do this:

1. The `permission` keyword is completely omitted from the result
2. Only _truthy_ access results are returned
3. All nested permissions are consolidated when possible

---------

Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
Co-authored-by: James <james@trbl.design>
2024-11-15 15:08:06 -05:00

116 lines
3.4 KiB
TypeScript

import type { Data, SanitizedDocumentPermissions, SanitizedPermissions } from 'payload'
import * as qs from 'qs-esm'
import React from 'react'
import { hasSavePermission as getHasSavePermission } from '../../utilities/hasSavePermission.js'
import { isEditing as getIsEditing } from '../../utilities/isEditing.js'
export const useGetDocPermissions = ({
id,
api,
collectionSlug,
globalSlug,
i18n,
locale,
permissions,
serverURL,
setDocPermissions,
setHasPublishPermission,
setHasSavePermission,
}: {
api: string
collectionSlug: string
globalSlug: string
i18n: any
id: string
locale: string
permissions: SanitizedPermissions
serverURL: string
setDocPermissions: React.Dispatch<React.SetStateAction<SanitizedDocumentPermissions>>
setHasPublishPermission: React.Dispatch<React.SetStateAction<boolean>>
setHasSavePermission: React.Dispatch<React.SetStateAction<boolean>>
}) =>
React.useCallback(
async (data: Data) => {
const params = {
locale: locale || undefined,
}
const idToUse = data?.id || id
const newIsEditing = getIsEditing({ id: idToUse, collectionSlug, globalSlug })
if (newIsEditing) {
const docAccessURL = collectionSlug
? `/${collectionSlug}/access/${idToUse}`
: globalSlug
? `/globals/${globalSlug}/access`
: null
if (docAccessURL) {
const res = await fetch(`${serverURL}${api}${docAccessURL}?${qs.stringify(params)}`, {
body: JSON.stringify({
...(data || {}),
_status: 'draft',
}),
credentials: 'include',
headers: {
'Accept-Language': i18n.language,
'Content-Type': 'application/json',
},
method: 'post',
})
const json: SanitizedDocumentPermissions = await res.json()
const publishedAccessJSON = await fetch(
`${serverURL}${api}${docAccessURL}?${qs.stringify(params)}`,
{
body: JSON.stringify({
...(data || {}),
_status: 'published',
}),
credentials: 'include',
headers: {
'Accept-Language': i18n.language,
'Content-Type': 'application/json',
},
method: 'POST',
},
).then((res) => res.json())
setDocPermissions(json)
setHasSavePermission(
getHasSavePermission({
collectionSlug,
docPermissions: json,
globalSlug,
isEditing: newIsEditing,
}),
)
setHasPublishPermission(publishedAccessJSON?.update)
}
} else {
// when creating new documents, there is no permissions saved for this document yet
// use the generic entity permissions instead
const newDocPermissions = collectionSlug
? permissions?.collections?.[collectionSlug]
: permissions?.globals?.[globalSlug]
setDocPermissions(newDocPermissions)
setHasSavePermission(
getHasSavePermission({
collectionSlug,
docPermissions: newDocPermissions,
globalSlug,
isEditing: newIsEditing,
}),
)
}
},
[serverURL, api, id, permissions, i18n.language, locale, collectionSlug, globalSlug],
)