@jmruthers/pace-core 0.5.189 → 0.5.191
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/core-usage-manifest.json +0 -4
- package/dist/{AuthService-B-cd2MA4.d.ts → AuthService-CbP_utw2.d.ts} +7 -3
- package/dist/{DataTable-IVYljGJ6.d.ts → DataTable-Be6dH_dR.d.ts} +1 -1
- package/dist/{DataTable-GUFUNZ3N.js → DataTable-WKRZD47S.js} +8 -8
- package/dist/{PublicPageProvider-B8HaLe69.d.ts → PublicPageProvider-ULXC_u6U.d.ts} +84 -25
- package/dist/{UnifiedAuthProvider-BG0AL5eE.d.ts → UnifiedAuthProvider-BYA9qB-o.d.ts} +4 -3
- package/dist/{UnifiedAuthProvider-643PUAIM.js → UnifiedAuthProvider-FTSG5XH7.js} +4 -2
- package/dist/{api-YP7XD5L6.js → api-IHKALJZD.js} +4 -2
- package/dist/{chunk-VGZZXKBR.js → chunk-6LTQQAT6.js} +351 -157
- package/dist/chunk-6LTQQAT6.js.map +1 -0
- package/dist/{chunk-MX64ZF6I.js → chunk-6TQDD426.js} +15 -15
- package/dist/chunk-6TQDD426.js.map +1 -0
- package/dist/{chunk-YHCN776L.js → chunk-G37KK66H.js} +2 -75
- package/dist/chunk-G37KK66H.js.map +1 -0
- package/dist/{chunk-THRPYOFK.js → chunk-HW3OVDUF.js} +5 -5
- package/dist/chunk-HW3OVDUF.js.map +1 -0
- package/dist/{chunk-F2IMUDXZ.js → chunk-I7PSE6JW.js} +75 -2
- package/dist/chunk-I7PSE6JW.js.map +1 -0
- package/dist/{chunk-IM4QE42D.js → chunk-LOMZXPSN.js} +141 -326
- package/dist/chunk-LOMZXPSN.js.map +1 -0
- package/dist/chunk-OETXORNB.js +614 -0
- package/dist/chunk-OETXORNB.js.map +1 -0
- package/dist/{chunk-HESYZWZW.js → chunk-QWWZ5CAQ.js} +2 -2
- package/dist/{chunk-HEHYGYOX.js → chunk-ROXMHMY2.js} +403 -46
- package/dist/chunk-ROXMHMY2.js.map +1 -0
- package/dist/{chunk-2UUZZJFT.js → chunk-ULHIJK66.js} +228 -177
- package/dist/{chunk-2UUZZJFT.js.map → chunk-ULHIJK66.js.map} +1 -1
- package/dist/{chunk-YGPFYGA6.js → chunk-VKB2CO4Z.js} +838 -503
- package/dist/chunk-VKB2CO4Z.js.map +1 -0
- package/dist/{chunk-3GOZZZYH.js → chunk-VRGWKHDB.js} +238 -301
- package/dist/chunk-VRGWKHDB.js.map +1 -0
- package/dist/{chunk-UCQSRW7Z.js → chunk-XNYQOL3Z.js} +431 -384
- package/dist/chunk-XNYQOL3Z.js.map +1 -0
- package/dist/{chunk-DDM4CCYT.js → chunk-XYXSXPUK.js} +79 -59
- package/dist/chunk-XYXSXPUK.js.map +1 -0
- package/dist/{chunk-SAUPYVLF.js → chunk-ZSAAAMVR.js} +1 -1
- package/dist/chunk-ZSAAAMVR.js.map +1 -0
- package/dist/components.d.ts +5 -6
- package/dist/components.js +19 -19
- package/dist/components.js.map +1 -1
- package/dist/{database.generated-DI89OQeI.d.ts → database.generated-CzIvgcPu.d.ts} +165 -201
- package/dist/eslint-rules/pace-core-compliance.cjs +0 -2
- package/dist/{file-reference-D037xOFK.d.ts → file-reference-BavO2eQj.d.ts} +13 -10
- package/dist/hooks.d.ts +20 -15
- package/dist/hooks.js +14 -8
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +17 -15
- package/dist/index.js +86 -81
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +3 -3
- package/dist/providers.js +3 -1
- package/dist/rbac/index.d.ts +77 -13
- package/dist/rbac/index.js +12 -9
- package/dist/{types-Bwgl--Xo.d.ts → types-CEpcvwwF.d.ts} +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/types.js +1 -1
- package/dist/{usePublicRouteParams-CTDELQ7H.d.ts → usePublicRouteParams-TZe0gy-4.d.ts} +17 -10
- package/dist/utils.d.ts +8 -8
- package/dist/utils.js +16 -16
- package/docs/README.md +2 -2
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +2 -2
- package/docs/api/classes/Logger.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +2 -2
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +2 -2
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +5 -5
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +2 -2
- package/docs/api/classes/SecureSupabaseClient.md +25 -20
- package/docs/api/classes/StorageUtils.md +7 -4
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/enums/LogLevel.md +1 -1
- package/docs/api/enums/RBACErrorCode.md +1 -1
- package/docs/api/enums/RPCFunction.md +1 -1
- package/docs/api/interfaces/AddressFieldProps.md +1 -1
- package/docs/api/interfaces/AddressFieldRef.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/AutocompleteOptions.md +1 -1
- package/docs/api/interfaces/AvatarProps.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CalendarProps.md +20 -6
- 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/ComplianceResult.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +9 -9
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +62 -16
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +2 -2
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +26 -12
- package/docs/api/interfaces/FileUploadProps.md +30 -19
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/FormFieldProps.md +1 -1
- package/docs/api/interfaces/FormProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.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/LoggerConfig.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +10 -10
- 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 +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +8 -8
- package/docs/api/interfaces/PagePermissionContextType.md +8 -8
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +7 -7
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/ParsedAddress.md +2 -2
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProgressProps.md +3 -11
- package/docs/api/interfaces/ProtectedRouteProps.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/QuickFix.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
- package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
- package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +2 -2
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
- package/docs/api/interfaces/RBACResult.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
- package/docs/api/interfaces/ResourcePermissions.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
- package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +10 -10
- package/docs/api/interfaces/RouteConfig.md +10 -10
- package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +9 -9
- package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
- package/docs/api/interfaces/SetupIssue.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +4 -4
- package/docs/api/interfaces/StorageFileInfo.md +7 -7
- package/docs/api/interfaces/StorageFileMetadata.md +25 -14
- package/docs/api/interfaces/StorageListOptions.md +22 -9
- package/docs/api/interfaces/StorageListResult.md +4 -4
- package/docs/api/interfaces/StorageUploadOptions.md +21 -8
- package/docs/api/interfaces/StorageUploadResult.md +6 -6
- package/docs/api/interfaces/StorageUrlOptions.md +19 -6
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/TabsContentProps.md +1 -1
- package/docs/api/interfaces/TabsListProps.md +1 -1
- package/docs/api/interfaces/TabsProps.md +1 -1
- package/docs/api/interfaces/TabsTriggerProps.md +1 -1
- package/docs/api/interfaces/TextareaProps.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 +53 -53
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
- package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
- 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/UsePublicFileDisplayOptions.md +2 -2
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +5 -5
- package/docs/api/interfaces/UseResolvedScopeReturn.md +4 -4
- package/docs/api/interfaces/UseResourcePermissionsOptions.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 +165 -106
- package/docs/api-reference/components.md +15 -7
- package/docs/api-reference/providers.md +2 -2
- package/docs/api-reference/rpc-functions.md +1 -0
- package/docs/best-practices/README.md +1 -1
- package/docs/best-practices/deployment.md +8 -8
- package/docs/getting-started/examples/README.md +2 -2
- package/docs/getting-started/installation-guide.md +4 -4
- package/docs/getting-started/quick-start.md +3 -3
- package/docs/migration/MIGRATION_GUIDE.md +3 -3
- package/docs/migration/README.md +18 -0
- package/docs/migration/database-changes-december-2025.md +767 -0
- package/docs/migration/person-scoped-profiles-migration-guide.md +472 -0
- package/docs/rbac/compliance/compliance-guide.md +2 -2
- package/docs/rbac/event-based-apps.md +2 -2
- package/docs/rbac/getting-started.md +2 -2
- package/docs/rbac/quick-start.md +2 -2
- package/docs/security/README.md +4 -4
- package/docs/standards/07-rbac-and-rls-standard.md +430 -7
- package/docs/troubleshooting/README.md +2 -2
- package/docs/troubleshooting/migration.md +3 -3
- package/package.json +1 -3
- package/scripts/check-pace-core-compliance.cjs +1 -1
- package/scripts/check-pace-core-compliance.js +1 -1
- package/src/__tests__/fixtures/supabase.ts +301 -0
- package/src/__tests__/public-recipe-view.test.ts +19 -19
- package/src/__tests__/rls-policies.test.ts +210 -74
- package/src/components/AddressField/AddressField.test.tsx +42 -0
- package/src/components/AddressField/AddressField.tsx +71 -60
- package/src/components/AddressField/README.md +7 -6
- package/src/components/Alert/Alert.test.tsx +50 -10
- package/src/components/Alert/Alert.tsx +5 -3
- package/src/components/Avatar/Avatar.test.tsx +95 -43
- package/src/components/Avatar/Avatar.tsx +16 -16
- package/src/components/Button/Button.test.tsx +2 -1
- package/src/components/Button/Button.tsx +3 -3
- package/src/components/Calendar/Calendar.test.tsx +53 -37
- package/src/components/Calendar/Calendar.tsx +409 -82
- package/src/components/Card/Card.test.tsx +7 -4
- package/src/components/Card/Card.tsx +3 -6
- package/src/components/Checkbox/Checkbox.tsx +2 -2
- package/src/components/DataTable/components/ActionButtons.tsx +5 -5
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
- package/src/components/DataTable/components/ColumnFilter.tsx +1 -1
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +3 -3
- package/src/components/DataTable/components/DataTableBody.tsx +12 -12
- package/src/components/DataTable/components/DataTableCore.tsx +3 -3
- package/src/components/DataTable/components/DataTableToolbar.tsx +5 -5
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +3 -3
- package/src/components/DataTable/components/EditableRow.tsx +2 -2
- package/src/components/DataTable/components/EmptyState.tsx +3 -3
- package/src/components/DataTable/components/GroupHeader.tsx +2 -2
- package/src/components/DataTable/components/GroupingDropdown.tsx +1 -1
- package/src/components/DataTable/components/ImportModal.tsx +4 -4
- package/src/components/DataTable/components/LoadingState.tsx +1 -1
- package/src/components/DataTable/components/PaginationControls.tsx +11 -11
- package/src/components/DataTable/components/UnifiedTableBody.tsx +9 -9
- package/src/components/DataTable/components/ViewRowModal.tsx +2 -2
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +11 -37
- package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +157 -0
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +2 -1
- package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +128 -0
- package/src/components/DataTable/core/__tests__/ActionManager.test.ts +19 -0
- package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +51 -0
- package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +84 -0
- package/src/components/DataTable/core/__tests__/DataManager.test.ts +14 -0
- package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +136 -0
- package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +16 -0
- package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +18 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +28 -7
- package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +30 -1
- package/src/components/DataTable/utils/hierarchicalUtils.ts +38 -10
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -3
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +4 -4
- package/src/components/Dialog/Dialog.tsx +2 -2
- package/src/components/EventSelector/EventSelector.tsx +7 -7
- package/src/components/FileDisplay/FileDisplay.tsx +291 -179
- package/src/components/FileUpload/FileUpload.tsx +7 -4
- package/src/components/Header/Header.test.tsx +28 -0
- package/src/components/Header/Header.tsx +22 -9
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +2 -2
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +19 -14
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +5 -5
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +127 -1
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +42 -22
- package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +4 -0
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +3 -0
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +3 -0
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +16 -6
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +37 -3
- package/src/components/PaceAppLayout/test-setup.tsx +1 -0
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +66 -45
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +6 -4
- package/src/components/Progress/Progress.test.tsx +18 -19
- package/src/components/Progress/Progress.tsx +31 -32
- package/src/components/PublicLayout/PublicLayout.test.tsx +6 -6
- package/src/components/PublicLayout/PublicPageProvider.tsx +5 -3
- package/src/components/Select/Select.test.tsx +4 -1
- package/src/components/Select/Select.tsx +65 -20
- package/src/components/Switch/Switch.test.tsx +2 -1
- package/src/components/Switch/Switch.tsx +1 -1
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/components/Tooltip/Tooltip.test.tsx +8 -2
- package/src/components/UserMenu/UserMenu.tsx +3 -3
- package/src/eslint-rules/pace-core-compliance.cjs +0 -2
- package/src/eslint-rules/pace-core-compliance.js +0 -2
- package/src/hooks/__tests__/hooks.integration.test.tsx +4 -1
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +76 -5
- package/src/hooks/__tests__/useDataTableState.test.ts +76 -0
- package/src/hooks/__tests__/useFileUrl.unit.test.ts +25 -69
- package/src/hooks/__tests__/useFileUrlCache.test.ts +129 -0
- package/src/hooks/__tests__/usePreventTabReload.test.ts +88 -0
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +1 -1
- package/src/hooks/__tests__/usePublicEvent.test.ts +608 -0
- package/src/hooks/__tests__/useQueryCache.test.ts +144 -0
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +67 -24
- package/src/hooks/index.ts +1 -1
- package/src/hooks/public/usePublicEvent.ts +10 -10
- package/src/hooks/public/usePublicFileDisplay.ts +173 -87
- package/src/hooks/useAppConfig.ts +24 -5
- package/src/hooks/useFileDisplay.ts +298 -36
- package/src/hooks/useFileReference.ts +56 -11
- package/src/hooks/useFileUrl.ts +1 -1
- package/src/hooks/useInactivityTracker.ts +16 -7
- package/src/hooks/usePermissionCache.test.ts +85 -8
- package/src/hooks/useQueryCache.ts +27 -6
- package/src/hooks/useSecureDataAccess.test.ts +87 -42
- package/src/hooks/useSecureDataAccess.ts +95 -48
- package/src/providers/__tests__/OrganisationProvider.test.tsx +27 -21
- package/src/providers/services/EventServiceProvider.tsx +37 -17
- package/src/providers/services/InactivityServiceProvider.tsx +4 -4
- package/src/providers/services/OrganisationServiceProvider.tsx +8 -1
- package/src/providers/services/UnifiedAuthProvider.tsx +115 -29
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +451 -0
- package/src/rbac/__tests__/engine.comprehensive.test.ts +12 -0
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +8 -0
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +4 -0
- package/src/rbac/api.ts +240 -36
- package/src/rbac/cache-invalidation.ts +21 -7
- package/src/rbac/compliance/quick-fix-suggestions.ts +1 -1
- package/src/rbac/components/NavigationGuard.tsx +23 -63
- package/src/rbac/components/NavigationProvider.test.tsx +52 -23
- package/src/rbac/components/NavigationProvider.tsx +13 -11
- package/src/rbac/components/PagePermissionGuard.tsx +77 -203
- package/src/rbac/components/PagePermissionProvider.tsx +13 -11
- package/src/rbac/components/PermissionEnforcer.tsx +24 -62
- package/src/rbac/components/RoleBasedRouter.tsx +14 -12
- package/src/rbac/components/SecureDataProvider.tsx +13 -11
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +104 -41
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +49 -12
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +22 -1
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +161 -82
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +22 -1
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +77 -30
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +39 -5
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +47 -4
- package/src/rbac/engine.ts +4 -2
- package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +144 -52
- package/src/rbac/hooks/index.ts +3 -0
- package/src/rbac/hooks/useCan.test.ts +101 -53
- package/src/rbac/hooks/usePermissions.ts +108 -41
- package/src/rbac/hooks/useRBAC.test.ts +11 -3
- package/src/rbac/hooks/useRBAC.ts +83 -40
- package/src/rbac/hooks/useResolvedScope.test.ts +189 -63
- package/src/rbac/hooks/useResolvedScope.ts +128 -70
- package/src/rbac/hooks/useSecureSupabase.ts +36 -19
- package/src/rbac/hooks/useSuperAdminBypass.ts +126 -0
- package/src/rbac/request-deduplication.ts +1 -1
- package/src/rbac/secureClient.ts +72 -12
- package/src/rbac/security.ts +29 -23
- package/src/rbac/types.ts +10 -0
- package/src/rbac/utils/__tests__/contextValidator.test.ts +150 -0
- package/src/rbac/utils/__tests__/deep-equal.test.ts +53 -0
- package/src/rbac/utils/__tests__/eventContext.test.ts +8 -3
- package/src/rbac/utils/__tests__/eventContext.unit.test.ts +74 -12
- package/src/rbac/utils/contextValidator.ts +288 -0
- package/src/rbac/utils/eventContext.ts +52 -3
- package/src/services/AuthService.ts +37 -8
- package/src/services/EventService.ts +165 -21
- package/src/services/OrganisationService.ts +125 -137
- package/src/services/__tests__/EventService.test.ts +26 -21
- package/src/services/__tests__/OrganisationService.pagination.test.ts +34 -8
- package/src/services/__tests__/OrganisationService.test.ts +218 -86
- package/src/types/database.generated.ts +166 -201
- package/src/types/file-reference.ts +13 -10
- package/src/types/supabase.ts +2 -2
- package/src/utils/__tests__/secureDataAccess.unit.test.ts +3 -2
- package/src/utils/app/appNameResolver.test.ts +346 -73
- package/src/utils/context/superAdminOverride.ts +58 -0
- package/src/utils/file-reference/index.ts +65 -37
- package/src/utils/google-places/googlePlacesUtils.test.ts +98 -0
- package/src/utils/google-places/googlePlacesUtils.ts +1 -1
- package/src/utils/google-places/loadGoogleMapsScript.test.ts +83 -0
- package/src/utils/google-places/types.ts +1 -1
- package/src/utils/request-deduplication.ts +4 -4
- package/src/utils/security/secureDataAccess.test.ts +1 -1
- package/src/utils/security/secureDataAccess.ts +7 -4
- package/src/utils/storage/README.md +1 -1
- package/src/utils/storage/helpers.test.ts +1 -1
- package/src/utils/storage/helpers.ts +38 -19
- package/src/utils/storage/types.ts +15 -8
- package/src/utils/validation/__tests__/csrf.test.ts +105 -0
- package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +92 -0
- package/src/vite-env.d.ts +2 -2
- package/dist/chunk-3GOZZZYH.js.map +0 -1
- package/dist/chunk-DDM4CCYT.js.map +0 -1
- package/dist/chunk-E7UAOUMY.js +0 -75
- package/dist/chunk-E7UAOUMY.js.map +0 -1
- package/dist/chunk-F2IMUDXZ.js.map +0 -1
- package/dist/chunk-HEHYGYOX.js.map +0 -1
- package/dist/chunk-IM4QE42D.js.map +0 -1
- package/dist/chunk-MX64ZF6I.js.map +0 -1
- package/dist/chunk-SAUPYVLF.js.map +0 -1
- package/dist/chunk-THRPYOFK.js.map +0 -1
- package/dist/chunk-UCQSRW7Z.js.map +0 -1
- package/dist/chunk-VGZZXKBR.js.map +0 -1
- package/dist/chunk-YGPFYGA6.js.map +0 -1
- package/dist/chunk-YHCN776L.js.map +0 -1
- /package/dist/{DataTable-GUFUNZ3N.js.map → DataTable-WKRZD47S.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-643PUAIM.js.map → UnifiedAuthProvider-FTSG5XH7.js.map} +0 -0
- /package/dist/{api-YP7XD5L6.js.map → api-IHKALJZD.js.map} +0 -0
- /package/dist/{chunk-HESYZWZW.js.map → chunk-QWWZ5CAQ.js.map} +0 -0
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* Comprehensive tests for the usePermissionCache hook covering all critical functionality.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { renderHook,
|
|
11
|
-
import { vi, describe, it, expect, beforeEach
|
|
10
|
+
import { renderHook, act } from '@testing-library/react';
|
|
11
|
+
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
|
12
12
|
import { usePermissionCache } from './usePermissionCache';
|
|
13
13
|
|
|
14
14
|
// Mock the dependencies
|
|
@@ -29,6 +29,16 @@ vi.mock('../rbac/api', () => ({
|
|
|
29
29
|
isPermittedCached: vi.fn()
|
|
30
30
|
}));
|
|
31
31
|
|
|
32
|
+
const loggerSpy = vi.hoisted(() => ({
|
|
33
|
+
warn: vi.fn(),
|
|
34
|
+
error: vi.fn()
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
vi.mock('../utils/core/logger', () => ({
|
|
38
|
+
logger: loggerSpy,
|
|
39
|
+
createLogger: () => loggerSpy
|
|
40
|
+
}));
|
|
41
|
+
|
|
32
42
|
import { useUnifiedAuth } from '../providers/services/UnifiedAuthProvider';
|
|
33
43
|
import { useOrganisations } from './useOrganisations';
|
|
34
44
|
import { useEvents } from './useEvents';
|
|
@@ -41,7 +51,7 @@ const mockIsPermittedCached = vi.mocked(isPermittedCached);
|
|
|
41
51
|
describe('usePermissionCache', () => {
|
|
42
52
|
beforeEach(() => {
|
|
43
53
|
vi.clearAllMocks();
|
|
44
|
-
|
|
54
|
+
|
|
45
55
|
// Setup default mocks
|
|
46
56
|
mockUseUnifiedAuthFn.mockReturnValue({
|
|
47
57
|
user: { id: 'user-123' },
|
|
@@ -50,19 +60,19 @@ describe('usePermissionCache', () => {
|
|
|
50
60
|
selectedOrganisationId: 'org-123',
|
|
51
61
|
selectedEventId: undefined
|
|
52
62
|
} as any);
|
|
53
|
-
|
|
63
|
+
|
|
54
64
|
mockUseOrganisations.mockReturnValue({
|
|
55
65
|
selectedOrganisation: { id: 'org-123' }
|
|
56
66
|
} as any);
|
|
57
|
-
|
|
67
|
+
|
|
58
68
|
mockUseEvents.mockReturnValue({
|
|
59
69
|
selectedEvent: null
|
|
60
70
|
} as any);
|
|
61
|
-
|
|
71
|
+
|
|
62
72
|
mockIsPermittedCached.mockResolvedValue(true);
|
|
63
73
|
});
|
|
64
74
|
|
|
65
|
-
|
|
75
|
+
describe('Hook Initialization', () => {
|
|
66
76
|
it('initializes with default configuration', () => {
|
|
67
77
|
const { result } = renderHook(() => usePermissionCache());
|
|
68
78
|
|
|
@@ -130,6 +140,16 @@ describe('usePermissionCache', () => {
|
|
|
130
140
|
expect(results.every(r => r.hasPermission)).toBe(true);
|
|
131
141
|
});
|
|
132
142
|
|
|
143
|
+
it('handles empty permission arrays gracefully', async () => {
|
|
144
|
+
const { result } = renderHook(() => usePermissionCache());
|
|
145
|
+
|
|
146
|
+
const results = await result.current.checkMultiplePermissions([]);
|
|
147
|
+
|
|
148
|
+
expect(results).toEqual([]);
|
|
149
|
+
expect(mockIsPermittedCached).not.toHaveBeenCalled();
|
|
150
|
+
expect(loggerSpy.warn).toHaveBeenCalled();
|
|
151
|
+
});
|
|
152
|
+
|
|
133
153
|
it('handles permission check errors gracefully', async () => {
|
|
134
154
|
mockIsPermittedCached.mockRejectedValueOnce(new Error('Permission check failed'));
|
|
135
155
|
|
|
@@ -137,6 +157,7 @@ describe('usePermissionCache', () => {
|
|
|
137
157
|
|
|
138
158
|
const hasPermission = await result.current.checkPermission('read', 'users');
|
|
139
159
|
expect(hasPermission).toBe(false);
|
|
160
|
+
expect(loggerSpy.error).toHaveBeenCalled();
|
|
140
161
|
});
|
|
141
162
|
|
|
142
163
|
it('handles missing user context gracefully', () => {
|
|
@@ -153,6 +174,23 @@ describe('usePermissionCache', () => {
|
|
|
153
174
|
expect(result.current).toBeDefined();
|
|
154
175
|
expect(result.current.checkPermission).toBeInstanceOf(Function);
|
|
155
176
|
});
|
|
177
|
+
|
|
178
|
+
it('deduplicates concurrent permission checks', async () => {
|
|
179
|
+
mockIsPermittedCached.mockImplementation(
|
|
180
|
+
() => new Promise(resolve => setTimeout(() => resolve(true), 50))
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
const { result } = renderHook(() => usePermissionCache());
|
|
184
|
+
|
|
185
|
+
const [first, second] = await Promise.all([
|
|
186
|
+
result.current.checkPermission('read', 'users'),
|
|
187
|
+
result.current.checkPermission('read', 'users')
|
|
188
|
+
]);
|
|
189
|
+
|
|
190
|
+
expect(first).toBe(true);
|
|
191
|
+
expect(second).toBe(true);
|
|
192
|
+
expect(mockIsPermittedCached).toHaveBeenCalledTimes(1);
|
|
193
|
+
});
|
|
156
194
|
});
|
|
157
195
|
|
|
158
196
|
describe('Cache Management', () => {
|
|
@@ -196,10 +234,23 @@ describe('usePermissionCache', () => {
|
|
|
196
234
|
|
|
197
235
|
// Check again - should not use cache
|
|
198
236
|
await result.current.checkPermission('read', 'users');
|
|
199
|
-
|
|
237
|
+
|
|
200
238
|
expect(mockIsPermittedCached).toHaveBeenCalledTimes(2);
|
|
201
239
|
});
|
|
202
240
|
|
|
241
|
+
it('invalidates cache entries by pattern', async () => {
|
|
242
|
+
const { result } = renderHook(() => usePermissionCache());
|
|
243
|
+
|
|
244
|
+
await result.current.checkPermission('read', 'dashboard');
|
|
245
|
+
await result.current.checkPermission('read', 'admin');
|
|
246
|
+
|
|
247
|
+
result.current.invalidateCache('dashboard');
|
|
248
|
+
|
|
249
|
+
await result.current.checkPermission('read', 'dashboard');
|
|
250
|
+
|
|
251
|
+
expect(mockIsPermittedCached).toHaveBeenCalledTimes(3);
|
|
252
|
+
});
|
|
253
|
+
|
|
203
254
|
it('respects max cache size', async () => {
|
|
204
255
|
const { result } = renderHook(() => usePermissionCache({
|
|
205
256
|
maxCacheSize: 2
|
|
@@ -246,6 +297,32 @@ describe('usePermissionCache', () => {
|
|
|
246
297
|
expect(debugInfo.totalChecks).toBe(2);
|
|
247
298
|
});
|
|
248
299
|
|
|
300
|
+
it('returns cached permissions for a page', async () => {
|
|
301
|
+
mockIsPermittedCached
|
|
302
|
+
.mockResolvedValueOnce(true)
|
|
303
|
+
.mockResolvedValueOnce(false)
|
|
304
|
+
.mockResolvedValueOnce(true)
|
|
305
|
+
.mockResolvedValueOnce(false);
|
|
306
|
+
|
|
307
|
+
const { result } = renderHook(() => usePermissionCache());
|
|
308
|
+
|
|
309
|
+
await result.current.checkMultiplePermissions([
|
|
310
|
+
['read', 'dashboard'],
|
|
311
|
+
['create', 'dashboard'],
|
|
312
|
+
['update', 'dashboard'],
|
|
313
|
+
['delete', 'dashboard']
|
|
314
|
+
]);
|
|
315
|
+
|
|
316
|
+
const cachedPermissions = result.current.getCachedPermissions('dashboard');
|
|
317
|
+
|
|
318
|
+
expect(cachedPermissions).toEqual([
|
|
319
|
+
{ operation: 'read', hasPermission: true },
|
|
320
|
+
{ operation: 'create', hasPermission: false },
|
|
321
|
+
{ operation: 'update', hasPermission: true },
|
|
322
|
+
{ operation: 'delete', hasPermission: false }
|
|
323
|
+
]);
|
|
324
|
+
});
|
|
325
|
+
|
|
249
326
|
it('tracks average response time', async () => {
|
|
250
327
|
const { result } = renderHook(() => usePermissionCache());
|
|
251
328
|
|
|
@@ -51,6 +51,27 @@ function runCacheCleanup() {
|
|
|
51
51
|
if (typeof window !== 'undefined' && !cleanupTimer) {
|
|
52
52
|
cleanupTimer = setInterval(runCacheCleanup, CLEANUP_INTERVAL_MS);
|
|
53
53
|
log.debug('Query cache cleanup initialized.');
|
|
54
|
+
|
|
55
|
+
// Cleanup on page unload to prevent memory leaks
|
|
56
|
+
window.addEventListener('beforeunload', () => {
|
|
57
|
+
if (cleanupTimer) {
|
|
58
|
+
clearInterval(cleanupTimer);
|
|
59
|
+
cleanupTimer = null;
|
|
60
|
+
log.debug('Query cache cleanup timer cleared on page unload.');
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Manually cleanup the query cache cleanup timer
|
|
67
|
+
* Useful for testing or when the module is reloaded
|
|
68
|
+
*/
|
|
69
|
+
export function cleanupQueryCache(): void {
|
|
70
|
+
if (cleanupTimer) {
|
|
71
|
+
clearInterval(cleanupTimer);
|
|
72
|
+
cleanupTimer = null;
|
|
73
|
+
log.debug('Query cache cleanup timer cleared.');
|
|
74
|
+
}
|
|
54
75
|
}
|
|
55
76
|
|
|
56
77
|
export interface UseQueryCacheOptions {
|
|
@@ -91,12 +112,12 @@ export interface UseQueryCacheReturn {
|
|
|
91
112
|
* const { getCachedQuery } = useQueryCache(supabase);
|
|
92
113
|
*
|
|
93
114
|
* const person = await getCachedQuery(
|
|
94
|
-
* '
|
|
115
|
+
* 'core_person',
|
|
95
116
|
* 'user_id',
|
|
96
117
|
* userId,
|
|
97
118
|
* async () => {
|
|
98
119
|
* const { data } = await supabase
|
|
99
|
-
* .from('
|
|
120
|
+
* .from('core_person')
|
|
100
121
|
* .select('id, first_name, last_name, email')
|
|
101
122
|
* .eq('user_id', userId)
|
|
102
123
|
* .single();
|
|
@@ -199,7 +220,7 @@ export function useQueryCache(supabase?: SupabaseClient<Database>): UseQueryCach
|
|
|
199
220
|
*/
|
|
200
221
|
export const queryCacheHelpers = {
|
|
201
222
|
/**
|
|
202
|
-
* Cache
|
|
223
|
+
* Cache core_person queries by user_id
|
|
203
224
|
* TTL: 5 minutes
|
|
204
225
|
*/
|
|
205
226
|
pacePersonByUserId: <T>(
|
|
@@ -207,7 +228,7 @@ export const queryCacheHelpers = {
|
|
|
207
228
|
userId: string,
|
|
208
229
|
fetchFn: () => Promise<T>
|
|
209
230
|
): Promise<T> => {
|
|
210
|
-
const cacheKey = `
|
|
231
|
+
const cacheKey = `core_person:user_id:${userId}`;
|
|
211
232
|
const now = Date.now();
|
|
212
233
|
const ttl = 300 * 1000; // 5 minutes
|
|
213
234
|
|
|
@@ -237,7 +258,7 @@ export const queryCacheHelpers = {
|
|
|
237
258
|
},
|
|
238
259
|
|
|
239
260
|
/**
|
|
240
|
-
* Cache
|
|
261
|
+
* Cache core_member queries by person_id
|
|
241
262
|
* TTL: 5 minutes
|
|
242
263
|
*/
|
|
243
264
|
paceMemberByPersonId: <T>(
|
|
@@ -245,7 +266,7 @@ export const queryCacheHelpers = {
|
|
|
245
266
|
personId: string,
|
|
246
267
|
fetchFn: () => Promise<T>
|
|
247
268
|
): Promise<T> => {
|
|
248
|
-
const cacheKey = `
|
|
269
|
+
const cacheKey = `core_member:person_id:${personId}`;
|
|
249
270
|
const now = Date.now();
|
|
250
271
|
const ttl = 300 * 1000; // 5 minutes
|
|
251
272
|
|
|
@@ -14,6 +14,8 @@ import { useUnifiedAuth } from '../providers';
|
|
|
14
14
|
import { useOrganisations } from './useOrganisations';
|
|
15
15
|
import { setOrganisationContext } from '../utils/context/organisationContext';
|
|
16
16
|
import { createMockSupabaseClient, createMockQueryBuilderWithData } from '../__tests__/helpers/supabaseMock';
|
|
17
|
+
import { useResolvedScope } from '../rbac/hooks/useResolvedScope';
|
|
18
|
+
import { useSuperAdminBypass } from '../rbac/hooks/useSuperAdminBypass';
|
|
17
19
|
|
|
18
20
|
// Mock the providers
|
|
19
21
|
vi.mock('../providers', () => ({
|
|
@@ -29,11 +31,23 @@ vi.mock('../utils/context/organisationContext', () => ({
|
|
|
29
31
|
setOrganisationContext: vi.fn()
|
|
30
32
|
}));
|
|
31
33
|
|
|
34
|
+
// Mock useResolvedScope
|
|
35
|
+
vi.mock('../rbac/hooks/useResolvedScope', () => ({
|
|
36
|
+
useResolvedScope: vi.fn()
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
// Mock useSuperAdminBypass
|
|
40
|
+
vi.mock('../rbac/hooks/useSuperAdminBypass', () => ({
|
|
41
|
+
useSuperAdminBypass: vi.fn()
|
|
42
|
+
}));
|
|
43
|
+
|
|
32
44
|
|
|
33
45
|
describe('useSecureDataAccess', () => {
|
|
34
46
|
const mockUseUnifiedAuth = vi.mocked(useUnifiedAuth);
|
|
35
47
|
const mockUseOrganisations = vi.mocked(useOrganisations);
|
|
36
48
|
const mockSetOrganisationContext = vi.mocked(setOrganisationContext);
|
|
49
|
+
const mockUseResolvedScope = vi.mocked(useResolvedScope);
|
|
50
|
+
const mockUseSuperAdminBypass = vi.mocked(useSuperAdminBypass);
|
|
37
51
|
|
|
38
52
|
const mockUser = {
|
|
39
53
|
id: 'user-123',
|
|
@@ -93,6 +107,22 @@ describe('useSecureDataAccess', () => {
|
|
|
93
107
|
ensureOrganisationContext: vi.fn().mockReturnValue(mockSelectedOrganisation),
|
|
94
108
|
// Add other required properties
|
|
95
109
|
} as any);
|
|
110
|
+
|
|
111
|
+
// Mock useResolvedScope to return resolved scope with organisationId
|
|
112
|
+
mockUseResolvedScope.mockReturnValue({
|
|
113
|
+
resolvedScope: {
|
|
114
|
+
organisationId: 'org-123',
|
|
115
|
+
eventId: undefined,
|
|
116
|
+
appId: undefined,
|
|
117
|
+
},
|
|
118
|
+
isLoading: false,
|
|
119
|
+
error: null,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Mock useSuperAdminBypass to return not super admin
|
|
123
|
+
mockUseSuperAdminBypass.mockReturnValue({
|
|
124
|
+
isSuperAdmin: false,
|
|
125
|
+
});
|
|
96
126
|
});
|
|
97
127
|
|
|
98
128
|
describe('Hook Initialization', () => {
|
|
@@ -113,9 +143,10 @@ describe('useSecureDataAccess', () => {
|
|
|
113
143
|
expect(mockUseUnifiedAuth).toHaveBeenCalled();
|
|
114
144
|
});
|
|
115
145
|
|
|
116
|
-
it('depends on
|
|
146
|
+
it('depends on useResolvedScope hook', () => {
|
|
117
147
|
renderHook(() => useSecureDataAccess());
|
|
118
|
-
|
|
148
|
+
// useSecureDataAccess now uses useResolvedScope instead of useOrganisations
|
|
149
|
+
expect(mockUseResolvedScope).toHaveBeenCalled();
|
|
119
150
|
});
|
|
120
151
|
});
|
|
121
152
|
|
|
@@ -123,9 +154,9 @@ describe('useSecureDataAccess', () => {
|
|
|
123
154
|
it('executes secure query with organisation filtering', async () => {
|
|
124
155
|
const { result } = renderHook(() => useSecureDataAccess());
|
|
125
156
|
|
|
126
|
-
const data = await result.current.secureQuery('
|
|
157
|
+
const data = await result.current.secureQuery('core_events', '*', { active: true });
|
|
127
158
|
|
|
128
|
-
expect(freshMockSupabase.from).toHaveBeenCalledWith('
|
|
159
|
+
expect(freshMockSupabase.from).toHaveBeenCalledWith('core_events');
|
|
129
160
|
expect(freshMockQueryBuilder.select).toHaveBeenCalledWith('*');
|
|
130
161
|
expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('organisation_id', 'org-123');
|
|
131
162
|
expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('active', true);
|
|
@@ -239,9 +270,9 @@ describe('useSecureDataAccess', () => {
|
|
|
239
270
|
it('executes secure update with organisation filtering', async () => {
|
|
240
271
|
const { result } = renderHook(() => useSecureDataAccess());
|
|
241
272
|
|
|
242
|
-
const data = await result.current.secureUpdate('
|
|
273
|
+
const data = await result.current.secureUpdate('core_events', { event_name: 'Updated Event' }, { id: 'event-123' });
|
|
243
274
|
|
|
244
|
-
expect(freshMockSupabase.from).toHaveBeenCalledWith('
|
|
275
|
+
expect(freshMockSupabase.from).toHaveBeenCalledWith('core_events');
|
|
245
276
|
expect(freshMockQueryBuilder.update).toHaveBeenCalledWith({ event_name: 'Updated Event' });
|
|
246
277
|
expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('id', 'event-123');
|
|
247
278
|
expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('organisation_id', 'org-123');
|
|
@@ -276,9 +307,9 @@ describe('useSecureDataAccess', () => {
|
|
|
276
307
|
it('executes secure delete with organisation filtering', async () => {
|
|
277
308
|
const { result } = renderHook(() => useSecureDataAccess());
|
|
278
309
|
|
|
279
|
-
await result.current.secureDelete('
|
|
310
|
+
await result.current.secureDelete('core_events', { id: 'event-123' });
|
|
280
311
|
|
|
281
|
-
expect(freshMockSupabase.from).toHaveBeenCalledWith('
|
|
312
|
+
expect(freshMockSupabase.from).toHaveBeenCalledWith('core_events');
|
|
282
313
|
expect(freshMockQueryBuilder.delete).toHaveBeenCalled();
|
|
283
314
|
expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('id', 'event-123');
|
|
284
315
|
expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('organisation_id', 'org-123');
|
|
@@ -328,15 +359,22 @@ describe('useSecureDataAccess', () => {
|
|
|
328
359
|
});
|
|
329
360
|
|
|
330
361
|
it('handles missing organisation context', () => {
|
|
331
|
-
|
|
362
|
+
mockUseUnifiedAuth.mockReturnValue({
|
|
363
|
+
user: mockUser,
|
|
364
|
+
session: mockSession,
|
|
365
|
+
supabase: freshMockSupabase,
|
|
366
|
+
isAuthenticated: true,
|
|
367
|
+
signOut: vi.fn(),
|
|
332
368
|
selectedOrganisation: null,
|
|
333
|
-
|
|
334
|
-
validateOrganisationAccess: vi.fn().mockResolvedValue(false),
|
|
335
|
-
ensureOrganisationContext: vi.fn().mockImplementation(() => {
|
|
336
|
-
throw new Error('Organisation context is required but not available');
|
|
337
|
-
}),
|
|
338
|
-
// Add other required properties
|
|
369
|
+
selectedEvent: null,
|
|
339
370
|
} as any);
|
|
371
|
+
|
|
372
|
+
// Mock useResolvedScope to return null scope when no context available
|
|
373
|
+
mockUseResolvedScope.mockReturnValue({
|
|
374
|
+
resolvedScope: null,
|
|
375
|
+
isLoading: false,
|
|
376
|
+
error: null,
|
|
377
|
+
});
|
|
340
378
|
|
|
341
379
|
const { result } = renderHook(() => useSecureDataAccess());
|
|
342
380
|
|
|
@@ -344,21 +382,15 @@ describe('useSecureDataAccess', () => {
|
|
|
344
382
|
});
|
|
345
383
|
|
|
346
384
|
it('validates organisation access before operations', async () => {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
selectedOrganisation: mockSelectedOrganisation,
|
|
350
|
-
getUserRole: vi.fn().mockReturnValue('member'),
|
|
351
|
-
validateOrganisationAccess: mockValidateAccess,
|
|
352
|
-
ensureOrganisationContext: vi.fn().mockReturnValue(mockSelectedOrganisation),
|
|
353
|
-
// Add other required properties
|
|
354
|
-
} as any);
|
|
355
|
-
|
|
385
|
+
// The hook uses useResolvedScope to get organisationId, which is already mocked in beforeEach
|
|
386
|
+
// The validation happens in validateContext which checks resolvedScope.organisationId
|
|
356
387
|
const { result } = renderHook(() => useSecureDataAccess());
|
|
357
388
|
|
|
358
|
-
|
|
389
|
+
// Use 'event' table which is in the tablesWithOrganisation list
|
|
390
|
+
await result.current.secureQuery('core_events', '*');
|
|
359
391
|
|
|
360
|
-
//
|
|
361
|
-
expect(
|
|
392
|
+
// Verify that the query was executed with organisation filter
|
|
393
|
+
expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('organisation_id', 'org-123');
|
|
362
394
|
});
|
|
363
395
|
});
|
|
364
396
|
|
|
@@ -394,16 +426,16 @@ describe('useSecureDataAccess', () => {
|
|
|
394
426
|
});
|
|
395
427
|
|
|
396
428
|
it('handles organisation access validation failures', async () => {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
429
|
+
// Mock useResolvedScope to return null scope to simulate missing context
|
|
430
|
+
mockUseResolvedScope.mockReturnValue({
|
|
431
|
+
resolvedScope: null,
|
|
432
|
+
isLoading: false,
|
|
433
|
+
error: null,
|
|
434
|
+
});
|
|
403
435
|
|
|
404
436
|
const { result } = renderHook(() => useSecureDataAccess());
|
|
405
437
|
|
|
406
|
-
await expect(result.current.secureQuery('users', '*')).rejects.toThrow();
|
|
438
|
+
await expect(result.current.secureQuery('users', '*')).rejects.toThrow('Organisation context is required for data access');
|
|
407
439
|
});
|
|
408
440
|
});
|
|
409
441
|
|
|
@@ -411,7 +443,7 @@ describe('useSecureDataAccess', () => {
|
|
|
411
443
|
it('prevents data leaks between organisations', async () => {
|
|
412
444
|
const { result } = renderHook(() => useSecureDataAccess());
|
|
413
445
|
|
|
414
|
-
await result.current.secureQuery('
|
|
446
|
+
await result.current.secureQuery('core_events', '*');
|
|
415
447
|
|
|
416
448
|
// Verify organisation_id filter is always applied
|
|
417
449
|
expect(freshMockQueryBuilder.eq).toHaveBeenCalledWith('organisation_id', 'org-123');
|
|
@@ -467,13 +499,25 @@ describe('useSecureDataAccess', () => {
|
|
|
467
499
|
|
|
468
500
|
// Change organisation
|
|
469
501
|
const newOrganisation = { id: 'org-456', name: 'New Organisation' };
|
|
470
|
-
|
|
502
|
+
mockUseUnifiedAuth.mockReturnValue({
|
|
503
|
+
user: mockUser,
|
|
504
|
+
session: mockSession,
|
|
505
|
+
supabase: freshMockSupabase,
|
|
506
|
+
isAuthenticated: true,
|
|
507
|
+
signOut: vi.fn(),
|
|
471
508
|
selectedOrganisation: newOrganisation,
|
|
472
|
-
getUserRole: vi.fn().mockReturnValue('member'),
|
|
473
|
-
validateOrganisationAccess: vi.fn().mockResolvedValue(true),
|
|
474
|
-
ensureOrganisationContext: vi.fn().mockReturnValue(newOrganisation),
|
|
475
|
-
// Add other required properties
|
|
476
509
|
} as any);
|
|
510
|
+
|
|
511
|
+
// Update useResolvedScope mock to return new organisation
|
|
512
|
+
mockUseResolvedScope.mockReturnValue({
|
|
513
|
+
resolvedScope: {
|
|
514
|
+
organisationId: 'org-456',
|
|
515
|
+
eventId: undefined,
|
|
516
|
+
appId: undefined,
|
|
517
|
+
},
|
|
518
|
+
isLoading: false,
|
|
519
|
+
error: null,
|
|
520
|
+
});
|
|
477
521
|
|
|
478
522
|
rerender();
|
|
479
523
|
|
|
@@ -487,9 +531,10 @@ describe('useSecureDataAccess', () => {
|
|
|
487
531
|
expect(mockUseUnifiedAuth).toHaveBeenCalled();
|
|
488
532
|
});
|
|
489
533
|
|
|
490
|
-
it('integrates with
|
|
534
|
+
it('integrates with useResolvedScope hook correctly', () => {
|
|
491
535
|
renderHook(() => useSecureDataAccess());
|
|
492
|
-
|
|
536
|
+
// useSecureDataAccess now uses useResolvedScope instead of useOrganisations
|
|
537
|
+
expect(mockUseResolvedScope).toHaveBeenCalled();
|
|
493
538
|
});
|
|
494
539
|
|
|
495
540
|
it('uses organisation context from provider', () => {
|