convex-cms 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/admin.d.ts +16 -0
- package/dist/cli/commands/admin.d.ts.map +1 -0
- package/dist/cli/commands/admin.js +88 -0
- package/dist/cli/commands/admin.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +18 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils/detectConvexUrl.d.ts +13 -0
- package/dist/cli/utils/detectConvexUrl.d.ts.map +1 -0
- package/dist/cli/utils/detectConvexUrl.js +48 -0
- package/dist/cli/utils/detectConvexUrl.js.map +1 -0
- package/dist/cli/utils/openBrowser.d.ts +7 -0
- package/dist/cli/utils/openBrowser.d.ts.map +1 -0
- package/dist/cli/utils/openBrowser.js +17 -0
- package/dist/cli/utils/openBrowser.js.map +1 -0
- package/dist/client/admin-config.d.ts +126 -0
- package/dist/client/admin-config.d.ts.map +1 -0
- package/dist/client/admin-config.js +117 -0
- package/dist/client/admin-config.js.map +1 -0
- package/dist/client/adminApi.d.ts +2273 -0
- package/dist/client/adminApi.d.ts.map +1 -0
- package/dist/client/adminApi.js +716 -0
- package/dist/client/adminApi.js.map +1 -0
- package/dist/client/agentTools.d.ts +933 -0
- package/dist/client/agentTools.d.ts.map +1 -0
- package/dist/client/agentTools.js +1004 -0
- package/dist/client/agentTools.js.map +1 -0
- package/dist/client/argTypes.d.ts +212 -0
- package/dist/client/argTypes.d.ts.map +1 -0
- package/dist/client/argTypes.js +5 -0
- package/dist/client/argTypes.js.map +1 -0
- package/dist/client/field-types.d.ts +55 -0
- package/dist/client/field-types.d.ts.map +1 -0
- package/dist/client/field-types.js +152 -0
- package/dist/client/field-types.js.map +1 -0
- package/dist/client/index.d.ts +189 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +668 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/queryBuilder.d.ts +765 -0
- package/dist/client/queryBuilder.d.ts.map +1 -0
- package/dist/client/queryBuilder.js +970 -0
- package/dist/client/queryBuilder.js.map +1 -0
- package/dist/client/schema/codegen.d.ts +128 -0
- package/dist/client/schema/codegen.d.ts.map +1 -0
- package/dist/client/schema/codegen.js +318 -0
- package/dist/client/schema/codegen.js.map +1 -0
- package/dist/client/schema/defineContentType.d.ts +221 -0
- package/dist/client/schema/defineContentType.d.ts.map +1 -0
- package/dist/client/schema/defineContentType.js +380 -0
- package/dist/client/schema/defineContentType.js.map +1 -0
- package/dist/client/schema/index.d.ts +85 -0
- package/dist/client/schema/index.d.ts.map +1 -0
- package/dist/client/schema/index.js +92 -0
- package/dist/client/schema/index.js.map +1 -0
- package/dist/client/schema/schemaDrift.d.ts +199 -0
- package/dist/client/schema/schemaDrift.d.ts.map +1 -0
- package/dist/client/schema/schemaDrift.js +340 -0
- package/dist/client/schema/schemaDrift.js.map +1 -0
- package/dist/client/schema/typedClient.d.ts +401 -0
- package/dist/client/schema/typedClient.d.ts.map +1 -0
- package/dist/client/schema/typedClient.js +269 -0
- package/dist/client/schema/typedClient.js.map +1 -0
- package/dist/client/schema/types.d.ts +477 -0
- package/dist/client/schema/types.d.ts.map +1 -0
- package/dist/client/schema/types.js +39 -0
- package/dist/client/schema/types.js.map +1 -0
- package/dist/client/types.d.ts +449 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +149 -0
- package/dist/client/types.js.map +1 -0
- package/dist/client/workflows.d.ts +51 -0
- package/dist/client/workflows.d.ts.map +1 -0
- package/dist/client/workflows.js +103 -0
- package/dist/client/workflows.js.map +1 -0
- package/dist/client/wrapper.d.ts +2198 -0
- package/dist/client/wrapper.d.ts.map +1 -0
- package/dist/client/wrapper.js +2651 -0
- package/dist/client/wrapper.js.map +1 -0
- package/dist/component/_generated/api.d.ts +124 -0
- package/dist/component/_generated/api.d.ts.map +1 -0
- package/dist/component/_generated/api.js +31 -0
- package/dist/component/_generated/api.js.map +1 -0
- package/dist/component/_generated/component.d.ts +4321 -0
- package/dist/component/_generated/component.d.ts.map +1 -0
- package/dist/component/_generated/component.js +11 -0
- package/dist/component/_generated/component.js.map +1 -0
- package/dist/component/_generated/dataModel.d.ts +46 -0
- package/dist/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/component/_generated/dataModel.js +11 -0
- package/dist/component/_generated/dataModel.js.map +1 -0
- package/dist/component/_generated/server.d.ts +121 -0
- package/dist/component/_generated/server.d.ts.map +1 -0
- package/dist/component/_generated/server.js +78 -0
- package/dist/component/_generated/server.js.map +1 -0
- package/dist/component/auditLog.d.ts +410 -0
- package/dist/component/auditLog.d.ts.map +1 -0
- package/dist/component/auditLog.js +607 -0
- package/dist/component/auditLog.js.map +1 -0
- package/dist/component/authorization.d.ts +323 -0
- package/dist/component/authorization.d.ts.map +1 -0
- package/dist/component/authorization.js +464 -0
- package/dist/component/authorization.js.map +1 -0
- package/dist/component/authorizationHooks.d.ts +184 -0
- package/dist/component/authorizationHooks.d.ts.map +1 -0
- package/dist/component/authorizationHooks.js +521 -0
- package/dist/component/authorizationHooks.js.map +1 -0
- package/dist/component/bulkOperations.d.ts +200 -0
- package/dist/component/bulkOperations.d.ts.map +1 -0
- package/dist/component/bulkOperations.js +568 -0
- package/dist/component/bulkOperations.js.map +1 -0
- package/dist/component/contentEntries.d.ts +719 -0
- package/dist/component/contentEntries.d.ts.map +1 -0
- package/dist/component/contentEntries.js +1617 -0
- package/dist/component/contentEntries.js.map +1 -0
- package/dist/component/contentEntryMutations.d.ts +505 -0
- package/dist/component/contentEntryMutations.d.ts.map +1 -0
- package/dist/component/contentEntryMutations.js +1009 -0
- package/dist/component/contentEntryMutations.js.map +1 -0
- package/dist/component/contentEntryValidation.d.ts +115 -0
- package/dist/component/contentEntryValidation.d.ts.map +1 -0
- package/dist/component/contentEntryValidation.js +546 -0
- package/dist/component/contentEntryValidation.js.map +1 -0
- package/dist/component/contentLock.d.ts +328 -0
- package/dist/component/contentLock.d.ts.map +1 -0
- package/dist/component/contentLock.js +471 -0
- package/dist/component/contentLock.js.map +1 -0
- package/dist/component/contentTypeMigration.d.ts +411 -0
- package/dist/component/contentTypeMigration.d.ts.map +1 -0
- package/dist/component/contentTypeMigration.js +805 -0
- package/dist/component/contentTypeMigration.js.map +1 -0
- package/dist/component/contentTypeMutations.d.ts +975 -0
- package/dist/component/contentTypeMutations.d.ts.map +1 -0
- package/dist/component/contentTypeMutations.js +768 -0
- package/dist/component/contentTypeMutations.js.map +1 -0
- package/dist/component/contentTypes.d.ts +538 -0
- package/dist/component/contentTypes.d.ts.map +1 -0
- package/dist/component/contentTypes.js +304 -0
- package/dist/component/contentTypes.js.map +1 -0
- package/dist/component/convex.config.d.ts +42 -0
- package/dist/component/convex.config.d.ts.map +1 -0
- package/dist/component/convex.config.js +43 -0
- package/dist/component/convex.config.js.map +1 -0
- package/dist/component/documentTypes.d.ts +186 -0
- package/dist/component/documentTypes.d.ts.map +1 -0
- package/dist/component/documentTypes.js +23 -0
- package/dist/component/documentTypes.js.map +1 -0
- package/dist/component/eventEmitter.d.ts +281 -0
- package/dist/component/eventEmitter.d.ts.map +1 -0
- package/dist/component/eventEmitter.js +300 -0
- package/dist/component/eventEmitter.js.map +1 -0
- package/dist/component/exportImport.d.ts +1120 -0
- package/dist/component/exportImport.d.ts.map +1 -0
- package/dist/component/exportImport.js +931 -0
- package/dist/component/exportImport.js.map +1 -0
- package/dist/component/index.d.ts +28 -0
- package/dist/component/index.d.ts.map +1 -0
- package/dist/component/index.js +142 -0
- package/dist/component/index.js.map +1 -0
- package/dist/component/lib/deepReferenceResolver.d.ts +252 -0
- package/dist/component/lib/deepReferenceResolver.d.ts.map +1 -0
- package/dist/component/lib/deepReferenceResolver.js +601 -0
- package/dist/component/lib/deepReferenceResolver.js.map +1 -0
- package/dist/component/lib/errors.d.ts +306 -0
- package/dist/component/lib/errors.d.ts.map +1 -0
- package/dist/component/lib/errors.js +407 -0
- package/dist/component/lib/errors.js.map +1 -0
- package/dist/component/lib/index.d.ts +10 -0
- package/dist/component/lib/index.d.ts.map +1 -0
- package/dist/component/lib/index.js +33 -0
- package/dist/component/lib/index.js.map +1 -0
- package/dist/component/lib/mediaReferenceResolver.d.ts +217 -0
- package/dist/component/lib/mediaReferenceResolver.d.ts.map +1 -0
- package/dist/component/lib/mediaReferenceResolver.js +326 -0
- package/dist/component/lib/mediaReferenceResolver.js.map +1 -0
- package/dist/component/lib/metadataExtractor.d.ts +245 -0
- package/dist/component/lib/metadataExtractor.d.ts.map +1 -0
- package/dist/component/lib/metadataExtractor.js +548 -0
- package/dist/component/lib/metadataExtractor.js.map +1 -0
- package/dist/component/lib/mutationAuth.d.ts +95 -0
- package/dist/component/lib/mutationAuth.d.ts.map +1 -0
- package/dist/component/lib/mutationAuth.js +146 -0
- package/dist/component/lib/mutationAuth.js.map +1 -0
- package/dist/component/lib/queries.d.ts +17 -0
- package/dist/component/lib/queries.d.ts.map +1 -0
- package/dist/component/lib/queries.js +49 -0
- package/dist/component/lib/queries.js.map +1 -0
- package/dist/component/lib/ragContentChunker.d.ts +423 -0
- package/dist/component/lib/ragContentChunker.d.ts.map +1 -0
- package/dist/component/lib/ragContentChunker.js +897 -0
- package/dist/component/lib/ragContentChunker.js.map +1 -0
- package/dist/component/lib/referenceResolver.d.ts +175 -0
- package/dist/component/lib/referenceResolver.d.ts.map +1 -0
- package/dist/component/lib/referenceResolver.js +293 -0
- package/dist/component/lib/referenceResolver.js.map +1 -0
- package/dist/component/lib/slugGenerator.d.ts +71 -0
- package/dist/component/lib/slugGenerator.d.ts.map +1 -0
- package/dist/component/lib/slugGenerator.js +207 -0
- package/dist/component/lib/slugGenerator.js.map +1 -0
- package/dist/component/lib/slugUniqueness.d.ts +131 -0
- package/dist/component/lib/slugUniqueness.d.ts.map +1 -0
- package/dist/component/lib/slugUniqueness.js +229 -0
- package/dist/component/lib/slugUniqueness.js.map +1 -0
- package/dist/component/lib/softDelete.d.ts +18 -0
- package/dist/component/lib/softDelete.d.ts.map +1 -0
- package/dist/component/lib/softDelete.js +29 -0
- package/dist/component/lib/softDelete.js.map +1 -0
- package/dist/component/localeFallbackChain.d.ts +410 -0
- package/dist/component/localeFallbackChain.d.ts.map +1 -0
- package/dist/component/localeFallbackChain.js +467 -0
- package/dist/component/localeFallbackChain.js.map +1 -0
- package/dist/component/localeFields.d.ts +508 -0
- package/dist/component/localeFields.d.ts.map +1 -0
- package/dist/component/localeFields.js +592 -0
- package/dist/component/localeFields.js.map +1 -0
- package/dist/component/mediaAssetMutations.d.ts +235 -0
- package/dist/component/mediaAssetMutations.d.ts.map +1 -0
- package/dist/component/mediaAssetMutations.js +558 -0
- package/dist/component/mediaAssetMutations.js.map +1 -0
- package/dist/component/mediaAssets.d.ts +168 -0
- package/dist/component/mediaAssets.d.ts.map +1 -0
- package/dist/component/mediaAssets.js +618 -0
- package/dist/component/mediaAssets.js.map +1 -0
- package/dist/component/mediaFolderMutations.d.ts +642 -0
- package/dist/component/mediaFolderMutations.d.ts.map +1 -0
- package/dist/component/mediaFolderMutations.js +849 -0
- package/dist/component/mediaFolderMutations.js.map +1 -0
- package/dist/component/mediaUploadMutations.d.ts +136 -0
- package/dist/component/mediaUploadMutations.d.ts.map +1 -0
- package/dist/component/mediaUploadMutations.js +205 -0
- package/dist/component/mediaUploadMutations.js.map +1 -0
- package/dist/component/mediaVariantMutations.d.ts +468 -0
- package/dist/component/mediaVariantMutations.d.ts.map +1 -0
- package/dist/component/mediaVariantMutations.js +737 -0
- package/dist/component/mediaVariantMutations.js.map +1 -0
- package/dist/component/mediaVariants.d.ts +525 -0
- package/dist/component/mediaVariants.d.ts.map +1 -0
- package/dist/component/mediaVariants.js +661 -0
- package/dist/component/mediaVariants.js.map +1 -0
- package/dist/component/ragContentIndexer.d.ts +595 -0
- package/dist/component/ragContentIndexer.d.ts.map +1 -0
- package/dist/component/ragContentIndexer.js +794 -0
- package/dist/component/ragContentIndexer.js.map +1 -0
- package/dist/component/rateLimitHooks.d.ts +266 -0
- package/dist/component/rateLimitHooks.d.ts.map +1 -0
- package/dist/component/rateLimitHooks.js +412 -0
- package/dist/component/rateLimitHooks.js.map +1 -0
- package/dist/component/roles.d.ts +649 -0
- package/dist/component/roles.d.ts.map +1 -0
- package/dist/component/roles.js +884 -0
- package/dist/component/roles.js.map +1 -0
- package/dist/component/scheduledPublish.d.ts +182 -0
- package/dist/component/scheduledPublish.d.ts.map +1 -0
- package/dist/component/scheduledPublish.js +304 -0
- package/dist/component/scheduledPublish.js.map +1 -0
- package/dist/component/schema.d.ts +4114 -0
- package/dist/component/schema.d.ts.map +1 -0
- package/dist/component/schema.js +469 -0
- package/dist/component/schema.js.map +1 -0
- package/dist/component/taxonomies.d.ts +476 -0
- package/dist/component/taxonomies.d.ts.map +1 -0
- package/dist/component/taxonomies.js +785 -0
- package/dist/component/taxonomies.js.map +1 -0
- package/dist/component/taxonomyMutations.d.ts +206 -0
- package/dist/component/taxonomyMutations.d.ts.map +1 -0
- package/dist/component/taxonomyMutations.js +1001 -0
- package/dist/component/taxonomyMutations.js.map +1 -0
- package/dist/component/trash.d.ts +265 -0
- package/dist/component/trash.d.ts.map +1 -0
- package/dist/component/trash.js +621 -0
- package/dist/component/trash.js.map +1 -0
- package/dist/component/types.d.ts +4 -0
- package/dist/component/types.d.ts.map +1 -0
- package/dist/component/types.js +2 -0
- package/dist/component/types.js.map +1 -0
- package/dist/component/userContext.d.ts +508 -0
- package/dist/component/userContext.d.ts.map +1 -0
- package/dist/component/userContext.js +615 -0
- package/dist/component/userContext.js.map +1 -0
- package/dist/component/validation.d.ts +387 -0
- package/dist/component/validation.d.ts.map +1 -0
- package/dist/component/validation.js +1052 -0
- package/dist/component/validation.js.map +1 -0
- package/dist/component/validators.d.ts +4645 -0
- package/dist/component/validators.d.ts.map +1 -0
- package/dist/component/validators.js +641 -0
- package/dist/component/validators.js.map +1 -0
- package/dist/component/versionMutations.d.ts +216 -0
- package/dist/component/versionMutations.d.ts.map +1 -0
- package/dist/component/versionMutations.js +321 -0
- package/dist/component/versionMutations.js.map +1 -0
- package/dist/component/webhookTrigger.d.ts +770 -0
- package/dist/component/webhookTrigger.d.ts.map +1 -0
- package/dist/component/webhookTrigger.js +1413 -0
- package/dist/component/webhookTrigger.js.map +1 -0
- package/dist/react/index.d.ts +316 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +558 -0
- package/dist/react/index.js.map +1 -0
- package/dist/test.d.ts +2230 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +1107 -0
- package/dist/test.js.map +1 -0
- package/package.json +95 -0
- package/src/cli/commands/admin.ts +104 -0
- package/src/cli/index.ts +21 -0
- package/src/cli/utils/detectConvexUrl.ts +54 -0
- package/src/cli/utils/openBrowser.ts +16 -0
- package/src/client/admin-config.ts +138 -0
- package/src/client/adminApi.ts +942 -0
- package/src/client/agentTools.ts +1311 -0
- package/src/client/argTypes.ts +316 -0
- package/src/client/field-types.ts +187 -0
- package/src/client/index.ts +1301 -0
- package/src/client/queryBuilder.ts +1100 -0
- package/src/client/schema/codegen.ts +500 -0
- package/src/client/schema/defineContentType.ts +501 -0
- package/src/client/schema/index.ts +169 -0
- package/src/client/schema/schemaDrift.ts +574 -0
- package/src/client/schema/typedClient.ts +688 -0
- package/src/client/schema/types.ts +666 -0
- package/src/client/types.ts +723 -0
- package/src/client/workflows.ts +141 -0
- package/src/client/wrapper.ts +4304 -0
- package/src/component/_generated/api.ts +140 -0
- package/src/component/_generated/component.ts +5029 -0
- package/src/component/_generated/dataModel.ts +60 -0
- package/src/component/_generated/server.ts +156 -0
- package/src/component/authorization.ts +647 -0
- package/src/component/authorizationHooks.ts +668 -0
- package/src/component/bulkOperations.ts +687 -0
- package/src/component/contentEntries.ts +1976 -0
- package/src/component/contentEntryMutations.ts +1223 -0
- package/src/component/contentEntryValidation.ts +707 -0
- package/src/component/contentLock.ts +550 -0
- package/src/component/contentTypeMigration.ts +1064 -0
- package/src/component/contentTypeMutations.ts +969 -0
- package/src/component/contentTypes.ts +346 -0
- package/src/component/convex.config.ts +44 -0
- package/src/component/documentTypes.ts +240 -0
- package/src/component/eventEmitter.ts +485 -0
- package/src/component/exportImport.ts +1169 -0
- package/src/component/index.ts +491 -0
- package/src/component/lib/deepReferenceResolver.ts +999 -0
- package/src/component/lib/errors.ts +816 -0
- package/src/component/lib/index.ts +145 -0
- package/src/component/lib/mediaReferenceResolver.ts +495 -0
- package/src/component/lib/metadataExtractor.ts +792 -0
- package/src/component/lib/mutationAuth.ts +199 -0
- package/src/component/lib/queries.ts +79 -0
- package/src/component/lib/ragContentChunker.ts +1371 -0
- package/src/component/lib/referenceResolver.ts +430 -0
- package/src/component/lib/slugGenerator.ts +262 -0
- package/src/component/lib/slugUniqueness.ts +333 -0
- package/src/component/lib/softDelete.ts +44 -0
- package/src/component/localeFallbackChain.ts +673 -0
- package/src/component/localeFields.ts +896 -0
- package/src/component/mediaAssetMutations.ts +725 -0
- package/src/component/mediaAssets.ts +932 -0
- package/src/component/mediaFolderMutations.ts +1046 -0
- package/src/component/mediaUploadMutations.ts +224 -0
- package/src/component/mediaVariantMutations.ts +900 -0
- package/src/component/mediaVariants.ts +793 -0
- package/src/component/ragContentIndexer.ts +1067 -0
- package/src/component/rateLimitHooks.ts +572 -0
- package/src/component/roles.ts +1360 -0
- package/src/component/scheduledPublish.ts +358 -0
- package/src/component/schema.ts +617 -0
- package/src/component/taxonomies.ts +949 -0
- package/src/component/taxonomyMutations.ts +1210 -0
- package/src/component/trash.ts +724 -0
- package/src/component/userContext.ts +898 -0
- package/src/component/validation.ts +1388 -0
- package/src/component/validators.ts +949 -0
- package/src/component/versionMutations.ts +392 -0
- package/src/component/webhookTrigger.ts +1922 -0
- package/src/react/index.ts +898 -0
- package/src/test.ts +1580 -0
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authorization Module
|
|
3
|
+
*
|
|
4
|
+
* Core permission checking logic that evaluates user roles against requested
|
|
5
|
+
* actions and resources. This module is called internally by all CMS operations
|
|
6
|
+
* to enforce access control.
|
|
7
|
+
*
|
|
8
|
+
* Key concepts:
|
|
9
|
+
* - Users are mapped to roles via the getUserRole hook (configured in ComponentConfig)
|
|
10
|
+
* - Roles have permissions that define what actions can be performed on resources
|
|
11
|
+
* - Permissions can be scoped to "all" (any resource) or "own" (resources created by the user)
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { requirePermission, checkPermission, UnauthorizedError } from './authorization';
|
|
16
|
+
*
|
|
17
|
+
* // In a mutation handler:
|
|
18
|
+
* const userRole = await getUserRole(userId);
|
|
19
|
+
* await requirePermission({
|
|
20
|
+
* userId,
|
|
21
|
+
* role: userRole,
|
|
22
|
+
* resource: 'contentEntries',
|
|
23
|
+
* action: 'update',
|
|
24
|
+
* resourceOwnerId: entry.createdBy,
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* // Throws UnauthorizedError if permission denied
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
import { hasPermission, getRole, } from "./roles.js";
|
|
31
|
+
/**
|
|
32
|
+
* Error thrown when a user lacks permission to perform an action.
|
|
33
|
+
*
|
|
34
|
+
* Includes detailed context about the failed authorization check,
|
|
35
|
+
* making it easy to understand why access was denied.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* try {
|
|
40
|
+
* await requirePermission({ ... });
|
|
41
|
+
* } catch (error) {
|
|
42
|
+
* if (error instanceof UnauthorizedError) {
|
|
43
|
+
* console.log(error.code); // 'PERMISSION_DENIED'
|
|
44
|
+
* console.log(error.resource); // 'contentEntries'
|
|
45
|
+
* console.log(error.action); // 'delete'
|
|
46
|
+
* console.log(error.message); // Human-readable message
|
|
47
|
+
* }
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export class UnauthorizedError extends Error {
|
|
52
|
+
/** Machine-readable error code for classification */
|
|
53
|
+
code;
|
|
54
|
+
/** The resource being accessed (if applicable) */
|
|
55
|
+
resource;
|
|
56
|
+
/** The action being attempted (if applicable) */
|
|
57
|
+
action;
|
|
58
|
+
/** The user's role (if known) */
|
|
59
|
+
role;
|
|
60
|
+
/** The user ID (if provided) */
|
|
61
|
+
userId;
|
|
62
|
+
/** The scope that was required but not granted */
|
|
63
|
+
requiredScope;
|
|
64
|
+
constructor(message, options) {
|
|
65
|
+
super(message);
|
|
66
|
+
this.name = "UnauthorizedError";
|
|
67
|
+
this.code = options.code;
|
|
68
|
+
this.resource = options.resource;
|
|
69
|
+
this.action = options.action;
|
|
70
|
+
this.role = options.role;
|
|
71
|
+
this.userId = options.userId;
|
|
72
|
+
this.requiredScope = options.requiredScope;
|
|
73
|
+
// Maintains proper stack trace in V8 environments
|
|
74
|
+
if (Error.captureStackTrace) {
|
|
75
|
+
Error.captureStackTrace(this, UnauthorizedError);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Create a JSON-serializable representation of the error.
|
|
80
|
+
* Useful for logging or API responses.
|
|
81
|
+
*/
|
|
82
|
+
toJSON() {
|
|
83
|
+
return {
|
|
84
|
+
name: this.name,
|
|
85
|
+
message: this.message,
|
|
86
|
+
code: this.code,
|
|
87
|
+
resource: this.resource,
|
|
88
|
+
action: this.action,
|
|
89
|
+
role: this.role,
|
|
90
|
+
userId: this.userId,
|
|
91
|
+
requiredScope: this.requiredScope,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Get a human-readable label for a resource.
|
|
97
|
+
*/
|
|
98
|
+
function getResourceLabel(resource) {
|
|
99
|
+
const labels = {
|
|
100
|
+
contentTypes: "content types",
|
|
101
|
+
contentEntries: "content entries",
|
|
102
|
+
mediaItems: "media items",
|
|
103
|
+
settings: "settings",
|
|
104
|
+
};
|
|
105
|
+
return labels[resource] ?? resource;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Get a human-readable label for an action.
|
|
109
|
+
*/
|
|
110
|
+
function getActionLabel(action) {
|
|
111
|
+
const labels = {
|
|
112
|
+
create: "create",
|
|
113
|
+
read: "view",
|
|
114
|
+
update: "update",
|
|
115
|
+
delete: "delete",
|
|
116
|
+
publish: "publish",
|
|
117
|
+
unpublish: "unpublish",
|
|
118
|
+
restore: "restore",
|
|
119
|
+
manage: "manage",
|
|
120
|
+
move: "move",
|
|
121
|
+
};
|
|
122
|
+
return labels[action] ?? action;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Generate a descriptive error message for a permission denial.
|
|
126
|
+
*/
|
|
127
|
+
function generateDenialMessage(options) {
|
|
128
|
+
const { code, role, resource, action, requiredScope: _requiredScope } = options;
|
|
129
|
+
const resourceLabel = getResourceLabel(resource);
|
|
130
|
+
const actionLabel = getActionLabel(action);
|
|
131
|
+
switch (code) {
|
|
132
|
+
case "NO_ROLE":
|
|
133
|
+
return `Access denied: No role assigned. You need a valid role to ${actionLabel} ${resourceLabel}.`;
|
|
134
|
+
case "UNKNOWN_ROLE":
|
|
135
|
+
return `Access denied: Unknown role "${role}". Contact an administrator to fix your role assignment.`;
|
|
136
|
+
case "PERMISSION_DENIED":
|
|
137
|
+
return `Access denied: The "${role}" role does not have permission to ${actionLabel} ${resourceLabel}.`;
|
|
138
|
+
case "OWNERSHIP_REQUIRED":
|
|
139
|
+
return `Access denied: The "${role}" role can only ${actionLabel} their own ${resourceLabel}. ` +
|
|
140
|
+
`You can only perform this action on items you created.`;
|
|
141
|
+
case "INVALID_RESOURCE":
|
|
142
|
+
return `Invalid resource type: "${resource}". This is a system error.`;
|
|
143
|
+
case "INVALID_ACTION":
|
|
144
|
+
return `Invalid action type: "${action}". This is a system error.`;
|
|
145
|
+
default:
|
|
146
|
+
return `Access denied: Cannot ${actionLabel} ${resourceLabel}.`;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Check if a user has permission to perform an action on a resource.
|
|
151
|
+
*
|
|
152
|
+
* This is the core permission evaluation function. It returns a result object
|
|
153
|
+
* indicating whether the permission was granted or denied, with details about
|
|
154
|
+
* why.
|
|
155
|
+
*
|
|
156
|
+
* The function checks permissions in the following order:
|
|
157
|
+
* 1. Validates that the user has a role assigned
|
|
158
|
+
* 2. Validates that the role exists (in default or custom roles)
|
|
159
|
+
* 3. Checks if the role has the required permission
|
|
160
|
+
* 4. For "own" scope permissions, validates ownership if resourceOwnerId is provided
|
|
161
|
+
*
|
|
162
|
+
* @param options - The permission check configuration
|
|
163
|
+
* @returns Result indicating whether permission was granted or denied
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```typescript
|
|
167
|
+
* // Check if an editor can update any content entry
|
|
168
|
+
* const result = checkPermission({
|
|
169
|
+
* role: 'editor',
|
|
170
|
+
* resource: 'contentEntries',
|
|
171
|
+
* action: 'update',
|
|
172
|
+
* });
|
|
173
|
+
* if (result.allowed) {
|
|
174
|
+
* console.log('Permission granted with scope:', result.grantedScope);
|
|
175
|
+
* }
|
|
176
|
+
*
|
|
177
|
+
* // Check if an author can update their own content entry
|
|
178
|
+
* const result = checkPermission({
|
|
179
|
+
* userId: 'user123',
|
|
180
|
+
* role: 'author',
|
|
181
|
+
* resource: 'contentEntries',
|
|
182
|
+
* action: 'update',
|
|
183
|
+
* resourceOwnerId: 'user123', // Same as userId - ownership verified
|
|
184
|
+
* });
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
export function checkPermission(options) {
|
|
188
|
+
const { userId, role, resource, action, resourceOwnerId, customRoles } = options;
|
|
189
|
+
// Check 1: User must have a role assigned
|
|
190
|
+
if (role === null || role === undefined) {
|
|
191
|
+
return {
|
|
192
|
+
allowed: false,
|
|
193
|
+
reason: "No role assigned to user",
|
|
194
|
+
code: "NO_ROLE",
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
// Check 2: Role must exist
|
|
198
|
+
const roleDefinition = getRole(role, customRoles);
|
|
199
|
+
if (!roleDefinition) {
|
|
200
|
+
return {
|
|
201
|
+
allowed: false,
|
|
202
|
+
reason: `Unknown role: ${role}`,
|
|
203
|
+
code: "UNKNOWN_ROLE",
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
// Check 3: Check if role has permission with "all" scope
|
|
207
|
+
if (hasPermission(role, { resource, action, scope: "all" }, customRoles)) {
|
|
208
|
+
return {
|
|
209
|
+
allowed: true,
|
|
210
|
+
grantedScope: "all",
|
|
211
|
+
ownershipVerified: false,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
// Check 4: Check if role has permission with "own" scope
|
|
215
|
+
if (hasPermission(role, { resource, action, scope: "own" }, customRoles)) {
|
|
216
|
+
// If no resourceOwnerId provided, we cannot verify ownership - deny access
|
|
217
|
+
// for defense-in-depth (callers must always provide resourceOwnerId for
|
|
218
|
+
// ownership-scoped operations)
|
|
219
|
+
if (resourceOwnerId === undefined) {
|
|
220
|
+
return {
|
|
221
|
+
allowed: false,
|
|
222
|
+
reason: "Ownership cannot be verified: resourceOwnerId not provided",
|
|
223
|
+
code: "OWNERSHIP_REQUIRED",
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
// Verify ownership: user must own the resource
|
|
227
|
+
if (userId !== undefined && resourceOwnerId === userId) {
|
|
228
|
+
return {
|
|
229
|
+
allowed: true,
|
|
230
|
+
grantedScope: "own",
|
|
231
|
+
ownershipVerified: true,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
// User has "own" permission but doesn't own this resource
|
|
235
|
+
return {
|
|
236
|
+
allowed: false,
|
|
237
|
+
reason: `Ownership required: user does not own this resource`,
|
|
238
|
+
code: "OWNERSHIP_REQUIRED",
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
// No matching permission found
|
|
242
|
+
return {
|
|
243
|
+
allowed: false,
|
|
244
|
+
reason: `Role "${role}" does not have ${action} permission on ${resource}`,
|
|
245
|
+
code: "PERMISSION_DENIED",
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Require that a user has permission to perform an action.
|
|
250
|
+
*
|
|
251
|
+
* This is the throwing version of `checkPermission`. It's designed to be used
|
|
252
|
+
* at the start of mutation/query handlers to enforce access control. If the
|
|
253
|
+
* permission check fails, it throws an UnauthorizedError with a descriptive
|
|
254
|
+
* message.
|
|
255
|
+
*
|
|
256
|
+
* @param options - The permission check configuration
|
|
257
|
+
* @returns The granted permission details (if allowed)
|
|
258
|
+
* @throws UnauthorizedError if the permission is denied
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* ```typescript
|
|
262
|
+
* // In a content entry update mutation:
|
|
263
|
+
* export const updateEntry = mutation({
|
|
264
|
+
* args: { id: v.id("contentEntries"), data: v.any() },
|
|
265
|
+
* handler: async (ctx, { id, data }) => {
|
|
266
|
+
* const entry = await ctx.db.get(id);
|
|
267
|
+
* if (!entry) throw new Error("Entry not found");
|
|
268
|
+
*
|
|
269
|
+
* // Check authorization before proceeding
|
|
270
|
+
* const userRole = await getUserRole(ctx.auth.userId);
|
|
271
|
+
* await requirePermission({
|
|
272
|
+
* userId: ctx.auth.userId,
|
|
273
|
+
* role: userRole,
|
|
274
|
+
* resource: 'contentEntries',
|
|
275
|
+
* action: 'update',
|
|
276
|
+
* resourceOwnerId: entry.createdBy,
|
|
277
|
+
* });
|
|
278
|
+
*
|
|
279
|
+
* // If we get here, the user is authorized
|
|
280
|
+
* await ctx.db.patch(id, data);
|
|
281
|
+
* },
|
|
282
|
+
* });
|
|
283
|
+
* ```
|
|
284
|
+
*/
|
|
285
|
+
export function requirePermission(options) {
|
|
286
|
+
const result = checkPermission(options);
|
|
287
|
+
if (!result.allowed) {
|
|
288
|
+
// Type narrowing: result is PermissionDenied when allowed is false
|
|
289
|
+
const denied = result;
|
|
290
|
+
throw new UnauthorizedError(generateDenialMessage({
|
|
291
|
+
code: denied.code,
|
|
292
|
+
role: options.role,
|
|
293
|
+
resource: options.resource,
|
|
294
|
+
action: options.action,
|
|
295
|
+
}), {
|
|
296
|
+
code: denied.code,
|
|
297
|
+
resource: options.resource,
|
|
298
|
+
action: options.action,
|
|
299
|
+
role: options.role ?? undefined,
|
|
300
|
+
userId: options.userId,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
return result;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Check if a user owns a resource.
|
|
307
|
+
*
|
|
308
|
+
* This is a simple helper for ownership checks without full permission validation.
|
|
309
|
+
* Use this when you've already verified the permission and just need to check
|
|
310
|
+
* ownership for scope enforcement.
|
|
311
|
+
*
|
|
312
|
+
* @param userId - The ID of the user performing the action
|
|
313
|
+
* @param resourceOwnerId - The ID of the user who created/owns the resource
|
|
314
|
+
* @returns True if the user owns the resource
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* ```typescript
|
|
318
|
+
* // Check ownership before allowing a delete
|
|
319
|
+
* if (!isResourceOwner(currentUserId, entry.createdBy)) {
|
|
320
|
+
* throw new UnauthorizedError(
|
|
321
|
+
* 'You can only delete your own content entries',
|
|
322
|
+
* { code: 'OWNERSHIP_REQUIRED', resource: 'contentEntries', action: 'delete' }
|
|
323
|
+
* );
|
|
324
|
+
* }
|
|
325
|
+
* ```
|
|
326
|
+
*/
|
|
327
|
+
export function isResourceOwner(userId, resourceOwnerId) {
|
|
328
|
+
// Both must be defined and equal for ownership to be verified
|
|
329
|
+
if (userId === undefined || resourceOwnerId === undefined) {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
return userId === resourceOwnerId;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Require that a user owns a resource.
|
|
336
|
+
*
|
|
337
|
+
* Throws an UnauthorizedError if the user doesn't own the resource.
|
|
338
|
+
* Use this when you need to enforce "own" scope on a resource.
|
|
339
|
+
*
|
|
340
|
+
* @param userId - The ID of the user performing the action
|
|
341
|
+
* @param resourceOwnerId - The ID of the user who created/owns the resource
|
|
342
|
+
* @param options - Additional context for the error message
|
|
343
|
+
* @throws UnauthorizedError if the user doesn't own the resource
|
|
344
|
+
*
|
|
345
|
+
* @example
|
|
346
|
+
* ```typescript
|
|
347
|
+
* // Require ownership before allowing update
|
|
348
|
+
* requireResourceOwnership(currentUserId, entry.createdBy, {
|
|
349
|
+
* resource: 'contentEntries',
|
|
350
|
+
* action: 'update',
|
|
351
|
+
* role: userRole,
|
|
352
|
+
* });
|
|
353
|
+
* ```
|
|
354
|
+
*/
|
|
355
|
+
export function requireResourceOwnership(userId, resourceOwnerId, options) {
|
|
356
|
+
if (!isResourceOwner(userId, resourceOwnerId)) {
|
|
357
|
+
throw new UnauthorizedError(generateDenialMessage({
|
|
358
|
+
code: "OWNERSHIP_REQUIRED",
|
|
359
|
+
role: options.role ?? null,
|
|
360
|
+
resource: options.resource,
|
|
361
|
+
action: options.action,
|
|
362
|
+
}), {
|
|
363
|
+
code: "OWNERSHIP_REQUIRED",
|
|
364
|
+
resource: options.resource,
|
|
365
|
+
action: options.action,
|
|
366
|
+
role: options.role,
|
|
367
|
+
userId: userId,
|
|
368
|
+
requiredScope: "own",
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Create an authorization context for a user.
|
|
374
|
+
*
|
|
375
|
+
* This is a convenience function for building the context object used by
|
|
376
|
+
* authorization functions. It validates that the user has a role assigned.
|
|
377
|
+
*
|
|
378
|
+
* @param userId - The user's ID
|
|
379
|
+
* @param role - The user's role (from getUserRole hook)
|
|
380
|
+
* @param customRoles - Optional custom role definitions
|
|
381
|
+
* @returns Authorization context for permission checks
|
|
382
|
+
* @throws UnauthorizedError if the user has no role assigned
|
|
383
|
+
*
|
|
384
|
+
* @example
|
|
385
|
+
* ```typescript
|
|
386
|
+
* // At the start of a mutation handler:
|
|
387
|
+
* const userRole = await getUserRole({ userId });
|
|
388
|
+
* const authCtx = createAuthContext(userId, userRole);
|
|
389
|
+
*
|
|
390
|
+
* // Later, check permissions with the context:
|
|
391
|
+
* requirePermission({
|
|
392
|
+
* ...authCtx,
|
|
393
|
+
* resource: 'contentEntries',
|
|
394
|
+
* action: 'create',
|
|
395
|
+
* });
|
|
396
|
+
* ```
|
|
397
|
+
*/
|
|
398
|
+
export function createAuthContext(userId, role, customRoles) {
|
|
399
|
+
if (role === null) {
|
|
400
|
+
throw new UnauthorizedError("No CMS role assigned. Contact an administrator to get access.", {
|
|
401
|
+
code: "NO_ROLE",
|
|
402
|
+
userId,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
const roleDefinition = getRole(role, customRoles);
|
|
406
|
+
if (!roleDefinition) {
|
|
407
|
+
throw new UnauthorizedError(`Unknown role "${role}". Contact an administrator to fix your role assignment.`, {
|
|
408
|
+
code: "UNKNOWN_ROLE",
|
|
409
|
+
role,
|
|
410
|
+
userId,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
return {
|
|
414
|
+
userId,
|
|
415
|
+
role,
|
|
416
|
+
customRoles,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Check if a user can perform an action using an authorization context.
|
|
421
|
+
*
|
|
422
|
+
* This is a convenience wrapper around checkPermission that uses a pre-built
|
|
423
|
+
* authorization context.
|
|
424
|
+
*
|
|
425
|
+
* @param authCtx - The authorization context
|
|
426
|
+
* @param resource - The resource type being accessed
|
|
427
|
+
* @param action - The action being performed
|
|
428
|
+
* @param resourceOwnerId - Optional owner ID for ownership validation
|
|
429
|
+
* @returns Permission check result
|
|
430
|
+
*/
|
|
431
|
+
export function canPerform(authCtx, resource, action, resourceOwnerId) {
|
|
432
|
+
return checkPermission({
|
|
433
|
+
userId: authCtx.userId,
|
|
434
|
+
role: authCtx.role,
|
|
435
|
+
resource,
|
|
436
|
+
action,
|
|
437
|
+
resourceOwnerId,
|
|
438
|
+
customRoles: authCtx.customRoles,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Require that a user can perform an action using an authorization context.
|
|
443
|
+
*
|
|
444
|
+
* This is a convenience wrapper around requirePermission that uses a pre-built
|
|
445
|
+
* authorization context.
|
|
446
|
+
*
|
|
447
|
+
* @param authCtx - The authorization context
|
|
448
|
+
* @param resource - The resource type being accessed
|
|
449
|
+
* @param action - The action being performed
|
|
450
|
+
* @param resourceOwnerId - Optional owner ID for ownership validation
|
|
451
|
+
* @returns The granted permission details
|
|
452
|
+
* @throws UnauthorizedError if permission is denied
|
|
453
|
+
*/
|
|
454
|
+
export function mustPerform(authCtx, resource, action, resourceOwnerId) {
|
|
455
|
+
return requirePermission({
|
|
456
|
+
userId: authCtx.userId,
|
|
457
|
+
role: authCtx.role,
|
|
458
|
+
resource,
|
|
459
|
+
action,
|
|
460
|
+
resourceOwnerId,
|
|
461
|
+
customRoles: authCtx.customRoles,
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
//# sourceMappingURL=authorization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorization.js","sourceRoot":"","sources":["../../src/component/authorization.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authorization Hooks Execution Module
|
|
3
|
+
*
|
|
4
|
+
* This module provides the infrastructure for executing authorization hooks
|
|
5
|
+
* that enable custom permission logic beyond the built-in RBAC system.
|
|
6
|
+
*
|
|
7
|
+
* Authorization hooks allow parent applications to:
|
|
8
|
+
* - Implement custom authorization logic (team-based, subscription-based, etc.)
|
|
9
|
+
* - Add additional restrictions beyond RBAC
|
|
10
|
+
* - Override RBAC decisions in special cases
|
|
11
|
+
* - Log and audit authorization decisions
|
|
12
|
+
*
|
|
13
|
+
* Hook execution order:
|
|
14
|
+
* 1. beforeRbac hook (can reject early or skip RBAC)
|
|
15
|
+
* 2. Built-in RBAC permission check (if not skipped)
|
|
16
|
+
* 3. afterRbac hook (additional restrictions)
|
|
17
|
+
* 4. Operation-specific hook (fine-grained control)
|
|
18
|
+
* 5. onDeny hook (if denied, can override)
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* import { executeAuthorizationHooks } from './authorizationHooks';
|
|
23
|
+
*
|
|
24
|
+
* // In a mutation handler
|
|
25
|
+
* const authResult = await executeAuthorizationHooks({
|
|
26
|
+
* hooks: config.authorizationHooks,
|
|
27
|
+
* context: {
|
|
28
|
+
* operation: 'contentEntries.create',
|
|
29
|
+
* userId: args.createdBy,
|
|
30
|
+
* role: userRole,
|
|
31
|
+
* contentTypeId: args.contentTypeId,
|
|
32
|
+
* operationData: args,
|
|
33
|
+
* },
|
|
34
|
+
* rbacCheck: () => checkPermission({ role: userRole, ... }),
|
|
35
|
+
* skipRbac: config.skipRbac,
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* if (!authResult.allowed) {
|
|
39
|
+
* throw new UnauthorizedError(authResult.reason ?? 'Access denied', { ... });
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
import type { AuthorizationHooks, AuthorizationHookContext, CmsOperation } from "../client/types.js";
|
|
44
|
+
import { type PermissionCheckOptions, type PermissionCheckResult } from "./authorization.js";
|
|
45
|
+
import type { Resource, Action, RoleDefinition } from "./roles.js";
|
|
46
|
+
/**
|
|
47
|
+
* Options for executing the authorization hook chain.
|
|
48
|
+
*/
|
|
49
|
+
export interface ExecuteAuthorizationOptions {
|
|
50
|
+
/**
|
|
51
|
+
* The authorization hooks configuration from ComponentConfig.
|
|
52
|
+
*/
|
|
53
|
+
hooks?: AuthorizationHooks;
|
|
54
|
+
/**
|
|
55
|
+
* The context for this authorization check.
|
|
56
|
+
*/
|
|
57
|
+
context: AuthorizationHookContext;
|
|
58
|
+
/**
|
|
59
|
+
* Options for the built-in RBAC permission check.
|
|
60
|
+
* If not provided, RBAC is skipped.
|
|
61
|
+
*/
|
|
62
|
+
rbacOptions?: PermissionCheckOptions;
|
|
63
|
+
/**
|
|
64
|
+
* Whether to skip built-in RBAC checks entirely.
|
|
65
|
+
* From ComponentConfig.skipRbac
|
|
66
|
+
*/
|
|
67
|
+
skipRbac?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Custom role definitions to use for RBAC checks.
|
|
70
|
+
*/
|
|
71
|
+
customRoles?: Record<string, RoleDefinition>;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Result from executing the authorization hook chain.
|
|
75
|
+
*/
|
|
76
|
+
export interface AuthorizationResult {
|
|
77
|
+
/**
|
|
78
|
+
* Whether the operation is allowed.
|
|
79
|
+
*/
|
|
80
|
+
allowed: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* The reason for denial (if denied).
|
|
83
|
+
*/
|
|
84
|
+
reason?: string;
|
|
85
|
+
/**
|
|
86
|
+
* Modified operation data from hooks (if any).
|
|
87
|
+
*/
|
|
88
|
+
modifiedData?: Record<string, unknown>;
|
|
89
|
+
/**
|
|
90
|
+
* Which check denied the operation.
|
|
91
|
+
*/
|
|
92
|
+
deniedBy?: "beforeRbac" | "rbac" | "authorize" | "afterRbac" | "operationHook";
|
|
93
|
+
/**
|
|
94
|
+
* The RBAC check result (if RBAC was run).
|
|
95
|
+
*/
|
|
96
|
+
rbacResult?: PermissionCheckResult;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Maps a CMS operation to an RBAC resource and action.
|
|
100
|
+
*/
|
|
101
|
+
export declare function operationToRbac(operation: CmsOperation): {
|
|
102
|
+
resource: Resource;
|
|
103
|
+
action: Action;
|
|
104
|
+
} | null;
|
|
105
|
+
/**
|
|
106
|
+
* Execute the full authorization hook chain for an operation.
|
|
107
|
+
*
|
|
108
|
+
* This function orchestrates the execution of all authorization hooks and the
|
|
109
|
+
* built-in RBAC check in the correct order:
|
|
110
|
+
*
|
|
111
|
+
* 1. **beforeRbac hook**: Can reject early or skip RBAC
|
|
112
|
+
* 2. **Built-in RBAC**: Standard role-based permission check
|
|
113
|
+
* 3. **authorize hook**: Receives RBAC decision, can override allow/deny
|
|
114
|
+
* 4. **afterRbac hook**: Additional restrictions after authorize passes
|
|
115
|
+
* 5. **Operation hook**: Operation-specific restrictions
|
|
116
|
+
* 6. **onDeny hook**: Can override denials
|
|
117
|
+
*
|
|
118
|
+
* @param options - Configuration for the authorization execution
|
|
119
|
+
* @returns AuthorizationResult indicating if the operation is allowed
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```typescript
|
|
123
|
+
* const result = await executeAuthorizationHooks({
|
|
124
|
+
* hooks: config.authorizationHooks,
|
|
125
|
+
* context: {
|
|
126
|
+
* operation: 'contentEntries.publish',
|
|
127
|
+
* userId: currentUser,
|
|
128
|
+
* role: 'editor',
|
|
129
|
+
* resourceId: entryId,
|
|
130
|
+
* resourceOwnerId: entry.createdBy,
|
|
131
|
+
* },
|
|
132
|
+
* rbacOptions: {
|
|
133
|
+
* role: 'editor',
|
|
134
|
+
* resource: 'contentEntries',
|
|
135
|
+
* action: 'publish',
|
|
136
|
+
* userId: currentUser,
|
|
137
|
+
* resourceOwnerId: entry.createdBy,
|
|
138
|
+
* },
|
|
139
|
+
* });
|
|
140
|
+
*
|
|
141
|
+
* if (!result.allowed) {
|
|
142
|
+
* throw new Error(result.reason ?? 'Operation not allowed');
|
|
143
|
+
* }
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
export declare function executeAuthorizationHooks(options: ExecuteAuthorizationOptions): Promise<AuthorizationResult>;
|
|
147
|
+
/**
|
|
148
|
+
* Create an authorization context for a content entry operation.
|
|
149
|
+
*
|
|
150
|
+
* @param operation - The CMS operation being performed
|
|
151
|
+
* @param userId - The user performing the operation
|
|
152
|
+
* @param role - The user's CMS role
|
|
153
|
+
* @param entry - The content entry (if available)
|
|
154
|
+
* @param contentType - The content type (if available)
|
|
155
|
+
* @param operationData - Additional operation data (args)
|
|
156
|
+
* @returns AuthorizationHookContext
|
|
157
|
+
*/
|
|
158
|
+
export declare function createContentEntryAuthContext(operation: CmsOperation, userId: string | undefined, role: string | null | undefined, entry?: {
|
|
159
|
+
_id: unknown;
|
|
160
|
+
createdBy?: string;
|
|
161
|
+
contentTypeId: unknown;
|
|
162
|
+
}, contentType?: {
|
|
163
|
+
_id: unknown;
|
|
164
|
+
name: string;
|
|
165
|
+
}, operationData?: Record<string, unknown>): Omit<AuthorizationHookContext, "ctx">;
|
|
166
|
+
/**
|
|
167
|
+
* Create RBAC options from an authorization context.
|
|
168
|
+
*
|
|
169
|
+
* @param context - The authorization context
|
|
170
|
+
* @returns PermissionCheckOptions for the RBAC check
|
|
171
|
+
*/
|
|
172
|
+
export declare function contextToRbacOptions(context: AuthorizationHookContext): PermissionCheckOptions | null;
|
|
173
|
+
/**
|
|
174
|
+
* Execute authorization for an operation and throw if denied.
|
|
175
|
+
*
|
|
176
|
+
* This is a convenience wrapper that executes hooks and throws
|
|
177
|
+
* UnauthorizedError if the operation is not allowed.
|
|
178
|
+
*
|
|
179
|
+
* @param options - Authorization execution options
|
|
180
|
+
* @throws UnauthorizedError if the operation is denied
|
|
181
|
+
* @returns The authorization result (if allowed)
|
|
182
|
+
*/
|
|
183
|
+
export declare function requireAuthorization(options: ExecuteAuthorizationOptions): Promise<AuthorizationResult>;
|
|
184
|
+
//# sourceMappingURL=authorizationHooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorizationHooks.d.ts","sourceRoot":"","sources":["../../src/component/authorizationHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,wBAAwB,EAGxB,YAAY,EACb,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAEL,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAE3B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAkBnE;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAE3B;;OAEG;IACH,OAAO,EAAE,wBAAwB,CAAC;IAElC;;;OAGG;IACH,WAAW,CAAC,EAAE,sBAAsB,CAAC;IAErC;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CAC9C;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEvC;;OAEG;IACH,QAAQ,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,WAAW,GAAG,WAAW,GAAG,eAAe,CAAC;IAE/E;;OAEG;IACH,UAAU,CAAC,EAAE,qBAAqB,CAAC;CACpC;AAMD;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,YAAY,GAAG;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA+BtG;AAkED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,mBAAmB,CAAC,CA2R9B;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,YAAY,EACvB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/B,KAAK,CAAC,EAAE;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,OAAO,CAAA;CAAE,EACpE,WAAW,CAAC,EAAE;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAC5C,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACtC,IAAI,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAWvC;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,wBAAwB,GAChC,sBAAsB,GAAG,IAAI,CAa/B;AAED;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,mBAAmB,CAAC,CA2B9B"}
|