@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
|
@@ -141,17 +141,87 @@ const PagePermissionGuardComponent = ({
|
|
|
141
141
|
const { user, selectedOrganisation, selectedEvent, supabase, appId: contextAppId, appName } = useUnifiedAuth();
|
|
142
142
|
|
|
143
143
|
const [hasChecked, setHasChecked] = useState(false);
|
|
144
|
+
const hasLoggedSuperAdminRef = useRef(false);
|
|
145
|
+
|
|
146
|
+
// Determine the page ID for permission checking
|
|
147
|
+
const effectivePageId = useMemo((): string => {
|
|
148
|
+
return pageId || pageName;
|
|
149
|
+
}, [pageId, pageName]);
|
|
150
|
+
|
|
151
|
+
// Check super admin status directly - don't rely on useRBAC which might not be loaded yet
|
|
152
|
+
// This ensures super admins get immediate access without waiting for RBAC context to load
|
|
153
|
+
const [isSuperAdmin, setIsSuperAdmin] = useState<boolean | null>(null);
|
|
154
|
+
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
if (!user?.id) {
|
|
157
|
+
setIsSuperAdmin(false);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let cancelled = false;
|
|
162
|
+
const checkSuperAdmin = async () => {
|
|
163
|
+
const startTime = Date.now();
|
|
164
|
+
try {
|
|
165
|
+
const { isSuperAdmin: checkSuperAdmin } = await import('../api');
|
|
166
|
+
|
|
167
|
+
// Add timeout to prevent hanging
|
|
168
|
+
const timeoutPromise = new Promise<boolean>((_, reject) => {
|
|
169
|
+
setTimeout(() => reject(new Error('Super admin check timeout')), 10000);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const isSuper = await Promise.race([
|
|
173
|
+
checkSuperAdmin(user.id),
|
|
174
|
+
timeoutPromise
|
|
175
|
+
]);
|
|
176
|
+
|
|
177
|
+
const elapsed = Date.now() - startTime;
|
|
178
|
+
|
|
179
|
+
if (!cancelled) {
|
|
180
|
+
setIsSuperAdmin(isSuper);
|
|
181
|
+
if (process.env.NODE_ENV === 'development') {
|
|
182
|
+
console.log('[PagePermissionGuard] Super admin check completed', {
|
|
183
|
+
userId: user.id,
|
|
184
|
+
isSuperAdmin: isSuper,
|
|
185
|
+
elapsedMs: elapsed
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} catch (err) {
|
|
190
|
+
const elapsed = Date.now() - startTime;
|
|
191
|
+
if (!cancelled) {
|
|
192
|
+
console.error('[PagePermissionGuard] Error checking super admin', {
|
|
193
|
+
error: err,
|
|
194
|
+
userId: user.id,
|
|
195
|
+
elapsedMs: elapsed
|
|
196
|
+
});
|
|
197
|
+
// On timeout or error, assume not super admin (fail secure)
|
|
198
|
+
// But log it so we can debug
|
|
199
|
+
setIsSuperAdmin(false);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
checkSuperAdmin();
|
|
205
|
+
return () => {
|
|
206
|
+
cancelled = true;
|
|
207
|
+
};
|
|
208
|
+
}, [user?.id]);
|
|
144
209
|
|
|
145
210
|
// Use useResolvedScope hook for consistent scope resolution
|
|
146
211
|
// For event-required apps: selectedOrganisation is null, org derived from event
|
|
147
212
|
// For org-required apps: selectedOrganisation is available, event optional
|
|
148
213
|
// For page-level permissions, PORTAL app allows both contexts to be optional
|
|
214
|
+
// NOTE: Super admins bypass scope requirements, but we still need to call the hook
|
|
149
215
|
const { resolvedScope: hookResolvedScope, isLoading: scopeLoading, error: scopeError } = useResolvedScope({
|
|
150
216
|
supabase,
|
|
151
217
|
selectedOrganisationId: selectedOrganisation?.id || null,
|
|
152
218
|
selectedEventId: selectedEvent?.event_id || null
|
|
153
219
|
});
|
|
154
220
|
|
|
221
|
+
// For super admins, we can use a minimal scope since they bypass all checks
|
|
222
|
+
// This prevents scope resolution from blocking super admin access
|
|
223
|
+
const shouldBypassScopeForSuperAdmin = isSuperAdmin === true;
|
|
224
|
+
|
|
155
225
|
// Use provided scope if available, otherwise use resolved scope
|
|
156
226
|
// For PORTAL app page-level permissions, allow scope without org/event
|
|
157
227
|
// Ensure appId is available for PORTAL/ADMIN (use contextAppId as fallback)
|
|
@@ -171,11 +241,6 @@ const PagePermissionGuardComponent = ({
|
|
|
171
241
|
} : null)));
|
|
172
242
|
const checkError = scopeError;
|
|
173
243
|
|
|
174
|
-
// Determine the page ID for permission checking
|
|
175
|
-
const effectivePageId = useMemo((): string => {
|
|
176
|
-
return pageId || pageName;
|
|
177
|
-
}, [pageId, pageName]);
|
|
178
|
-
|
|
179
244
|
// Build the permission string
|
|
180
245
|
const permission = useMemo((): Permission => {
|
|
181
246
|
return `${operation}:page.${pageName}` as Permission;
|
|
@@ -225,22 +290,44 @@ const PagePermissionGuardComponent = ({
|
|
|
225
290
|
return newScope;
|
|
226
291
|
}, [effectiveScope, appName, contextAppId, selectedEvent?.event_id]);
|
|
227
292
|
|
|
293
|
+
// For super admins, use a minimal valid scope since they bypass all checks
|
|
294
|
+
// This prevents scope validation from blocking super admin access
|
|
295
|
+
const scopeForPermissionCheck = shouldBypassScopeForSuperAdmin && !stableScope?.organisationId
|
|
296
|
+
? {
|
|
297
|
+
organisationId: undefined,
|
|
298
|
+
appId: contextAppId || undefined,
|
|
299
|
+
eventId: selectedEvent?.event_id || undefined
|
|
300
|
+
}
|
|
301
|
+
: stableScope;
|
|
302
|
+
|
|
303
|
+
// CRITICAL: If super admin is confirmed, skip useCan entirely to avoid any blocking
|
|
304
|
+
// Super admins have access to everything, so no need to check permissions
|
|
305
|
+
const shouldSkipPermissionCheck = isSuperAdmin === true;
|
|
306
|
+
|
|
228
307
|
// Check if user has permission - only call useCan when we have a resolved scope with valid organisationId
|
|
229
308
|
// If resolvedScope is null or has no organisationId, useCan will keep isLoading=true
|
|
230
309
|
// Pass appName to useCan so it can be passed to isPermitted for PORTAL/ADMIN special case
|
|
310
|
+
// Pass precomputed super admin status to avoid duplicate checks in useCan
|
|
311
|
+
// For super admins, we can skip the check entirely, but still call useCan with a dummy scope to avoid hook rule violations
|
|
231
312
|
const { can, isLoading: canIsLoading, error: canError } = useCan(
|
|
232
313
|
user?.id || '',
|
|
233
|
-
|
|
314
|
+
shouldSkipPermissionCheck ? { organisationId: undefined, appId: contextAppId || undefined, eventId: undefined } : scopeForPermissionCheck,
|
|
234
315
|
permission,
|
|
235
316
|
effectivePageId,
|
|
236
317
|
true, // Use cache
|
|
318
|
+
isSuperAdmin, // precomputedSuperAdmin - null if checking, true/false if checked
|
|
237
319
|
appName // Pass appName for PORTAL/ADMIN special case
|
|
238
320
|
);
|
|
239
321
|
|
|
322
|
+
// Override can for super admins - they always have access
|
|
323
|
+
const effectiveCan = shouldSkipPermissionCheck ? true : can;
|
|
324
|
+
const effectiveIsLoading = shouldSkipPermissionCheck ? false : canIsLoading;
|
|
325
|
+
|
|
240
326
|
|
|
241
327
|
// Combine loading states - we're loading if scope resolution or permission check is loading
|
|
242
328
|
// For page-level permissions, PORTAL/ADMIN apps allow undefined organisationId
|
|
243
|
-
|
|
329
|
+
// Super admins bypass scope resolution - don't wait for it
|
|
330
|
+
const isLoading = shouldBypassScopeForSuperAdmin ? effectiveIsLoading : (scopeLoading || effectiveIsLoading);
|
|
244
331
|
const error = checkError || canError;
|
|
245
332
|
|
|
246
333
|
// Handle permission check completion
|
|
@@ -248,13 +335,13 @@ const PagePermissionGuardComponent = ({
|
|
|
248
335
|
if (!isLoading && !error) {
|
|
249
336
|
setHasChecked(true);
|
|
250
337
|
|
|
251
|
-
if (!
|
|
338
|
+
if (!effectiveCan && onDenied) {
|
|
252
339
|
onDenied(pageName, operation);
|
|
253
340
|
}
|
|
254
341
|
} else if (error) {
|
|
255
342
|
setHasChecked(true);
|
|
256
343
|
}
|
|
257
|
-
}, [
|
|
344
|
+
}, [effectiveCan, isLoading, error, pageName, operation, onDenied]);
|
|
258
345
|
|
|
259
346
|
// Log page access attempt for audit
|
|
260
347
|
useEffect(() => {
|
|
@@ -265,16 +352,17 @@ const PagePermissionGuardComponent = ({
|
|
|
265
352
|
operation,
|
|
266
353
|
userId: user?.id,
|
|
267
354
|
scope: effectiveScope,
|
|
268
|
-
allowed:
|
|
355
|
+
allowed: effectiveCan,
|
|
356
|
+
isSuperAdmin,
|
|
269
357
|
timestamp: new Date().toISOString()
|
|
270
358
|
});
|
|
271
359
|
}
|
|
272
|
-
}, [auditLog, hasChecked, isLoading, pageName, operation, user?.id, effectiveScope,
|
|
360
|
+
}, [auditLog, hasChecked, isLoading, pageName, operation, user?.id, effectiveScope, effectiveCan, isSuperAdmin]);
|
|
273
361
|
|
|
274
362
|
|
|
275
363
|
// Handle strict mode violations
|
|
276
364
|
useEffect(() => {
|
|
277
|
-
if (strictMode && hasChecked && !isLoading && !
|
|
365
|
+
if (strictMode && hasChecked && !isLoading && !effectiveCan && !shouldBypassScopeForSuperAdmin) {
|
|
278
366
|
const logger = getRBACLogger();
|
|
279
367
|
logger.error(`STRICT MODE VIOLATION: User attempted to access protected page without permission`, {
|
|
280
368
|
pageName,
|
|
@@ -286,20 +374,22 @@ const PagePermissionGuardComponent = ({
|
|
|
286
374
|
scopeValid: allowsOptionalContexts ? true : (effectiveScope !== null), // PORTAL/ADMIN allow scope without org/event
|
|
287
375
|
checkError,
|
|
288
376
|
canError,
|
|
377
|
+
isSuperAdmin,
|
|
289
378
|
timestamp: new Date().toISOString()
|
|
290
379
|
});
|
|
291
380
|
}
|
|
292
|
-
}, [strictMode, hasChecked, isLoading,
|
|
381
|
+
}, [strictMode, hasChecked, isLoading, effectiveCan, shouldBypassScopeForSuperAdmin, pageName, operation, effectivePageId, user?.id, effectiveScope, allowsOptionalContexts, checkError, canError, isSuperAdmin]);
|
|
293
382
|
|
|
294
383
|
// Calculate the actual render state - FIXED: Proper state calculation
|
|
295
384
|
// Add defensive checks to ensure we have valid state
|
|
296
385
|
// For page-level permissions, PORTAL/ADMIN apps allow scope without org/event
|
|
297
|
-
|
|
386
|
+
// Super admins bypass scope validation
|
|
387
|
+
const hasValidScopeForPagePermissions = shouldBypassScopeForSuperAdmin ? true : (allowsOptionalContexts ? true : (effectiveScope !== null));
|
|
298
388
|
const hasValidUser = user && user.id;
|
|
299
389
|
const isPermissionCheckComplete = hasChecked && !isLoading;
|
|
300
390
|
|
|
301
|
-
const shouldShowAccessDenied = isPermissionCheckComplete && hasValidScopeForPagePermissions && hasValidUser && !checkError && !
|
|
302
|
-
const shouldShowContent = isPermissionCheckComplete && hasValidScopeForPagePermissions && hasValidUser && !checkError &&
|
|
391
|
+
const shouldShowAccessDenied = isPermissionCheckComplete && hasValidScopeForPagePermissions && hasValidUser && !checkError && !effectiveCan;
|
|
392
|
+
const shouldShowContent = isPermissionCheckComplete && hasValidScopeForPagePermissions && hasValidUser && !checkError && effectiveCan;
|
|
303
393
|
|
|
304
394
|
// Create a key to force re-render when scope or permission state changes
|
|
305
395
|
const scopeKey = effectiveScope ? `${effectiveScope.organisationId}-${effectiveScope.eventId}-${effectiveScope.appId}` : 'no-scope';
|
|
@@ -307,9 +397,87 @@ const PagePermissionGuardComponent = ({
|
|
|
307
397
|
|
|
308
398
|
|
|
309
399
|
|
|
400
|
+
// Debug logging for permission check state (only log when state actually changes, not on every render)
|
|
401
|
+
const lastLogStateRef = useRef<string>('');
|
|
402
|
+
useEffect(() => {
|
|
403
|
+
if (process.env.NODE_ENV === 'development') {
|
|
404
|
+
const currentState = JSON.stringify({
|
|
405
|
+
pageName,
|
|
406
|
+
userId: user?.id,
|
|
407
|
+
isSuperAdmin,
|
|
408
|
+
isLoading,
|
|
409
|
+
scopeLoading,
|
|
410
|
+
canIsLoading,
|
|
411
|
+
hasChecked,
|
|
412
|
+
hasValidUser,
|
|
413
|
+
effectiveCan
|
|
414
|
+
});
|
|
415
|
+
// Only log if state actually changed
|
|
416
|
+
if (currentState !== lastLogStateRef.current) {
|
|
417
|
+
lastLogStateRef.current = currentState;
|
|
418
|
+
console.log('[PagePermissionGuard] Permission check state', {
|
|
419
|
+
pageName,
|
|
420
|
+
userId: user?.id,
|
|
421
|
+
isSuperAdmin,
|
|
422
|
+
isLoading,
|
|
423
|
+
scopeLoading,
|
|
424
|
+
canIsLoading,
|
|
425
|
+
hasChecked,
|
|
426
|
+
hasValidUser,
|
|
427
|
+
effectiveCan,
|
|
428
|
+
stableScope,
|
|
429
|
+
effectiveScope
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}, [pageName, user?.id, isSuperAdmin, isLoading, scopeLoading, canIsLoading, hasChecked, hasValidUser, effectiveCan, stableScope, effectiveScope]);
|
|
434
|
+
|
|
310
435
|
// Show loading state - if we're still loading or don't have valid state
|
|
311
436
|
// For page-level permissions, we don't require organisation context, so only check for user and loading state
|
|
312
|
-
|
|
437
|
+
// Add timeout warning for debugging
|
|
438
|
+
useEffect(() => {
|
|
439
|
+
if (isLoading && isSuperAdmin === null && hasValidUser) {
|
|
440
|
+
const timeout = setTimeout(() => {
|
|
441
|
+
console.warn('[PagePermissionGuard] Permission check taking longer than expected', {
|
|
442
|
+
pageName,
|
|
443
|
+
userId: user?.id,
|
|
444
|
+
isSuperAdmin,
|
|
445
|
+
scopeLoading,
|
|
446
|
+
canIsLoading,
|
|
447
|
+
hasChecked,
|
|
448
|
+
stableScope,
|
|
449
|
+
effectiveScope,
|
|
450
|
+
appName
|
|
451
|
+
});
|
|
452
|
+
}, 5000);
|
|
453
|
+
return () => clearTimeout(timeout);
|
|
454
|
+
}
|
|
455
|
+
}, [isLoading, isSuperAdmin, hasValidUser, pageName, user?.id, scopeLoading, canIsLoading, hasChecked, stableScope, effectiveScope, appName]);
|
|
456
|
+
|
|
457
|
+
// CRITICAL: Super admins bypass all checks - show content immediately once confirmed
|
|
458
|
+
// This must be checked AFTER all hooks are called to avoid React hooks rule violations
|
|
459
|
+
// Log super admin access only once when it's first confirmed (not on every render)
|
|
460
|
+
useEffect(() => {
|
|
461
|
+
if (isSuperAdmin === true && hasValidUser && !hasLoggedSuperAdminRef.current && process.env.NODE_ENV === 'development') {
|
|
462
|
+
hasLoggedSuperAdminRef.current = true;
|
|
463
|
+
console.log('[PagePermissionGuard] Super admin access granted - bypassing all checks', {
|
|
464
|
+
pageName,
|
|
465
|
+
userId: user?.id,
|
|
466
|
+
operation
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
// Reset log flag if super admin status changes back to false/null
|
|
470
|
+
if (isSuperAdmin !== true) {
|
|
471
|
+
hasLoggedSuperAdminRef.current = false;
|
|
472
|
+
}
|
|
473
|
+
}, [isSuperAdmin, hasValidUser, pageName, user?.id, operation]);
|
|
474
|
+
|
|
475
|
+
if (isSuperAdmin === true && hasValidUser) {
|
|
476
|
+
// Super admin confirmed - grant access immediately
|
|
477
|
+
return <>{children}</>;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (isLoading || !hasValidUser || !hasChecked || isSuperAdmin === null) {
|
|
313
481
|
return loading || <div>Checking permissions...</div>;
|
|
314
482
|
}
|
|
315
483
|
|
|
@@ -231,11 +231,15 @@ export function RoleBasedRouter({
|
|
|
231
231
|
}, [user?.id, currentScope, routes]);
|
|
232
232
|
|
|
233
233
|
// Use useCan hook for actual permission checking
|
|
234
|
+
// Pass null for super admin status (not checked yet - hook will check if needed)
|
|
234
235
|
const { can: canAccessCurrentRoute, isLoading: permissionLoading } = useCan(
|
|
235
236
|
user?.id || '',
|
|
236
237
|
currentScope || { organisationId: '', eventId: undefined, appId: undefined },
|
|
237
238
|
currentRouteConfig?.permissions?.[0] || 'read:page',
|
|
238
|
-
currentRouteConfig?.pageId
|
|
239
|
+
currentRouteConfig?.pageId,
|
|
240
|
+
true, // useCache
|
|
241
|
+
null, // precomputedSuperAdmin - not checked yet
|
|
242
|
+
undefined // appName
|
|
239
243
|
);
|
|
240
244
|
|
|
241
245
|
// Check if route is public
|
|
@@ -12,7 +12,7 @@ import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
|
12
12
|
import React, { ReactNode } from 'react';
|
|
13
13
|
import { SecureDataProvider, useSecureData } from './SecureDataProvider';
|
|
14
14
|
import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
|
|
15
|
-
|
|
15
|
+
// useSecureDataAccess has been removed - SecureDataProvider now uses useSecureSupabase internally
|
|
16
16
|
import { UUID, Scope, Permission } from '../types';
|
|
17
17
|
|
|
18
18
|
// Mock the auth provider
|
|
@@ -23,8 +23,77 @@ vi.mock('../../providers/services/UnifiedAuthProvider', () => ({
|
|
|
23
23
|
}));
|
|
24
24
|
|
|
25
25
|
// Mock the secure data access hook
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
// useSecureDataAccess has been removed - no longer needed
|
|
27
|
+
|
|
28
|
+
// Mock useResolvedScope
|
|
29
|
+
const mockUseResolvedScopeFn = vi.fn();
|
|
30
|
+
vi.mock('../../hooks/useResolvedScope', () => ({
|
|
31
|
+
useResolvedScope: () => mockUseResolvedScopeFn(),
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
// Mock useOrganisations to prevent provider requirement
|
|
35
|
+
vi.mock('../../hooks/useOrganisations', () => ({
|
|
36
|
+
useOrganisations: vi.fn(() => ({
|
|
37
|
+
organisations: [],
|
|
38
|
+
isLoading: false,
|
|
39
|
+
error: null,
|
|
40
|
+
refetch: vi.fn(),
|
|
41
|
+
selectedOrganisation: {
|
|
42
|
+
id: 'org-123',
|
|
43
|
+
name: 'Test Org',
|
|
44
|
+
display_name: 'Test Organisation',
|
|
45
|
+
description: 'Test',
|
|
46
|
+
subscription_tier: 'basic',
|
|
47
|
+
settings: {},
|
|
48
|
+
is_active: true,
|
|
49
|
+
created_at: '2023-01-01T00:00:00Z',
|
|
50
|
+
updated_at: '2023-01-01T00:00:00Z'
|
|
51
|
+
}
|
|
52
|
+
}))
|
|
53
|
+
}));
|
|
54
|
+
|
|
55
|
+
// Mock useEvents
|
|
56
|
+
vi.mock('../../hooks/useEvents', () => ({
|
|
57
|
+
useEvents: vi.fn(() => ({
|
|
58
|
+
events: [],
|
|
59
|
+
isLoading: false,
|
|
60
|
+
error: null,
|
|
61
|
+
refetch: vi.fn(),
|
|
62
|
+
selectedEvent: {
|
|
63
|
+
id: 'event-456',
|
|
64
|
+
event_id: 'event-456',
|
|
65
|
+
event_name: 'Test Event',
|
|
66
|
+
event_date: '2023-01-01T00:00:00Z',
|
|
67
|
+
event_venue: 'Test Venue',
|
|
68
|
+
event_participants: 100,
|
|
69
|
+
event_colours: '#FF0000',
|
|
70
|
+
event_logo: '',
|
|
71
|
+
organisation_id: 'org-123' as any,
|
|
72
|
+
is_visible: true,
|
|
73
|
+
created_at: '2023-01-01T00:00:00Z',
|
|
74
|
+
updated_at: '2023-01-01T00:00:00Z'
|
|
75
|
+
},
|
|
76
|
+
eventLoading: false
|
|
77
|
+
}))
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
// Mock useOrganisationSecurity
|
|
81
|
+
vi.mock('../../hooks/useOrganisationSecurity', () => ({
|
|
82
|
+
useOrganisationSecurity: vi.fn(() => ({
|
|
83
|
+
superAdminContext: {
|
|
84
|
+
isSuperAdmin: false,
|
|
85
|
+
isLoading: false
|
|
86
|
+
},
|
|
87
|
+
organisationSecurity: {
|
|
88
|
+
canAccessOrganisation: vi.fn(() => true),
|
|
89
|
+
canAccessEvent: vi.fn(() => true)
|
|
90
|
+
}
|
|
91
|
+
}))
|
|
92
|
+
}));
|
|
93
|
+
|
|
94
|
+
// Mock useSecureSupabase
|
|
95
|
+
vi.mock('../hooks/useSecureSupabase', () => ({
|
|
96
|
+
useSecureSupabase: vi.fn((supabase) => supabase)
|
|
28
97
|
}));
|
|
29
98
|
|
|
30
99
|
// Mock the RBAC hooks
|
|
@@ -55,7 +124,7 @@ const TestComponent = ({ children }: { children: ReactNode }) => (
|
|
|
55
124
|
);
|
|
56
125
|
|
|
57
126
|
describe('SecureDataProvider', () => {
|
|
58
|
-
|
|
127
|
+
// useSecureDataAccess has been removed
|
|
59
128
|
const mockUseCan = vi.mocked(useCan);
|
|
60
129
|
|
|
61
130
|
beforeEach(() => {
|
|
@@ -67,12 +136,7 @@ describe('SecureDataProvider', () => {
|
|
|
67
136
|
// Add other required properties
|
|
68
137
|
} as any);
|
|
69
138
|
|
|
70
|
-
|
|
71
|
-
isDataAccessAllowed: vi.fn().mockReturnValue(true),
|
|
72
|
-
getDataAccessPermissions: vi.fn().mockReturnValue({}),
|
|
73
|
-
validateDataAccess: vi.fn().mockReturnValue(true),
|
|
74
|
-
// Add other required properties
|
|
75
|
-
} as any);
|
|
139
|
+
// useSecureDataAccess has been removed - SecureDataProvider handles this internally
|
|
76
140
|
});
|
|
77
141
|
|
|
78
142
|
describe('Provider Initialization', () => {
|
|
@@ -160,12 +224,7 @@ describe('SecureDataProvider', () => {
|
|
|
160
224
|
});
|
|
161
225
|
|
|
162
226
|
it('denies data access when user lacks permissions', async () => {
|
|
163
|
-
|
|
164
|
-
isDataAccessAllowed: vi.fn().mockReturnValue(false),
|
|
165
|
-
getDataAccessPermissions: vi.fn().mockReturnValue({}),
|
|
166
|
-
validateDataAccess: vi.fn().mockReturnValue(false),
|
|
167
|
-
// Add other required properties
|
|
168
|
-
} as any);
|
|
227
|
+
// useSecureDataAccess has been removed - SecureDataProvider handles this internally
|
|
169
228
|
|
|
170
229
|
const TestConsumer = () => {
|
|
171
230
|
const context = useSecureData();
|
|
@@ -216,12 +275,7 @@ describe('SecureDataProvider', () => {
|
|
|
216
275
|
'events': ['read', 'update']
|
|
217
276
|
};
|
|
218
277
|
|
|
219
|
-
|
|
220
|
-
isDataAccessAllowed: vi.fn().mockReturnValue(true),
|
|
221
|
-
getDataAccessPermissions: vi.fn().mockReturnValue(mockPermissions),
|
|
222
|
-
validateDataAccess: vi.fn().mockReturnValue(true),
|
|
223
|
-
// Add other required properties
|
|
224
|
-
} as any);
|
|
278
|
+
// useSecureDataAccess has been removed - SecureDataProvider handles this internally
|
|
225
279
|
|
|
226
280
|
const TestConsumer = () => {
|
|
227
281
|
const context = useSecureData();
|
|
@@ -309,13 +363,7 @@ describe('SecureDataProvider', () => {
|
|
|
309
363
|
|
|
310
364
|
it('calls onStrictModeViolation callback when strict mode is violated', async () => {
|
|
311
365
|
const onStrictModeViolation = vi.fn();
|
|
312
|
-
|
|
313
|
-
isDataAccessAllowed: vi.fn().mockReturnValue(false),
|
|
314
|
-
getDataAccessPermissions: vi.fn().mockReturnValue({}),
|
|
315
|
-
validateDataAccess: vi.fn().mockReturnValue(false),
|
|
316
|
-
// Add other required properties
|
|
317
|
-
} as any);
|
|
318
|
-
|
|
366
|
+
// useSecureDataAccess has been removed - SecureDataProvider handles this internally
|
|
319
367
|
const TestConsumer = () => {
|
|
320
368
|
const context = useSecureData();
|
|
321
369
|
context.isDataAccessAllowed('admin_users', 'delete', mockScope);
|
|
@@ -340,14 +388,7 @@ describe('SecureDataProvider', () => {
|
|
|
340
388
|
|
|
341
389
|
describe('Error Handling', () => {
|
|
342
390
|
it('handles data access validation errors gracefully', async () => {
|
|
343
|
-
|
|
344
|
-
isDataAccessAllowed: vi.fn().mockImplementation(() => {
|
|
345
|
-
throw new Error('Data access validation failed');
|
|
346
|
-
}),
|
|
347
|
-
getDataAccessPermissions: vi.fn().mockReturnValue({}),
|
|
348
|
-
validateDataAccess: vi.fn().mockReturnValue(false),
|
|
349
|
-
// Add other required properties
|
|
350
|
-
} as any);
|
|
391
|
+
// useSecureDataAccess has been removed - SecureDataProvider handles this internally
|
|
351
392
|
|
|
352
393
|
const TestConsumer = () => {
|
|
353
394
|
const context = useSecureData();
|
|
@@ -462,7 +503,7 @@ describe('SecureDataProvider', () => {
|
|
|
462
503
|
});
|
|
463
504
|
|
|
464
505
|
describe('Integration with useSecureDataAccess', () => {
|
|
465
|
-
it('integrates with
|
|
506
|
+
it('integrates with secure data access system', () => {
|
|
466
507
|
const TestConsumer = () => {
|
|
467
508
|
const context = useSecureData();
|
|
468
509
|
context.isDataAccessAllowed(mockTable, mockOperation, mockScope);
|
|
@@ -476,19 +517,12 @@ describe('SecureDataProvider', () => {
|
|
|
476
517
|
</SecureDataProvider>
|
|
477
518
|
);
|
|
478
519
|
|
|
479
|
-
|
|
520
|
+
// useSecureDataAccess has been removed - this test verifies the component works
|
|
521
|
+
// expect(mockUseSecureDataAccess).toHaveBeenCalled();
|
|
480
522
|
});
|
|
481
523
|
|
|
482
524
|
it('passes through secure data access functionality', () => {
|
|
483
|
-
|
|
484
|
-
isDataAccessAllowed: vi.fn().mockReturnValue(true),
|
|
485
|
-
getDataAccessPermissions: vi.fn().mockReturnValue({}),
|
|
486
|
-
validateDataAccess: vi.fn().mockReturnValue(true),
|
|
487
|
-
// Add other required properties
|
|
488
|
-
};
|
|
489
|
-
|
|
490
|
-
mockUseSecureDataAccess.mockReturnValue(mockSecureDataAccess as any);
|
|
491
|
-
|
|
525
|
+
// useSecureDataAccess has been removed - SecureDataProvider handles this internally
|
|
492
526
|
const TestConsumer = () => {
|
|
493
527
|
const context = useSecureData();
|
|
494
528
|
context.isDataAccessAllowed(mockTable, mockOperation, mockScope);
|
|
@@ -502,7 +536,8 @@ describe('SecureDataProvider', () => {
|
|
|
502
536
|
</SecureDataProvider>
|
|
503
537
|
);
|
|
504
538
|
|
|
505
|
-
|
|
539
|
+
// Component should render without errors
|
|
540
|
+
expect(screen.getByText('Test')).toBeInTheDocument();
|
|
506
541
|
});
|
|
507
542
|
});
|
|
508
543
|
});
|
|
@@ -52,13 +52,14 @@
|
|
|
52
52
|
* @dependencies
|
|
53
53
|
* - React 19+ - Context and hooks
|
|
54
54
|
* - useUnifiedAuth - Authentication context
|
|
55
|
-
* -
|
|
55
|
+
* - useSecureSupabase - Secure Supabase client hook
|
|
56
56
|
* - RBAC types - Type definitions
|
|
57
57
|
*/
|
|
58
58
|
|
|
59
59
|
import React, { createContext, useContext, useState, useCallback, useMemo, useEffect } from 'react';
|
|
60
60
|
import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
|
|
61
|
-
import {
|
|
61
|
+
import { useSecureSupabase } from '../hooks/useSecureSupabase';
|
|
62
|
+
import { useOrganisationSecurity } from '../../hooks/useOrganisationSecurity';
|
|
62
63
|
import { useResolvedScope } from '../hooks/useResolvedScope';
|
|
63
64
|
import { UUID, Scope, Permission } from '../types';
|
|
64
65
|
import { getRBACLogger } from '../config';
|
|
@@ -144,7 +145,8 @@ export function SecureDataProvider({
|
|
|
144
145
|
enforceRLS = true
|
|
145
146
|
}: SecureDataProviderProps) {
|
|
146
147
|
const { user, selectedOrganisation, selectedEvent, supabase } = useUnifiedAuth();
|
|
147
|
-
const
|
|
148
|
+
const secureSupabase = useSecureSupabase(supabase);
|
|
149
|
+
const { superAdminContext } = useOrganisationSecurity();
|
|
148
150
|
const [dataAccessHistory, setDataAccessHistory] = useState<DataAccessRecord[]>([]);
|
|
149
151
|
const [isEnabled, setIsEnabled] = useState(true);
|
|
150
152
|
|
|
@@ -154,6 +156,19 @@ export function SecureDataProvider({
|
|
|
154
156
|
selectedOrganisationId: selectedOrganisation?.id || null,
|
|
155
157
|
selectedEventId: selectedEvent?.event_id || null
|
|
156
158
|
});
|
|
159
|
+
|
|
160
|
+
// Validate context - similar to useSecureDataAccess.validateContext
|
|
161
|
+
const validateContext = useCallback((): void => {
|
|
162
|
+
if (!secureSupabase) {
|
|
163
|
+
throw new Error('No Supabase client available');
|
|
164
|
+
}
|
|
165
|
+
if (!user) {
|
|
166
|
+
throw new Error('User must be authenticated');
|
|
167
|
+
}
|
|
168
|
+
if (!superAdminContext.isSuperAdmin && !resolvedScope?.organisationId) {
|
|
169
|
+
throw new Error('Organisation context is required for data access');
|
|
170
|
+
}
|
|
171
|
+
}, [secureSupabase, user, superAdminContext.isSuperAdmin, resolvedScope?.organisationId]);
|
|
157
172
|
|
|
158
173
|
// Get current scope from resolved scope
|
|
159
174
|
// For event-required apps: org is derived from event
|
|
@@ -174,10 +189,10 @@ export function SecureDataProvider({
|
|
|
174
189
|
|
|
175
190
|
// Use the existing RBAC system to check data access permissions
|
|
176
191
|
// This is a synchronous check for the context - actual permission checking
|
|
177
|
-
// happens
|
|
192
|
+
// happens using the RBAC engine via useSecureSupabase
|
|
178
193
|
const permission = `${operation}:data.${table}` as Permission;
|
|
179
194
|
|
|
180
|
-
// For now, we'll return true and let the
|
|
195
|
+
// For now, we'll return true and let the RBAC system
|
|
181
196
|
// handle the actual permission checking asynchronously
|
|
182
197
|
// This context is mainly for tracking and audit purposes
|
|
183
198
|
return true;
|