convex-cms 0.0.2 → 0.0.3
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/admin-dist/nitro.json +15 -0
- package/admin-dist/public/assets/CmsEmptyState-CRswfTzk.js +5 -0
- package/admin-dist/public/assets/CmsPageHeader-CirpXndm.js +1 -0
- package/admin-dist/public/assets/CmsStatusBadge-CbEUpQu-.js +1 -0
- package/admin-dist/public/assets/CmsToolbar-BI2nZOXp.js +1 -0
- package/admin-dist/public/assets/ContentEntryEditor-CBeCyK_m.js +4 -0
- package/admin-dist/public/assets/ErrorState-BIVaWmom.js +1 -0
- package/admin-dist/public/assets/TaxonomyFilter-ChaY6Y_x.js +1 -0
- package/admin-dist/public/assets/_contentTypeId-DQ8k_Rvw.js +1 -0
- package/admin-dist/public/assets/_entryId-CKU_glsK.js +1 -0
- package/admin-dist/public/assets/alert-BXjTqrwQ.js +1 -0
- package/admin-dist/public/assets/badge-hvUOzpVZ.js +1 -0
- package/admin-dist/public/assets/circle-check-big-CF_pR17r.js +1 -0
- package/admin-dist/public/assets/command-DU82cJlt.js +1 -0
- package/admin-dist/public/assets/content-_LXl3pp7.js +1 -0
- package/admin-dist/public/assets/content-types-KjxaXGxY.js +2 -0
- package/admin-dist/public/assets/globals-CS6BZ0zp.css +1 -0
- package/admin-dist/public/assets/index-DNGIZHL-.js +1 -0
- package/admin-dist/public/assets/label-KNtpL71g.js +1 -0
- package/admin-dist/public/assets/link-2-Bw2aI4V4.js +1 -0
- package/admin-dist/public/assets/list-sYepHjt_.js +1 -0
- package/admin-dist/public/assets/main-CKj5yfEi.js +97 -0
- package/admin-dist/public/assets/media-Bkrkffm7.js +1 -0
- package/admin-dist/public/assets/new._contentTypeId-C3LstjNs.js +1 -0
- package/admin-dist/public/assets/plus-DUn8v_Xf.js +1 -0
- package/admin-dist/public/assets/rotate-ccw-DJEoHcRI.js +1 -0
- package/admin-dist/public/assets/scroll-area-DfIlT0in.js +1 -0
- package/admin-dist/public/assets/search-MuAUDJKR.js +1 -0
- package/admin-dist/public/assets/select-BD29IXCI.js +1 -0
- package/admin-dist/public/assets/settings-DmMyn_6A.js +1 -0
- package/admin-dist/public/assets/switch-h3Rrnl5i.js +1 -0
- package/admin-dist/public/assets/tabs-imc8h-Dp.js +1 -0
- package/admin-dist/public/assets/taxonomies-dAsrT65H.js +1 -0
- package/admin-dist/public/assets/textarea-BTy7nwzR.js +1 -0
- package/admin-dist/public/assets/trash-SAWKZZHv.js +1 -0
- package/admin-dist/public/assets/triangle-alert-E52Vfeuh.js +1 -0
- package/admin-dist/public/assets/useBreadcrumbLabel-BECBMCzM.js +1 -0
- package/admin-dist/public/assets/usePermissions-Basjs9BT.js +1 -0
- package/admin-dist/public/favicon.ico +0 -0
- package/admin-dist/server/_chunks/_libs/@date-fns/tz.mjs +217 -0
- package/admin-dist/server/_chunks/_libs/@floating-ui/core.mjs +719 -0
- package/admin-dist/server/_chunks/_libs/@floating-ui/dom.mjs +622 -0
- package/admin-dist/server/_chunks/_libs/@floating-ui/react-dom.mjs +292 -0
- package/admin-dist/server/_chunks/_libs/@floating-ui/utils.mjs +320 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/number.mjs +6 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/primitive.mjs +11 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-arrow.mjs +23 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-avatar.mjs +119 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-checkbox.mjs +270 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-collection.mjs +69 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-compose-refs.mjs +39 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-context.mjs +137 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-dialog.mjs +325 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-direction.mjs +9 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-dismissable-layer.mjs +210 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-dropdown-menu.mjs +253 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-focus-guards.mjs +29 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-focus-scope.mjs +206 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-id.mjs +14 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-label.mjs +23 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-menu.mjs +812 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-popover.mjs +300 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-popper.mjs +286 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-portal.mjs +16 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-presence.mjs +128 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-primitive.mjs +141 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-roving-focus.mjs +224 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-scroll-area.mjs +721 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-select.mjs +1163 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-separator.mjs +28 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-slot.mjs +601 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-switch.mjs +152 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-tabs.mjs +189 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-callback-ref.mjs +11 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-controllable-state.mjs +69 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-effect-event.mjs +1 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-escape-keydown.mjs +17 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-is-hydrated.mjs +15 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-layout-effect.mjs +6 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-previous.mjs +14 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-size.mjs +39 -0
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-visually-hidden.mjs +33 -0
- package/admin-dist/server/_chunks/_libs/@tanstack/history.mjs +409 -0
- package/admin-dist/server/_chunks/_libs/@tanstack/react-router.mjs +1711 -0
- package/admin-dist/server/_chunks/_libs/@tanstack/react-store.mjs +56 -0
- package/admin-dist/server/_chunks/_libs/@tanstack/router-core.mjs +4829 -0
- package/admin-dist/server/_chunks/_libs/@tanstack/store.mjs +134 -0
- package/admin-dist/server/_chunks/_libs/react-dom.mjs +10781 -0
- package/admin-dist/server/_chunks/_libs/react.mjs +513 -0
- package/admin-dist/server/_libs/aria-hidden.mjs +122 -0
- package/admin-dist/server/_libs/class-variance-authority.mjs +44 -0
- package/admin-dist/server/_libs/clsx.mjs +16 -0
- package/admin-dist/server/_libs/cmdk.mjs +315 -0
- package/admin-dist/server/_libs/convex.mjs +4841 -0
- package/admin-dist/server/_libs/cookie-es.mjs +58 -0
- package/admin-dist/server/_libs/croner.mjs +1 -0
- package/admin-dist/server/_libs/crossws.mjs +1 -0
- package/admin-dist/server/_libs/date-fns.mjs +1716 -0
- package/admin-dist/server/_libs/detect-node-es.mjs +1 -0
- package/admin-dist/server/_libs/get-nonce.mjs +9 -0
- package/admin-dist/server/_libs/h3-v2.mjs +277 -0
- package/admin-dist/server/_libs/h3.mjs +401 -0
- package/admin-dist/server/_libs/hookable.mjs +1 -0
- package/admin-dist/server/_libs/isbot.mjs +20 -0
- package/admin-dist/server/_libs/lucide-react.mjs +850 -0
- package/admin-dist/server/_libs/ohash.mjs +1 -0
- package/admin-dist/server/_libs/react-day-picker.mjs +2201 -0
- package/admin-dist/server/_libs/react-remove-scroll-bar.mjs +82 -0
- package/admin-dist/server/_libs/react-remove-scroll.mjs +328 -0
- package/admin-dist/server/_libs/react-style-singleton.mjs +69 -0
- package/admin-dist/server/_libs/rou3.mjs +8 -0
- package/admin-dist/server/_libs/seroval-plugins.mjs +58 -0
- package/admin-dist/server/_libs/seroval.mjs +1765 -0
- package/admin-dist/server/_libs/srvx.mjs +719 -0
- package/admin-dist/server/_libs/tailwind-merge.mjs +3010 -0
- package/admin-dist/server/_libs/tiny-invariant.mjs +12 -0
- package/admin-dist/server/_libs/tiny-warning.mjs +5 -0
- package/admin-dist/server/_libs/tslib.mjs +39 -0
- package/admin-dist/server/_libs/ufo.mjs +54 -0
- package/admin-dist/server/_libs/unctx.mjs +1 -0
- package/admin-dist/server/_libs/unstorage.mjs +1 -0
- package/admin-dist/server/_libs/use-callback-ref.mjs +66 -0
- package/admin-dist/server/_libs/use-sidecar.mjs +106 -0
- package/admin-dist/server/_libs/use-sync-external-store.mjs +139 -0
- package/admin-dist/server/_libs/zod.mjs +4223 -0
- package/admin-dist/server/_ssr/CmsEmptyState-DU7-7-mV.mjs +290 -0
- package/admin-dist/server/_ssr/CmsPageHeader-CseW0AHm.mjs +24 -0
- package/admin-dist/server/_ssr/CmsStatusBadge-B_pi4KCp.mjs +127 -0
- package/admin-dist/server/_ssr/CmsToolbar-X75ex6ek.mjs +49 -0
- package/admin-dist/server/_ssr/ContentEntryEditor-CepusRsA.mjs +3720 -0
- package/admin-dist/server/_ssr/ErrorState-cI-bKLez.mjs +89 -0
- package/admin-dist/server/_ssr/TaxonomyFilter-Bwrq0-cz.mjs +188 -0
- package/admin-dist/server/_ssr/_contentTypeId-BqYKEcLr.mjs +379 -0
- package/admin-dist/server/_ssr/_entryId-CRfnqeDf.mjs +161 -0
- package/admin-dist/server/_ssr/_tanstack-start-manifest_v-BwDlABVk.mjs +4 -0
- package/admin-dist/server/_ssr/alert-CVt45UUP.mjs +92 -0
- package/admin-dist/server/_ssr/badge-6BsP37vG.mjs +125 -0
- package/admin-dist/server/_ssr/command-fy8epIKf.mjs +128 -0
- package/admin-dist/server/_ssr/config.server-D7JHDcDv.mjs +117 -0
- package/admin-dist/server/_ssr/content-B5RhL7uW.mjs +532 -0
- package/admin-dist/server/_ssr/content-types-BIOqCQYN.mjs +1166 -0
- package/admin-dist/server/_ssr/index-DHSHDPt1.mjs +193 -0
- package/admin-dist/server/_ssr/index.mjs +1275 -0
- package/admin-dist/server/_ssr/label-C8Dko1j7.mjs +22 -0
- package/admin-dist/server/_ssr/media-CSx3XttC.mjs +1832 -0
- package/admin-dist/server/_ssr/new._contentTypeId-DzanEZQM.mjs +144 -0
- package/admin-dist/server/_ssr/router-DDWcF-kt.mjs +1556 -0
- package/admin-dist/server/_ssr/scroll-area-bjPYwhXN.mjs +59 -0
- package/admin-dist/server/_ssr/select-BUhDDf4T.mjs +142 -0
- package/admin-dist/server/_ssr/settings-DAsxnw2q.mjs +348 -0
- package/admin-dist/server/_ssr/start-HYkvq4Ni.mjs +4 -0
- package/admin-dist/server/_ssr/switch-BgyRtQ1Z.mjs +31 -0
- package/admin-dist/server/_ssr/tabs-DzMdRB1A.mjs +628 -0
- package/admin-dist/server/_ssr/taxonomies-C8j8g5Q5.mjs +915 -0
- package/admin-dist/server/_ssr/textarea-9jNeYJSc.mjs +18 -0
- package/admin-dist/server/_ssr/trash-DYMxwhZB.mjs +291 -0
- package/admin-dist/server/_ssr/useBreadcrumbLabel-FNSAr2Ha.mjs +16 -0
- package/admin-dist/server/_ssr/usePermissions-BJGGahrJ.mjs +68 -0
- package/admin-dist/server/favicon.ico +0 -0
- package/admin-dist/server/index.mjs +627 -0
- package/dist/cli/index.js +0 -0
- package/dist/client/admin-config.d.ts +0 -1
- package/dist/client/admin-config.d.ts.map +1 -1
- package/dist/client/admin-config.js +0 -1
- package/dist/client/admin-config.js.map +1 -1
- package/dist/client/adminApi.d.ts.map +1 -1
- package/dist/client/agentTools.d.ts +1237 -135
- package/dist/client/agentTools.d.ts.map +1 -1
- package/dist/client/agentTools.js +33 -9
- package/dist/client/agentTools.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/component.d.ts +9 -0
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/mediaAssets.d.ts +35 -0
- package/dist/component/mediaAssets.d.ts.map +1 -1
- package/dist/component/mediaAssets.js +81 -0
- package/dist/component/mediaAssets.js.map +1 -1
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +2 -1
- package/dist/test.js.map +1 -1
- package/package.json +9 -5
- package/dist/component/auditLog.d.ts +0 -410
- package/dist/component/auditLog.d.ts.map +0 -1
- package/dist/component/auditLog.js +0 -607
- package/dist/component/auditLog.js.map +0 -1
- package/dist/component/types.d.ts +0 -4
- package/dist/component/types.d.ts.map +0 -1
- package/dist/component/types.js +0 -2
- package/dist/component/types.js.map +0 -1
- package/src/cli/commands/admin.ts +0 -104
- package/src/cli/index.ts +0 -21
- package/src/cli/utils/detectConvexUrl.ts +0 -54
- package/src/cli/utils/openBrowser.ts +0 -16
- package/src/client/admin-config.ts +0 -138
- package/src/client/adminApi.ts +0 -942
- package/src/client/agentTools.ts +0 -1311
- package/src/client/argTypes.ts +0 -316
- package/src/client/field-types.ts +0 -187
- package/src/client/index.ts +0 -1301
- package/src/client/queryBuilder.ts +0 -1100
- package/src/client/schema/codegen.ts +0 -500
- package/src/client/schema/defineContentType.ts +0 -501
- package/src/client/schema/index.ts +0 -169
- package/src/client/schema/schemaDrift.ts +0 -574
- package/src/client/schema/typedClient.ts +0 -688
- package/src/client/schema/types.ts +0 -666
- package/src/client/types.ts +0 -723
- package/src/client/workflows.ts +0 -141
- package/src/client/wrapper.ts +0 -4304
- package/src/component/_generated/api.ts +0 -140
- package/src/component/_generated/component.ts +0 -5029
- package/src/component/_generated/dataModel.ts +0 -60
- package/src/component/_generated/server.ts +0 -156
- package/src/component/authorization.ts +0 -647
- package/src/component/authorizationHooks.ts +0 -668
- package/src/component/bulkOperations.ts +0 -687
- package/src/component/contentEntries.ts +0 -1976
- package/src/component/contentEntryMutations.ts +0 -1223
- package/src/component/contentEntryValidation.ts +0 -707
- package/src/component/contentLock.ts +0 -550
- package/src/component/contentTypeMigration.ts +0 -1064
- package/src/component/contentTypeMutations.ts +0 -969
- package/src/component/contentTypes.ts +0 -346
- package/src/component/convex.config.ts +0 -44
- package/src/component/documentTypes.ts +0 -240
- package/src/component/eventEmitter.ts +0 -485
- package/src/component/exportImport.ts +0 -1169
- package/src/component/index.ts +0 -491
- package/src/component/lib/deepReferenceResolver.ts +0 -999
- package/src/component/lib/errors.ts +0 -816
- package/src/component/lib/index.ts +0 -145
- package/src/component/lib/mediaReferenceResolver.ts +0 -495
- package/src/component/lib/metadataExtractor.ts +0 -792
- package/src/component/lib/mutationAuth.ts +0 -199
- package/src/component/lib/queries.ts +0 -79
- package/src/component/lib/ragContentChunker.ts +0 -1371
- package/src/component/lib/referenceResolver.ts +0 -430
- package/src/component/lib/slugGenerator.ts +0 -262
- package/src/component/lib/slugUniqueness.ts +0 -333
- package/src/component/lib/softDelete.ts +0 -44
- package/src/component/localeFallbackChain.ts +0 -673
- package/src/component/localeFields.ts +0 -896
- package/src/component/mediaAssetMutations.ts +0 -725
- package/src/component/mediaAssets.ts +0 -932
- package/src/component/mediaFolderMutations.ts +0 -1046
- package/src/component/mediaUploadMutations.ts +0 -224
- package/src/component/mediaVariantMutations.ts +0 -900
- package/src/component/mediaVariants.ts +0 -793
- package/src/component/ragContentIndexer.ts +0 -1067
- package/src/component/rateLimitHooks.ts +0 -572
- package/src/component/roles.ts +0 -1360
- package/src/component/scheduledPublish.ts +0 -358
- package/src/component/schema.ts +0 -617
- package/src/component/taxonomies.ts +0 -949
- package/src/component/taxonomyMutations.ts +0 -1210
- package/src/component/trash.ts +0 -724
- package/src/component/userContext.ts +0 -898
- package/src/component/validation.ts +0 -1388
- package/src/component/validators.ts +0 -949
- package/src/component/versionMutations.ts +0 -392
- package/src/component/webhookTrigger.ts +0 -1922
- package/src/react/index.ts +0 -898
- package/src/test.ts +0 -1580
|
@@ -1,647 +0,0 @@
|
|
|
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
|
-
|
|
31
|
-
import {
|
|
32
|
-
hasPermission,
|
|
33
|
-
getRole,
|
|
34
|
-
type Resource,
|
|
35
|
-
type Action,
|
|
36
|
-
type OwnershipScope,
|
|
37
|
-
type RoleDefinition,
|
|
38
|
-
} from "./roles.js";
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Error codes for authorization failures.
|
|
42
|
-
* These provide machine-readable error classification.
|
|
43
|
-
*/
|
|
44
|
-
export type AuthorizationErrorCode =
|
|
45
|
-
| "NO_ROLE"
|
|
46
|
-
| "UNKNOWN_ROLE"
|
|
47
|
-
| "PERMISSION_DENIED"
|
|
48
|
-
| "OWNERSHIP_REQUIRED"
|
|
49
|
-
| "INVALID_RESOURCE"
|
|
50
|
-
| "INVALID_ACTION";
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Error thrown when a user lacks permission to perform an action.
|
|
54
|
-
*
|
|
55
|
-
* Includes detailed context about the failed authorization check,
|
|
56
|
-
* making it easy to understand why access was denied.
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* ```typescript
|
|
60
|
-
* try {
|
|
61
|
-
* await requirePermission({ ... });
|
|
62
|
-
* } catch (error) {
|
|
63
|
-
* if (error instanceof UnauthorizedError) {
|
|
64
|
-
* console.log(error.code); // 'PERMISSION_DENIED'
|
|
65
|
-
* console.log(error.resource); // 'contentEntries'
|
|
66
|
-
* console.log(error.action); // 'delete'
|
|
67
|
-
* console.log(error.message); // Human-readable message
|
|
68
|
-
* }
|
|
69
|
-
* }
|
|
70
|
-
* ```
|
|
71
|
-
*/
|
|
72
|
-
export class UnauthorizedError extends Error {
|
|
73
|
-
/** Machine-readable error code for classification */
|
|
74
|
-
readonly code: AuthorizationErrorCode;
|
|
75
|
-
|
|
76
|
-
/** The resource being accessed (if applicable) */
|
|
77
|
-
readonly resource?: Resource;
|
|
78
|
-
|
|
79
|
-
/** The action being attempted (if applicable) */
|
|
80
|
-
readonly action?: Action;
|
|
81
|
-
|
|
82
|
-
/** The user's role (if known) */
|
|
83
|
-
readonly role?: string;
|
|
84
|
-
|
|
85
|
-
/** The user ID (if provided) */
|
|
86
|
-
readonly userId?: string;
|
|
87
|
-
|
|
88
|
-
/** The scope that was required but not granted */
|
|
89
|
-
readonly requiredScope?: OwnershipScope;
|
|
90
|
-
|
|
91
|
-
constructor(
|
|
92
|
-
message: string,
|
|
93
|
-
options: {
|
|
94
|
-
code: AuthorizationErrorCode;
|
|
95
|
-
resource?: Resource;
|
|
96
|
-
action?: Action;
|
|
97
|
-
role?: string;
|
|
98
|
-
userId?: string;
|
|
99
|
-
requiredScope?: OwnershipScope;
|
|
100
|
-
}
|
|
101
|
-
) {
|
|
102
|
-
super(message);
|
|
103
|
-
this.name = "UnauthorizedError";
|
|
104
|
-
this.code = options.code;
|
|
105
|
-
this.resource = options.resource;
|
|
106
|
-
this.action = options.action;
|
|
107
|
-
this.role = options.role;
|
|
108
|
-
this.userId = options.userId;
|
|
109
|
-
this.requiredScope = options.requiredScope;
|
|
110
|
-
|
|
111
|
-
// Maintains proper stack trace in V8 environments
|
|
112
|
-
if (Error.captureStackTrace) {
|
|
113
|
-
Error.captureStackTrace(this, UnauthorizedError);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Create a JSON-serializable representation of the error.
|
|
119
|
-
* Useful for logging or API responses.
|
|
120
|
-
*/
|
|
121
|
-
toJSON(): Record<string, unknown> {
|
|
122
|
-
return {
|
|
123
|
-
name: this.name,
|
|
124
|
-
message: this.message,
|
|
125
|
-
code: this.code,
|
|
126
|
-
resource: this.resource,
|
|
127
|
-
action: this.action,
|
|
128
|
-
role: this.role,
|
|
129
|
-
userId: this.userId,
|
|
130
|
-
requiredScope: this.requiredScope,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Options for checking a user's permission to perform an action.
|
|
137
|
-
*/
|
|
138
|
-
export interface PermissionCheckOptions {
|
|
139
|
-
/** The user ID performing the action (for error messages and ownership checks) */
|
|
140
|
-
userId?: string;
|
|
141
|
-
|
|
142
|
-
/** The role name to check permissions for (null means no role assigned) */
|
|
143
|
-
role: string | null;
|
|
144
|
-
|
|
145
|
-
/** The resource type being accessed */
|
|
146
|
-
resource: Resource;
|
|
147
|
-
|
|
148
|
-
/** The action being performed on the resource */
|
|
149
|
-
action: Action;
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* The ID of the user who owns the resource.
|
|
153
|
-
* Required when the user's role only has "own" scope permission.
|
|
154
|
-
* If not provided and "own" scope is needed, the check will fail.
|
|
155
|
-
*/
|
|
156
|
-
resourceOwnerId?: string;
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Custom roles to check in addition to default roles.
|
|
160
|
-
* Use this to support organization-specific role definitions.
|
|
161
|
-
*/
|
|
162
|
-
customRoles?: Record<string, RoleDefinition>;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Result of a permission check that passed.
|
|
167
|
-
*/
|
|
168
|
-
export interface PermissionGranted {
|
|
169
|
-
allowed: true;
|
|
170
|
-
/** The scope that was granted (how the permission was satisfied) */
|
|
171
|
-
grantedScope: OwnershipScope;
|
|
172
|
-
/** Whether ownership was verified (true if resourceOwnerId matched userId) */
|
|
173
|
-
ownershipVerified: boolean;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Result of a permission check that failed.
|
|
178
|
-
*/
|
|
179
|
-
export interface PermissionDenied {
|
|
180
|
-
allowed: false;
|
|
181
|
-
/** The reason the permission was denied */
|
|
182
|
-
reason: string;
|
|
183
|
-
/** Machine-readable error code */
|
|
184
|
-
code: AuthorizationErrorCode;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Result of a permission check.
|
|
189
|
-
*/
|
|
190
|
-
export type PermissionCheckResult = PermissionGranted | PermissionDenied;
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Get a human-readable label for a resource.
|
|
194
|
-
*/
|
|
195
|
-
function getResourceLabel(resource: Resource): string {
|
|
196
|
-
const labels: Record<Resource, string> = {
|
|
197
|
-
contentTypes: "content types",
|
|
198
|
-
contentEntries: "content entries",
|
|
199
|
-
mediaItems: "media items",
|
|
200
|
-
settings: "settings",
|
|
201
|
-
};
|
|
202
|
-
return labels[resource] ?? resource;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Get a human-readable label for an action.
|
|
207
|
-
*/
|
|
208
|
-
function getActionLabel(action: Action): string {
|
|
209
|
-
const labels: Record<Action, string> = {
|
|
210
|
-
create: "create",
|
|
211
|
-
read: "view",
|
|
212
|
-
update: "update",
|
|
213
|
-
delete: "delete",
|
|
214
|
-
publish: "publish",
|
|
215
|
-
unpublish: "unpublish",
|
|
216
|
-
restore: "restore",
|
|
217
|
-
manage: "manage",
|
|
218
|
-
move: "move",
|
|
219
|
-
};
|
|
220
|
-
return labels[action] ?? action;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Generate a descriptive error message for a permission denial.
|
|
225
|
-
*/
|
|
226
|
-
function generateDenialMessage(options: {
|
|
227
|
-
code: AuthorizationErrorCode;
|
|
228
|
-
role: string | null;
|
|
229
|
-
resource: Resource;
|
|
230
|
-
action: Action;
|
|
231
|
-
requiredScope?: OwnershipScope;
|
|
232
|
-
}): string {
|
|
233
|
-
const { code, role, resource, action, requiredScope: _requiredScope } = options;
|
|
234
|
-
const resourceLabel = getResourceLabel(resource);
|
|
235
|
-
const actionLabel = getActionLabel(action);
|
|
236
|
-
|
|
237
|
-
switch (code) {
|
|
238
|
-
case "NO_ROLE":
|
|
239
|
-
return `Access denied: No role assigned. You need a valid role to ${actionLabel} ${resourceLabel}.`;
|
|
240
|
-
|
|
241
|
-
case "UNKNOWN_ROLE":
|
|
242
|
-
return `Access denied: Unknown role "${role}". Contact an administrator to fix your role assignment.`;
|
|
243
|
-
|
|
244
|
-
case "PERMISSION_DENIED":
|
|
245
|
-
return `Access denied: The "${role}" role does not have permission to ${actionLabel} ${resourceLabel}.`;
|
|
246
|
-
|
|
247
|
-
case "OWNERSHIP_REQUIRED":
|
|
248
|
-
return `Access denied: The "${role}" role can only ${actionLabel} their own ${resourceLabel}. ` +
|
|
249
|
-
`You can only perform this action on items you created.`;
|
|
250
|
-
|
|
251
|
-
case "INVALID_RESOURCE":
|
|
252
|
-
return `Invalid resource type: "${resource}". This is a system error.`;
|
|
253
|
-
|
|
254
|
-
case "INVALID_ACTION":
|
|
255
|
-
return `Invalid action type: "${action}". This is a system error.`;
|
|
256
|
-
|
|
257
|
-
default:
|
|
258
|
-
return `Access denied: Cannot ${actionLabel} ${resourceLabel}.`;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Check if a user has permission to perform an action on a resource.
|
|
264
|
-
*
|
|
265
|
-
* This is the core permission evaluation function. It returns a result object
|
|
266
|
-
* indicating whether the permission was granted or denied, with details about
|
|
267
|
-
* why.
|
|
268
|
-
*
|
|
269
|
-
* The function checks permissions in the following order:
|
|
270
|
-
* 1. Validates that the user has a role assigned
|
|
271
|
-
* 2. Validates that the role exists (in default or custom roles)
|
|
272
|
-
* 3. Checks if the role has the required permission
|
|
273
|
-
* 4. For "own" scope permissions, validates ownership if resourceOwnerId is provided
|
|
274
|
-
*
|
|
275
|
-
* @param options - The permission check configuration
|
|
276
|
-
* @returns Result indicating whether permission was granted or denied
|
|
277
|
-
*
|
|
278
|
-
* @example
|
|
279
|
-
* ```typescript
|
|
280
|
-
* // Check if an editor can update any content entry
|
|
281
|
-
* const result = checkPermission({
|
|
282
|
-
* role: 'editor',
|
|
283
|
-
* resource: 'contentEntries',
|
|
284
|
-
* action: 'update',
|
|
285
|
-
* });
|
|
286
|
-
* if (result.allowed) {
|
|
287
|
-
* console.log('Permission granted with scope:', result.grantedScope);
|
|
288
|
-
* }
|
|
289
|
-
*
|
|
290
|
-
* // Check if an author can update their own content entry
|
|
291
|
-
* const result = checkPermission({
|
|
292
|
-
* userId: 'user123',
|
|
293
|
-
* role: 'author',
|
|
294
|
-
* resource: 'contentEntries',
|
|
295
|
-
* action: 'update',
|
|
296
|
-
* resourceOwnerId: 'user123', // Same as userId - ownership verified
|
|
297
|
-
* });
|
|
298
|
-
* ```
|
|
299
|
-
*/
|
|
300
|
-
export function checkPermission(
|
|
301
|
-
options: PermissionCheckOptions
|
|
302
|
-
): PermissionCheckResult {
|
|
303
|
-
const { userId, role, resource, action, resourceOwnerId, customRoles } = options;
|
|
304
|
-
|
|
305
|
-
// Check 1: User must have a role assigned
|
|
306
|
-
if (role === null || role === undefined) {
|
|
307
|
-
return {
|
|
308
|
-
allowed: false,
|
|
309
|
-
reason: "No role assigned to user",
|
|
310
|
-
code: "NO_ROLE",
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Check 2: Role must exist
|
|
315
|
-
const roleDefinition = getRole(role, customRoles);
|
|
316
|
-
if (!roleDefinition) {
|
|
317
|
-
return {
|
|
318
|
-
allowed: false,
|
|
319
|
-
reason: `Unknown role: ${role}`,
|
|
320
|
-
code: "UNKNOWN_ROLE",
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Check 3: Check if role has permission with "all" scope
|
|
325
|
-
if (hasPermission(role, { resource, action, scope: "all" }, customRoles)) {
|
|
326
|
-
return {
|
|
327
|
-
allowed: true,
|
|
328
|
-
grantedScope: "all",
|
|
329
|
-
ownershipVerified: false,
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// Check 4: Check if role has permission with "own" scope
|
|
334
|
-
if (hasPermission(role, { resource, action, scope: "own" }, customRoles)) {
|
|
335
|
-
// If no resourceOwnerId provided, we cannot verify ownership - deny access
|
|
336
|
-
// for defense-in-depth (callers must always provide resourceOwnerId for
|
|
337
|
-
// ownership-scoped operations)
|
|
338
|
-
if (resourceOwnerId === undefined) {
|
|
339
|
-
return {
|
|
340
|
-
allowed: false,
|
|
341
|
-
reason: "Ownership cannot be verified: resourceOwnerId not provided",
|
|
342
|
-
code: "OWNERSHIP_REQUIRED",
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// Verify ownership: user must own the resource
|
|
347
|
-
if (userId !== undefined && resourceOwnerId === userId) {
|
|
348
|
-
return {
|
|
349
|
-
allowed: true,
|
|
350
|
-
grantedScope: "own",
|
|
351
|
-
ownershipVerified: true,
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// User has "own" permission but doesn't own this resource
|
|
356
|
-
return {
|
|
357
|
-
allowed: false,
|
|
358
|
-
reason: `Ownership required: user does not own this resource`,
|
|
359
|
-
code: "OWNERSHIP_REQUIRED",
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// No matching permission found
|
|
364
|
-
return {
|
|
365
|
-
allowed: false,
|
|
366
|
-
reason: `Role "${role}" does not have ${action} permission on ${resource}`,
|
|
367
|
-
code: "PERMISSION_DENIED",
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/**
|
|
372
|
-
* Require that a user has permission to perform an action.
|
|
373
|
-
*
|
|
374
|
-
* This is the throwing version of `checkPermission`. It's designed to be used
|
|
375
|
-
* at the start of mutation/query handlers to enforce access control. If the
|
|
376
|
-
* permission check fails, it throws an UnauthorizedError with a descriptive
|
|
377
|
-
* message.
|
|
378
|
-
*
|
|
379
|
-
* @param options - The permission check configuration
|
|
380
|
-
* @returns The granted permission details (if allowed)
|
|
381
|
-
* @throws UnauthorizedError if the permission is denied
|
|
382
|
-
*
|
|
383
|
-
* @example
|
|
384
|
-
* ```typescript
|
|
385
|
-
* // In a content entry update mutation:
|
|
386
|
-
* export const updateEntry = mutation({
|
|
387
|
-
* args: { id: v.id("contentEntries"), data: v.any() },
|
|
388
|
-
* handler: async (ctx, { id, data }) => {
|
|
389
|
-
* const entry = await ctx.db.get(id);
|
|
390
|
-
* if (!entry) throw new Error("Entry not found");
|
|
391
|
-
*
|
|
392
|
-
* // Check authorization before proceeding
|
|
393
|
-
* const userRole = await getUserRole(ctx.auth.userId);
|
|
394
|
-
* await requirePermission({
|
|
395
|
-
* userId: ctx.auth.userId,
|
|
396
|
-
* role: userRole,
|
|
397
|
-
* resource: 'contentEntries',
|
|
398
|
-
* action: 'update',
|
|
399
|
-
* resourceOwnerId: entry.createdBy,
|
|
400
|
-
* });
|
|
401
|
-
*
|
|
402
|
-
* // If we get here, the user is authorized
|
|
403
|
-
* await ctx.db.patch(id, data);
|
|
404
|
-
* },
|
|
405
|
-
* });
|
|
406
|
-
* ```
|
|
407
|
-
*/
|
|
408
|
-
export function requirePermission(
|
|
409
|
-
options: PermissionCheckOptions
|
|
410
|
-
): PermissionGranted {
|
|
411
|
-
const result = checkPermission(options);
|
|
412
|
-
|
|
413
|
-
if (!result.allowed) {
|
|
414
|
-
// Type narrowing: result is PermissionDenied when allowed is false
|
|
415
|
-
const denied = result as PermissionDenied;
|
|
416
|
-
throw new UnauthorizedError(
|
|
417
|
-
generateDenialMessage({
|
|
418
|
-
code: denied.code,
|
|
419
|
-
role: options.role,
|
|
420
|
-
resource: options.resource,
|
|
421
|
-
action: options.action,
|
|
422
|
-
}),
|
|
423
|
-
{
|
|
424
|
-
code: denied.code,
|
|
425
|
-
resource: options.resource,
|
|
426
|
-
action: options.action,
|
|
427
|
-
role: options.role ?? undefined,
|
|
428
|
-
userId: options.userId,
|
|
429
|
-
}
|
|
430
|
-
);
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
return result;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* Check if a user owns a resource.
|
|
438
|
-
*
|
|
439
|
-
* This is a simple helper for ownership checks without full permission validation.
|
|
440
|
-
* Use this when you've already verified the permission and just need to check
|
|
441
|
-
* ownership for scope enforcement.
|
|
442
|
-
*
|
|
443
|
-
* @param userId - The ID of the user performing the action
|
|
444
|
-
* @param resourceOwnerId - The ID of the user who created/owns the resource
|
|
445
|
-
* @returns True if the user owns the resource
|
|
446
|
-
*
|
|
447
|
-
* @example
|
|
448
|
-
* ```typescript
|
|
449
|
-
* // Check ownership before allowing a delete
|
|
450
|
-
* if (!isResourceOwner(currentUserId, entry.createdBy)) {
|
|
451
|
-
* throw new UnauthorizedError(
|
|
452
|
-
* 'You can only delete your own content entries',
|
|
453
|
-
* { code: 'OWNERSHIP_REQUIRED', resource: 'contentEntries', action: 'delete' }
|
|
454
|
-
* );
|
|
455
|
-
* }
|
|
456
|
-
* ```
|
|
457
|
-
*/
|
|
458
|
-
export function isResourceOwner(
|
|
459
|
-
userId: string | undefined,
|
|
460
|
-
resourceOwnerId: string | undefined
|
|
461
|
-
): boolean {
|
|
462
|
-
// Both must be defined and equal for ownership to be verified
|
|
463
|
-
if (userId === undefined || resourceOwnerId === undefined) {
|
|
464
|
-
return false;
|
|
465
|
-
}
|
|
466
|
-
return userId === resourceOwnerId;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
/**
|
|
470
|
-
* Require that a user owns a resource.
|
|
471
|
-
*
|
|
472
|
-
* Throws an UnauthorizedError if the user doesn't own the resource.
|
|
473
|
-
* Use this when you need to enforce "own" scope on a resource.
|
|
474
|
-
*
|
|
475
|
-
* @param userId - The ID of the user performing the action
|
|
476
|
-
* @param resourceOwnerId - The ID of the user who created/owns the resource
|
|
477
|
-
* @param options - Additional context for the error message
|
|
478
|
-
* @throws UnauthorizedError if the user doesn't own the resource
|
|
479
|
-
*
|
|
480
|
-
* @example
|
|
481
|
-
* ```typescript
|
|
482
|
-
* // Require ownership before allowing update
|
|
483
|
-
* requireResourceOwnership(currentUserId, entry.createdBy, {
|
|
484
|
-
* resource: 'contentEntries',
|
|
485
|
-
* action: 'update',
|
|
486
|
-
* role: userRole,
|
|
487
|
-
* });
|
|
488
|
-
* ```
|
|
489
|
-
*/
|
|
490
|
-
export function requireResourceOwnership(
|
|
491
|
-
userId: string | undefined,
|
|
492
|
-
resourceOwnerId: string | undefined,
|
|
493
|
-
options: {
|
|
494
|
-
resource: Resource;
|
|
495
|
-
action: Action;
|
|
496
|
-
role?: string;
|
|
497
|
-
}
|
|
498
|
-
): void {
|
|
499
|
-
if (!isResourceOwner(userId, resourceOwnerId)) {
|
|
500
|
-
throw new UnauthorizedError(
|
|
501
|
-
generateDenialMessage({
|
|
502
|
-
code: "OWNERSHIP_REQUIRED",
|
|
503
|
-
role: options.role ?? null,
|
|
504
|
-
resource: options.resource,
|
|
505
|
-
action: options.action,
|
|
506
|
-
}),
|
|
507
|
-
{
|
|
508
|
-
code: "OWNERSHIP_REQUIRED",
|
|
509
|
-
resource: options.resource,
|
|
510
|
-
action: options.action,
|
|
511
|
-
role: options.role,
|
|
512
|
-
userId: userId,
|
|
513
|
-
requiredScope: "own",
|
|
514
|
-
}
|
|
515
|
-
);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
/**
|
|
520
|
-
* Context for performing authorization checks within a request.
|
|
521
|
-
* This can be built up at the start of a handler and reused for multiple checks.
|
|
522
|
-
*/
|
|
523
|
-
export interface AuthorizationContext {
|
|
524
|
-
/** The user's ID */
|
|
525
|
-
userId: string;
|
|
526
|
-
/** The user's CMS role */
|
|
527
|
-
role: string;
|
|
528
|
-
/** Optional custom roles to check */
|
|
529
|
-
customRoles?: Record<string, RoleDefinition>;
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
/**
|
|
533
|
-
* Create an authorization context for a user.
|
|
534
|
-
*
|
|
535
|
-
* This is a convenience function for building the context object used by
|
|
536
|
-
* authorization functions. It validates that the user has a role assigned.
|
|
537
|
-
*
|
|
538
|
-
* @param userId - The user's ID
|
|
539
|
-
* @param role - The user's role (from getUserRole hook)
|
|
540
|
-
* @param customRoles - Optional custom role definitions
|
|
541
|
-
* @returns Authorization context for permission checks
|
|
542
|
-
* @throws UnauthorizedError if the user has no role assigned
|
|
543
|
-
*
|
|
544
|
-
* @example
|
|
545
|
-
* ```typescript
|
|
546
|
-
* // At the start of a mutation handler:
|
|
547
|
-
* const userRole = await getUserRole({ userId });
|
|
548
|
-
* const authCtx = createAuthContext(userId, userRole);
|
|
549
|
-
*
|
|
550
|
-
* // Later, check permissions with the context:
|
|
551
|
-
* requirePermission({
|
|
552
|
-
* ...authCtx,
|
|
553
|
-
* resource: 'contentEntries',
|
|
554
|
-
* action: 'create',
|
|
555
|
-
* });
|
|
556
|
-
* ```
|
|
557
|
-
*/
|
|
558
|
-
export function createAuthContext(
|
|
559
|
-
userId: string,
|
|
560
|
-
role: string | null,
|
|
561
|
-
customRoles?: Record<string, RoleDefinition>
|
|
562
|
-
): AuthorizationContext {
|
|
563
|
-
if (role === null) {
|
|
564
|
-
throw new UnauthorizedError(
|
|
565
|
-
"No CMS role assigned. Contact an administrator to get access.",
|
|
566
|
-
{
|
|
567
|
-
code: "NO_ROLE",
|
|
568
|
-
userId,
|
|
569
|
-
}
|
|
570
|
-
);
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
const roleDefinition = getRole(role, customRoles);
|
|
574
|
-
if (!roleDefinition) {
|
|
575
|
-
throw new UnauthorizedError(
|
|
576
|
-
`Unknown role "${role}". Contact an administrator to fix your role assignment.`,
|
|
577
|
-
{
|
|
578
|
-
code: "UNKNOWN_ROLE",
|
|
579
|
-
role,
|
|
580
|
-
userId,
|
|
581
|
-
}
|
|
582
|
-
);
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
return {
|
|
586
|
-
userId,
|
|
587
|
-
role,
|
|
588
|
-
customRoles,
|
|
589
|
-
};
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
/**
|
|
593
|
-
* Check if a user can perform an action using an authorization context.
|
|
594
|
-
*
|
|
595
|
-
* This is a convenience wrapper around checkPermission that uses a pre-built
|
|
596
|
-
* authorization context.
|
|
597
|
-
*
|
|
598
|
-
* @param authCtx - The authorization context
|
|
599
|
-
* @param resource - The resource type being accessed
|
|
600
|
-
* @param action - The action being performed
|
|
601
|
-
* @param resourceOwnerId - Optional owner ID for ownership validation
|
|
602
|
-
* @returns Permission check result
|
|
603
|
-
*/
|
|
604
|
-
export function canPerform(
|
|
605
|
-
authCtx: AuthorizationContext,
|
|
606
|
-
resource: Resource,
|
|
607
|
-
action: Action,
|
|
608
|
-
resourceOwnerId?: string
|
|
609
|
-
): PermissionCheckResult {
|
|
610
|
-
return checkPermission({
|
|
611
|
-
userId: authCtx.userId,
|
|
612
|
-
role: authCtx.role,
|
|
613
|
-
resource,
|
|
614
|
-
action,
|
|
615
|
-
resourceOwnerId,
|
|
616
|
-
customRoles: authCtx.customRoles,
|
|
617
|
-
});
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
/**
|
|
621
|
-
* Require that a user can perform an action using an authorization context.
|
|
622
|
-
*
|
|
623
|
-
* This is a convenience wrapper around requirePermission that uses a pre-built
|
|
624
|
-
* authorization context.
|
|
625
|
-
*
|
|
626
|
-
* @param authCtx - The authorization context
|
|
627
|
-
* @param resource - The resource type being accessed
|
|
628
|
-
* @param action - The action being performed
|
|
629
|
-
* @param resourceOwnerId - Optional owner ID for ownership validation
|
|
630
|
-
* @returns The granted permission details
|
|
631
|
-
* @throws UnauthorizedError if permission is denied
|
|
632
|
-
*/
|
|
633
|
-
export function mustPerform(
|
|
634
|
-
authCtx: AuthorizationContext,
|
|
635
|
-
resource: Resource,
|
|
636
|
-
action: Action,
|
|
637
|
-
resourceOwnerId?: string
|
|
638
|
-
): PermissionGranted {
|
|
639
|
-
return requirePermission({
|
|
640
|
-
userId: authCtx.userId,
|
|
641
|
-
role: authCtx.role,
|
|
642
|
-
resource,
|
|
643
|
-
action,
|
|
644
|
-
resourceOwnerId,
|
|
645
|
-
customRoles: authCtx.customRoles,
|
|
646
|
-
});
|
|
647
|
-
}
|