@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
|
@@ -1,645 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file RBAC Provider
|
|
3
|
-
* @package @jmruthers/pace-core
|
|
4
|
-
* @module RBAC/Providers
|
|
5
|
-
* @since 0.1.0
|
|
6
|
-
*
|
|
7
|
-
* Handles role-based access control, permissions, and event access management.
|
|
8
|
-
* Separated from UnifiedAuthProvider for better maintainability.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import React, { createContext, useContext, useState, useEffect, useCallback, useMemo } from 'react';
|
|
12
|
-
import { type SupabaseClient, type User, type Session } from '@supabase/supabase-js';
|
|
13
|
-
import { AccessLevel } from '../../types/unified';
|
|
14
|
-
import { DebugLogger } from '../../utils/debugLogger';
|
|
15
|
-
|
|
16
|
-
// App configuration type
|
|
17
|
-
interface AppConfig {
|
|
18
|
-
supports_direct_access: boolean;
|
|
19
|
-
requires_event: boolean;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// User event access type - now includes organisation context for security
|
|
23
|
-
export interface UserEventAccess {
|
|
24
|
-
event_id: string;
|
|
25
|
-
event_name: string;
|
|
26
|
-
event_description?: string | null;
|
|
27
|
-
start_date: string;
|
|
28
|
-
end_date: string;
|
|
29
|
-
event_status: string;
|
|
30
|
-
app_id: string;
|
|
31
|
-
access_level: string;
|
|
32
|
-
granted_at: string;
|
|
33
|
-
organisation_id: string; // MANDATORY for security
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// RBAC context type
|
|
37
|
-
export interface RBACContextType {
|
|
38
|
-
// RBAC state
|
|
39
|
-
permissions: Record<string, boolean>;
|
|
40
|
-
roles: string[];
|
|
41
|
-
accessLevel: AccessLevel;
|
|
42
|
-
rbacLoading: boolean;
|
|
43
|
-
rbacError: Error | null;
|
|
44
|
-
selectedEventId: string | null;
|
|
45
|
-
appConfig: AppConfig | null;
|
|
46
|
-
userEventAccess: UserEventAccess[];
|
|
47
|
-
eventAccessLoading: boolean;
|
|
48
|
-
|
|
49
|
-
// Organisation context integration
|
|
50
|
-
selectedOrganisationId: string | null;
|
|
51
|
-
requireOrganisationContext: () => string; // Throws if no organisation context
|
|
52
|
-
|
|
53
|
-
// RBAC methods
|
|
54
|
-
hasPermission: (permission: string, orgId?: string) => boolean;
|
|
55
|
-
hasAnyPermission: (permissions: string[], orgId?: string) => boolean;
|
|
56
|
-
hasAllPermissions: (permissions: string[], orgId?: string) => boolean;
|
|
57
|
-
hasRole: (role: string) => boolean;
|
|
58
|
-
hasAccessLevel: (level: AccessLevel) => boolean;
|
|
59
|
-
canAccess: (resource: string, action: string, orgId?: string) => boolean;
|
|
60
|
-
validatePermission: (permission: string, orgId?: string) => Promise<boolean>;
|
|
61
|
-
validateAccess: (resource: string, action: string, orgId?: string) => Promise<boolean>;
|
|
62
|
-
refreshPermissions: (eventId?: string, orgId?: string) => Promise<void>;
|
|
63
|
-
setSelectedEventId: (eventId: string | null) => void;
|
|
64
|
-
loadUserEventAccess: (orgId?: string) => Promise<void>;
|
|
65
|
-
getUserEventAccess: (eventId: string) => UserEventAccess | undefined;
|
|
66
|
-
|
|
67
|
-
// New RBAC system support
|
|
68
|
-
rbacEnabled: boolean;
|
|
69
|
-
rbacContext?: any; // Will be populated by useRBAC hook when enabled
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const RBACContext = createContext<RBACContextType | undefined>(undefined);
|
|
73
|
-
|
|
74
|
-
export const useRBAC = () => {
|
|
75
|
-
const context = useContext(RBACContext);
|
|
76
|
-
if (!context) {
|
|
77
|
-
throw new Error('useRBAC must be used within an RBACProvider');
|
|
78
|
-
}
|
|
79
|
-
return context;
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
export interface RBACProviderProps {
|
|
83
|
-
children: React.ReactNode;
|
|
84
|
-
supabaseClient?: SupabaseClient;
|
|
85
|
-
user: User | null;
|
|
86
|
-
session: Session | null;
|
|
87
|
-
appName: string;
|
|
88
|
-
enableRBAC?: boolean;
|
|
89
|
-
persistState?: boolean;
|
|
90
|
-
enablePersistence?: boolean;
|
|
91
|
-
requireOrganisationContext?: boolean;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Storage keys for persistence
|
|
95
|
-
const STORAGE_KEYS = {
|
|
96
|
-
SELECTED_EVENT: 'pace-core-selected-event',
|
|
97
|
-
} as const;
|
|
98
|
-
|
|
99
|
-
// Helper function to transform RBAC permissions to the format expected by UnifiedAuthProvider
|
|
100
|
-
const transformRBACPermissions = (rbacData: any[], _appName: string) => {
|
|
101
|
-
const permissions: Record<string, boolean> = {};
|
|
102
|
-
let roles: string[] = [];
|
|
103
|
-
let access_level: AccessLevel = AccessLevel.VIEWER;
|
|
104
|
-
|
|
105
|
-
if (!rbacData || !Array.isArray(rbacData)) {
|
|
106
|
-
return { permissions: {}, roles: ['viewer'], access_level: AccessLevel.VIEWER };
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Check for super admin first
|
|
110
|
-
const superAdminPerm = rbacData.find((p: any) => p.permission_type === 'all_permissions');
|
|
111
|
-
if (superAdminPerm) {
|
|
112
|
-
return {
|
|
113
|
-
permissions: { 'all:all': true } as Record<string, boolean>,
|
|
114
|
-
roles: ['super'],
|
|
115
|
-
access_level: AccessLevel.SUPER
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Process event-app permissions
|
|
120
|
-
const eventAppPerms = rbacData.filter((p: any) => p.permission_type === 'event_app_access');
|
|
121
|
-
if (eventAppPerms.length > 0) {
|
|
122
|
-
const role = eventAppPerms[0].role_name;
|
|
123
|
-
|
|
124
|
-
// Map RBAC roles to AccessLevel
|
|
125
|
-
switch (role) {
|
|
126
|
-
case 'event_admin':
|
|
127
|
-
access_level = AccessLevel.ADMIN;
|
|
128
|
-
roles = ['admin'];
|
|
129
|
-
break;
|
|
130
|
-
case 'planner':
|
|
131
|
-
access_level = AccessLevel.PLANNER;
|
|
132
|
-
roles = ['planner'];
|
|
133
|
-
break;
|
|
134
|
-
case 'participant':
|
|
135
|
-
access_level = AccessLevel.PARTICIPANT;
|
|
136
|
-
roles = ['participant'];
|
|
137
|
-
break;
|
|
138
|
-
case 'editor':
|
|
139
|
-
access_level = AccessLevel.EDITOR;
|
|
140
|
-
roles = ['editor'];
|
|
141
|
-
break;
|
|
142
|
-
case 'viewer':
|
|
143
|
-
default:
|
|
144
|
-
access_level = AccessLevel.VIEWER;
|
|
145
|
-
roles = ['viewer'];
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// For now, we'll set basic permissions based on role
|
|
150
|
-
// In a full implementation, you'd want to fetch page-specific permissions
|
|
151
|
-
const basePermissions = ['read'];
|
|
152
|
-
if (['event_admin', 'planner'].includes(role)) {
|
|
153
|
-
basePermissions.push('create', 'update');
|
|
154
|
-
}
|
|
155
|
-
if (role === 'event_admin') {
|
|
156
|
-
basePermissions.push('delete');
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Set permissions for all pages (simplified approach)
|
|
160
|
-
// In a real implementation, you'd want to get actual page permissions
|
|
161
|
-
basePermissions.forEach(operation => {
|
|
162
|
-
permissions[`default:${operation}`] = true;
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Process organisation permissions
|
|
167
|
-
const orgPerms = rbacData.filter((p: any) => p.permission_type === 'organisation_access');
|
|
168
|
-
if (orgPerms.length > 0) {
|
|
169
|
-
const role = orgPerms[0].role_name;
|
|
170
|
-
if (role === 'org_admin') {
|
|
171
|
-
access_level = AccessLevel.ADMIN;
|
|
172
|
-
roles = ['admin'];
|
|
173
|
-
// Org admins get all permissions
|
|
174
|
-
['create', 'read', 'update', 'delete'].forEach(operation => {
|
|
175
|
-
permissions[`default:${operation}`] = true;
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return { permissions, roles, access_level };
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
export function RBACProvider({
|
|
184
|
-
children,
|
|
185
|
-
supabaseClient,
|
|
186
|
-
user,
|
|
187
|
-
session,
|
|
188
|
-
appName,
|
|
189
|
-
enableRBAC = false,
|
|
190
|
-
persistState = true,
|
|
191
|
-
enablePersistence,
|
|
192
|
-
requireOrganisationContext: _requireOrganisationContext = true,
|
|
193
|
-
}: RBACProviderProps) {
|
|
194
|
-
// Use enablePersistence if provided, otherwise use persistState
|
|
195
|
-
const shouldPersist = enablePersistence !== undefined ? enablePersistence : persistState;
|
|
196
|
-
|
|
197
|
-
// RBAC state
|
|
198
|
-
const [permissions, setPermissions] = useState<Record<string, boolean>>({});
|
|
199
|
-
const [roles, setRoles] = useState<string[]>([]);
|
|
200
|
-
const [accessLevel, setAccessLevel] = useState<AccessLevel>(AccessLevel.VIEWER);
|
|
201
|
-
const [rbacLoading, setRbacLoading] = useState(false);
|
|
202
|
-
const [rbacError, setRbacError] = useState<Error | null>(null);
|
|
203
|
-
const [selectedEventId, setSelectedEventId] = useState<string | null>(null);
|
|
204
|
-
const [appConfig, setAppConfig] = useState<AppConfig | null>(null);
|
|
205
|
-
const [userEventAccess, setUserEventAccess] = useState<UserEventAccess[]>([]);
|
|
206
|
-
const [eventAccessLoading, setEventAccessLoading] = useState(false);
|
|
207
|
-
|
|
208
|
-
// Organisation context state
|
|
209
|
-
const [selectedOrganisationId, _setSelectedOrganisationId] = useState<string | null>(null);
|
|
210
|
-
|
|
211
|
-
// Load app configuration on mount and when appName changes
|
|
212
|
-
useEffect(() => {
|
|
213
|
-
if (!supabaseClient) return;
|
|
214
|
-
|
|
215
|
-
const loadAppConfig = async () => {
|
|
216
|
-
try {
|
|
217
|
-
// Use the same app name resolution as PagePermissionGuard
|
|
218
|
-
const { getCurrentAppName } = await import('../../utils/appNameResolver');
|
|
219
|
-
const resolvedAppName = getCurrentAppName() || appName;
|
|
220
|
-
|
|
221
|
-
// First resolve app name to app_id
|
|
222
|
-
const { data: appData, error: appError } = await supabaseClient
|
|
223
|
-
.from('rbac_apps')
|
|
224
|
-
.select('id')
|
|
225
|
-
.eq('name', resolvedAppName)
|
|
226
|
-
.eq('is_active', true)
|
|
227
|
-
.single();
|
|
228
|
-
|
|
229
|
-
if (appError || !appData) {
|
|
230
|
-
console.warn('App not found or inactive:', resolvedAppName);
|
|
231
|
-
setAppConfig({
|
|
232
|
-
supports_direct_access: false, // Field removed from rbac_apps table
|
|
233
|
-
requires_event: true
|
|
234
|
-
});
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const response = await supabaseClient.rpc('get_app_config', {
|
|
239
|
-
app_id: appData.id
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
const { data } = response || {};
|
|
243
|
-
|
|
244
|
-
if (data && data.length > 0) {
|
|
245
|
-
setAppConfig({
|
|
246
|
-
supports_direct_access: false, // Field removed from rbac_apps table
|
|
247
|
-
requires_event: data[0].requires_event
|
|
248
|
-
});
|
|
249
|
-
} else {
|
|
250
|
-
// Default configuration if app not found
|
|
251
|
-
setAppConfig({
|
|
252
|
-
supports_direct_access: false,
|
|
253
|
-
requires_event: true
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
} catch (error) {
|
|
257
|
-
console.warn("Clearing corrupted localStorage data");
|
|
258
|
-
if (typeof localStorage !== 'undefined') {
|
|
259
|
-
localStorage.removeItem(STORAGE_KEYS.SELECTED_EVENT);
|
|
260
|
-
}
|
|
261
|
-
console.warn('Failed to load app configuration:', error);
|
|
262
|
-
// Default configuration on error
|
|
263
|
-
setAppConfig({
|
|
264
|
-
supports_direct_access: false, // Field removed from rbac_apps table
|
|
265
|
-
requires_event: true
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
loadAppConfig();
|
|
271
|
-
}, [supabaseClient, appName]);
|
|
272
|
-
|
|
273
|
-
// Set super admin roles based on user metadata
|
|
274
|
-
useEffect(() => {
|
|
275
|
-
// Check if user has super admin role in metadata
|
|
276
|
-
const isSuperAdmin = user?.user_metadata?.globalRole === 'super_admin';
|
|
277
|
-
if (isSuperAdmin) {
|
|
278
|
-
setRoles(['super_admin']);
|
|
279
|
-
} else {
|
|
280
|
-
setRoles([]);
|
|
281
|
-
}
|
|
282
|
-
}, [user]);
|
|
283
|
-
|
|
284
|
-
// Load persisted auth state on mount
|
|
285
|
-
useEffect(() => {
|
|
286
|
-
if (!shouldPersist) return;
|
|
287
|
-
|
|
288
|
-
try {
|
|
289
|
-
const persistedEvent = localStorage.getItem(STORAGE_KEYS.SELECTED_EVENT);
|
|
290
|
-
if (persistedEvent) {
|
|
291
|
-
setSelectedEventId(JSON.parse(persistedEvent));
|
|
292
|
-
}
|
|
293
|
-
} catch (error) {
|
|
294
|
-
console.warn("Clearing corrupted localStorage data");
|
|
295
|
-
localStorage.removeItem(STORAGE_KEYS.SELECTED_EVENT);
|
|
296
|
-
}
|
|
297
|
-
}, [shouldPersist]);
|
|
298
|
-
|
|
299
|
-
// Persist auth state changes
|
|
300
|
-
useEffect(() => {
|
|
301
|
-
if (!shouldPersist) return;
|
|
302
|
-
|
|
303
|
-
try {
|
|
304
|
-
if (selectedEventId) {
|
|
305
|
-
localStorage.setItem(
|
|
306
|
-
STORAGE_KEYS.SELECTED_EVENT,
|
|
307
|
-
JSON.stringify(selectedEventId),
|
|
308
|
-
);
|
|
309
|
-
} else {
|
|
310
|
-
localStorage.removeItem(STORAGE_KEYS.SELECTED_EVENT);
|
|
311
|
-
}
|
|
312
|
-
} catch (error) {
|
|
313
|
-
console.warn("Clearing corrupted localStorage data");
|
|
314
|
-
localStorage.removeItem(STORAGE_KEYS.SELECTED_EVENT);
|
|
315
|
-
console.warn('Failed to persist auth state:', error);
|
|
316
|
-
}
|
|
317
|
-
}, [selectedEventId, shouldPersist]);
|
|
318
|
-
|
|
319
|
-
// Load RBAC data when user is authenticated
|
|
320
|
-
const refreshPermissions = useCallback(async (eventId?: string) => {
|
|
321
|
-
if (!supabaseClient || !user || !appConfig || !session) {
|
|
322
|
-
DebugLogger.log('RBACProvider', 'refreshPermissions: Missing required dependencies, clearing permissions');
|
|
323
|
-
setPermissions({});
|
|
324
|
-
setRoles([]);
|
|
325
|
-
setAccessLevel(AccessLevel.VIEWER);
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Check for super admin first by querying the database
|
|
330
|
-
// This ensures we check against the rbac_global_roles table, not just user metadata
|
|
331
|
-
const { data: globalRoles } = await supabaseClient
|
|
332
|
-
.from('rbac_global_roles')
|
|
333
|
-
.select('role')
|
|
334
|
-
.eq('user_id', user.id)
|
|
335
|
-
.eq('role', 'super_admin')
|
|
336
|
-
.lte('valid_from', new Date().toISOString())
|
|
337
|
-
.or(`valid_to.is.null,valid_to.gte.${new Date().toISOString()}`)
|
|
338
|
-
.limit(1);
|
|
339
|
-
|
|
340
|
-
const isSuperAdmin = globalRoles && globalRoles.length > 0;
|
|
341
|
-
|
|
342
|
-
if (isSuperAdmin) {
|
|
343
|
-
setPermissions({
|
|
344
|
-
'admin:create': true,
|
|
345
|
-
'admin:read': true,
|
|
346
|
-
'admin:update': true,
|
|
347
|
-
'admin:delete': true,
|
|
348
|
-
'users:create': true,
|
|
349
|
-
'users:read': true,
|
|
350
|
-
'users:update': true,
|
|
351
|
-
'users:delete': true,
|
|
352
|
-
'events:create': true,
|
|
353
|
-
'events:read': true,
|
|
354
|
-
'events:update': true,
|
|
355
|
-
'events:delete': true
|
|
356
|
-
});
|
|
357
|
-
setRoles(['super_admin']);
|
|
358
|
-
setAccessLevel(AccessLevel.SUPER);
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// Determine if we should load permissions without an event
|
|
363
|
-
const shouldLoadDirectPermissions = !eventId && !appConfig.requires_event;
|
|
364
|
-
const shouldLoadEventPermissions = eventId;
|
|
365
|
-
const shouldClearPermissions = !eventId && appConfig.requires_event;
|
|
366
|
-
|
|
367
|
-
// If no eventId and app requires events, clear permissions
|
|
368
|
-
if (shouldClearPermissions) {
|
|
369
|
-
setPermissions({});
|
|
370
|
-
setRoles([]);
|
|
371
|
-
setAccessLevel(AccessLevel.VIEWER);
|
|
372
|
-
return;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Only proceed if we should load permissions
|
|
376
|
-
if (!shouldLoadDirectPermissions && !shouldLoadEventPermissions) {
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
setRbacLoading(true);
|
|
381
|
-
setRbacError(null);
|
|
382
|
-
|
|
383
|
-
try {
|
|
384
|
-
// Use the same app name resolution as PagePermissionGuard
|
|
385
|
-
const { getCurrentAppName } = await import('../../utils/appNameResolver');
|
|
386
|
-
const resolvedAppName = getCurrentAppName() || appName;
|
|
387
|
-
|
|
388
|
-
// First resolve app name to app_id
|
|
389
|
-
const { data: appData, error: appError } = await supabaseClient
|
|
390
|
-
.from('rbac_apps')
|
|
391
|
-
.select('id')
|
|
392
|
-
.eq('name', resolvedAppName)
|
|
393
|
-
.eq('is_active', true)
|
|
394
|
-
.single();
|
|
395
|
-
|
|
396
|
-
if (appError || !appData) {
|
|
397
|
-
console.warn('App not found or inactive:', resolvedAppName);
|
|
398
|
-
setRbacLoading(false);
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
const { data, error } = await supabaseClient.rpc('rbac_permissions_get', {
|
|
403
|
-
p_user_id: user.id,
|
|
404
|
-
p_app_id: appData.id,
|
|
405
|
-
p_event_id: eventId || null,
|
|
406
|
-
p_organisation_id: selectedOrganisationId || null
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
if (error) {
|
|
410
|
-
throw error;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
const { permissions, roles, access_level } = transformRBACPermissions(data, appName);
|
|
414
|
-
|
|
415
|
-
setPermissions(permissions);
|
|
416
|
-
setRoles(roles);
|
|
417
|
-
setAccessLevel(access_level);
|
|
418
|
-
} catch (err: any) {
|
|
419
|
-
setRbacError(err);
|
|
420
|
-
} finally {
|
|
421
|
-
setRbacLoading(false);
|
|
422
|
-
}
|
|
423
|
-
}, [supabaseClient, user, session, appName, appConfig, selectedOrganisationId]);
|
|
424
|
-
|
|
425
|
-
// Load user event access data
|
|
426
|
-
const loadUserEventAccess = useCallback(async () => {
|
|
427
|
-
if (!supabaseClient || !user || !session) {
|
|
428
|
-
DebugLogger.log('RBACProvider', 'loadUserEventAccess: Missing required dependencies, clearing event access');
|
|
429
|
-
setUserEventAccess([]);
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
setEventAccessLoading(true);
|
|
434
|
-
try {
|
|
435
|
-
// Use the same app name resolution as PagePermissionGuard
|
|
436
|
-
const { getCurrentAppName } = await import('../../utils/appNameResolver');
|
|
437
|
-
const resolvedAppName = getCurrentAppName() || appName;
|
|
438
|
-
|
|
439
|
-
// First resolve app name to app_id
|
|
440
|
-
const { data: appData, error: appError } = await supabaseClient
|
|
441
|
-
.from('rbac_apps')
|
|
442
|
-
.select('id')
|
|
443
|
-
.eq('name', resolvedAppName)
|
|
444
|
-
.eq('is_active', true)
|
|
445
|
-
.single();
|
|
446
|
-
|
|
447
|
-
if (appError || !appData) {
|
|
448
|
-
console.warn('App not found or inactive:', resolvedAppName);
|
|
449
|
-
setEventAccessLoading(false);
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
const { data, error } = await supabaseClient
|
|
454
|
-
.from('rbac_event_app_roles')
|
|
455
|
-
.select(`
|
|
456
|
-
event_id,
|
|
457
|
-
role,
|
|
458
|
-
granted_at
|
|
459
|
-
`)
|
|
460
|
-
.eq('user_id', user.id)
|
|
461
|
-
.eq('app_id', appData.id);
|
|
462
|
-
|
|
463
|
-
if (error) {
|
|
464
|
-
console.error('Failed to load user event access:', error);
|
|
465
|
-
setUserEventAccess([]);
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
const eventAccess = data?.map(item => ({
|
|
470
|
-
event_id: item.event_id,
|
|
471
|
-
event_name: 'Unknown Event', // Event details not available in this query
|
|
472
|
-
event_description: null, // Not available in this schema
|
|
473
|
-
start_date: '', // Event date not available in this query
|
|
474
|
-
end_date: '', // Event date not available in this query
|
|
475
|
-
event_status: 'unknown', // Not available in this schema
|
|
476
|
-
app_id: appData.id,
|
|
477
|
-
access_level: item.role, // Map role to access_level
|
|
478
|
-
granted_at: item.granted_at,
|
|
479
|
-
organisation_id: '' // Will be populated from event's organisation_id if needed
|
|
480
|
-
})) || [];
|
|
481
|
-
|
|
482
|
-
setUserEventAccess(eventAccess);
|
|
483
|
-
} catch (error) {
|
|
484
|
-
console.warn("Clearing corrupted localStorage data");
|
|
485
|
-
localStorage.removeItem(STORAGE_KEYS.SELECTED_EVENT);
|
|
486
|
-
console.error('Error loading user event access:', error);
|
|
487
|
-
setUserEventAccess([]);
|
|
488
|
-
} finally {
|
|
489
|
-
setEventAccessLoading(false);
|
|
490
|
-
}
|
|
491
|
-
}, [supabaseClient, user, session, appName]);
|
|
492
|
-
|
|
493
|
-
// Helper function to get access for a specific event
|
|
494
|
-
const getUserEventAccess = useCallback((eventId: string) => {
|
|
495
|
-
return userEventAccess.find(access => access.event_id === eventId);
|
|
496
|
-
}, [userEventAccess]);
|
|
497
|
-
|
|
498
|
-
// When the selected event ID changes, user logs in, or app config loads, refresh permissions
|
|
499
|
-
useEffect(() => {
|
|
500
|
-
// Don't run if user is null (signed out) or no app config
|
|
501
|
-
if (!user || !appConfig || !session) {
|
|
502
|
-
DebugLogger.log('RBACProvider', 'Skipping permission refresh - no user, session, or app config');
|
|
503
|
-
return;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
// Check for super admin first - admin override should happen regardless of app configuration
|
|
507
|
-
const isSuperAdmin = user?.user_metadata?.globalRole === 'super_admin';
|
|
508
|
-
if (isSuperAdmin) {
|
|
509
|
-
setPermissions({
|
|
510
|
-
'admin:create': true,
|
|
511
|
-
'admin:read': true,
|
|
512
|
-
'admin:update': true,
|
|
513
|
-
'admin:delete': true,
|
|
514
|
-
'users:create': true,
|
|
515
|
-
'users:read': true,
|
|
516
|
-
'users:update': true,
|
|
517
|
-
'users:delete': true,
|
|
518
|
-
'events:create': true,
|
|
519
|
-
'events:read': true,
|
|
520
|
-
'events:update': true,
|
|
521
|
-
'events:delete': true
|
|
522
|
-
});
|
|
523
|
-
setRoles(['super_admin']);
|
|
524
|
-
setAccessLevel(AccessLevel.ADMIN);
|
|
525
|
-
return;
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
if (selectedEventId) {
|
|
529
|
-
// Event-based permissions
|
|
530
|
-
refreshPermissions(selectedEventId);
|
|
531
|
-
} else if (!appConfig.requires_event) {
|
|
532
|
-
// Direct app permissions (no event needed)
|
|
533
|
-
refreshPermissions();
|
|
534
|
-
} else {
|
|
535
|
-
// No event and app requires events - clear permissions
|
|
536
|
-
setPermissions({});
|
|
537
|
-
setRoles([]);
|
|
538
|
-
setAccessLevel(AccessLevel.VIEWER);
|
|
539
|
-
}
|
|
540
|
-
}, [selectedEventId, user, session, appConfig, refreshPermissions]);
|
|
541
|
-
|
|
542
|
-
// Load user event access when user changes
|
|
543
|
-
useEffect(() => {
|
|
544
|
-
let isMounted = true;
|
|
545
|
-
|
|
546
|
-
if (user && session) {
|
|
547
|
-
DebugLogger.log('RBACProvider', 'Loading user event access for authenticated user');
|
|
548
|
-
loadUserEventAccess().catch(error => {
|
|
549
|
-
if (isMounted) {
|
|
550
|
-
console.error('Error loading user event access:', error);
|
|
551
|
-
}
|
|
552
|
-
});
|
|
553
|
-
} else {
|
|
554
|
-
DebugLogger.log('RBACProvider', 'Clearing user event access - no user or session');
|
|
555
|
-
if (isMounted) {
|
|
556
|
-
setUserEventAccess([]);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
return () => {
|
|
561
|
-
isMounted = false;
|
|
562
|
-
};
|
|
563
|
-
}, [user, session, loadUserEventAccess]);
|
|
564
|
-
|
|
565
|
-
// Permission methods
|
|
566
|
-
const hasPermission = useCallback((permission: string) => {
|
|
567
|
-
const hasPerm = !!permissions[permission];
|
|
568
|
-
return hasPerm;
|
|
569
|
-
}, [permissions]);
|
|
570
|
-
const hasAnyPermission = useCallback((perms: string[]) => perms.some(p => !!permissions[p]), [permissions]);
|
|
571
|
-
const hasAllPermissions = useCallback((perms: string[]) => perms.every(p => !!permissions[p]), [permissions]);
|
|
572
|
-
const hasRole = useCallback((role: string) => {
|
|
573
|
-
const isSuperAdmin = user?.user_metadata?.globalRole === 'super_admin';
|
|
574
|
-
if (role.toLowerCase() === 'super_admin') {
|
|
575
|
-
return isSuperAdmin;
|
|
576
|
-
}
|
|
577
|
-
return roles.includes(role);
|
|
578
|
-
}, [roles, user]);
|
|
579
|
-
const hasAccessLevel = useCallback((level: AccessLevel) => {
|
|
580
|
-
const levels = Object.values(AccessLevel);
|
|
581
|
-
return levels.indexOf(accessLevel) >= levels.indexOf(level);
|
|
582
|
-
}, [accessLevel]);
|
|
583
|
-
const canAccess = useCallback((resource: string, action: string) => {
|
|
584
|
-
const permission = `${resource}:${action}`;
|
|
585
|
-
const hasAccess = hasPermission(permission);
|
|
586
|
-
return hasAccess;
|
|
587
|
-
}, [hasPermission]);
|
|
588
|
-
const validatePermission = useCallback(async (permission: string) => hasPermission(permission), [hasPermission]);
|
|
589
|
-
const validateAccess = useCallback(async (resource: string, action: string) => {
|
|
590
|
-
// Placeholder for more complex logic, e.g., re-validating with backend
|
|
591
|
-
return Promise.resolve(canAccess(resource, action));
|
|
592
|
-
}, [canAccess]);
|
|
593
|
-
|
|
594
|
-
// Memoized context value
|
|
595
|
-
const contextValue = useMemo<RBACContextType>(() => ({
|
|
596
|
-
permissions,
|
|
597
|
-
roles,
|
|
598
|
-
accessLevel,
|
|
599
|
-
rbacLoading,
|
|
600
|
-
rbacError,
|
|
601
|
-
selectedEventId,
|
|
602
|
-
appConfig,
|
|
603
|
-
userEventAccess,
|
|
604
|
-
eventAccessLoading,
|
|
605
|
-
|
|
606
|
-
// Organisation context
|
|
607
|
-
selectedOrganisationId,
|
|
608
|
-
requireOrganisationContext: () => {
|
|
609
|
-
if (!selectedOrganisationId) {
|
|
610
|
-
throw new Error('Organisation context is required but not available');
|
|
611
|
-
}
|
|
612
|
-
return selectedOrganisationId;
|
|
613
|
-
},
|
|
614
|
-
|
|
615
|
-
hasPermission,
|
|
616
|
-
hasAnyPermission,
|
|
617
|
-
hasAllPermissions,
|
|
618
|
-
hasRole,
|
|
619
|
-
hasAccessLevel,
|
|
620
|
-
canAccess,
|
|
621
|
-
validatePermission,
|
|
622
|
-
validateAccess,
|
|
623
|
-
refreshPermissions,
|
|
624
|
-
setSelectedEventId,
|
|
625
|
-
|
|
626
|
-
// New RBAC system support
|
|
627
|
-
rbacEnabled: enableRBAC,
|
|
628
|
-
rbacContext: undefined, // Will be populated by useRBAC hook when enabled
|
|
629
|
-
|
|
630
|
-
loadUserEventAccess,
|
|
631
|
-
getUserEventAccess
|
|
632
|
-
}), [
|
|
633
|
-
permissions, roles, accessLevel, rbacLoading, rbacError, selectedEventId, appConfig,
|
|
634
|
-
userEventAccess, eventAccessLoading, selectedOrganisationId,
|
|
635
|
-
hasPermission, hasAnyPermission, hasAllPermissions, hasRole, hasAccessLevel, canAccess,
|
|
636
|
-
validatePermission, validateAccess, refreshPermissions, setSelectedEventId,
|
|
637
|
-
enableRBAC, loadUserEventAccess, getUserEventAccess
|
|
638
|
-
]);
|
|
639
|
-
|
|
640
|
-
return (
|
|
641
|
-
<RBACContext.Provider value={contextValue}>
|
|
642
|
-
{children}
|
|
643
|
-
</RBACContext.Provider>
|
|
644
|
-
);
|
|
645
|
-
}
|