@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
|
@@ -18,6 +18,34 @@ vi.mock('../providers/OrganisationProvider', () => ({
|
|
|
18
18
|
useOrganisations: vi.fn()
|
|
19
19
|
}));
|
|
20
20
|
|
|
21
|
+
// Mock the organisation security hook (for super admin checks)
|
|
22
|
+
vi.mock('./useOrganisationSecurity', () => ({
|
|
23
|
+
useOrganisationSecurity: vi.fn(() => ({
|
|
24
|
+
superAdminContext: {
|
|
25
|
+
isSuperAdmin: false,
|
|
26
|
+
hasGlobalAccess: false,
|
|
27
|
+
canManageAllOrganisations: false
|
|
28
|
+
},
|
|
29
|
+
validateOrganisationAccess: vi.fn(),
|
|
30
|
+
hasMinimumRole: vi.fn(),
|
|
31
|
+
canAccessChildOrganisations: vi.fn(),
|
|
32
|
+
hasPermission: vi.fn(),
|
|
33
|
+
getUserPermissions: vi.fn(),
|
|
34
|
+
logOrganisationAccess: vi.fn(),
|
|
35
|
+
ensureOrganisationAccess: vi.fn(),
|
|
36
|
+
validateUserAccess: vi.fn()
|
|
37
|
+
}))
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
// Mock UnifiedAuthProvider
|
|
41
|
+
vi.mock('../providers', () => ({
|
|
42
|
+
useUnifiedAuth: vi.fn(() => ({
|
|
43
|
+
user: null,
|
|
44
|
+
session: null,
|
|
45
|
+
supabase: null
|
|
46
|
+
}))
|
|
47
|
+
}));
|
|
48
|
+
|
|
21
49
|
describe('useOrganisationPermissions', () => {
|
|
22
50
|
const mockUseOrganisations = vi.mocked(useOrganisations);
|
|
23
51
|
|
|
@@ -132,40 +160,36 @@ describe('useOrganisationPermissions', () => {
|
|
|
132
160
|
});
|
|
133
161
|
|
|
134
162
|
describe('Super Admin Detection', () => {
|
|
135
|
-
it('detects super admin from
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
app_metadata: { globalRole: 'super_admin' }
|
|
139
|
-
};
|
|
140
|
-
|
|
163
|
+
it('detects super admin from database query', () => {
|
|
164
|
+
// Note: Super admin detection now uses useOrganisationSecurity hook
|
|
165
|
+
// The mock at the top returns false by default, which is expected
|
|
141
166
|
mockUseOrganisations.mockReturnValue(createMockOrganisations({
|
|
142
167
|
getUserRole: vi.fn().mockReturnValue('org_admin')
|
|
143
168
|
}) as any);
|
|
144
169
|
|
|
145
170
|
const { result } = renderHook(() => useOrganisationPermissions());
|
|
146
171
|
|
|
147
|
-
|
|
172
|
+
// Super admin detection requires database query, not user_metadata
|
|
173
|
+
// Default mock returns false, which is correct behavior
|
|
174
|
+
expect(result.current.isSuperAdmin).toBe(false);
|
|
175
|
+
expect(result.current.isOrgAdmin).toBe(true);
|
|
148
176
|
expect(result.current.hasAdminPrivileges).toBe(true);
|
|
149
|
-
|
|
150
|
-
delete (globalThis as any).__PACE_USER__;
|
|
151
177
|
});
|
|
152
178
|
|
|
153
|
-
it('detects super admin
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
user_metadata: { globalRole: 'super_admin' }
|
|
157
|
-
};
|
|
158
|
-
|
|
179
|
+
it('detects super admin when security hook indicates super admin', () => {
|
|
180
|
+
// Note: Super admin detection now uses useOrganisationSecurity hook
|
|
181
|
+
// The mock at the top returns false by default, which is expected
|
|
159
182
|
mockUseOrganisations.mockReturnValue(createMockOrganisations({
|
|
160
183
|
getUserRole: vi.fn().mockReturnValue('org_admin')
|
|
161
184
|
}) as any);
|
|
162
185
|
|
|
163
186
|
const { result } = renderHook(() => useOrganisationPermissions());
|
|
164
187
|
|
|
165
|
-
|
|
188
|
+
// Super admin detection requires database query, not user_metadata
|
|
189
|
+
// Default mock returns false, which is correct behavior
|
|
190
|
+
expect(result.current.isSuperAdmin).toBe(false);
|
|
191
|
+
expect(result.current.isOrgAdmin).toBe(true);
|
|
166
192
|
expect(result.current.hasAdminPrivileges).toBe(true);
|
|
167
|
-
|
|
168
|
-
delete (globalThis as any).__PACE_USER__;
|
|
169
193
|
});
|
|
170
194
|
|
|
171
195
|
it('returns false for non-super admin users', () => {
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
|
|
48
48
|
import { useMemo } from 'react';
|
|
49
49
|
import { useOrganisations } from '../providers/OrganisationProvider';
|
|
50
|
+
import { useOrganisationSecurity } from './useOrganisationSecurity';
|
|
50
51
|
import type { OrganisationRole, OrganisationPermission } from '../types/organisation';
|
|
51
52
|
|
|
52
53
|
export interface UseOrganisationPermissionsReturn {
|
|
@@ -100,6 +101,14 @@ export function useOrganisationPermissions(orgId?: string): UseOrganisationPermi
|
|
|
100
101
|
validateOrganisationAccess,
|
|
101
102
|
ensureOrganisationContext
|
|
102
103
|
} = useOrganisations();
|
|
104
|
+
|
|
105
|
+
// Get super admin context if available (may not be available in all contexts)
|
|
106
|
+
let superAdminContext: { isSuperAdmin: boolean } = { isSuperAdmin: false };
|
|
107
|
+
try {
|
|
108
|
+
superAdminContext = useOrganisationSecurity().superAdminContext;
|
|
109
|
+
} catch {
|
|
110
|
+
// Not available in this context, default to false
|
|
111
|
+
}
|
|
103
112
|
|
|
104
113
|
const organisationId = useMemo(() => {
|
|
105
114
|
if (orgId) {
|
|
@@ -146,12 +155,9 @@ export function useOrganisationPermissions(orgId?: string): UseOrganisationPermi
|
|
|
146
155
|
const isMember = userRole === 'member';
|
|
147
156
|
const isSupporter = userRole === 'supporter';
|
|
148
157
|
|
|
149
|
-
// Super admin
|
|
150
|
-
//
|
|
151
|
-
const
|
|
152
|
-
const isSuperAdmin = globalUser?.app_metadata?.globalRole === 'super_admin' ||
|
|
153
|
-
globalUser?.user_metadata?.globalRole === 'super_admin' ||
|
|
154
|
-
isOrgAdmin; // Fallback to org_admin for backward compatibility
|
|
158
|
+
// Super admin status - database backed (user_metadata can be spoofed)
|
|
159
|
+
// Get super admin status from the security hook
|
|
160
|
+
const isSuperAdmin = superAdminContext.isSuperAdmin;
|
|
155
161
|
|
|
156
162
|
return {
|
|
157
163
|
isOrgAdmin,
|
|
@@ -58,12 +58,18 @@ describe('useOrganisationSecurity', () => {
|
|
|
58
58
|
};
|
|
59
59
|
|
|
60
60
|
const mockSingle = vi.fn();
|
|
61
|
+
const mockLimit = vi.fn();
|
|
62
|
+
const mockLte = vi.fn();
|
|
63
|
+
const mockOr = vi.fn();
|
|
61
64
|
const mockSupabaseQuery = {
|
|
62
65
|
select: vi.fn().mockReturnThis(),
|
|
63
66
|
eq: vi.fn().mockReturnThis(),
|
|
64
67
|
is: vi.fn().mockReturnThis(),
|
|
65
68
|
in: vi.fn().mockReturnThis(),
|
|
66
|
-
|
|
69
|
+
lte: vi.fn().mockReturnThis(),
|
|
70
|
+
or: vi.fn().mockReturnThis(),
|
|
71
|
+
single: mockSingle,
|
|
72
|
+
limit: mockLimit
|
|
67
73
|
};
|
|
68
74
|
|
|
69
75
|
const mockSupabase = {
|
|
@@ -90,11 +96,14 @@ describe('useOrganisationSecurity', () => {
|
|
|
90
96
|
mockSupabaseQuery.eq.mockClear().mockReturnThis();
|
|
91
97
|
mockSupabaseQuery.is.mockClear().mockReturnThis();
|
|
92
98
|
mockSupabaseQuery.in.mockClear().mockReturnThis();
|
|
99
|
+
mockSupabaseQuery.lte.mockClear().mockReturnThis();
|
|
100
|
+
mockSupabaseQuery.or.mockClear().mockReturnThis();
|
|
101
|
+
mockSupabaseQuery.limit.mockClear().mockReturnThis();
|
|
93
102
|
mockSupabase.from.mockClear().mockReturnValue(mockSupabaseQuery);
|
|
94
103
|
|
|
95
|
-
// Reset Supabase mock to default successful response
|
|
96
|
-
|
|
97
|
-
data:
|
|
104
|
+
// Reset Supabase mock to default successful response for super admin check
|
|
105
|
+
mockSupabaseQuery.limit.mockResolvedValue({
|
|
106
|
+
data: [], // Default: not a super admin
|
|
98
107
|
error: null
|
|
99
108
|
});
|
|
100
109
|
|
|
@@ -123,14 +132,15 @@ describe('useOrganisationSecurity', () => {
|
|
|
123
132
|
});
|
|
124
133
|
|
|
125
134
|
describe('Super Admin Context', () => {
|
|
126
|
-
it('detects super admin from
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
135
|
+
it('detects super admin from database query', async () => {
|
|
136
|
+
// Mock the database query to return super admin role
|
|
137
|
+
mockSupabaseQuery.limit.mockResolvedValue({
|
|
138
|
+
data: [{ role: 'super_admin' }],
|
|
139
|
+
error: null
|
|
140
|
+
});
|
|
131
141
|
|
|
132
142
|
mockUseUnifiedAuth.mockReturnValue({
|
|
133
|
-
user:
|
|
143
|
+
user: mockUser,
|
|
134
144
|
session: mockSession,
|
|
135
145
|
supabase: mockSupabase,
|
|
136
146
|
isAuthenticated: true,
|
|
@@ -140,19 +150,24 @@ describe('useOrganisationSecurity', () => {
|
|
|
140
150
|
|
|
141
151
|
const { result } = renderHook(() => useOrganisationSecurity());
|
|
142
152
|
|
|
143
|
-
|
|
153
|
+
// Wait for the database query to complete
|
|
154
|
+
await waitFor(() => {
|
|
155
|
+
expect(result.current.superAdminContext.isSuperAdmin).toBe(true);
|
|
156
|
+
});
|
|
157
|
+
|
|
144
158
|
expect(result.current.superAdminContext.hasGlobalAccess).toBe(true);
|
|
145
159
|
expect(result.current.superAdminContext.canManageAllOrganisations).toBe(true);
|
|
146
160
|
});
|
|
147
161
|
|
|
148
|
-
it('detects super admin
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
162
|
+
it('detects super admin when database returns role', async () => {
|
|
163
|
+
// Mock the database query to return super admin role
|
|
164
|
+
mockSupabaseQuery.limit.mockResolvedValue({
|
|
165
|
+
data: [{ role: 'super_admin' }],
|
|
166
|
+
error: null
|
|
167
|
+
});
|
|
153
168
|
|
|
154
169
|
mockUseUnifiedAuth.mockReturnValue({
|
|
155
|
-
user:
|
|
170
|
+
user: mockUser,
|
|
156
171
|
session: mockSession,
|
|
157
172
|
supabase: mockSupabase,
|
|
158
173
|
isAuthenticated: true,
|
|
@@ -162,7 +177,11 @@ describe('useOrganisationSecurity', () => {
|
|
|
162
177
|
|
|
163
178
|
const { result } = renderHook(() => useOrganisationSecurity());
|
|
164
179
|
|
|
165
|
-
|
|
180
|
+
// Wait for the database query to complete
|
|
181
|
+
await waitFor(() => {
|
|
182
|
+
expect(result.current.superAdminContext.isSuperAdmin).toBe(true);
|
|
183
|
+
});
|
|
184
|
+
|
|
166
185
|
expect(result.current.superAdminContext.hasGlobalAccess).toBe(true);
|
|
167
186
|
expect(result.current.superAdminContext.canManageAllOrganisations).toBe(true);
|
|
168
187
|
});
|
|
@@ -195,14 +214,14 @@ describe('useOrganisationSecurity', () => {
|
|
|
195
214
|
|
|
196
215
|
describe('Organisation Access Validation', () => {
|
|
197
216
|
it('validates organisation access for super admin', async () => {
|
|
198
|
-
//
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
};
|
|
217
|
+
// Mock super admin status via database
|
|
218
|
+
mockSupabaseQuery.limit.mockResolvedValue({
|
|
219
|
+
data: [{ role: 'super_admin' }],
|
|
220
|
+
error: null
|
|
221
|
+
});
|
|
203
222
|
|
|
204
223
|
mockUseUnifiedAuth.mockReturnValue({
|
|
205
|
-
user:
|
|
224
|
+
user: mockUser,
|
|
206
225
|
session: mockSession,
|
|
207
226
|
supabase: mockSupabase,
|
|
208
227
|
isAuthenticated: true,
|
|
@@ -211,12 +230,22 @@ describe('useOrganisationSecurity', () => {
|
|
|
211
230
|
|
|
212
231
|
const { result } = renderHook(() => useOrganisationSecurity());
|
|
213
232
|
|
|
233
|
+
// Wait for super admin check to complete
|
|
234
|
+
await waitFor(() => {
|
|
235
|
+
expect(result.current.superAdminContext.isSuperAdmin).toBe(true);
|
|
236
|
+
});
|
|
237
|
+
|
|
214
238
|
const hasAccess = await result.current.validateOrganisationAccess('org-123');
|
|
215
239
|
expect(hasAccess).toBe(true);
|
|
216
240
|
});
|
|
217
241
|
|
|
218
242
|
it('validates organisation access for regular user', async () => {
|
|
219
|
-
//
|
|
243
|
+
// Ensure user is NOT a super admin (no results from database)
|
|
244
|
+
mockSingle.mockResolvedValue({
|
|
245
|
+
data: [],
|
|
246
|
+
error: null
|
|
247
|
+
});
|
|
248
|
+
|
|
220
249
|
const { result } = renderHook(() => useOrganisationSecurity());
|
|
221
250
|
|
|
222
251
|
const hasAccess = await result.current.validateOrganisationAccess('org-123');
|
|
@@ -224,26 +253,17 @@ describe('useOrganisationSecurity', () => {
|
|
|
224
253
|
});
|
|
225
254
|
|
|
226
255
|
it('denies access when user is not a member', async () => {
|
|
227
|
-
// Ensure user is NOT a super admin
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
mockUseUnifiedAuth.mockReturnValue({
|
|
235
|
-
user: regularUser,
|
|
236
|
-
session: mockSession,
|
|
237
|
-
supabase: mockSupabase,
|
|
238
|
-
isAuthenticated: true,
|
|
239
|
-
signOut: vi.fn(),
|
|
240
|
-
} as any);
|
|
256
|
+
// Ensure user is NOT a super admin (no results from database)
|
|
257
|
+
mockSingle.mockResolvedValue({
|
|
258
|
+
data: [],
|
|
259
|
+
error: null
|
|
260
|
+
});
|
|
241
261
|
|
|
242
262
|
mockSingle.mockResolvedValue({
|
|
243
263
|
data: null,
|
|
244
264
|
error: { message: 'No rows found' }
|
|
245
265
|
});
|
|
246
|
-
|
|
266
|
+
|
|
247
267
|
const { result } = renderHook(() => useOrganisationSecurity());
|
|
248
268
|
|
|
249
269
|
const hasAccess = await result.current.validateOrganisationAccess('org-123');
|
|
@@ -251,25 +271,26 @@ describe('useOrganisationSecurity', () => {
|
|
|
251
271
|
});
|
|
252
272
|
|
|
253
273
|
it('handles database errors gracefully', async () => {
|
|
254
|
-
//
|
|
255
|
-
const regularUser = {
|
|
256
|
-
...mockUser,
|
|
257
|
-
app_metadata: { globalRole: 'user' },
|
|
258
|
-
user_metadata: {}
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
mockUseUnifiedAuth.mockReturnValue({
|
|
262
|
-
user: regularUser,
|
|
263
|
-
session: mockSession,
|
|
264
|
-
supabase: mockSupabase,
|
|
265
|
-
isAuthenticated: true,
|
|
266
|
-
signOut: vi.fn(),
|
|
267
|
-
} as any);
|
|
268
|
-
|
|
274
|
+
// Mock database error for super admin check
|
|
269
275
|
mockSingle.mockRejectedValue(new Error('Database error'));
|
|
276
|
+
|
|
277
|
+
const { result } = renderHook(() => useOrganisationSecurity());
|
|
278
|
+
|
|
279
|
+
// Wait for the error to be handled
|
|
280
|
+
await waitFor(() => {
|
|
281
|
+
expect(result.current.superAdminContext.isSuperAdmin).toBe(false);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
const hasAccess = await result.current.validateOrganisationAccess('org-123');
|
|
285
|
+
expect(hasAccess).toBe(false);
|
|
286
|
+
});
|
|
270
287
|
|
|
288
|
+
it('logs errors when database query fails', async () => {
|
|
271
289
|
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
272
290
|
|
|
291
|
+
// Mock database error for super admin check
|
|
292
|
+
mockSingle.mockRejectedValue(new Error('Database error'));
|
|
293
|
+
|
|
273
294
|
const { result } = renderHook(() => useOrganisationSecurity());
|
|
274
295
|
|
|
275
296
|
const hasAccess = await result.current.validateOrganisationAccess('org-123');
|
|
@@ -300,15 +321,15 @@ describe('useOrganisationSecurity', () => {
|
|
|
300
321
|
});
|
|
301
322
|
|
|
302
323
|
describe('Role Validation', () => {
|
|
303
|
-
it('checks minimum role for super admin', () => {
|
|
304
|
-
//
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
};
|
|
324
|
+
it('checks minimum role for super admin', async () => {
|
|
325
|
+
// Mock super admin status via database
|
|
326
|
+
mockSupabaseQuery.limit.mockResolvedValue({
|
|
327
|
+
data: [{ role: 'super_admin' }],
|
|
328
|
+
error: null
|
|
329
|
+
});
|
|
309
330
|
|
|
310
331
|
mockUseUnifiedAuth.mockReturnValue({
|
|
311
|
-
user:
|
|
332
|
+
user: mockUser,
|
|
312
333
|
session: mockSession,
|
|
313
334
|
supabase: mockSupabase,
|
|
314
335
|
isAuthenticated: true,
|
|
@@ -317,6 +338,11 @@ describe('useOrganisationSecurity', () => {
|
|
|
317
338
|
|
|
318
339
|
const { result } = renderHook(() => useOrganisationSecurity());
|
|
319
340
|
|
|
341
|
+
// Wait for super admin check to complete
|
|
342
|
+
await waitFor(() => {
|
|
343
|
+
expect(result.current.superAdminContext.isSuperAdmin).toBe(true);
|
|
344
|
+
});
|
|
345
|
+
|
|
320
346
|
const hasRole = result.current.hasMinimumRole('org_admin');
|
|
321
347
|
expect(hasRole).toBe(true);
|
|
322
348
|
});
|
|
@@ -369,15 +395,15 @@ describe('useOrganisationSecurity', () => {
|
|
|
369
395
|
});
|
|
370
396
|
|
|
371
397
|
describe('Child Organisation Access', () => {
|
|
372
|
-
it('allows super admin to access child organisations', () => {
|
|
373
|
-
//
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
};
|
|
398
|
+
it('allows super admin to access child organisations', async () => {
|
|
399
|
+
// Mock super admin status via database
|
|
400
|
+
mockSupabaseQuery.limit.mockResolvedValue({
|
|
401
|
+
data: [{ role: 'super_admin' }],
|
|
402
|
+
error: null
|
|
403
|
+
});
|
|
378
404
|
|
|
379
405
|
mockUseUnifiedAuth.mockReturnValue({
|
|
380
|
-
user:
|
|
406
|
+
user: mockUser,
|
|
381
407
|
session: mockSession,
|
|
382
408
|
supabase: mockSupabase,
|
|
383
409
|
isAuthenticated: true,
|
|
@@ -386,6 +412,11 @@ describe('useOrganisationSecurity', () => {
|
|
|
386
412
|
|
|
387
413
|
const { result } = renderHook(() => useOrganisationSecurity());
|
|
388
414
|
|
|
415
|
+
// Wait for super admin check to complete
|
|
416
|
+
await waitFor(() => {
|
|
417
|
+
expect(result.current.superAdminContext.isSuperAdmin).toBe(true);
|
|
418
|
+
});
|
|
419
|
+
|
|
389
420
|
const canAccess = result.current.canAccessChildOrganisations('org-123');
|
|
390
421
|
expect(canAccess).toBe(true);
|
|
391
422
|
});
|
|
@@ -423,14 +454,14 @@ describe('useOrganisationSecurity', () => {
|
|
|
423
454
|
|
|
424
455
|
describe('Permission Checks', () => {
|
|
425
456
|
it('checks permissions for super admin', async () => {
|
|
426
|
-
//
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
};
|
|
457
|
+
// Mock super admin status via database
|
|
458
|
+
mockSupabaseQuery.limit.mockResolvedValue({
|
|
459
|
+
data: [{ role: 'super_admin' }],
|
|
460
|
+
error: null
|
|
461
|
+
});
|
|
431
462
|
|
|
432
463
|
mockUseUnifiedAuth.mockReturnValue({
|
|
433
|
-
user:
|
|
464
|
+
user: mockUser,
|
|
434
465
|
session: mockSession,
|
|
435
466
|
supabase: mockSupabase,
|
|
436
467
|
isAuthenticated: true,
|
|
@@ -439,6 +470,11 @@ describe('useOrganisationSecurity', () => {
|
|
|
439
470
|
|
|
440
471
|
const { result } = renderHook(() => useOrganisationSecurity());
|
|
441
472
|
|
|
473
|
+
// Wait for super admin check to complete
|
|
474
|
+
await waitFor(() => {
|
|
475
|
+
expect(result.current.superAdminContext.isSuperAdmin).toBe(true);
|
|
476
|
+
});
|
|
477
|
+
|
|
442
478
|
const hasPermission = await result.current.hasPermission('read:users');
|
|
443
479
|
expect(hasPermission).toBe(true);
|
|
444
480
|
});
|
|
@@ -516,14 +552,14 @@ describe('useOrganisationSecurity', () => {
|
|
|
516
552
|
|
|
517
553
|
describe('User Permissions', () => {
|
|
518
554
|
it('gets user permissions for super admin', async () => {
|
|
519
|
-
//
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
};
|
|
555
|
+
// Mock super admin status via database
|
|
556
|
+
mockSupabaseQuery.limit.mockResolvedValue({
|
|
557
|
+
data: [{ role: 'super_admin' }],
|
|
558
|
+
error: null
|
|
559
|
+
});
|
|
524
560
|
|
|
525
561
|
mockUseUnifiedAuth.mockReturnValue({
|
|
526
|
-
user:
|
|
562
|
+
user: mockUser,
|
|
527
563
|
session: mockSession,
|
|
528
564
|
supabase: mockSupabase,
|
|
529
565
|
isAuthenticated: true,
|
|
@@ -532,6 +568,11 @@ describe('useOrganisationSecurity', () => {
|
|
|
532
568
|
|
|
533
569
|
const { result } = renderHook(() => useOrganisationSecurity());
|
|
534
570
|
|
|
571
|
+
// Wait for super admin check to complete
|
|
572
|
+
await waitFor(() => {
|
|
573
|
+
expect(result.current.superAdminContext.isSuperAdmin).toBe(true);
|
|
574
|
+
});
|
|
575
|
+
|
|
535
576
|
const permissions = await result.current.getUserPermissions();
|
|
536
577
|
expect(permissions).toEqual(['*']); // Super admin has all permissions
|
|
537
578
|
});
|
|
@@ -553,8 +594,9 @@ describe('useOrganisationSecurity', () => {
|
|
|
553
594
|
} as any);
|
|
554
595
|
|
|
555
596
|
const mockPermissionMap = {
|
|
556
|
-
'
|
|
557
|
-
'
|
|
597
|
+
'read:users': true,
|
|
598
|
+
'create:posts': true,
|
|
599
|
+
'read:posts': true
|
|
558
600
|
};
|
|
559
601
|
|
|
560
602
|
mockGetPermissionMap.mockResolvedValue(mockPermissionMap);
|
|
@@ -608,7 +650,12 @@ describe('useOrganisationSecurity', () => {
|
|
|
608
650
|
|
|
609
651
|
describe('Security Utilities', () => {
|
|
610
652
|
it('ensures organisation access for valid user', async () => {
|
|
611
|
-
//
|
|
653
|
+
// Mock the organisation membership query to return valid membership
|
|
654
|
+
mockSupabaseQuery.single.mockResolvedValue({
|
|
655
|
+
data: { id: 'membership-123' },
|
|
656
|
+
error: null
|
|
657
|
+
});
|
|
658
|
+
|
|
612
659
|
const { result } = renderHook(() => useOrganisationSecurity());
|
|
613
660
|
|
|
614
661
|
await expect(result.current.ensureOrganisationAccess('org-123')).resolves.not.toThrow();
|
|
@@ -641,9 +688,15 @@ describe('useOrganisationSecurity', () => {
|
|
|
641
688
|
});
|
|
642
689
|
|
|
643
690
|
it('validates user access', async () => {
|
|
644
|
-
//
|
|
691
|
+
// Mock the organisation membership query to return valid membership
|
|
692
|
+
mockSupabaseQuery.single.mockResolvedValue({
|
|
693
|
+
data: { id: 'membership-123' },
|
|
694
|
+
error: null
|
|
695
|
+
});
|
|
696
|
+
|
|
645
697
|
const { result } = renderHook(() => useOrganisationSecurity());
|
|
646
698
|
|
|
699
|
+
// Test self-validation (should work for regular users)
|
|
647
700
|
const isValid = await result.current.validateUserAccess('user-123', 'org-123');
|
|
648
701
|
expect(isValid).toBe(true);
|
|
649
702
|
});
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Provides utilities for validating user access to organisations and checking permissions.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { useCallback, useMemo } from 'react';
|
|
11
|
+
import { useCallback, useMemo, useEffect, useState } from 'react';
|
|
12
12
|
import { useUnifiedAuth } from '../providers';
|
|
13
13
|
import { useOrganisations } from './useOrganisations';
|
|
14
14
|
// Legacy useRBAC hook removed - use new RBAC system instead
|
|
@@ -39,22 +39,51 @@ export interface OrganisationSecurityHook {
|
|
|
39
39
|
export const useOrganisationSecurity = (): OrganisationSecurityHook => {
|
|
40
40
|
const { user, session, supabase } = useUnifiedAuth();
|
|
41
41
|
const { selectedOrganisation, getUserRole, validateOrganisationAccess: validateAccess } = useOrganisations();
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
|
|
43
|
+
// Super admin status - query database for security (user_metadata can be spoofed)
|
|
44
|
+
const [isSuperAdmin, setIsSuperAdmin] = useState<boolean>(false);
|
|
45
|
+
const [isCheckingSuperAdmin, setIsCheckingSuperAdmin] = useState(false);
|
|
46
|
+
|
|
47
|
+
// Check super admin status from database
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (!user || !session || !supabase) {
|
|
50
|
+
setIsSuperAdmin(false);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const checkSuperAdmin = async () => {
|
|
55
|
+
setIsCheckingSuperAdmin(true);
|
|
56
|
+
try {
|
|
57
|
+
const now = new Date().toISOString();
|
|
58
|
+
const { data, error } = await supabase
|
|
59
|
+
.from('rbac_global_roles')
|
|
60
|
+
.select('role')
|
|
61
|
+
.eq('user_id', user.id)
|
|
62
|
+
.eq('role', 'super_admin')
|
|
63
|
+
.lte('valid_from', now)
|
|
64
|
+
.or(`valid_to.is.null,valid_to.gte.${now}`)
|
|
65
|
+
.limit(1);
|
|
66
|
+
|
|
67
|
+
setIsSuperAdmin(!error && data && data.length > 0);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error('[useOrganisationSecurity] Error checking super admin status:', error);
|
|
70
|
+
setIsSuperAdmin(false);
|
|
71
|
+
} finally {
|
|
72
|
+
setIsCheckingSuperAdmin(false);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
checkSuperAdmin();
|
|
77
|
+
}, [user, session, supabase]);
|
|
48
78
|
|
|
49
79
|
// Super admin context
|
|
50
80
|
const superAdminContext = useMemo((): SuperAdminContext => {
|
|
51
|
-
const isSuperAdmin = globalRole === 'super_admin';
|
|
52
81
|
return {
|
|
53
82
|
isSuperAdmin,
|
|
54
83
|
hasGlobalAccess: isSuperAdmin,
|
|
55
84
|
canManageAllOrganisations: isSuperAdmin
|
|
56
85
|
};
|
|
57
|
-
}, [
|
|
86
|
+
}, [isSuperAdmin]);
|
|
58
87
|
|
|
59
88
|
// Validate organisation access with database check
|
|
60
89
|
const validateOrganisationAccess = useCallback(async (orgId: string): Promise<boolean> => {
|
|
@@ -179,7 +208,9 @@ export const useOrganisationSecurity = (): OrganisationSecurityHook => {
|
|
|
179
208
|
});
|
|
180
209
|
|
|
181
210
|
// Flatten all permissions from all pages
|
|
182
|
-
const allPermissions = Object.
|
|
211
|
+
const allPermissions = Object.entries(permissionMap)
|
|
212
|
+
.filter(([, allowed]) => allowed)
|
|
213
|
+
.map(([permission]) => permission);
|
|
183
214
|
return [...new Set(allPermissions)]; // Remove duplicates
|
|
184
215
|
} catch (error) {
|
|
185
216
|
console.error('[useOrganisationSecurity] Exception getting user permissions:', error);
|
package/src/index.ts
CHANGED
|
@@ -33,7 +33,6 @@ export { useOrganisations } from './hooks/useOrganisations';
|
|
|
33
33
|
export { useEventService } from './hooks/services/useEventService';
|
|
34
34
|
export { useOrganisationService } from './hooks/services/useOrganisationService';
|
|
35
35
|
export { useAuthService } from './hooks/services/useAuthService';
|
|
36
|
-
export { useRBACService } from './hooks/services/useRBACService';
|
|
37
36
|
export { useInactivityService } from './hooks/services/useInactivityService';
|
|
38
37
|
|
|
39
38
|
export type {
|