@jmruthers/pace-core 0.5.193 → 0.6.2
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 +62 -0
- package/README.md +7 -1
- package/cursor-rules/00-pace-core-compliance.mdc +299 -0
- package/cursor-rules/01-standards-compliance.mdc +244 -0
- package/cursor-rules/02-project-structure.mdc +200 -0
- package/cursor-rules/03-solid-principles.mdc +222 -0
- package/cursor-rules/04-testing-standards.mdc +268 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
- package/cursor-rules/06-code-quality.mdc +309 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +214 -0
- package/cursor-rules/08-markup-quality.mdc +452 -0
- package/cursor-rules/CHANGELOG.md +119 -0
- package/cursor-rules/README.md +192 -0
- package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-BPvc3Ka0.d.ts} +54 -0
- package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-BMRU8a1j.d.ts} +34 -2
- package/dist/{DataTable-5FU7IESH.js → DataTable-TPTKCX4D.js} +10 -9
- package/dist/{PublicPageProvider-C0Sm_e5k.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +385 -261
- package/dist/{UnifiedAuthProvider-RGJTDE2C.js → UnifiedAuthProvider-CH6Z342H.js} +3 -3
- package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CVcTjx-d.d.ts} +29 -0
- package/dist/{api-N774RPUA.js → api-MVVQZLJI.js} +2 -2
- package/dist/{chunk-KNC55RTG.js → chunk-24UVZUZG.js} +90 -54
- package/dist/chunk-24UVZUZG.js.map +1 -0
- package/dist/{chunk-HWIIPPNI.js → chunk-2UOI2FG5.js} +20 -20
- package/dist/chunk-2UOI2FG5.js.map +1 -0
- package/dist/{chunk-E3SPN4VZ 5.js → chunk-3XC4CPTD.js} +4345 -3986
- package/dist/chunk-3XC4CPTD.js.map +1 -0
- package/dist/{chunk-7EQTDTTJ.js → chunk-6J4GEEJR.js} +172 -45
- package/dist/chunk-6J4GEEJR.js.map +1 -0
- package/dist/{chunk-6C4YBBJM 5.js → chunk-6SOIHG6Z.js} +1 -1
- package/dist/chunk-6SOIHG6Z.js.map +1 -0
- package/dist/{chunk-7FLMSG37.js → chunk-EHMR7VYL.js} +25 -25
- package/dist/chunk-EHMR7VYL.js.map +1 -0
- package/dist/{chunk-I7PSE6JW.js → chunk-F2IMUDXZ.js} +2 -75
- package/dist/chunk-F2IMUDXZ.js.map +1 -0
- package/dist/{chunk-QWWZ5CAQ.js → chunk-FFQEQTNW.js} +7 -9
- package/dist/chunk-FFQEQTNW.js.map +1 -0
- package/dist/chunk-FMUCXFII.js +76 -0
- package/dist/chunk-FMUCXFII.js.map +1 -0
- package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
- package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
- package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
- package/dist/chunk-L4OXEN46.js.map +1 -0
- package/dist/{chunk-R77UEZ4E 3.js → chunk-M43Y4SSO.js} +1 -1
- package/dist/chunk-M43Y4SSO.js.map +1 -0
- package/dist/{chunk-IIELH4DL.js → chunk-MMZ7JXPU.js} +60 -223
- package/dist/chunk-MMZ7JXPU.js.map +1 -0
- package/dist/{chunk-NOAYCWCX 5.js → chunk-NECFR5MM.js} +394 -312
- package/dist/chunk-NECFR5MM.js.map +1 -0
- package/dist/{chunk-BC4IJKSL.js → chunk-SFZUDBL5.js} +40 -4
- package/dist/chunk-SFZUDBL5.js.map +1 -0
- package/dist/{chunk-XNXXZ43G.js → chunk-XWQCNGTQ.js} +748 -364
- package/dist/chunk-XWQCNGTQ.js.map +1 -0
- package/dist/components.d.ts +6 -6
- package/dist/components.js +15 -12
- package/dist/components.js.map +1 -1
- package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
- package/dist/hooks.d.ts +59 -126
- package/dist/hooks.js +19 -28
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +63 -16
- package/dist/index.js +23 -24
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +21 -3
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +146 -115
- package/dist/rbac/index.js +8 -11
- package/dist/theming/runtime.d.ts +1 -13
- package/dist/theming/runtime.js +1 -1
- package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
- package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
- package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
- package/dist/types.d.ts +2 -2
- package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +34 -4
- package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
- package/dist/utils.d.ts +4 -5
- package/dist/utils.js +15 -15
- package/dist/utils.js.map +1 -1
- package/docs/api/README.md +7 -1
- package/docs/api/classes/ColumnFactory.md +8 -8
- package/docs/api/classes/InvalidScopeError.md +4 -4
- package/docs/api/classes/Logger.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +4 -4
- package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
- package/docs/api/classes/PermissionDeniedError.md +4 -4
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +4 -4
- package/docs/api/classes/RBACNotInitializedError.md +4 -4
- package/docs/api/classes/SecureSupabaseClient.md +18 -15
- package/docs/api/classes/StorageUtils.md +1 -1
- 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 +4 -4
- package/docs/api/interfaces/AutocompleteOptions.md +1 -1
- package/docs/api/interfaces/AvatarProps.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +9 -2
- package/docs/api/interfaces/ButtonProps.md +7 -4
- package/docs/api/interfaces/CalendarProps.md +8 -5
- package/docs/api/interfaces/CardProps.md +8 -5
- 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 +24 -21
- package/docs/api/interfaces/DataTableColumn.md +31 -31
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
- package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +5 -5
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/ErrorBoundaryProps.md +147 -0
- package/docs/api/interfaces/ErrorBoundaryProviderProps.md +36 -0
- package/docs/api/interfaces/ErrorBoundaryState.md +75 -0
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +8 -8
- 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 +26 -23
- package/docs/api/interfaces/FooterProps.md +10 -8
- package/docs/api/interfaces/FormFieldProps.md +10 -10
- 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 +7 -4
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoggerConfig.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +14 -11
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +11 -11
- package/docs/api/interfaces/NavigationMenuProps.md +15 -15
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- 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 +30 -27
- package/docs/api/interfaces/PaceLoginPageProps.md +6 -4
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/ParsedAddress.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProgressProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +7 -26
- package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
- package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
- package/docs/api/interfaces/PublicPageLayoutProps.md +7 -20
- 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 +1 -1
- 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 +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- 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 +3 -3
- package/docs/api/interfaces/SetupIssue.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/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 +3 -3
- package/docs/api/interfaces/TextareaProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +4 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +58 -55
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +15 -13
- package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +11 -9
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +9 -6
- package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
- package/docs/api/interfaces/UsePublicEventReturn.md +8 -5
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +12 -9
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +10 -7
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +14 -11
- package/docs/api/interfaces/UserMenuProps.md +8 -6
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +575 -634
- package/docs/architecture/database-schema-requirements.md +161 -0
- package/docs/core-concepts/rbac-system.md +3 -3
- package/docs/documentation-index.md +2 -4
- package/docs/getting-started/cursor-rules.md +263 -0
- package/docs/getting-started/installation-guide.md +6 -1
- package/docs/getting-started/quick-start.md +6 -1
- package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
- package/docs/migration/MIGRATION_GUIDE.md +6 -28
- package/docs/migration/README.md +52 -6
- package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
- package/docs/migration/V0.6.0_REACT_19_MIGRATION.md +227 -0
- package/docs/migration/database-changes-december-2025.md +3 -3
- package/docs/rbac/event-based-apps.md +1 -1
- package/docs/rbac/getting-started.md +1 -1
- package/docs/rbac/quick-start.md +1 -1
- package/docs/standards/README.md +40 -0
- package/docs/troubleshooting/migration.md +4 -4
- package/examples/PublicPages/PublicEventPage.tsx +1 -1
- package/package.json +12 -6
- package/scripts/audit/core/checks/accessibility.cjs +197 -0
- package/scripts/audit/core/checks/api-usage.cjs +191 -0
- package/scripts/audit/core/checks/bundle.cjs +142 -0
- package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +737 -691
- package/scripts/audit/core/checks/config.cjs +54 -0
- package/scripts/audit/core/checks/coverage.cjs +84 -0
- package/scripts/audit/core/checks/dependencies.cjs +454 -0
- package/scripts/audit/core/checks/documentation.cjs +203 -0
- package/scripts/audit/core/checks/environment.cjs +128 -0
- package/scripts/audit/core/checks/error-handling.cjs +299 -0
- package/scripts/audit/core/checks/forms.cjs +172 -0
- package/scripts/audit/core/checks/heuristics.cjs +68 -0
- package/scripts/audit/core/checks/hooks.cjs +334 -0
- package/scripts/audit/core/checks/imports.cjs +244 -0
- package/scripts/audit/core/checks/performance.cjs +325 -0
- package/scripts/audit/core/checks/routes.cjs +117 -0
- package/scripts/audit/core/checks/state.cjs +130 -0
- package/scripts/audit/core/checks/structure.cjs +65 -0
- package/scripts/audit/core/checks/style.cjs +584 -0
- package/scripts/audit/core/checks/testing.cjs +122 -0
- package/scripts/audit/core/checks/typescript.cjs +61 -0
- package/scripts/audit/core/scanner.cjs +199 -0
- package/scripts/audit/core/utils.cjs +137 -0
- package/scripts/audit/index.cjs +223 -0
- package/scripts/audit/reporters/console.cjs +151 -0
- package/scripts/audit/reporters/json.cjs +54 -0
- package/scripts/audit/reporters/markdown.cjs +124 -0
- package/scripts/audit-consuming-app.cjs +86 -0
- package/scripts/build-docs/build-decision.js +240 -0
- package/scripts/build-docs/cache-utils.js +105 -0
- package/scripts/build-docs/content-normalization.js +150 -0
- package/scripts/build-docs/file-utils.js +105 -0
- package/scripts/build-docs/git-utils.js +86 -0
- package/scripts/build-docs/hash-utils.js +116 -0
- package/scripts/build-docs/typedoc-runner.js +220 -0
- package/scripts/build-docs-incremental.js +77 -913
- package/scripts/install-cursor-rules.cjs +236 -0
- package/scripts/utils/command-runner.js +16 -11
- package/scripts/validate-formats.js +61 -56
- package/scripts/validate-master.js +74 -69
- package/scripts/validate-pre-publish.js +70 -65
- package/src/__tests__/helpers/test-providers.tsx +1 -1
- package/src/__tests__/helpers/test-utils.tsx +1 -1
- package/src/__tests__/hooks/usePermissions.test.ts +2 -2
- package/src/components/Alert/Alert.test.tsx +12 -18
- package/src/components/Alert/Alert.tsx +5 -7
- package/src/components/Avatar/Avatar.test.tsx +4 -4
- package/src/components/Badge/Badge.tsx +16 -4
- package/src/components/Button/Button.tsx +27 -4
- package/src/components/Calendar/Calendar.tsx +9 -3
- package/src/components/Card/Card.tsx +4 -0
- package/src/components/Checkbox/Checkbox.test.tsx +12 -12
- package/src/components/Checkbox/Checkbox.tsx +2 -2
- package/src/components/DataTable/DataTable.test.tsx +57 -93
- package/src/components/DataTable/DataTable.tsx +40 -6
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +29 -7
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
- package/src/components/DataTable/components/AccessDeniedPage.tsx +17 -26
- package/src/components/DataTable/components/ActionButtons.tsx +10 -7
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
- package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
- package/src/components/DataTable/components/DataTableBody.tsx +8 -0
- package/src/components/DataTable/components/DataTableCore.tsx +200 -561
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
- package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
- package/src/components/DataTable/components/DataTableModals.tsx +9 -1
- package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
- package/src/components/DataTable/components/EditFields.tsx +307 -0
- package/src/components/DataTable/components/EditableRow.tsx +9 -1
- package/src/components/DataTable/components/EmptyState.tsx +10 -0
- package/src/components/DataTable/components/FilterRow.tsx +12 -0
- package/src/components/DataTable/components/GroupHeader.tsx +12 -0
- package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
- package/src/components/DataTable/components/ImportModal.tsx +7 -0
- package/src/components/DataTable/components/LoadingState.tsx +6 -0
- package/src/components/DataTable/components/PaginationControls.tsx +16 -1
- package/src/components/DataTable/components/RowComponent.tsx +391 -0
- package/src/components/DataTable/components/UnifiedTableBody.tsx +62 -852
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
- package/src/components/DataTable/components/cellValueUtils.ts +40 -0
- package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
- package/src/components/DataTable/context/DataTableContext.tsx +50 -0
- package/src/components/DataTable/core/ColumnFactory.ts +31 -0
- package/src/components/DataTable/core/DataTableContext.tsx +32 -1
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
- package/src/components/DataTable/hooks/useColumnReordering.ts +14 -2
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
- package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +124 -32
- package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
- package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
- package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
- package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
- package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
- package/src/components/DataTable/styles.ts +6 -6
- package/src/components/DataTable/types.ts +6 -10
- package/src/components/DataTable/utils/a11yUtils.ts +7 -0
- package/src/components/DataTable/utils/debugTools.ts +18 -113
- package/src/components/DataTable/utils/errorHandling.ts +12 -0
- package/src/components/DataTable/utils/exportUtils.ts +9 -0
- package/src/components/DataTable/utils/flexibleImport.ts +12 -48
- package/src/components/DataTable/utils/paginationUtils.ts +8 -0
- package/src/components/DataTable/utils/performanceUtils.ts +5 -1
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
- package/src/components/Dialog/Dialog.tsx +8 -7
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +46 -6
- package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
- package/src/components/ErrorBoundary/index.ts +27 -2
- package/src/components/EventSelector/EventSelector.tsx +4 -1
- package/src/components/FileDisplay/FileDisplay.test.tsx +2 -2
- package/src/components/FileDisplay/FileDisplay.tsx +32 -18
- package/src/components/FileUpload/FileUpload.tsx +22 -2
- package/src/components/Footer/Footer.test.tsx +16 -16
- package/src/components/Footer/Footer.tsx +15 -12
- package/src/components/Form/Form.test.tsx +36 -15
- package/src/components/Form/Form.tsx +31 -26
- package/src/components/Header/Header.tsx +22 -11
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
- package/src/components/Input/Input.test.tsx +2 -2
- package/src/components/Input/Input.tsx +36 -34
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
- package/src/components/LoginForm/LoginForm.test.tsx +42 -42
- package/src/components/LoginForm/LoginForm.tsx +12 -8
- package/src/components/NavigationMenu/NavigationMenu.tsx +15 -514
- package/src/components/NavigationMenu/types.ts +56 -0
- package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -0
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +54 -52
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +33 -12
- package/src/components/PaceAppLayout/README.md +1 -1
- package/src/components/PaceAppLayout/test-setup.tsx +1 -2
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +4 -1
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
- package/src/components/PasswordChange/PasswordChangeForm.tsx +10 -1
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
- package/src/components/PublicLayout/PublicPageLayout.tsx +3 -6
- package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
- package/src/components/Select/Select.tsx +95 -438
- package/src/components/Select/context.ts +23 -0
- package/src/components/Select/hooks/useSelectEvents.ts +87 -0
- package/src/components/Select/hooks/useSelectSearch.ts +91 -0
- package/src/components/Select/hooks/useSelectState.ts +104 -0
- package/src/components/Select/index.ts +9 -1
- package/src/components/Select/types.ts +123 -0
- package/src/components/Select/utils/text.ts +26 -0
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +5 -6
- package/src/components/Switch/Switch.tsx +4 -4
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Tabs/Tabs.tsx +1 -1
- package/src/components/Textarea/Textarea.tsx +27 -29
- package/src/components/Toast/Toast.tsx +5 -1
- package/src/components/Tooltip/Tooltip.tsx +3 -3
- package/src/components/UserMenu/UserMenu.test.tsx +24 -11
- package/src/components/UserMenu/UserMenu.tsx +22 -19
- package/src/components/index.ts +2 -2
- package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
- package/src/hooks/__tests__/index.unit.test.ts +2 -5
- package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
- package/src/hooks/index.ts +1 -2
- package/src/hooks/public/usePublicEvent.ts +5 -1
- package/src/hooks/public/usePublicEventLogo.ts +5 -1
- package/src/hooks/public/usePublicFileDisplay.ts +4 -0
- package/src/hooks/public/usePublicRouteParams.ts +5 -1
- package/src/hooks/services/useAuth.ts +32 -0
- package/src/hooks/services/useCurrentEvent.ts +6 -0
- package/src/hooks/services/useCurrentOrganisation.ts +6 -0
- package/src/hooks/useDataTableState.ts +8 -18
- package/src/hooks/useDebounce.ts +9 -0
- package/src/hooks/useEventTheme.ts +6 -0
- package/src/hooks/useFileDisplay.ts +4 -0
- package/src/hooks/useFileReference.ts +25 -7
- package/src/hooks/useFileUrl.ts +11 -1
- package/src/hooks/useFocusManagement.ts +16 -2
- package/src/hooks/useFocusTrap.ts +7 -4
- package/src/hooks/useFormDialog.ts +8 -7
- package/src/hooks/useInactivityTracker.ts +4 -1
- package/src/hooks/useKeyboardShortcuts.ts +4 -0
- package/src/hooks/useOrganisationPermissions.ts +4 -0
- package/src/hooks/useOrganisationSecurity.ts +4 -0
- package/src/hooks/usePerformanceMonitor.ts +4 -0
- package/src/hooks/usePermissionCache.ts +8 -1
- package/src/hooks/useQueryCache.ts +12 -1
- package/src/hooks/useSessionRestoration.ts +4 -0
- package/src/hooks/useStorage.ts +4 -0
- package/src/hooks/useToast.ts +3 -3
- package/src/index.ts +2 -1
- package/src/providers/__tests__/OrganisationProvider.test.tsx +115 -49
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
- package/src/providers/services/AuthServiceProvider.tsx +18 -0
- package/src/providers/services/EventServiceProvider.tsx +18 -0
- package/src/providers/services/InactivityServiceProvider.tsx +18 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
- package/src/providers/services/UnifiedAuthProvider.tsx +58 -22
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +33 -7
- package/src/rbac/README.md +1 -1
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +26 -26
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
- package/src/rbac/adapters.tsx +14 -5
- package/src/rbac/api.ts +100 -67
- package/src/rbac/components/EnhancedNavigationMenu.tsx +1 -1
- package/src/rbac/components/NavigationGuard.tsx +1 -1
- package/src/rbac/components/NavigationProvider.tsx +5 -2
- package/src/rbac/components/PagePermissionGuard.tsx +158 -18
- package/src/rbac/components/PagePermissionProvider.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +1 -1
- package/src/rbac/components/RoleBasedRouter.tsx +6 -2
- package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
- package/src/rbac/components/SecureDataProvider.tsx +21 -6
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
- package/src/rbac/engine.ts +38 -14
- package/src/rbac/hooks/permissions/index.ts +7 -0
- package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
- package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
- package/src/rbac/hooks/permissions/useCan.ts +347 -0
- package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
- package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
- package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
- package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
- package/src/rbac/hooks/useCan.test.ts +71 -64
- package/src/rbac/hooks/usePermissions.ts +14 -995
- package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
- package/src/rbac/hooks/useResourcePermissions.ts +14 -4
- package/src/rbac/hooks/useSecureSupabase.ts +33 -13
- package/src/rbac/permissions.ts +0 -30
- package/src/rbac/secureClient.ts +212 -61
- package/src/rbac/types.ts +8 -0
- package/src/theming/__tests__/parseEventColours.test.ts +6 -9
- package/src/theming/parseEventColours.ts +5 -19
- package/src/types/vitest-globals.d.ts +51 -26
- package/src/utils/__mocks__/supabaseMock.ts +1 -3
- package/src/utils/__tests__/formatting.unit.test.ts +4 -4
- package/src/utils/__tests__/index.unit.test.ts +2 -2
- package/src/utils/audit/audit.ts +0 -3
- package/src/utils/core/cn.ts +1 -1
- package/src/utils/file-reference/index.ts +53 -1
- package/src/utils/formatting/formatting.ts +8 -18
- package/src/utils/index.ts +0 -1
- package/src/utils/security/secureDataAccess.test.ts +31 -20
- package/src/utils/security/secureDataAccess.ts +4 -3
- package/dist/chunk-6C4YBBJM.js +0 -628
- package/dist/chunk-6C4YBBJM.js.map +0 -1
- package/dist/chunk-7D4SUZUM.js 2.map +0 -1
- package/dist/chunk-7EQTDTTJ.js 2.map +0 -1
- package/dist/chunk-7EQTDTTJ.js.map +0 -1
- package/dist/chunk-7FLMSG37.js 2.map +0 -1
- package/dist/chunk-7FLMSG37.js.map +0 -1
- package/dist/chunk-BC4IJKSL.js.map +0 -1
- package/dist/chunk-E3SPN4VZ.js +0 -12917
- package/dist/chunk-E3SPN4VZ.js.map +0 -1
- package/dist/chunk-E66EQZE6 5.js +0 -37
- package/dist/chunk-E66EQZE6.js 2.map +0 -1
- package/dist/chunk-HWIIPPNI.js.map +0 -1
- package/dist/chunk-I7PSE6JW 5.js +0 -191
- package/dist/chunk-I7PSE6JW.js 2.map +0 -1
- package/dist/chunk-I7PSE6JW.js.map +0 -1
- package/dist/chunk-IIELH4DL.js.map +0 -1
- package/dist/chunk-KNC55RTG.js 5.map +0 -1
- package/dist/chunk-KNC55RTG.js.map +0 -1
- package/dist/chunk-KQCRWDSA.js 5.map +0 -1
- package/dist/chunk-LFNCN2SP.js +0 -412
- package/dist/chunk-LFNCN2SP.js 2.map +0 -1
- package/dist/chunk-LFNCN2SP.js.map +0 -1
- package/dist/chunk-LMC26NLJ 2.js +0 -84
- package/dist/chunk-NOAYCWCX.js +0 -4993
- package/dist/chunk-NOAYCWCX.js.map +0 -1
- package/dist/chunk-QWWZ5CAQ.js 3.map +0 -1
- package/dist/chunk-QWWZ5CAQ.js.map +0 -1
- package/dist/chunk-QXHPKYJV 3.js +0 -113
- package/dist/chunk-R77UEZ4E.js +0 -68
- package/dist/chunk-R77UEZ4E.js.map +0 -1
- package/dist/chunk-SQGMNID3.js.map +0 -1
- package/dist/chunk-VBXEHIUJ.js 6.map +0 -1
- package/dist/chunk-XNXXZ43G.js.map +0 -1
- package/dist/chunk-ZSAAAMVR 6.js +0 -25
- package/dist/components.js 5.map +0 -1
- package/dist/styles/index 2.js +0 -12
- package/dist/styles/index.js 5.map +0 -1
- package/dist/theming/runtime 5.js +0 -19
- package/dist/theming/runtime.js 5.map +0 -1
- package/docs/api/classes/ErrorBoundary.md +0 -144
- package/docs/migration/quick-migration-guide.md +0 -356
- package/docs/migration/service-architecture.md +0 -281
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
- package/src/hooks/useSecureDataAccess.test.ts +0 -559
- package/src/hooks/useSecureDataAccess.ts +0 -666
- /package/dist/{DataTable-5FU7IESH.js.map → DataTable-TPTKCX4D.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-RGJTDE2C.js.map → UnifiedAuthProvider-CH6Z342H.js.map} +0 -0
- /package/dist/{api-N774RPUA.js.map → api-MVVQZLJI.js.map} +0 -0
- /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
- /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
- /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
- /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
- /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/index.ts +0 -0
package/dist/chunk-LFNCN2SP.js
DELETED
|
@@ -1,412 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
useOrganisationSecurity,
|
|
3
|
-
useResolvedScope
|
|
4
|
-
} from "./chunk-IIELH4DL.js";
|
|
5
|
-
import {
|
|
6
|
-
EventServiceContext,
|
|
7
|
-
useUnifiedAuth
|
|
8
|
-
} from "./chunk-7FLMSG37.js";
|
|
9
|
-
import {
|
|
10
|
-
setOrganisationContext
|
|
11
|
-
} from "./chunk-VBXEHIUJ.js";
|
|
12
|
-
import {
|
|
13
|
-
logger
|
|
14
|
-
} from "./chunk-PWLANIRT.js";
|
|
15
|
-
|
|
16
|
-
// src/hooks/useSecureDataAccess.ts
|
|
17
|
-
import { useCallback, useState, useContext } from "react";
|
|
18
|
-
function useSecureDataAccess() {
|
|
19
|
-
const { supabase, user, session, selectedOrganisation, selectedEvent } = useUnifiedAuth();
|
|
20
|
-
const eventServiceContext = useContext(EventServiceContext);
|
|
21
|
-
const eventFromContext = eventServiceContext?.eventService?.getSelectedEvent() || null;
|
|
22
|
-
const effectiveSelectedEvent = selectedEvent || eventFromContext;
|
|
23
|
-
const { superAdminContext } = useOrganisationSecurity();
|
|
24
|
-
const isSuperAdmin = superAdminContext.isSuperAdmin;
|
|
25
|
-
const { resolvedScope } = useResolvedScope({
|
|
26
|
-
supabase,
|
|
27
|
-
selectedOrganisationId: selectedOrganisation?.id || null,
|
|
28
|
-
selectedEventId: effectiveSelectedEvent?.event_id || null
|
|
29
|
-
});
|
|
30
|
-
const validateContext = useCallback(() => {
|
|
31
|
-
if (!supabase) {
|
|
32
|
-
throw new Error("No Supabase client available");
|
|
33
|
-
}
|
|
34
|
-
if (!user || !session) {
|
|
35
|
-
throw new Error("User must be authenticated with valid session");
|
|
36
|
-
}
|
|
37
|
-
if (isSuperAdmin) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
if (!resolvedScope?.organisationId) {
|
|
41
|
-
throw new Error("Organisation context is required for data access");
|
|
42
|
-
}
|
|
43
|
-
}, [supabase, user, session, resolvedScope, isSuperAdmin]);
|
|
44
|
-
const getCurrentOrganisationId = useCallback(() => {
|
|
45
|
-
if (isSuperAdmin) {
|
|
46
|
-
return resolvedScope?.organisationId || selectedOrganisation?.id || "";
|
|
47
|
-
}
|
|
48
|
-
validateContext();
|
|
49
|
-
return resolvedScope?.organisationId || "";
|
|
50
|
-
}, [validateContext, resolvedScope, selectedOrganisation, isSuperAdmin]);
|
|
51
|
-
const setOrganisationContextInSession = useCallback(async (organisationId) => {
|
|
52
|
-
if (!supabase || !organisationId) {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
await setOrganisationContext(supabase, organisationId);
|
|
56
|
-
}, [supabase]);
|
|
57
|
-
const secureQuery = useCallback(async (table, columns, filters = {}, options = {}) => {
|
|
58
|
-
validateContext();
|
|
59
|
-
const bypassOrganisationFilter = isSuperAdmin;
|
|
60
|
-
const organisationId = bypassOrganisationFilter ? void 0 : getCurrentOrganisationId();
|
|
61
|
-
await setOrganisationContextInSession(organisationId);
|
|
62
|
-
let query = supabase.from(table).select(columns);
|
|
63
|
-
const tablesWithOrganisation = [
|
|
64
|
-
"core_events",
|
|
65
|
-
"organisation_settings",
|
|
66
|
-
"rbac_event_app_roles",
|
|
67
|
-
"rbac_organisation_roles",
|
|
68
|
-
// SECURITY: Phase 2 additions - complete organisation table mapping
|
|
69
|
-
"organisation_audit_log",
|
|
70
|
-
"organisation_invitations",
|
|
71
|
-
"organisation_app_access",
|
|
72
|
-
// SECURITY: Emergency additions for Phase 1 fixes
|
|
73
|
-
"cake_meal",
|
|
74
|
-
"cake_mealtype",
|
|
75
|
-
"core_person",
|
|
76
|
-
// NOTE: core_member, medi_profile, core_contact, core_consent, core_identification, core_qualification
|
|
77
|
-
// are now person-scoped (not organisation-scoped) - removed from this list
|
|
78
|
-
// SECURITY: Phase 3A additions - medical and personal data
|
|
79
|
-
// NOTE: medi_condition, medi_diet, medi_action_plan, medi_profile_versions are now person-scoped
|
|
80
|
-
// (via medi_profile) - removed from this list
|
|
81
|
-
// core_identification_type remains organisation-scoped (lookup table)
|
|
82
|
-
"core_identification_type",
|
|
83
|
-
"form_responses",
|
|
84
|
-
"form_response_values",
|
|
85
|
-
"forms",
|
|
86
|
-
// SECURITY: Phase 3B additions - remaining critical tables
|
|
87
|
-
"invoice",
|
|
88
|
-
"line_item",
|
|
89
|
-
"credit_balance",
|
|
90
|
-
"payment_method",
|
|
91
|
-
"form_contexts",
|
|
92
|
-
"form_field_config",
|
|
93
|
-
"form_fields",
|
|
94
|
-
"cake_delivery",
|
|
95
|
-
"cake_diettype",
|
|
96
|
-
"cake_diner",
|
|
97
|
-
"cake_dish",
|
|
98
|
-
"cake_item",
|
|
99
|
-
"cake_logistics",
|
|
100
|
-
"cake_mealplan",
|
|
101
|
-
"cake_package",
|
|
102
|
-
"cake_recipe",
|
|
103
|
-
"cake_supplier",
|
|
104
|
-
"cake_supply",
|
|
105
|
-
"cake_unit",
|
|
106
|
-
"event_app_access",
|
|
107
|
-
"base_application",
|
|
108
|
-
"base_questions",
|
|
109
|
-
// rbac_user_profiles has organisation_id but uses conditional filtering
|
|
110
|
-
"rbac_user_profiles"
|
|
111
|
-
];
|
|
112
|
-
if (!bypassOrganisationFilter && organisationId && tablesWithOrganisation.includes(table)) {
|
|
113
|
-
if (table === "rbac_user_profiles" && isSuperAdmin) {
|
|
114
|
-
} else {
|
|
115
|
-
query = query.eq("organisation_id", organisationId);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
Object.entries(filters).forEach(([key, value]) => {
|
|
119
|
-
if (value !== void 0 && value !== null) {
|
|
120
|
-
const columnName = key.includes(".") ? key.split(".").pop() : key;
|
|
121
|
-
query = query.eq(columnName, value);
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
if (options.orderBy) {
|
|
125
|
-
const orderByColumn = options.orderBy.split(".").pop();
|
|
126
|
-
if (orderByColumn) {
|
|
127
|
-
query = query.order(orderByColumn, { ascending: options.ascending ?? true });
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
if (options.limit) {
|
|
131
|
-
query = query.limit(options.limit);
|
|
132
|
-
}
|
|
133
|
-
if (options.offset) {
|
|
134
|
-
query = query.range(options.offset, options.offset + (options.limit || 100) - 1);
|
|
135
|
-
}
|
|
136
|
-
const { data, error } = await query;
|
|
137
|
-
if (error) {
|
|
138
|
-
logger.error("useSecureDataAccess", "Query failed", { table, columns, filters, error });
|
|
139
|
-
recordDataAccess(table, "read", false, `SELECT ${columns} FROM ${table}`, filters);
|
|
140
|
-
throw error;
|
|
141
|
-
}
|
|
142
|
-
recordDataAccess(table, "read", true, `SELECT ${columns} FROM ${table}`, filters);
|
|
143
|
-
return data || [];
|
|
144
|
-
}, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, isSuperAdmin]);
|
|
145
|
-
const secureInsert = useCallback(async (table, data) => {
|
|
146
|
-
validateContext();
|
|
147
|
-
const bypassOrganisationFilter = isSuperAdmin;
|
|
148
|
-
const organisationId = bypassOrganisationFilter ? void 0 : getCurrentOrganisationId();
|
|
149
|
-
await setOrganisationContextInSession(organisationId);
|
|
150
|
-
const secureData = bypassOrganisationFilter ? { ...data } : {
|
|
151
|
-
...data,
|
|
152
|
-
organisation_id: organisationId
|
|
153
|
-
};
|
|
154
|
-
const { data: insertData, error } = await supabase.from(table).insert(secureData).select().single();
|
|
155
|
-
if (error) {
|
|
156
|
-
logger.error("useSecureDataAccess", "Insert failed", { table, data: secureData, error });
|
|
157
|
-
throw error;
|
|
158
|
-
}
|
|
159
|
-
return insertData;
|
|
160
|
-
}, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, isSuperAdmin]);
|
|
161
|
-
const secureUpdate = useCallback(async (table, data, filters) => {
|
|
162
|
-
validateContext();
|
|
163
|
-
const bypassOrganisationFilter = isSuperAdmin;
|
|
164
|
-
const organisationId = bypassOrganisationFilter ? void 0 : getCurrentOrganisationId();
|
|
165
|
-
await setOrganisationContextInSession(organisationId);
|
|
166
|
-
const { organisation_id, ...secureData } = data;
|
|
167
|
-
let query = supabase.from(table).update(secureData);
|
|
168
|
-
const tablesWithOrganisation = [
|
|
169
|
-
"core_events",
|
|
170
|
-
"organisation_settings",
|
|
171
|
-
"rbac_event_app_roles",
|
|
172
|
-
"rbac_organisation_roles",
|
|
173
|
-
// SECURITY: Phase 2 additions - complete organisation table mapping
|
|
174
|
-
"organisation_audit_log",
|
|
175
|
-
"organisation_invitations",
|
|
176
|
-
"organisation_app_access",
|
|
177
|
-
// SECURITY: Emergency additions for Phase 1 fixes
|
|
178
|
-
"cake_meal",
|
|
179
|
-
"cake_mealtype",
|
|
180
|
-
"core_person",
|
|
181
|
-
"core_member"
|
|
182
|
-
];
|
|
183
|
-
if (!bypassOrganisationFilter && organisationId && tablesWithOrganisation.includes(table)) {
|
|
184
|
-
query = query.eq("organisation_id", organisationId);
|
|
185
|
-
}
|
|
186
|
-
Object.entries(filters).forEach(([key, value]) => {
|
|
187
|
-
if (value !== void 0 && value !== null) {
|
|
188
|
-
query = query.eq(key, value);
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
const { data: updateData, error } = await query.select();
|
|
192
|
-
if (error) {
|
|
193
|
-
logger.error("useSecureDataAccess", "Update failed", { table, data: secureData, filters, error });
|
|
194
|
-
throw error;
|
|
195
|
-
}
|
|
196
|
-
return updateData || [];
|
|
197
|
-
}, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, isSuperAdmin]);
|
|
198
|
-
const secureDelete = useCallback(async (table, filters) => {
|
|
199
|
-
validateContext();
|
|
200
|
-
const bypassOrganisationFilter = isSuperAdmin;
|
|
201
|
-
const organisationId = bypassOrganisationFilter ? void 0 : getCurrentOrganisationId();
|
|
202
|
-
await setOrganisationContextInSession(organisationId);
|
|
203
|
-
let query = supabase.from(table).delete();
|
|
204
|
-
const tablesWithOrganisation = [
|
|
205
|
-
"core_events",
|
|
206
|
-
"organisation_settings",
|
|
207
|
-
"rbac_event_app_roles",
|
|
208
|
-
"rbac_organisation_roles",
|
|
209
|
-
// SECURITY: Phase 2 additions - complete organisation table mapping
|
|
210
|
-
"organisation_audit_log",
|
|
211
|
-
"organisation_invitations",
|
|
212
|
-
"organisation_app_access",
|
|
213
|
-
// SECURITY: Emergency additions for Phase 1 fixes
|
|
214
|
-
"cake_meal",
|
|
215
|
-
"cake_mealtype",
|
|
216
|
-
"core_person",
|
|
217
|
-
"core_member",
|
|
218
|
-
// SECURITY: Phase 3A additions - medical and personal data
|
|
219
|
-
"medi_profile",
|
|
220
|
-
"medi_condition",
|
|
221
|
-
"medi_diet",
|
|
222
|
-
"medi_action_plan",
|
|
223
|
-
"medi_profile_versions",
|
|
224
|
-
"core_consent",
|
|
225
|
-
"core_contact",
|
|
226
|
-
"core_identification",
|
|
227
|
-
"core_identification_type",
|
|
228
|
-
"core_qualification",
|
|
229
|
-
"form_responses",
|
|
230
|
-
"form_response_values",
|
|
231
|
-
"forms",
|
|
232
|
-
// SECURITY: Phase 3B additions - remaining critical tables
|
|
233
|
-
"invoice",
|
|
234
|
-
"line_item",
|
|
235
|
-
"credit_balance",
|
|
236
|
-
"payment_method",
|
|
237
|
-
"form_contexts",
|
|
238
|
-
"form_field_config",
|
|
239
|
-
"form_fields",
|
|
240
|
-
"cake_delivery",
|
|
241
|
-
"cake_diettype",
|
|
242
|
-
"cake_diner",
|
|
243
|
-
"cake_dish",
|
|
244
|
-
"cake_item",
|
|
245
|
-
"cake_logistics",
|
|
246
|
-
"cake_mealplan",
|
|
247
|
-
"cake_package",
|
|
248
|
-
"cake_recipe",
|
|
249
|
-
"cake_supplier",
|
|
250
|
-
"cake_supply",
|
|
251
|
-
"cake_unit",
|
|
252
|
-
"event_app_access",
|
|
253
|
-
"base_application",
|
|
254
|
-
"base_questions"
|
|
255
|
-
];
|
|
256
|
-
if (!bypassOrganisationFilter && organisationId && tablesWithOrganisation.includes(table)) {
|
|
257
|
-
query = query.eq("organisation_id", organisationId);
|
|
258
|
-
}
|
|
259
|
-
Object.entries(filters).forEach(([key, value]) => {
|
|
260
|
-
if (value !== void 0 && value !== null) {
|
|
261
|
-
query = query.eq(key, value);
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
const { error } = await query;
|
|
265
|
-
if (error) {
|
|
266
|
-
logger.error("useSecureDataAccess", "Delete failed", { table, filters, error });
|
|
267
|
-
throw error;
|
|
268
|
-
}
|
|
269
|
-
}, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, isSuperAdmin]);
|
|
270
|
-
const secureRpc = useCallback(async (functionName, params = {}) => {
|
|
271
|
-
validateContext();
|
|
272
|
-
const bypassOrganisationFilter = isSuperAdmin;
|
|
273
|
-
const organisationId = bypassOrganisationFilter ? void 0 : getCurrentOrganisationId();
|
|
274
|
-
await setOrganisationContextInSession(organisationId);
|
|
275
|
-
const functionsWithPOrganisationId = [
|
|
276
|
-
"data_cake_diners_list",
|
|
277
|
-
"data_cake_mealplans_list"
|
|
278
|
-
];
|
|
279
|
-
const paramName = functionsWithPOrganisationId.includes(functionName) ? "p_organisation_id" : "organisation_id";
|
|
280
|
-
const functionsNeedingEventId = [
|
|
281
|
-
"data_cake_items_list",
|
|
282
|
-
"data_cake_packages_list",
|
|
283
|
-
"data_cake_suppliers_list",
|
|
284
|
-
"data_cake_diettypes_list",
|
|
285
|
-
"data_cake_mealtypes_list",
|
|
286
|
-
"data_cake_diners_list",
|
|
287
|
-
"data_cake_mealplans_list",
|
|
288
|
-
"data_cake_dishes_list",
|
|
289
|
-
"data_cake_recipes_list",
|
|
290
|
-
"data_cake_meals_list",
|
|
291
|
-
"data_cake_units_list",
|
|
292
|
-
"data_cake_orders_list",
|
|
293
|
-
"app_cake_item_create",
|
|
294
|
-
"app_cake_item_update",
|
|
295
|
-
"app_cake_package_create",
|
|
296
|
-
"app_cake_package_update",
|
|
297
|
-
"app_cake_supplier_create",
|
|
298
|
-
"app_cake_supplier_update",
|
|
299
|
-
"app_cake_supplier_delete",
|
|
300
|
-
"app_cake_meal_create",
|
|
301
|
-
"app_cake_meal_update",
|
|
302
|
-
"app_cake_meal_delete",
|
|
303
|
-
"app_cake_unit_create",
|
|
304
|
-
"app_cake_unit_update",
|
|
305
|
-
"app_cake_unit_delete",
|
|
306
|
-
"app_cake_delivery_upsert"
|
|
307
|
-
];
|
|
308
|
-
const secureParams = {};
|
|
309
|
-
const functionsWithEventIdFirst = [
|
|
310
|
-
"data_cake_meals_list",
|
|
311
|
-
"data_cake_units_list"
|
|
312
|
-
];
|
|
313
|
-
if (user?.id) {
|
|
314
|
-
secureParams.p_user_id = user.id;
|
|
315
|
-
}
|
|
316
|
-
if (!bypassOrganisationFilter && organisationId) {
|
|
317
|
-
secureParams[paramName] = organisationId;
|
|
318
|
-
} else if (organisationId && !(paramName in params)) {
|
|
319
|
-
secureParams[paramName] = organisationId;
|
|
320
|
-
}
|
|
321
|
-
if (functionsNeedingEventId.includes(functionName) && selectedEvent?.event_id) {
|
|
322
|
-
secureParams.p_event_id = selectedEvent.event_id;
|
|
323
|
-
}
|
|
324
|
-
Object.assign(secureParams, params);
|
|
325
|
-
if (functionsNeedingEventId.includes(functionName) && selectedEvent?.event_id) {
|
|
326
|
-
secureParams.p_event_id = selectedEvent.event_id;
|
|
327
|
-
}
|
|
328
|
-
const { data, error } = await supabase.rpc(functionName, secureParams);
|
|
329
|
-
if (error) {
|
|
330
|
-
logger.error("useSecureDataAccess", "RPC failed", { functionName, params: secureParams, error });
|
|
331
|
-
throw error;
|
|
332
|
-
}
|
|
333
|
-
return data;
|
|
334
|
-
}, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, selectedEvent?.event_id, user?.id, isSuperAdmin]);
|
|
335
|
-
const [dataAccessHistory, setDataAccessHistory] = useState([]);
|
|
336
|
-
const [isStrictMode] = useState(true);
|
|
337
|
-
const [isAuditLogEnabled] = useState(true);
|
|
338
|
-
const isDataAccessAllowed = useCallback((table, operation) => {
|
|
339
|
-
if (!user?.id) return false;
|
|
340
|
-
const permission = `${operation}:data.${table}`;
|
|
341
|
-
return true;
|
|
342
|
-
}, [user?.id]);
|
|
343
|
-
const getDataAccessPermissions = useCallback(() => {
|
|
344
|
-
if (!user?.id) return {};
|
|
345
|
-
return {};
|
|
346
|
-
}, [user?.id]);
|
|
347
|
-
const getDataAccessHistory = useCallback(() => {
|
|
348
|
-
return [...dataAccessHistory];
|
|
349
|
-
}, [dataAccessHistory]);
|
|
350
|
-
const clearDataAccessHistory = useCallback(() => {
|
|
351
|
-
setDataAccessHistory([]);
|
|
352
|
-
}, []);
|
|
353
|
-
const validateDataAccess = useCallback((table, operation) => {
|
|
354
|
-
if (!user?.id) return false;
|
|
355
|
-
try {
|
|
356
|
-
validateContext();
|
|
357
|
-
} catch (error) {
|
|
358
|
-
logger.error("useSecureDataAccess", "Organisation context validation failed", { table, operation, error });
|
|
359
|
-
return false;
|
|
360
|
-
}
|
|
361
|
-
return isDataAccessAllowed(table, operation);
|
|
362
|
-
}, [user?.id, validateContext, isDataAccessAllowed]);
|
|
363
|
-
const recordDataAccess = useCallback((table, operation, allowed, query, filters) => {
|
|
364
|
-
if (!isAuditLogEnabled || !user?.id) return;
|
|
365
|
-
const auditOrganisationId = getCurrentOrganisationId() || "super-admin-bypass";
|
|
366
|
-
const record = {
|
|
367
|
-
table,
|
|
368
|
-
operation,
|
|
369
|
-
userId: user.id,
|
|
370
|
-
organisationId: auditOrganisationId,
|
|
371
|
-
allowed,
|
|
372
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
373
|
-
query,
|
|
374
|
-
filters
|
|
375
|
-
};
|
|
376
|
-
setDataAccessHistory((prev) => {
|
|
377
|
-
const newHistory = [record, ...prev];
|
|
378
|
-
return newHistory.slice(0, 1e3);
|
|
379
|
-
});
|
|
380
|
-
if (isStrictMode && !allowed) {
|
|
381
|
-
logger.error("useSecureDataAccess", "STRICT MODE VIOLATION: User attempted data access without permission", {
|
|
382
|
-
table,
|
|
383
|
-
operation,
|
|
384
|
-
userId: user.id,
|
|
385
|
-
organisationId: auditOrganisationId,
|
|
386
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
}, [isAuditLogEnabled, isStrictMode, user?.id, getCurrentOrganisationId]);
|
|
390
|
-
return {
|
|
391
|
-
secureQuery,
|
|
392
|
-
secureInsert,
|
|
393
|
-
secureUpdate,
|
|
394
|
-
secureDelete,
|
|
395
|
-
secureRpc,
|
|
396
|
-
getCurrentOrganisationId,
|
|
397
|
-
validateContext,
|
|
398
|
-
// NEW: Phase 1 - Enhanced Security Features
|
|
399
|
-
isDataAccessAllowed,
|
|
400
|
-
getDataAccessPermissions,
|
|
401
|
-
isStrictMode,
|
|
402
|
-
isAuditLogEnabled,
|
|
403
|
-
getDataAccessHistory,
|
|
404
|
-
clearDataAccessHistory,
|
|
405
|
-
validateDataAccess
|
|
406
|
-
};
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
export {
|
|
410
|
-
useSecureDataAccess
|
|
411
|
-
};
|
|
412
|
-
//# sourceMappingURL=chunk-LFNCN2SP.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useSecureDataAccess.ts"],"sourcesContent":["/**\n * @file useSecureDataAccess Hook\n * @package @jmruthers/pace-core\n * @module Hooks/useSecureDataAccess\n * @since 0.4.0\n *\n * Hook for secure database operations with mandatory organisation context.\n * Ensures all data access is properly scoped to the user's current organisation.\n *\n * @example\n * ```tsx\n * function DataComponent() {\n * const { secureQuery, secureInsert, secureUpdate, secureDelete } = useSecureDataAccess();\n * \n * const loadData = async () => {\n * try {\n * // Automatically includes organisation_id filter\n * const events = await secureQuery('core_events', '*', { is_visible: true });\n * console.log('Organisation events:', events);\n * } catch (error) {\n * console.error('Failed to load data:', error);\n * }\n * };\n * \n * const createEvent = async (eventData) => {\n * try {\n * // Automatically sets organisation_id\n * const newEvent = await secureInsert('core_events', eventData);\n * console.log('Created event:', newEvent);\n * } catch (error) {\n * console.error('Failed to create event:', error);\n * }\n * };\n * \n * return (\n * <div>\n * <button onClick={loadData}>Load Data</button>\n * <button onClick={() => createEvent({ event_name: 'New Event' })}>\n * Create Event\n * </button>\n * </div>\n * );\n * }\n * ```\n *\n * @security\n * - All queries automatically include organisation_id filter\n * - Validates organisation context before any operation\n * - Prevents data leaks between organisations\n * - Error handling for security violations\n * - Type-safe database operations\n */\n\nimport { useCallback, useState, useContext } from 'react';\nimport { useUnifiedAuth } from '../providers';\nimport { useOrganisations } from './useOrganisations';\nimport { EventServiceContext } from '../providers/services/EventServiceProvider';\nimport { setOrganisationContext } from '../utils/context/organisationContext';\nimport { logger } from '../utils/core/logger';\nimport type { Permission } from '../rbac/types';\nimport type { OrganisationSecurityError } from '../types/organisation';\nimport { useOrganisationSecurity } from './useOrganisationSecurity';\nimport { useResolvedScope } from '../rbac/hooks/useResolvedScope';\n\nexport interface SecureDataAccessReturn {\n /** Execute a secure query with organisation filtering */\n secureQuery: <T = any>(\n table: string,\n columns: string,\n filters?: Record<string, any>,\n options?: {\n orderBy?: string;\n ascending?: boolean;\n limit?: number;\n offset?: number;\n }\n ) => Promise<T[]>;\n \n /** Execute a secure insert with organisation context */\n secureInsert: <T = any>(\n table: string,\n data: Record<string, any>\n ) => Promise<T>;\n \n /** Execute a secure update with organisation filtering */\n secureUpdate: <T = any>(\n table: string,\n data: Record<string, any>,\n filters: Record<string, any>\n ) => Promise<T[]>;\n \n /** Execute a secure delete with organisation filtering */\n secureDelete: (\n table: string,\n filters: Record<string, any>\n ) => Promise<void>;\n \n /** Execute a secure RPC call with organisation context */\n secureRpc: <T = any>(\n functionName: string,\n params?: Record<string, any>\n ) => Promise<T>;\n \n /** Get current organisation ID */\n getCurrentOrganisationId: () => string;\n \n /** Validate organisation context */\n validateContext: () => void;\n \n // NEW: Phase 1 - Enhanced Security Features\n /** Check if data access is allowed for a table and operation */\n isDataAccessAllowed: (table: string, operation: string) => boolean;\n \n /** Get all data access permissions for current user */\n getDataAccessPermissions: () => Record<string, string[]>;\n \n /** Check if strict mode is enabled */\n isStrictMode: boolean;\n \n /** Check if audit logging is enabled */\n isAuditLogEnabled: boolean;\n \n /** Get data access history */\n getDataAccessHistory: () => DataAccessRecord[];\n \n /** Clear data access history */\n clearDataAccessHistory: () => void;\n \n /** Validate data access attempt */\n validateDataAccess: (table: string, operation: string) => boolean;\n}\n\nexport interface DataAccessRecord {\n table: string;\n operation: string;\n userId: string;\n organisationId: string;\n allowed: boolean;\n timestamp: string;\n query?: string;\n filters?: Record<string, any>;\n}\n\n/**\n * Hook for secure data access with automatic organisation filtering\n * \n * All database operations automatically include organisation context:\n * - Queries filter by organisation_id\n * - Inserts include organisation_id\n * - Updates/deletes are scoped to organisation\n * - RPC calls include organisation_id parameter\n */\nexport function useSecureDataAccess(): SecureDataAccessReturn {\n const { supabase, user, session, selectedOrganisation, selectedEvent } = useUnifiedAuth();\n \n // Get selected event for event-scoped RPC calls\n // Use useContext directly to safely check if EventServiceProvider is available\n const eventServiceContext = useContext(EventServiceContext);\n const eventFromContext = eventServiceContext?.eventService?.getSelectedEvent() || null;\n const effectiveSelectedEvent = selectedEvent || eventFromContext;\n const { superAdminContext } = useOrganisationSecurity();\n const isSuperAdmin = superAdminContext.isSuperAdmin;\n\n // Use resolved scope to get organisationId (derived from event if needed)\n const { resolvedScope } = useResolvedScope({\n supabase,\n selectedOrganisationId: selectedOrganisation?.id || null,\n selectedEventId: effectiveSelectedEvent?.event_id || null\n });\n\n const validateContext = useCallback((): void => {\n if (!supabase) {\n throw new Error('No Supabase client available') as OrganisationSecurityError;\n }\n if (!user || !session) {\n throw new Error('User must be authenticated with valid session') as OrganisationSecurityError;\n }\n \n if (isSuperAdmin) {\n return;\n }\n \n if (!resolvedScope?.organisationId) {\n throw new Error('Organisation context is required for data access') as OrganisationSecurityError;\n }\n }, [supabase, user, session, resolvedScope, isSuperAdmin]);\n\n const getCurrentOrganisationId = useCallback((): string => {\n if (isSuperAdmin) {\n // For super admins, try to get org from resolved scope or selectedOrganisation\n return resolvedScope?.organisationId || selectedOrganisation?.id || '';\n }\n\n validateContext();\n return resolvedScope?.organisationId || '';\n }, [validateContext, resolvedScope, selectedOrganisation, isSuperAdmin]);\n\n // Set organisation context in database session\n const setOrganisationContextInSession = useCallback(async (organisationId?: string): Promise<void> => {\n if (!supabase || !organisationId) {\n return;\n }\n\n await setOrganisationContext(supabase, organisationId);\n }, [supabase]);\n\n const secureQuery = useCallback(async <T = any>(\n table: string,\n columns: string,\n filters: Record<string, any> = {},\n options: {\n orderBy?: string;\n ascending?: boolean;\n limit?: number;\n offset?: number;\n } = {}\n ): Promise<T[]> => {\n validateContext();\n const bypassOrganisationFilter = isSuperAdmin;\n const organisationId = bypassOrganisationFilter ? undefined : getCurrentOrganisationId();\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Build query with organisation filter\n let query = supabase!\n .from(table)\n .select(columns);\n\n // Add organisation filter only if table has organisation_id column\n // Defense in depth strategy:\n // - RLS policies are the primary security layer (cannot be bypassed)\n // - Application-level filtering adds an additional layer of protection\n const tablesWithOrganisation = [\n 'core_events', 'organisation_settings',\n 'rbac_event_app_roles', 'rbac_organisation_roles',\n // SECURITY: Phase 2 additions - complete organisation table mapping\n 'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',\n // SECURITY: Emergency additions for Phase 1 fixes\n 'cake_meal', 'cake_mealtype', 'core_person',\n // NOTE: core_member, medi_profile, core_contact, core_consent, core_identification, core_qualification\n // are now person-scoped (not organisation-scoped) - removed from this list\n // SECURITY: Phase 3A additions - medical and personal data\n // NOTE: medi_condition, medi_diet, medi_action_plan, medi_profile_versions are now person-scoped\n // (via medi_profile) - removed from this list\n // core_identification_type remains organisation-scoped (lookup table)\n 'core_identification_type',\n 'form_responses', 'form_response_values', 'forms',\n // SECURITY: Phase 3B additions - remaining critical tables\n 'invoice', 'line_item', 'credit_balance', 'payment_method',\n 'form_contexts', 'form_field_config', 'form_fields',\n 'cake_delivery', 'cake_diettype', 'cake_diner', 'cake_dish', 'cake_item', \n 'cake_logistics', 'cake_mealplan', 'cake_package', 'cake_recipe', 'cake_supplier', \n 'cake_supply', 'cake_unit', 'event_app_access', 'base_application', 'base_questions',\n // rbac_user_profiles has organisation_id but uses conditional filtering\n 'rbac_user_profiles'\n ];\n \n // Apply organisation filtering based on table and super admin status\n if (!bypassOrganisationFilter && organisationId && tablesWithOrganisation.includes(table)) {\n // For rbac_user_profiles: Super admins see all (no filter), non-super-admins get filtered (defense in depth)\n // For other tables: Always apply filter\n if (table === 'rbac_user_profiles' && isSuperAdmin) {\n // Super admin: No org filter - RLS handles access control\n // This allows super admins to see all users across all organisations\n } else {\n // Non-super-admin OR other tables: Apply org filter as defense in depth\n query = query.eq('organisation_id', organisationId);\n }\n }\n\n // Apply additional filters\n Object.entries(filters).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n // Handle qualified column names (e.g., 'users.role')\n const columnName = key.includes('.') ? key.split('.').pop()! : key;\n query = query.eq(columnName, value);\n }\n });\n\n // Apply options\n if (options.orderBy) {\n // Only use the column name, not a qualified name\n const orderByColumn = options.orderBy.split('.').pop();\n if (orderByColumn) {\n query = query.order(orderByColumn, { ascending: options.ascending ?? true });\n }\n }\n \n if (options.limit) {\n query = query.limit(options.limit);\n }\n \n if (options.offset) {\n query = query.range(options.offset, options.offset + (options.limit || 100) - 1);\n }\n\n const { data, error } = await query;\n \n if (error) {\n logger.error('useSecureDataAccess', 'Query failed', { table, columns, filters, error });\n // NEW: Phase 1 - Record failed data access attempt\n recordDataAccess(table, 'read', false, `SELECT ${columns} FROM ${table}`, filters);\n throw error;\n }\n\n // NEW: Phase 1 - Record successful data access attempt\n recordDataAccess(table, 'read', true, `SELECT ${columns} FROM ${table}`, filters);\n\n return (data as T[]) || [];\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, isSuperAdmin]);\n\n const secureInsert = useCallback(async <T = any>(\n table: string,\n data: Record<string, any>\n ): Promise<T> => {\n validateContext();\n const bypassOrganisationFilter = isSuperAdmin;\n const organisationId = bypassOrganisationFilter ? undefined : getCurrentOrganisationId();\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Ensure organisation_id is set\n const secureData = bypassOrganisationFilter\n ? { ...data }\n : {\n ...data,\n organisation_id: organisationId\n };\n\n const { data: insertData, error } = await supabase!\n .from(table)\n .insert(secureData)\n .select()\n .single();\n\n if (error) {\n logger.error('useSecureDataAccess', 'Insert failed', { table, data: secureData, error });\n throw error;\n }\n\n return insertData as T;\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, isSuperAdmin]);\n\n const secureUpdate = useCallback(async <T = any>(\n table: string,\n data: Record<string, any>,\n filters: Record<string, any>\n ): Promise<T[]> => {\n validateContext();\n const bypassOrganisationFilter = isSuperAdmin;\n const organisationId = bypassOrganisationFilter ? undefined : getCurrentOrganisationId();\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Filter out organisation_id from data to prevent manipulation\n const { organisation_id, ...secureData } = data;\n \n // Build update query with organisation filter\n let query = supabase!\n .from(table)\n .update(secureData);\n\n // Add organisation filter only if table has organisation_id column\n const tablesWithOrganisation = [\n 'core_events', 'organisation_settings',\n 'rbac_event_app_roles', 'rbac_organisation_roles',\n // SECURITY: Phase 2 additions - complete organisation table mapping\n 'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',\n // SECURITY: Emergency additions for Phase 1 fixes\n 'cake_meal', 'cake_mealtype', 'core_person', 'core_member'\n ];\n \n if (!bypassOrganisationFilter && organisationId && tablesWithOrganisation.includes(table)) {\n query = query.eq('organisation_id', organisationId);\n }\n\n // Apply filters\n Object.entries(filters).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n query = query.eq(key, value);\n }\n });\n\n const { data: updateData, error } = await query.select();\n\n if (error) {\n logger.error('useSecureDataAccess', 'Update failed', { table, data: secureData, filters, error });\n throw error;\n }\n\n return (updateData as T[]) || [];\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, isSuperAdmin]);\n\n const secureDelete = useCallback(async (\n table: string,\n filters: Record<string, any>\n ): Promise<void> => {\n validateContext();\n const bypassOrganisationFilter = isSuperAdmin;\n const organisationId = bypassOrganisationFilter ? undefined : getCurrentOrganisationId();\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Build delete query with organisation filter\n let query = supabase!\n .from(table)\n .delete();\n\n // Add organisation filter only if table has organisation_id column\n const tablesWithOrganisation = [\n 'core_events', 'organisation_settings',\n 'rbac_event_app_roles', 'rbac_organisation_roles',\n // SECURITY: Phase 2 additions - complete organisation table mapping\n 'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',\n // SECURITY: Emergency additions for Phase 1 fixes\n 'cake_meal', 'cake_mealtype', 'core_person', 'core_member',\n // SECURITY: Phase 3A additions - medical and personal data\n 'medi_profile', 'medi_condition', 'medi_diet', 'medi_action_plan', 'medi_profile_versions',\n 'core_consent', 'core_contact', 'core_identification', 'core_identification_type', 'core_qualification',\n 'form_responses', 'form_response_values', 'forms',\n // SECURITY: Phase 3B additions - remaining critical tables\n 'invoice', 'line_item', 'credit_balance', 'payment_method',\n 'form_contexts', 'form_field_config', 'form_fields',\n 'cake_delivery', 'cake_diettype', 'cake_diner', 'cake_dish', 'cake_item', \n 'cake_logistics', 'cake_mealplan', 'cake_package', 'cake_recipe', 'cake_supplier', \n 'cake_supply', 'cake_unit', 'event_app_access', 'base_application', 'base_questions'\n ];\n \n if (!bypassOrganisationFilter && organisationId && tablesWithOrganisation.includes(table)) {\n query = query.eq('organisation_id', organisationId);\n }\n\n // Apply filters\n Object.entries(filters).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n query = query.eq(key, value);\n }\n });\n\n const { error } = await query;\n\n if (error) {\n logger.error('useSecureDataAccess', 'Delete failed', { table, filters, error });\n throw error;\n }\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, isSuperAdmin]);\n\n const secureRpc = useCallback(async <T = any>(\n functionName: string,\n params: Record<string, any> = {}\n ): Promise<T> => {\n validateContext();\n const bypassOrganisationFilter = isSuperAdmin;\n const organisationId = bypassOrganisationFilter ? undefined : getCurrentOrganisationId();\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Include organisation_id in RPC parameters\n // Some functions use p_organisation_id instead of organisation_id (to avoid conflicts with RETURNS TABLE columns)\n const functionsWithPOrganisationId = [\n 'data_cake_diners_list',\n 'data_cake_mealplans_list'\n ];\n \n const paramName = functionsWithPOrganisationId.includes(functionName) \n ? 'p_organisation_id' \n : 'organisation_id';\n \n // Functions that need p_event_id for event-app role permission checks\n // Note: Even org-scoped functions (like items, packages, suppliers) need event_id\n // for permission checks when users have event-app roles\n const functionsNeedingEventId = [\n 'data_cake_items_list',\n 'data_cake_packages_list',\n 'data_cake_suppliers_list',\n 'data_cake_diettypes_list',\n 'data_cake_mealtypes_list',\n 'data_cake_diners_list',\n 'data_cake_mealplans_list',\n 'data_cake_dishes_list',\n 'data_cake_recipes_list',\n 'data_cake_meals_list',\n 'data_cake_units_list',\n 'data_cake_orders_list',\n 'app_cake_item_create',\n 'app_cake_item_update',\n 'app_cake_package_create',\n 'app_cake_package_update',\n 'app_cake_supplier_create',\n 'app_cake_supplier_update',\n 'app_cake_supplier_delete',\n 'app_cake_meal_create',\n 'app_cake_meal_update',\n 'app_cake_meal_delete',\n 'app_cake_unit_create',\n 'app_cake_unit_update',\n 'app_cake_unit_delete',\n 'app_cake_delivery_upsert'\n ];\n \n // Build secureParams with correct parameter order\n // For functions that require p_event_id as first parameter, ensure it's first\n const secureParams: Record<string, any> = {};\n \n // Functions where p_event_id is the FIRST required parameter (no default)\n const functionsWithEventIdFirst = [\n 'data_cake_meals_list',\n 'data_cake_units_list'\n ];\n \n // Add p_user_id explicitly for functions that need it (even though it has a default)\n // This ensures parameter matching works correctly\n if (user?.id) {\n secureParams.p_user_id = user.id;\n }\n \n // Add organisation_id parameter when needed\n if (!bypassOrganisationFilter && organisationId) {\n secureParams[paramName] = organisationId;\n } else if (organisationId && !(paramName in params)) {\n // Default to the current organisation if caller didn't specify one\n secureParams[paramName] = organisationId;\n }\n \n // Add p_event_id if function needs it and event is selected\n // CRITICAL: This must be added AFTER organisation_id but BEFORE caller params\n // to ensure it's not overwritten. For data_cake_items_list, p_event_id is the 3rd param.\n if (functionsNeedingEventId.includes(functionName) && selectedEvent?.event_id) {\n secureParams.p_event_id = selectedEvent.event_id;\n }\n \n // Add any other params passed by caller (limit, offset, etc.)\n // NOTE: This will NOT overwrite p_event_id if caller passes it, but we want to ensure\n // our value takes precedence if event is selected\n Object.assign(secureParams, params);\n \n // Ensure p_event_id is set if needed (after Object.assign, so it overrides caller params)\n if (functionsNeedingEventId.includes(functionName) && selectedEvent?.event_id) {\n secureParams.p_event_id = selectedEvent.event_id;\n }\n\n const { data, error } = await supabase!.rpc(functionName, secureParams);\n\n if (error) {\n logger.error('useSecureDataAccess', 'RPC failed', { functionName, params: secureParams, error });\n throw error;\n }\n\n return data as T;\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, selectedEvent?.event_id, user?.id, isSuperAdmin]);\n\n // NEW: Phase 1 - Enhanced Security Features\n const [dataAccessHistory, setDataAccessHistory] = useState<DataAccessRecord[]>([]);\n const [isStrictMode] = useState(true); // Always enabled in Phase 1\n const [isAuditLogEnabled] = useState(true); // Always enabled in Phase 1\n\n // Check if data access is allowed for a table and operation\n const isDataAccessAllowed = useCallback((table: string, operation: string): boolean => {\n if (!user?.id) return false;\n \n // Use the existing RBAC system to check data access permissions\n // This is a synchronous check for the context - actual permission checking\n // happens in the secure data operations using the RBAC engine\n const permission = `${operation}:data.${table}` as Permission;\n \n // For now, we'll return true and let the secure data operations\n // handle the actual permission checking asynchronously\n // This context is mainly for tracking and audit purposes\n return true;\n }, [user?.id]);\n\n // Get all data access permissions for current user\n const getDataAccessPermissions = useCallback((): Record<string, string[]> => {\n if (!user?.id) return {};\n \n // For now, return empty object - this will be enhanced with actual permission checking\n // when we integrate with the existing RBAC system\n return {};\n }, [user?.id]);\n\n // Get data access history\n const getDataAccessHistory = useCallback((): DataAccessRecord[] => {\n return [...dataAccessHistory];\n }, [dataAccessHistory]);\n\n // Clear data access history\n const clearDataAccessHistory = useCallback(() => {\n setDataAccessHistory([]);\n }, []);\n\n // Validate data access attempt\n const validateDataAccess = useCallback((table: string, operation: string): boolean => {\n if (!user?.id) return false;\n \n // Validate organisation context\n try {\n validateContext();\n } catch (error) {\n logger.error('useSecureDataAccess', 'Organisation context validation failed', { table, operation, error });\n return false;\n }\n \n return isDataAccessAllowed(table, operation);\n }, [user?.id, validateContext, isDataAccessAllowed]);\n\n // Record data access attempt\n const recordDataAccess = useCallback((\n table: string,\n operation: string,\n allowed: boolean,\n query?: string,\n filters?: Record<string, any>\n ) => {\n if (!isAuditLogEnabled || !user?.id) return;\n const auditOrganisationId = getCurrentOrganisationId() || 'super-admin-bypass';\n\n const record: DataAccessRecord = {\n table,\n operation,\n userId: user.id,\n organisationId: auditOrganisationId,\n allowed,\n timestamp: new Date().toISOString(),\n query,\n filters\n };\n \n setDataAccessHistory(prev => {\n const newHistory = [record, ...prev];\n return newHistory.slice(0, 1000); // Keep last 1000 records\n });\n \n if (isStrictMode && !allowed) {\n logger.error('useSecureDataAccess', 'STRICT MODE VIOLATION: User attempted data access without permission', {\n table,\n operation,\n userId: user.id,\n organisationId: auditOrganisationId,\n timestamp: new Date().toISOString()\n });\n }\n }, [isAuditLogEnabled, isStrictMode, user?.id, getCurrentOrganisationId]);\n\n return {\n secureQuery,\n secureInsert,\n secureUpdate,\n secureDelete,\n secureRpc,\n getCurrentOrganisationId,\n validateContext,\n // NEW: Phase 1 - Enhanced Security Features\n isDataAccessAllowed,\n getDataAccessPermissions,\n isStrictMode,\n isAuditLogEnabled,\n getDataAccessHistory,\n clearDataAccessHistory,\n validateDataAccess\n };\n} "],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useSecureDataAccess.ts"],"sourcesContent":["/**\n * @file useSecureDataAccess Hook\n * @package @jmruthers/pace-core\n * @module Hooks/useSecureDataAccess\n * @since 0.4.0\n *\n * Hook for secure database operations with mandatory organisation context.\n * Ensures all data access is properly scoped to the user's current organisation.\n *\n * @example\n * ```tsx\n * function DataComponent() {\n * const { secureQuery, secureInsert, secureUpdate, secureDelete } = useSecureDataAccess();\n * \n * const loadData = async () => {\n * try {\n * // Automatically includes organisation_id filter\n * const events = await secureQuery('core_events', '*', { is_visible: true });\n * console.log('Organisation events:', events);\n * } catch (error) {\n * console.error('Failed to load data:', error);\n * }\n * };\n * \n * const createEvent = async (eventData) => {\n * try {\n * // Automatically sets organisation_id\n * const newEvent = await secureInsert('core_events', eventData);\n * console.log('Created event:', newEvent);\n * } catch (error) {\n * console.error('Failed to create event:', error);\n * }\n * };\n * \n * return (\n * <div>\n * <button onClick={loadData}>Load Data</button>\n * <button onClick={() => createEvent({ event_name: 'New Event' })}>\n * Create Event\n * </button>\n * </div>\n * );\n * }\n * ```\n *\n * @security\n * - All queries automatically include organisation_id filter\n * - Validates organisation context before any operation\n * - Prevents data leaks between organisations\n * - Error handling for security violations\n * - Type-safe database operations\n */\n\nimport { useCallback, useState, useContext } from 'react';\nimport { useUnifiedAuth } from '../providers';\nimport { useOrganisations } from './useOrganisations';\nimport { EventServiceContext } from '../providers/services/EventServiceProvider';\nimport { setOrganisationContext } from '../utils/context/organisationContext';\nimport { logger } from '../utils/core/logger';\nimport type { Permission } from '../rbac/types';\nimport type { OrganisationSecurityError } from '../types/organisation';\nimport { useOrganisationSecurity } from './useOrganisationSecurity';\nimport { useResolvedScope } from '../rbac/hooks/useResolvedScope';\n\nexport interface SecureDataAccessReturn {\n /** Execute a secure query with organisation filtering */\n secureQuery: <T = any>(\n table: string,\n columns: string,\n filters?: Record<string, any>,\n options?: {\n orderBy?: string;\n ascending?: boolean;\n limit?: number;\n offset?: number;\n }\n ) => Promise<T[]>;\n \n /** Execute a secure insert with organisation context */\n secureInsert: <T = any>(\n table: string,\n data: Record<string, any>\n ) => Promise<T>;\n \n /** Execute a secure update with organisation filtering */\n secureUpdate: <T = any>(\n table: string,\n data: Record<string, any>,\n filters: Record<string, any>\n ) => Promise<T[]>;\n \n /** Execute a secure delete with organisation filtering */\n secureDelete: (\n table: string,\n filters: Record<string, any>\n ) => Promise<void>;\n \n /** Execute a secure RPC call with organisation context */\n secureRpc: <T = any>(\n functionName: string,\n params?: Record<string, any>\n ) => Promise<T>;\n \n /** Get current organisation ID */\n getCurrentOrganisationId: () => string;\n \n /** Validate organisation context */\n validateContext: () => void;\n \n // NEW: Phase 1 - Enhanced Security Features\n /** Check if data access is allowed for a table and operation */\n isDataAccessAllowed: (table: string, operation: string) => boolean;\n \n /** Get all data access permissions for current user */\n getDataAccessPermissions: () => Record<string, string[]>;\n \n /** Check if strict mode is enabled */\n isStrictMode: boolean;\n \n /** Check if audit logging is enabled */\n isAuditLogEnabled: boolean;\n \n /** Get data access history */\n getDataAccessHistory: () => DataAccessRecord[];\n \n /** Clear data access history */\n clearDataAccessHistory: () => void;\n \n /** Validate data access attempt */\n validateDataAccess: (table: string, operation: string) => boolean;\n}\n\nexport interface DataAccessRecord {\n table: string;\n operation: string;\n userId: string;\n organisationId: string;\n allowed: boolean;\n timestamp: string;\n query?: string;\n filters?: Record<string, any>;\n}\n\n/**\n * Hook for secure data access with automatic organisation filtering\n * \n * All database operations automatically include organisation context:\n * - Queries filter by organisation_id\n * - Inserts include organisation_id\n * - Updates/deletes are scoped to organisation\n * - RPC calls include organisation_id parameter\n */\nexport function useSecureDataAccess(): SecureDataAccessReturn {\n const { supabase, user, session, selectedOrganisation, selectedEvent } = useUnifiedAuth();\n \n // Get selected event for event-scoped RPC calls\n // Use useContext directly to safely check if EventServiceProvider is available\n const eventServiceContext = useContext(EventServiceContext);\n const eventFromContext = eventServiceContext?.eventService?.getSelectedEvent() || null;\n const effectiveSelectedEvent = selectedEvent || eventFromContext;\n const { superAdminContext } = useOrganisationSecurity();\n const isSuperAdmin = superAdminContext.isSuperAdmin;\n\n // Use resolved scope to get organisationId (derived from event if needed)\n const { resolvedScope } = useResolvedScope({\n supabase,\n selectedOrganisationId: selectedOrganisation?.id || null,\n selectedEventId: effectiveSelectedEvent?.event_id || null\n });\n\n const validateContext = useCallback((): void => {\n if (!supabase) {\n throw new Error('No Supabase client available') as OrganisationSecurityError;\n }\n if (!user || !session) {\n throw new Error('User must be authenticated with valid session') as OrganisationSecurityError;\n }\n \n if (isSuperAdmin) {\n return;\n }\n \n if (!resolvedScope?.organisationId) {\n throw new Error('Organisation context is required for data access') as OrganisationSecurityError;\n }\n }, [supabase, user, session, resolvedScope, isSuperAdmin]);\n\n const getCurrentOrganisationId = useCallback((): string => {\n if (isSuperAdmin) {\n // For super admins, try to get org from resolved scope or selectedOrganisation\n return resolvedScope?.organisationId || selectedOrganisation?.id || '';\n }\n\n validateContext();\n return resolvedScope?.organisationId || '';\n }, [validateContext, resolvedScope, selectedOrganisation, isSuperAdmin]);\n\n // Set organisation context in database session\n const setOrganisationContextInSession = useCallback(async (organisationId?: string): Promise<void> => {\n if (!supabase || !organisationId) {\n return;\n }\n\n await setOrganisationContext(supabase, organisationId);\n }, [supabase]);\n\n const secureQuery = useCallback(async <T = any>(\n table: string,\n columns: string,\n filters: Record<string, any> = {},\n options: {\n orderBy?: string;\n ascending?: boolean;\n limit?: number;\n offset?: number;\n } = {}\n ): Promise<T[]> => {\n validateContext();\n const bypassOrganisationFilter = isSuperAdmin;\n const organisationId = bypassOrganisationFilter ? undefined : getCurrentOrganisationId();\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Build query with organisation filter\n let query = supabase!\n .from(table)\n .select(columns);\n\n // Add organisation filter only if table has organisation_id column\n // Defense in depth strategy:\n // - RLS policies are the primary security layer (cannot be bypassed)\n // - Application-level filtering adds an additional layer of protection\n const tablesWithOrganisation = [\n 'core_events', 'organisation_settings',\n 'rbac_event_app_roles', 'rbac_organisation_roles',\n // SECURITY: Phase 2 additions - complete organisation table mapping\n 'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',\n // SECURITY: Emergency additions for Phase 1 fixes\n 'cake_meal', 'cake_mealtype', 'core_person',\n // NOTE: core_member, medi_profile, core_contact, core_consent, core_identification, core_qualification\n // are now person-scoped (not organisation-scoped) - removed from this list\n // SECURITY: Phase 3A additions - medical and personal data\n // NOTE: medi_condition, medi_diet, medi_action_plan, medi_profile_versions are now person-scoped\n // (via medi_profile) - removed from this list\n // core_identification_type remains organisation-scoped (lookup table)\n 'core_identification_type',\n 'form_responses', 'form_response_values', 'forms',\n // SECURITY: Phase 3B additions - remaining critical tables\n 'invoice', 'line_item', 'credit_balance', 'payment_method',\n 'form_contexts', 'form_field_config', 'form_fields',\n 'cake_delivery', 'cake_diettype', 'cake_diner', 'cake_dish', 'cake_item', \n 'cake_logistics', 'cake_mealplan', 'cake_package', 'cake_recipe', 'cake_supplier', \n 'cake_supply', 'cake_unit', 'event_app_access', 'base_application', 'base_questions',\n // rbac_user_profiles has organisation_id but uses conditional filtering\n 'rbac_user_profiles'\n ];\n \n // Apply organisation filtering based on table and super admin status\n if (!bypassOrganisationFilter && organisationId && tablesWithOrganisation.includes(table)) {\n // For rbac_user_profiles: Super admins see all (no filter), non-super-admins get filtered (defense in depth)\n // For other tables: Always apply filter\n if (table === 'rbac_user_profiles' && isSuperAdmin) {\n // Super admin: No org filter - RLS handles access control\n // This allows super admins to see all users across all organisations\n } else {\n // Non-super-admin OR other tables: Apply org filter as defense in depth\n query = query.eq('organisation_id', organisationId);\n }\n }\n\n // Apply additional filters\n Object.entries(filters).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n // Handle qualified column names (e.g., 'users.role')\n const columnName = key.includes('.') ? key.split('.').pop()! : key;\n query = query.eq(columnName, value);\n }\n });\n\n // Apply options\n if (options.orderBy) {\n // Only use the column name, not a qualified name\n const orderByColumn = options.orderBy.split('.').pop();\n if (orderByColumn) {\n query = query.order(orderByColumn, { ascending: options.ascending ?? true });\n }\n }\n \n if (options.limit) {\n query = query.limit(options.limit);\n }\n \n if (options.offset) {\n query = query.range(options.offset, options.offset + (options.limit || 100) - 1);\n }\n\n const { data, error } = await query;\n \n if (error) {\n logger.error('useSecureDataAccess', 'Query failed', { table, columns, filters, error });\n // NEW: Phase 1 - Record failed data access attempt\n recordDataAccess(table, 'read', false, `SELECT ${columns} FROM ${table}`, filters);\n throw error;\n }\n\n // NEW: Phase 1 - Record successful data access attempt\n recordDataAccess(table, 'read', true, `SELECT ${columns} FROM ${table}`, filters);\n\n return (data as T[]) || [];\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, isSuperAdmin]);\n\n const secureInsert = useCallback(async <T = any>(\n table: string,\n data: Record<string, any>\n ): Promise<T> => {\n validateContext();\n const bypassOrganisationFilter = isSuperAdmin;\n const organisationId = bypassOrganisationFilter ? undefined : getCurrentOrganisationId();\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Ensure organisation_id is set\n const secureData = bypassOrganisationFilter\n ? { ...data }\n : {\n ...data,\n organisation_id: organisationId\n };\n\n const { data: insertData, error } = await supabase!\n .from(table)\n .insert(secureData)\n .select()\n .single();\n\n if (error) {\n logger.error('useSecureDataAccess', 'Insert failed', { table, data: secureData, error });\n throw error;\n }\n\n return insertData as T;\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, isSuperAdmin]);\n\n const secureUpdate = useCallback(async <T = any>(\n table: string,\n data: Record<string, any>,\n filters: Record<string, any>\n ): Promise<T[]> => {\n validateContext();\n const bypassOrganisationFilter = isSuperAdmin;\n const organisationId = bypassOrganisationFilter ? undefined : getCurrentOrganisationId();\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Filter out organisation_id from data to prevent manipulation\n const { organisation_id, ...secureData } = data;\n \n // Build update query with organisation filter\n let query = supabase!\n .from(table)\n .update(secureData);\n\n // Add organisation filter only if table has organisation_id column\n const tablesWithOrganisation = [\n 'core_events', 'organisation_settings',\n 'rbac_event_app_roles', 'rbac_organisation_roles',\n // SECURITY: Phase 2 additions - complete organisation table mapping\n 'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',\n // SECURITY: Emergency additions for Phase 1 fixes\n 'cake_meal', 'cake_mealtype', 'core_person', 'core_member'\n ];\n \n if (!bypassOrganisationFilter && organisationId && tablesWithOrganisation.includes(table)) {\n query = query.eq('organisation_id', organisationId);\n }\n\n // Apply filters\n Object.entries(filters).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n query = query.eq(key, value);\n }\n });\n\n const { data: updateData, error } = await query.select();\n\n if (error) {\n logger.error('useSecureDataAccess', 'Update failed', { table, data: secureData, filters, error });\n throw error;\n }\n\n return (updateData as T[]) || [];\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, isSuperAdmin]);\n\n const secureDelete = useCallback(async (\n table: string,\n filters: Record<string, any>\n ): Promise<void> => {\n validateContext();\n const bypassOrganisationFilter = isSuperAdmin;\n const organisationId = bypassOrganisationFilter ? undefined : getCurrentOrganisationId();\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Build delete query with organisation filter\n let query = supabase!\n .from(table)\n .delete();\n\n // Add organisation filter only if table has organisation_id column\n const tablesWithOrganisation = [\n 'core_events', 'organisation_settings',\n 'rbac_event_app_roles', 'rbac_organisation_roles',\n // SECURITY: Phase 2 additions - complete organisation table mapping\n 'organisation_audit_log', 'organisation_invitations', 'organisation_app_access',\n // SECURITY: Emergency additions for Phase 1 fixes\n 'cake_meal', 'cake_mealtype', 'core_person', 'core_member',\n // SECURITY: Phase 3A additions - medical and personal data\n 'medi_profile', 'medi_condition', 'medi_diet', 'medi_action_plan', 'medi_profile_versions',\n 'core_consent', 'core_contact', 'core_identification', 'core_identification_type', 'core_qualification',\n 'form_responses', 'form_response_values', 'forms',\n // SECURITY: Phase 3B additions - remaining critical tables\n 'invoice', 'line_item', 'credit_balance', 'payment_method',\n 'form_contexts', 'form_field_config', 'form_fields',\n 'cake_delivery', 'cake_diettype', 'cake_diner', 'cake_dish', 'cake_item', \n 'cake_logistics', 'cake_mealplan', 'cake_package', 'cake_recipe', 'cake_supplier', \n 'cake_supply', 'cake_unit', 'event_app_access', 'base_application', 'base_questions'\n ];\n \n if (!bypassOrganisationFilter && organisationId && tablesWithOrganisation.includes(table)) {\n query = query.eq('organisation_id', organisationId);\n }\n\n // Apply filters\n Object.entries(filters).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n query = query.eq(key, value);\n }\n });\n\n const { error } = await query;\n\n if (error) {\n logger.error('useSecureDataAccess', 'Delete failed', { table, filters, error });\n throw error;\n }\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, isSuperAdmin]);\n\n const secureRpc = useCallback(async <T = any>(\n functionName: string,\n params: Record<string, any> = {}\n ): Promise<T> => {\n validateContext();\n const bypassOrganisationFilter = isSuperAdmin;\n const organisationId = bypassOrganisationFilter ? undefined : getCurrentOrganisationId();\n\n // Set organisation context in database session\n await setOrganisationContextInSession(organisationId);\n\n // Include organisation_id in RPC parameters\n // Some functions use p_organisation_id instead of organisation_id (to avoid conflicts with RETURNS TABLE columns)\n const functionsWithPOrganisationId = [\n 'data_cake_diners_list',\n 'data_cake_mealplans_list'\n ];\n \n const paramName = functionsWithPOrganisationId.includes(functionName) \n ? 'p_organisation_id' \n : 'organisation_id';\n \n // Functions that need p_event_id for event-app role permission checks\n // Note: Even org-scoped functions (like items, packages, suppliers) need event_id\n // for permission checks when users have event-app roles\n const functionsNeedingEventId = [\n 'data_cake_items_list',\n 'data_cake_packages_list',\n 'data_cake_suppliers_list',\n 'data_cake_diettypes_list',\n 'data_cake_mealtypes_list',\n 'data_cake_diners_list',\n 'data_cake_mealplans_list',\n 'data_cake_dishes_list',\n 'data_cake_recipes_list',\n 'data_cake_meals_list',\n 'data_cake_units_list',\n 'data_cake_orders_list',\n 'app_cake_item_create',\n 'app_cake_item_update',\n 'app_cake_package_create',\n 'app_cake_package_update',\n 'app_cake_supplier_create',\n 'app_cake_supplier_update',\n 'app_cake_supplier_delete',\n 'app_cake_meal_create',\n 'app_cake_meal_update',\n 'app_cake_meal_delete',\n 'app_cake_unit_create',\n 'app_cake_unit_update',\n 'app_cake_unit_delete',\n 'app_cake_delivery_upsert'\n ];\n \n // Build secureParams with correct parameter order\n // For functions that require p_event_id as first parameter, ensure it's first\n const secureParams: Record<string, any> = {};\n \n // Functions where p_event_id is the FIRST required parameter (no default)\n const functionsWithEventIdFirst = [\n 'data_cake_meals_list',\n 'data_cake_units_list'\n ];\n \n // Add p_user_id explicitly for functions that need it (even though it has a default)\n // This ensures parameter matching works correctly\n if (user?.id) {\n secureParams.p_user_id = user.id;\n }\n \n // Add organisation_id parameter when needed\n if (!bypassOrganisationFilter && organisationId) {\n secureParams[paramName] = organisationId;\n } else if (organisationId && !(paramName in params)) {\n // Default to the current organisation if caller didn't specify one\n secureParams[paramName] = organisationId;\n }\n \n // Add p_event_id if function needs it and event is selected\n // CRITICAL: This must be added AFTER organisation_id but BEFORE caller params\n // to ensure it's not overwritten. For data_cake_items_list, p_event_id is the 3rd param.\n if (functionsNeedingEventId.includes(functionName) && selectedEvent?.event_id) {\n secureParams.p_event_id = selectedEvent.event_id;\n }\n \n // Add any other params passed by caller (limit, offset, etc.)\n // NOTE: This will NOT overwrite p_event_id if caller passes it, but we want to ensure\n // our value takes precedence if event is selected\n Object.assign(secureParams, params);\n \n // Ensure p_event_id is set if needed (after Object.assign, so it overrides caller params)\n if (functionsNeedingEventId.includes(functionName) && selectedEvent?.event_id) {\n secureParams.p_event_id = selectedEvent.event_id;\n }\n\n const { data, error } = await supabase!.rpc(functionName, secureParams);\n\n if (error) {\n logger.error('useSecureDataAccess', 'RPC failed', { functionName, params: secureParams, error });\n throw error;\n }\n\n return data as T;\n }, [validateContext, getCurrentOrganisationId, setOrganisationContextInSession, supabase, selectedEvent?.event_id, user?.id, isSuperAdmin]);\n\n // NEW: Phase 1 - Enhanced Security Features\n const [dataAccessHistory, setDataAccessHistory] = useState<DataAccessRecord[]>([]);\n const [isStrictMode] = useState(true); // Always enabled in Phase 1\n const [isAuditLogEnabled] = useState(true); // Always enabled in Phase 1\n\n // Check if data access is allowed for a table and operation\n const isDataAccessAllowed = useCallback((table: string, operation: string): boolean => {\n if (!user?.id) return false;\n \n // Use the existing RBAC system to check data access permissions\n // This is a synchronous check for the context - actual permission checking\n // happens in the secure data operations using the RBAC engine\n const permission = `${operation}:data.${table}` as Permission;\n \n // For now, we'll return true and let the secure data operations\n // handle the actual permission checking asynchronously\n // This context is mainly for tracking and audit purposes\n return true;\n }, [user?.id]);\n\n // Get all data access permissions for current user\n const getDataAccessPermissions = useCallback((): Record<string, string[]> => {\n if (!user?.id) return {};\n \n // For now, return empty object - this will be enhanced with actual permission checking\n // when we integrate with the existing RBAC system\n return {};\n }, [user?.id]);\n\n // Get data access history\n const getDataAccessHistory = useCallback((): DataAccessRecord[] => {\n return [...dataAccessHistory];\n }, [dataAccessHistory]);\n\n // Clear data access history\n const clearDataAccessHistory = useCallback(() => {\n setDataAccessHistory([]);\n }, []);\n\n // Validate data access attempt\n const validateDataAccess = useCallback((table: string, operation: string): boolean => {\n if (!user?.id) return false;\n \n // Validate organisation context\n try {\n validateContext();\n } catch (error) {\n logger.error('useSecureDataAccess', 'Organisation context validation failed', { table, operation, error });\n return false;\n }\n \n return isDataAccessAllowed(table, operation);\n }, [user?.id, validateContext, isDataAccessAllowed]);\n\n // Record data access attempt\n const recordDataAccess = useCallback((\n table: string,\n operation: string,\n allowed: boolean,\n query?: string,\n filters?: Record<string, any>\n ) => {\n if (!isAuditLogEnabled || !user?.id) return;\n const auditOrganisationId = getCurrentOrganisationId() || 'super-admin-bypass';\n\n const record: DataAccessRecord = {\n table,\n operation,\n userId: user.id,\n organisationId: auditOrganisationId,\n allowed,\n timestamp: new Date().toISOString(),\n query,\n filters\n };\n \n setDataAccessHistory(prev => {\n const newHistory = [record, ...prev];\n return newHistory.slice(0, 1000); // Keep last 1000 records\n });\n \n if (isStrictMode && !allowed) {\n logger.error('useSecureDataAccess', 'STRICT MODE VIOLATION: User attempted data access without permission', {\n table,\n operation,\n userId: user.id,\n organisationId: auditOrganisationId,\n timestamp: new Date().toISOString()\n });\n }\n }, [isAuditLogEnabled, isStrictMode, user?.id, getCurrentOrganisationId]);\n\n return {\n secureQuery,\n secureInsert,\n secureUpdate,\n secureDelete,\n secureRpc,\n getCurrentOrganisationId,\n validateContext,\n // NEW: Phase 1 - Enhanced Security Features\n isDataAccessAllowed,\n getDataAccessPermissions,\n isStrictMode,\n isAuditLogEnabled,\n getDataAccessHistory,\n clearDataAccessHistory,\n validateDataAccess\n };\n} "],"mappings":"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","names":[]}
|
package/dist/chunk-LMC26NLJ 2.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
// src/utils/validation/schema.ts
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
function pickSchema(schema, keys) {
|
|
4
|
-
const shape = Object.entries(schema.shape).filter(([key]) => keys.includes(key)).reduce((acc, [key, value]) => {
|
|
5
|
-
acc[key] = value;
|
|
6
|
-
return acc;
|
|
7
|
-
}, {});
|
|
8
|
-
return z.object(shape);
|
|
9
|
-
}
|
|
10
|
-
function combineSchemas(schemas) {
|
|
11
|
-
return schemas.reduce(
|
|
12
|
-
(merged, schema) => merged.merge(schema),
|
|
13
|
-
z.object({})
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// src/types/validation.ts
|
|
18
|
-
import { z as z2 } from "zod";
|
|
19
|
-
var emailSchema = z2.string().email("Please enter a valid email address");
|
|
20
|
-
var nameSchema = z2.string().min(1, "Name is required").max(100, "Name must be less than 100 characters");
|
|
21
|
-
var phoneSchema = z2.string().regex(/^\+?[\d\s\-\(\)]+$/, "Please enter a valid phone number");
|
|
22
|
-
var urlSchema = z2.string().url("Please enter a valid URL");
|
|
23
|
-
var dateSchema = z2.string().refine((date) => !isNaN(Date.parse(date)), "Please enter a valid date");
|
|
24
|
-
var passwordSchema = z2.string().min(8, "Password must be at least 8 characters").regex(/[A-Z]/, "Password must contain at least one uppercase letter").regex(/[a-z]/, "Password must contain at least one lowercase letter").regex(/[0-9]/, "Password must contain at least one number");
|
|
25
|
-
var securePasswordSchema = passwordSchema.regex(/[!@#$%^&*(),.?":{}|<>]/, "Password must contain at least one special character");
|
|
26
|
-
var loginSchema = z2.object({
|
|
27
|
-
email: emailSchema,
|
|
28
|
-
password: z2.string().min(1, "Password is required")
|
|
29
|
-
});
|
|
30
|
-
var registrationSchema = z2.object({
|
|
31
|
-
email: emailSchema,
|
|
32
|
-
password: passwordSchema,
|
|
33
|
-
confirmPassword: z2.string()
|
|
34
|
-
}).refine((data) => data.password === data.confirmPassword, {
|
|
35
|
-
message: "Passwords don't match",
|
|
36
|
-
path: ["confirmPassword"]
|
|
37
|
-
});
|
|
38
|
-
var secureLoginSchema = z2.object({
|
|
39
|
-
email: emailSchema,
|
|
40
|
-
password: securePasswordSchema
|
|
41
|
-
});
|
|
42
|
-
var passwordResetSchema = z2.object({
|
|
43
|
-
email: emailSchema
|
|
44
|
-
});
|
|
45
|
-
var changePasswordSchema = z2.object({
|
|
46
|
-
currentPassword: z2.string().min(1, "Current password is required"),
|
|
47
|
-
newPassword: securePasswordSchema,
|
|
48
|
-
confirmPassword: z2.string()
|
|
49
|
-
}).refine((data) => data.newPassword === data.confirmPassword, {
|
|
50
|
-
message: "Passwords don't match",
|
|
51
|
-
path: ["confirmPassword"]
|
|
52
|
-
});
|
|
53
|
-
var userProfileSchema = z2.object({
|
|
54
|
-
name: nameSchema,
|
|
55
|
-
email: emailSchema,
|
|
56
|
-
phone: phoneSchema.optional(),
|
|
57
|
-
website: urlSchema.optional(),
|
|
58
|
-
bio: z2.string().max(500).optional()
|
|
59
|
-
});
|
|
60
|
-
var contactFormSchema = z2.object({
|
|
61
|
-
name: nameSchema,
|
|
62
|
-
email: emailSchema,
|
|
63
|
-
message: z2.string().min(1, "Message is required").max(1e3, "Message must be less than 1000 characters")
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
export {
|
|
67
|
-
pickSchema,
|
|
68
|
-
combineSchemas,
|
|
69
|
-
emailSchema,
|
|
70
|
-
nameSchema,
|
|
71
|
-
phoneSchema,
|
|
72
|
-
urlSchema,
|
|
73
|
-
dateSchema,
|
|
74
|
-
passwordSchema,
|
|
75
|
-
securePasswordSchema,
|
|
76
|
-
loginSchema,
|
|
77
|
-
registrationSchema,
|
|
78
|
-
secureLoginSchema,
|
|
79
|
-
passwordResetSchema,
|
|
80
|
-
changePasswordSchema,
|
|
81
|
-
userProfileSchema,
|
|
82
|
-
contactFormSchema
|
|
83
|
-
};
|
|
84
|
-
//# sourceMappingURL=chunk-LMC26NLJ.js.map
|