{"version":3,"file":"DocumentRBAC.mjs","sources":["../../../admin/src/features/DocumentRBAC.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport {\n useRBAC,\n useAuth,\n type Permission,\n createContext,\n Page,\n useQueryParams,\n} from '@strapi/admin/strapi-admin';\nimport { useParams } from 'react-router-dom';\n\nimport type { Schema } from '@strapi/types';\n\n/**\n * The boolean values indicate the global actions a user can perform on the document.\n * The `string[]` values tell us specifically which fields the actions can be performed on,\n * for example, if the `canReadFields` array is empty, than no fields can be read by the user.\n * This can happen even if the user can read the document.\n */\ninterface DocumentRBACContextValue {\n canCreate?: boolean;\n canCreateFields: string[];\n canDelete?: boolean;\n canPublish?: boolean;\n canRead?: boolean;\n canReadFields: string[];\n canUpdate?: boolean;\n canUpdateFields: string[];\n canUserAction: (\n fieldName: string,\n fieldsUserCanAction: string[],\n fieldType: Schema.Attribute.Kind\n ) => boolean;\n isLoading: boolean;\n}\n\nconst [DocumentRBACProvider, useDocumentRBAC] = createContext(\n 'DocumentRBAC',\n {\n canCreate: false,\n canCreateFields: [],\n canDelete: false,\n canPublish: false,\n canRead: false,\n canReadFields: [],\n canUpdate: false,\n canUpdateFields: [],\n canUserAction: () => false,\n isLoading: false,\n }\n);\n\ninterface DocumentRBACProps {\n children: React.ReactNode;\n permissions: Permission[] | null;\n model?: string;\n}\n\n/**\n * @internal This component is not meant to be used outside of the Content Manager plugin.\n * It depends on knowing the slug/model of the content-type using the params of the URL or the model if it is passed as arg.\n * If you do use the hook outside of the context, we default to `false` for all actions.\n *\n * It then creates an list of `can{Action}` that are passed to the context for consumption\n * within the app to enforce RBAC.\n */\nconst DocumentRBAC = ({ children, permissions, model }: DocumentRBACProps) => {\n const { slug } = useParams<{ slug: string }>();\n\n if (!slug && !model) {\n throw new Error('Cannot find the slug param in the URL or the model prop is not provided.');\n }\n\n const contentTypeUid = model ?? slug;\n\n const [{ rawQuery }] = useQueryParams<{ plugins?: { i18n?: { locale?: string } } }>();\n\n const userPermissions = useAuth('DocumentRBAC', (state) => state.permissions);\n\n const contentTypePermissions = React.useMemo(() => {\n const contentTypePermissions = userPermissions.filter(\n (permission) => permission.subject === contentTypeUid\n );\n return contentTypePermissions.reduce>((acc, permission) => {\n const [action] = permission.action.split('.').slice(-1);\n return { ...acc, [action]: [...(acc[action] ?? []), permission] };\n }, {});\n }, [contentTypeUid, userPermissions]);\n\n const { isLoading, allowedActions } = useRBAC(\n contentTypePermissions,\n permissions ?? undefined,\n // TODO: useRBAC context should be typed and built differently\n // We are passing raw query as context to the hook so that it can\n // rely on the locale provided from DocumentRBAC for its permission calculations.\n rawQuery\n );\n\n const canCreateFields =\n !isLoading && allowedActions.canCreate\n ? extractAndDedupeFields(contentTypePermissions.create)\n : [];\n\n const canReadFields =\n !isLoading && allowedActions.canRead ? extractAndDedupeFields(contentTypePermissions.read) : [];\n\n const canUpdateFields =\n !isLoading && allowedActions.canUpdate\n ? extractAndDedupeFields(contentTypePermissions.update)\n : [];\n\n /**\n * @description Checks if the user can perform an action on a field based on the field names\n * provided as the second argument.\n */\n const canUserAction: DocumentRBACContextValue['canUserAction'] = React.useCallback(\n (fieldName, fieldsUserCanAction, fieldType) => {\n const name = removeNumericalStrings(fieldName.split('.'));\n\n const componentFieldNames = fieldsUserCanAction\n // filter out fields that aren't components (components are dot separated)\n .filter((field) => field.split('.').length > 1);\n\n if (fieldType === 'component') {\n // check if the field name is within any of those arrays\n return componentFieldNames.some((field) => {\n return field.includes(name.join('.'));\n });\n }\n\n /**\n * The field is within a component.\n */\n if (name.length > 1) {\n return componentFieldNames.includes(name.join('.'));\n }\n\n /**\n * just a regular field\n */\n return fieldsUserCanAction.includes(fieldName);\n },\n []\n );\n\n if (isLoading) {\n return ;\n }\n\n return (\n \n {children}\n \n );\n};\n\n/**\n * @internal it's really small, but it's used three times in a row and DRY for something this straight forward.\n */\nconst extractAndDedupeFields = (permissions: Permission[] = []) => {\n const allFields = permissions.flatMap((permission) => permission.properties?.fields);\n // An undefined entry means this permission grants access to all fields\n // (no field-level restriction). Returning [] signals \"no restriction\" to callers.\n if (allFields.some((field) => field === undefined)) return [];\n // Deduplicate fields\n return Array.from(new Set(allFields as string[]));\n};\n\n/**\n * @internal removes numerical strings from arrays.\n * @example\n * ```ts\n * const name = 'a.0.b';\n * const res = removeNumericalStrings(name.split('.'));\n * console.log(res); // ['a', 'b']\n * ```\n */\nconst removeNumericalStrings = (arr: string[]) => arr.filter((item) => isNaN(Number(item)));\n\nexport { DocumentRBAC, useDocumentRBAC, DocumentRBACContextValue, DocumentRBACProps };\n"],"names":["DocumentRBACProvider","useDocumentRBAC","createContext","canCreate","canCreateFields","canDelete","canPublish","canRead","canReadFields","canUpdate","canUpdateFields","canUserAction","isLoading","DocumentRBAC","children","permissions","model","slug","useParams","Error","contentTypeUid","rawQuery","useQueryParams","userPermissions","useAuth","state","contentTypePermissions","React","useMemo","filter","permission","subject","reduce","acc","action","split","slice","allowedActions","useRBAC","undefined","extractAndDedupeFields","create","read","update","useCallback","fieldName","fieldsUserCanAction","fieldType","name","removeNumericalStrings","componentFieldNames","field","length","some","includes","join","_jsx","Page","Loading","allFields","flatMap","properties","fields","Array","from","Set","arr","item","isNaN","Number"],"mappings":"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"}