@jmruthers/pace-core 0.6.1 → 0.6.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/CHANGELOG.md +88 -10
- package/cursor-rules/00-pace-core-compliance.mdc +46 -87
- package/cursor-rules/01-standards-compliance.mdc +16 -47
- package/cursor-rules/02-project-structure.mdc +4 -4
- package/cursor-rules/03-solid-principles.mdc +45 -164
- package/cursor-rules/04-testing-standards.mdc +22 -69
- package/cursor-rules/05-bug-reports-and-features.mdc +2 -2
- package/cursor-rules/06-code-quality.mdc +42 -125
- package/cursor-rules/07-tech-stack-compliance.mdc +33 -128
- package/cursor-rules/08-markup-quality.mdc +452 -0
- package/cursor-rules/CHANGELOG.md +18 -0
- package/cursor-rules/README.md +2 -1
- package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-Cb34EQs3.d.ts} +63 -1
- package/dist/{DataTable-CH1U5Tpy.d.ts → DataTable-BMRU8a1j.d.ts} +33 -1
- package/dist/{DataTable-DQ7RSOHE.js → DataTable-THFPBKTP.js} +12 -10
- package/dist/{PublicPageProvider-ce4xlHYA.d.ts → PublicPageProvider-DEMpysFR.d.ts} +394 -171
- package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CKvHP1MK.d.ts} +30 -8
- package/dist/{UnifiedAuthProvider-ATAP5UTR.js → UnifiedAuthProvider-KAGUYQ4J.js} +5 -4
- package/dist/{api-N774RPUA.js → api-IAGWF3ZG.js} +10 -10
- package/dist/{audit-B5P6FFIR.js → audit-V53FV5AG.js} +2 -2
- package/dist/{chunk-JBKQ3SAO.js → chunk-2T2IG7T7.js} +107 -57
- package/dist/chunk-2T2IG7T7.js.map +1 -0
- package/dist/{chunk-3QRJFVBR.js → chunk-6SOIHG6Z.js} +1 -1
- package/dist/chunk-6SOIHG6Z.js.map +1 -0
- package/dist/{chunk-3XTALGJF.js → chunk-6Z7LTB3D.js} +69 -240
- package/dist/chunk-6Z7LTB3D.js.map +1 -0
- package/dist/{chunk-4ZC4GX36.js → chunk-CNCQDFLN.js} +199 -46
- package/dist/chunk-CNCQDFLN.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/{chunk-BYFSK72L.js → chunk-DWUBLJJM.js} +361 -187
- package/dist/chunk-DWUBLJJM.js.map +1 -0
- package/dist/{chunk-LXQLPRQ2.js → chunk-FFQEQTNW.js} +6 -8
- package/dist/chunk-FFQEQTNW.js.map +1 -0
- package/dist/chunk-FMUCXFII.js +76 -0
- package/dist/chunk-FMUCXFII.js.map +1 -0
- package/dist/{chunk-4N5C5XZU.js → chunk-HFZBI76P.js} +4 -4
- package/dist/chunk-HFZBI76P.js.map +1 -0
- package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
- package/dist/chunk-L4OXEN46.js.map +1 -0
- package/dist/{chunk-R77UEZ4E.js → chunk-M43Y4SSO.js} +1 -1
- package/dist/chunk-M43Y4SSO.js.map +1 -0
- package/dist/{chunk-I7PSE6JW.js → chunk-M7MPQISP.js} +3 -76
- package/dist/chunk-M7MPQISP.js.map +1 -0
- package/dist/chunk-PQBSKX33.js +7793 -0
- package/dist/chunk-PQBSKX33.js.map +1 -0
- package/dist/chunk-QRPVRXYT.js +226 -0
- package/dist/chunk-QRPVRXYT.js.map +1 -0
- package/dist/{chunk-KNC55RTG.js → chunk-RWEBCB47.js} +194 -416
- package/dist/chunk-RWEBCB47.js.map +1 -0
- package/dist/{chunk-XM25TVIE.js → chunk-YDQHOZNA.js} +843 -388
- package/dist/chunk-YDQHOZNA.js.map +1 -0
- package/dist/{chunk-GLK6VM3F.js → chunk-ZNIWI3UC.js} +739 -737
- package/dist/chunk-ZNIWI3UC.js.map +1 -0
- package/dist/components.d.ts +5 -5
- package/dist/components.js +18 -16
- package/dist/components.js.map +1 -1
- package/dist/contextValidator-3JNZKUTX.js +9 -0
- package/dist/contextValidator-3JNZKUTX.js.map +1 -0
- package/dist/eslint-rules/pace-core-compliance.cjs +106 -0
- package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
- package/dist/hooks.d.ts +55 -122
- package/dist/hooks.js +10 -13
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +60 -13
- package/dist/index.js +30 -25
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +21 -3
- package/dist/providers.js +4 -3
- package/dist/rbac/index.d.ts +210 -139
- package/dist/rbac/index.js +17 -13
- package/dist/styles/index.js +1 -1
- package/dist/theming/runtime.d.ts +1 -13
- package/dist/theming/runtime.js +2 -2
- package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
- package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
- package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/dist/{usePublicRouteParams-BJAlWfuJ.d.ts → usePublicRouteParams-i3qtoBgg.d.ts} +38 -17
- package/dist/utils.d.ts +4 -5
- package/dist/utils.js +17 -19
- package/dist/utils.js.map +1 -1
- package/docs/api/README.md +21 -17
- package/docs/api/modules.md +4191 -2967
- package/docs/architecture/database-schema-requirements.md +161 -0
- package/docs/components/context-selector.md +126 -0
- package/docs/core-concepts/rbac-system.md +3 -3
- package/docs/documentation-index.md +2 -4
- package/docs/getting-started/cursor-rules.md +2 -1
- package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
- package/docs/migration/MIGRATION_GUIDE.md +2 -24
- package/docs/migration/RBAC_SCOPE_MIGRATION.md +385 -0
- package/docs/migration/README.md +52 -6
- package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
- package/docs/migration/database-changes-december-2025.md +3 -3
- package/docs/pace-mint-fix-auto-selection.md +218 -0
- package/docs/pace-mint-rbac-setup.md +391 -0
- package/docs/rbac/event-based-apps.md +1 -1
- package/docs/rbac/getting-started.md +1 -1
- package/docs/rbac/quick-start.md +1 -1
- package/docs/rbac/secure-client-protection.md +330 -0
- package/docs/standards/README.md +1 -0
- package/package.json +4 -3
- package/scripts/audit/core/checks/accessibility.cjs +197 -0
- package/scripts/audit/core/checks/api-usage.cjs +191 -0
- package/scripts/audit/core/checks/bundle.cjs +142 -0
- package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +784 -685
- package/scripts/audit/core/checks/config.cjs +54 -0
- package/scripts/audit/core/checks/coverage.cjs +84 -0
- package/scripts/audit/core/checks/dependencies.cjs +985 -0
- package/scripts/audit/core/checks/documentation.cjs +268 -0
- package/scripts/audit/core/checks/environment.cjs +116 -0
- package/scripts/audit/core/checks/error-handling.cjs +340 -0
- package/scripts/audit/core/checks/forms.cjs +172 -0
- package/scripts/audit/core/checks/heuristics.cjs +68 -0
- package/scripts/audit/core/checks/hooks.cjs +334 -0
- package/scripts/audit/core/checks/imports.cjs +244 -0
- package/scripts/audit/core/checks/performance.cjs +325 -0
- package/scripts/audit/core/checks/routes.cjs +117 -0
- package/scripts/audit/core/checks/state.cjs +130 -0
- package/scripts/audit/core/checks/structure.cjs +65 -0
- package/scripts/audit/core/checks/style.cjs +584 -0
- package/scripts/audit/core/checks/testing.cjs +122 -0
- package/scripts/audit/core/checks/typescript.cjs +61 -0
- package/scripts/audit/core/scanner.cjs +199 -0
- package/scripts/audit/core/utils.cjs +137 -0
- package/scripts/audit/index.cjs +223 -0
- package/scripts/audit/reporters/console.cjs +151 -0
- package/scripts/audit/reporters/json.cjs +54 -0
- package/scripts/audit/reporters/markdown.cjs +124 -0
- package/scripts/audit-consuming-app.cjs +61 -936
- package/scripts/build-docs/build-decision.js +240 -0
- package/scripts/build-docs/cache-utils.js +105 -0
- package/scripts/build-docs/content-normalization.js +150 -0
- package/scripts/build-docs/file-utils.js +105 -0
- package/scripts/build-docs/git-utils.js +86 -0
- package/scripts/build-docs/hash-utils.js +116 -0
- package/scripts/build-docs/typedoc-runner.js +220 -0
- package/scripts/build-docs-incremental.js +77 -913
- package/scripts/utils/command-runner.js +16 -11
- package/scripts/validate-formats.js +61 -56
- package/scripts/validate-master.js +74 -69
- package/scripts/validate-pre-publish.js +70 -65
- package/src/__tests__/hooks/usePermissions.test.ts +2 -2
- package/src/components/Alert/Alert.test.tsx +12 -18
- package/src/components/Alert/Alert.tsx +5 -7
- package/src/components/Avatar/Avatar.test.tsx +4 -4
- package/src/components/Badge/Badge.tsx +14 -0
- package/src/components/Button/Button.tsx +22 -0
- package/src/components/Calendar/Calendar.tsx +8 -2
- package/src/components/Card/Card.tsx +4 -0
- package/src/components/Checkbox/Checkbox.test.tsx +12 -12
- package/src/components/Checkbox/Checkbox.tsx +2 -2
- package/src/components/ContextSelector/ContextSelector.tsx +384 -0
- package/src/components/ContextSelector/index.ts +3 -0
- package/src/components/DataTable/DataTable.tsx +38 -4
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +18 -4
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
- package/src/components/DataTable/components/AccessDeniedPage.tsx +16 -25
- package/src/components/DataTable/components/ActionButtons.tsx +10 -7
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
- package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
- package/src/components/DataTable/components/DataTableBody.tsx +8 -0
- package/src/components/DataTable/components/DataTableCore.tsx +196 -554
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
- package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
- package/src/components/DataTable/components/DataTableModals.tsx +8 -0
- package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
- package/src/components/DataTable/components/EditFields.tsx +307 -0
- package/src/components/DataTable/components/EditableRow.tsx +8 -0
- package/src/components/DataTable/components/EmptyState.tsx +10 -0
- package/src/components/DataTable/components/FilterRow.tsx +12 -0
- package/src/components/DataTable/components/GroupHeader.tsx +12 -0
- package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
- package/src/components/DataTable/components/ImportModal.tsx +7 -0
- package/src/components/DataTable/components/LoadingState.tsx +6 -0
- package/src/components/DataTable/components/PaginationControls.tsx +16 -1
- package/src/components/DataTable/components/RowComponent.tsx +391 -0
- package/src/components/DataTable/components/UnifiedTableBody.tsx +63 -851
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
- package/src/components/DataTable/components/cellValueUtils.ts +40 -0
- package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
- package/src/components/DataTable/context/DataTableContext.tsx +50 -0
- package/src/components/DataTable/core/ColumnFactory.ts +31 -0
- package/src/components/DataTable/core/DataTableContext.tsx +32 -1
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
- package/src/components/DataTable/hooks/useColumnReordering.ts +12 -0
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
- package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +127 -33
- package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
- package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
- package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
- package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
- package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
- package/src/components/DataTable/styles.ts +6 -6
- package/src/components/DataTable/types.ts +6 -10
- package/src/components/DataTable/utils/a11yUtils.ts +7 -0
- package/src/components/DataTable/utils/debugTools.ts +18 -113
- package/src/components/DataTable/utils/errorHandling.ts +12 -0
- package/src/components/DataTable/utils/exportUtils.ts +9 -0
- package/src/components/DataTable/utils/flexibleImport.ts +12 -48
- package/src/components/DataTable/utils/paginationUtils.ts +8 -0
- package/src/components/DataTable/utils/performanceUtils.ts +5 -1
- package/src/components/Dialog/Dialog.tsx +31 -3
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +45 -5
- package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
- package/src/components/ErrorBoundary/index.ts +27 -2
- package/src/components/FileDisplay/FileDisplay.tsx +74 -28
- package/src/components/FileUpload/FileUpload.tsx +22 -2
- package/src/components/Footer/Footer.test.tsx +16 -16
- package/src/components/Footer/Footer.tsx +14 -11
- package/src/components/Form/Form.tsx +1 -0
- package/src/components/Header/Header.test.tsx +43 -73
- package/src/components/Header/Header.tsx +59 -49
- package/src/components/Input/Input.test.tsx +2 -2
- package/src/components/Input/Input.tsx +8 -4
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
- package/src/components/LoginForm/LoginForm.tsx +4 -0
- package/src/components/NavigationMenu/NavigationMenu.tsx +14 -513
- package/src/components/NavigationMenu/types.ts +56 -0
- package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
- package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +10 -19
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +2 -2
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +5 -5
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +13 -11
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +167 -44
- package/src/components/PaceAppLayout/README.md +14 -17
- package/src/components/PaceAppLayout/test-setup.tsx +3 -4
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +3 -0
- package/src/components/PasswordChange/PasswordChangeForm.tsx +9 -0
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
- package/src/components/PublicLayout/PublicPageLayout.tsx +2 -5
- package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
- package/src/components/Select/Select.tsx +80 -434
- package/src/components/Select/context.ts +23 -0
- package/src/components/Select/hooks/useSelectEvents.ts +87 -0
- package/src/components/Select/hooks/useSelectSearch.ts +91 -0
- package/src/components/Select/hooks/useSelectState.ts +104 -0
- package/src/components/Select/index.ts +9 -1
- package/src/components/Select/types.ts +123 -0
- package/src/components/Select/utils/text.ts +26 -0
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +4 -5
- package/src/components/Switch/Switch.tsx +4 -4
- package/src/components/Tabs/Tabs.tsx +1 -1
- package/src/components/Toast/Toast.tsx +4 -0
- package/src/components/Tooltip/Tooltip.tsx +2 -2
- package/src/components/UserMenu/UserMenu.test.tsx +24 -11
- package/src/components/UserMenu/UserMenu.tsx +21 -18
- package/src/components/index.ts +7 -7
- package/src/eslint-rules/pace-core-compliance.cjs +106 -0
- package/src/hooks/__tests__/index.unit.test.ts +2 -5
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +4 -98
- package/src/hooks/index.ts +1 -2
- package/src/hooks/public/usePublicEvent.ts +4 -0
- package/src/hooks/public/usePublicEventLogo.ts +4 -0
- package/src/hooks/public/usePublicFileDisplay.ts +4 -0
- package/src/hooks/public/usePublicRouteParams.ts +4 -0
- package/src/hooks/services/useAuth.ts +32 -0
- package/src/hooks/services/useCurrentEvent.ts +6 -0
- package/src/hooks/services/useCurrentOrganisation.ts +6 -0
- package/src/hooks/useAppConfig.ts +15 -30
- package/src/hooks/useDebounce.ts +9 -0
- package/src/hooks/useEventTheme.ts +6 -0
- package/src/hooks/useFileDisplay.ts +81 -50
- package/src/hooks/useFileReference.ts +25 -7
- package/src/hooks/useFileUrl.ts +11 -1
- package/src/hooks/useFocusManagement.ts +14 -0
- package/src/hooks/useFocusTrap.ts +3 -0
- package/src/hooks/useInactivityTracker.ts +3 -0
- package/src/hooks/useKeyboardShortcuts.ts +4 -0
- package/src/hooks/useOrganisationPermissions.ts +4 -0
- package/src/hooks/useOrganisationSecurity.ts +4 -0
- package/src/hooks/usePerformanceMonitor.ts +4 -0
- package/src/hooks/usePermissionCache.ts +7 -0
- package/src/hooks/useQueryCache.ts +12 -1
- package/src/hooks/useSessionRestoration.ts +4 -0
- package/src/hooks/useStorage.ts +4 -0
- package/src/hooks/useToast.ts +1 -1
- package/src/index.ts +6 -6
- package/src/providers/__tests__/OrganisationProvider.test.tsx +92 -70
- package/src/providers/services/AuthServiceProvider.tsx +35 -7
- package/src/providers/services/EventServiceProvider.tsx +51 -5
- package/src/providers/services/InactivityServiceProvider.tsx +18 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
- package/src/providers/services/UnifiedAuthProvider.tsx +126 -134
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +29 -13
- package/src/rbac/README.md +1 -1
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +1 -1
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
- package/src/rbac/adapters.tsx +12 -3
- package/src/rbac/api.test.ts +59 -51
- package/src/rbac/api.ts +246 -167
- package/src/rbac/components/NavigationProvider.tsx +4 -1
- package/src/rbac/components/PagePermissionGuard.tsx +185 -17
- package/src/rbac/components/RoleBasedRouter.tsx +5 -1
- package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
- package/src/rbac/components/SecureDataProvider.tsx +20 -5
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
- package/src/rbac/engine.ts +38 -14
- package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +32 -21
- package/src/rbac/hooks/permissions/index.ts +7 -0
- package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
- package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
- package/src/rbac/hooks/permissions/useCan.ts +377 -0
- package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
- package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
- package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
- package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
- package/src/rbac/hooks/useCan.test.ts +64 -66
- package/src/rbac/hooks/usePermissions.ts +14 -995
- package/src/rbac/hooks/useRBAC.test.ts +1 -5
- package/src/rbac/hooks/useRBAC.ts +36 -37
- package/src/rbac/hooks/useResolvedScope.test.ts +120 -35
- package/src/rbac/hooks/useResolvedScope.ts +35 -40
- package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
- package/src/rbac/hooks/useResourcePermissions.ts +14 -4
- package/src/rbac/hooks/useSecureSupabase.ts +27 -7
- package/src/rbac/index.ts +7 -0
- package/src/rbac/permissions.ts +0 -30
- package/src/rbac/secureClient.test.ts +22 -18
- package/src/rbac/secureClient.ts +294 -68
- package/src/rbac/security.ts +0 -17
- package/src/rbac/types.ts +9 -0
- package/src/rbac/utils/__tests__/contextValidator.test.ts +64 -86
- package/src/rbac/utils/clientSecurity.ts +93 -0
- package/src/rbac/utils/contextValidator.ts +77 -168
- package/src/services/AuthService.ts +39 -7
- package/src/services/EventService.ts +186 -54
- package/src/services/OrganisationService.ts +81 -14
- package/src/services/__tests__/EventService.test.ts +1 -2
- package/src/services/base/BaseService.ts +3 -0
- package/src/theming/__tests__/parseEventColours.test.ts +6 -9
- package/src/theming/parseEventColours.ts +5 -19
- package/src/types/vitest-globals.d.ts +51 -26
- package/src/utils/__mocks__/supabaseMock.ts +1 -3
- package/src/utils/__tests__/formatting.unit.test.ts +4 -4
- package/src/utils/__tests__/index.unit.test.ts +2 -2
- package/src/utils/audit/audit.ts +0 -3
- package/src/utils/core/cn.ts +1 -1
- package/src/utils/dynamic/dynamicUtils.ts +7 -4
- package/src/utils/file-reference/index.ts +53 -1
- package/src/utils/formatting/formatting.ts +8 -18
- package/src/utils/index.ts +0 -1
- package/dist/chunk-3QRJFVBR.js.map +0 -1
- package/dist/chunk-3XTALGJF.js.map +0 -1
- package/dist/chunk-4N5C5XZU.js.map +0 -1
- package/dist/chunk-4ZC4GX36.js.map +0 -1
- package/dist/chunk-7D4SUZUM.js +0 -38
- package/dist/chunk-BYFSK72L.js.map +0 -1
- package/dist/chunk-EXUD6RNJ.js +0 -451
- package/dist/chunk-EXUD6RNJ.js.map +0 -1
- package/dist/chunk-GLK6VM3F.js.map +0 -1
- package/dist/chunk-I7PSE6JW.js.map +0 -1
- package/dist/chunk-JBKQ3SAO.js.map +0 -1
- package/dist/chunk-KNC55RTG.js.map +0 -1
- package/dist/chunk-LXQLPRQ2.js.map +0 -1
- package/dist/chunk-R77UEZ4E.js.map +0 -1
- package/dist/chunk-SQGMNID3.js.map +0 -1
- package/dist/chunk-T33XF5ZC.js +0 -12922
- package/dist/chunk-T33XF5ZC.js.map +0 -1
- package/dist/chunk-XM25TVIE.js.map +0 -1
- package/docs/api/classes/ColumnFactory.md +0 -243
- package/docs/api/classes/ErrorBoundary.md +0 -144
- package/docs/api/classes/InvalidScopeError.md +0 -73
- package/docs/api/classes/Logger.md +0 -178
- package/docs/api/classes/MissingUserContextError.md +0 -66
- package/docs/api/classes/OrganisationContextRequiredError.md +0 -66
- package/docs/api/classes/PermissionDeniedError.md +0 -73
- package/docs/api/classes/RBACAuditManager.md +0 -297
- package/docs/api/classes/RBACCache.md +0 -322
- package/docs/api/classes/RBACEngine.md +0 -171
- package/docs/api/classes/RBACError.md +0 -76
- package/docs/api/classes/RBACNotInitializedError.md +0 -66
- package/docs/api/classes/SecureSupabaseClient.md +0 -160
- package/docs/api/classes/StorageUtils.md +0 -328
- package/docs/api/enums/FileCategory.md +0 -184
- package/docs/api/enums/LogLevel.md +0 -54
- package/docs/api/enums/RBACErrorCode.md +0 -228
- package/docs/api/enums/RPCFunction.md +0 -118
- package/docs/api/interfaces/AddressFieldProps.md +0 -241
- package/docs/api/interfaces/AddressFieldRef.md +0 -94
- package/docs/api/interfaces/AggregateConfig.md +0 -43
- package/docs/api/interfaces/AutocompleteOptions.md +0 -75
- package/docs/api/interfaces/AvatarProps.md +0 -128
- package/docs/api/interfaces/BadgeProps.md +0 -27
- package/docs/api/interfaces/ButtonProps.md +0 -53
- package/docs/api/interfaces/CalendarProps.md +0 -70
- package/docs/api/interfaces/CardProps.md +0 -66
- package/docs/api/interfaces/ColorPalette.md +0 -7
- package/docs/api/interfaces/ColorShade.md +0 -66
- package/docs/api/interfaces/ComplianceResult.md +0 -30
- package/docs/api/interfaces/DataAccessRecord.md +0 -96
- package/docs/api/interfaces/DataRecord.md +0 -11
- package/docs/api/interfaces/DataTableAction.md +0 -249
- package/docs/api/interfaces/DataTableColumn.md +0 -504
- package/docs/api/interfaces/DataTableProps.md +0 -625
- package/docs/api/interfaces/DataTableToolbarButton.md +0 -96
- package/docs/api/interfaces/DatabaseComplianceResult.md +0 -85
- package/docs/api/interfaces/DatabaseIssue.md +0 -41
- package/docs/api/interfaces/EmptyStateConfig.md +0 -61
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +0 -235
- package/docs/api/interfaces/EventAppRoleData.md +0 -71
- package/docs/api/interfaces/ExportColumn.md +0 -90
- package/docs/api/interfaces/ExportOptions.md +0 -126
- package/docs/api/interfaces/FileDisplayProps.md +0 -249
- package/docs/api/interfaces/FileMetadata.md +0 -129
- package/docs/api/interfaces/FileReference.md +0 -118
- package/docs/api/interfaces/FileSizeLimits.md +0 -7
- package/docs/api/interfaces/FileUploadOptions.md +0 -139
- package/docs/api/interfaces/FileUploadProps.md +0 -293
- package/docs/api/interfaces/FooterProps.md +0 -105
- package/docs/api/interfaces/FormFieldProps.md +0 -166
- package/docs/api/interfaces/FormProps.md +0 -113
- package/docs/api/interfaces/GrantEventAppRoleParams.md +0 -122
- package/docs/api/interfaces/InactivityWarningModalProps.md +0 -115
- package/docs/api/interfaces/InputProps.md +0 -53
- package/docs/api/interfaces/LabelProps.md +0 -107
- package/docs/api/interfaces/LoggerConfig.md +0 -62
- package/docs/api/interfaces/LoginFormProps.md +0 -184
- package/docs/api/interfaces/NavigationAccessRecord.md +0 -107
- package/docs/api/interfaces/NavigationContextType.md +0 -164
- package/docs/api/interfaces/NavigationGuardProps.md +0 -139
- package/docs/api/interfaces/NavigationItem.md +0 -120
- package/docs/api/interfaces/NavigationMenuProps.md +0 -221
- package/docs/api/interfaces/NavigationProviderProps.md +0 -117
- package/docs/api/interfaces/Organisation.md +0 -140
- package/docs/api/interfaces/OrganisationContextType.md +0 -388
- package/docs/api/interfaces/OrganisationMembership.md +0 -140
- package/docs/api/interfaces/OrganisationProviderProps.md +0 -76
- package/docs/api/interfaces/OrganisationSecurityError.md +0 -62
- package/docs/api/interfaces/PaceAppLayoutProps.md +0 -406
- package/docs/api/interfaces/PaceLoginPageProps.md +0 -47
- package/docs/api/interfaces/PageAccessRecord.md +0 -85
- package/docs/api/interfaces/PagePermissionContextType.md +0 -140
- package/docs/api/interfaces/PagePermissionGuardProps.md +0 -153
- package/docs/api/interfaces/PagePermissionProviderProps.md +0 -119
- package/docs/api/interfaces/PaletteData.md +0 -41
- package/docs/api/interfaces/ParsedAddress.md +0 -120
- package/docs/api/interfaces/PermissionEnforcerProps.md +0 -153
- package/docs/api/interfaces/ProgressProps.md +0 -42
- package/docs/api/interfaces/ProtectedRouteProps.md +0 -97
- package/docs/api/interfaces/PublicPageFooterProps.md +0 -112
- package/docs/api/interfaces/PublicPageHeaderProps.md +0 -125
- package/docs/api/interfaces/PublicPageLayoutProps.md +0 -198
- package/docs/api/interfaces/QuickFix.md +0 -52
- package/docs/api/interfaces/RBACAccessValidateParams.md +0 -52
- package/docs/api/interfaces/RBACAccessValidateResult.md +0 -41
- package/docs/api/interfaces/RBACAuditLogParams.md +0 -85
- package/docs/api/interfaces/RBACAuditLogResult.md +0 -52
- package/docs/api/interfaces/RBACConfig.md +0 -133
- package/docs/api/interfaces/RBACContext.md +0 -52
- package/docs/api/interfaces/RBACLogger.md +0 -112
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +0 -74
- package/docs/api/interfaces/RBACPerformanceMetrics.md +0 -138
- package/docs/api/interfaces/RBACPermissionCheckParams.md +0 -74
- package/docs/api/interfaces/RBACPermissionCheckResult.md +0 -52
- package/docs/api/interfaces/RBACPermissionsGetParams.md +0 -63
- package/docs/api/interfaces/RBACPermissionsGetResult.md +0 -63
- package/docs/api/interfaces/RBACResult.md +0 -58
- package/docs/api/interfaces/RBACRoleGrantParams.md +0 -63
- package/docs/api/interfaces/RBACRoleGrantResult.md +0 -52
- package/docs/api/interfaces/RBACRoleRevokeParams.md +0 -63
- package/docs/api/interfaces/RBACRoleRevokeResult.md +0 -52
- package/docs/api/interfaces/RBACRoleValidateParams.md +0 -52
- package/docs/api/interfaces/RBACRoleValidateResult.md +0 -63
- package/docs/api/interfaces/RBACRolesListParams.md +0 -52
- package/docs/api/interfaces/RBACRolesListResult.md +0 -74
- package/docs/api/interfaces/RBACSessionTrackParams.md +0 -74
- package/docs/api/interfaces/RBACSessionTrackResult.md +0 -52
- package/docs/api/interfaces/ResourcePermissions.md +0 -155
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +0 -100
- package/docs/api/interfaces/RoleBasedRouterContextType.md +0 -151
- package/docs/api/interfaces/RoleBasedRouterProps.md +0 -156
- package/docs/api/interfaces/RoleManagementResult.md +0 -52
- package/docs/api/interfaces/RouteAccessRecord.md +0 -107
- package/docs/api/interfaces/RouteConfig.md +0 -134
- package/docs/api/interfaces/RuntimeComplianceResult.md +0 -55
- package/docs/api/interfaces/SecureDataContextType.md +0 -168
- package/docs/api/interfaces/SecureDataProviderProps.md +0 -132
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +0 -34
- package/docs/api/interfaces/SetupIssue.md +0 -41
- package/docs/api/interfaces/StorageConfig.md +0 -41
- package/docs/api/interfaces/StorageFileInfo.md +0 -74
- package/docs/api/interfaces/StorageFileMetadata.md +0 -151
- package/docs/api/interfaces/StorageListOptions.md +0 -99
- package/docs/api/interfaces/StorageListResult.md +0 -41
- package/docs/api/interfaces/StorageUploadOptions.md +0 -101
- package/docs/api/interfaces/StorageUploadResult.md +0 -63
- package/docs/api/interfaces/StorageUrlOptions.md +0 -60
- package/docs/api/interfaces/StyleImport.md +0 -19
- package/docs/api/interfaces/SwitchProps.md +0 -34
- package/docs/api/interfaces/TabsContentProps.md +0 -9
- package/docs/api/interfaces/TabsListProps.md +0 -9
- package/docs/api/interfaces/TabsProps.md +0 -9
- package/docs/api/interfaces/TabsTriggerProps.md +0 -50
- package/docs/api/interfaces/TextareaProps.md +0 -53
- package/docs/api/interfaces/ToastActionElement.md +0 -9
- package/docs/api/interfaces/ToastProps.md +0 -9
- package/docs/api/interfaces/UnifiedAuthContextType.md +0 -820
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +0 -171
- package/docs/api/interfaces/UseFormDialogOptions.md +0 -62
- package/docs/api/interfaces/UseFormDialogReturn.md +0 -117
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +0 -136
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +0 -123
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +0 -87
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +0 -81
- package/docs/api/interfaces/UsePublicEventOptions.md +0 -34
- package/docs/api/interfaces/UsePublicEventReturn.md +0 -68
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +0 -47
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +0 -120
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +0 -94
- package/docs/api/interfaces/UseResolvedScopeOptions.md +0 -47
- package/docs/api/interfaces/UseResolvedScopeReturn.md +0 -47
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +0 -34
- package/docs/api/interfaces/UserEventAccess.md +0 -118
- package/docs/api/interfaces/UserMenuProps.md +0 -86
- package/docs/api/interfaces/UserProfile.md +0 -63
- package/docs/migration/quick-migration-guide.md +0 -356
- package/docs/migration/service-architecture.md +0 -281
- package/src/components/EventSelector/EventSelector.test.tsx +0 -720
- package/src/components/EventSelector/EventSelector.tsx +0 -420
- package/src/components/EventSelector/index.ts +0 -3
- package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +0 -784
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -324
- package/src/components/OrganisationSelector/index.ts +0 -9
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
- package/src/hooks/useSecureDataAccess.test.ts +0 -559
- package/src/hooks/useSecureDataAccess.ts +0 -681
- /package/dist/{DataTable-DQ7RSOHE.js.map → DataTable-THFPBKTP.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-ATAP5UTR.js.map → UnifiedAuthProvider-KAGUYQ4J.js.map} +0 -0
- /package/dist/{api-N774RPUA.js.map → api-IAGWF3ZG.js.map} +0 -0
- /package/dist/{audit-B5P6FFIR.js.map → audit-V53FV5AG.js.map} +0 -0
- /package/dist/{chunk-7D4SUZUM.js.map → chunk-DGUM43GV.js.map} +0 -0
- /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
- /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
- /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
- /package/docs/migration/{REACT_19_MIGRATION.md → V0.6.0_REACT_19_MIGRATION.md} +0 -0
package/src/rbac/api.ts
CHANGED
|
@@ -30,7 +30,6 @@ import { createLogger } from '../utils/core/logger';
|
|
|
30
30
|
import { enablePerformanceMonitoring } from './performance';
|
|
31
31
|
import { getOrCreateRequest } from './request-deduplication';
|
|
32
32
|
import { ContextValidator } from './utils/contextValidator';
|
|
33
|
-
import type { AppConfig } from './utils/contextValidator';
|
|
34
33
|
|
|
35
34
|
const log = createLogger('RBACAPI');
|
|
36
35
|
|
|
@@ -92,6 +91,15 @@ export function setupRBAC(supabase: SupabaseClient<Database>, config?: Partial<R
|
|
|
92
91
|
|
|
93
92
|
}
|
|
94
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Check if RBAC system is initialized
|
|
96
|
+
*
|
|
97
|
+
* @returns True if RBAC is initialized
|
|
98
|
+
*/
|
|
99
|
+
export function isRBACInitialized(): boolean {
|
|
100
|
+
return globalEngine !== null;
|
|
101
|
+
}
|
|
102
|
+
|
|
95
103
|
/**
|
|
96
104
|
* Get the global RBAC engine
|
|
97
105
|
*
|
|
@@ -126,42 +134,39 @@ export async function getAccessLevel(
|
|
|
126
134
|
userId: UUID;
|
|
127
135
|
scope: Scope;
|
|
128
136
|
},
|
|
129
|
-
appConfig?: AppConfig | null,
|
|
130
137
|
appName?: string
|
|
131
138
|
): Promise<AccessLevel> {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
139
|
+
try {
|
|
140
|
+
const engine = getEngine();
|
|
141
|
+
|
|
142
|
+
// Check super admin status first - super admins bypass context requirements
|
|
143
|
+
const isSuperAdminUser = await engine['checkSuperAdmin'](input.userId);
|
|
144
|
+
if (isSuperAdminUser) {
|
|
145
|
+
return 'super';
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// For functions without pageId, default to organisation scope validation
|
|
149
|
+
// This is a safe default - most operations require organisation context
|
|
150
|
+
const validation = await ContextValidator.resolveScopeForPage(
|
|
151
|
+
input.scope,
|
|
152
|
+
'organisation', // Default to organisation scope when no page context
|
|
153
|
+
appName,
|
|
154
|
+
engine['supabase']
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
if (!validation.isValid || !validation.resolvedScope) {
|
|
158
|
+
throw validation.error || new OrganisationContextRequiredError();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Use resolved scope
|
|
162
|
+
return engine.getAccessLevel({
|
|
163
|
+
...input,
|
|
164
|
+
scope: validation.resolvedScope
|
|
165
|
+
});
|
|
166
|
+
} catch (error) {
|
|
167
|
+
// Re-throw the error - this is an API function that should propagate errors
|
|
168
|
+
throw error;
|
|
158
169
|
}
|
|
159
|
-
|
|
160
|
-
// Use resolved scope
|
|
161
|
-
return engine.getAccessLevel({
|
|
162
|
-
...input,
|
|
163
|
-
scope: validation.resolvedScope
|
|
164
|
-
});
|
|
165
170
|
}
|
|
166
171
|
|
|
167
172
|
/**
|
|
@@ -189,44 +194,46 @@ export async function getPermissionMap(
|
|
|
189
194
|
userId: UUID;
|
|
190
195
|
scope: Scope;
|
|
191
196
|
},
|
|
192
|
-
appConfig?: AppConfig | null,
|
|
193
197
|
appName?: string
|
|
194
198
|
): Promise<PermissionMap> {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
199
|
+
try {
|
|
200
|
+
const engine = getEngine();
|
|
201
|
+
|
|
202
|
+
// For functions without pageId, default to organisation scope validation
|
|
203
|
+
// This is a safe default - most operations require organisation context
|
|
204
|
+
const validation = await ContextValidator.resolveScopeForPage(
|
|
205
|
+
input.scope,
|
|
206
|
+
'organisation', // Default to organisation scope when no page context
|
|
207
|
+
appName,
|
|
208
|
+
engine['supabase']
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
if (!validation.isValid || !validation.resolvedScope) {
|
|
212
|
+
throw validation.error || new OrganisationContextRequiredError();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Use resolved scope
|
|
216
|
+
return engine.getPermissionMap({
|
|
217
|
+
...input,
|
|
218
|
+
scope: validation.resolvedScope
|
|
219
|
+
});
|
|
220
|
+
} catch (error) {
|
|
221
|
+
// Re-throw the error - this is an API function that should propagate errors
|
|
222
|
+
throw error;
|
|
215
223
|
}
|
|
216
|
-
|
|
217
|
-
// Use resolved scope
|
|
218
|
-
return engine.getPermissionMap({
|
|
219
|
-
...input,
|
|
220
|
-
scope: validation.resolvedScope
|
|
221
|
-
});
|
|
222
224
|
}
|
|
223
225
|
|
|
224
226
|
export async function resolveAppContext(input: {
|
|
225
227
|
userId: UUID;
|
|
226
228
|
appName: string;
|
|
227
229
|
}): Promise<RBACAppContext | null> {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
+
try {
|
|
231
|
+
const engine = getEngine();
|
|
232
|
+
return await engine.resolveAppContext(input);
|
|
233
|
+
} catch (error) {
|
|
234
|
+
// Re-throw the error - this is an API function that should propagate errors
|
|
235
|
+
throw error;
|
|
236
|
+
}
|
|
230
237
|
}
|
|
231
238
|
|
|
232
239
|
export async function getRoleContext(
|
|
@@ -234,24 +241,15 @@ export async function getRoleContext(
|
|
|
234
241
|
userId: UUID;
|
|
235
242
|
scope: Scope;
|
|
236
243
|
},
|
|
237
|
-
appConfig?: AppConfig | null,
|
|
238
244
|
appName?: string
|
|
239
245
|
): Promise<RBACRoleContext> {
|
|
240
246
|
const engine = getEngine();
|
|
241
247
|
|
|
242
|
-
//
|
|
243
|
-
|
|
244
|
-
let resolvedAppName = appName;
|
|
245
|
-
|
|
246
|
-
if (!resolvedAppConfig && input.scope.appId) {
|
|
247
|
-
resolvedAppConfig = await getAppConfig(input.scope.appId);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Validate context using ContextValidator
|
|
251
|
-
const validation = await ContextValidator.resolveRequiredContext(
|
|
248
|
+
// For functions without pageId, default to organisation scope validation
|
|
249
|
+
const validation = await ContextValidator.resolveScopeForPage(
|
|
252
250
|
input.scope,
|
|
253
|
-
|
|
254
|
-
|
|
251
|
+
'organisation', // Default to organisation scope when no page context
|
|
252
|
+
appName,
|
|
255
253
|
engine['supabase']
|
|
256
254
|
);
|
|
257
255
|
|
|
@@ -286,27 +284,35 @@ export async function getRoleContext(
|
|
|
286
284
|
*/
|
|
287
285
|
export async function isPermitted(
|
|
288
286
|
input: PermissionCheck,
|
|
289
|
-
|
|
290
|
-
|
|
287
|
+
appName?: string,
|
|
288
|
+
/**
|
|
289
|
+
* Pre-computed super admin status to avoid duplicate checks.
|
|
290
|
+
* Pass null if not checked yet (will check), true if already checked and is super admin,
|
|
291
|
+
* or false if already checked and is not super admin.
|
|
292
|
+
* @default null
|
|
293
|
+
*/
|
|
294
|
+
precomputedSuperAdmin: boolean | null = null
|
|
291
295
|
): Promise<boolean> {
|
|
292
296
|
const engine = getEngine();
|
|
293
297
|
|
|
294
298
|
// Check super admin status first - super admins bypass context requirements
|
|
295
299
|
// Super admins have access to all permissions regardless of organisation context
|
|
296
|
-
|
|
297
|
-
if (
|
|
300
|
+
// PERFORMANCE: Use precomputed value if provided to avoid duplicate checks
|
|
301
|
+
if (precomputedSuperAdmin === true) {
|
|
298
302
|
return true;
|
|
299
303
|
}
|
|
300
304
|
|
|
301
|
-
//
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
305
|
+
// If null, check super admin status
|
|
306
|
+
if (precomputedSuperAdmin === null) {
|
|
307
|
+
const isSuperAdminUser = await engine['checkSuperAdmin'](input.userId);
|
|
308
|
+
if (isSuperAdminUser) {
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
307
311
|
}
|
|
312
|
+
// If precomputedSuperAdmin === false, skip check and proceed with permission check
|
|
308
313
|
|
|
309
|
-
//
|
|
314
|
+
// Get app name if not provided (for PORTAL/ADMIN special case)
|
|
315
|
+
let resolvedAppName = appName;
|
|
310
316
|
if (!resolvedAppName && input.scope.appId) {
|
|
311
317
|
try {
|
|
312
318
|
const { data } = await engine['supabase']
|
|
@@ -323,10 +329,34 @@ export async function isPermitted(
|
|
|
323
329
|
}
|
|
324
330
|
}
|
|
325
331
|
|
|
326
|
-
//
|
|
327
|
-
|
|
332
|
+
// Get page scope type (required for all permission checks)
|
|
333
|
+
// All pages must have scope_type set - this is the single source of truth
|
|
334
|
+
let pageScopeType: 'event' | 'organisation' | 'both';
|
|
335
|
+
if (input.pageId) {
|
|
336
|
+
try {
|
|
337
|
+
const scopeType = await getPageScopeType(
|
|
338
|
+
input.pageId,
|
|
339
|
+
input.scope.appId,
|
|
340
|
+
resolvedAppName
|
|
341
|
+
);
|
|
342
|
+
if (!scopeType) {
|
|
343
|
+
throw new Error(`Page ${input.pageId} does not have scope_type set`);
|
|
344
|
+
}
|
|
345
|
+
pageScopeType = scopeType;
|
|
346
|
+
} catch (err) {
|
|
347
|
+
log.error('Failed to get page scope type:', err);
|
|
348
|
+
throw new Error(`Failed to determine page scope type: ${err instanceof Error ? err.message : String(err)}`);
|
|
349
|
+
}
|
|
350
|
+
} else {
|
|
351
|
+
// No pageId provided - default to organisation scope
|
|
352
|
+
// This should rarely happen, but provides a safe fallback
|
|
353
|
+
pageScopeType = 'organisation';
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Validate context using page-level scope (single source of truth)
|
|
357
|
+
const validation = await ContextValidator.resolveScopeForPage(
|
|
328
358
|
input.scope,
|
|
329
|
-
|
|
359
|
+
pageScopeType,
|
|
330
360
|
resolvedAppName,
|
|
331
361
|
engine['supabase']
|
|
332
362
|
);
|
|
@@ -338,7 +368,64 @@ export async function isPermitted(
|
|
|
338
368
|
// Use resolved scope for permission check
|
|
339
369
|
const validatedScope = validation.resolvedScope;
|
|
340
370
|
|
|
341
|
-
//
|
|
371
|
+
// Handle 'both' scope pages - check both scopes and return union
|
|
372
|
+
if (pageScopeType === 'both' && input.pageId) {
|
|
373
|
+
// Check permission with event scope
|
|
374
|
+
const eventScope: Scope = {
|
|
375
|
+
organisationId: validatedScope.organisationId, // Org derived from event
|
|
376
|
+
eventId: validatedScope.eventId,
|
|
377
|
+
appId: validatedScope.appId
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
// Check permission with organisation scope (if org is available separately)
|
|
381
|
+
// For 'both' pages, we check the permission in both contexts and return the union
|
|
382
|
+
// Higher permission wins (true > false, admin > user, etc.)
|
|
383
|
+
|
|
384
|
+
const eventSecurityContext: SecurityContext = {
|
|
385
|
+
userId: input.userId,
|
|
386
|
+
organisationId: eventScope.organisationId || null,
|
|
387
|
+
timestamp: new Date(),
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
const eventInput: PermissionCheck = {
|
|
391
|
+
...input,
|
|
392
|
+
scope: eventScope
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
const hasEventPermission = await engine.isPermitted(eventInput, eventSecurityContext);
|
|
396
|
+
|
|
397
|
+
// Also check with organisation scope if we have a separate org context
|
|
398
|
+
// (This handles cases where user has org permissions separate from event)
|
|
399
|
+
if (validatedScope.organisationId && validatedScope.eventId) {
|
|
400
|
+
const orgScope: Scope = {
|
|
401
|
+
organisationId: validatedScope.organisationId,
|
|
402
|
+
eventId: undefined, // Clear event for org-only check
|
|
403
|
+
appId: validatedScope.appId
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
const orgSecurityContext: SecurityContext = {
|
|
407
|
+
userId: input.userId,
|
|
408
|
+
organisationId: orgScope.organisationId || null,
|
|
409
|
+
timestamp: new Date(),
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
const orgInput: PermissionCheck = {
|
|
413
|
+
...input,
|
|
414
|
+
scope: orgScope
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
const hasOrgPermission = await engine.isPermitted(orgInput, orgSecurityContext);
|
|
418
|
+
|
|
419
|
+
// Return union (true if either scope grants permission)
|
|
420
|
+
// For access levels, the database function handles the "higher wins" logic
|
|
421
|
+
return hasEventPermission || hasOrgPermission;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// If only event scope available, return event permission
|
|
425
|
+
return hasEventPermission;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Standard permission check for single-scope pages
|
|
342
429
|
const securityContext: SecurityContext = {
|
|
343
430
|
userId: input.userId,
|
|
344
431
|
organisationId: validatedScope.organisationId || null,
|
|
@@ -362,13 +449,11 @@ export async function isPermitted(
|
|
|
362
449
|
* and checks cache before making new requests. Uses session cache for page-level checks.
|
|
363
450
|
*
|
|
364
451
|
* @param input - Permission check input
|
|
365
|
-
* @param
|
|
366
|
-
* @param appName - Optional app name
|
|
452
|
+
* @param appName - Optional app name (for PORTAL/ADMIN special case)
|
|
367
453
|
* @returns Promise resolving to permission result
|
|
368
454
|
*/
|
|
369
455
|
export async function isPermittedCached(
|
|
370
456
|
input: PermissionCheck,
|
|
371
|
-
appConfig?: AppConfig | null,
|
|
372
457
|
appName?: string
|
|
373
458
|
): Promise<boolean> {
|
|
374
459
|
const { userId, scope, permission, pageId } = input;
|
|
@@ -391,7 +476,9 @@ export async function isPermittedCached(
|
|
|
391
476
|
// Use request deduplication - if same request is in-flight, share the promise
|
|
392
477
|
return getOrCreateRequest(input, async (checkInput) => {
|
|
393
478
|
// Check permission with context validation
|
|
394
|
-
|
|
479
|
+
// Note: We can't pass precomputedSuperAdmin here because getOrCreateRequest doesn't support it
|
|
480
|
+
// The super admin check in isPermitted will be cached, so it's not a huge performance hit
|
|
481
|
+
const result = await isPermitted(checkInput, appName, null);
|
|
395
482
|
|
|
396
483
|
// Determine if this is a page-level check (has pageId or permission contains 'page.')
|
|
397
484
|
const isPageLevelCheck = !!pageId || permission.includes('page.');
|
|
@@ -480,89 +567,81 @@ export async function isSuperAdmin(userId: UUID): Promise<boolean> {
|
|
|
480
567
|
return engine['checkSuperAdmin'](userId);
|
|
481
568
|
}
|
|
482
569
|
|
|
570
|
+
|
|
483
571
|
/**
|
|
484
|
-
* Get
|
|
572
|
+
* Get page scope type (effective scope for a page)
|
|
485
573
|
*
|
|
486
|
-
*
|
|
487
|
-
*
|
|
574
|
+
* Returns the scope type for a page. After migration, all pages have explicit scope_type set.
|
|
575
|
+
* This is the single source of truth for page scoping.
|
|
576
|
+
*
|
|
577
|
+
* @param pageId - Page ID (UUID) or page name
|
|
578
|
+
* @param appId - App ID (required if pageId is a page name)
|
|
579
|
+
* @param appName - App name (alternative to appId, used to resolve appId)
|
|
580
|
+
* @returns Promise resolving to page scope type: 'event', 'organisation', or 'both'
|
|
488
581
|
*/
|
|
489
|
-
export async function
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
if (err instanceof RBACNotInitializedError) {
|
|
496
|
-
return null;
|
|
497
|
-
}
|
|
498
|
-
throw err;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
export async function getAppConfigWithClient(client: SupabaseClient | null | undefined, appId: UUID): Promise<AppConfig | null> {
|
|
503
|
-
// Return null if client is not available
|
|
504
|
-
if (!client) {
|
|
505
|
-
return null;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
// Cache key for app config - cache for 5 minutes (app config rarely changes)
|
|
509
|
-
const cacheKey = `app_config:${appId}`;
|
|
510
|
-
|
|
511
|
-
// Check cache first
|
|
512
|
-
const cached = rbacCache.get<AppConfig>(cacheKey, true);
|
|
513
|
-
if (cached !== null) {
|
|
514
|
-
return cached;
|
|
515
|
-
}
|
|
582
|
+
export async function getPageScopeType(
|
|
583
|
+
pageId: UUID | string,
|
|
584
|
+
appId?: UUID,
|
|
585
|
+
appName?: string
|
|
586
|
+
): Promise<'event' | 'organisation' | 'both'> {
|
|
587
|
+
const engine = getEngine();
|
|
516
588
|
|
|
517
589
|
try {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
590
|
+
// Resolve appId if not provided
|
|
591
|
+
let resolvedAppId = appId;
|
|
592
|
+
if (!resolvedAppId && appName) {
|
|
593
|
+
// Get appId directly from app name
|
|
594
|
+
const { data: app } = await engine['supabase']
|
|
595
|
+
.from('rbac_apps')
|
|
596
|
+
.select('id')
|
|
597
|
+
.eq('name', appName)
|
|
598
|
+
.eq('is_active', true)
|
|
599
|
+
.single() as { data: { id: UUID } | null; error: any };
|
|
600
|
+
resolvedAppId = app?.id;
|
|
527
601
|
}
|
|
528
|
-
|
|
529
|
-
const appConfig: AppConfig = { requires_event: data.requires_event ?? false };
|
|
530
602
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
603
|
+
if (!resolvedAppId) {
|
|
604
|
+
throw new Error(`Could not resolve appId for page ${pageId}`);
|
|
605
|
+
}
|
|
534
606
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
607
|
+
// Resolve pageId if it's a page name
|
|
608
|
+
let resolvedPageId: UUID | string = pageId;
|
|
609
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
610
|
+
if (!uuidRegex.test(pageId)) {
|
|
611
|
+
// It's a page name, resolve to UUID
|
|
612
|
+
const { data: page } = await engine['supabase']
|
|
613
|
+
.from('rbac_app_pages')
|
|
614
|
+
.select('id')
|
|
615
|
+
.eq('app_id', resolvedAppId)
|
|
616
|
+
.eq('page_name', pageId)
|
|
617
|
+
.maybeSingle() as { data: { id: UUID } | null; error: any };
|
|
618
|
+
resolvedPageId = page?.id || pageId;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// If still not a UUID, can't proceed
|
|
622
|
+
if (!uuidRegex.test(resolvedPageId)) {
|
|
623
|
+
throw new Error(`Could not resolve pageId ${pageId} to a valid UUID`);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// Call the database function to get scope type (always returns a value)
|
|
551
627
|
const { data, error } = await engine['supabase']
|
|
552
|
-
.
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
return null;
|
|
628
|
+
.rpc('get_page_scope_type' as any, {
|
|
629
|
+
p_page_id: resolvedPageId
|
|
630
|
+
}) as { data: 'event' | 'organisation' | 'both' | null; error: any };
|
|
631
|
+
|
|
632
|
+
if (error) {
|
|
633
|
+
log.error('Error fetching page scope type:', { pageId, appId, error });
|
|
634
|
+
throw new Error(`Failed to get page scope type: ${error.message}`);
|
|
560
635
|
}
|
|
561
|
-
|
|
562
|
-
|
|
636
|
+
|
|
637
|
+
if (!data) {
|
|
638
|
+
throw new Error(`Page ${resolvedPageId} does not have scope_type set`);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return data;
|
|
563
642
|
} catch (err) {
|
|
564
|
-
log.error('Error fetching
|
|
565
|
-
|
|
643
|
+
log.error('Error fetching page scope type:', err);
|
|
644
|
+
throw err instanceof Error ? err : new Error(`Failed to get page scope type: ${String(err)}`);
|
|
566
645
|
}
|
|
567
646
|
}
|
|
568
647
|
|
|
@@ -211,12 +211,15 @@ export function NavigationProvider({
|
|
|
211
211
|
const permission = item.permissions[0];
|
|
212
212
|
|
|
213
213
|
// Call useCan hook for actual permission checking
|
|
214
|
+
// Pass null for super admin status (not checked yet - hook will check if needed)
|
|
214
215
|
const { can, error } = useCan(
|
|
215
216
|
user.id,
|
|
216
217
|
currentScope,
|
|
217
218
|
permission,
|
|
218
219
|
item.pageId,
|
|
219
|
-
true // useCache
|
|
220
|
+
true, // useCache
|
|
221
|
+
null, // precomputedSuperAdmin - not checked yet
|
|
222
|
+
undefined // appName
|
|
220
223
|
);
|
|
221
224
|
|
|
222
225
|
// Handle errors gracefully - allow access when there are permission check errors (graceful degradation)
|