@jmruthers/pace-core 0.5.76 → 0.5.78
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 +8 -0
- package/dist/{RBACService-C4udt_Zp.d.ts → AuthService-Df3IozMG.d.ts} +10 -118
- package/dist/{DataTable-ntgmhO2W.d.ts → DataTable-BE0OXZKQ.d.ts} +9 -2
- package/dist/{DataTable-4GAVPIEG.js → DataTable-ETGVF4Y5.js} +50 -13
- package/dist/{PublicLoadingSpinner-BiNER8F5.d.ts → PublicLoadingSpinner-CnUaz0vG.d.ts} +5 -2
- package/dist/{UnifiedAuthProvider-Bj6YCf7c.d.ts → UnifiedAuthProvider-B391Aqum.d.ts} +42 -45
- package/dist/{UnifiedAuthProvider-3NKDOSOK.js → UnifiedAuthProvider-P5SOJAQ6.js} +4 -5
- package/dist/{api-DDMUKIUD.js → api-KG4A2X7P.js} +9 -3
- package/dist/{audit-6TOCAMKO.js → audit-65VNHEV2.js} +2 -2
- package/dist/{chunk-K34IM5CT.js → chunk-2OGV6IRV.js} +196 -626
- package/dist/chunk-2OGV6IRV.js.map +1 -0
- package/dist/{chunk-NTNILOBC.js → chunk-5BO3MI5Y.js} +4 -4
- package/dist/{chunk-XLZ7U46Z.js → chunk-CVMVPYAL.js} +9 -60
- package/dist/chunk-CVMVPYAL.js.map +1 -0
- package/dist/{chunk-URUTVZ7N.js → chunk-FL4ZCQLD.js} +2 -2
- package/dist/{chunk-LW7MMEAQ.js → chunk-FT2M4R4F.js} +2 -2
- package/dist/{chunk-5BSLGBYI.js → chunk-JCQZ6LA7.js} +2 -8
- package/dist/{chunk-5BSLGBYI.js.map → chunk-JCQZ6LA7.js.map} +1 -1
- package/dist/{chunk-KHJS6VIA.js → chunk-LRQ6RBJC.js} +157 -112
- package/dist/chunk-LRQ6RBJC.js.map +1 -0
- package/dist/{chunk-WN6XJWOS.js → chunk-MNJXXD6C.js} +274 -743
- package/dist/chunk-MNJXXD6C.js.map +1 -0
- package/dist/{chunk-KK73ZB4E.js → chunk-PTR5PMPE.js} +153 -132
- package/dist/chunk-PTR5PMPE.js.map +1 -0
- package/dist/{chunk-B2WTCLCV.js → chunk-Q7APDV6H.js} +18 -8
- package/dist/chunk-Q7APDV6H.js.map +1 -0
- package/dist/{chunk-A4FUBC7B.js → chunk-QGVSOUJ2.js} +2 -4
- package/dist/{chunk-A4FUBC7B.js.map → chunk-QGVSOUJ2.js.map} +1 -1
- package/dist/{chunk-FGMFQSHX.js → chunk-S63MFSY6.js} +500 -551
- package/dist/chunk-S63MFSY6.js.map +1 -0
- package/dist/{chunk-AFGTSUAD.js → chunk-VSOKOFRF.js} +4 -4
- package/dist/chunk-WUXCWRL6.js +20 -0
- package/dist/chunk-WUXCWRL6.js.map +1 -0
- package/dist/{chunk-Y6TXWPJO.js → chunk-YVVGHRGI.js} +105 -31
- package/dist/chunk-YVVGHRGI.js.map +1 -0
- package/dist/{chunk-M5IWZRBT.js → chunk-ZMNXIJP4.js} +2187 -981
- package/dist/chunk-ZMNXIJP4.js.map +1 -0
- package/dist/components.d.ts +6 -6
- package/dist/components.js +14 -18
- package/dist/components.js.map +1 -1
- package/dist/{database-C3Szpi5J.d.ts → database-BXAfr2Y_.d.ts} +18 -0
- package/dist/hooks.d.ts +5 -5
- package/dist/hooks.js +8 -9
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +19 -27
- package/dist/index.js +21 -29
- package/dist/index.js.map +1 -1
- package/dist/{organisation-BtshODVF.d.ts → organisation-D6qRDtbF.d.ts} +1 -1
- package/dist/providers.d.ts +7 -21
- package/dist/providers.js +3 -10
- package/dist/rbac/index.d.ts +71 -221
- package/dist/rbac/index.js +15 -16
- package/dist/{types-CGX9Vyf5.d.ts → types-BDg1mAGG.d.ts} +36 -6
- package/dist/types.d.ts +3 -3
- package/dist/types.js +61 -18
- package/dist/types.js.map +1 -1
- package/dist/{unified-CM7T0aTK.d.ts → unified-DQ4VcT7H.d.ts} +1 -1
- package/dist/{usePublicRouteParams-B-CumWRc.d.ts → usePublicRouteParams-BlgwXweB.d.ts} +3 -3
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +52 -9
- package/dist/utils.js.map +1 -1
- package/docs/CONTENT_AUDIT_REPORT.md +253 -0
- package/docs/DOCUMENTATION_AUDIT.md +172 -0
- package/docs/README.md +142 -147
- package/docs/STYLE_GUIDE.md +37 -0
- package/docs/api/classes/ColumnFactory.md +17 -17
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +4 -4
- package/docs/api/classes/MissingUserContextError.md +4 -4
- package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
- package/docs/api/classes/PermissionDeniedError.md +5 -5
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +8 -8
- package/docs/api/classes/RBACCache.md +35 -5
- package/docs/api/classes/RBACEngine.md +49 -20
- package/docs/api/classes/RBACError.md +4 -4
- package/docs/api/classes/RBACNotInitializedError.md +4 -4
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +4 -4
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +11 -0
- package/docs/api/interfaces/DataTableAction.md +65 -29
- package/docs/api/interfaces/DataTableColumn.md +36 -23
- package/docs/api/interfaces/DataTableProps.md +80 -38
- package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
- package/docs/api/interfaces/EmptyStateConfig.md +5 -5
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +11 -11
- package/docs/api/interfaces/NavigationContextType.md +9 -9
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +7 -7
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +16 -3
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +2 -2
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +2 -2
- package/docs/api/interfaces/RouteConfig.md +2 -2
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +94 -521
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +16 -16
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +11 -11
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +251 -269
- package/docs/api-reference/components.md +193 -0
- package/docs/api-reference/hooks.md +265 -0
- package/docs/api-reference/providers.md +6 -0
- package/docs/api-reference/types.md +6 -0
- package/docs/api-reference/utilities.md +207 -0
- package/docs/architecture/README.md +6 -0
- package/docs/{database-schema-requirements.md → architecture/database-schema-requirements.md} +6 -0
- package/docs/architecture/rbac-security-architecture.md +258 -0
- package/docs/architecture/services.md +9 -1
- package/docs/best-practices/README.md +6 -0
- package/docs/best-practices/accessibility.md +6 -0
- package/docs/{common-patterns.md → best-practices/common-patterns.md} +6 -0
- package/docs/best-practices/deployment.md +6 -0
- package/docs/best-practices/performance.md +475 -2
- package/docs/best-practices/security.md +6 -0
- package/docs/best-practices/testing.md +6 -0
- package/docs/core-concepts/authentication.md +6 -0
- package/docs/core-concepts/events.md +6 -0
- package/docs/core-concepts/organisations.md +6 -0
- package/docs/core-concepts/permissions.md +6 -0
- package/docs/core-concepts/rbac-system.md +8 -0
- package/docs/documentation-index.md +121 -182
- package/docs/{consuming-app-vite-config.md → getting-started/consuming-app-vite-config.md} +6 -0
- package/docs/getting-started/documentation-index.md +40 -0
- package/docs/getting-started/examples/README.md +878 -35
- package/docs/{faq.md → getting-started/faq.md} +7 -1
- package/docs/getting-started/installation-guide.md +6 -0
- package/docs/{quick-reference.md → getting-started/quick-reference.md} +6 -0
- package/docs/implementation-guides/app-layout.md +6 -0
- package/docs/implementation-guides/authentication.md +1021 -0
- package/docs/implementation-guides/component-styling.md +6 -0
- package/docs/implementation-guides/data-tables.md +1264 -2076
- package/docs/implementation-guides/dynamic-colors.md +6 -0
- package/docs/implementation-guides/event-theming-summary.md +6 -0
- package/docs/{file-reference-system.md → implementation-guides/file-reference-system.md} +6 -0
- package/docs/implementation-guides/file-upload-storage.md +6 -0
- package/docs/implementation-guides/forms.md +6 -0
- package/docs/implementation-guides/inactivity-tracking.md +6 -0
- package/docs/implementation-guides/navigation.md +6 -0
- package/docs/implementation-guides/organisation-security.md +6 -0
- package/docs/implementation-guides/permission-enforcement.md +6 -0
- package/docs/implementation-guides/public-pages-advanced.md +6 -0
- package/docs/implementation-guides/public-pages.md +6 -0
- package/docs/migration/MIGRATION_GUIDE.md +827 -351
- package/docs/migration/README.md +7 -1
- package/docs/migration/organisation-context-timing-fix.md +6 -0
- package/docs/migration/rbac-migration.md +44 -1
- package/docs/migration/service-architecture.md +6 -0
- package/docs/migration/v0.4.15-tailwind-scanning.md +6 -0
- package/docs/migration/v0.4.16-css-first-approach.md +6 -0
- package/docs/migration/v0.4.17-source-path-fix.md +6 -0
- package/docs/rbac/README-rbac-rls-integration.md +6 -0
- package/docs/rbac/README.md +6 -0
- package/docs/rbac/advanced-patterns.md +6 -0
- package/docs/rbac/api-reference.md +7 -1
- package/docs/rbac/breaking-changes-v3.md +222 -0
- package/docs/rbac/examples/rbac-rls-integration-example.md +6 -0
- package/docs/rbac/examples.md +6 -0
- package/docs/rbac/getting-started.md +6 -0
- package/docs/rbac/migration-guide.md +260 -0
- package/docs/rbac/quick-start.md +70 -13
- package/docs/rbac/rbac-rls-integration.md +6 -0
- package/docs/rbac/super-admin-guide.md +6 -0
- package/docs/rbac/troubleshooting.md +6 -0
- package/docs/security/README.md +6 -0
- package/docs/security/checklist.md +6 -0
- package/docs/styles/README.md +7 -1
- package/docs/{usage.md → styles/usage.md} +6 -0
- package/docs/testing/README.md +6 -0
- package/docs/{visual-testing.md → testing/visual-testing.md} +6 -0
- package/docs/troubleshooting/README.md +387 -5
- package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +6 -0
- package/docs/troubleshooting/common-issues.md +6 -0
- package/docs/troubleshooting/database-view-compatibility.md +6 -0
- package/docs/troubleshooting/organisation-context-setup.md +6 -0
- package/docs/troubleshooting/react-hooks-issue-analysis.md +6 -0
- package/docs/troubleshooting/styling-issues.md +6 -0
- package/docs/troubleshooting/tailwind-content-scanning.md +6 -0
- package/package.json +1 -1
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -1
- package/src/__tests__/helpers/test-providers.tsx +3 -53
- package/src/components/DataTable/DataTable.test.tsx +319 -0
- package/src/components/DataTable/DataTable.tsx +32 -11
- package/src/components/DataTable/__tests__/{DataTable.comprehensive.test.tsx → DataTable.comprehensive.test.tsx.skip} +6 -4
- package/src/components/DataTable/__tests__/{DataTable.test.tsx → DataTable.test.tsx.skip} +6 -4
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +31 -9
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +601 -0
- package/src/components/DataTable/__tests__/keyboard.test.tsx +615 -0
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +639 -0
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx.skip +330 -0
- package/src/components/DataTable/components/AccessDeniedPage.tsx +2 -2
- package/src/components/DataTable/components/ActionButtons.tsx +88 -104
- package/src/components/DataTable/components/DataTableCore.tsx +309 -337
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +4 -2
- package/src/components/DataTable/components/DataTableModals.tsx +22 -1
- package/src/components/DataTable/components/EditableRow.tsx +69 -84
- package/src/components/DataTable/components/EmptyState.tsx +5 -1
- package/src/components/DataTable/components/ImportModal.tsx +65 -36
- package/src/components/DataTable/components/PaginationControls.tsx +40 -100
- package/src/components/DataTable/components/UnifiedTableBody.tsx +125 -148
- package/src/components/DataTable/context/DataTableContext.tsx +1 -1
- package/src/components/DataTable/core/ColumnFactory.ts +5 -0
- package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +12 -10
- package/src/components/DataTable/examples/HierarchicalExample.tsx +1 -1
- package/src/components/DataTable/examples/InitialPageSizeExample.tsx +1 -0
- package/src/components/DataTable/examples/PerformanceExample.tsx +1 -0
- package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +1 -5
- package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +167 -0
- package/src/components/DataTable/hooks/index.ts +7 -0
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +32 -15
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +102 -0
- package/src/components/DataTable/hooks/useDataTableConfiguration.ts +89 -0
- package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +117 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +71 -27
- package/src/components/DataTable/hooks/useDataTableState.ts +39 -11
- package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +33 -0
- package/src/components/DataTable/hooks/useHierarchicalState.ts +15 -1
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +447 -0
- package/src/components/DataTable/hooks/useServerSideDataEffect.ts +94 -0
- package/src/components/DataTable/hooks/useTableColumns.ts +10 -7
- package/src/components/DataTable/hooks/useTableHandlers.ts +174 -0
- package/src/components/DataTable/index.ts +12 -3
- package/src/components/DataTable/types.ts +129 -9
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +159 -22
- package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +111 -0
- package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +15 -29
- package/src/components/DataTable/utils/a11yUtils.ts +244 -0
- package/src/components/DataTable/utils/debugTools.ts +609 -0
- package/src/components/DataTable/utils/exportUtils.ts +114 -16
- package/src/components/DataTable/utils/flexibleImport.ts +202 -32
- package/src/components/DataTable/utils/hierarchicalUtils.ts +1 -1
- package/src/components/DataTable/utils/index.ts +2 -0
- package/src/components/DataTable/utils/paginationUtils.ts +350 -0
- package/src/components/DataTable/utils/rowUtils.ts +6 -5
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +19 -24
- package/src/components/NavigationMenu/NavigationMenu.tsx +19 -8
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +1 -23
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +56 -6
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +137 -13
- package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +1 -1
- package/src/components/Select/Select.tsx +1 -0
- package/src/components/examples/PermissionExample.tsx +173 -0
- package/src/examples/CorrectPublicPageImplementation.tsx +301 -0
- package/src/examples/PublicEventPage.tsx +274 -0
- package/src/examples/PublicPageApp.tsx +308 -0
- package/src/examples/PublicPageUsageExample.tsx +216 -0
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +12 -1
- package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +129 -17
- package/src/hooks/__tests__/useRBAC.unit.test.ts +151 -846
- package/src/hooks/useOrganisationPermissions.test.ts +42 -18
- package/src/hooks/useOrganisationPermissions.ts +12 -6
- package/src/hooks/useOrganisationSecurity.test.ts +138 -85
- package/src/hooks/useOrganisationSecurity.ts +41 -10
- package/src/index.ts +0 -1
- package/src/providers/AuthProvider.simplified.tsx +880 -0
- package/src/providers/UnifiedAuthProvider.test.simple.tsx +8 -8
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +29 -19
- package/src/providers/index.ts +0 -1
- package/src/providers/services/EventServiceProvider.tsx +19 -15
- package/src/providers/services/InactivityServiceProvider.tsx +19 -15
- package/src/providers/services/OrganisationServiceProvider.tsx +19 -15
- package/src/providers/services/UnifiedAuthProvider.tsx +156 -127
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +1 -1
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -3
- package/src/rbac/README.md +1 -1
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +25 -27
- package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +313 -0
- package/src/rbac/__tests__/engine.comprehensive.test.ts +114 -348
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +28 -110
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +33 -85
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +2 -2
- package/src/rbac/adapters.tsx +26 -69
- package/src/rbac/api.test.ts +90 -27
- package/src/rbac/api.ts +61 -10
- package/src/rbac/audit.test.ts +33 -38
- package/src/rbac/audit.ts +21 -6
- package/src/rbac/cache.ts +33 -1
- package/src/rbac/components/NavigationGuard.tsx +11 -11
- package/src/rbac/components/NavigationProvider.test.tsx +11 -5
- package/src/rbac/components/NavigationProvider.tsx +37 -13
- package/src/rbac/components/PagePermissionGuard.tsx +111 -50
- package/src/rbac/components/PagePermissionProvider.tsx +5 -5
- package/src/rbac/components/PermissionEnforcer.tsx +11 -11
- package/src/rbac/components/RoleBasedRouter.tsx +5 -5
- package/src/rbac/components/SecureDataProvider.tsx +5 -5
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +8 -8
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +14 -14
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +12 -12
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +6 -6
- package/src/rbac/engine.test.simple.ts +19 -13
- package/src/rbac/engine.test.ts +1 -0
- package/src/rbac/engine.ts +330 -766
- package/src/rbac/errors.ts +156 -0
- package/src/rbac/hooks/usePermissions.ts +32 -10
- package/src/rbac/hooks/useRBAC.test.ts +126 -512
- package/src/rbac/hooks/useRBAC.ts +147 -193
- package/src/rbac/hooks/useResolvedScope.ts +12 -0
- package/src/rbac/index.ts +7 -4
- package/src/rbac/security.ts +109 -18
- package/src/rbac/types.ts +12 -1
- package/src/services/AuthService.ts +2 -15
- package/src/services/EventService.ts +43 -46
- package/src/services/OrganisationService.ts +51 -31
- package/src/services/__tests__/AuthService.test.ts +1 -1
- package/src/services/__tests__/EventService.test.ts +1 -1
- package/src/services/__tests__/OrganisationService.test.ts +1 -1
- package/src/services/base/BaseService.ts +8 -0
- package/src/styles/base.css +208 -0
- package/src/styles/semantic.css +24 -0
- package/src/types/database.generated.ts +7347 -0
- package/src/types/database.ts +20 -0
- package/src/utils/logger.ts +179 -0
- package/src/utils/organisationContext.ts +11 -4
- package/src/utils/storage/__tests__/helpers.unit.test.ts +6 -2
- package/dist/appNameResolver-UURKN7NF.js +0 -22
- package/dist/audit-6TOCAMKO.js.map +0 -1
- package/dist/chunk-B2WTCLCV.js.map +0 -1
- package/dist/chunk-FGMFQSHX.js.map +0 -1
- package/dist/chunk-K34IM5CT.js.map +0 -1
- package/dist/chunk-KHJS6VIA.js.map +0 -1
- package/dist/chunk-KK73ZB4E.js.map +0 -1
- package/dist/chunk-M5IWZRBT.js.map +0 -1
- package/dist/chunk-ULBI5JGB.js +0 -109
- package/dist/chunk-ULBI5JGB.js.map +0 -1
- package/dist/chunk-WN6XJWOS.js.map +0 -1
- package/dist/chunk-XLZ7U46Z.js.map +0 -1
- package/dist/chunk-Y6TXWPJO.js.map +0 -1
- package/docs/DOCUMENTATION_CHECKLIST.md +0 -281
- package/docs/TERMINOLOGY.md +0 -231
- package/docs/api/interfaces/RBACContextType.md +0 -468
- package/docs/api/interfaces/RBACProviderProps.md +0 -107
- package/docs/best-practices/performance-expansion.md +0 -473
- package/docs/breaking-changes.md +0 -179
- package/docs/consuming-app-example.md +0 -290
- package/docs/documentation-templates.md +0 -539
- package/docs/examples/navigation-menu-auth-fix.md +0 -344
- package/docs/getting-started/examples/basic-auth-app.md +0 -520
- package/docs/getting-started/examples/full-featured-app.md +0 -616
- package/docs/getting-started/quick-start.md +0 -376
- package/docs/implementation-guides/datatable-filtering.md +0 -313
- package/docs/implementation-guides/datatable-rbac-usage.md +0 -317
- package/docs/implementation-guides/hierarchical-datatable.md +0 -850
- package/docs/implementation-guides/large-datasets.md +0 -281
- package/docs/implementation-guides/performance.md +0 -403
- package/docs/migration/quick-migration-guide.md +0 -320
- package/docs/migration-guide.md +0 -193
- package/docs/migration-guides/unified-auth-provider-mandatory-timeouts.md +0 -226
- package/docs/performance/README.md +0 -551
- package/docs/style-guide.md +0 -964
- package/docs/troubleshooting/authentication-issues.md +0 -334
- package/docs/troubleshooting/debugging.md +0 -1117
- package/docs/troubleshooting/migration.md +0 -918
- package/src/__tests__/hooks/usePermissions.test.ts +0 -261
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +0 -574
- package/src/hooks/__tests__/ServiceHooks.test.tsx +0 -613
- package/src/hooks/services/__tests__/useServiceHooks.test.tsx +0 -137
- package/src/hooks/services/usePermissions.ts +0 -70
- package/src/hooks/services/useRBACService.ts +0 -30
- package/src/hooks/usePermissionCheck.ts +0 -150
- package/src/providers/__tests__/ServiceProviders.test.tsx +0 -477
- package/src/providers/services/RBACServiceProvider.tsx +0 -79
- package/src/rbac/__tests__/integration.authflow.test.tsx +0 -119
- package/src/rbac/__tests__/integration.navigation.test.tsx +0 -69
- package/src/rbac/__tests__/integration.securedata.test.tsx +0 -92
- package/src/rbac/__tests__/integration.smoke.test.tsx +0 -73
- package/src/rbac/providers/RBACProvider.tsx +0 -645
- package/src/rbac/providers/__tests__/RBACProvider.integration.test.tsx +0 -688
- package/src/rbac/providers/__tests__/RBACProvider.test.tsx +0 -1186
- package/src/rbac/providers/index.ts +0 -11
- package/src/services/RBACService.ts +0 -522
- package/src/services/__tests__/RBACService.test.ts +0 -492
- package/src/services/interfaces/IRBACService.ts +0 -62
- package/src/utils/appNameResolver.test 2.ts +0 -494
- /package/dist/{DataTable-4GAVPIEG.js.map → DataTable-ETGVF4Y5.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-3NKDOSOK.js.map → UnifiedAuthProvider-P5SOJAQ6.js.map} +0 -0
- /package/dist/{api-DDMUKIUD.js.map → api-KG4A2X7P.js.map} +0 -0
- /package/dist/{appNameResolver-UURKN7NF.js.map → audit-65VNHEV2.js.map} +0 -0
- /package/dist/{chunk-NTNILOBC.js.map → chunk-5BO3MI5Y.js.map} +0 -0
- /package/dist/{chunk-URUTVZ7N.js.map → chunk-FL4ZCQLD.js.map} +0 -0
- /package/dist/{chunk-LW7MMEAQ.js.map → chunk-FT2M4R4F.js.map} +0 -0
- /package/dist/{chunk-AFGTSUAD.js.map → chunk-VSOKOFRF.js.map} +0 -0
- /package/docs/{app.css.example → styles/app.css.example} +0 -0
package/src/rbac/api.ts
CHANGED
|
@@ -9,19 +9,22 @@
|
|
|
9
9
|
|
|
10
10
|
import { SupabaseClient } from '@supabase/supabase-js';
|
|
11
11
|
import { Database } from '../types/database';
|
|
12
|
-
import {
|
|
13
|
-
UUID,
|
|
14
|
-
Scope,
|
|
15
|
-
Permission,
|
|
16
|
-
AccessLevel,
|
|
17
|
-
PermissionMap,
|
|
12
|
+
import {
|
|
13
|
+
UUID,
|
|
14
|
+
Scope,
|
|
15
|
+
Permission,
|
|
16
|
+
AccessLevel,
|
|
17
|
+
PermissionMap,
|
|
18
18
|
PermissionCheck,
|
|
19
|
-
RBACNotInitializedError
|
|
19
|
+
RBACNotInitializedError,
|
|
20
|
+
RBACAppContext,
|
|
21
|
+
RBACRoleContext,
|
|
20
22
|
} from './types';
|
|
21
23
|
import { createRBACEngine, RBACEngine } from './engine';
|
|
22
24
|
import { createAuditManager, setGlobalAuditManager } from './audit';
|
|
23
25
|
import { rbacCache, RBACCache, CACHE_PATTERNS } from './cache';
|
|
24
26
|
import { createRBACConfig, RBACConfig, getRBACLogger } from './config';
|
|
27
|
+
import { SecurityContext } from './security';
|
|
25
28
|
|
|
26
29
|
// Global engine instance
|
|
27
30
|
let globalEngine: RBACEngine | null = null;
|
|
@@ -116,6 +119,22 @@ export async function getPermissionMap(input: {
|
|
|
116
119
|
return engine.getPermissionMap(input);
|
|
117
120
|
}
|
|
118
121
|
|
|
122
|
+
export async function resolveAppContext(input: {
|
|
123
|
+
userId: UUID;
|
|
124
|
+
appName: string;
|
|
125
|
+
}): Promise<RBACAppContext | null> {
|
|
126
|
+
const engine = getEngine();
|
|
127
|
+
return engine.resolveAppContext(input);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export async function getRoleContext(input: {
|
|
131
|
+
userId: UUID;
|
|
132
|
+
scope: Scope;
|
|
133
|
+
}): Promise<RBACRoleContext> {
|
|
134
|
+
const engine = getEngine();
|
|
135
|
+
return engine.getRoleContext(input);
|
|
136
|
+
}
|
|
137
|
+
|
|
119
138
|
/**
|
|
120
139
|
* Check if user has a specific permission
|
|
121
140
|
*
|
|
@@ -134,7 +153,17 @@ export async function getPermissionMap(input: {
|
|
|
134
153
|
*/
|
|
135
154
|
export async function isPermitted(input: PermissionCheck): Promise<boolean> {
|
|
136
155
|
const engine = getEngine();
|
|
137
|
-
|
|
156
|
+
|
|
157
|
+
// Create security context from input
|
|
158
|
+
// SecurityContext requires organisationId as UUID, so we use a valid fallback
|
|
159
|
+
const securityContext: SecurityContext = {
|
|
160
|
+
userId: input.userId,
|
|
161
|
+
organisationId: input.scope.organisationId || input.userId, // Fallback to userId as UUID
|
|
162
|
+
timestamp: new Date(),
|
|
163
|
+
// Optional fields can be omitted
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
return engine.isPermitted(input, securityContext);
|
|
138
167
|
}
|
|
139
168
|
|
|
140
169
|
/**
|
|
@@ -254,8 +283,30 @@ export async function isSuperAdmin(userId: UUID): Promise<boolean> {
|
|
|
254
283
|
* @returns Promise resolving to app configuration
|
|
255
284
|
*/
|
|
256
285
|
export async function getAppConfig(appId: UUID): Promise<{ requires_event: boolean } | null> {
|
|
257
|
-
|
|
258
|
-
|
|
286
|
+
// This function requires a Supabase client to be provided
|
|
287
|
+
// Callers should pass the client as a parameter
|
|
288
|
+
console.warn('[RBAC] getAppConfig called without Supabase client - returning null');
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export async function getAppConfigWithClient(client: SupabaseClient, appId: UUID): Promise<{ requires_event: boolean } | null> {
|
|
293
|
+
try {
|
|
294
|
+
const { data, error } = await client
|
|
295
|
+
.from('rbac_apps')
|
|
296
|
+
.select('requires_event')
|
|
297
|
+
.eq('id', appId)
|
|
298
|
+
.eq('is_active', true)
|
|
299
|
+
.single() as { data: { requires_event: boolean } | null; error: any };
|
|
300
|
+
|
|
301
|
+
if (error || !data) {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return { requires_event: data.requires_event };
|
|
306
|
+
} catch (err) {
|
|
307
|
+
console.error('[RBAC] Error fetching app config:', err);
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
259
310
|
}
|
|
260
311
|
|
|
261
312
|
/**
|
package/src/rbac/audit.test.ts
CHANGED
|
@@ -86,24 +86,23 @@ describe('RBACAuditManager', () => {
|
|
|
86
86
|
await auditManager.emitPermissionCheck(event);
|
|
87
87
|
|
|
88
88
|
expect(mockSupabase.from).toHaveBeenCalledWith('rbac_audit_events');
|
|
89
|
-
expect(mockSupabase.insert).toHaveBeenCalledWith(
|
|
90
|
-
expect.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
);
|
|
89
|
+
expect(mockSupabase.insert).toHaveBeenCalledWith([
|
|
90
|
+
expect.objectContaining({
|
|
91
|
+
event_type: 'permission_check',
|
|
92
|
+
user_id: 'user-123',
|
|
93
|
+
organisation_id: 'org-456',
|
|
94
|
+
event_id: 'event-789',
|
|
95
|
+
app_id: 'app-101',
|
|
96
|
+
page_id: 'page-202',
|
|
97
|
+
permission: 'read:users',
|
|
98
|
+
decision: true,
|
|
99
|
+
source: 'api',
|
|
100
|
+
bypass: false,
|
|
101
|
+
duration_ms: 150,
|
|
102
|
+
metadata: expect.objectContaining({ ip: '192.168.1.1' })
|
|
103
|
+
})
|
|
104
|
+
]
|
|
105
|
+
);
|
|
107
106
|
});
|
|
108
107
|
|
|
109
108
|
it('handles permission check events without optional fields', async () => {
|
|
@@ -152,9 +151,8 @@ describe('RBACAuditManager', () => {
|
|
|
152
151
|
await auditManager.emitPermissionDenied(event);
|
|
153
152
|
|
|
154
153
|
expect(mockSupabase.from).toHaveBeenCalledWith('rbac_audit_events');
|
|
155
|
-
expect(mockSupabase.insert).toHaveBeenCalledWith(
|
|
156
|
-
expect.
|
|
157
|
-
expect.objectContaining({
|
|
154
|
+
expect(mockSupabase.insert).toHaveBeenCalledWith([
|
|
155
|
+
expect.objectContaining({
|
|
158
156
|
event_type: 'permission_denied',
|
|
159
157
|
user_id: 'user-123',
|
|
160
158
|
organisation_id: 'org-456',
|
|
@@ -163,9 +161,9 @@ describe('RBACAuditManager', () => {
|
|
|
163
161
|
page_id: 'page-202',
|
|
164
162
|
permission: 'manage:users',
|
|
165
163
|
source: 'api',
|
|
166
|
-
metadata: { reason: 'Insufficient role' }
|
|
164
|
+
metadata: expect.objectContaining({ reason: 'Insufficient role' })
|
|
167
165
|
})
|
|
168
|
-
]
|
|
166
|
+
]
|
|
169
167
|
);
|
|
170
168
|
});
|
|
171
169
|
});
|
|
@@ -186,18 +184,17 @@ describe('RBACAuditManager', () => {
|
|
|
186
184
|
await auditManager.emitRoleGranted(event);
|
|
187
185
|
|
|
188
186
|
expect(mockSupabase.from).toHaveBeenCalledWith('rbac_audit_events');
|
|
189
|
-
expect(mockSupabase.insert).toHaveBeenCalledWith(
|
|
190
|
-
expect.
|
|
191
|
-
expect.objectContaining({
|
|
187
|
+
expect(mockSupabase.insert).toHaveBeenCalledWith([
|
|
188
|
+
expect.objectContaining({
|
|
192
189
|
event_type: 'role_granted',
|
|
193
190
|
user_id: 'user-123',
|
|
194
191
|
organisation_id: 'org-456',
|
|
195
192
|
event_id: 'event-789',
|
|
196
193
|
app_id: 'app-101',
|
|
197
194
|
source: 'api',
|
|
198
|
-
metadata: { reason: 'Promotion' }
|
|
195
|
+
metadata: expect.objectContaining({ reason: 'Promotion' })
|
|
199
196
|
})
|
|
200
|
-
]
|
|
197
|
+
]
|
|
201
198
|
);
|
|
202
199
|
});
|
|
203
200
|
});
|
|
@@ -218,18 +215,17 @@ describe('RBACAuditManager', () => {
|
|
|
218
215
|
await auditManager.emitRoleRevoked(event);
|
|
219
216
|
|
|
220
217
|
expect(mockSupabase.from).toHaveBeenCalledWith('rbac_audit_events');
|
|
221
|
-
expect(mockSupabase.insert).toHaveBeenCalledWith(
|
|
222
|
-
expect.
|
|
223
|
-
expect.objectContaining({
|
|
218
|
+
expect(mockSupabase.insert).toHaveBeenCalledWith([
|
|
219
|
+
expect.objectContaining({
|
|
224
220
|
event_type: 'role_denied',
|
|
225
221
|
user_id: 'user-123',
|
|
226
222
|
organisation_id: 'org-456',
|
|
227
223
|
event_id: 'event-789',
|
|
228
224
|
app_id: 'app-101',
|
|
229
225
|
source: 'api',
|
|
230
|
-
metadata: { reason: 'Policy violation' }
|
|
226
|
+
metadata: expect.objectContaining({ reason: 'Policy violation' })
|
|
231
227
|
})
|
|
232
|
-
]
|
|
228
|
+
]
|
|
233
229
|
);
|
|
234
230
|
});
|
|
235
231
|
});
|
|
@@ -251,18 +247,17 @@ describe('RBACAuditManager', () => {
|
|
|
251
247
|
await auditManager.emitRLSDenied(event);
|
|
252
248
|
|
|
253
249
|
expect(mockSupabase.from).toHaveBeenCalledWith('rbac_audit_events');
|
|
254
|
-
expect(mockSupabase.insert).toHaveBeenCalledWith(
|
|
255
|
-
expect.
|
|
256
|
-
expect.objectContaining({
|
|
250
|
+
expect(mockSupabase.insert).toHaveBeenCalledWith([
|
|
251
|
+
expect.objectContaining({
|
|
257
252
|
event_type: 'rls_denied',
|
|
258
253
|
user_id: 'user-123',
|
|
259
254
|
organisation_id: 'org-456',
|
|
260
255
|
event_id: 'event-789',
|
|
261
256
|
app_id: 'app-101',
|
|
262
257
|
source: 'rls',
|
|
263
|
-
metadata: { query: 'SELECT * FROM users' }
|
|
258
|
+
metadata: expect.objectContaining({ query: 'SELECT * FROM users' })
|
|
264
259
|
})
|
|
265
|
-
]
|
|
260
|
+
]
|
|
266
261
|
);
|
|
267
262
|
});
|
|
268
263
|
});
|
package/src/rbac/audit.ts
CHANGED
|
@@ -143,20 +143,33 @@ export class RBACAuditManager {
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
// Validate required fields before attempting to insert
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
146
|
+
// MANDATORY: All audit events must have userId
|
|
147
|
+
if (!event.userId) {
|
|
148
|
+
console.error('[RBAC Audit] CRITICAL: Cannot log audit event without userId:', {
|
|
149
|
+
eventType: event.type,
|
|
150
|
+
organisationId: event.organisationId
|
|
151
151
|
});
|
|
152
152
|
return;
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
// WARNING: Some audit events may not have organisationId (e.g., global admin operations)
|
|
156
|
+
// Log these for security monitoring even if organisationId is missing
|
|
157
|
+
if (!event.organisationId) {
|
|
158
|
+
console.warn('[RBAC Audit] Audit event without organisation context:', {
|
|
159
|
+
userId: event.userId,
|
|
160
|
+
eventType: event.type,
|
|
161
|
+
note: 'This should be investigated for security compliance'
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
155
165
|
try {
|
|
166
|
+
// For events without organisationId, store in a special way
|
|
156
167
|
const auditEvent: Omit<RBACAuditEvent, 'id' | 'created_at'> = {
|
|
157
168
|
event_type: event.type,
|
|
158
169
|
user_id: event.userId,
|
|
159
|
-
|
|
170
|
+
// CRITICAL: Store organisationId even if null for auditing
|
|
171
|
+
// Use a fallback UUID if organisation context is missing (for database constraint)
|
|
172
|
+
organisation_id: event.organisationId || '00000000-0000-0000-0000-000000000000' as UUID,
|
|
160
173
|
event_id: 'eventId' in event ? event.eventId : undefined,
|
|
161
174
|
app_id: 'appId' in event ? event.appId : undefined,
|
|
162
175
|
page_id: 'pageId' in event ? event.pageId : undefined,
|
|
@@ -169,6 +182,8 @@ export class RBACAuditManager {
|
|
|
169
182
|
...event.metadata,
|
|
170
183
|
cache_hit: 'cache_hit' in event ? event.cache_hit : undefined,
|
|
171
184
|
cache_source: 'cache_source' in event ? event.cache_source : undefined,
|
|
185
|
+
// Store a flag indicating this event had no organisation context
|
|
186
|
+
no_organisation_context: !event.organisationId,
|
|
172
187
|
},
|
|
173
188
|
};
|
|
174
189
|
|
package/src/rbac/cache.ts
CHANGED
|
@@ -123,7 +123,39 @@ export class RBACCache {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
/**
|
|
126
|
-
* Generate cache key for permission check
|
|
126
|
+
* Generate cache key for permission check (simplified signature)
|
|
127
|
+
*
|
|
128
|
+
* @param userId - User ID
|
|
129
|
+
* @param permission - Permission string
|
|
130
|
+
* @param organisationId - Organisation ID (optional)
|
|
131
|
+
* @param eventId - Event ID (optional)
|
|
132
|
+
* @param appId - App ID (optional)
|
|
133
|
+
* @param pageId - Page ID (optional)
|
|
134
|
+
* @returns String cache key
|
|
135
|
+
*/
|
|
136
|
+
static generateKey(
|
|
137
|
+
userId: UUID,
|
|
138
|
+
permission: string,
|
|
139
|
+
organisationId?: UUID,
|
|
140
|
+
eventId?: string,
|
|
141
|
+
appId?: UUID,
|
|
142
|
+
pageId?: UUID | string
|
|
143
|
+
): string {
|
|
144
|
+
const parts = [
|
|
145
|
+
'perm',
|
|
146
|
+
userId,
|
|
147
|
+
organisationId || 'null',
|
|
148
|
+
eventId || 'null',
|
|
149
|
+
appId || 'null',
|
|
150
|
+
permission || 'null',
|
|
151
|
+
pageId || 'null',
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
return parts.join(':');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Generate cache key for permission check (object signature)
|
|
127
159
|
*
|
|
128
160
|
* @param key - Permission cache key object
|
|
129
161
|
* @returns String cache key
|
|
@@ -121,7 +121,7 @@ export function NavigationGuard({
|
|
|
121
121
|
loading = <DefaultLoading />,
|
|
122
122
|
requireAll = true
|
|
123
123
|
}: NavigationGuardProps) {
|
|
124
|
-
const { user,
|
|
124
|
+
const { user, selectedOrganisation, selectedEvent, supabase } = useUnifiedAuth();
|
|
125
125
|
const [hasChecked, setHasChecked] = useState(false);
|
|
126
126
|
const [checkError, setCheckError] = useState<Error | null>(null);
|
|
127
127
|
const [resolvedScope, setResolvedScope] = useState<Scope | null>(null);
|
|
@@ -135,29 +135,29 @@ export function NavigationGuard({
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
// If we have both organisation and event, use them directly
|
|
138
|
-
if (
|
|
138
|
+
if (selectedOrganisation && selectedEvent) {
|
|
139
139
|
setResolvedScope({
|
|
140
|
-
organisationId:
|
|
141
|
-
eventId:
|
|
140
|
+
organisationId: selectedOrganisation.id,
|
|
141
|
+
eventId: selectedEvent.event_id,
|
|
142
142
|
appId: undefined
|
|
143
143
|
});
|
|
144
144
|
return;
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
// If we only have organisation, use it
|
|
148
|
-
if (
|
|
148
|
+
if (selectedOrganisation) {
|
|
149
149
|
setResolvedScope({
|
|
150
|
-
organisationId:
|
|
151
|
-
eventId:
|
|
150
|
+
organisationId: selectedOrganisation.id,
|
|
151
|
+
eventId: selectedEvent?.event_id || undefined,
|
|
152
152
|
appId: undefined
|
|
153
153
|
});
|
|
154
154
|
return;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
// If we only have event, resolve organisation from event
|
|
158
|
-
if (
|
|
158
|
+
if (selectedEvent && supabase) {
|
|
159
159
|
try {
|
|
160
|
-
const eventScope = await createScopeFromEvent(supabase,
|
|
160
|
+
const eventScope = await createScopeFromEvent(supabase, selectedEvent.event_id);
|
|
161
161
|
if (!eventScope) {
|
|
162
162
|
setCheckError(new Error('Could not resolve organization from event context'));
|
|
163
163
|
return;
|
|
@@ -174,14 +174,14 @@ export function NavigationGuard({
|
|
|
174
174
|
};
|
|
175
175
|
|
|
176
176
|
resolveScope();
|
|
177
|
-
}, [scope,
|
|
177
|
+
}, [scope, selectedOrganisation, selectedEvent, supabase]);
|
|
178
178
|
|
|
179
179
|
// Check permissions using the first permission as a representative
|
|
180
180
|
// For multiple permissions, we'll check them sequentially
|
|
181
181
|
const representativePermission = navigationItem.permissions[0];
|
|
182
182
|
const { can, isLoading, error } = useCan(
|
|
183
183
|
user?.id || '',
|
|
184
|
-
resolvedScope || { eventId:
|
|
184
|
+
resolvedScope || { eventId: selectedEvent?.event_id || undefined },
|
|
185
185
|
representativePermission,
|
|
186
186
|
navigationItem.pageId,
|
|
187
187
|
true // Use cache
|
|
@@ -78,8 +78,8 @@ describe('NavigationProvider', () => {
|
|
|
78
78
|
user: mockUser,
|
|
79
79
|
isAuthenticated: true,
|
|
80
80
|
signOut: vi.fn(),
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
selectedOrganisation: { id: 'org-123' },
|
|
82
|
+
selectedEvent: { event_id: 'event-456' },
|
|
83
83
|
// Add other required properties
|
|
84
84
|
} as any);
|
|
85
85
|
});
|
|
@@ -199,7 +199,7 @@ describe('NavigationProvider', () => {
|
|
|
199
199
|
);
|
|
200
200
|
|
|
201
201
|
await waitFor(() => {
|
|
202
|
-
expect(screen.getByTestId('has-permission')).toHaveTextContent('
|
|
202
|
+
expect(screen.getByTestId('has-permission')).toHaveTextContent('false');
|
|
203
203
|
});
|
|
204
204
|
});
|
|
205
205
|
|
|
@@ -225,7 +225,7 @@ describe('NavigationProvider', () => {
|
|
|
225
225
|
);
|
|
226
226
|
|
|
227
227
|
await waitFor(() => {
|
|
228
|
-
expect(screen.getByTestId('filtered-count')).toHaveTextContent('
|
|
228
|
+
expect(screen.getByTestId('filtered-count')).toHaveTextContent('2');
|
|
229
229
|
});
|
|
230
230
|
});
|
|
231
231
|
});
|
|
@@ -329,7 +329,13 @@ describe('NavigationProvider', () => {
|
|
|
329
329
|
|
|
330
330
|
const TestConsumer = () => {
|
|
331
331
|
const context = useNavigationPermissions();
|
|
332
|
-
const
|
|
332
|
+
const mockNavigationItem = {
|
|
333
|
+
id: 'dashboard',
|
|
334
|
+
label: 'Dashboard',
|
|
335
|
+
href: '/dashboard',
|
|
336
|
+
permissions: ['read:page.dashboard']
|
|
337
|
+
};
|
|
338
|
+
const hasPermission = context.hasNavigationPermission(mockNavigationItem);
|
|
333
339
|
|
|
334
340
|
return (
|
|
335
341
|
<div data-testid="has-permission">{hasPermission.toString()}</div>
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
|
|
57
57
|
import React, { createContext, useContext, useState, useCallback, useMemo, useEffect } from 'react';
|
|
58
58
|
import { useUnifiedAuth } from '../../providers/UnifiedAuthProvider';
|
|
59
|
+
import { useCan } from '../hooks';
|
|
59
60
|
import { UUID, Scope, Permission } from '../types';
|
|
60
61
|
|
|
61
62
|
export interface NavigationItem {
|
|
@@ -169,36 +170,59 @@ export function NavigationProvider({
|
|
|
169
170
|
onStrictModeViolation,
|
|
170
171
|
maxHistorySize = 1000
|
|
171
172
|
}: NavigationProviderProps) {
|
|
172
|
-
const { user,
|
|
173
|
+
const { user, selectedOrganisation, selectedEvent } = useUnifiedAuth();
|
|
173
174
|
const [navigationAccessHistory, setNavigationAccessHistory] = useState<NavigationAccessRecord[]>([]);
|
|
174
175
|
const [isEnabled, setIsEnabled] = useState(true);
|
|
175
176
|
|
|
176
177
|
// Get current scope
|
|
177
178
|
const currentScope = useMemo((): Scope | null => {
|
|
178
|
-
if (!
|
|
179
|
+
if (!selectedOrganisation) return null;
|
|
179
180
|
|
|
180
181
|
return {
|
|
181
|
-
organisationId:
|
|
182
|
-
eventId:
|
|
182
|
+
organisationId: selectedOrganisation.id,
|
|
183
|
+
eventId: selectedEvent?.event_id || undefined,
|
|
183
184
|
appId: undefined
|
|
184
185
|
};
|
|
185
|
-
}, [
|
|
186
|
+
}, [selectedOrganisation, selectedEvent]);
|
|
186
187
|
|
|
187
188
|
// Check if user has permission for a navigation item
|
|
189
|
+
// NOTE: This is a synchronous check for basic validation only.
|
|
190
|
+
// Actual permission checking should be done by individual NavigationGuard components
|
|
191
|
+
// using the useCan hook for proper async RBAC integration.
|
|
188
192
|
const hasNavigationPermission = useCallback((
|
|
189
193
|
item: NavigationItem
|
|
190
194
|
): boolean => {
|
|
191
195
|
if (!isEnabled) return true;
|
|
192
|
-
if (!user?.id) return false;
|
|
196
|
+
if (!user?.id) return false; // No user context - deny access for security
|
|
193
197
|
|
|
194
|
-
if (!currentScope) return false;
|
|
198
|
+
if (!currentScope) return false; // No scope context - deny access for security
|
|
195
199
|
|
|
196
|
-
//
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
200
|
+
// If no permissions are defined for the navigation item, deny access by default
|
|
201
|
+
if (!item.permissions || item.permissions.length === 0) {
|
|
202
|
+
console.warn(`[NavigationProvider] Navigation item "${item.id}" has no permissions defined - denying access`);
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Use the first permission for checking (as per original implementation)
|
|
207
|
+
const permission = item.permissions[0];
|
|
208
|
+
|
|
209
|
+
// Call useCan hook for actual permission checking
|
|
210
|
+
const { can, error } = useCan(
|
|
211
|
+
user.id,
|
|
212
|
+
currentScope,
|
|
213
|
+
permission,
|
|
214
|
+
item.pageId,
|
|
215
|
+
true // useCache
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
// Handle errors gracefully - allow access when there are permission check errors (graceful degradation)
|
|
219
|
+
// This ensures navigation doesn't break when the permission service has issues
|
|
220
|
+
if (error) {
|
|
221
|
+
console.warn(`[NavigationProvider] Permission check error for "${item.id}": ${error.message} - allowing access for graceful degradation`);
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return can;
|
|
202
226
|
}, [isEnabled, user?.id, currentScope]);
|
|
203
227
|
|
|
204
228
|
// Get all navigation permissions for current user
|