@jmruthers/pace-core 0.5.134 → 0.5.136
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/dist/{DataTable-C7GaRZye.d.ts → DataTable-CWAZZcXC.d.ts} +1 -1
- package/dist/{DataTable-A36PJG6N.js → DataTable-CYOHOX3O.js} +25 -13
- package/dist/{PublicLoadingSpinner-CUAnTvcg.d.ts → EventLogo-801uofbR.d.ts} +51 -135
- package/dist/UnifiedAuthProvider-5E5TUNMS.js +17 -0
- package/dist/{UnifiedAuthProvider-BVKmQd9u.d.ts → UnifiedAuthProvider-DJxGTftH.d.ts} +1 -1
- package/dist/{api-TNIBJWLM.js → api-45XYYO2A.js} +4 -3
- package/dist/{audit-T36HM7IM.js → audit-64X3VJXB.js} +3 -2
- package/dist/{chunk-CTJRBUX2.js → chunk-2TWNJ46Y.js} +2 -2
- package/dist/{chunk-UJI6WSMD.js → chunk-444EZN6N.js} +3 -3
- package/dist/chunk-444EZN6N.js.map +1 -0
- package/dist/{chunk-3CG5L6RN.js → chunk-4MT5BGGL.js} +90 -73
- package/dist/chunk-4MT5BGGL.js.map +1 -0
- package/dist/{chunk-PYUXFQJ3.js → chunk-56XJ3TU6.js} +2 -2
- package/dist/chunk-56XJ3TU6.js.map +1 -0
- package/dist/chunk-5DPZ5EAT.js +60 -0
- package/dist/chunk-5DPZ5EAT.js.map +1 -0
- package/dist/{chunk-66C4BSAY.js → chunk-ANBQRTPX.js} +9 -2
- package/dist/chunk-ANBQRTPX.js.map +1 -0
- package/dist/chunk-APIBCTL2.js +670 -0
- package/dist/chunk-APIBCTL2.js.map +1 -0
- package/dist/{chunk-GKHF54DI.js → chunk-BESYRHQM.js} +10 -4
- package/dist/chunk-BESYRHQM.js.map +1 -0
- package/dist/{chunk-WP5I5GLN.js → chunk-BVYWGZVV.js} +112 -97
- package/dist/chunk-BVYWGZVV.js.map +1 -0
- package/dist/{chunk-GEVIB2UB.js → chunk-ERISIBYU.js} +14 -5
- package/dist/chunk-ERISIBYU.js.map +1 -0
- package/dist/{chunk-CQZU6TFE.js → chunk-FHWWBIHA.js} +100 -62
- package/dist/chunk-FHWWBIHA.js.map +1 -0
- package/dist/{chunk-O3NWNXDY.js → chunk-FMUCXFII.js} +2 -2
- package/dist/chunk-FMUCXFII.js.map +1 -0
- package/dist/{chunk-GVDR7WNV.js → chunk-HJGGOMQ6.js} +194 -518
- package/dist/chunk-HJGGOMQ6.js.map +1 -0
- package/dist/{chunk-BDZUMRBD.js → chunk-K2WWTH7O.js} +13 -6
- package/dist/chunk-K2WWTH7O.js.map +1 -0
- package/dist/{chunk-BYXRHAIF.js → chunk-L6PGMCMD.js} +23 -14
- package/dist/chunk-L6PGMCMD.js.map +1 -0
- package/dist/chunk-LMC26NLJ.js +84 -0
- package/dist/chunk-LMC26NLJ.js.map +1 -0
- package/dist/{chunk-M6DDYFUD.js → chunk-LS353YLY.js} +19 -16
- package/dist/chunk-LS353YLY.js.map +1 -0
- package/dist/{chunk-ZYZCRSBD.js → chunk-LTV3XIJJ.js} +16 -11
- package/dist/chunk-LTV3XIJJ.js.map +1 -0
- package/dist/{chunk-HMNOSGVA.js → chunk-NOHEVYVX.js} +377 -666
- package/dist/chunk-NOHEVYVX.js.map +1 -0
- package/dist/{chunk-JCQZ6LA7.js → chunk-Q5QRDWKI.js} +9 -3
- package/dist/chunk-Q5QRDWKI.js.map +1 -0
- package/dist/chunk-S5OFRT4M.js +94 -0
- package/dist/chunk-S5OFRT4M.js.map +1 -0
- package/dist/{chunk-3DBFLLLU.js → chunk-SBVILCCA.js} +14 -9
- package/dist/chunk-SBVILCCA.js.map +1 -0
- package/dist/{chunk-TGIY2AR2.js → chunk-SL2YQDR6.js} +4 -3
- package/dist/{chunk-TGIY2AR2.js.map → chunk-SL2YQDR6.js.map} +1 -1
- package/dist/{chunk-VZ5OR6HD.js → chunk-TVYPTYOY.js} +55 -179
- package/dist/chunk-TVYPTYOY.js.map +1 -0
- package/dist/{chunk-ZV77RZMU.js → chunk-XARJS7CD.js} +2 -2
- package/dist/chunk-XDNLUEXI.js +138 -0
- package/dist/chunk-XDNLUEXI.js.map +1 -0
- package/dist/{chunk-F64FFPOZ.js → chunk-YLKIDTUK.js} +26 -20
- package/dist/chunk-YLKIDTUK.js.map +1 -0
- package/dist/{chunk-5F3NDPJV.js → chunk-ZZ2SS7NI.js} +10 -5
- package/dist/chunk-ZZ2SS7NI.js.map +1 -0
- package/dist/components.d.ts +7 -287
- package/dist/components.js +26 -157
- package/dist/components.js.map +1 -1
- package/dist/{file-reference-C9isKNPn.d.ts → file-reference-C6Gkn77H.d.ts} +1 -1
- package/dist/{formatting-DFcCxUEk.d.ts → formatting-CvUXy2mF.d.ts} +1 -1
- package/dist/hooks.d.ts +3 -3
- package/dist/hooks.js +22 -16
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +219 -9
- package/dist/index.js +49 -31
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +5 -4
- package/dist/rbac/index.js +13 -12
- package/dist/styles/index.js +2 -1
- package/dist/theming/runtime.d.ts +2 -19
- package/dist/theming/runtime.js +2 -1
- package/dist/{types-D5rqZQXk.d.ts → types-Dfz9dmVH.d.ts} +12 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/dist/{useInactivityTracker-MRUU55XI.js → useInactivityTracker-TO6ZOF35.js} +3 -2
- package/dist/{usePublicRouteParams-Dyt1tzI9.d.ts → usePublicRouteParams-B7PabvuH.d.ts} +1 -1
- package/dist/utils.d.ts +195 -232
- package/dist/utils.js +173 -331
- package/dist/utils.js.map +1 -1
- package/dist/{validation-DnhrNMju.d.ts → validation-8npbysjg.d.ts} +26 -8
- package/dist/validation.d.ts +261 -10
- package/dist/validation.js +82 -440
- package/dist/validation.js.map +1 -1
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +6 -6
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +6 -6
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +7 -7
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +4 -4
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +29 -4
- package/docs/api/interfaces/DataAccessRecord.md +9 -9
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +18 -18
- package/docs/api/interfaces/DataTableColumn.md +61 -1
- package/docs/api/interfaces/DataTableProps.md +3 -3
- package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
- package/docs/api/interfaces/EmptyStateConfig.md +5 -5
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +14 -14
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +152 -0
- package/docs/api/interfaces/ExportColumn.md +90 -0
- package/docs/api/interfaces/ExportOptions.md +126 -0
- package/docs/api/interfaces/FileDisplayProps.md +15 -15
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +10 -10
- package/docs/api/interfaces/NavigationContextType.md +9 -9
- package/docs/api/interfaces/NavigationGuardProps.md +10 -10
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +7 -7
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +27 -27
- package/docs/api/interfaces/PaceLoginPageProps.md +4 -4
- package/docs/api/interfaces/PageAccessRecord.md +8 -8
- package/docs/api/interfaces/PagePermissionContextType.md +8 -8
- package/docs/api/interfaces/PagePermissionGuardProps.md +11 -11
- package/docs/api/interfaces/PagePermissionProviderProps.md +7 -7
- package/docs/api/interfaces/PaletteData.md +4 -4
- package/docs/api/interfaces/PermissionEnforcerProps.md +11 -11
- package/docs/api/interfaces/ProtectedRouteProps.md +6 -6
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
- package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +10 -10
- package/docs/api/interfaces/RouteConfig.md +10 -10
- package/docs/api/interfaces/SecureDataContextType.md +9 -9
- package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +21 -0
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +53 -53
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +9 -9
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
- package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
- package/docs/api/interfaces/UsePublicEventReturn.md +5 -5
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +9 -9
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +4 -4
- package/docs/api/interfaces/UseResolvedScopeReturn.md +4 -4
- package/docs/api/interfaces/UserEventAccess.md +11 -11
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +648 -212
- package/docs/api-reference/components.md +106 -26
- package/docs/architecture/README.md +0 -2
- package/docs/implementation-guides/data-tables.md +277 -13
- package/docs/implementation-guides/forms.md +1 -16
- package/docs/implementation-guides/permission-enforcement.md +8 -2
- package/examples/README.md +30 -14
- package/examples/STRUCTURE.md +125 -0
- package/examples/components/DataTable/HierarchicalActionsExample.tsx +421 -0
- package/examples/components/DataTable/HierarchicalExample.tsx +475 -0
- package/examples/components/DataTable/InitialPageSizeExample.tsx +177 -0
- package/examples/components/DataTable/PerformanceExample.tsx +506 -0
- package/examples/components/DataTable/index.ts +13 -0
- package/examples/components/Dialog/BasicHtmlTest.tsx +55 -0
- package/examples/components/Dialog/DebugHtmlExample.tsx +68 -0
- package/examples/components/Dialog/HtmlDialogExample.tsx +202 -0
- package/examples/components/Dialog/ScrollableDialogExample.tsx +290 -0
- package/examples/components/Dialog/SimpleHtmlTest.tsx +61 -0
- package/examples/components/Dialog/SmartDialogExample.tsx +322 -0
- package/examples/components/Dialog/index.ts +15 -0
- package/examples/components/index.ts +11 -0
- package/examples/features/index.ts +12 -0
- package/examples/{public-pages → features/public-pages}/CorrectPublicPageImplementation.tsx +1 -1
- package/examples/{public-pages → features/public-pages}/PublicEventPage.tsx +1 -1
- package/examples/{public-pages → features/public-pages}/PublicPageApp.tsx +1 -1
- package/examples/{public-pages → features/public-pages}/PublicPageUsageExample.tsx +1 -1
- package/examples/index.ts +11 -3
- package/package.json +30 -10
- package/src/components/Alert/Alert.tsx +1 -1
- package/src/components/Avatar/Avatar.tsx +1 -1
- package/src/components/Button/Button.tsx +1 -1
- package/src/components/Card/Card.tsx +1 -1
- package/src/components/Checkbox/Checkbox.tsx +1 -1
- package/src/components/DataTable/DataTable.test.tsx +1 -1
- package/src/components/DataTable/DataTable.tsx +1 -30
- package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +562 -0
- package/src/components/DataTable/__tests__/styles.test.ts +2 -2
- package/src/components/DataTable/components/ActionButtons.tsx +0 -15
- package/src/components/DataTable/components/DataTableCore.tsx +4 -185
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +1 -1
- package/src/components/DataTable/components/DataTableModals.tsx +1 -27
- package/src/components/DataTable/components/EditableRow.tsx +1 -1
- package/src/components/DataTable/components/ImportModal.tsx +2 -14
- package/src/components/DataTable/components/PaginationControls.tsx +1 -1
- package/src/components/DataTable/components/UnifiedTableBody.tsx +109 -82
- package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +1 -1
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +1 -1
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +1 -1
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +1 -1
- package/src/components/DataTable/examples/GroupingAggregationExample.tsx +273 -0
- package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +1 -1
- package/src/components/DataTable/examples/__tests__/HierarchicalActionsExample.test.tsx +1 -1
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +1 -1
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +1 -1
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +2 -23
- package/src/components/DataTable/index.ts +4 -0
- package/src/components/DataTable/styles.ts +1 -1
- package/src/components/DataTable/types.ts +13 -0
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +1 -1
- package/src/components/DataTable/utils/aggregationUtils.ts +161 -0
- package/src/components/DataTable/utils/exportUtils.ts +1 -1
- package/src/components/DataTable/utils/flexibleImport.ts +1 -11
- package/src/components/DataTable/utils/index.ts +1 -0
- package/src/components/DataTable/utils/paginationUtils.ts +1 -1
- package/src/components/Dialog/Dialog.tsx +2 -2
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +35 -7
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +5 -4
- package/src/components/EventSelector/EventSelector.tsx +3 -2
- package/src/components/FileDisplay/FileDisplay.tsx +2 -36
- package/src/components/FileUpload/FileUpload.test.tsx +2 -2
- package/src/components/FileUpload/FileUpload.tsx +2 -2
- package/src/components/Footer/Footer.tsx +1 -1
- package/src/components/Form/Form.test.tsx +4 -509
- package/src/components/Form/Form.tsx +1 -1
- package/src/components/Form/FormField.tsx +1 -1
- package/src/components/Form/index.ts +0 -12
- package/src/components/Header/Header.tsx +1 -1
- package/src/components/Input/Input.tsx +1 -1
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/LoginForm/LoginForm.tsx +1 -1
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +19 -3
- package/src/components/NavigationMenu/NavigationMenu.tsx +9 -8
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +4 -3
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +14 -12
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +0 -16
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +0 -1
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +0 -9
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +35 -3
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +13 -12
- package/src/components/PasswordReset/PasswordChangeForm.tsx +1 -1
- package/src/components/PasswordReset/index.ts +0 -2
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +35 -8
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -2
- package/src/components/PublicLayout/PublicErrorBoundary.tsx +1 -1
- package/src/components/PublicLayout/PublicLoadingSpinner.tsx +1 -1
- package/src/components/PublicLayout/PublicPageContextChecker.tsx +44 -43
- package/src/components/PublicLayout/PublicPageFooter.tsx +1 -1
- package/src/components/PublicLayout/PublicPageHeader.tsx +1 -15
- package/src/components/PublicLayout/PublicPageProvider.tsx +3 -2
- package/src/components/PublicLayout/__tests__/PublicPageContextChecker.test.tsx +2 -0
- package/src/components/PublicLayout/index.ts +4 -2
- package/src/components/Select/Select.tsx +1 -1
- package/src/components/{SessionRestorationLoader.tsx → SessionRestorationLoader/SessionRestorationLoader.tsx} +3 -2
- package/src/components/SessionRestorationLoader/index.ts +3 -0
- package/src/components/Switch/Switch.tsx +1 -1
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/components/Tooltip/Tooltip.tsx +1 -1
- package/src/components/index.ts +4 -10
- package/src/hooks/__tests__/hooks.integration.test.tsx +37 -22
- package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +33 -17
- package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +28 -3
- package/src/hooks/__tests__/useFileDisplay.unit.test.ts +36 -9
- package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +26 -2
- package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +19 -6
- package/src/hooks/__tests__/usePermissionCache.simple.test.ts +17 -4
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +17 -4
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +26 -6
- package/src/hooks/__tests__/usePublicFileDisplay.test.ts +16 -6
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +3 -3
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +17 -3
- package/src/hooks/public/usePublicEvent.ts +7 -6
- package/src/hooks/public/usePublicEventLogo.ts +7 -4
- package/src/hooks/public/usePublicFileDisplay.ts +6 -150
- package/src/hooks/useComponentPerformance.ts +4 -1
- package/src/hooks/useDataTablePerformance.ts +4 -3
- package/src/hooks/useEventTheme.test.ts +18 -5
- package/src/hooks/useEventTheme.ts +4 -1
- package/src/hooks/useEvents.ts +2 -0
- package/src/hooks/useFileDisplay.ts +9 -8
- package/src/hooks/useFileReference.ts +4 -1
- package/src/hooks/useFileUrl.ts +4 -1
- package/src/hooks/useInactivityTracker.ts +5 -4
- package/src/hooks/useOrganisationSecurity.test.ts +33 -12
- package/src/hooks/useOrganisationSecurity.ts +8 -7
- package/src/hooks/usePerformanceMonitor.ts +6 -3
- package/src/hooks/usePermissionCache.ts +13 -6
- package/src/hooks/useSecureDataAccess.test.ts +2 -2
- package/src/hooks/useSecureDataAccess.ts +9 -8
- package/src/hooks/useSessionRestoration.ts +4 -1
- package/src/hooks/useStorage.ts +4 -1
- package/src/index.ts +25 -8
- package/src/providers/services/AuthServiceProvider.tsx +3 -2
- package/src/providers/services/EventServiceProvider.tsx +2 -1
- package/src/providers/services/InactivityServiceProvider.tsx +2 -1
- package/src/providers/services/OrganisationServiceProvider.tsx +2 -1
- package/src/providers/services/UnifiedAuthProvider.tsx +4 -3
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +22 -2
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +24 -2
- package/src/rbac/__tests__/cache-invalidation.test.ts +20 -6
- package/src/rbac/api.ts +5 -2
- package/src/rbac/audit-enhanced.ts +6 -6
- package/src/rbac/audit.test.ts +60 -38
- package/src/rbac/audit.ts +8 -8
- package/src/rbac/cache-invalidation.ts +7 -4
- package/src/rbac/components/EnhancedNavigationMenu.tsx +11 -5
- package/src/rbac/components/NavigationGuard.tsx +7 -3
- package/src/rbac/components/NavigationProvider.tsx +6 -3
- package/src/rbac/components/PagePermissionGuard.tsx +28 -16
- package/src/rbac/components/PagePermissionProvider.tsx +4 -1
- package/src/rbac/components/PermissionEnforcer.tsx +9 -3
- package/src/rbac/components/RoleBasedRouter.tsx +3 -1
- package/src/rbac/components/SecureDataProvider.tsx +7 -3
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +87 -61
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +83 -33
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +36 -13
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +2 -2
- package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +22 -8
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +19 -6
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +43 -17
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +42 -17
- package/src/rbac/engine.ts +15 -7
- package/src/rbac/hooks/usePermissions.ts +7 -3
- package/src/rbac/hooks/useResolvedScope.test.ts +2 -2
- package/src/rbac/hooks/useResolvedScope.ts +10 -7
- package/src/rbac/permissions.ts +5 -2
- package/src/rbac/security.test.ts +27 -16
- package/src/rbac/security.ts +5 -4
- package/src/services/AuthService.ts +22 -21
- package/src/services/EventService.ts +12 -12
- package/src/services/InactivityService.ts +5 -4
- package/src/services/OrganisationService.ts +26 -25
- package/src/services/__tests__/AuthService.test.ts +51 -19
- package/src/services/__tests__/EventService.test.ts +37 -5
- package/src/services/__tests__/InactivityService.test.ts +38 -4
- package/src/services/__tests__/OrganisationService.test.ts +3 -8
- package/src/services/base/BaseService.ts +3 -1
- package/src/theming/__tests__/runtime.test.ts +21 -12
- package/src/theming/parseEventColours.ts +5 -19
- package/src/theming/runtime.ts +8 -4
- package/src/types/validation.ts +2 -29
- package/src/utils/__tests__/appConfig.unit.test.ts +1 -1
- package/src/utils/__tests__/audit.unit.test.ts +1 -1
- package/src/utils/__tests__/auth-utils.unit.test.ts +1 -1
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +19 -19
- package/src/utils/__tests__/cn.unit.test.ts +1 -1
- package/src/utils/__tests__/debugLogger.test.ts +1 -1
- package/src/utils/__tests__/deviceFingerprint.unit.test.ts +1 -1
- package/src/utils/__tests__/dynamicUtils.unit.test.ts +1 -1
- package/src/utils/__tests__/formatting.unit.test.ts +1 -1
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +1 -1
- package/src/utils/__tests__/logger.unit.test.ts +1 -1
- package/src/utils/__tests__/organisationContext.unit.test.ts +1 -1
- package/src/utils/__tests__/performanceBenchmark.test.ts +1 -1
- package/src/utils/__tests__/performanceBudgets.unit.test.ts +1 -1
- package/src/utils/__tests__/permissionTypes.unit.test.ts +1 -1
- package/src/utils/__tests__/permissionUtils.unit.test.ts +1 -1
- package/src/utils/__tests__/sanitization.unit.test.ts +1 -1
- package/src/utils/__tests__/schemaUtils.unit.test.ts +1 -1
- package/src/utils/__tests__/secureDataAccess.unit.test.ts +1 -1
- package/src/utils/__tests__/secureErrors.unit.test.ts +33 -15
- package/src/utils/__tests__/secureStorage.unit.test.ts +1 -1
- package/src/utils/__tests__/security.unit.test.ts +40 -18
- package/src/utils/__tests__/securityMonitor.unit.test.ts +1 -1
- package/src/utils/__tests__/sessionTracking.unit.test.ts +40 -29
- package/src/utils/__tests__/validationUtils.unit.test.ts +19 -6
- package/src/utils/{appIdResolver.test.ts → app/appIdResolver.test.ts} +28 -30
- package/src/utils/{appIdResolver.ts → app/appIdResolver.ts} +8 -5
- package/src/utils/{appNameResolver.test.ts → app/appNameResolver.test.ts} +1 -1
- package/src/utils/{appNameResolver.ts → app/appNameResolver.ts} +5 -1
- package/src/utils/{organisationContext.ts → context/organisationContext.ts} +6 -3
- package/src/utils/{sessionTracking.ts → context/sessionTracking.ts} +11 -12
- package/src/utils/{logger.ts → core/logger.ts} +4 -2
- package/src/utils/{deviceFingerprint.ts → device/deviceFingerprint.ts} +1 -1
- package/src/utils/{lazyLoad.tsx → dynamic/lazyLoad.tsx} +2 -2
- package/src/utils/{file-reference.test.ts → file-reference/__tests__/file-reference.test.ts} +5 -5
- package/src/utils/{file-reference.ts → file-reference/index.ts} +20 -38
- package/src/utils/index.ts +32 -54
- package/src/utils/{secureErrors.ts → security/secureErrors.ts} +6 -3
- package/src/utils/{security.ts → security/security.ts} +5 -2
- package/src/utils/storage/__tests__/helpers.unit.test.ts +1 -4
- package/src/utils/storage/helpers.ts +15 -8
- package/src/{components/Dialog/utils/__tests__/safeHtml.unit.test.ts → utils/validation/__tests__/htmlSanitization.unit.test.ts} +9 -15
- package/src/{validation → utils/validation}/csrf.ts +1 -1
- package/src/{components/Dialog/utils/safeHtml.ts → utils/validation/htmlSanitization.ts} +9 -10
- package/src/utils/validation/index.ts +79 -0
- package/src/utils/{sanitization.ts → validation/sanitization.ts} +71 -2
- package/src/{validation/schemaUtils.ts → utils/validation/schema.ts} +11 -6
- package/src/{validation → utils/validation}/sqlInjectionProtection.ts +2 -0
- package/src/utils/{validationUtils.ts → validation/validationUtils.ts} +4 -1
- package/src/validation/index.ts +3 -34
- package/dist/UnifiedAuthProvider-CQDZRJIS.js +0 -16
- package/dist/chunk-24MKLB7U.js +0 -81
- package/dist/chunk-24MKLB7U.js.map +0 -1
- package/dist/chunk-3CG5L6RN.js.map +0 -1
- package/dist/chunk-3DBFLLLU.js.map +0 -1
- package/dist/chunk-5F3NDPJV.js.map +0 -1
- package/dist/chunk-66C4BSAY.js.map +0 -1
- package/dist/chunk-BDZUMRBD.js.map +0 -1
- package/dist/chunk-BYXRHAIF.js.map +0 -1
- package/dist/chunk-CDQ3PX7L.js +0 -18
- package/dist/chunk-CDQ3PX7L.js.map +0 -1
- package/dist/chunk-CQZU6TFE.js.map +0 -1
- package/dist/chunk-F64FFPOZ.js.map +0 -1
- package/dist/chunk-GEVIB2UB.js.map +0 -1
- package/dist/chunk-GKHF54DI.js.map +0 -1
- package/dist/chunk-GVDR7WNV.js.map +0 -1
- package/dist/chunk-HMNOSGVA.js.map +0 -1
- package/dist/chunk-JCQZ6LA7.js.map +0 -1
- package/dist/chunk-M6DDYFUD.js.map +0 -1
- package/dist/chunk-O3NWNXDY.js.map +0 -1
- package/dist/chunk-PYUXFQJ3.js.map +0 -1
- package/dist/chunk-UJI6WSMD.js.map +0 -1
- package/dist/chunk-VZ5OR6HD.js.map +0 -1
- package/dist/chunk-WP5I5GLN.js.map +0 -1
- package/dist/chunk-ZYZCRSBD.js.map +0 -1
- package/src/components/Dialog/README.md +0 -804
- package/src/components/Form/FormErrorSummary.tsx +0 -113
- package/src/components/Form/FormFieldset.tsx +0 -127
- package/src/components/Form/FormLiveRegion.tsx +0 -198
- package/src/components/PasswordReset/PasswordResetForm.test.tsx +0 -597
- package/src/components/PasswordReset/PasswordResetForm.tsx +0 -201
- package/src/components/PublicLayout/PublicPageDebugger.tsx +0 -104
- package/src/components/PublicLayout/PublicPageDiagnostic.tsx +0 -162
- package/src/components/PublicLayout/__tests__/PublicPageDebugger.test.tsx +0 -185
- package/src/examples/CorrectPublicPageImplementation.tsx +0 -304
- package/src/examples/PublicEventPage.tsx +0 -287
- package/src/examples/PublicPageApp.tsx +0 -321
- package/src/examples/PublicPageUsageExample.tsx +0 -218
- package/src/utils/schemaUtils.ts +0 -37
- package/src/validation/__tests__/common.unit.test.ts +0 -101
- package/src/validation/__tests__/csrf.unit.test.ts +0 -365
- package/src/validation/__tests__/passwordSchema.unit.test.ts +0 -203
- package/src/validation/__tests__/sanitization.unit.test.ts +0 -250
- package/src/validation/__tests__/schemaUtils.unit.test.ts +0 -451
- package/src/validation/__tests__/sqlInjectionProtection.unit.test.ts +0 -462
- package/src/validation/__tests__/user.unit.test.ts +0 -440
- package/src/validation/sanitization.ts +0 -96
- /package/dist/{DataTable-A36PJG6N.js.map → DataTable-CYOHOX3O.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-CQDZRJIS.js.map → UnifiedAuthProvider-5E5TUNMS.js.map} +0 -0
- /package/dist/{api-TNIBJWLM.js.map → api-45XYYO2A.js.map} +0 -0
- /package/dist/{audit-T36HM7IM.js.map → audit-64X3VJXB.js.map} +0 -0
- /package/dist/{chunk-CTJRBUX2.js.map → chunk-2TWNJ46Y.js.map} +0 -0
- /package/dist/{chunk-ZV77RZMU.js.map → chunk-XARJS7CD.js.map} +0 -0
- /package/dist/{useInactivityTracker-MRUU55XI.js.map → useInactivityTracker-TO6ZOF35.js.map} +0 -0
- /package/examples/{public-pages → features/public-pages}/index.ts +0 -0
- /package/examples/{RBAC → features/rbac}/CompleteRBACExample.tsx +0 -0
- /package/examples/{RBAC → features/rbac}/EventBasedApp.tsx +0 -0
- /package/examples/{RBAC → features/rbac}/PermissionExample.tsx +0 -0
- /package/examples/{RBAC → features/rbac}/index.ts +0 -0
- /package/src/utils/{appConfig.ts → app/appConfig.ts} +0 -0
- /package/src/utils/{appNameResolver.simple.test.ts → app/appNameResolver.simple.test.ts} +0 -0
- /package/src/utils/{audit.ts → audit/audit.ts} +0 -0
- /package/src/utils/{organisationContext.test.ts → context/organisationContext.test.ts} +0 -0
- /package/src/utils/{cn.ts → core/cn.ts} +0 -0
- /package/src/utils/{debugLogger.ts → core/debugLogger.ts} +0 -0
- /package/src/utils/{dynamicUtils.ts → dynamic/dynamicUtils.ts} +0 -0
- /package/src/utils/{formatDate.test.ts → formatting/formatDate.test.ts} +0 -0
- /package/src/utils/{formatting.ts → formatting/formatting.ts} +0 -0
- /package/src/utils/{bundleAnalysis.ts → performance/bundleAnalysis.ts} +0 -0
- /package/src/utils/{performanceBenchmark.ts → performance/performanceBenchmark.ts} +0 -0
- /package/src/utils/{performanceBudgets.ts → performance/performanceBudgets.ts} +0 -0
- /package/src/utils/{permissionTypes.ts → permissions/permissionTypes.ts} +0 -0
- /package/src/utils/{permissionUtils.test.ts → permissions/permissionUtils.test.ts} +0 -0
- /package/src/utils/{permissionUtils.ts → permissions/permissionUtils.ts} +0 -0
- /package/src/utils/{auth-utils.ts → security/auth-utils.ts} +0 -0
- /package/src/utils/{secureDataAccess.test.ts → security/secureDataAccess.test.ts} +0 -0
- /package/src/utils/{secureDataAccess.ts → security/secureDataAccess.ts} +0 -0
- /package/src/utils/{secureStorage.ts → security/secureStorage.ts} +0 -0
- /package/src/utils/{securityMonitor.ts → security/securityMonitor.ts} +0 -0
- /package/src/{validation → utils/validation}/common.ts +0 -0
- /package/src/{validation → utils/validation}/passwordSchema.ts +0 -0
- /package/src/{validation → utils/validation}/user.ts +0 -0
- /package/src/utils/{validation.ts → validation/validation.ts} +0 -0
|
@@ -1,365 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file CSRF Protection Unit Tests
|
|
3
|
-
* @package @jmruthers/pace-core
|
|
4
|
-
* @module Validation/CSRF/Tests
|
|
5
|
-
* @since 0.1.0
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
9
|
-
import {
|
|
10
|
-
generateCSRFToken,
|
|
11
|
-
validateCSRFToken,
|
|
12
|
-
getCSRFToken,
|
|
13
|
-
csrfManager,
|
|
14
|
-
CSRFTokenData,
|
|
15
|
-
} from '../csrf';
|
|
16
|
-
|
|
17
|
-
// Mock crypto for testing
|
|
18
|
-
const mockCrypto = {
|
|
19
|
-
getRandomValues: vi.fn(),
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
Object.defineProperty(global, 'crypto', {
|
|
23
|
-
value: mockCrypto,
|
|
24
|
-
writable: true,
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
// Mock secure storage
|
|
28
|
-
vi.mock('../../utils/secureStorage', () => ({
|
|
29
|
-
secureStorage: {
|
|
30
|
-
setItem: vi.fn().mockResolvedValue(undefined),
|
|
31
|
-
getItem: vi.fn().mockResolvedValue(null),
|
|
32
|
-
removeItem: vi.fn().mockResolvedValue(undefined),
|
|
33
|
-
},
|
|
34
|
-
}));
|
|
35
|
-
|
|
36
|
-
describe('CSRF Protection', () => {
|
|
37
|
-
const testSessionId = 'test-session-123';
|
|
38
|
-
const testSessionId2 = 'test-session-456';
|
|
39
|
-
|
|
40
|
-
beforeEach(() => {
|
|
41
|
-
vi.clearAllMocks();
|
|
42
|
-
mockCrypto.getRandomValues.mockImplementation((array) => {
|
|
43
|
-
for (let i = 0; i < array.length; i++) {
|
|
44
|
-
array[i] = Math.floor(Math.random() * 256);
|
|
45
|
-
}
|
|
46
|
-
return array;
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
afterEach(async () => {
|
|
51
|
-
// Clear session tokens
|
|
52
|
-
await csrfManager.clearSession(testSessionId);
|
|
53
|
-
await csrfManager.clearSession(testSessionId2);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
describe('generateCSRFToken', () => {
|
|
57
|
-
it('should generate a valid CSRF token', async () => {
|
|
58
|
-
const token = await generateCSRFToken(testSessionId);
|
|
59
|
-
|
|
60
|
-
expect(token).toBeDefined();
|
|
61
|
-
expect(typeof token).toBe('string');
|
|
62
|
-
expect(token.length).toBe(64); // 32 bytes * 2 hex chars
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('should generate different tokens on subsequent calls', async () => {
|
|
66
|
-
const token1 = await generateCSRFToken(testSessionId);
|
|
67
|
-
const token2 = await generateCSRFToken(testSessionId);
|
|
68
|
-
|
|
69
|
-
expect(token1).not.toBe(token2);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('should generate tokens for different sessions', async () => {
|
|
73
|
-
const token1 = await generateCSRFToken(testSessionId);
|
|
74
|
-
const token2 = await generateCSRFToken(testSessionId2);
|
|
75
|
-
|
|
76
|
-
expect(token1).not.toBe(token2);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('should handle crypto.getRandomValues errors', async () => {
|
|
80
|
-
mockCrypto.getRandomValues.mockImplementation(() => {
|
|
81
|
-
throw new Error('Crypto unavailable');
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
await expect(generateCSRFToken(testSessionId)).rejects.toThrow('CSRF token generation failed');
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
describe('validateCSRFToken', () => {
|
|
89
|
-
it('should validate a valid token for correct session', async () => {
|
|
90
|
-
const token = await generateCSRFToken(testSessionId);
|
|
91
|
-
const isValid = await validateCSRFToken(token, testSessionId);
|
|
92
|
-
|
|
93
|
-
expect(isValid).toBe(true);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('should reject token for wrong session', async () => {
|
|
97
|
-
const token = await generateCSRFToken(testSessionId);
|
|
98
|
-
const isValid = await validateCSRFToken(token, testSessionId2);
|
|
99
|
-
|
|
100
|
-
expect(isValid).toBe(false);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should reject invalid token format', async () => {
|
|
104
|
-
const invalidToken = 'invalid-token';
|
|
105
|
-
const isValid = await validateCSRFToken(invalidToken, testSessionId);
|
|
106
|
-
|
|
107
|
-
expect(isValid).toBe(false);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('should reject reused tokens (one-time use)', async () => {
|
|
111
|
-
const token = await generateCSRFToken(testSessionId);
|
|
112
|
-
|
|
113
|
-
// First use should succeed
|
|
114
|
-
const firstUse = await validateCSRFToken(token, testSessionId);
|
|
115
|
-
expect(firstUse).toBe(true);
|
|
116
|
-
|
|
117
|
-
// Second use should fail
|
|
118
|
-
const secondUse = await validateCSRFToken(token, testSessionId);
|
|
119
|
-
expect(secondUse).toBe(false);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('should handle empty or null tokens', async () => {
|
|
123
|
-
expect(await validateCSRFToken('', testSessionId)).toBe(false);
|
|
124
|
-
expect(await validateCSRFToken(null as any, testSessionId)).toBe(false);
|
|
125
|
-
expect(await validateCSRFToken(undefined as any, testSessionId)).toBe(false);
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
describe('getCSRFToken', () => {
|
|
130
|
-
it('should return existing valid token for session', async () => {
|
|
131
|
-
const generatedToken = await generateCSRFToken(testSessionId);
|
|
132
|
-
const retrievedToken = await getCSRFToken(testSessionId);
|
|
133
|
-
|
|
134
|
-
expect(retrievedToken).toBeDefined();
|
|
135
|
-
expect(typeof retrievedToken).toBe('string');
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it('should generate new token if none exists', async () => {
|
|
139
|
-
const token = await getCSRFToken(testSessionId);
|
|
140
|
-
|
|
141
|
-
expect(token).toBeDefined();
|
|
142
|
-
expect(typeof token).toBe('string');
|
|
143
|
-
expect(token!.length).toBe(64);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it('should return different tokens for different sessions', async () => {
|
|
147
|
-
const token1 = await getCSRFToken(testSessionId);
|
|
148
|
-
const token2 = await getCSRFToken(testSessionId2);
|
|
149
|
-
|
|
150
|
-
expect(token1).not.toBe(token2);
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
describe('csrfManager', () => {
|
|
155
|
-
it('should manage multiple sessions', async () => {
|
|
156
|
-
const token1 = await csrfManager.generateToken(testSessionId);
|
|
157
|
-
const token2 = await csrfManager.generateToken(testSessionId2);
|
|
158
|
-
|
|
159
|
-
expect(await csrfManager.validateToken(token1, testSessionId)).toBe(true);
|
|
160
|
-
expect(await csrfManager.validateToken(token2, testSessionId2)).toBe(true);
|
|
161
|
-
|
|
162
|
-
// Cross-session validation should fail
|
|
163
|
-
expect(await csrfManager.validateToken(token1, testSessionId2)).toBe(false);
|
|
164
|
-
expect(await csrfManager.validateToken(token2, testSessionId)).toBe(false);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('should clear session tokens', async () => {
|
|
168
|
-
const token = await csrfManager.generateToken(testSessionId);
|
|
169
|
-
|
|
170
|
-
// Token should be valid
|
|
171
|
-
expect(await csrfManager.validateToken(token, testSessionId)).toBe(true);
|
|
172
|
-
|
|
173
|
-
// Clear session
|
|
174
|
-
await csrfManager.clearSession(testSessionId);
|
|
175
|
-
|
|
176
|
-
// Token should no longer be valid
|
|
177
|
-
expect(await csrfManager.validateToken(token, testSessionId)).toBe(false);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it('should handle getCurrentToken for existing session', async () => {
|
|
181
|
-
await csrfManager.generateToken(testSessionId);
|
|
182
|
-
const currentToken = await csrfManager.getCurrentToken(testSessionId);
|
|
183
|
-
|
|
184
|
-
expect(currentToken).toBeDefined();
|
|
185
|
-
expect(typeof currentToken).toBe('string');
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it('should handle getCurrentToken for new session', async () => {
|
|
189
|
-
const currentToken = await csrfManager.getCurrentToken('new-session-789');
|
|
190
|
-
|
|
191
|
-
expect(currentToken).toBeDefined();
|
|
192
|
-
expect(typeof currentToken).toBe('string');
|
|
193
|
-
expect(currentToken!.length).toBe(64);
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
describe('Token Lifecycle and Expiry', () => {
|
|
198
|
-
it('should handle token expiry', async () => {
|
|
199
|
-
// Mock Date.now to simulate time passage
|
|
200
|
-
const originalNow = Date.now;
|
|
201
|
-
const mockNow = vi.fn();
|
|
202
|
-
Date.now = mockNow;
|
|
203
|
-
|
|
204
|
-
try {
|
|
205
|
-
const initialTime = 1000000;
|
|
206
|
-
mockNow.mockReturnValue(initialTime);
|
|
207
|
-
|
|
208
|
-
const token = await generateCSRFToken(testSessionId);
|
|
209
|
-
|
|
210
|
-
// Token should be valid immediately
|
|
211
|
-
expect(await validateCSRFToken(token, testSessionId)).toBe(true);
|
|
212
|
-
|
|
213
|
-
// Regenerate token since it was consumed
|
|
214
|
-
const token2 = await generateCSRFToken(testSessionId);
|
|
215
|
-
|
|
216
|
-
// Simulate time passage beyond expiry (30 minutes + 1 second)
|
|
217
|
-
mockNow.mockReturnValue(initialTime + (30 * 60 * 1000) + 1000);
|
|
218
|
-
|
|
219
|
-
// Token should now be expired
|
|
220
|
-
expect(await validateCSRFToken(token2, testSessionId)).toBe(false);
|
|
221
|
-
} finally {
|
|
222
|
-
Date.now = originalNow;
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
describe('Security Features', () => {
|
|
228
|
-
it('should limit tokens per session', async () => {
|
|
229
|
-
// Generate maximum tokens for session
|
|
230
|
-
const tokens: string[] = [];
|
|
231
|
-
for (let i = 0; i < 15; i++) { // More than MAX_TOKENS_PER_SESSION (10)
|
|
232
|
-
const token = await generateCSRFToken(testSessionId);
|
|
233
|
-
tokens.push(token);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
expect(tokens.length).toBe(15);
|
|
237
|
-
expect(new Set(tokens).size).toBe(15); // All should be unique
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
it('should use cryptographically secure random values', async () => {
|
|
241
|
-
const calledValues: Uint8Array[] = [];
|
|
242
|
-
mockCrypto.getRandomValues.mockImplementation((array) => {
|
|
243
|
-
calledValues.push(new Uint8Array(array));
|
|
244
|
-
for (let i = 0; i < array.length; i++) {
|
|
245
|
-
array[i] = i % 256; // Deterministic but different values
|
|
246
|
-
}
|
|
247
|
-
return array;
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
await generateCSRFToken(testSessionId);
|
|
251
|
-
|
|
252
|
-
expect(mockCrypto.getRandomValues).toHaveBeenCalledTimes(1);
|
|
253
|
-
expect(calledValues[0].length).toBe(32); // 32 bytes
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
describe('Error Handling', () => {
|
|
258
|
-
it('should handle storage failures gracefully', async () => {
|
|
259
|
-
// Mock storage to fail
|
|
260
|
-
const { secureStorage } = await import('../../utils/secureStorage');
|
|
261
|
-
vi.mocked(secureStorage.setItem).mockRejectedValue(new Error('Storage failed'));
|
|
262
|
-
|
|
263
|
-
// Should still generate token despite storage failure
|
|
264
|
-
const token = await generateCSRFToken(testSessionId);
|
|
265
|
-
expect(token).toBeDefined();
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
it('should handle storage retrieval failures', async () => {
|
|
269
|
-
const { secureStorage } = await import('../../utils/secureStorage');
|
|
270
|
-
vi.mocked(secureStorage.getItem).mockRejectedValue(new Error('Retrieval failed'));
|
|
271
|
-
|
|
272
|
-
// Should still work by creating new tokens
|
|
273
|
-
const token = await getCSRFToken(testSessionId);
|
|
274
|
-
expect(token).toBeDefined();
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
it('should handle malformed stored data', async () => {
|
|
278
|
-
const { secureStorage } = await import('../../utils/secureStorage');
|
|
279
|
-
vi.mocked(secureStorage.getItem).mockResolvedValue('invalid-json');
|
|
280
|
-
|
|
281
|
-
// Should clear cache and continue working
|
|
282
|
-
const token = await getCSRFToken(testSessionId);
|
|
283
|
-
expect(token).toBeDefined();
|
|
284
|
-
});
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
describe('Type Safety', () => {
|
|
288
|
-
it('should have correct CSRFTokenData interface', () => {
|
|
289
|
-
const tokenData: CSRFTokenData = {
|
|
290
|
-
token: 'test-token',
|
|
291
|
-
sessionId: 'test-session',
|
|
292
|
-
timestamp: Date.now(),
|
|
293
|
-
used: false,
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
expect(tokenData.token).toBe('test-token');
|
|
297
|
-
expect(tokenData.sessionId).toBe('test-session');
|
|
298
|
-
expect(typeof tokenData.timestamp).toBe('number');
|
|
299
|
-
expect(typeof tokenData.used).toBe('boolean');
|
|
300
|
-
});
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
describe('Edge Cases', () => {
|
|
304
|
-
it('should handle very long session IDs', async () => {
|
|
305
|
-
const longSessionId = 'a'.repeat(500);
|
|
306
|
-
const token = await generateCSRFToken(longSessionId);
|
|
307
|
-
|
|
308
|
-
expect(token).toBeDefined();
|
|
309
|
-
expect(token.length).toBe(64);
|
|
310
|
-
|
|
311
|
-
// Should be valid for that session
|
|
312
|
-
const isValid = await validateCSRFToken(token, longSessionId);
|
|
313
|
-
expect(isValid).toBe(true);
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
it('should handle rapid token generation', async () => {
|
|
317
|
-
const tokens = await Promise.all(
|
|
318
|
-
Array.from({ length: 20 }, () => generateCSRFToken(testSessionId))
|
|
319
|
-
);
|
|
320
|
-
|
|
321
|
-
expect(tokens).toHaveLength(20);
|
|
322
|
-
// All should be unique 32-byte tokens
|
|
323
|
-
const uniqueTokens = new Set(tokens);
|
|
324
|
-
expect(uniqueTokens.size).toBe(20);
|
|
325
|
-
|
|
326
|
-
// Last token should be valid
|
|
327
|
-
const lastToken = tokens[tokens.length - 1];
|
|
328
|
-
expect(await validateCSRFToken(lastToken, testSessionId)).toBe(true);
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
it('should handle storage corruption by clearing cache', async () => {
|
|
332
|
-
const { secureStorage } = await import('../../utils/secureStorage');
|
|
333
|
-
|
|
334
|
-
// Simulate malformed storage data
|
|
335
|
-
vi.mocked(secureStorage.getItem).mockResolvedValueOnce('{{invalid json}');
|
|
336
|
-
|
|
337
|
-
// Should handle gracefully and generate new token
|
|
338
|
-
const token = await getCSRFToken(testSessionId);
|
|
339
|
-
expect(token).toBeDefined();
|
|
340
|
-
expect(typeof token).toBe('string');
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
it('should handle concurrent token generation safely', async () => {
|
|
344
|
-
const promises = Array.from({ length: 10 }, (_, i) =>
|
|
345
|
-
generateCSRFToken(`session-${i}`)
|
|
346
|
-
);
|
|
347
|
-
|
|
348
|
-
const tokens = await Promise.all(promises);
|
|
349
|
-
|
|
350
|
-
expect(tokens).toHaveLength(10);
|
|
351
|
-
expect(new Set(tokens).size).toBe(10);
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
it('should handle session ID collision edge cases', async () => {
|
|
355
|
-
const sameSession = 'collision-session';
|
|
356
|
-
|
|
357
|
-
const token1 = await generateCSRFToken(sameSession);
|
|
358
|
-
const token2 = await generateCSRFToken(sameSession);
|
|
359
|
-
|
|
360
|
-
// Both should be valid for same session
|
|
361
|
-
expect(await validateCSRFToken(token1, sameSession)).toBe(true);
|
|
362
|
-
expect(await validateCSRFToken(token2, sameSession)).toBe(true);
|
|
363
|
-
});
|
|
364
|
-
});
|
|
365
|
-
});
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { passwordSchema, securePasswordSchema, calculatePasswordStrength } from '../passwordSchema';
|
|
3
|
-
|
|
4
|
-
describe('Password Validation Schemas', () => {
|
|
5
|
-
describe('passwordSchema (Basic)', () => {
|
|
6
|
-
it('should validate passwords with minimum length', () => {
|
|
7
|
-
expect(passwordSchema.safeParse('123456').success).toBe(true);
|
|
8
|
-
expect(passwordSchema.safeParse('password').success).toBe(true);
|
|
9
|
-
expect(passwordSchema.safeParse('StrongPass123!').success).toBe(true);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it('should reject passwords that are too short', () => {
|
|
13
|
-
expect(passwordSchema.safeParse('12345').success).toBe(false);
|
|
14
|
-
expect(passwordSchema.safeParse('').success).toBe(false);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('should reject passwords that are too long', () => {
|
|
18
|
-
const longPassword = 'a'.repeat(129);
|
|
19
|
-
expect(passwordSchema.safeParse(longPassword).success).toBe(false);
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
describe('securePasswordSchema (Strict)', () => {
|
|
24
|
-
it('should validate strong passwords', () => {
|
|
25
|
-
expect(securePasswordSchema.safeParse('StrongPass123!').success).toBe(true);
|
|
26
|
-
expect(securePasswordSchema.safeParse('Complex@Password1').success).toBe(true);
|
|
27
|
-
expect(securePasswordSchema.safeParse('MySecureP@ssw0rd').success).toBe(true);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should reject weak passwords', () => {
|
|
31
|
-
expect(securePasswordSchema.safeParse('weak').success).toBe(false);
|
|
32
|
-
expect(securePasswordSchema.safeParse('12345678').success).toBe(false);
|
|
33
|
-
expect(securePasswordSchema.safeParse('').success).toBe(false);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('should reject passwords without uppercase letters', () => {
|
|
37
|
-
expect(securePasswordSchema.safeParse('password123!').success).toBe(false);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('should reject passwords without lowercase letters', () => {
|
|
41
|
-
expect(securePasswordSchema.safeParse('PASSWORD123!').success).toBe(false);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should reject passwords without numbers', () => {
|
|
45
|
-
expect(securePasswordSchema.safeParse('Password!').success).toBe(false);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('should reject passwords without special characters', () => {
|
|
49
|
-
expect(securePasswordSchema.safeParse('Password123').success).toBe(false);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('should reject passwords that are too short', () => {
|
|
53
|
-
expect(securePasswordSchema.safeParse('Pass1!').success).toBe(false);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('should reject common passwords', () => {
|
|
57
|
-
expect(securePasswordSchema.safeParse('password').success).toBe(false);
|
|
58
|
-
expect(securePasswordSchema.safeParse('123456').success).toBe(false);
|
|
59
|
-
expect(securePasswordSchema.safeParse('qwerty').success).toBe(false);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('should provide meaningful error messages', () => {
|
|
63
|
-
const result = securePasswordSchema.safeParse('weak');
|
|
64
|
-
expect(result.success).toBe(false);
|
|
65
|
-
if (!result.success) {
|
|
66
|
-
expect(result.error.issues.length).toBeGreaterThan(0);
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
describe('calculatePasswordStrength', () => {
|
|
72
|
-
it('should calculate strength for strong passwords', () => {
|
|
73
|
-
const result = calculatePasswordStrength('StrongPass123!');
|
|
74
|
-
expect(result.score).toBeGreaterThan(70);
|
|
75
|
-
expect(result.level).toBe('strong');
|
|
76
|
-
expect(result.feedback).toHaveLength(0);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('should calculate strength for weak passwords', () => {
|
|
80
|
-
const result = calculatePasswordStrength('weak');
|
|
81
|
-
expect(result.score).toBeLessThan(50);
|
|
82
|
-
expect(result.level).toBe('very-weak');
|
|
83
|
-
expect(result.feedback.length).toBeGreaterThan(0);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('should provide feedback for improvement', () => {
|
|
87
|
-
const result = calculatePasswordStrength('password');
|
|
88
|
-
expect(result.feedback).toContain('Add uppercase letters');
|
|
89
|
-
expect(result.feedback).toContain('Add numbers');
|
|
90
|
-
expect(result.feedback).toContain('Add special characters');
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('should penalize common passwords', () => {
|
|
94
|
-
const result = calculatePasswordStrength('password');
|
|
95
|
-
expect(result.feedback).toContain('Avoid common passwords');
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
describe('Edge Cases', () => {
|
|
100
|
-
it('should handle empty string password', () => {
|
|
101
|
-
expect(passwordSchema.safeParse('').success).toBe(false);
|
|
102
|
-
expect(securePasswordSchema.safeParse('').success).toBe(false);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('should handle whitespace-only passwords', () => {
|
|
106
|
-
expect(passwordSchema.safeParse(' ').success).toBe(true);
|
|
107
|
-
expect(securePasswordSchema.safeParse(' ').success).toBe(false);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('should handle exactly 6 character passwords', () => {
|
|
111
|
-
expect(passwordSchema.safeParse('123456').success).toBe(true);
|
|
112
|
-
expect(securePasswordSchema.safeParse('123456').success).toBe(false);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('should handle exactly 128 character passwords', () => {
|
|
116
|
-
const password = 'a'.repeat(128);
|
|
117
|
-
expect(passwordSchema.safeParse(password).success).toBe(true);
|
|
118
|
-
expect(securePasswordSchema.safeParse(password).success).toBe(false);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('should handle unicode passwords', () => {
|
|
122
|
-
const unicodePassword = 'Test🔒Pass123!';
|
|
123
|
-
expect(passwordSchema.safeParse(unicodePassword).success).toBe(true);
|
|
124
|
-
// Secure schema might not pass if unicode doesn't include uppercase/numbers
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('should handle passwords with special unicode characters', () => {
|
|
128
|
-
const password = 'Pass123😊!';
|
|
129
|
-
expect(passwordSchema.safeParse(password).success).toBe(true);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('should reject passwords with null characters', () => {
|
|
133
|
-
const nullPassword = 'Pass123!\x00';
|
|
134
|
-
// The schema may actually allow null bytes, so we just test it doesn't crash
|
|
135
|
-
expect(() => securePasswordSchema.safeParse(nullPassword)).not.toThrow();
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it('should handle passwords with maximum variety', () => {
|
|
139
|
-
const complexPassword = 'Complex123!@#$%^&*()[]';
|
|
140
|
-
expect(securePasswordSchema.safeParse(complexPassword).success).toBe(true);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('should calculate strength for very long passwords', () => {
|
|
144
|
-
const longPassword = 'A'.repeat(100) + '1!';
|
|
145
|
-
const result = calculatePasswordStrength(longPassword);
|
|
146
|
-
|
|
147
|
-
expect(result.score).toBeGreaterThan(50);
|
|
148
|
-
expect(result.level).toBeDefined();
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('should detect sequential patterns', () => {
|
|
152
|
-
const sequentialPassword = 'qwerty123!';
|
|
153
|
-
const result = calculatePasswordStrength(sequentialPassword);
|
|
154
|
-
|
|
155
|
-
expect(result.level).not.toBe('strong');
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it('should recognize keyboard pattern passwords', () => {
|
|
159
|
-
const keyboardPattern = 'asdfgh123!';
|
|
160
|
-
const result = calculatePasswordStrength(keyboardPattern);
|
|
161
|
-
|
|
162
|
-
// Should penalize sequential keyboard patterns
|
|
163
|
-
expect(result.score).toBeLessThan(80);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it('should handle mixed case passwords correctly', () => {
|
|
167
|
-
const mixedCasePassword = 'PaSsWoRd123!';
|
|
168
|
-
expect(securePasswordSchema.safeParse(mixedCasePassword).success).toBe(true);
|
|
169
|
-
|
|
170
|
-
const result = calculatePasswordStrength(mixedCasePassword);
|
|
171
|
-
expect(result.score).toBeGreaterThan(40);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it('should handle passwords with repeat characters', () => {
|
|
175
|
-
const repeatingPassword = 'Aaaa1111!';
|
|
176
|
-
expect(securePasswordSchema.safeParse(repeatingPassword).success).toBe(true);
|
|
177
|
-
|
|
178
|
-
const result = calculatePasswordStrength(repeatingPassword);
|
|
179
|
-
expect(result.score).toBeLessThan(result.score + 50); // Should still score reasonably
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it('should validate passwords with only required characters (minimum secure)', () => {
|
|
183
|
-
// Minimum secure password: 8 chars with uppercase, lowercase, number, and special
|
|
184
|
-
const minSecurePassword = 'A1!bcdef';
|
|
185
|
-
expect(securePasswordSchema.safeParse(minSecurePassword).success).toBe(true);
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it('should reject secure schema passwords missing each requirement', () => {
|
|
189
|
-
// Test one requirement at a time to avoid confusion
|
|
190
|
-
const result1 = securePasswordSchema.safeParse('PASSWORD!');
|
|
191
|
-
expect(result1.success).toBe(false); // No lowercase
|
|
192
|
-
|
|
193
|
-
const result2 = securePasswordSchema.safeParse('password1!');
|
|
194
|
-
expect(result2.success).toBe(false); // No uppercase
|
|
195
|
-
|
|
196
|
-
const result3 = securePasswordSchema.safeParse('Password!');
|
|
197
|
-
expect(result3.success).toBe(false); // No number
|
|
198
|
-
|
|
199
|
-
const result4 = securePasswordSchema.safeParse('Password1');
|
|
200
|
-
expect(result4.success).toBe(false); // No special
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
});
|