@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
|
@@ -39,6 +39,8 @@ describe('RBACEngine - Core Logic Tests', () => {
|
|
|
39
39
|
|
|
40
40
|
beforeEach(() => {
|
|
41
41
|
mockSupabase = createMockSupabaseClient();
|
|
42
|
+
// Set default RPC mock to return true (can be overridden per test)
|
|
43
|
+
mockSupabase.rpc = vi.fn().mockResolvedValue({ data: true, error: null });
|
|
42
44
|
engine = new RBACEngine(mockSupabase as any);
|
|
43
45
|
// Clear cache before each test
|
|
44
46
|
rbacCache.clear();
|
|
@@ -52,25 +54,22 @@ describe('RBACEngine - Core Logic Tests', () => {
|
|
|
52
54
|
|
|
53
55
|
describe('Super Admin Bypass', () => {
|
|
54
56
|
it('allows super admin to bypass all permissions', async () => {
|
|
55
|
-
// Mock the
|
|
56
|
-
mockSupabase.
|
|
57
|
-
select: vi.fn().mockReturnThis(),
|
|
58
|
-
eq: vi.fn().mockReturnThis(),
|
|
59
|
-
lte: vi.fn().mockReturnThis(),
|
|
60
|
-
or: vi.fn().mockReturnThis(),
|
|
61
|
-
limit: vi.fn().mockResolvedValue({
|
|
62
|
-
data: [{ role: 'super_admin' }],
|
|
63
|
-
error: null
|
|
64
|
-
})
|
|
65
|
-
});
|
|
57
|
+
// Mock the RPC call to return true for super admin (may be called multiple times)
|
|
58
|
+
mockSupabase.rpc = vi.fn().mockResolvedValue({ data: true, error: null });
|
|
66
59
|
|
|
67
60
|
const permissionCheck: PermissionCheck = {
|
|
68
|
-
userId: '
|
|
69
|
-
scope: { organisationId: '
|
|
61
|
+
userId: '00000000-0000-0000-0000-000000000001' as UUID,
|
|
62
|
+
scope: { organisationId: '00000000-0000-0000-0000-000000000002' as UUID },
|
|
70
63
|
permission: 'manage:everything' as Permission
|
|
71
64
|
};
|
|
72
65
|
|
|
73
|
-
const
|
|
66
|
+
const securityContext = {
|
|
67
|
+
userId: '00000000-0000-0000-0000-000000000001' as UUID,
|
|
68
|
+
organisationId: '00000000-0000-0000-0000-000000000002' as UUID,
|
|
69
|
+
timestamp: new Date()
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const result = await engine.isPermitted(permissionCheck, securityContext);
|
|
74
73
|
expect(result).toBe(true);
|
|
75
74
|
});
|
|
76
75
|
|
|
@@ -142,108 +141,26 @@ describe('RBACEngine - Core Logic Tests', () => {
|
|
|
142
141
|
|
|
143
142
|
describe('Organisation Admin Permissions', () => {
|
|
144
143
|
it('grants organisation admin permissions', async () => {
|
|
145
|
-
// Mock
|
|
146
|
-
mockSupabase.rpc.mockResolvedValue({
|
|
147
|
-
data: [{ has_permission: false, role_name: null, permission_source: 'none', granted_at: null }],
|
|
148
|
-
error: null
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
// Mock app config
|
|
152
|
-
mockSupabase.from.mockReturnValue({
|
|
153
|
-
select: vi.fn().mockReturnThis(),
|
|
154
|
-
eq: vi.fn().mockReturnThis(),
|
|
155
|
-
single: vi.fn().mockResolvedValue({
|
|
156
|
-
data: { requires_event: false },
|
|
157
|
-
error: null
|
|
158
|
-
})
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
// Mock organisation roles query to return org_admin role
|
|
162
|
-
const mockOrgRolesQuery = {
|
|
163
|
-
select: vi.fn().mockReturnThis(),
|
|
164
|
-
eq: vi.fn().mockReturnThis(),
|
|
165
|
-
lte: vi.fn().mockReturnThis(),
|
|
166
|
-
or: vi.fn().mockReturnThis(),
|
|
167
|
-
data: [{
|
|
168
|
-
role: 'org_admin',
|
|
169
|
-
status: 'active',
|
|
170
|
-
valid_from: '2023-01-01',
|
|
171
|
-
valid_to: null
|
|
172
|
-
}],
|
|
173
|
-
error: null
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
// Mock the specific table queries
|
|
177
|
-
mockSupabase.from.mockImplementation((tableName: string) => {
|
|
178
|
-
if (tableName === 'rbac_organisation_roles') {
|
|
179
|
-
return mockOrgRolesQuery;
|
|
180
|
-
}
|
|
181
|
-
if (tableName === 'rbac_apps') {
|
|
182
|
-
return {
|
|
183
|
-
select: vi.fn().mockReturnThis(),
|
|
184
|
-
eq: vi.fn().mockReturnThis(),
|
|
185
|
-
single: vi.fn().mockResolvedValue({
|
|
186
|
-
data: { requires_event: false },
|
|
187
|
-
error: null
|
|
188
|
-
})
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
if (tableName === 'rbac_app_pages') {
|
|
192
|
-
return {
|
|
193
|
-
select: vi.fn().mockReturnThis(),
|
|
194
|
-
eq: vi.fn().mockReturnThis(),
|
|
195
|
-
single: vi.fn().mockResolvedValue({
|
|
196
|
-
data: { id: 'test-page', page_name: 'organisation' },
|
|
197
|
-
error: null
|
|
198
|
-
})
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
// Return empty data for other tables
|
|
202
|
-
return {
|
|
203
|
-
select: vi.fn().mockReturnThis(),
|
|
204
|
-
eq: vi.fn().mockReturnThis(),
|
|
205
|
-
lte: vi.fn().mockReturnThis(),
|
|
206
|
-
or: vi.fn().mockReturnThis(),
|
|
207
|
-
limit: vi.fn().mockResolvedValue({ data: [], error: null }),
|
|
208
|
-
data: [],
|
|
209
|
-
error: null
|
|
210
|
-
};
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
// Mock the rbac_permissions_get RPC call
|
|
214
|
-
mockSupabase.rpc.mockImplementation((functionName: string, params: any) => {
|
|
215
|
-
if (functionName === 'rbac_permissions_get') {
|
|
216
|
-
return Promise.resolve({
|
|
217
|
-
data: [
|
|
218
|
-
{
|
|
219
|
-
permission_type: 'read:page.organisation',
|
|
220
|
-
role_name: 'org_admin',
|
|
221
|
-
has_permission: true,
|
|
222
|
-
granted_at: '2023-01-01T00:00:00Z'
|
|
223
|
-
}
|
|
224
|
-
],
|
|
225
|
-
error: null
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
// Default RPC response
|
|
229
|
-
return Promise.resolve({
|
|
230
|
-
data: null,
|
|
231
|
-
error: null
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
|
|
144
|
+
// Mock the RPC call to return true (may be called multiple times)
|
|
145
|
+
mockSupabase.rpc = vi.fn().mockResolvedValue({ data: true, error: null });
|
|
235
146
|
|
|
236
147
|
const permissionCheck: PermissionCheck = {
|
|
237
|
-
userId: '
|
|
148
|
+
userId: '00000000-0000-0000-0000-000000000001' as UUID,
|
|
238
149
|
scope: {
|
|
239
|
-
organisationId: '
|
|
240
|
-
appId: '
|
|
150
|
+
organisationId: '00000000-0000-0000-0000-000000000002' as UUID,
|
|
151
|
+
appId: '00000000-0000-0000-0000-000000000003' as UUID
|
|
241
152
|
},
|
|
242
|
-
permission: 'read:
|
|
243
|
-
pageId: '
|
|
153
|
+
permission: 'read:organisation' as Permission,
|
|
154
|
+
pageId: '00000000-0000-0000-0000-000000000004' as UUID
|
|
244
155
|
};
|
|
245
156
|
|
|
246
|
-
const
|
|
157
|
+
const securityContext = {
|
|
158
|
+
userId: '00000000-0000-0000-0000-000000000001' as UUID,
|
|
159
|
+
organisationId: '00000000-0000-0000-0000-000000000002' as UUID,
|
|
160
|
+
timestamp: new Date()
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const result = await engine.isPermitted(permissionCheck, securityContext);
|
|
247
164
|
expect(result).toBe(true);
|
|
248
165
|
});
|
|
249
166
|
});
|
|
@@ -287,6 +204,7 @@ describe('RBACEngine - Core Logic Tests', () => {
|
|
|
287
204
|
return {
|
|
288
205
|
select: vi.fn().mockReturnThis(),
|
|
289
206
|
eq: vi.fn().mockReturnThis(),
|
|
207
|
+
is: vi.fn().mockReturnThis(),
|
|
290
208
|
single: vi.fn().mockResolvedValue({
|
|
291
209
|
data: { role: 'org_admin' },
|
|
292
210
|
error: null
|
|
@@ -40,6 +40,8 @@ describe('RBACEngine - Simplified Tests', () => {
|
|
|
40
40
|
|
|
41
41
|
beforeEach(() => {
|
|
42
42
|
mockSupabase = createMockSupabaseClient();
|
|
43
|
+
// Set default RPC mock to return true (can be overridden per test)
|
|
44
|
+
mockSupabase.rpc.mockResolvedValue({ data: true, error: null });
|
|
43
45
|
engine = new RBACEngine(mockSupabase as any);
|
|
44
46
|
// Clear cache before each test
|
|
45
47
|
rbacCache.clear();
|
|
@@ -92,101 +94,45 @@ describe('RBACEngine - Simplified Tests', () => {
|
|
|
92
94
|
|
|
93
95
|
describe('Engine Behavior', () => {
|
|
94
96
|
it('handles permission checks with proper database calls', async () => {
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
if (funcName === 'rbac_permissions_get') {
|
|
98
|
-
return Promise.resolve({
|
|
99
|
-
data: [{
|
|
100
|
-
permission_type: 'read:page.users',
|
|
101
|
-
resource_name: 'users',
|
|
102
|
-
role_name: 'org_admin',
|
|
103
|
-
has_permission: true,
|
|
104
|
-
granted_at: '2023-01-01T00:00:00Z'
|
|
105
|
-
}],
|
|
106
|
-
error: null
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
// For other RPC calls like rbac_permission_check (used in checkSuperAdmin)
|
|
110
|
-
return Promise.resolve({
|
|
111
|
-
data: false, // Return false for super admin check
|
|
112
|
-
error: null
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// Mock the from() chain for various database queries
|
|
117
|
-
mockSupabase.from.mockImplementation((table: string) => {
|
|
118
|
-
const mockChain = {
|
|
119
|
-
select: vi.fn().mockReturnThis(),
|
|
120
|
-
eq: vi.fn().mockReturnThis(),
|
|
121
|
-
lte: vi.fn().mockReturnThis(),
|
|
122
|
-
or: vi.fn().mockReturnThis(),
|
|
123
|
-
limit: vi.fn().mockResolvedValue({ data: [], error: null }),
|
|
124
|
-
single: vi.fn(),
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
if (table === 'rbac_organisation_roles') {
|
|
128
|
-
// Return organisation role for the user - make the chain thenable
|
|
129
|
-
Object.assign(mockChain, {
|
|
130
|
-
then: (resolve: any) => resolve({
|
|
131
|
-
data: [{ role: 'org_admin', status: 'active', valid_from: '2023-01-01', valid_to: null }],
|
|
132
|
-
error: null
|
|
133
|
-
})
|
|
134
|
-
});
|
|
135
|
-
} else if (table === 'rbac_global_roles') {
|
|
136
|
-
// Return global roles for the user - make the chain thenable
|
|
137
|
-
mockChain.limit = vi.fn().mockResolvedValue({
|
|
138
|
-
data: [],
|
|
139
|
-
error: null
|
|
140
|
-
});
|
|
141
|
-
} else if (table === 'rbac_event_app_roles') {
|
|
142
|
-
// Return empty event-app roles - make the chain thenable
|
|
143
|
-
Object.assign(mockChain, {
|
|
144
|
-
then: (resolve: any) => resolve({
|
|
145
|
-
data: [],
|
|
146
|
-
error: null
|
|
147
|
-
})
|
|
148
|
-
});
|
|
149
|
-
} else if (table === 'rbac_app_pages') {
|
|
150
|
-
// Return page name for page ID resolution
|
|
151
|
-
// The engine will call .select().eq().single()
|
|
152
|
-
mockChain.single.mockImplementation(() => Promise.resolve({
|
|
153
|
-
data: { id: 'page-123', page_name: 'users' },
|
|
154
|
-
error: null
|
|
155
|
-
}));
|
|
156
|
-
} else if (table === 'rbac_apps') {
|
|
157
|
-
// Return app config
|
|
158
|
-
mockChain.single.mockResolvedValue({
|
|
159
|
-
data: { requires_event: false },
|
|
160
|
-
error: null
|
|
161
|
-
});
|
|
162
|
-
} else {
|
|
163
|
-
mockChain.single.mockResolvedValue({
|
|
164
|
-
data: { requires_event: false },
|
|
165
|
-
error: null
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return mockChain;
|
|
170
|
-
});
|
|
171
|
-
|
|
97
|
+
// Verify that the RPC call is made with correct parameters
|
|
98
|
+
// Use valid UUID format for validation
|
|
172
99
|
const permissionCheck: PermissionCheck = {
|
|
173
|
-
userId: '
|
|
100
|
+
userId: '00000000-0000-0000-0000-000000000001' as UUID,
|
|
174
101
|
scope: {
|
|
175
|
-
organisationId: '
|
|
176
|
-
appId: '
|
|
102
|
+
organisationId: '00000000-0000-0000-0000-000000000002' as UUID,
|
|
103
|
+
appId: '00000000-0000-0000-0000-000000000003' as UUID
|
|
177
104
|
},
|
|
178
|
-
permission: 'read:
|
|
179
|
-
pageId: '
|
|
105
|
+
permission: 'read:users' as Permission,
|
|
106
|
+
pageId: '00000000-0000-0000-0000-000000000004' as UUID
|
|
180
107
|
};
|
|
181
108
|
|
|
182
|
-
const
|
|
109
|
+
const securityContext = {
|
|
110
|
+
userId: '00000000-0000-0000-0000-000000000001' as UUID,
|
|
111
|
+
organisationId: '00000000-0000-0000-0000-000000000002' as UUID,
|
|
112
|
+
timestamp: new Date()
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const result = await engine.isPermitted(permissionCheck, securityContext);
|
|
116
|
+
|
|
117
|
+
// Verify RPC was called
|
|
118
|
+
expect(mockSupabase.rpc).toHaveBeenCalledWith(
|
|
119
|
+
'rbac_check_permission_simplified',
|
|
120
|
+
expect.objectContaining({
|
|
121
|
+
p_user_id: '00000000-0000-0000-0000-000000000001',
|
|
122
|
+
p_permission: 'read:users',
|
|
123
|
+
p_organisation_id: '00000000-0000-0000-0000-000000000002',
|
|
124
|
+
p_app_id: '00000000-0000-0000-0000-000000000003',
|
|
125
|
+
p_page_id: '00000000-0000-0000-0000-000000000004'
|
|
126
|
+
})
|
|
127
|
+
);
|
|
128
|
+
|
|
183
129
|
expect(result).toBe(true);
|
|
184
130
|
});
|
|
185
131
|
|
|
186
132
|
it('returns false for denied permissions', async () => {
|
|
187
|
-
// Mock denied permission check
|
|
133
|
+
// Mock denied permission check - use the simplified RPC
|
|
188
134
|
mockSupabase.rpc.mockResolvedValue({
|
|
189
|
-
data:
|
|
135
|
+
data: false,
|
|
190
136
|
error: null
|
|
191
137
|
});
|
|
192
138
|
|
|
@@ -228,6 +174,7 @@ describe('RBACEngine - Simplified Tests', () => {
|
|
|
228
174
|
return {
|
|
229
175
|
select: vi.fn().mockReturnThis(),
|
|
230
176
|
eq: vi.fn().mockReturnThis(),
|
|
177
|
+
is: vi.fn().mockReturnThis(),
|
|
231
178
|
single: vi.fn().mockResolvedValue({
|
|
232
179
|
data: { role: 'org_admin' },
|
|
233
180
|
error: null
|
|
@@ -237,6 +184,7 @@ describe('RBACEngine - Simplified Tests', () => {
|
|
|
237
184
|
return {
|
|
238
185
|
select: vi.fn().mockReturnThis(),
|
|
239
186
|
eq: vi.fn().mockReturnThis(),
|
|
187
|
+
is: vi.fn().mockReturnThis(),
|
|
240
188
|
lte: vi.fn().mockReturnThis(),
|
|
241
189
|
or: vi.fn().mockReturnThis(),
|
|
242
190
|
limit: vi.fn().mockResolvedValue({ data: [], error: null })
|
|
@@ -5,7 +5,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
|
5
5
|
import { createMemoryRouter, RouterProvider } from 'react-router-dom';
|
|
6
6
|
import { UnifiedAuthProvider } from '../../providers/UnifiedAuthProvider';
|
|
7
7
|
import { OrganisationProvider } from '../../providers/OrganisationProvider';
|
|
8
|
-
|
|
8
|
+
// Note: RBACProvider was removed as part of Phase 1 - use useRBAC() hook instead
|
|
9
9
|
vi.mock('../hooks/useRBAC', () => ({
|
|
10
10
|
useRBAC: () => ({
|
|
11
11
|
user: { email: 'test@example.com' },
|
|
@@ -144,7 +144,7 @@ const Wrapper = ({ children }: { children: React.ReactNode }) => {
|
|
|
144
144
|
<QueryClientProvider client={queryClient}>
|
|
145
145
|
<UnifiedAuthProvider supabase={mockSupabaseClient}>
|
|
146
146
|
<OrganisationProvider>
|
|
147
|
-
|
|
147
|
+
{children}
|
|
148
148
|
</OrganisationProvider>
|
|
149
149
|
</UnifiedAuthProvider>
|
|
150
150
|
</QueryClientProvider>
|
package/src/rbac/adapters.tsx
CHANGED
|
@@ -7,11 +7,12 @@
|
|
|
7
7
|
* This module provides adapters for different frameworks and server runtimes.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import React, { ReactNode
|
|
10
|
+
import React, { ReactNode } from 'react';
|
|
11
11
|
import { UUID, Permission } from './types';
|
|
12
12
|
import { useCan } from './hooks';
|
|
13
13
|
import { rbacCache, RBACCache } from './cache';
|
|
14
14
|
import { getRBACLogger } from './config';
|
|
15
|
+
import { useUnifiedAuth } from '../providers/UnifiedAuthProvider';
|
|
15
16
|
|
|
16
17
|
// ============================================================================
|
|
17
18
|
// REACT COMPONENTS
|
|
@@ -74,49 +75,27 @@ export function PermissionGuard({
|
|
|
74
75
|
enforceAudit?: boolean;
|
|
75
76
|
}): React.ReactNode {
|
|
76
77
|
const logger = getRBACLogger();
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (authContext?.user?.id) {
|
|
87
|
-
effectiveUserId = authContext.user.id;
|
|
88
|
-
} else {
|
|
89
|
-
// Try to get from window or global context
|
|
90
|
-
const globalUser = (window as any).__PACE_USER__;
|
|
91
|
-
if (globalUser?.id) {
|
|
92
|
-
effectiveUserId = globalUser.id;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
} catch (error) {
|
|
96
|
-
logger.debug('Could not infer userId from context:', error);
|
|
78
|
+
|
|
79
|
+
let authContext: ReturnType<typeof useUnifiedAuth> | null = null;
|
|
80
|
+
try {
|
|
81
|
+
authContext = useUnifiedAuth();
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (error instanceof Error && error.message.includes('must be used within')) {
|
|
84
|
+
authContext = null;
|
|
85
|
+
} else {
|
|
86
|
+
throw error;
|
|
97
87
|
}
|
|
98
88
|
}
|
|
99
89
|
|
|
90
|
+
const effectiveUserId = userId ?? authContext?.user?.id ?? null;
|
|
91
|
+
|
|
100
92
|
// Always call useCan hook, but handle the case where userId might be undefined
|
|
101
93
|
const { can, isLoading, error } = useCan(effectiveUserId || '', scope, permission, pageId);
|
|
102
94
|
|
|
103
95
|
// If still no userId, show helpful error
|
|
104
96
|
if (!effectiveUserId) {
|
|
105
97
|
logger.error('PermissionGuard: No userId provided and could not infer from context');
|
|
106
|
-
return
|
|
107
|
-
<div className="rbac-error" role="alert">
|
|
108
|
-
<p>Permission check failed: User context not available</p>
|
|
109
|
-
<details>
|
|
110
|
-
<summary>Debug info</summary>
|
|
111
|
-
<p>Make sure to either:</p>
|
|
112
|
-
<ul>
|
|
113
|
-
<li>Pass userId prop explicitly</li>
|
|
114
|
-
<li>Wrap your app with an auth provider</li>
|
|
115
|
-
<li>Set window.__PACE_USER__ with user data</li>
|
|
116
|
-
</ul>
|
|
117
|
-
</details>
|
|
118
|
-
</div>
|
|
119
|
-
);
|
|
98
|
+
return fallback ?? null;
|
|
120
99
|
}
|
|
121
100
|
|
|
122
101
|
// Handle loading state
|
|
@@ -234,49 +213,27 @@ export function AccessLevelGuard({
|
|
|
234
213
|
loading?: ReactNode;
|
|
235
214
|
}): React.ReactNode {
|
|
236
215
|
const logger = getRBACLogger();
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
if (authContext?.user?.id) {
|
|
247
|
-
effectiveUserId = authContext.user.id;
|
|
248
|
-
} else {
|
|
249
|
-
// Try to get from window or global context
|
|
250
|
-
const globalUser = (window as any).__PACE_USER__;
|
|
251
|
-
if (globalUser?.id) {
|
|
252
|
-
effectiveUserId = globalUser.id;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
} catch (error) {
|
|
256
|
-
logger.debug('Could not infer userId from context:', error);
|
|
216
|
+
|
|
217
|
+
let authContext: ReturnType<typeof useUnifiedAuth> | null = null;
|
|
218
|
+
try {
|
|
219
|
+
authContext = useUnifiedAuth();
|
|
220
|
+
} catch (error) {
|
|
221
|
+
if (error instanceof Error && error.message.includes('must be used within')) {
|
|
222
|
+
authContext = null;
|
|
223
|
+
} else {
|
|
224
|
+
throw error;
|
|
257
225
|
}
|
|
258
226
|
}
|
|
259
227
|
|
|
228
|
+
const effectiveUserId = userId ?? authContext?.user?.id ?? null;
|
|
229
|
+
|
|
260
230
|
// Always call useAccessLevel hook, but handle the case where userId might be undefined
|
|
261
231
|
const { accessLevel, isLoading, error } = useAccessLevel(effectiveUserId || '', scope);
|
|
262
232
|
|
|
263
233
|
// If still no userId, show helpful error
|
|
264
234
|
if (!effectiveUserId) {
|
|
265
235
|
logger.error('AccessLevelGuard: No userId provided and could not infer from context');
|
|
266
|
-
return
|
|
267
|
-
<div className="rbac-error" role="alert">
|
|
268
|
-
<p>Access level check failed: User context not available</p>
|
|
269
|
-
<details>
|
|
270
|
-
<summary>Debug info</summary>
|
|
271
|
-
<p>Make sure to either:</p>
|
|
272
|
-
<ul>
|
|
273
|
-
<li>Pass userId prop explicitly</li>
|
|
274
|
-
<li>Wrap your app with an auth provider</li>
|
|
275
|
-
<li>Set window.__PACE_USER__ with user data</li>
|
|
276
|
-
</ul>
|
|
277
|
-
</details>
|
|
278
|
-
</div>
|
|
279
|
-
);
|
|
236
|
+
return fallback ?? null;
|
|
280
237
|
}
|
|
281
238
|
|
|
282
239
|
// Handle loading state
|
package/src/rbac/api.test.ts
CHANGED
|
@@ -451,6 +451,8 @@ describe('RBAC API', () => {
|
|
|
451
451
|
getAccessLevel: vi.fn(),
|
|
452
452
|
getPermissionMap: vi.fn(),
|
|
453
453
|
isPermitted: vi.fn(),
|
|
454
|
+
resolveAppContext: vi.fn(),
|
|
455
|
+
getRoleContext: vi.fn(),
|
|
454
456
|
checkSuperAdmin: vi.fn(),
|
|
455
457
|
getAppConfig: vi.fn()
|
|
456
458
|
};
|
|
@@ -534,6 +536,52 @@ describe('RBAC API', () => {
|
|
|
534
536
|
});
|
|
535
537
|
});
|
|
536
538
|
|
|
539
|
+
describe('resolveAppContext', () => {
|
|
540
|
+
it('resolves app context via engine', async () => {
|
|
541
|
+
const { resolveAppContext } = await import('./api');
|
|
542
|
+
const context = { appId: 'app-123', hasAccess: true };
|
|
543
|
+
mockEngine.resolveAppContext.mockResolvedValue(context);
|
|
544
|
+
|
|
545
|
+
const result = await resolveAppContext({ userId: 'user-1', appName: 'test-app' });
|
|
546
|
+
|
|
547
|
+
expect(result).toEqual(context);
|
|
548
|
+
expect(mockEngine.resolveAppContext).toHaveBeenCalledWith({ userId: 'user-1', appName: 'test-app' });
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
it('returns null when engine fails', async () => {
|
|
552
|
+
const { resolveAppContext } = await import('./api');
|
|
553
|
+
mockEngine.resolveAppContext.mockResolvedValue(null);
|
|
554
|
+
|
|
555
|
+
const result = await resolveAppContext({ userId: 'user-1', appName: 'test-app' });
|
|
556
|
+
|
|
557
|
+
expect(result).toBeNull();
|
|
558
|
+
});
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
describe('getRoleContext', () => {
|
|
562
|
+
it('returns role context from engine', async () => {
|
|
563
|
+
const { getRoleContext } = await import('./api');
|
|
564
|
+
const roleContext = {
|
|
565
|
+
globalRole: 'super_admin',
|
|
566
|
+
organisationRole: 'org_admin',
|
|
567
|
+
eventAppRole: 'event_admin',
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
mockEngine.getRoleContext.mockResolvedValue(roleContext);
|
|
571
|
+
|
|
572
|
+
const result = await getRoleContext({
|
|
573
|
+
userId: 'user-123',
|
|
574
|
+
scope: { organisationId: 'org-456' },
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
expect(result).toEqual(roleContext);
|
|
578
|
+
expect(mockEngine.getRoleContext).toHaveBeenCalledWith({
|
|
579
|
+
userId: 'user-123',
|
|
580
|
+
scope: { organisationId: 'org-456' },
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
|
|
537
585
|
describe('isPermitted', () => {
|
|
538
586
|
it('returns permission result', async () => {
|
|
539
587
|
const { isPermitted } = await import('./api');
|
|
@@ -548,12 +596,18 @@ describe('RBAC API', () => {
|
|
|
548
596
|
});
|
|
549
597
|
|
|
550
598
|
expect(result).toBe(true);
|
|
551
|
-
expect(mockEngine.isPermitted).toHaveBeenCalledWith(
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
599
|
+
expect(mockEngine.isPermitted).toHaveBeenCalledWith(
|
|
600
|
+
{
|
|
601
|
+
userId: 'user-123',
|
|
602
|
+
scope: { organisationId: 'org-456' },
|
|
603
|
+
permission: 'read:users',
|
|
604
|
+
pageId: 'page-789'
|
|
605
|
+
},
|
|
606
|
+
expect.objectContaining({
|
|
607
|
+
userId: 'user-123',
|
|
608
|
+
organisationId: 'org-456'
|
|
609
|
+
})
|
|
610
|
+
);
|
|
557
611
|
});
|
|
558
612
|
|
|
559
613
|
it('handles engine errors', async () => {
|
|
@@ -603,12 +657,18 @@ describe('RBAC API', () => {
|
|
|
603
657
|
});
|
|
604
658
|
|
|
605
659
|
expect(result).toBe(true);
|
|
606
|
-
expect(mockEngine.isPermitted).toHaveBeenCalledWith(
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
660
|
+
expect(mockEngine.isPermitted).toHaveBeenCalledWith(
|
|
661
|
+
{
|
|
662
|
+
userId: 'user-123',
|
|
663
|
+
scope: { organisationId: 'org-456' },
|
|
664
|
+
permission: 'read:users',
|
|
665
|
+
pageId: 'page-789'
|
|
666
|
+
},
|
|
667
|
+
expect.objectContaining({
|
|
668
|
+
userId: 'user-123',
|
|
669
|
+
organisationId: 'org-456'
|
|
670
|
+
})
|
|
671
|
+
);
|
|
612
672
|
expect(rbacCache.set).toHaveBeenCalledWith(
|
|
613
673
|
expect.any(String),
|
|
614
674
|
true
|
|
@@ -629,11 +689,17 @@ describe('RBAC API', () => {
|
|
|
629
689
|
});
|
|
630
690
|
|
|
631
691
|
expect(result).toBe(true);
|
|
632
|
-
expect(mockEngine.isPermitted).toHaveBeenCalledWith(
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
692
|
+
expect(mockEngine.isPermitted).toHaveBeenCalledWith(
|
|
693
|
+
{
|
|
694
|
+
userId: 'user-123',
|
|
695
|
+
scope: { organisationId: 'org-456' },
|
|
696
|
+
permission: 'read:users'
|
|
697
|
+
},
|
|
698
|
+
expect.objectContaining({
|
|
699
|
+
userId: 'user-123',
|
|
700
|
+
organisationId: 'org-456'
|
|
701
|
+
})
|
|
702
|
+
);
|
|
637
703
|
});
|
|
638
704
|
});
|
|
639
705
|
|
|
@@ -731,22 +797,19 @@ describe('RBAC API', () => {
|
|
|
731
797
|
it('returns app configuration', async () => {
|
|
732
798
|
const { getAppConfig } = await import('./api');
|
|
733
799
|
|
|
734
|
-
|
|
735
|
-
mockEngine.getAppConfig.mockResolvedValue(mockConfig);
|
|
736
|
-
|
|
800
|
+
// getAppConfig now returns null (needs Supabase client)
|
|
737
801
|
const result = await getAppConfig('app-123');
|
|
738
802
|
|
|
739
|
-
expect(result).
|
|
740
|
-
expect(mockEngine.getAppConfig).toHaveBeenCalledWith('app-123');
|
|
803
|
+
expect(result).toBeNull();
|
|
741
804
|
});
|
|
742
805
|
|
|
743
|
-
it('handles
|
|
806
|
+
it('handles missing Supabase client gracefully', async () => {
|
|
744
807
|
const { getAppConfig } = await import('./api');
|
|
745
808
|
|
|
746
|
-
const
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
809
|
+
const result = await getAppConfig('app-123');
|
|
810
|
+
|
|
811
|
+
// Should return null when no client is available
|
|
812
|
+
expect(result).toBeNull();
|
|
750
813
|
});
|
|
751
814
|
});
|
|
752
815
|
|