@jmruthers/pace-core 0.6.9 → 0.6.11
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 +21 -0
- package/audit-tool/00-dependencies.cjs +46 -13
- package/audit-tool/audits/01-pace-core-compliance.cjs +96 -21
- package/audit-tool/audits/02-project-structure.cjs +74 -2
- package/audit-tool/audits/03-architecture.cjs +220 -20
- package/audit-tool/audits/04-code-quality.cjs +95 -3
- package/audit-tool/audits/05-styling.cjs +19 -7
- package/audit-tool/audits/06-security-rbac.cjs +214 -25
- package/audit-tool/audits/07-api-tech-stack.cjs +31 -15
- package/audit-tool/audits/08-testing-documentation.cjs +11 -3
- package/audit-tool/audits/09-operations.cjs +19 -7
- package/audit-tool/index.cjs +22 -11
- package/audit-tool/utils/report-utils.cjs +4 -0
- package/cursor-rules/01-pace-core-compliance.mdc +1 -0
- package/cursor-rules/02-project-structure.mdc +3 -26
- package/cursor-rules/03-architecture.mdc +3 -1
- package/cursor-rules/04-code-quality.mdc +1 -0
- package/cursor-rules/05-styling.mdc +120 -8
- package/cursor-rules/06-security-rbac.mdc +126 -2
- package/cursor-rules/07-api-tech-stack.mdc +1 -0
- package/cursor-rules/08-testing-documentation.mdc +1 -0
- package/cursor-rules/09-operations.mdc +1 -0
- package/dist/DataTable-EFYP2QLE.js +16 -0
- package/dist/InactivityServiceProvider-BbxwwDz1.d.ts +308 -0
- package/dist/UnifiedAuthProvider-Bkt_tzdS.d.ts +183 -0
- package/dist/api-BZR2CYXL.js +5 -0
- package/dist/api-result-USV1Czr-.d.ts +51 -0
- package/dist/assets/app-icons/admin_favicon.svg +462 -0
- package/dist/assets/app-icons/base_favicon.svg +85 -0
- package/dist/assets/app-icons/cake_favicon.svg +68 -0
- package/dist/assets/app-icons/core_favicon.svg +256 -0
- package/dist/assets/app-icons/gear_favicon.svg +91 -0
- package/dist/assets/app-icons/medi_favicon.svg +92 -0
- package/dist/assets/app-icons/mint_favicon.svg +83 -0
- package/dist/assets/app-icons/pace_favicon.svg +49 -0
- package/dist/assets/app-icons/pump_favicon.svg +68 -0
- package/dist/assets/app-icons/seed_favicon.svg +91 -0
- package/dist/assets/app-icons/team_favicon.svg +67 -0
- package/dist/assets/app-icons/trac_favicon.svg +112 -0
- package/dist/assets/app-icons/trip_favicon.svg +102 -0
- package/dist/audit-HI2DHUVU.js +4 -0
- package/dist/auth-JvdRVaud.d.ts +49 -0
- package/dist/chunk-2DL2WSOE.js +327 -0
- package/dist/chunk-2OEVOGGR.js +9598 -0
- package/dist/chunk-44CNXN4P.js +15 -0
- package/dist/chunk-4R3T5ENU.js +2943 -0
- package/dist/chunk-7A6IMHH2.js +2321 -0
- package/dist/chunk-BTHN5MKC.js +121 -0
- package/dist/chunk-CU2BU2MQ.js +2 -0
- package/dist/chunk-D6BMFMQZ.js +200 -0
- package/dist/chunk-DDMPHZ3D.js +58 -0
- package/dist/chunk-ENLXB7GP.js +721 -0
- package/dist/chunk-J2KQK6DG.js +2159 -0
- package/dist/chunk-KJXRL3XE.js +6434 -0
- package/dist/chunk-L5LFKKLJ.js +61 -0
- package/dist/chunk-PCSHBLPB.js +811 -0
- package/dist/chunk-QRYSEPHB.js +429 -0
- package/dist/chunk-RMLY6KB5.js +187 -0
- package/dist/chunk-SACF5YSM.js +31 -0
- package/dist/chunk-UZNAFKGW.js +125 -0
- package/dist/chunk-V7FTM2LU.js +1080 -0
- package/dist/chunk-WY6Y7KC3.js +264 -0
- package/dist/chunk-XOJME5T7.js +407 -0
- package/dist/chunk-XPFVT3GN.js +492 -0
- package/dist/chunk-YFTFFJIV.js +529 -0
- package/dist/chunk-YYTWKVHO.js +1334 -0
- package/dist/components.d.ts +12 -89
- package/dist/components.js +23 -55
- package/dist/database.generated-qkdoiVrJ.d.ts +9441 -0
- package/dist/eslint-rules/index.cjs +3 -0
- package/dist/eslint-rules/rules/03-architecture.cjs +74 -0
- package/dist/eslint-rules/rules/05-styling.cjs +507 -0
- package/dist/eslint-rules/rules/06-security-rbac.cjs +84 -0
- package/dist/event-BfCox3N2.d.ts +265 -0
- package/dist/file-reference-DU1hcawx.d.ts +164 -0
- package/dist/functions-DH45k8ec.d.ts +208 -0
- package/dist/hooks.d.ts +28 -14
- package/dist/hooks.js +90 -56
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +392 -155
- package/dist/index.js +337 -347
- package/dist/pagination-BW1mqywp.d.ts +201 -0
- package/dist/papaparseLoader-WG2UXQ22.js +7 -0
- package/dist/providers.d.ts +29 -14
- package/dist/providers.js +7 -5
- package/dist/rbac/eslint-rules.js +2 -2
- package/dist/rbac/index.d.ts +180 -351
- package/dist/rbac/index.js +13 -11
- package/dist/theming/runtime.d.ts +28 -5
- package/dist/theming/runtime.js +2 -2
- package/dist/timezone-BTWWXKVY.d.ts +696 -0
- package/dist/types-BE2sEHKd.d.ts +55 -0
- package/dist/types-CvOPXWWZ.d.ts +111 -0
- package/dist/types-Dr8sNhER.d.ts +50 -0
- package/dist/types.d.ts +20 -13
- package/dist/types.js +1 -0
- package/dist/usePublicPageContext-B91dGYW1.d.ts +4367 -0
- package/dist/usePublicRouteParams-BgV6VhMi.d.ts +946 -0
- package/dist/utils.d.ts +338 -156
- package/dist/utils.js +78 -60
- package/dist/validation-g5n0hDkh.d.ts +177 -0
- package/docs/api/modules.md +1226 -1094
- package/docs/api-reference/components.md +5 -5
- package/docs/api-reference/rpc-functions.md +12 -3
- package/docs/core-concepts/rbac-system.md +8 -0
- package/docs/getting-started/cursor-rules.md +17 -20
- package/docs/getting-started/dependencies.md +1 -1
- package/docs/getting-started/setup.md +235 -0
- package/docs/implementation-guides/authentication.md +27 -0
- package/docs/implementation-guides/data-tables.md +365 -10
- package/docs/migration/ApiResult-migration.md +25 -0
- package/docs/rbac/RBAC_CONTRACT.md +0 -12
- package/docs/rbac/api-reference.md +33 -31
- package/docs/standards/0-standards-overview.md +50 -15
- package/docs/standards/1-pace-core-compliance-standards.md +62 -57
- package/docs/standards/2-project-structure-standards.md +45 -90
- package/docs/standards/3-architecture-standards.md +41 -1
- package/docs/standards/4-code-quality-standards.md +26 -6
- package/docs/standards/5-styling-standards.md +35 -1
- package/docs/standards/6-security-rbac-standards.md +288 -7
- package/docs/standards/7-api-tech-stack-standards.md +116 -17
- package/docs/standards/8-testing-documentation-standards.md +31 -0
- package/docs/standards/9-operations-standards.md +19 -0
- package/docs/standards/README.md +20 -201
- package/docs/testing/README.md +10 -0
- package/docs/testing/test-setup-for-consumers.md +916 -0
- package/docs/troubleshooting/common-issues.md +17 -1
- package/docs/troubleshooting/organisation-context-setup.md +8 -0
- package/docs/troubleshooting/print-event-name-css-variable-analysis.md +217 -0
- package/eslint-config-pace-core.cjs +24 -0
- package/package.json +14 -20
- package/scripts/build-docs.js +180 -0
- package/scripts/setup.cjs +536 -0
- package/scripts/validate.cjs +480 -0
- package/src/__mocks__/lucide-react.ts +0 -2
- package/src/__tests__/helpers/component-test-utils.test.tsx +260 -0
- package/src/__tests__/helpers/optimized-test-setup.test.ts +224 -0
- package/src/__tests__/helpers/supabaseMock.test.ts +273 -0
- package/src/__tests__/helpers/test-providers.test.tsx +99 -0
- package/src/__tests__/helpers/test-providers.tsx +37 -39
- package/src/__tests__/helpers/test-utils.test.tsx +447 -0
- package/src/__tests__/helpers/timer-utils.test.ts +371 -0
- package/src/assets/app-icons/admin_favicon.svg +462 -0
- package/src/assets/app-icons/base_favicon.svg +85 -0
- package/src/assets/app-icons/cake_favicon.svg +68 -0
- package/src/assets/app-icons/core_favicon.svg +256 -0
- package/src/assets/app-icons/gear_favicon.svg +91 -0
- package/src/assets/app-icons/index.test.ts +304 -0
- package/src/assets/app-icons/index.ts +83 -0
- package/src/assets/app-icons/medi_favicon.svg +92 -0
- package/src/assets/app-icons/mint_favicon.svg +83 -0
- package/src/assets/app-icons/pace_favicon.svg +49 -0
- package/src/assets/app-icons/pump_favicon.svg +68 -0
- package/src/assets/app-icons/seed_favicon.svg +91 -0
- package/src/assets/app-icons/team_favicon.svg +67 -0
- package/src/assets/app-icons/trac_favicon.svg +112 -0
- package/src/assets/app-icons/trip_favicon.svg +102 -0
- package/src/components/AddressField/AddressField.test.tsx +379 -4
- package/src/components/AddressField/AddressField.tsx +239 -213
- package/src/components/AddressField/types.ts +2 -2
- package/src/components/Alert/Alert.test.tsx +35 -25
- package/src/components/Alert/Alert.tsx +8 -8
- package/src/components/AppSwitcher/AppSwitcher.test.tsx +1250 -0
- package/src/components/AppSwitcher/AppSwitcher.tsx +315 -0
- package/src/components/Avatar/Avatar.test.tsx +11 -1
- package/src/components/Avatar/Avatar.tsx +3 -2
- package/src/components/Badge/Badge.test.tsx +11 -1
- package/src/components/Button/Button.test.tsx +13 -3
- package/src/components/Button/Button.tsx +1 -1
- package/src/components/Calendar/Calendar.test.tsx +523 -131
- package/src/components/Calendar/Calendar.tsx +107 -488
- package/src/components/Card/Card.test.tsx +384 -258
- package/src/components/Card/Card.tsx +19 -10
- package/src/components/Checkbox/Checkbox.test.tsx +58 -174
- package/src/components/ContextSelector/ContextSelector.internals.tsx +204 -0
- package/src/components/ContextSelector/ContextSelector.test.tsx +360 -0
- package/src/components/ContextSelector/ContextSelector.tsx +66 -280
- package/src/components/ContextSelector/ContextSelector.types.ts +35 -0
- package/src/components/ContextSelector/useContextSelectorState.tsx +195 -0
- package/src/components/DataTable/AUDIT_REPORT.md +59 -44
- package/src/components/DataTable/DataTable.comprehensive.test.tsx +759 -0
- package/src/components/DataTable/DataTable.default-state.test.tsx +524 -0
- package/src/components/DataTable/DataTable.export.test.tsx +705 -0
- package/src/components/DataTable/DataTable.grouping-aggregation.test.tsx +658 -0
- package/src/components/DataTable/DataTable.hooks.test.tsx +192 -0
- package/src/components/DataTable/DataTable.select-label-display.test.tsx +485 -0
- package/src/components/DataTable/DataTable.test.tsx +787 -416
- package/src/components/DataTable/DataTable.tsx +14 -14
- package/src/components/DataTable/DataTableCore.integration.test.tsx +458 -0
- package/src/components/DataTable/DataTableCore.test-setup.ts +221 -0
- package/src/components/DataTable/DataTableCore.test.tsx +970 -0
- package/src/components/DataTable/README.md +155 -0
- package/src/components/DataTable/TESTING.md +101 -0
- package/src/components/DataTable/a11y.basic.test.tsx +788 -0
- package/src/components/DataTable/components/DataTableCore.tsx +126 -894
- package/src/components/DataTable/components/GroupingDropdown.test.tsx +621 -0
- package/src/components/DataTable/components/GroupingDropdown.tsx +2 -3
- package/src/components/DataTable/components/ImportModal.tsx +82 -408
- package/src/components/DataTable/components/ImportModalFileSection.tsx +148 -0
- package/src/components/DataTable/context/DataTableContext.test.tsx +328 -0
- package/src/components/DataTable/context/DataTableContext.tsx +13 -13
- package/src/components/DataTable/core/ColumnFactory.test.ts +403 -0
- package/src/components/DataTable/core/ColumnFactory.ts +3 -3
- package/src/components/DataTable/hooks/useColumnOrderPersistence.test.ts +516 -0
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +12 -9
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.test.ts +256 -0
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +12 -9
- package/src/components/DataTable/hooks/useDataTableConfiguration.test.ts +297 -0
- package/src/components/DataTable/hooks/useDataTableConfiguration.ts +15 -3
- package/src/components/DataTable/hooks/useDataTableDataPipeline.test.ts +270 -0
- package/src/components/DataTable/hooks/useDataTableDeletionBatching.test.ts +127 -0
- package/src/components/DataTable/hooks/useDataTableDeletionBatching.ts +106 -0
- package/src/components/DataTable/hooks/useDataTableEffectiveActions.test.ts +461 -0
- package/src/components/DataTable/hooks/useDataTableEffectiveActions.ts +238 -0
- package/src/components/DataTable/hooks/useDataTableLayoutHandlers.test.ts +296 -0
- package/src/components/DataTable/hooks/useDataTableLayoutHandlers.ts +175 -0
- package/src/components/DataTable/hooks/useDataTablePaginationSync.test.ts +203 -0
- package/src/components/DataTable/hooks/useDataTablePaginationSync.ts +109 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.test.ts +280 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +81 -260
- package/src/components/DataTable/hooks/useDataTablePipeline.test.tsx +219 -0
- package/src/components/DataTable/hooks/useDataTablePipeline.tsx +239 -0
- package/src/components/DataTable/hooks/useDataTableRenderGuard.test.tsx +316 -0
- package/src/components/DataTable/hooks/useDataTableRenderGuard.tsx +195 -0
- package/src/components/DataTable/hooks/useDataTableScope.test.ts +110 -0
- package/src/components/DataTable/hooks/useDataTableScope.ts +123 -0
- package/src/components/DataTable/hooks/useDataTableState.test.ts +733 -0
- package/src/components/DataTable/hooks/useDataTableState.ts +161 -114
- package/src/components/DataTable/hooks/useDataTableStateAndPersistence.test.ts +277 -0
- package/src/components/DataTable/hooks/useDataTableStateAndPersistence.ts +222 -0
- package/src/components/DataTable/hooks/useDataTableSuperAdmin.test.ts +93 -0
- package/src/components/DataTable/hooks/useDataTableSuperAdmin.ts +86 -0
- package/src/components/DataTable/hooks/useDataTableTableInstance.test.ts +185 -0
- package/src/components/DataTable/hooks/useDataTableTableInstance.ts +178 -0
- package/src/components/DataTable/hooks/useEffectiveColumnOrder.test.ts +183 -0
- package/src/components/DataTable/hooks/useHierarchicalState.test.ts +294 -0
- package/src/components/DataTable/hooks/useImportModalFocus.test.ts +184 -0
- package/src/components/DataTable/hooks/useImportModalFocus.ts +53 -0
- package/src/components/DataTable/hooks/useImportModalState.test.ts +390 -0
- package/src/components/DataTable/hooks/useImportModalState.ts +345 -0
- package/src/components/DataTable/hooks/useKeyboardNavigation.test.ts +787 -0
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +311 -271
- package/src/components/DataTable/hooks/usePermissionTracking.test.ts +381 -0
- package/src/components/DataTable/hooks/usePermissionTracking.ts +122 -0
- package/src/components/DataTable/hooks/useServerSideDataEffect.test.ts +258 -0
- package/src/components/DataTable/hooks/useServerSideDataEffect.ts +27 -4
- package/src/components/DataTable/hooks/useTableColumns.test.ts +499 -0
- package/src/components/DataTable/hooks/useTableColumns.ts +15 -39
- package/src/components/DataTable/hooks/useTableHandlers.test.ts +461 -0
- package/src/components/DataTable/hooks/useTableHandlers.ts +13 -22
- package/src/components/DataTable/index.ts +28 -5
- package/src/components/DataTable/keyboard.test.tsx +734 -0
- package/src/components/DataTable/mocks/MockRBACProvider.tsx +66 -0
- package/src/components/DataTable/pagination.modes.test.tsx +728 -0
- package/src/components/DataTable/ssr.strict-mode.test.tsx +319 -0
- package/src/components/DataTable/styles.test.ts +379 -0
- package/src/components/DataTable/styles.ts +0 -1
- package/src/components/DataTable/test-utils/MockDataTableComponents.tsx +55 -0
- package/src/components/DataTable/test-utils/dataFactories.ts +103 -0
- package/src/components/DataTable/test-utils/featureConfig.ts +10 -0
- package/src/components/DataTable/test-utils/sharedTestUtils.ts +419 -0
- package/src/components/DataTable/test-utils.ts +94 -0
- package/src/components/DataTable/types/actions.ts +71 -0
- package/src/components/DataTable/types/base.ts +39 -0
- package/src/components/DataTable/types/columns.ts +125 -0
- package/src/components/DataTable/types/export.ts +32 -0
- package/src/components/DataTable/types/features.ts +81 -0
- package/src/components/DataTable/types/hierarchical.ts +44 -0
- package/src/components/DataTable/types/index.ts +43 -0
- package/src/components/DataTable/types/pagination.ts +85 -0
- package/src/components/DataTable/types/performance.ts +47 -0
- package/src/components/DataTable/types/props.ts +62 -0
- package/src/components/DataTable/types/rbac.ts +45 -0
- package/src/components/DataTable/ui/layout/DataTableCore.test.tsx +1194 -0
- package/src/components/DataTable/ui/layout/DataTableCore.tsx +345 -0
- package/src/components/DataTable/ui/layout/DataTableErrorBoundary.test.tsx +438 -0
- package/src/components/DataTable/ui/layout/DataTableErrorBoundary.tsx +225 -0
- package/src/components/DataTable/ui/layout/DataTableLayout.test.tsx +1352 -0
- package/src/components/DataTable/ui/layout/DataTableLayout.tsx +661 -0
- package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.test.tsx +91 -0
- package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.tsx +43 -0
- package/src/components/DataTable/ui/modals/DataTableModals.test.tsx +749 -0
- package/src/components/DataTable/ui/modals/DataTableModals.tsx +341 -0
- package/src/components/DataTable/ui/modals/ImportModal.test.tsx +1834 -0
- package/src/components/DataTable/ui/modals/ImportModal.tsx +197 -0
- package/src/components/DataTable/ui/modals/ImportModalFailedRowsSection.tsx +60 -0
- package/src/components/DataTable/ui/modals/ImportModalFileSection.tsx +148 -0
- package/src/components/DataTable/ui/modals/ImportModalPreviewSection.tsx +60 -0
- package/src/components/DataTable/ui/modals/ImportModalSummarySection.tsx +59 -0
- package/src/components/DataTable/ui/modals/importModalPersistence.ts +73 -0
- package/src/components/DataTable/ui/shared/AccessDeniedPage.test.tsx +245 -0
- package/src/components/DataTable/ui/shared/AccessDeniedPage.tsx +159 -0
- package/src/components/DataTable/ui/shared/ActionButtons.test.tsx +921 -0
- package/src/components/DataTable/ui/shared/ActionButtons.tsx +195 -0
- package/src/components/DataTable/ui/shared/ColumnFilter.test.tsx +497 -0
- package/src/components/DataTable/ui/shared/ColumnFilter.tsx +113 -0
- package/src/components/DataTable/ui/shared/PaginationControls.test.tsx +451 -0
- package/src/components/DataTable/ui/shared/PaginationControls.tsx +291 -0
- package/src/components/DataTable/ui/shared/SortIndicator.test.tsx +135 -0
- package/src/components/DataTable/ui/shared/SortIndicator.tsx +50 -0
- package/src/components/DataTable/ui/table/EditFields.test.tsx +526 -0
- package/src/components/DataTable/ui/table/EditFields.tsx +355 -0
- package/src/components/DataTable/ui/table/EditableRow.test.tsx +1003 -0
- package/src/components/DataTable/ui/table/EditableRow.tsx +444 -0
- package/src/components/DataTable/ui/table/EmptyState.test.tsx +360 -0
- package/src/components/DataTable/ui/table/EmptyState.tsx +74 -0
- package/src/components/DataTable/ui/table/FilterRow.test.tsx +416 -0
- package/src/components/DataTable/ui/table/FilterRow.tsx +148 -0
- package/src/components/DataTable/ui/table/LoadingState.test.tsx +77 -0
- package/src/components/DataTable/ui/table/LoadingState.tsx +17 -0
- package/src/components/DataTable/ui/table/RowComponent.test.tsx +1024 -0
- package/src/components/DataTable/ui/table/RowComponent.tsx +429 -0
- package/src/components/DataTable/ui/table/UnifiedTableBody.test.tsx +1273 -0
- package/src/components/DataTable/ui/table/UnifiedTableBody.tsx +440 -0
- package/src/components/DataTable/ui/table/cellValueUtils.test.ts +453 -0
- package/src/components/DataTable/ui/table/cellValueUtils.ts +40 -0
- package/src/components/DataTable/ui/toolbar/BulkOperationsDropdown.test.tsx +551 -0
- package/src/components/DataTable/ui/toolbar/BulkOperationsDropdown.tsx +160 -0
- package/src/components/DataTable/ui/toolbar/ColumnVisibilityDropdown.test.tsx +751 -0
- package/src/components/DataTable/ui/toolbar/ColumnVisibilityDropdown.tsx +114 -0
- package/src/components/DataTable/ui/toolbar/DataTableToolbar.test.tsx +629 -0
- package/src/components/DataTable/ui/toolbar/DataTableToolbar.tsx +271 -0
- package/src/components/DataTable/ui/toolbar/GroupingDropdown.test.tsx +621 -0
- package/src/components/DataTable/ui/toolbar/GroupingDropdown.tsx +107 -0
- package/src/components/DataTable/utils/a11yUtils.test.ts +548 -0
- package/src/components/DataTable/utils/a11yUtils.ts +1 -1
- package/src/components/DataTable/utils/aggregationUtils.test.ts +288 -0
- package/src/components/DataTable/utils/aggregationUtils.ts +5 -5
- package/src/components/DataTable/utils/columnUtils.test.ts +94 -0
- package/src/components/DataTable/utils/csvParse.test.ts +74 -0
- package/src/components/DataTable/utils/csvParse.ts +65 -0
- package/src/components/DataTable/utils/errorHandling.test.ts +209 -0
- package/src/components/DataTable/utils/errorHandling.ts +3 -1
- package/src/components/DataTable/utils/exportUtils.test.ts +954 -0
- package/src/components/DataTable/utils/exportUtils.ts +1 -1
- package/src/components/DataTable/utils/flexibleImport.test.ts +573 -0
- package/src/components/DataTable/utils/flexibleImport.ts +3 -186
- package/src/components/DataTable/utils/hierarchicalSorting.test.ts +235 -0
- package/src/components/DataTable/utils/hierarchicalSorting.ts +3 -3
- package/src/components/DataTable/utils/hierarchicalUtils.test.ts +586 -0
- package/src/components/DataTable/utils/importDateParser.test.ts +162 -0
- package/src/components/DataTable/utils/importDateParser.ts +114 -0
- package/src/components/DataTable/utils/importValueParser.test.ts +138 -0
- package/src/components/DataTable/utils/importValueParser.ts +91 -0
- package/src/components/DataTable/utils/paginationUtils.test.ts +593 -0
- package/src/components/DataTable/utils/paginationUtils.ts +7 -4
- package/src/components/DataTable/utils/performanceUtils.test.ts +470 -0
- package/src/components/DataTable/utils/performanceUtils.ts +1 -1
- package/src/components/DataTable/utils/rowUtils.test.ts +235 -0
- package/src/components/DataTable/utils/selectFieldUtils.test.ts +271 -0
- package/src/components/DataTable/utils/selectFieldUtils.ts +97 -67
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +18 -25
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +1 -1
- package/src/components/DateTimeField/DateTimeField.test.tsx +3 -16
- package/src/components/DateTimeField/DateTimeField.tsx +1 -1
- package/src/components/Dialog/Dialog.test-utils.ts +49 -0
- package/src/components/Dialog/Dialog.test.tsx +2865 -458
- package/src/components/Dialog/Dialog.tsx +183 -986
- package/src/components/Dialog/dialogLock.test.ts +238 -0
- package/src/components/Dialog/dialogLock.ts +98 -0
- package/src/components/Dialog/index.ts +2 -0
- package/src/components/Dialog/useDialogDimensions.test.ts +163 -0
- package/src/components/Dialog/useDialogDimensions.ts +140 -0
- package/src/components/Dialog/useDialogLifecycle.test.ts +358 -0
- package/src/components/Dialog/useDialogLifecycle.ts +135 -0
- package/src/components/Dialog/useDialogPersistence.test.ts +381 -0
- package/src/components/Dialog/useDialogPersistence.ts +357 -0
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +2 -62
- package/src/components/ErrorBoundary/ErrorBoundaryContext.context.ts +17 -0
- package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +2 -45
- package/src/components/ErrorBoundary/ErrorBoundaryContext.types.ts +41 -0
- package/src/components/ErrorBoundary/index.ts +3 -4
- package/src/components/ErrorBoundary/useErrorBoundaryContext.ts +20 -0
- package/src/components/FileDisplay/FileDisplay.test.tsx +479 -247
- package/src/components/FileDisplay/FileDisplay.tsx +29 -659
- package/src/components/FileDisplay/FileDisplayContent.test.tsx +395 -0
- package/src/components/FileDisplay/FileDisplayContent.tsx +242 -0
- package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.test.tsx +74 -0
- package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.tsx +38 -0
- package/src/components/FileDisplay/FileDisplayEmptyView.test.tsx +33 -0
- package/src/components/FileDisplay/FileDisplayEmptyView.tsx +33 -0
- package/src/components/FileDisplay/FileDisplayErrorView.test.tsx +71 -0
- package/src/components/FileDisplay/FileDisplayErrorView.tsx +50 -0
- package/src/components/FileDisplay/FileDisplayLoadingFallbackView.test.tsx +22 -0
- package/src/components/FileDisplay/FileDisplayLoadingFallbackView.tsx +22 -0
- package/src/components/FileDisplay/FileDisplayLoadingView.test.tsx +21 -0
- package/src/components/FileDisplay/FileDisplayLoadingView.tsx +23 -0
- package/src/components/FileDisplay/FileDisplayMultipleFilesView.test.tsx +101 -0
- package/src/components/FileDisplay/FileDisplayMultipleFilesView.tsx +109 -0
- package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.test.tsx +58 -0
- package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.tsx +48 -0
- package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.test.tsx +111 -0
- package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.tsx +270 -0
- package/src/components/FileDisplay/FileDisplaySingleImageView.test.tsx +78 -0
- package/src/components/FileDisplay/FileDisplaySingleImageView.tsx +67 -0
- package/src/components/FileDisplay/fallbackUtils.test.ts +50 -0
- package/src/components/FileDisplay/fallbackUtils.ts +44 -0
- package/src/components/FileDisplay/fetchFileDisplayData.ts +24 -0
- package/src/components/FileDisplay/fetchFileDisplayData.unit.test.ts +183 -0
- package/src/components/FileDisplay/fileDisplayUtils.test.ts +58 -0
- package/src/components/FileDisplay/fileDisplayUtils.ts +24 -0
- package/src/components/FileDisplay/index.tsx +1 -1
- package/src/components/FileDisplay/useFileDisplay.test.ts +538 -0
- package/src/components/FileDisplay/useFileDisplay.ts +515 -0
- package/src/components/FileDisplay/useFileDisplay.unit.test.ts +1438 -0
- package/src/components/FileDisplay/useFileDisplayData.ts +126 -0
- package/src/components/FileDisplay/usePublicFileDisplay.test.ts +729 -0
- package/src/components/FileDisplay/usePublicFileDisplay.ts +579 -0
- package/src/components/FileUpload/FileUpload.test.tsx +69 -27
- package/src/components/FileUpload/FileUpload.tsx +112 -527
- package/src/components/FileUpload/FileUploadDropZone.tsx +112 -0
- package/src/components/FileUpload/FileUploadProgressItem.tsx +86 -0
- package/src/components/FileUpload/FileUploadProgressList.tsx +40 -0
- package/src/components/FileUpload/index.tsx +1 -1
- package/src/components/FileUpload/useFileUploadManager.test.ts +308 -0
- package/src/components/FileUpload/useFileUploadManager.ts +454 -0
- package/src/components/FileUpload/useResolvedAppId.test.ts +102 -0
- package/src/components/FileUpload/useResolvedAppId.ts +77 -0
- package/src/components/Footer/Footer.test.tsx +15 -382
- package/src/components/Footer/Footer.tsx +8 -125
- package/src/components/Form/Form.test.tsx +425 -88
- package/src/components/Form/Form.tsx +91 -299
- package/src/components/Form/useFormPersistence.ts +257 -0
- package/src/components/Header/Header.test.tsx +653 -163
- package/src/components/Header/Header.tsx +62 -44
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +35 -76
- package/src/components/Input/Input.test.tsx +34 -120
- package/src/components/Input/Input.tsx +1 -1
- package/src/components/Label/Label.test.tsx +46 -45
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +8 -11
- package/src/components/LoginForm/LoginForm.test.tsx +0 -1
- package/src/components/NavigationMenu/HierarchicalNavItem.tsx +104 -0
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +2422 -102
- package/src/components/NavigationMenu/NavigationMenu.tsx +62 -362
- package/src/components/NavigationMenu/index.ts +6 -1
- package/src/components/NavigationMenu/navigationPermissionHelper.ts +188 -0
- package/src/components/NavigationMenu/useNavigationFiltering.test.ts +1949 -0
- package/src/components/NavigationMenu/useNavigationFiltering.ts +199 -308
- package/src/components/NavigationMenu/useNavigationScope.ts +125 -0
- package/src/components/PaceAppLayout/PaceAppLayout.edge-cases.test.tsx +1322 -0
- package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +50 -49
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +81 -38
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +103 -85
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +774 -44
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +282 -764
- package/src/components/PaceAppLayout/README.md +0 -9
- package/src/components/PaceAppLayout/test-setup.tsx +15 -9
- package/src/components/PaceAppLayout/useFilteredNavItems.ts +304 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutConfig.ts +142 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutGate.tsx +150 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutPermissions.ts +162 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutScope.ts +79 -0
- package/src/components/PaceAppLayout/useRoleBasedRouteAccess.ts +157 -0
- package/src/components/PaceAppLayout/useSuperAdminFallback.ts +58 -0
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +782 -20
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +33 -125
- package/src/components/PaceLoginPage/useLoginAppAccess.ts +153 -0
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +1 -1
- package/src/components/Progress/Progress.test.tsx +127 -1
- package/src/components/Progress/Progress.tsx +1 -2
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +1196 -4
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +29 -217
- package/src/components/ProtectedRoute/useProtectedRouteState.ts +128 -0
- package/src/components/ProtectedRoute/useVisibilityRedirectGrace.ts +89 -0
- package/src/components/PublicLayout/PublicLayout.test.tsx +1640 -38
- package/src/components/PublicLayout/PublicPageContext.ts +28 -0
- package/src/components/PublicLayout/PublicPageLayout.tsx +134 -75
- package/src/components/PublicLayout/PublicPageProvider.tsx +7 -42
- package/src/components/PublicLayout/usePublicPageContext.ts +36 -0
- package/src/components/Select/Select.test.tsx +45 -8
- package/src/components/Select/Select.tsx +57 -40
- package/src/components/Select/context.test.tsx +56 -0
- package/src/components/Select/text.test.tsx +104 -0
- package/src/components/Select/text.ts +26 -0
- package/src/components/Select/types.ts +3 -0
- package/src/components/Select/useSelectEvents.test.ts +279 -0
- package/src/components/Select/useSelectEvents.ts +87 -0
- package/src/components/Select/useSelectSearch.test.tsx +295 -0
- package/src/components/Select/useSelectSearch.ts +91 -0
- package/src/components/Select/useSelectState.test.ts +268 -0
- package/src/components/Select/useSelectState.ts +104 -0
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.test.tsx +28 -112
- package/src/components/Switch/Switch.test.tsx +57 -153
- package/src/components/Table/Table.test.tsx +395 -317
- package/src/components/Tabs/Tabs.test.tsx +270 -0
- package/src/components/Tabs/Tabs.tsx +4 -4
- package/src/components/Textarea/Textarea.test.tsx +11 -38
- package/src/components/Toast/Toast.test.tsx +425 -496
- package/src/components/Tooltip/Tooltip.test.tsx +4 -21
- package/src/components/UserMenu/UserMenu.test.tsx +1 -21
- package/src/components/UserMenu/UserMenu.tsx +0 -1
- package/src/components/index.test.ts +346 -0
- package/src/components/index.ts +12 -1
- package/src/constants/performance.test.ts +91 -0
- package/src/hooks/ServiceHooks.test.tsx +725 -0
- package/src/hooks/hooks.integration.test.tsx +608 -0
- package/src/hooks/index.ts +18 -3
- package/src/hooks/index.unit.test.ts +220 -0
- package/src/hooks/public/usePublicEvent.test.ts +304 -0
- package/src/hooks/public/usePublicEvent.ts +11 -11
- package/src/hooks/public/usePublicEventLogo.test.ts +655 -120
- package/src/hooks/public/usePublicEventLogo.ts +2 -2
- package/src/hooks/public/usePublicRouteParams.test.ts +595 -0
- package/src/hooks/public/usePublicRouteParams.ts +2 -2
- package/src/hooks/services/useAuth.ts +9 -7
- package/src/hooks/services/useAuthService.ts +1 -1
- package/src/hooks/services/useEventService.ts +1 -1
- package/src/hooks/useAccessibleApps.test.ts +400 -0
- package/src/hooks/useAccessibleApps.ts +264 -0
- package/src/hooks/useAddressAutocomplete.test.ts +170 -47
- package/src/hooks/useAddressAutocomplete.ts +109 -81
- package/src/hooks/useApiFetch.unit.test.ts +111 -0
- package/src/hooks/useAppConfig.ts +13 -3
- package/src/hooks/useAppConfig.unit.test.ts +712 -0
- package/src/hooks/useComponentPerformance.unit.test.tsx +314 -0
- package/src/hooks/useDataTablePerformance.ts +111 -130
- package/src/hooks/useDataTablePerformance.unit.test.ts +720 -0
- package/src/hooks/useDataTableState.test.ts +170 -0
- package/src/hooks/useDataTableState.ts +5 -5
- package/src/hooks/useDebounce.unit.test.ts +157 -0
- package/src/hooks/useEventTheme.test.ts +70 -18
- package/src/hooks/useEventTheme.ts +50 -22
- package/src/hooks/useEvents.ts +49 -2
- package/src/hooks/useEvents.unit.test.ts +227 -0
- package/src/hooks/useFileReference.test.ts +388 -107
- package/src/hooks/useFileReference.ts +184 -179
- package/src/hooks/useFileUrl.ts +1 -1
- package/src/hooks/useFileUrl.unit.test.ts +686 -0
- package/src/hooks/useFileUrlCache.test.ts +319 -0
- package/src/hooks/useFileUrlCache.ts +5 -2
- package/src/hooks/useFocusManagement.unit.test.ts +604 -0
- package/src/hooks/useFocusTrap.unit.test.tsx +613 -0
- package/src/hooks/useFormDialog.test.ts +307 -0
- package/src/hooks/useFormDialog.ts +2 -2
- package/src/hooks/useInactivityTracker.ts +141 -134
- package/src/hooks/useInactivityTracker.unit.test.ts +446 -0
- package/src/hooks/useIsMobile.unit.test.ts +317 -0
- package/src/hooks/useIsPrint.ts +62 -0
- package/src/hooks/useIsPrint.unit.test.ts +545 -0
- package/src/hooks/useKeyboardShortcuts.unit.test.ts +907 -0
- package/src/hooks/useOrganisationPermissions.test.ts +1 -2
- package/src/hooks/useOrganisationPermissions.ts +1 -4
- package/src/hooks/useOrganisationPermissions.unit.test.tsx +293 -0
- package/src/hooks/useOrganisationSecurity.test.ts +4 -33
- package/src/hooks/useOrganisationSecurity.ts +192 -203
- package/src/hooks/useOrganisationSecurity.unit.test.tsx +959 -0
- package/src/hooks/useOrganisations.ts +1 -1
- package/src/hooks/useOrganisations.unit.test.ts +369 -0
- package/src/hooks/usePerformanceMonitor.ts +1 -1
- package/src/hooks/usePerformanceMonitor.unit.test.ts +693 -0
- package/src/hooks/usePermissionCache.test.ts +298 -329
- package/src/hooks/usePermissionCache.ts +277 -276
- package/src/hooks/usePreventTabReload.test.ts +307 -0
- package/src/hooks/usePublicEvent.simple.test.ts +794 -0
- package/src/hooks/usePublicEvent.test.ts +670 -0
- package/src/hooks/usePublicEvent.unit.test.ts +638 -0
- package/src/hooks/usePublicFileDisplay.test.ts +948 -0
- package/src/hooks/usePublicRouteParams.unit.test.ts +442 -0
- package/src/hooks/useQueryCache.test.ts +391 -0
- package/src/hooks/useQueryCache.ts +7 -9
- package/src/hooks/useRBAC.unit.test.ts +253 -0
- package/src/hooks/useSessionDraft.test.ts +556 -0
- package/src/hooks/useSessionDraft.ts +14 -11
- package/src/hooks/useSessionRestoration.ts +1 -1
- package/src/hooks/useSessionRestoration.unit.test.tsx +381 -0
- package/src/hooks/useStorage.ts +94 -54
- package/src/hooks/useStorage.unit.test.ts +684 -0
- package/src/hooks/useToast.test.ts +413 -0
- package/src/hooks/useToast.ts +2 -2
- package/src/hooks/useToast.unit.test.tsx +481 -0
- package/src/hooks/useZodForm.ts +3 -3
- package/src/hooks/useZodForm.unit.test.tsx +191 -0
- package/src/icons/index.test.ts +133 -0
- package/src/icons/index.ts +3 -1
- package/src/index.test.ts +528 -0
- package/src/index.ts +56 -9
- package/src/providers/AuthProvider.test.tsx +218 -0
- package/src/providers/EventProvider.test.tsx +487 -0
- package/src/providers/InactivityProvider.test-helper.tsx +40 -0
- package/src/providers/InactivityProvider.test.tsx +421 -0
- package/src/providers/ProviderLifecycle.test.tsx +308 -0
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +7 -12
- package/src/providers/UnifiedAuthProvider.test.tsx +503 -0
- package/src/providers/index.test.ts +138 -0
- package/src/providers/services/AuthServiceContext.ts +27 -0
- package/src/providers/services/AuthServiceProvider.integration.test.tsx +229 -0
- package/src/providers/services/AuthServiceProvider.test.tsx +638 -0
- package/src/providers/services/AuthServiceProvider.tsx +81 -20
- package/src/providers/services/EventServiceContext.ts +25 -0
- package/src/providers/services/EventServiceProvider.test.tsx +839 -0
- package/src/providers/services/EventServiceProvider.tsx +11 -20
- package/src/providers/services/InactivityServiceContext.ts +25 -0
- package/src/providers/services/InactivityServiceProvider.test.tsx +662 -0
- package/src/providers/services/InactivityServiceProvider.tsx +7 -17
- package/src/providers/services/OrganisationServiceContext.ts +25 -0
- package/src/providers/services/OrganisationServiceProvider.test.tsx +440 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +7 -17
- package/src/providers/services/UnifiedAuthContext.ts +102 -0
- package/src/providers/services/UnifiedAuthProvider.advanced.test.tsx +434 -0
- package/src/providers/services/UnifiedAuthProvider.appId.test.tsx +408 -0
- package/src/providers/services/UnifiedAuthProvider.integration.test.tsx +304 -0
- package/src/providers/services/UnifiedAuthProvider.test.tsx +212 -0
- package/src/providers/services/UnifiedAuthProvider.tsx +147 -497
- package/src/providers/services/contexts.test.tsx +281 -0
- package/src/providers/services/useUnifiedAuth.test.tsx +251 -0
- package/src/providers/services/useUnifiedAuth.ts +29 -0
- package/src/providers/services/useUnifiedAuthContextValue.ts +279 -0
- package/src/providers/useInactivity.test-helper.ts +27 -0
- package/src/rbac/README.md +5 -5
- package/src/rbac/adapters.comprehensive.test.tsx +429 -0
- package/src/rbac/adapters.test.tsx +654 -0
- package/src/rbac/adapters.tsx +53 -38
- package/src/rbac/api.test.ts +986 -259
- package/src/rbac/api.ts +260 -216
- package/src/rbac/audit-batched.test.ts +550 -0
- package/src/rbac/audit-batched.ts +5 -4
- package/src/rbac/audit.test.ts +225 -28
- package/src/rbac/audit.ts +26 -18
- package/src/rbac/auth-rbac-security.integration.test.tsx +300 -0
- package/src/rbac/auth-rbac.e2e.test.tsx +510 -0
- package/src/rbac/cache-invalidation.test.ts +715 -0
- package/src/rbac/cache-invalidation.ts +18 -15
- package/src/rbac/cache.test.ts +123 -63
- package/src/rbac/cache.ts +3 -4
- package/src/rbac/components/AccessDenied.test.tsx +324 -0
- package/src/rbac/components/AccessDenied.tsx +20 -18
- package/src/rbac/components/NavigationGuard.test.tsx +1148 -0
- package/src/rbac/components/NavigationGuard.tsx +10 -8
- package/src/rbac/components/PagePermissionGuard.guard.test.tsx +236 -0
- package/src/rbac/components/PagePermissionGuard.performance.test.tsx +252 -0
- package/src/rbac/components/PagePermissionGuard.race-condition.test.tsx +243 -0
- package/src/rbac/components/PagePermissionGuard.test.tsx +1430 -0
- package/src/rbac/components/PagePermissionGuard.tsx +188 -381
- package/src/rbac/components/PagePermissionGuard.verification.test.tsx +185 -0
- package/src/rbac/config.test.ts +131 -48
- package/src/rbac/config.ts +69 -26
- package/src/rbac/docs/event-based-apps.md +26 -13
- package/src/rbac/engine.comprehensive.test.ts +808 -0
- package/src/rbac/engine.test.ts +974 -130
- package/src/rbac/engine.ts +53 -13
- package/src/rbac/errors.test.ts +99 -87
- package/src/rbac/errors.ts +89 -55
- package/src/rbac/eslint-rules.js +2 -2
- package/src/rbac/hooks/permissions/runPermissionCheck.ts +77 -0
- package/src/rbac/hooks/permissions/useAccessLevel.test.ts +622 -0
- package/src/rbac/hooks/permissions/useAccessLevel.ts +23 -14
- package/src/rbac/hooks/permissions/useCan.test.ts +798 -0
- package/src/rbac/hooks/permissions/useCan.ts +173 -253
- package/src/rbac/hooks/permissions/useMultiplePermissions.test.ts +843 -0
- package/src/rbac/hooks/permissions/useMultiplePermissions.ts +63 -10
- package/src/rbac/hooks/permissions/usePermissions.test.ts +543 -0
- package/src/rbac/hooks/permissions/usePermissions.ts +50 -78
- package/src/rbac/hooks/useCan.test.ts +348 -32
- package/src/rbac/hooks/usePageAccessLogging.ts +160 -0
- package/src/rbac/hooks/usePageGuardScope.ts +117 -0
- package/src/rbac/hooks/usePagePermissionCheck.ts +67 -0
- package/src/rbac/hooks/usePermissions.integration.test.ts +427 -0
- package/src/rbac/hooks/usePermissions.stability.test.ts +268 -0
- package/src/rbac/hooks/usePermissions.test.ts +459 -33
- package/src/rbac/hooks/usePermissions.ts +5 -7
- package/src/rbac/hooks/useRBAC.test.ts +1784 -21
- package/src/rbac/hooks/useRBAC.ts +148 -88
- package/src/rbac/hooks/useResolvedScope.test.ts +442 -5
- package/src/rbac/hooks/useResolvedScope.ts +4 -1
- package/src/rbac/hooks/useResourcePermissions.test.ts +561 -24
- package/src/rbac/hooks/useResourcePermissions.ts +76 -140
- package/src/rbac/hooks/useResourcePermissionsSuperAdmin.ts +67 -0
- package/src/rbac/hooks/useRoleManagement.test.ts +634 -61
- package/src/rbac/hooks/useRoleManagement.ts +158 -586
- package/src/rbac/hooks/useSecureSupabase.test.ts +1179 -0
- package/src/rbac/hooks/useSecureSupabase.ts +21 -14
- package/src/rbac/hooks/useSuperAdminCheck.ts +80 -0
- package/src/rbac/index.test.ts +107 -0
- package/src/rbac/index.ts +32 -32
- package/src/rbac/performance.test.ts +451 -0
- package/src/rbac/permissions.test.ts +149 -68
- package/src/rbac/permissions.ts +0 -3
- package/src/rbac/rbac-core.test.tsx +276 -0
- package/src/rbac/rbac-engine-core-logic.test.ts +387 -0
- package/src/rbac/rbac-engine-simplified.test.ts +252 -0
- package/src/rbac/rbac-functions.test.ts +703 -0
- package/src/rbac/rbac-integration.test.ts +523 -0
- package/src/rbac/rbac-role-isolation.test.ts +456 -0
- package/src/rbac/request-deduplication.test.ts +352 -0
- package/src/rbac/request-deduplication.ts +5 -4
- package/src/rbac/scenarios.user-role.test.tsx +271 -0
- package/src/rbac/secureClient.test.ts +499 -115
- package/src/rbac/secureClient.ts +54 -28
- package/src/rbac/security.test.ts +448 -44
- package/src/rbac/security.ts +7 -6
- package/src/rbac/types/roleManagement.ts +66 -0
- package/src/rbac/types.test.ts +236 -0
- package/src/rbac/types.ts +7 -5
- package/src/rbac/utils/clientSecurity.test.ts +192 -0
- package/src/rbac/utils/clientSecurity.ts +6 -4
- package/src/rbac/utils/contextValidator.test.ts +126 -0
- package/src/rbac/utils/contextValidator.ts +6 -3
- package/src/rbac/utils/deep-equal.test.ts +76 -0
- package/src/rbac/utils/eventContext.test.ts +401 -0
- package/src/rbac/utils/eventContext.ts +38 -34
- package/src/rbac/utils/fetchPermissionMap.ts +13 -0
- package/src/rbac/utils/permissionMapHelpers.ts +34 -0
- package/src/rbac/utils/roleManagementRpc.ts +303 -0
- package/src/services/AuthService.edge-cases.test.ts +746 -0
- package/src/services/AuthService.restoreSession.test.ts +59 -0
- package/src/services/AuthService.test.ts +1362 -0
- package/src/services/AuthService.ts +197 -216
- package/src/services/BaseService.edge-cases.test.ts +506 -0
- package/src/services/BaseService.test.ts +363 -0
- package/src/services/EventService.edge-cases.test.ts +636 -0
- package/src/services/EventService.eventColours.test.ts +64 -0
- package/src/services/EventService.test.ts +1250 -0
- package/src/services/EventService.ts +244 -315
- package/src/services/InactivityService.edge-cases.test.ts +492 -0
- package/src/services/InactivityService.lifecycle.test.ts +406 -0
- package/src/services/InactivityService.test.ts +829 -0
- package/src/services/InactivityService.ts +172 -213
- package/src/services/OrganisationService.edge-cases.test.ts +633 -0
- package/src/services/OrganisationService.pagination.test.ts +409 -0
- package/src/services/OrganisationService.test.ts +1579 -0
- package/src/services/OrganisationService.ts +186 -257
- package/src/services/base/BaseService.test.ts +214 -0
- package/src/services/interfaces/IAuthService.test.ts +184 -0
- package/src/services/interfaces/IAuthService.ts +10 -9
- package/src/services/interfaces/IEventService.test.ts +176 -0
- package/src/services/interfaces/IInactivityService.test.ts +183 -0
- package/src/services/interfaces/IOrganisationService.test.ts +207 -0
- package/src/services/interfaces/IOrganisationService.ts +0 -1
- package/src/styles/core.css +244 -12
- package/src/theming/parseEventColours.test.ts +321 -0
- package/src/theming/parseEventColours.ts +18 -9
- package/src/theming/runtime.test.ts +495 -0
- package/src/theming/runtime.ts +72 -7
- package/src/types/api-result.ts +53 -0
- package/src/types/auth.ts +0 -1
- package/src/types/core.test.ts +397 -0
- package/src/types/database-generated.test.ts +78 -0
- package/src/types/database.generated.ts +45 -10
- package/src/types/event.ts +39 -19
- package/src/types/file-reference.test.ts +351 -0
- package/src/types/file-reference.ts +37 -12
- package/src/types/guards.test.ts +246 -0
- package/src/types/index.test.ts +265 -0
- package/src/types/index.ts +3 -0
- package/src/types/organisation.roles.test.ts +55 -0
- package/src/types/organisation.test.ts +1105 -0
- package/src/types/organisation.ts +15 -15
- package/src/types/rpc-responses.ts +33 -0
- package/src/types/supabase.ts +14 -6
- package/src/types/theme.test.ts +830 -0
- package/src/types/type-validation.test.ts +526 -0
- package/src/types/validation.test.ts +729 -0
- package/src/types/vitest-globals.d.ts +1 -1
- package/src/utils/app/appConfig.test.ts +235 -0
- package/src/utils/app/appIdResolver.test.ts +252 -57
- package/src/utils/app/appIdResolver.ts +31 -20
- package/src/utils/app/appNameResolver.test.ts +18 -10
- package/src/utils/app/appNameResolver.ts +11 -9
- package/src/utils/app/appPortMap.test.ts +125 -0
- package/src/utils/app/appPortMap.ts +51 -0
- package/src/utils/app/buildAppUrl.test.ts +273 -0
- package/src/utils/app/buildAppUrl.ts +114 -0
- package/src/utils/appConfig.unit.test.ts +55 -0
- package/src/utils/audit/audit.test.ts +354 -39
- package/src/utils/audit.unit.test.ts +69 -0
- package/src/utils/auth-utils.unit.test.ts +69 -0
- package/src/utils/bundleAnalysis.unit.test.ts +326 -0
- package/src/utils/cn.unit.test.ts +34 -0
- package/src/utils/context/organisationContext.test.ts +115 -95
- package/src/utils/context/organisationContext.ts +32 -43
- package/src/utils/context/sessionTracking.test.ts +354 -0
- package/src/utils/core/cn.test.ts +66 -0
- package/src/utils/core/debugLogger.test.ts +113 -0
- package/src/utils/core/debugLogger.ts +15 -8
- package/src/utils/core/logger.test.ts +217 -0
- package/src/utils/core/logger.ts +20 -16
- package/src/utils/core/mergeRefs.ts +24 -0
- package/src/utils/debugLogger.test.ts +417 -0
- package/src/utils/device/deviceFingerprint.test.ts +8 -5
- package/src/utils/device/deviceFingerprint.ts +3 -3
- package/src/utils/deviceFingerprint.unit.test.ts +818 -0
- package/src/utils/dynamic/createLazyComponent.tsx +46 -0
- package/src/utils/dynamic/dynamicUtils.test.ts +185 -0
- package/src/utils/dynamic/dynamicUtils.ts +6 -6
- package/src/utils/dynamic/lazyLoad.test.tsx +156 -0
- package/src/utils/dynamic/lazyLoad.tsx +8 -36
- package/src/utils/dynamic/papaparseLoader.ts +7 -0
- package/src/utils/dynamicUtils.unit.test.ts +331 -0
- package/src/utils/file-reference/file-reference.test.ts +1238 -0
- package/src/utils/file-reference/index.ts +330 -348
- package/src/utils/formatDate.unit.test.ts +109 -0
- package/src/utils/formatting/formatDate.test.ts +22 -148
- package/src/utils/formatting/formatDateTime.test.ts +41 -119
- package/src/utils/formatting/formatDateTimeTimezone.test.ts +41 -85
- package/src/utils/formatting/formatNumber.test.ts +259 -0
- package/src/utils/formatting/formatTime.test.ts +36 -128
- package/src/utils/formatting/formatting.ts +1 -1
- package/src/utils/formatting.unit.test.ts +99 -0
- package/src/utils/google-places/googlePlacesUtils.test.ts +127 -36
- package/src/utils/google-places/googlePlacesUtils.ts +67 -86
- package/src/utils/google-places/loadGoogleMapsScript.test.ts +68 -8
- package/src/utils/google-places/loadGoogleMapsScript.ts +140 -118
- package/src/utils/index.ts +52 -11
- package/src/utils/index.unit.test.ts +251 -0
- package/src/utils/lazyLoad.unit.test.tsx +319 -0
- package/src/utils/location/location.test.ts +19 -116
- package/src/utils/logger.unit.test.ts +398 -0
- package/src/utils/organisationContext.unit.test.ts +180 -0
- package/src/utils/performance/bundleAnalysis.test.ts +148 -0
- package/src/utils/performance/bundleAnalysis.ts +16 -22
- package/src/utils/performance/performanceBenchmark.test.ts +251 -0
- package/src/utils/performance/performanceBenchmark.ts +12 -4
- package/src/utils/performance/performanceBudgets.test.ts +241 -0
- package/src/utils/performance/performanceBudgets.ts +9 -6
- package/src/utils/performanceBenchmark.test.ts +174 -0
- package/src/utils/performanceBudgets.unit.test.ts +288 -0
- package/src/utils/permissionTypes.unit.test.ts +250 -0
- package/src/utils/permissionUtils.unit.test.ts +362 -0
- package/src/utils/permissions/permissionTypes.test.ts +149 -0
- package/src/utils/permissions/permissionUtils.test.ts +20 -42
- package/src/utils/persistence/keyDerivation.test.ts +306 -0
- package/src/utils/persistence/sensitiveFieldDetection.test.ts +271 -0
- package/src/utils/persistence/sensitiveFieldDetection.ts +2 -2
- package/src/utils/request-deduplication.test.ts +349 -0
- package/src/utils/request-deduplication.ts +6 -4
- package/src/utils/sanitization.unit.test.ts +346 -0
- package/src/utils/schemaUtils.unit.test.ts +441 -0
- package/src/utils/secureDataAccess.unit.test.ts +334 -0
- package/src/utils/secureErrors.unit.test.ts +390 -0
- package/src/utils/secureStorage.unit.test.ts +289 -0
- package/src/utils/security/auth-utils.ts +38 -27
- package/src/utils/security/secureDataAccess.test.ts +22 -191
- package/src/utils/security/secureDataAccess.ts +241 -281
- package/src/utils/security/secureErrors.test.ts +163 -0
- package/src/utils/security/secureStorage.test.ts +156 -0
- package/src/utils/security/secureStorage.ts +1 -1
- package/src/utils/security/security.test.ts +212 -0
- package/src/utils/security/security.ts +15 -18
- package/src/utils/security/securityMonitor.test.ts +90 -0
- package/src/utils/security/securityMonitor.ts +1 -1
- package/src/utils/security.unit.test.ts +155 -0
- package/src/utils/securityMonitor.unit.test.ts +276 -0
- package/src/utils/sessionTracking.unit.test.ts +218 -0
- package/src/utils/storage/config.unit.test.ts +239 -0
- package/src/utils/storage/helpers.test.ts +769 -456
- package/src/utils/storage/helpers.ts +174 -253
- package/src/utils/storage/index.unit.test.ts +68 -0
- package/src/utils/storage/storageUtils.ts +32 -0
- package/src/utils/storage/types.ts +9 -2
- package/src/utils/supabase/createBaseClient.test.ts +201 -0
- package/src/utils/supabase/createBaseClient.ts +2 -1
- package/src/utils/timezone/timezone.test.ts +26 -44
- package/src/utils/timezone.test.ts +345 -0
- package/src/utils/validation/common.test.ts +115 -0
- package/src/utils/validation/csrf.test.ts +198 -0
- package/src/utils/validation/csrf.ts +42 -41
- package/src/utils/validation/htmlSanitization.ts +27 -31
- package/src/utils/validation/htmlSanitization.unit.test.ts +618 -0
- package/src/utils/validation/passwordSchema.test.ts +164 -0
- package/src/utils/validation/schema.test.ts +127 -0
- package/src/utils/validation/schema.ts +6 -3
- package/src/utils/validation/sqlInjectionProtection.test.ts +165 -0
- package/src/utils/validation/sqlInjectionProtection.ts +2 -2
- package/src/utils/validation/user.test.ts +173 -0
- package/src/utils/validation/validation.test.ts +197 -0
- package/src/utils/validation/validationUtils.test.ts +294 -0
- package/src/utils/validation.unit.test.ts +307 -0
- package/src/utils/validationUtils.unit.test.ts +558 -0
- package/src/vite-env.d.ts +6 -0
- package/dist/AuthService-DmfO5rGS.d.ts +0 -524
- package/dist/DataTable-DRUIgtUH.d.ts +0 -166
- package/dist/DataTable-SOAFXIWY.js +0 -15
- package/dist/PublicPageProvider-CIGSujI2.d.ts +0 -4147
- package/dist/UnifiedAuthProvider-7SNDOWYD.js +0 -7
- package/dist/UnifiedAuthProvider-CKvHP1MK.d.ts +0 -139
- package/dist/api-7P7DI652.js +0 -4
- package/dist/audit-MYQXYZFU.js +0 -3
- package/dist/auth-BZOJqrdd.d.ts +0 -49
- package/dist/chunk-4DDCYDQ3.js +0 -544
- package/dist/chunk-5HNSDQWH.js +0 -5046
- package/dist/chunk-5W2A3DRC.js +0 -164
- package/dist/chunk-6GLLNA6U.js +0 -31
- package/dist/chunk-7ILTDCL2.js +0 -80
- package/dist/chunk-A3W6LW53.js +0 -70
- package/dist/chunk-AHU7G2R5.js +0 -423
- package/dist/chunk-C7ZQ5O4C.js +0 -481
- package/dist/chunk-EF2UGZWY.js +0 -611
- package/dist/chunk-FEJLJNWA.js +0 -181
- package/dist/chunk-FYHN4DD5.js +0 -415
- package/dist/chunk-GS5672WG.js +0 -2003
- package/dist/chunk-HF6O3O37.js +0 -187
- package/dist/chunk-J2U36LHD.js +0 -8517
- package/dist/chunk-LX6U42O3.js +0 -2177
- package/dist/chunk-MPBLMWVR.js +0 -2161
- package/dist/chunk-OJ4SKRSV.js +0 -105
- package/dist/chunk-S6ZQKDY6.js +0 -62
- package/dist/chunk-S7DKJPLT.js +0 -699
- package/dist/chunk-T5CVK4R3.js +0 -2816
- package/dist/chunk-TTRFSOKR.js +0 -121
- package/dist/chunk-Z2FNRKF3.js +0 -994
- package/dist/database.generated-DT8JTZiP.d.ts +0 -9406
- package/dist/event-CW5YB_2p.d.ts +0 -239
- package/dist/file-reference-BavO2eQj.d.ts +0 -148
- package/dist/functions-lBy5L2ry.d.ts +0 -208
- package/dist/timezone-0AyangqX.d.ts +0 -697
- package/dist/types-BeoeWV5I.d.ts +0 -110
- package/dist/types-DXstZpNI.d.ts +0 -614
- package/dist/types-t9H8qKRw.d.ts +0 -55
- package/dist/usePublicRouteParams-DQLrDqDb.d.ts +0 -876
- package/dist/useToast-AyaT-x7p.d.ts +0 -68
- package/dist/validation-643vUDZW.d.ts +0 -177
- package/scripts/build-docs-incremental.js +0 -179
- package/scripts/eslint-audit.cjs +0 -123
- package/scripts/generate-docs.js +0 -157
- package/scripts/install-cursor-rules.cjs +0 -255
- package/scripts/install-eslint-config.cjs +0 -349
- package/scripts/setup-build-cache.js +0 -73
- package/scripts/validate-pre-publish.js +0 -145
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +0 -260
- package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +0 -224
- package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +0 -273
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +0 -99
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +0 -448
- package/src/__tests__/helpers/__tests__/timer-utils.test.ts +0 -371
- package/src/__tests__/hooks/usePermissions.test.ts +0 -268
- package/src/__tests__/integration/UserProfile.test.tsx +0 -124
- package/src/__tests__/public-recipe-view.test.ts +0 -228
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +0 -220
- package/src/__tests__/rls-policies.test.ts +0 -471
- package/src/components/DataTable/__tests__/DataTable.comprehensive.test.tsx +0 -759
- package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +0 -524
- package/src/components/DataTable/__tests__/DataTable.export.test.tsx +0 -705
- package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +0 -658
- package/src/components/DataTable/__tests__/DataTable.hooks.test.tsx +0 -192
- package/src/components/DataTable/__tests__/DataTable.select-label-display.test.tsx +0 -483
- package/src/components/DataTable/__tests__/DataTable.test.tsx +0 -876
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +0 -220
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +0 -1474
- package/src/components/DataTable/__tests__/README.md +0 -145
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +0 -788
- package/src/components/DataTable/__tests__/keyboard.test.tsx +0 -756
- package/src/components/DataTable/__tests__/mocks/MockRBACProvider.tsx +0 -66
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +0 -730
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +0 -325
- package/src/components/DataTable/__tests__/styles.test.ts +0 -382
- package/src/components/DataTable/__tests__/test-utils/dataFactories.ts +0 -103
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +0 -380
- package/src/components/DataTable/__tests__/test-utils.ts +0 -94
- package/src/components/DataTable/components/AccessDeniedPage.tsx +0 -159
- package/src/components/DataTable/components/ActionButtons.tsx +0 -190
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +0 -160
- package/src/components/DataTable/components/ColumnFilter.tsx +0 -118
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +0 -114
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +0 -225
- package/src/components/DataTable/components/DataTableLayout.tsx +0 -573
- package/src/components/DataTable/components/DataTableModals.tsx +0 -245
- package/src/components/DataTable/components/DataTableToolbar.tsx +0 -271
- package/src/components/DataTable/components/EditFields.tsx +0 -327
- package/src/components/DataTable/components/EditableRow.tsx +0 -462
- package/src/components/DataTable/components/EmptyState.tsx +0 -79
- package/src/components/DataTable/components/FilterRow.tsx +0 -141
- package/src/components/DataTable/components/LoadingState.tsx +0 -17
- package/src/components/DataTable/components/PaginationControls.tsx +0 -289
- package/src/components/DataTable/components/RowComponent.tsx +0 -403
- package/src/components/DataTable/components/SortIndicator.tsx +0 -50
- package/src/components/DataTable/components/UnifiedTableBody.tsx +0 -355
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +0 -657
- package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +0 -913
- package/src/components/DataTable/components/__tests__/BulkOperationsDropdown.test.tsx +0 -572
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +0 -612
- package/src/components/DataTable/components/__tests__/ColumnVisibilityDropdown.test.tsx +0 -708
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +0 -479
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +0 -475
- package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +0 -157
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +0 -1061
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +0 -437
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +0 -474
- package/src/components/DataTable/components/__tests__/GroupingDropdown.test.tsx +0 -617
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +0 -1093
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +0 -139
- package/src/components/DataTable/components/__tests__/PaginationControls.test.tsx +0 -519
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +0 -1004
- package/src/components/DataTable/components/cellValueUtils.ts +0 -40
- package/src/components/DataTable/components/hooks/useImportModalFocus.ts +0 -53
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -122
- package/src/components/DataTable/components/index.ts +0 -16
- package/src/components/DataTable/context/__tests__/DataTableContext.test.tsx +0 -342
- package/src/components/DataTable/core/ActionManager.ts +0 -235
- package/src/components/DataTable/core/ColumnManager.ts +0 -205
- package/src/components/DataTable/core/DataManager.ts +0 -188
- package/src/components/DataTable/core/LocalDataAdapter.ts +0 -274
- package/src/components/DataTable/core/PluginRegistry.ts +0 -229
- package/src/components/DataTable/core/StateManager.ts +0 -312
- package/src/components/DataTable/core/__tests__/ActionManager.test.ts +0 -123
- package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +0 -305
- package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +0 -84
- package/src/components/DataTable/core/__tests__/DataManager.test.ts +0 -115
- package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +0 -100
- package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +0 -120
- package/src/components/DataTable/core/__tests__/StateManager.test.ts +0 -104
- package/src/components/DataTable/core/index.ts +0 -1
- package/src/components/DataTable/core/interfaces.ts +0 -338
- package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +0 -521
- package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +0 -167
- package/src/components/DataTable/hooks/__tests__/useDataTableConfiguration.test.ts +0 -124
- package/src/components/DataTable/hooks/__tests__/useDataTableDataPipeline.test.ts +0 -117
- package/src/components/DataTable/hooks/__tests__/useDataTablePermissions.test.ts +0 -102
- package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +0 -596
- package/src/components/DataTable/hooks/__tests__/useEffectiveColumnOrder.test.ts +0 -53
- package/src/components/DataTable/hooks/__tests__/useHierarchicalState.test.ts +0 -214
- package/src/components/DataTable/hooks/__tests__/useTableColumns.test.ts +0 -448
- package/src/components/DataTable/hooks/index.ts +0 -13
- package/src/components/DataTable/types.ts +0 -761
- package/src/components/DataTable/utils/__tests__/a11yUtils.test.ts +0 -612
- package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +0 -94
- package/src/components/DataTable/utils/__tests__/errorHandling.test.ts +0 -266
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +0 -954
- package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +0 -573
- package/src/components/DataTable/utils/__tests__/hierarchicalSorting.test.ts +0 -247
- package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +0 -570
- package/src/components/DataTable/utils/__tests__/performanceUtils.test.ts +0 -470
- package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +0 -251
- package/src/components/DataTable/utils/__tests__/selectFieldUtils.test.ts +0 -207
- package/src/components/DataTable/utils/index.ts +0 -10
- package/src/components/PublicLayout/index.ts +0 -32
- package/src/components/Select/hooks/useSelectEvents.ts +0 -87
- package/src/components/Select/hooks/useSelectSearch.ts +0 -91
- package/src/components/Select/hooks/useSelectState.ts +0 -104
- package/src/components/Select/utils/text.ts +0 -26
- package/src/hooks/__tests__/ServiceHooks.test.tsx +0 -615
- package/src/hooks/__tests__/hooks.integration.test.tsx +0 -607
- package/src/hooks/__tests__/index.unit.test.ts +0 -220
- package/src/hooks/__tests__/useApiFetch.unit.test.ts +0 -111
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +0 -347
- package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +0 -144
- package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +0 -776
- package/src/hooks/__tests__/useDataTableState.test.ts +0 -76
- package/src/hooks/__tests__/useDebounce.unit.test.ts +0 -82
- package/src/hooks/__tests__/useEvents.unit.test.ts +0 -252
- package/src/hooks/__tests__/useFileDisplay.unit.test.ts +0 -1112
- package/src/hooks/__tests__/useFileUrl.unit.test.ts +0 -916
- package/src/hooks/__tests__/useFileUrlCache.test.ts +0 -129
- package/src/hooks/__tests__/useFocusManagement.unit.test.ts +0 -230
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +0 -828
- package/src/hooks/__tests__/useFormDialog.test.ts +0 -478
- package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +0 -446
- package/src/hooks/__tests__/useIsMobile.unit.test.ts +0 -317
- package/src/hooks/__tests__/useKeyboardShortcuts.unit.test.ts +0 -910
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +0 -294
- package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +0 -961
- package/src/hooks/__tests__/useOrganisations.unit.test.ts +0 -369
- package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +0 -694
- package/src/hooks/__tests__/usePermissionCache.simple.test.ts +0 -192
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +0 -741
- package/src/hooks/__tests__/usePreventTabReload.test.ts +0 -88
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +0 -785
- package/src/hooks/__tests__/usePublicEvent.test.ts +0 -678
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +0 -630
- package/src/hooks/__tests__/usePublicFileDisplay.test.ts +0 -951
- package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +0 -443
- package/src/hooks/__tests__/useQueryCache.test.ts +0 -144
- package/src/hooks/__tests__/useRBAC.unit.test.ts +0 -236
- package/src/hooks/__tests__/useSessionDraft.test.ts +0 -163
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +0 -390
- package/src/hooks/__tests__/useStorage.unit.test.ts +0 -751
- package/src/hooks/__tests__/useToast.unit.test.tsx +0 -481
- package/src/hooks/__tests__/useZodForm.unit.test.tsx +0 -37
- package/src/hooks/public/index.ts +0 -36
- package/src/hooks/public/usePublicFileDisplay.ts +0 -504
- package/src/hooks/useFileDisplay.ts +0 -715
- package/src/providers/OrganisationProvider.tsx +0 -92
- package/src/providers/__tests__/AuthProvider.test.tsx +0 -287
- package/src/providers/__tests__/EventProvider.test.tsx +0 -551
- package/src/providers/__tests__/InactivityProvider.test-helper.tsx +0 -65
- package/src/providers/__tests__/InactivityProvider.test.tsx +0 -572
- package/src/providers/__tests__/OrganisationProvider.test.tsx +0 -617
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +0 -424
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +0 -596
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +0 -263
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +0 -294
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +0 -434
- package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +0 -313
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +0 -486
- package/src/rbac/__tests__/cache-invalidation.test.ts +0 -399
- package/src/rbac/__tests__/engine.comprehensive.test.ts +0 -813
- package/src/rbac/__tests__/isSuperAdmin.real.test.ts +0 -82
- package/src/rbac/__tests__/rbac-core.test.tsx +0 -276
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +0 -392
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +0 -258
- package/src/rbac/__tests__/rbac-functions.test.ts +0 -647
- package/src/rbac/__tests__/rbac-integration.test.ts +0 -524
- package/src/rbac/__tests__/rbac-role-isolation.test.ts +0 -456
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +0 -282
- package/src/rbac/audit-enhanced.ts +0 -384
- package/src/rbac/compliance/database-validator.ts +0 -165
- package/src/rbac/compliance/index.ts +0 -48
- package/src/rbac/compliance/pattern-detector.ts +0 -553
- package/src/rbac/compliance/quick-fix-suggestions.ts +0 -209
- package/src/rbac/compliance/runtime-compliance.ts +0 -99
- package/src/rbac/compliance/setup-validator.ts +0 -131
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +0 -975
- package/src/rbac/components/__tests__/PagePermissionGuard.performance.test.tsx +0 -248
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +0 -242
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +0 -1107
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +0 -184
- package/src/rbac/components/index.ts +0 -26
- package/src/rbac/hooks/__tests__/usePermissions.integration.test.ts +0 -432
- package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +0 -579
- package/src/rbac/hooks/index.ts +0 -34
- package/src/rbac/hooks/permissions/index.ts +0 -4
- package/src/rbac/hooks/useRBAC.simple.test.ts +0 -95
- package/src/rbac/utils/__tests__/contextValidator.test.ts +0 -128
- package/src/rbac/utils/__tests__/deep-equal.test.ts +0 -53
- package/src/rbac/utils/__tests__/eventContext.test.ts +0 -433
- package/src/rbac/utils/__tests__/eventContext.unit.test.ts +0 -490
- package/src/services/__tests__/AuthService.restoreSession.test.ts +0 -39
- package/src/services/__tests__/AuthService.test.ts +0 -1332
- package/src/services/__tests__/BaseService.test.ts +0 -314
- package/src/services/__tests__/EventService.eventColours.test.ts +0 -76
- package/src/services/__tests__/EventService.test.ts +0 -1025
- package/src/services/__tests__/InactivityService.lifecycle.test.ts +0 -411
- package/src/services/__tests__/InactivityService.test.ts +0 -654
- package/src/services/__tests__/OrganisationService.pagination.test.ts +0 -409
- package/src/services/__tests__/OrganisationService.test.ts +0 -1176
- package/src/theming/__tests__/parseEventColours.test.ts +0 -321
- package/src/theming/__tests__/runtime.test.ts +0 -569
- package/src/types/__tests__/file-reference.test.ts +0 -447
- package/src/types/__tests__/guards.test.ts +0 -246
- package/src/types/__tests__/organisation.roles.test.ts +0 -55
- package/src/types/__tests__/organisation.test.ts +0 -1133
- package/src/types/__tests__/theme.test.ts +0 -830
- package/src/types/__tests__/type-validation.test.ts +0 -526
- package/src/types/__tests__/validation.test.ts +0 -731
- package/src/utils/__tests__/appConfig.unit.test.ts +0 -55
- package/src/utils/__tests__/audit.unit.test.ts +0 -69
- package/src/utils/__tests__/auth-utils.unit.test.ts +0 -70
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +0 -339
- package/src/utils/__tests__/cn.unit.test.ts +0 -34
- package/src/utils/__tests__/debugLogger.test.ts +0 -417
- package/src/utils/__tests__/deviceFingerprint.unit.test.ts +0 -818
- package/src/utils/__tests__/dynamicUtils.unit.test.ts +0 -318
- package/src/utils/__tests__/formatDate.unit.test.ts +0 -109
- package/src/utils/__tests__/formatting.unit.test.ts +0 -99
- package/src/utils/__tests__/index.unit.test.ts +0 -251
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +0 -321
- package/src/utils/__tests__/logger.unit.test.ts +0 -398
- package/src/utils/__tests__/organisationContext.unit.test.ts +0 -191
- package/src/utils/__tests__/performanceBenchmark.test.ts +0 -175
- package/src/utils/__tests__/performanceBudgets.unit.test.ts +0 -253
- package/src/utils/__tests__/permissionTypes.unit.test.ts +0 -250
- package/src/utils/__tests__/permissionUtils.unit.test.ts +0 -362
- package/src/utils/__tests__/sanitization.unit.test.ts +0 -346
- package/src/utils/__tests__/schemaUtils.unit.test.ts +0 -441
- package/src/utils/__tests__/secureDataAccess.unit.test.ts +0 -335
- package/src/utils/__tests__/secureErrors.unit.test.ts +0 -390
- package/src/utils/__tests__/secureStorage.unit.test.ts +0 -289
- package/src/utils/__tests__/security.unit.test.ts +0 -149
- package/src/utils/__tests__/securityMonitor.unit.test.ts +0 -276
- package/src/utils/__tests__/sessionTracking.unit.test.ts +0 -218
- package/src/utils/__tests__/timezone.test.ts +0 -345
- package/src/utils/__tests__/validation.unit.test.ts +0 -308
- package/src/utils/__tests__/validationUtils.unit.test.ts +0 -555
- package/src/utils/app/appNameResolver.simple.test.ts +0 -212
- package/src/utils/file-reference/__tests__/file-reference.test.ts +0 -875
- package/src/utils/google-places/index.ts +0 -26
- package/src/utils/location/index.ts +0 -16
- package/src/utils/persistence/__tests__/keyDerivation.test.ts +0 -135
- package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +0 -123
- package/src/utils/storage/__tests__/helpers.unit.test.ts +0 -332
- package/src/utils/storage/__tests__/index.unit.test.ts +0 -16
- package/src/utils/storage/index.ts +0 -67
- package/src/utils/timezone/index.ts +0 -17
- package/src/utils/validation/__tests__/csrf.test.ts +0 -105
- package/src/utils/validation/__tests__/htmlSanitization.unit.test.ts +0 -598
- package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +0 -92
- package/src/utils/validation/__tests__/validationUtils.test.ts +0 -72
- package/src/utils/validation/index.ts +0 -73
- /package/src/components/DataTable/{components/__tests__ → ui}/COVERAGE_NOTE.md +0 -0
- /package/src/components/DataTable/utils/{__tests__/COVERAGE_NOTE.md → COVERAGE_NOTE.md} +0 -0
- /package/src/providers/{__tests__/README.md → README.md} +0 -0
- /package/src/types/{__tests__/README.md → README.md} +0 -0
package/dist/chunk-LX6U42O3.js
DELETED
|
@@ -1,2177 +0,0 @@
|
|
|
1
|
-
import { emitAuditEvent, createAuditManager, setGlobalAuditManager } from './chunk-AHU7G2R5.js';
|
|
2
|
-
import { createLogger } from './chunk-TTRFSOKR.js';
|
|
3
|
-
|
|
4
|
-
// src/rbac/types.ts
|
|
5
|
-
var RBACError = class extends Error {
|
|
6
|
-
constructor(message, code, context) {
|
|
7
|
-
super(message);
|
|
8
|
-
this.code = code;
|
|
9
|
-
this.context = context;
|
|
10
|
-
this.name = "RBACError";
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
|
-
var PermissionDeniedError = class extends RBACError {
|
|
14
|
-
constructor(permission, context) {
|
|
15
|
-
super(
|
|
16
|
-
`Permission denied: ${permission}`,
|
|
17
|
-
"PERMISSION_DENIED",
|
|
18
|
-
{ permission, ...context }
|
|
19
|
-
);
|
|
20
|
-
this.name = "PermissionDeniedError";
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
var OrganisationContextRequiredError = class extends RBACError {
|
|
24
|
-
constructor() {
|
|
25
|
-
super(
|
|
26
|
-
"Organisation context is required for this operation",
|
|
27
|
-
"ORGANISATION_CONTEXT_REQUIRED"
|
|
28
|
-
);
|
|
29
|
-
this.name = "OrganisationContextRequiredError";
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
var EventContextRequiredError = class extends RBACError {
|
|
33
|
-
constructor() {
|
|
34
|
-
super(
|
|
35
|
-
"Event context is required for this operation",
|
|
36
|
-
"EVENT_CONTEXT_REQUIRED"
|
|
37
|
-
);
|
|
38
|
-
this.name = "EventContextRequiredError";
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
var RBACNotInitializedError = class extends RBACError {
|
|
42
|
-
constructor() {
|
|
43
|
-
super(
|
|
44
|
-
"RBAC system not initialized. Please call setupRBAC(supabase) before using any RBAC components or hooks. See: https://docs.pace-core.dev/rbac/setup",
|
|
45
|
-
"RBAC_NOT_INITIALIZED"
|
|
46
|
-
);
|
|
47
|
-
this.name = "RBACNotInitializedError";
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
var InvalidScopeError = class extends RBACError {
|
|
51
|
-
constructor(scope, reason) {
|
|
52
|
-
super(
|
|
53
|
-
`Invalid scope provided: ${JSON.stringify(scope)}. ${reason}`,
|
|
54
|
-
"INVALID_SCOPE",
|
|
55
|
-
{ scope, reason }
|
|
56
|
-
);
|
|
57
|
-
this.name = "InvalidScopeError";
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
var MissingUserContextError = class extends RBACError {
|
|
61
|
-
constructor() {
|
|
62
|
-
super(
|
|
63
|
-
"User context is required but not available. Make sure to wrap your app with an auth provider.",
|
|
64
|
-
"MISSING_USER_CONTEXT"
|
|
65
|
-
);
|
|
66
|
-
this.name = "MissingUserContextError";
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
// src/rbac/cache.ts
|
|
71
|
-
var RBACCache = class {
|
|
72
|
-
constructor() {
|
|
73
|
-
this.cache = /* @__PURE__ */ new Map();
|
|
74
|
-
this.sessionCache = /* @__PURE__ */ new Map();
|
|
75
|
-
this.TTL = 120 * 1e3;
|
|
76
|
-
// 120 seconds (short-term) - increased from 60s
|
|
77
|
-
this.SESSION_TTL = 15 * 60 * 1e3;
|
|
78
|
-
// 15 minutes (session-level) - increased from 5min
|
|
79
|
-
this.invalidationCallbacks = /* @__PURE__ */ new Set();
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Get a value from the cache
|
|
83
|
-
*
|
|
84
|
-
* Checks both short-term cache and session cache.
|
|
85
|
-
*
|
|
86
|
-
* @param key - Cache key
|
|
87
|
-
* @param useSessionCache - Whether to check session cache (default: true)
|
|
88
|
-
* @returns Cached value or null if not found/expired
|
|
89
|
-
*/
|
|
90
|
-
get(key, useSessionCache = true) {
|
|
91
|
-
const now = Date.now();
|
|
92
|
-
const entry = this.cache.get(key);
|
|
93
|
-
if (entry && now <= entry.expires) {
|
|
94
|
-
return entry.data;
|
|
95
|
-
}
|
|
96
|
-
if (entry && now > entry.expires) {
|
|
97
|
-
this.cache.delete(key);
|
|
98
|
-
}
|
|
99
|
-
if (useSessionCache) {
|
|
100
|
-
const sessionEntry = this.sessionCache.get(key);
|
|
101
|
-
if (sessionEntry && now <= sessionEntry.expires) {
|
|
102
|
-
return sessionEntry.data;
|
|
103
|
-
}
|
|
104
|
-
if (sessionEntry && now > sessionEntry.expires) {
|
|
105
|
-
this.sessionCache.delete(key);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return null;
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Set a value in the cache
|
|
112
|
-
*
|
|
113
|
-
* @param key - Cache key
|
|
114
|
-
* @param data - Data to cache
|
|
115
|
-
* @param ttl - Time to live in milliseconds (defaults to 60s)
|
|
116
|
-
* @param useSessionCache - Whether to also store in session cache (default: false for page-level checks)
|
|
117
|
-
*/
|
|
118
|
-
set(key, data, ttl = this.TTL, useSessionCache = false) {
|
|
119
|
-
const now = Date.now();
|
|
120
|
-
const expires = ttl <= 0 ? now - 1 : now + ttl;
|
|
121
|
-
this.cache.set(key, {
|
|
122
|
-
data,
|
|
123
|
-
expires
|
|
124
|
-
});
|
|
125
|
-
if (useSessionCache) {
|
|
126
|
-
const sessionExpires = ttl <= 0 ? now - 1 : now + this.SESSION_TTL;
|
|
127
|
-
this.sessionCache.set(key, {
|
|
128
|
-
data,
|
|
129
|
-
expires: sessionExpires
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Delete a specific key from the cache
|
|
135
|
-
*
|
|
136
|
-
* @param key - Cache key to delete
|
|
137
|
-
*/
|
|
138
|
-
delete(key) {
|
|
139
|
-
this.cache.delete(key);
|
|
140
|
-
this.sessionCache.delete(key);
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Invalidate cache entries matching a pattern
|
|
144
|
-
*
|
|
145
|
-
* @param pattern - Pattern to match against cache keys
|
|
146
|
-
*/
|
|
147
|
-
invalidate(pattern) {
|
|
148
|
-
const trimmedPattern = pattern?.trim();
|
|
149
|
-
if (!trimmedPattern) {
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
const matcher = this.createMatcher(trimmedPattern);
|
|
153
|
-
const keysToDelete = [];
|
|
154
|
-
for (const key of this.cache.keys()) {
|
|
155
|
-
if (matcher(key)) {
|
|
156
|
-
keysToDelete.push(key);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
for (const key of this.sessionCache.keys()) {
|
|
160
|
-
if (matcher(key) && !keysToDelete.includes(key)) {
|
|
161
|
-
keysToDelete.push(key);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
keysToDelete.forEach((key) => {
|
|
165
|
-
this.cache.delete(key);
|
|
166
|
-
this.sessionCache.delete(key);
|
|
167
|
-
});
|
|
168
|
-
this.invalidationCallbacks.forEach((callback) => callback(trimmedPattern));
|
|
169
|
-
}
|
|
170
|
-
createMatcher(pattern) {
|
|
171
|
-
if (pattern.includes("*")) {
|
|
172
|
-
const escapedSegments = pattern.split("*").map((segment) => segment.replace(/[|\\{}()[\]^$+?.-]/g, "\\$&"));
|
|
173
|
-
const regexPattern = escapedSegments.join(".*");
|
|
174
|
-
const regex = new RegExp(regexPattern);
|
|
175
|
-
return (key) => regex.test(key);
|
|
176
|
-
}
|
|
177
|
-
return (key) => key.includes(pattern);
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Clear all cache entries
|
|
181
|
-
*/
|
|
182
|
-
clear() {
|
|
183
|
-
this.cache.clear();
|
|
184
|
-
this.sessionCache.clear();
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* Get cache statistics
|
|
188
|
-
*/
|
|
189
|
-
getStats() {
|
|
190
|
-
return {
|
|
191
|
-
size: this.cache.size,
|
|
192
|
-
sessionSize: this.sessionCache.size,
|
|
193
|
-
ttl: this.TTL,
|
|
194
|
-
sessionTtl: this.SESSION_TTL,
|
|
195
|
-
keys: Array.from(this.cache.keys())
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* Add an invalidation callback
|
|
200
|
-
*
|
|
201
|
-
* @param callback - Function to call when cache is invalidated
|
|
202
|
-
*/
|
|
203
|
-
onInvalidate(callback) {
|
|
204
|
-
this.invalidationCallbacks.add(callback);
|
|
205
|
-
return () => {
|
|
206
|
-
this.invalidationCallbacks.delete(callback);
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* Generate cache key for permission check (simplified signature)
|
|
211
|
-
*
|
|
212
|
-
* @param userId - User ID
|
|
213
|
-
* @param permission - Permission string
|
|
214
|
-
* @param organisationId - Organisation ID (optional)
|
|
215
|
-
* @param eventId - Event ID (optional)
|
|
216
|
-
* @param appId - App ID (optional)
|
|
217
|
-
* @param pageId - Page ID (optional)
|
|
218
|
-
* @returns String cache key
|
|
219
|
-
*/
|
|
220
|
-
static generateKey(userId, permission, organisationId, eventId, appId, pageId) {
|
|
221
|
-
const parts = [
|
|
222
|
-
"perm",
|
|
223
|
-
userId,
|
|
224
|
-
organisationId || "null",
|
|
225
|
-
eventId || "null",
|
|
226
|
-
appId || "null",
|
|
227
|
-
permission || "null",
|
|
228
|
-
pageId || "null"
|
|
229
|
-
];
|
|
230
|
-
return parts.join(":");
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Generate cache key for permission check (object signature)
|
|
234
|
-
*
|
|
235
|
-
* @param key - Permission cache key object
|
|
236
|
-
* @returns String cache key
|
|
237
|
-
*/
|
|
238
|
-
static generatePermissionKey(key) {
|
|
239
|
-
const parts = [
|
|
240
|
-
"perm",
|
|
241
|
-
key.userId,
|
|
242
|
-
key.organisationId || "null",
|
|
243
|
-
key.eventId || "null",
|
|
244
|
-
key.appId || "null",
|
|
245
|
-
key.permission || "null",
|
|
246
|
-
key.pageId || "null"
|
|
247
|
-
];
|
|
248
|
-
return parts.join(":");
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Generate cache key for access level
|
|
252
|
-
*
|
|
253
|
-
* @param userId - User ID
|
|
254
|
-
* @param organisationId - Organisation ID
|
|
255
|
-
* @param eventId - Event ID (optional)
|
|
256
|
-
* @param appId - App ID (optional)
|
|
257
|
-
* @returns String cache key
|
|
258
|
-
*/
|
|
259
|
-
static generateAccessLevelKey(userId, organisationId, eventId, appId) {
|
|
260
|
-
const parts = [
|
|
261
|
-
"access",
|
|
262
|
-
userId,
|
|
263
|
-
organisationId,
|
|
264
|
-
eventId || "null",
|
|
265
|
-
appId || "null"
|
|
266
|
-
];
|
|
267
|
-
return parts.join(":");
|
|
268
|
-
}
|
|
269
|
-
/**
|
|
270
|
-
* Generate cache key for permission map
|
|
271
|
-
*
|
|
272
|
-
* @param userId - User ID
|
|
273
|
-
* @param organisationId - Organisation ID
|
|
274
|
-
* @param eventId - Event ID (optional)
|
|
275
|
-
* @param appId - App ID (optional)
|
|
276
|
-
* @returns String cache key
|
|
277
|
-
*/
|
|
278
|
-
static generatePermissionMapKey(userId, organisationId, eventId, appId) {
|
|
279
|
-
const parts = [
|
|
280
|
-
"map",
|
|
281
|
-
userId,
|
|
282
|
-
organisationId,
|
|
283
|
-
eventId || "null",
|
|
284
|
-
appId || "null"
|
|
285
|
-
];
|
|
286
|
-
return parts.join(":");
|
|
287
|
-
}
|
|
288
|
-
};
|
|
289
|
-
var rbacCache = new RBACCache();
|
|
290
|
-
var CACHE_PATTERNS = {
|
|
291
|
-
USER: (userId) => `:${userId}:`,
|
|
292
|
-
ORGANISATION: (organisationId) => `:${organisationId}:`,
|
|
293
|
-
EVENT: (eventId) => `:${eventId}:`,
|
|
294
|
-
APP: (appId) => `:${appId}`,
|
|
295
|
-
PERMISSION: (userId, organisationId) => `perm:${userId}:${organisationId}:`
|
|
296
|
-
};
|
|
297
|
-
|
|
298
|
-
// src/rbac/cache-invalidation.ts
|
|
299
|
-
var log = createLogger("RBACCache");
|
|
300
|
-
var INVALIDATION_PATTERNS = {
|
|
301
|
-
// User-level invalidation
|
|
302
|
-
USER_ROLES_CHANGED: (userId) => [
|
|
303
|
-
CACHE_PATTERNS.USER(userId),
|
|
304
|
-
`perm:${userId}:*`,
|
|
305
|
-
`access:${userId}:*`,
|
|
306
|
-
`map:${userId}:*`
|
|
307
|
-
],
|
|
308
|
-
// Organisation-level invalidation
|
|
309
|
-
ORGANISATION_PERMISSIONS_CHANGED: (organisationId) => [
|
|
310
|
-
CACHE_PATTERNS.ORGANISATION(organisationId),
|
|
311
|
-
`perm:*:${organisationId}:*`,
|
|
312
|
-
`access:*:${organisationId}:*`,
|
|
313
|
-
`map:*:${organisationId}:*`
|
|
314
|
-
],
|
|
315
|
-
// Event-level invalidation
|
|
316
|
-
EVENT_PERMISSIONS_CHANGED: (eventId) => [
|
|
317
|
-
CACHE_PATTERNS.EVENT(eventId),
|
|
318
|
-
`perm:*:*:${eventId}:*`,
|
|
319
|
-
`access:*:*:${eventId}:*`,
|
|
320
|
-
`map:*:*:${eventId}:*`
|
|
321
|
-
],
|
|
322
|
-
// App-level invalidation
|
|
323
|
-
APP_PERMISSIONS_CHANGED: (appId) => [
|
|
324
|
-
CACHE_PATTERNS.APP(appId),
|
|
325
|
-
`perm:*:*:*:${appId}:*`,
|
|
326
|
-
`access:*:*:*:${appId}`,
|
|
327
|
-
`map:*:*:*:${appId}`
|
|
328
|
-
],
|
|
329
|
-
// Page-level invalidation
|
|
330
|
-
PAGE_PERMISSIONS_CHANGED: (pageId) => [
|
|
331
|
-
`perm:*:*:*:*:${pageId}`,
|
|
332
|
-
`map:*:*:*:*`
|
|
333
|
-
]
|
|
334
|
-
};
|
|
335
|
-
var RBACCacheInvalidationManager = class {
|
|
336
|
-
constructor(supabase) {
|
|
337
|
-
this.invalidationCallbacks = /* @__PURE__ */ new Set();
|
|
338
|
-
this.channels = [];
|
|
339
|
-
this.supabase = supabase;
|
|
340
|
-
this.setupRealtimeSubscriptions();
|
|
341
|
-
}
|
|
342
|
-
/**
|
|
343
|
-
* Add a callback for cache invalidation events
|
|
344
|
-
*
|
|
345
|
-
* @param callback - Function to call when cache is invalidated
|
|
346
|
-
* @returns Unsubscribe function
|
|
347
|
-
*/
|
|
348
|
-
onInvalidation(callback) {
|
|
349
|
-
this.invalidationCallbacks.add(callback);
|
|
350
|
-
return () => this.invalidationCallbacks.delete(callback);
|
|
351
|
-
}
|
|
352
|
-
/**
|
|
353
|
-
* Invalidate cache for a specific user
|
|
354
|
-
*
|
|
355
|
-
* @param userId - User ID
|
|
356
|
-
* @param reason - Reason for invalidation
|
|
357
|
-
*/
|
|
358
|
-
invalidateUser(userId, reason) {
|
|
359
|
-
const patterns = INVALIDATION_PATTERNS.USER_ROLES_CHANGED(userId);
|
|
360
|
-
this.invalidatePatterns(patterns, reason);
|
|
361
|
-
}
|
|
362
|
-
/**
|
|
363
|
-
* Invalidate cache for a specific organisation
|
|
364
|
-
*
|
|
365
|
-
* @param organisationId - Organisation ID
|
|
366
|
-
* @param reason - Reason for invalidation
|
|
367
|
-
*/
|
|
368
|
-
invalidateOrganisation(organisationId, reason) {
|
|
369
|
-
const patterns = INVALIDATION_PATTERNS.ORGANISATION_PERMISSIONS_CHANGED(organisationId);
|
|
370
|
-
this.invalidatePatterns(patterns, reason);
|
|
371
|
-
}
|
|
372
|
-
/**
|
|
373
|
-
* Invalidate cache for a specific event
|
|
374
|
-
*
|
|
375
|
-
* @param eventId - Event ID
|
|
376
|
-
* @param reason - Reason for invalidation
|
|
377
|
-
*/
|
|
378
|
-
invalidateEvent(eventId, reason) {
|
|
379
|
-
const patterns = INVALIDATION_PATTERNS.EVENT_PERMISSIONS_CHANGED(eventId);
|
|
380
|
-
this.invalidatePatterns(patterns, reason);
|
|
381
|
-
}
|
|
382
|
-
/**
|
|
383
|
-
* Invalidate cache for a specific app
|
|
384
|
-
*
|
|
385
|
-
* @param appId - App ID
|
|
386
|
-
* @param reason - Reason for invalidation
|
|
387
|
-
*/
|
|
388
|
-
invalidateApp(appId, reason) {
|
|
389
|
-
const patterns = INVALIDATION_PATTERNS.APP_PERMISSIONS_CHANGED(appId);
|
|
390
|
-
this.invalidatePatterns(patterns, reason);
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* Invalidate cache for a specific page
|
|
394
|
-
*
|
|
395
|
-
* @param pageId - Page ID
|
|
396
|
-
* @param reason - Reason for invalidation
|
|
397
|
-
*/
|
|
398
|
-
invalidatePage(pageId, reason) {
|
|
399
|
-
const patterns = INVALIDATION_PATTERNS.PAGE_PERMISSIONS_CHANGED(pageId);
|
|
400
|
-
this.invalidatePatterns(patterns, reason);
|
|
401
|
-
}
|
|
402
|
-
/**
|
|
403
|
-
* Invalidate cache patterns and notify callbacks
|
|
404
|
-
*
|
|
405
|
-
* @param patterns - Array of cache patterns to invalidate
|
|
406
|
-
* @param reason - Reason for invalidation
|
|
407
|
-
*/
|
|
408
|
-
invalidatePatterns(patterns, reason) {
|
|
409
|
-
log.debug(`Invalidating patterns: ${patterns.join(", ")} (${reason})`);
|
|
410
|
-
patterns.forEach((pattern) => {
|
|
411
|
-
rbacCache.invalidate(pattern);
|
|
412
|
-
});
|
|
413
|
-
this.invalidationCallbacks.forEach((callback) => {
|
|
414
|
-
patterns.forEach((pattern) => callback(pattern));
|
|
415
|
-
});
|
|
416
|
-
emitAuditEvent({
|
|
417
|
-
type: "permission_check",
|
|
418
|
-
userId: "system",
|
|
419
|
-
organisationId: "00000000-0000-0000-0000-000000000000",
|
|
420
|
-
permission: "cache:invalidate",
|
|
421
|
-
decision: true,
|
|
422
|
-
source: "api",
|
|
423
|
-
duration_ms: 0,
|
|
424
|
-
metadata: {
|
|
425
|
-
reason,
|
|
426
|
-
patterns,
|
|
427
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
428
|
-
cache_invalidation: true
|
|
429
|
-
}
|
|
430
|
-
}).catch((error) => {
|
|
431
|
-
log.warn("Failed to log cache invalidation audit event:", error);
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* Cleanup subscriptions only (not all callbacks)
|
|
436
|
-
* Used internally to cleanup before setting up new subscriptions
|
|
437
|
-
*/
|
|
438
|
-
cleanupSubscriptions() {
|
|
439
|
-
this.channels.forEach((channel) => {
|
|
440
|
-
try {
|
|
441
|
-
if (channel && typeof channel.unsubscribe === "function") {
|
|
442
|
-
channel.unsubscribe();
|
|
443
|
-
}
|
|
444
|
-
} catch (error) {
|
|
445
|
-
log.warn("Failed to unsubscribe from channel:", error);
|
|
446
|
-
}
|
|
447
|
-
});
|
|
448
|
-
this.channels = [];
|
|
449
|
-
}
|
|
450
|
-
/**
|
|
451
|
-
* Setup realtime subscriptions for automatic cache invalidation
|
|
452
|
-
* Always cleans up existing subscriptions before setting up new ones to prevent duplicates
|
|
453
|
-
*/
|
|
454
|
-
setupRealtimeSubscriptions() {
|
|
455
|
-
this.cleanupSubscriptions();
|
|
456
|
-
if (!this.supabase.channel || typeof this.supabase.channel !== "function") {
|
|
457
|
-
log.debug("Realtime not available, skipping subscriptions");
|
|
458
|
-
return;
|
|
459
|
-
}
|
|
460
|
-
const orgRolesChannel = this.supabase.channel("rbac_organisation_roles_changes").on("postgres_changes", {
|
|
461
|
-
event: "*",
|
|
462
|
-
schema: "public",
|
|
463
|
-
table: "rbac_organisation_roles"
|
|
464
|
-
}, (payload) => {
|
|
465
|
-
const { organisation_id, user_id } = payload.new || payload.old || {};
|
|
466
|
-
if (organisation_id) {
|
|
467
|
-
this.invalidateOrganisation(organisation_id, `organisation_role_${payload.eventType}`);
|
|
468
|
-
}
|
|
469
|
-
if (user_id) {
|
|
470
|
-
this.invalidateUser(user_id, `organisation_role_${payload.eventType}`);
|
|
471
|
-
}
|
|
472
|
-
});
|
|
473
|
-
const orgRolesSubscription = orgRolesChannel.subscribe();
|
|
474
|
-
this.channels.push(orgRolesSubscription);
|
|
475
|
-
const eventAppRolesChannel = this.supabase.channel("rbac_event_app_roles_changes").on("postgres_changes", {
|
|
476
|
-
event: "*",
|
|
477
|
-
schema: "public",
|
|
478
|
-
table: "rbac_event_app_roles"
|
|
479
|
-
}, (payload) => {
|
|
480
|
-
const { organisation_id, user_id, event_id, app_id } = payload.new || payload.old || {};
|
|
481
|
-
if (organisation_id) {
|
|
482
|
-
this.invalidateOrganisation(organisation_id, `event_app_role_${payload.eventType}`);
|
|
483
|
-
}
|
|
484
|
-
if (user_id) {
|
|
485
|
-
this.invalidateUser(user_id, `event_app_role_${payload.eventType}`);
|
|
486
|
-
}
|
|
487
|
-
if (event_id) {
|
|
488
|
-
this.invalidateEvent(event_id, `event_app_role_${payload.eventType}`);
|
|
489
|
-
}
|
|
490
|
-
if (app_id) {
|
|
491
|
-
this.invalidateApp(app_id, `event_app_role_${payload.eventType}`);
|
|
492
|
-
}
|
|
493
|
-
});
|
|
494
|
-
const eventAppRolesSubscription = eventAppRolesChannel.subscribe();
|
|
495
|
-
this.channels.push(eventAppRolesSubscription);
|
|
496
|
-
const globalRolesChannel = this.supabase.channel("rbac_global_roles_changes").on("postgres_changes", {
|
|
497
|
-
event: "*",
|
|
498
|
-
schema: "public",
|
|
499
|
-
table: "rbac_global_roles"
|
|
500
|
-
}, (payload) => {
|
|
501
|
-
const { user_id } = payload.new || payload.old || {};
|
|
502
|
-
if (user_id) {
|
|
503
|
-
this.invalidateUser(user_id, `global_role_${payload.eventType}`);
|
|
504
|
-
}
|
|
505
|
-
});
|
|
506
|
-
const globalRolesSubscription = globalRolesChannel.subscribe();
|
|
507
|
-
this.channels.push(globalRolesSubscription);
|
|
508
|
-
const pagePermissionsChannel = this.supabase.channel("rbac_page_permissions_changes").on("postgres_changes", {
|
|
509
|
-
event: "*",
|
|
510
|
-
schema: "public",
|
|
511
|
-
table: "rbac_page_permissions"
|
|
512
|
-
}, (payload) => {
|
|
513
|
-
const { organisation_id, app_page_id, role_id } = payload.new || payload.old || {};
|
|
514
|
-
if (organisation_id) {
|
|
515
|
-
this.invalidateOrganisation(organisation_id, `page_permission_${payload.eventType}`);
|
|
516
|
-
}
|
|
517
|
-
if (app_page_id) {
|
|
518
|
-
this.invalidatePage(app_page_id, `page_permission_${payload.eventType}`);
|
|
519
|
-
}
|
|
520
|
-
});
|
|
521
|
-
const pagePermissionsSubscription = pagePermissionsChannel.subscribe();
|
|
522
|
-
this.channels.push(pagePermissionsSubscription);
|
|
523
|
-
}
|
|
524
|
-
/**
|
|
525
|
-
* Cleanup all realtime subscriptions
|
|
526
|
-
* Call this when the manager is no longer needed to prevent memory leaks
|
|
527
|
-
*/
|
|
528
|
-
cleanup() {
|
|
529
|
-
this.channels.forEach((channel) => {
|
|
530
|
-
try {
|
|
531
|
-
if (channel && typeof channel.unsubscribe === "function") {
|
|
532
|
-
channel.unsubscribe();
|
|
533
|
-
}
|
|
534
|
-
} catch (error) {
|
|
535
|
-
log.warn("Failed to unsubscribe from channel:", error);
|
|
536
|
-
}
|
|
537
|
-
});
|
|
538
|
-
this.channels = [];
|
|
539
|
-
this.invalidationCallbacks.clear();
|
|
540
|
-
}
|
|
541
|
-
/**
|
|
542
|
-
* Manually trigger cache invalidation for all users in an organisation
|
|
543
|
-
*
|
|
544
|
-
* @param organisationId - Organisation ID
|
|
545
|
-
* @param reason - Reason for invalidation
|
|
546
|
-
*/
|
|
547
|
-
async invalidateAllUsersInOrganisation(organisationId, reason) {
|
|
548
|
-
const { data: users } = await this.supabase.from("rbac_organisation_roles").select("user_id").eq("organisation_id", organisationId).eq("is_active", true);
|
|
549
|
-
if (users) {
|
|
550
|
-
users.forEach(({ user_id }) => {
|
|
551
|
-
this.invalidateUser(user_id, reason);
|
|
552
|
-
});
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
/**
|
|
556
|
-
* Clear all cache entries
|
|
557
|
-
*/
|
|
558
|
-
clearAllCache() {
|
|
559
|
-
log.debug("Clearing all cache entries");
|
|
560
|
-
rbacCache.clear();
|
|
561
|
-
}
|
|
562
|
-
};
|
|
563
|
-
var globalCacheInvalidationManager = null;
|
|
564
|
-
function initializeCacheInvalidation(supabase) {
|
|
565
|
-
if (globalCacheInvalidationManager) {
|
|
566
|
-
globalCacheInvalidationManager.cleanup();
|
|
567
|
-
}
|
|
568
|
-
globalCacheInvalidationManager = new RBACCacheInvalidationManager(supabase);
|
|
569
|
-
return globalCacheInvalidationManager;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
// src/rbac/errors.ts
|
|
573
|
-
var RATE_LIMIT_STATUS_CODES = /* @__PURE__ */ new Set([429]);
|
|
574
|
-
var AUTH_STATUS_CODES = /* @__PURE__ */ new Set([401]);
|
|
575
|
-
var AUTHZ_STATUS_CODES = /* @__PURE__ */ new Set([403]);
|
|
576
|
-
function normalize(value) {
|
|
577
|
-
if (!value) {
|
|
578
|
-
return "";
|
|
579
|
-
}
|
|
580
|
-
if (typeof value === "string") {
|
|
581
|
-
return value.toLowerCase();
|
|
582
|
-
}
|
|
583
|
-
if (value instanceof Error && typeof value.message === "string") {
|
|
584
|
-
return value.message.toLowerCase();
|
|
585
|
-
}
|
|
586
|
-
return String(value).toLowerCase();
|
|
587
|
-
}
|
|
588
|
-
function categorizeError(error) {
|
|
589
|
-
if (error instanceof PermissionDeniedError) {
|
|
590
|
-
return "authorization_error" /* AUTHORIZATION */;
|
|
591
|
-
}
|
|
592
|
-
if (error instanceof OrganisationContextRequiredError || error instanceof InvalidScopeError) {
|
|
593
|
-
return "validation_error" /* VALIDATION */;
|
|
594
|
-
}
|
|
595
|
-
if (error instanceof MissingUserContextError) {
|
|
596
|
-
return "authentication_error" /* AUTHENTICATION */;
|
|
597
|
-
}
|
|
598
|
-
if (error instanceof RBACError) {
|
|
599
|
-
switch (error.code) {
|
|
600
|
-
case "PERMISSION_DENIED":
|
|
601
|
-
return "authorization_error" /* AUTHORIZATION */;
|
|
602
|
-
case "ORGANISATION_CONTEXT_REQUIRED":
|
|
603
|
-
case "INVALID_SCOPE":
|
|
604
|
-
return "validation_error" /* VALIDATION */;
|
|
605
|
-
case "MISSING_USER_CONTEXT":
|
|
606
|
-
return "authentication_error" /* AUTHENTICATION */;
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
if (error && typeof error === "object") {
|
|
610
|
-
const status = error.status;
|
|
611
|
-
if (typeof status === "number") {
|
|
612
|
-
if (RATE_LIMIT_STATUS_CODES.has(status)) {
|
|
613
|
-
return "rate_limit_error" /* RATE_LIMIT */;
|
|
614
|
-
}
|
|
615
|
-
if (AUTH_STATUS_CODES.has(status)) {
|
|
616
|
-
return "authentication_error" /* AUTHENTICATION */;
|
|
617
|
-
}
|
|
618
|
-
if (AUTHZ_STATUS_CODES.has(status)) {
|
|
619
|
-
return "authorization_error" /* AUTHORIZATION */;
|
|
620
|
-
}
|
|
621
|
-
if (status >= 500) {
|
|
622
|
-
return "database_error" /* DATABASE */;
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
const codeValue = normalize(error.code);
|
|
626
|
-
if (codeValue) {
|
|
627
|
-
if (codeValue.includes("network")) {
|
|
628
|
-
return "network_error" /* NETWORK */;
|
|
629
|
-
}
|
|
630
|
-
if (codeValue.includes("postgres") || codeValue.includes("database") || codeValue.includes("db")) {
|
|
631
|
-
return "database_error" /* DATABASE */;
|
|
632
|
-
}
|
|
633
|
-
if (codeValue.includes("rate")) {
|
|
634
|
-
return "rate_limit_error" /* RATE_LIMIT */;
|
|
635
|
-
}
|
|
636
|
-
if (codeValue.includes("auth")) {
|
|
637
|
-
return "authentication_error" /* AUTHENTICATION */;
|
|
638
|
-
}
|
|
639
|
-
if (codeValue.includes("permission")) {
|
|
640
|
-
return "authorization_error" /* AUTHORIZATION */;
|
|
641
|
-
}
|
|
642
|
-
if (codeValue.includes("scope") || codeValue.includes("invalid")) {
|
|
643
|
-
return "validation_error" /* VALIDATION */;
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
const message = normalize(error);
|
|
648
|
-
if (message.includes("timeout") || message.includes("network") || message.includes("fetch")) {
|
|
649
|
-
return "network_error" /* NETWORK */;
|
|
650
|
-
}
|
|
651
|
-
if (message.includes("postgres") || message.includes("database") || message.includes("connection")) {
|
|
652
|
-
return "database_error" /* DATABASE */;
|
|
653
|
-
}
|
|
654
|
-
if (message.includes("rate limit") || message.includes("too many requests")) {
|
|
655
|
-
return "rate_limit_error" /* RATE_LIMIT */;
|
|
656
|
-
}
|
|
657
|
-
if (message.includes("permission") || message.includes("forbidden")) {
|
|
658
|
-
return "authorization_error" /* AUTHORIZATION */;
|
|
659
|
-
}
|
|
660
|
-
if (message.includes("auth") || message.includes("token") || message.includes("session")) {
|
|
661
|
-
return "authentication_error" /* AUTHENTICATION */;
|
|
662
|
-
}
|
|
663
|
-
if (message.includes("invalid") || message.includes("scope") || message.includes("validation")) {
|
|
664
|
-
return "validation_error" /* VALIDATION */;
|
|
665
|
-
}
|
|
666
|
-
return "unknown_error" /* UNKNOWN */;
|
|
667
|
-
}
|
|
668
|
-
function mapErrorCategoryToSecurityEventType(category) {
|
|
669
|
-
switch (category) {
|
|
670
|
-
case "authorization_error" /* AUTHORIZATION */:
|
|
671
|
-
return "permission_denied";
|
|
672
|
-
case "network_error" /* NETWORK */:
|
|
673
|
-
return "network_error";
|
|
674
|
-
case "database_error" /* DATABASE */:
|
|
675
|
-
return "database_error";
|
|
676
|
-
case "validation_error" /* VALIDATION */:
|
|
677
|
-
return "validation_error";
|
|
678
|
-
case "rate_limit_error" /* RATE_LIMIT */:
|
|
679
|
-
return "rate_limit_error";
|
|
680
|
-
case "authentication_error" /* AUTHENTICATION */:
|
|
681
|
-
return "authentication_error";
|
|
682
|
-
case "unknown_error" /* UNKNOWN */:
|
|
683
|
-
default:
|
|
684
|
-
return "unknown_error";
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
// src/rbac/security.ts
|
|
689
|
-
var log2 = createLogger("RBACSecurity");
|
|
690
|
-
var RBACSecurityValidator = class {
|
|
691
|
-
/**
|
|
692
|
-
* Validate permission string format
|
|
693
|
-
* @param permission - Permission string to validate
|
|
694
|
-
* @returns True if valid, false otherwise
|
|
695
|
-
*/
|
|
696
|
-
static validatePermission(permission) {
|
|
697
|
-
if (typeof permission !== "string" || permission.length === 0) {
|
|
698
|
-
return false;
|
|
699
|
-
}
|
|
700
|
-
const permissionRegex = /^(read|create|update|delete):[a-z0-9._-]+$/;
|
|
701
|
-
return permissionRegex.test(permission);
|
|
702
|
-
}
|
|
703
|
-
/**
|
|
704
|
-
* Validate UUID format
|
|
705
|
-
* @param uuid - UUID string to validate
|
|
706
|
-
* @returns True if valid, false otherwise
|
|
707
|
-
*/
|
|
708
|
-
static validateUUID(uuid) {
|
|
709
|
-
if (typeof uuid !== "string" || uuid.length === 0) {
|
|
710
|
-
return false;
|
|
711
|
-
}
|
|
712
|
-
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
713
|
-
return uuidRegex.test(uuid);
|
|
714
|
-
}
|
|
715
|
-
/**
|
|
716
|
-
* Validate scope object
|
|
717
|
-
* @param scope - Scope object to validate
|
|
718
|
-
* @returns True if valid, false otherwise
|
|
719
|
-
*/
|
|
720
|
-
static validateScope(scope) {
|
|
721
|
-
if (!scope || typeof scope !== "object") {
|
|
722
|
-
return false;
|
|
723
|
-
}
|
|
724
|
-
if (scope.organisationId !== void 0) {
|
|
725
|
-
if (typeof scope.organisationId === "string" && scope.organisationId.trim() === "") {
|
|
726
|
-
return false;
|
|
727
|
-
}
|
|
728
|
-
if (scope.organisationId && !this.validateUUID(scope.organisationId)) {
|
|
729
|
-
return false;
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
if (scope.eventId && typeof scope.eventId !== "string") {
|
|
733
|
-
return false;
|
|
734
|
-
}
|
|
735
|
-
if (scope.appId && !this.validateUUID(scope.appId)) {
|
|
736
|
-
return false;
|
|
737
|
-
}
|
|
738
|
-
return !!(scope.organisationId || scope.eventId || scope.appId);
|
|
739
|
-
}
|
|
740
|
-
/**
|
|
741
|
-
* Sanitize input string to prevent injection attacks
|
|
742
|
-
* @param input - Input string to sanitize
|
|
743
|
-
* @returns Sanitized string
|
|
744
|
-
*/
|
|
745
|
-
static sanitizeInput(input) {
|
|
746
|
-
if (typeof input !== "string") {
|
|
747
|
-
return "";
|
|
748
|
-
}
|
|
749
|
-
return input.replace(/<[^>]*>/g, "").replace(/[<>\"'&]/g, "").replace(/[;()]/g, "").replace(/javascript:/gi, "").replace(/data:/gi, "").trim();
|
|
750
|
-
}
|
|
751
|
-
/**
|
|
752
|
-
* Validate user ID format
|
|
753
|
-
* @param userId - User ID to validate
|
|
754
|
-
* @returns True if valid, false otherwise
|
|
755
|
-
*/
|
|
756
|
-
static validateUserId(userId) {
|
|
757
|
-
return this.validateUUID(userId);
|
|
758
|
-
}
|
|
759
|
-
/**
|
|
760
|
-
* Check if permission is a wildcard permission
|
|
761
|
-
* @param permission - Permission string to check
|
|
762
|
-
* @returns True if wildcard, false otherwise
|
|
763
|
-
*/
|
|
764
|
-
static isWildcardPermission(permission) {
|
|
765
|
-
return permission.includes("*") || permission.endsWith(":*");
|
|
766
|
-
}
|
|
767
|
-
/**
|
|
768
|
-
* Validate permission hierarchy
|
|
769
|
-
* @param permission - Permission to validate
|
|
770
|
-
* @param requiredOperation - Required operation
|
|
771
|
-
* @returns True if permission matches or is higher in hierarchy
|
|
772
|
-
*/
|
|
773
|
-
static validatePermissionHierarchy(permission, requiredOperation) {
|
|
774
|
-
if (!this.validatePermission(permission)) {
|
|
775
|
-
return false;
|
|
776
|
-
}
|
|
777
|
-
const [operation] = permission.split(":");
|
|
778
|
-
const hierarchy = ["read", "create", "update", "delete"];
|
|
779
|
-
const permissionLevel = hierarchy.indexOf(operation);
|
|
780
|
-
const requiredLevel = hierarchy.indexOf(requiredOperation);
|
|
781
|
-
if (permissionLevel === -1 || requiredLevel === -1) {
|
|
782
|
-
return false;
|
|
783
|
-
}
|
|
784
|
-
return permissionLevel >= requiredLevel;
|
|
785
|
-
}
|
|
786
|
-
/**
|
|
787
|
-
* Rate limiting check (placeholder for future implementation)
|
|
788
|
-
* @param userId - User ID
|
|
789
|
-
* @param operation - Operation being performed
|
|
790
|
-
* @returns True if within rate limit, false otherwise
|
|
791
|
-
*/
|
|
792
|
-
static async checkRateLimit(userId, operation) {
|
|
793
|
-
return true;
|
|
794
|
-
}
|
|
795
|
-
// Only warn once per 5 seconds per user
|
|
796
|
-
static logSecurityEvent(event) {
|
|
797
|
-
const securityEvent = {
|
|
798
|
-
...event,
|
|
799
|
-
timestamp: event.timestamp || /* @__PURE__ */ new Date(),
|
|
800
|
-
severity: this.getEventSeverity(event.type)
|
|
801
|
-
};
|
|
802
|
-
if (event.type === "rate_limit_exceeded") {
|
|
803
|
-
const now = Date.now();
|
|
804
|
-
const userWarning = this.rateLimitWarningCount.get(event.userId);
|
|
805
|
-
if (userWarning) {
|
|
806
|
-
const timeSinceLastWarning = now - userWarning.lastWarning;
|
|
807
|
-
if (timeSinceLastWarning < this.RATE_LIMIT_WARNING_THROTTLE_MS) {
|
|
808
|
-
userWarning.count++;
|
|
809
|
-
this.rateLimitWarningCount.set(event.userId, userWarning);
|
|
810
|
-
return;
|
|
811
|
-
} else {
|
|
812
|
-
log2.warn("Security event (throttled):", {
|
|
813
|
-
...securityEvent,
|
|
814
|
-
details: {
|
|
815
|
-
...securityEvent.details,
|
|
816
|
-
suppressedWarnings: userWarning.count,
|
|
817
|
-
message: `Rate limit exceeded (${userWarning.count + 1} times in last ${Math.round(timeSinceLastWarning / 1e3)}s)`
|
|
818
|
-
}
|
|
819
|
-
});
|
|
820
|
-
this.rateLimitWarningCount.set(event.userId, { count: 0, lastWarning: now });
|
|
821
|
-
return;
|
|
822
|
-
}
|
|
823
|
-
} else {
|
|
824
|
-
this.rateLimitWarningCount.set(event.userId, { count: 0, lastWarning: now });
|
|
825
|
-
log2.warn("Security event:", securityEvent);
|
|
826
|
-
return;
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
log2.warn("Security event:", securityEvent);
|
|
830
|
-
}
|
|
831
|
-
/**
|
|
832
|
-
* Get severity level for security event
|
|
833
|
-
* @param eventType - Type of security event
|
|
834
|
-
* @returns Severity level
|
|
835
|
-
*/
|
|
836
|
-
static getEventSeverity(eventType) {
|
|
837
|
-
switch (eventType) {
|
|
838
|
-
case "permission_denied":
|
|
839
|
-
return "low";
|
|
840
|
-
case "invalid_input":
|
|
841
|
-
case "rate_limit_exceeded":
|
|
842
|
-
case "rate_limit_error":
|
|
843
|
-
case "network_error":
|
|
844
|
-
return "medium";
|
|
845
|
-
case "validation_error":
|
|
846
|
-
return "high";
|
|
847
|
-
case "authentication_error":
|
|
848
|
-
case "database_error":
|
|
849
|
-
return "critical";
|
|
850
|
-
case "suspicious_activity":
|
|
851
|
-
case "unknown_error":
|
|
852
|
-
return "high";
|
|
853
|
-
default:
|
|
854
|
-
return "low";
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
};
|
|
858
|
-
/**
|
|
859
|
-
* Log security event for monitoring
|
|
860
|
-
* @param event - Security event details
|
|
861
|
-
*/
|
|
862
|
-
RBACSecurityValidator.rateLimitWarningCount = /* @__PURE__ */ new Map();
|
|
863
|
-
RBACSecurityValidator.RATE_LIMIT_WARNING_THROTTLE_MS = 5e3;
|
|
864
|
-
var DEFAULT_SECURITY_CONFIG = {
|
|
865
|
-
enableInputValidation: true,
|
|
866
|
-
enableRateLimiting: true,
|
|
867
|
-
enableAuditLogging: true,
|
|
868
|
-
maxPermissionChecksPerMinute: 1e3,
|
|
869
|
-
// Increased from 100 to 1000 for normal app usage
|
|
870
|
-
suspiciousActivityThreshold: 10
|
|
871
|
-
};
|
|
872
|
-
var RBACSecurityMiddleware = class {
|
|
873
|
-
constructor(config = DEFAULT_SECURITY_CONFIG) {
|
|
874
|
-
/**
|
|
875
|
-
* In-memory rate limiting cache (sliding window)
|
|
876
|
-
* Note: For production, this should use Redis or Supabase Edge Functions
|
|
877
|
-
*/
|
|
878
|
-
this.rateLimitCache = /* @__PURE__ */ new Map();
|
|
879
|
-
this.config = config;
|
|
880
|
-
this._startCleanupInterval();
|
|
881
|
-
}
|
|
882
|
-
/**
|
|
883
|
-
* Start periodic cleanup of expired entries
|
|
884
|
-
*/
|
|
885
|
-
_startCleanupInterval() {
|
|
886
|
-
setInterval(() => {
|
|
887
|
-
this.clearExpiredEntries();
|
|
888
|
-
}, 5 * 60 * 1e3);
|
|
889
|
-
}
|
|
890
|
-
/**
|
|
891
|
-
* Validate input before processing
|
|
892
|
-
* @param input - Input to validate
|
|
893
|
-
* @param context - Security context
|
|
894
|
-
* @returns Validation result
|
|
895
|
-
*/
|
|
896
|
-
async validateInput(input, context) {
|
|
897
|
-
const errors = [];
|
|
898
|
-
if (!RBACSecurityValidator.validateUserId(context.userId)) {
|
|
899
|
-
errors.push("Invalid user ID format");
|
|
900
|
-
}
|
|
901
|
-
const isPagePermission = input.permission?.includes(":page.") || !!input.pageId;
|
|
902
|
-
const requiresOrgId = !isPagePermission;
|
|
903
|
-
if (requiresOrgId) {
|
|
904
|
-
if (!context.organisationId) {
|
|
905
|
-
errors.push("Organisation ID is required for resource-level permissions");
|
|
906
|
-
} else if (!RBACSecurityValidator.validateUUID(context.organisationId)) {
|
|
907
|
-
errors.push("Invalid organisation ID format");
|
|
908
|
-
}
|
|
909
|
-
} else {
|
|
910
|
-
if (context.organisationId && !RBACSecurityValidator.validateUUID(context.organisationId)) {
|
|
911
|
-
errors.push("Invalid organisation ID format");
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
if (input.permission && !RBACSecurityValidator.validatePermission(input.permission)) {
|
|
915
|
-
errors.push("Invalid permission format");
|
|
916
|
-
}
|
|
917
|
-
if (input.scope && !RBACSecurityValidator.validateScope(input.scope)) {
|
|
918
|
-
errors.push("Invalid scope format");
|
|
919
|
-
}
|
|
920
|
-
if (this.config.enableInputValidation) {
|
|
921
|
-
if (context.ipAddress && typeof context.ipAddress !== "string") {
|
|
922
|
-
errors.push("Invalid IP address format");
|
|
923
|
-
}
|
|
924
|
-
if (context.userAgent && typeof context.userAgent !== "string") {
|
|
925
|
-
errors.push("Invalid user agent format");
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
if (errors.length > 0) {
|
|
929
|
-
RBACSecurityValidator.logSecurityEvent({
|
|
930
|
-
type: "invalid_input",
|
|
931
|
-
userId: context.userId,
|
|
932
|
-
details: { errors, input: this.sanitizeInput(JSON.stringify(input)) }
|
|
933
|
-
});
|
|
934
|
-
}
|
|
935
|
-
return {
|
|
936
|
-
isValid: errors.length === 0,
|
|
937
|
-
errors
|
|
938
|
-
};
|
|
939
|
-
}
|
|
940
|
-
/**
|
|
941
|
-
* Check rate limiting
|
|
942
|
-
* @param context - Security context
|
|
943
|
-
* @returns Rate limit check result
|
|
944
|
-
*/
|
|
945
|
-
async checkRateLimit(context) {
|
|
946
|
-
if (!this.config.enableRateLimiting) {
|
|
947
|
-
return { isAllowed: true, remaining: this.config.maxPermissionChecksPerMinute };
|
|
948
|
-
}
|
|
949
|
-
const isAllowed = await this._checkRateLimitInternal(context.userId);
|
|
950
|
-
const remaining = isAllowed ? this.config.maxPermissionChecksPerMinute - this._getRequestCount(context.userId) : 0;
|
|
951
|
-
return {
|
|
952
|
-
isAllowed,
|
|
953
|
-
remaining: Math.max(0, remaining)
|
|
954
|
-
};
|
|
955
|
-
}
|
|
956
|
-
async _checkRateLimitInternal(userId) {
|
|
957
|
-
const now = Date.now();
|
|
958
|
-
const windowMs = 60 * 1e3;
|
|
959
|
-
const entries = this.rateLimitCache.get(userId) || [];
|
|
960
|
-
const validEntries = entries.filter((entry) => now - entry.timestamp < windowMs);
|
|
961
|
-
const requestCount = validEntries.length;
|
|
962
|
-
const isAllowed = requestCount < this.config.maxPermissionChecksPerMinute;
|
|
963
|
-
if (isAllowed) {
|
|
964
|
-
validEntries.push({ timestamp: now });
|
|
965
|
-
}
|
|
966
|
-
this.rateLimitCache.set(userId, validEntries);
|
|
967
|
-
return isAllowed;
|
|
968
|
-
}
|
|
969
|
-
_getRequestCount(userId) {
|
|
970
|
-
const now = Date.now();
|
|
971
|
-
const windowMs = 60 * 1e3;
|
|
972
|
-
const entries = this.rateLimitCache.get(userId) || [];
|
|
973
|
-
const validEntries = entries.filter((entry) => now - entry.timestamp < windowMs);
|
|
974
|
-
return validEntries.length;
|
|
975
|
-
}
|
|
976
|
-
/**
|
|
977
|
-
* Clear old rate limit entries to prevent memory leaks
|
|
978
|
-
* Should be called periodically (e.g., every 5 minutes)
|
|
979
|
-
*/
|
|
980
|
-
clearExpiredEntries() {
|
|
981
|
-
const now = Date.now();
|
|
982
|
-
const windowMs = 60 * 1e3;
|
|
983
|
-
for (const [userId, entries] of this.rateLimitCache.entries()) {
|
|
984
|
-
const validEntries = entries.filter((entry) => now - entry.timestamp < windowMs);
|
|
985
|
-
if (validEntries.length === 0) {
|
|
986
|
-
this.rateLimitCache.delete(userId);
|
|
987
|
-
} else {
|
|
988
|
-
this.rateLimitCache.set(userId, validEntries);
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
/**
|
|
993
|
-
* Sanitize input data
|
|
994
|
-
* @param input - Input to sanitize
|
|
995
|
-
* @returns Sanitized input
|
|
996
|
-
*/
|
|
997
|
-
sanitizeInput(input) {
|
|
998
|
-
return RBACSecurityValidator.sanitizeInput(input);
|
|
999
|
-
}
|
|
1000
|
-
};
|
|
1001
|
-
|
|
1002
|
-
// src/rbac/config.ts
|
|
1003
|
-
var RBACConfigManager = class {
|
|
1004
|
-
constructor() {
|
|
1005
|
-
this.config = null;
|
|
1006
|
-
this.logger = null;
|
|
1007
|
-
}
|
|
1008
|
-
setConfig(config) {
|
|
1009
|
-
this.config = config;
|
|
1010
|
-
this.setupLogger();
|
|
1011
|
-
}
|
|
1012
|
-
getConfig() {
|
|
1013
|
-
return this.config;
|
|
1014
|
-
}
|
|
1015
|
-
getLogger() {
|
|
1016
|
-
if (!this.logger) {
|
|
1017
|
-
this.logger = this.createDefaultLogger();
|
|
1018
|
-
}
|
|
1019
|
-
return this.logger;
|
|
1020
|
-
}
|
|
1021
|
-
setupLogger() {
|
|
1022
|
-
if (!this.config) return;
|
|
1023
|
-
const { debug = false, logLevel = "warn" } = this.config;
|
|
1024
|
-
this.logger = {
|
|
1025
|
-
error: (message, ...args) => {
|
|
1026
|
-
console.error(`[RBAC ERROR] ${message}`, ...args);
|
|
1027
|
-
},
|
|
1028
|
-
warn: (message, ...args) => {
|
|
1029
|
-
if (logLevel === "warn" || logLevel === "info" || logLevel === "debug") {
|
|
1030
|
-
console.warn(`[RBAC WARN] ${message}`, ...args);
|
|
1031
|
-
}
|
|
1032
|
-
},
|
|
1033
|
-
info: (message, ...args) => {
|
|
1034
|
-
if (logLevel === "info" || logLevel === "debug") {
|
|
1035
|
-
console.info(`[RBAC INFO] ${message}`, ...args);
|
|
1036
|
-
}
|
|
1037
|
-
},
|
|
1038
|
-
debug: (message, ...args) => {
|
|
1039
|
-
if (debug && logLevel === "debug") {
|
|
1040
|
-
console.debug(`[RBAC DEBUG] ${message}`, ...args);
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
};
|
|
1044
|
-
}
|
|
1045
|
-
createDefaultLogger() {
|
|
1046
|
-
return {
|
|
1047
|
-
error: (message, ...args) => console.error(`[RBAC ERROR] ${message}`, ...args),
|
|
1048
|
-
warn: (message, ...args) => console.warn(`[RBAC WARN] ${message}`, ...args),
|
|
1049
|
-
info: (message, ...args) => console.info(`[RBAC INFO] ${message}`, ...args),
|
|
1050
|
-
debug: (message, ...args) => console.debug(`[RBAC DEBUG] ${message}`, ...args)
|
|
1051
|
-
};
|
|
1052
|
-
}
|
|
1053
|
-
isDebugMode() {
|
|
1054
|
-
return this.config?.debug ?? false;
|
|
1055
|
-
}
|
|
1056
|
-
isDevelopmentMode() {
|
|
1057
|
-
return this.config?.developmentMode ?? false;
|
|
1058
|
-
}
|
|
1059
|
-
getMockPermissions() {
|
|
1060
|
-
return this.config?.mockPermissions ?? null;
|
|
1061
|
-
}
|
|
1062
|
-
};
|
|
1063
|
-
var configManager = new RBACConfigManager();
|
|
1064
|
-
function createRBACConfig(config) {
|
|
1065
|
-
configManager.setConfig(config);
|
|
1066
|
-
return config;
|
|
1067
|
-
}
|
|
1068
|
-
function getRBACConfig() {
|
|
1069
|
-
return configManager.getConfig();
|
|
1070
|
-
}
|
|
1071
|
-
function getRBACLogger() {
|
|
1072
|
-
return configManager.getLogger();
|
|
1073
|
-
}
|
|
1074
|
-
function isDebugMode() {
|
|
1075
|
-
return configManager.isDebugMode();
|
|
1076
|
-
}
|
|
1077
|
-
function isDevelopmentMode() {
|
|
1078
|
-
return configManager.isDevelopmentMode();
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
// src/rbac/engine.ts
|
|
1082
|
-
var RBACEngine = class {
|
|
1083
|
-
constructor(supabase, securityConfig) {
|
|
1084
|
-
this.supabase = supabase;
|
|
1085
|
-
const mergedSecurityConfig = {
|
|
1086
|
-
...DEFAULT_SECURITY_CONFIG,
|
|
1087
|
-
...securityConfig
|
|
1088
|
-
};
|
|
1089
|
-
this.securityMiddleware = new RBACSecurityMiddleware(mergedSecurityConfig);
|
|
1090
|
-
initializeCacheInvalidation(supabase);
|
|
1091
|
-
}
|
|
1092
|
-
/**
|
|
1093
|
-
* Check if a user has a specific permission
|
|
1094
|
-
*
|
|
1095
|
-
* This method now delegates to the database RPC function for all the heavy lifting.
|
|
1096
|
-
*
|
|
1097
|
-
* @param input - Permission check input
|
|
1098
|
-
* @param securityContext - Security context for validation (required)
|
|
1099
|
-
* @returns Promise resolving to permission result
|
|
1100
|
-
*/
|
|
1101
|
-
async isPermitted(input, securityContext) {
|
|
1102
|
-
const startTime = Date.now();
|
|
1103
|
-
const { userId, permission, scope, pageId } = input;
|
|
1104
|
-
let cacheHit = false;
|
|
1105
|
-
let cacheSource = "rpc";
|
|
1106
|
-
try {
|
|
1107
|
-
const validation = await this.securityMiddleware.validateInput(input, securityContext);
|
|
1108
|
-
if (!validation.isValid) {
|
|
1109
|
-
RBACSecurityValidator.logSecurityEvent({
|
|
1110
|
-
type: "invalid_input",
|
|
1111
|
-
userId,
|
|
1112
|
-
details: { errors: validation.errors, input: JSON.stringify(input) }
|
|
1113
|
-
});
|
|
1114
|
-
return false;
|
|
1115
|
-
}
|
|
1116
|
-
const rateLimit = await this.securityMiddleware.checkRateLimit(securityContext);
|
|
1117
|
-
if (!rateLimit.isAllowed) {
|
|
1118
|
-
RBACSecurityValidator.logSecurityEvent({
|
|
1119
|
-
type: "rate_limit_exceeded",
|
|
1120
|
-
userId,
|
|
1121
|
-
details: { remaining: rateLimit.remaining }
|
|
1122
|
-
});
|
|
1123
|
-
return false;
|
|
1124
|
-
}
|
|
1125
|
-
if (!RBACSecurityValidator.validateUserId(userId)) {
|
|
1126
|
-
RBACSecurityValidator.logSecurityEvent({
|
|
1127
|
-
type: "invalid_input",
|
|
1128
|
-
userId,
|
|
1129
|
-
details: { error: "Invalid user ID format" }
|
|
1130
|
-
});
|
|
1131
|
-
return false;
|
|
1132
|
-
}
|
|
1133
|
-
if (!RBACSecurityValidator.validatePermission(permission)) {
|
|
1134
|
-
RBACSecurityValidator.logSecurityEvent({
|
|
1135
|
-
type: "invalid_input",
|
|
1136
|
-
userId,
|
|
1137
|
-
details: { error: "Invalid permission format", permission }
|
|
1138
|
-
});
|
|
1139
|
-
return false;
|
|
1140
|
-
}
|
|
1141
|
-
if (!RBACSecurityValidator.validateScope(scope)) {
|
|
1142
|
-
RBACSecurityValidator.logSecurityEvent({
|
|
1143
|
-
type: "invalid_input",
|
|
1144
|
-
userId,
|
|
1145
|
-
details: { error: "Invalid scope format", scope }
|
|
1146
|
-
});
|
|
1147
|
-
return false;
|
|
1148
|
-
}
|
|
1149
|
-
const cacheKey = RBACCache.generateKey(
|
|
1150
|
-
userId,
|
|
1151
|
-
permission,
|
|
1152
|
-
scope.organisationId,
|
|
1153
|
-
scope.eventId,
|
|
1154
|
-
scope.appId,
|
|
1155
|
-
pageId
|
|
1156
|
-
);
|
|
1157
|
-
const cached = rbacCache.get(cacheKey);
|
|
1158
|
-
if (cached !== null) {
|
|
1159
|
-
cacheHit = true;
|
|
1160
|
-
cacheSource = "memory";
|
|
1161
|
-
return cached;
|
|
1162
|
-
}
|
|
1163
|
-
const { data, error } = await this.supabase.rpc("rbac_check_permission_simplified", {
|
|
1164
|
-
p_user_id: userId,
|
|
1165
|
-
p_permission: permission,
|
|
1166
|
-
p_organisation_id: scope.organisationId || void 0,
|
|
1167
|
-
p_event_id: scope.eventId || void 0,
|
|
1168
|
-
p_app_id: scope.appId || void 0,
|
|
1169
|
-
p_page_id: pageId || void 0
|
|
1170
|
-
});
|
|
1171
|
-
if (error) {
|
|
1172
|
-
const logger = getRBACLogger();
|
|
1173
|
-
logger.error("RPC error:", error);
|
|
1174
|
-
const category = categorizeError(error);
|
|
1175
|
-
const eventType = mapErrorCategoryToSecurityEventType(category);
|
|
1176
|
-
const errorDetails = error;
|
|
1177
|
-
RBACSecurityValidator.logSecurityEvent({
|
|
1178
|
-
type: eventType,
|
|
1179
|
-
userId,
|
|
1180
|
-
details: {
|
|
1181
|
-
error: errorDetails?.message || "RPC call failed",
|
|
1182
|
-
code: errorDetails?.code,
|
|
1183
|
-
hint: errorDetails?.hint,
|
|
1184
|
-
details: errorDetails?.details,
|
|
1185
|
-
permission,
|
|
1186
|
-
scope: JSON.stringify(scope),
|
|
1187
|
-
category
|
|
1188
|
-
}
|
|
1189
|
-
});
|
|
1190
|
-
return false;
|
|
1191
|
-
}
|
|
1192
|
-
const hasPermission = data === true;
|
|
1193
|
-
rbacCache.set(cacheKey, hasPermission, 6e4);
|
|
1194
|
-
const duration = Date.now() - startTime;
|
|
1195
|
-
if (scope.organisationId) {
|
|
1196
|
-
const resolvedPageId = await this.resolvePageId(pageId, scope.appId);
|
|
1197
|
-
await emitAuditEvent({
|
|
1198
|
-
type: hasPermission ? "permission_check" : "permission_denied",
|
|
1199
|
-
userId,
|
|
1200
|
-
organisationId: scope.organisationId,
|
|
1201
|
-
eventId: scope.eventId,
|
|
1202
|
-
appId: scope.appId,
|
|
1203
|
-
pageId: resolvedPageId,
|
|
1204
|
-
permission,
|
|
1205
|
-
decision: hasPermission,
|
|
1206
|
-
source: "api",
|
|
1207
|
-
duration_ms: duration,
|
|
1208
|
-
cache_hit: cacheHit,
|
|
1209
|
-
cache_source: cacheSource
|
|
1210
|
-
});
|
|
1211
|
-
}
|
|
1212
|
-
return hasPermission;
|
|
1213
|
-
} catch (error) {
|
|
1214
|
-
const category = categorizeError(error);
|
|
1215
|
-
const eventType = mapErrorCategoryToSecurityEventType(category);
|
|
1216
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1217
|
-
RBACSecurityValidator.logSecurityEvent({
|
|
1218
|
-
type: eventType,
|
|
1219
|
-
userId,
|
|
1220
|
-
details: {
|
|
1221
|
-
error: errorMessage,
|
|
1222
|
-
permission,
|
|
1223
|
-
scope: JSON.stringify(scope),
|
|
1224
|
-
category
|
|
1225
|
-
}
|
|
1226
|
-
});
|
|
1227
|
-
const logger = getRBACLogger();
|
|
1228
|
-
logger.error("Permission check failed:", error);
|
|
1229
|
-
return false;
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
/**
|
|
1233
|
-
* Get user's access level in a scope
|
|
1234
|
-
*
|
|
1235
|
-
* This is derived from roles, not permissions.
|
|
1236
|
-
*
|
|
1237
|
-
* @param input - Access level input
|
|
1238
|
-
* @returns Promise resolving to access level
|
|
1239
|
-
*/
|
|
1240
|
-
async getAccessLevel(input) {
|
|
1241
|
-
const { userId, scope } = input;
|
|
1242
|
-
const cacheKey = RBACCache.generateAccessLevelKey(
|
|
1243
|
-
userId,
|
|
1244
|
-
scope.organisationId || "",
|
|
1245
|
-
scope.eventId,
|
|
1246
|
-
scope.appId
|
|
1247
|
-
);
|
|
1248
|
-
const cached = rbacCache.get(cacheKey);
|
|
1249
|
-
if (cached) {
|
|
1250
|
-
return cached;
|
|
1251
|
-
}
|
|
1252
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1253
|
-
const isSuperAdmin2 = await this.checkSuperAdmin(userId);
|
|
1254
|
-
if (isSuperAdmin2) {
|
|
1255
|
-
rbacCache.set(cacheKey, "super", 6e4);
|
|
1256
|
-
return "super";
|
|
1257
|
-
}
|
|
1258
|
-
if (scope.organisationId) {
|
|
1259
|
-
const { data: orgRoles } = await this.supabase.from("rbac_organisation_roles").select("role").eq("user_id", userId).eq("organisation_id", scope.organisationId).eq("status", "active").is("revoked_at", null).lte("valid_from", now).or(`valid_to.is.null,valid_to.gte.${now}`).limit(1);
|
|
1260
|
-
const orgRole = orgRoles?.[0];
|
|
1261
|
-
if (orgRole?.role === "org_admin") {
|
|
1262
|
-
rbacCache.set(cacheKey, "admin", 6e4);
|
|
1263
|
-
return "admin";
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
if (scope.eventId && scope.appId) {
|
|
1267
|
-
const { data: eventRole } = await this.supabase.from("rbac_event_app_roles").select("role").eq("user_id", userId).eq("event_id", scope.eventId).eq("app_id", scope.appId).eq("status", "active").lte("valid_from", now).or(`valid_to.is.null,valid_to.gte.${now}`).single();
|
|
1268
|
-
if (eventRole?.role === "event_admin") {
|
|
1269
|
-
rbacCache.set(cacheKey, "admin", 6e4);
|
|
1270
|
-
return "admin";
|
|
1271
|
-
}
|
|
1272
|
-
if (eventRole?.role === "planner") {
|
|
1273
|
-
rbacCache.set(cacheKey, "planner", 6e4);
|
|
1274
|
-
return "planner";
|
|
1275
|
-
}
|
|
1276
|
-
if (eventRole?.role === "participant") {
|
|
1277
|
-
rbacCache.set(cacheKey, "participant", 6e4);
|
|
1278
|
-
return "participant";
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
rbacCache.set(cacheKey, "viewer", 6e4);
|
|
1282
|
-
return "viewer";
|
|
1283
|
-
}
|
|
1284
|
-
/**
|
|
1285
|
-
* Get user's permission map for a scope
|
|
1286
|
-
*
|
|
1287
|
-
* This builds a map of page IDs to allowed operations.
|
|
1288
|
-
* Uses the simplified RPC for each permission check.
|
|
1289
|
-
*
|
|
1290
|
-
* @param input - Permission map input
|
|
1291
|
-
* @returns Promise resolving to permission map
|
|
1292
|
-
*/
|
|
1293
|
-
async getPermissionMap(input) {
|
|
1294
|
-
const { userId, scope } = input;
|
|
1295
|
-
const cacheKey = RBACCache.generatePermissionMapKey(
|
|
1296
|
-
userId,
|
|
1297
|
-
scope.organisationId || "",
|
|
1298
|
-
scope.eventId,
|
|
1299
|
-
scope.appId
|
|
1300
|
-
);
|
|
1301
|
-
const isSuperAdmin2 = await this.checkSuperAdmin(userId);
|
|
1302
|
-
if (isSuperAdmin2) {
|
|
1303
|
-
const wildcardMap = { "*": true };
|
|
1304
|
-
rbacCache.set(cacheKey, wildcardMap, 6e4);
|
|
1305
|
-
return wildcardMap;
|
|
1306
|
-
}
|
|
1307
|
-
if (!scope.organisationId) {
|
|
1308
|
-
return {};
|
|
1309
|
-
}
|
|
1310
|
-
const cached = rbacCache.get(cacheKey);
|
|
1311
|
-
if (cached) {
|
|
1312
|
-
return cached;
|
|
1313
|
-
}
|
|
1314
|
-
const permissionMap = {};
|
|
1315
|
-
if (scope.appId) {
|
|
1316
|
-
const { data: pages } = await this.supabase.from("rbac_app_pages").select("id, page_name").eq("app_id", scope.appId);
|
|
1317
|
-
if (pages) {
|
|
1318
|
-
if (!scope.organisationId) {
|
|
1319
|
-
rbacCache.set(cacheKey, permissionMap, 6e4);
|
|
1320
|
-
return permissionMap;
|
|
1321
|
-
}
|
|
1322
|
-
const securityContext = {
|
|
1323
|
-
userId,
|
|
1324
|
-
organisationId: scope.organisationId,
|
|
1325
|
-
// Required
|
|
1326
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
1327
|
-
};
|
|
1328
|
-
for (const page of pages) {
|
|
1329
|
-
for (const operation of ["read", "create", "update", "delete"]) {
|
|
1330
|
-
const permissionString = `${operation}:page.${page.page_name}`;
|
|
1331
|
-
const hasPermission = await this.isPermitted(
|
|
1332
|
-
{
|
|
1333
|
-
userId,
|
|
1334
|
-
scope,
|
|
1335
|
-
permission: permissionString,
|
|
1336
|
-
pageId: page.id
|
|
1337
|
-
},
|
|
1338
|
-
securityContext
|
|
1339
|
-
);
|
|
1340
|
-
const permissionKey = permissionString;
|
|
1341
|
-
permissionMap[permissionKey] = hasPermission;
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
rbacCache.set(cacheKey, permissionMap, 6e4);
|
|
1347
|
-
return permissionMap;
|
|
1348
|
-
}
|
|
1349
|
-
async resolveAppContext(input) {
|
|
1350
|
-
try {
|
|
1351
|
-
const { userId, appName } = input;
|
|
1352
|
-
const { data, error } = await this.supabase.rpc("data_app_resolve", {
|
|
1353
|
-
p_user_id: userId,
|
|
1354
|
-
p_app_name: appName
|
|
1355
|
-
});
|
|
1356
|
-
if (error) {
|
|
1357
|
-
const logger = getRBACLogger();
|
|
1358
|
-
logger.error("Failed to resolve app context:", error);
|
|
1359
|
-
return null;
|
|
1360
|
-
}
|
|
1361
|
-
if (!data || data.length === 0) {
|
|
1362
|
-
return null;
|
|
1363
|
-
}
|
|
1364
|
-
const appData = data[0];
|
|
1365
|
-
if (!appData?.app_id) {
|
|
1366
|
-
return null;
|
|
1367
|
-
}
|
|
1368
|
-
return {
|
|
1369
|
-
appId: appData.app_id,
|
|
1370
|
-
hasAccess: appData.has_access !== false
|
|
1371
|
-
};
|
|
1372
|
-
} catch (error) {
|
|
1373
|
-
const logger = getRBACLogger();
|
|
1374
|
-
logger.error("Unexpected error resolving app context:", error);
|
|
1375
|
-
return null;
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
async getRoleContext(input) {
|
|
1379
|
-
const result = {
|
|
1380
|
-
globalRole: null,
|
|
1381
|
-
organisationRole: null,
|
|
1382
|
-
eventAppRole: null
|
|
1383
|
-
};
|
|
1384
|
-
try {
|
|
1385
|
-
const { userId, scope } = input;
|
|
1386
|
-
const { data, error } = await this.supabase.rpc("rbac_permissions_get", {
|
|
1387
|
-
p_user_id: userId,
|
|
1388
|
-
p_organisation_id: scope.organisationId || null,
|
|
1389
|
-
p_event_id: scope.eventId || null,
|
|
1390
|
-
p_app_id: scope.appId || null,
|
|
1391
|
-
p_page_id: null
|
|
1392
|
-
// Optional: can filter to specific page if needed
|
|
1393
|
-
});
|
|
1394
|
-
if (error) {
|
|
1395
|
-
const logger = getRBACLogger();
|
|
1396
|
-
logger.error("Failed to load role context:", error);
|
|
1397
|
-
return result;
|
|
1398
|
-
}
|
|
1399
|
-
if (!Array.isArray(data)) {
|
|
1400
|
-
return result;
|
|
1401
|
-
}
|
|
1402
|
-
for (const permission of data) {
|
|
1403
|
-
if (permission.permission_type === "all_permissions") {
|
|
1404
|
-
result.globalRole = "super_admin";
|
|
1405
|
-
}
|
|
1406
|
-
if (permission.permission_type === "organisation_access") {
|
|
1407
|
-
result.organisationRole = permission.role_name;
|
|
1408
|
-
}
|
|
1409
|
-
if (permission.permission_type === "event_app_access") {
|
|
1410
|
-
result.eventAppRole = permission.role_name;
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
|
-
return result;
|
|
1414
|
-
} catch (error) {
|
|
1415
|
-
const logger = getRBACLogger();
|
|
1416
|
-
logger.error("Unexpected error loading role context:", error);
|
|
1417
|
-
return result;
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1420
|
-
/**
|
|
1421
|
-
* Check if user is super admin
|
|
1422
|
-
*
|
|
1423
|
-
* @param userId - User ID
|
|
1424
|
-
* @returns Promise resolving to super admin status
|
|
1425
|
-
*/
|
|
1426
|
-
async checkSuperAdmin(userId) {
|
|
1427
|
-
const cacheKey = `super_admin:${userId}`;
|
|
1428
|
-
const cached = rbacCache.get(cacheKey);
|
|
1429
|
-
if (cached !== null) {
|
|
1430
|
-
return cached;
|
|
1431
|
-
}
|
|
1432
|
-
const startTime = Date.now();
|
|
1433
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1434
|
-
try {
|
|
1435
|
-
const { data, error } = await this.supabase.from("rbac_global_roles").select("role").eq("user_id", userId).eq("role", "super_admin").lte("valid_from", now).or(`valid_to.is.null,valid_to.gte.${now}`).limit(1);
|
|
1436
|
-
const elapsed = Date.now() - startTime;
|
|
1437
|
-
if (elapsed > 2e3) {
|
|
1438
|
-
console.warn("[RBACEngine] Super admin check took longer than expected", {
|
|
1439
|
-
userId,
|
|
1440
|
-
elapsedMs: elapsed,
|
|
1441
|
-
error: error?.message
|
|
1442
|
-
});
|
|
1443
|
-
}
|
|
1444
|
-
const isSuperAdmin2 = !error && data && data.length > 0;
|
|
1445
|
-
rbacCache.set(cacheKey, isSuperAdmin2, 6e4);
|
|
1446
|
-
return Boolean(isSuperAdmin2);
|
|
1447
|
-
} catch (err) {
|
|
1448
|
-
const elapsed = Date.now() - startTime;
|
|
1449
|
-
console.error("[RBACEngine] Error checking super admin", {
|
|
1450
|
-
userId,
|
|
1451
|
-
error: err,
|
|
1452
|
-
elapsedMs: elapsed
|
|
1453
|
-
});
|
|
1454
|
-
return false;
|
|
1455
|
-
}
|
|
1456
|
-
}
|
|
1457
|
-
/**
|
|
1458
|
-
* Resolve a page ID to UUID if it's a page name
|
|
1459
|
-
*
|
|
1460
|
-
* @param pageId - Page ID (UUID) or page name (string)
|
|
1461
|
-
* @param appId - App ID to look up the page
|
|
1462
|
-
* @returns Resolved page ID (UUID) or original pageId
|
|
1463
|
-
*/
|
|
1464
|
-
async resolvePageId(pageId, appId) {
|
|
1465
|
-
if (!pageId) {
|
|
1466
|
-
return void 0;
|
|
1467
|
-
}
|
|
1468
|
-
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1469
|
-
if (uuidRegex.test(pageId)) {
|
|
1470
|
-
return pageId;
|
|
1471
|
-
}
|
|
1472
|
-
if (!appId) {
|
|
1473
|
-
return pageId;
|
|
1474
|
-
}
|
|
1475
|
-
try {
|
|
1476
|
-
const { data: page, error: pageError } = await this.supabase.from("rbac_app_pages").select("id").eq("app_id", appId).eq("page_name", pageId).maybeSingle();
|
|
1477
|
-
if (pageError) {
|
|
1478
|
-
const logger = getRBACLogger();
|
|
1479
|
-
if (pageError.code !== "PGRST116") {
|
|
1480
|
-
logger.warn("Failed to resolve page name to UUID:", { pageId, appId, error: pageError });
|
|
1481
|
-
}
|
|
1482
|
-
return pageId;
|
|
1483
|
-
}
|
|
1484
|
-
return page?.id || pageId;
|
|
1485
|
-
} catch (error) {
|
|
1486
|
-
const logger = getRBACLogger();
|
|
1487
|
-
logger.warn("Failed to resolve page name to UUID:", { pageId, appId, error });
|
|
1488
|
-
return pageId;
|
|
1489
|
-
}
|
|
1490
|
-
}
|
|
1491
|
-
};
|
|
1492
|
-
function createRBACEngine(supabase, securityConfig) {
|
|
1493
|
-
return new RBACEngine(supabase, securityConfig);
|
|
1494
|
-
}
|
|
1495
|
-
|
|
1496
|
-
// src/rbac/performance.ts
|
|
1497
|
-
var RBACPerformanceMonitor = class {
|
|
1498
|
-
constructor() {
|
|
1499
|
-
this.metrics = {
|
|
1500
|
-
totalChecks: 0,
|
|
1501
|
-
cacheHits: 0,
|
|
1502
|
-
cacheMisses: 0,
|
|
1503
|
-
cacheHitRate: 0,
|
|
1504
|
-
deduplicatedRequests: 0,
|
|
1505
|
-
networkRequests: 0,
|
|
1506
|
-
averageResponseTime: 0,
|
|
1507
|
-
totalResponseTime: 0,
|
|
1508
|
-
batchedAuditEvents: 0,
|
|
1509
|
-
individualAuditEvents: 0
|
|
1510
|
-
};
|
|
1511
|
-
this.enabled = false;
|
|
1512
|
-
}
|
|
1513
|
-
/**
|
|
1514
|
-
* Enable or disable performance monitoring
|
|
1515
|
-
*/
|
|
1516
|
-
setEnabled(enabled) {
|
|
1517
|
-
this.enabled = enabled;
|
|
1518
|
-
}
|
|
1519
|
-
/**
|
|
1520
|
-
* Check if performance monitoring is enabled
|
|
1521
|
-
*/
|
|
1522
|
-
isEnabled() {
|
|
1523
|
-
return this.enabled;
|
|
1524
|
-
}
|
|
1525
|
-
/**
|
|
1526
|
-
* Record a permission check
|
|
1527
|
-
*/
|
|
1528
|
-
recordCheck(cacheHit, responseTime, wasDeduplicated = false) {
|
|
1529
|
-
if (!this.enabled) {
|
|
1530
|
-
return;
|
|
1531
|
-
}
|
|
1532
|
-
this.metrics.totalChecks++;
|
|
1533
|
-
if (cacheHit) {
|
|
1534
|
-
this.metrics.cacheHits++;
|
|
1535
|
-
} else {
|
|
1536
|
-
this.metrics.cacheMisses++;
|
|
1537
|
-
this.metrics.networkRequests++;
|
|
1538
|
-
}
|
|
1539
|
-
if (wasDeduplicated) {
|
|
1540
|
-
this.metrics.deduplicatedRequests++;
|
|
1541
|
-
}
|
|
1542
|
-
this.metrics.totalResponseTime += responseTime;
|
|
1543
|
-
this.metrics.averageResponseTime = this.metrics.totalResponseTime / this.metrics.totalChecks;
|
|
1544
|
-
this.metrics.cacheHitRate = this.metrics.cacheHits / this.metrics.totalChecks;
|
|
1545
|
-
}
|
|
1546
|
-
/**
|
|
1547
|
-
* Record an audit event
|
|
1548
|
-
*/
|
|
1549
|
-
recordAuditEvent(batched) {
|
|
1550
|
-
if (!this.enabled) {
|
|
1551
|
-
return;
|
|
1552
|
-
}
|
|
1553
|
-
if (batched) {
|
|
1554
|
-
this.metrics.batchedAuditEvents++;
|
|
1555
|
-
} else {
|
|
1556
|
-
this.metrics.individualAuditEvents++;
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
/**
|
|
1560
|
-
* Get current metrics
|
|
1561
|
-
*/
|
|
1562
|
-
getMetrics() {
|
|
1563
|
-
return { ...this.metrics };
|
|
1564
|
-
}
|
|
1565
|
-
/**
|
|
1566
|
-
* Reset all metrics
|
|
1567
|
-
*/
|
|
1568
|
-
reset() {
|
|
1569
|
-
this.metrics = {
|
|
1570
|
-
totalChecks: 0,
|
|
1571
|
-
cacheHits: 0,
|
|
1572
|
-
cacheMisses: 0,
|
|
1573
|
-
cacheHitRate: 0,
|
|
1574
|
-
deduplicatedRequests: 0,
|
|
1575
|
-
networkRequests: 0,
|
|
1576
|
-
averageResponseTime: 0,
|
|
1577
|
-
totalResponseTime: 0,
|
|
1578
|
-
batchedAuditEvents: 0,
|
|
1579
|
-
individualAuditEvents: 0
|
|
1580
|
-
};
|
|
1581
|
-
}
|
|
1582
|
-
/**
|
|
1583
|
-
* Get metrics summary as a formatted string
|
|
1584
|
-
*/
|
|
1585
|
-
getSummary() {
|
|
1586
|
-
const m = this.metrics;
|
|
1587
|
-
return `
|
|
1588
|
-
RBAC Performance Metrics:
|
|
1589
|
-
Total Checks: ${m.totalChecks}
|
|
1590
|
-
Cache Hits: ${m.cacheHits} (${(m.cacheHitRate * 100).toFixed(1)}%)
|
|
1591
|
-
Cache Misses: ${m.cacheMisses}
|
|
1592
|
-
Deduplicated Requests: ${m.deduplicatedRequests}
|
|
1593
|
-
Network Requests: ${m.networkRequests}
|
|
1594
|
-
Average Response Time: ${m.averageResponseTime.toFixed(2)}ms
|
|
1595
|
-
Batched Audit Events: ${m.batchedAuditEvents}
|
|
1596
|
-
Individual Audit Events: ${m.individualAuditEvents}
|
|
1597
|
-
`;
|
|
1598
|
-
}
|
|
1599
|
-
};
|
|
1600
|
-
var performanceMonitor = new RBACPerformanceMonitor();
|
|
1601
|
-
function enablePerformanceMonitoring() {
|
|
1602
|
-
performanceMonitor.setEnabled(true);
|
|
1603
|
-
}
|
|
1604
|
-
function disablePerformanceMonitoring() {
|
|
1605
|
-
performanceMonitor.setEnabled(false);
|
|
1606
|
-
}
|
|
1607
|
-
function isPerformanceMonitoringEnabled() {
|
|
1608
|
-
return performanceMonitor.isEnabled();
|
|
1609
|
-
}
|
|
1610
|
-
function recordPermissionCheck(cacheHit, responseTime, wasDeduplicated = false) {
|
|
1611
|
-
performanceMonitor.recordCheck(cacheHit, responseTime, wasDeduplicated);
|
|
1612
|
-
}
|
|
1613
|
-
function recordAuditEvent(batched) {
|
|
1614
|
-
performanceMonitor.recordAuditEvent(batched);
|
|
1615
|
-
}
|
|
1616
|
-
function getPerformanceMetrics() {
|
|
1617
|
-
return performanceMonitor.getMetrics();
|
|
1618
|
-
}
|
|
1619
|
-
function resetPerformanceMetrics() {
|
|
1620
|
-
performanceMonitor.reset();
|
|
1621
|
-
}
|
|
1622
|
-
function getPerformanceSummary() {
|
|
1623
|
-
return performanceMonitor.getSummary();
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1626
|
-
// src/rbac/request-deduplication.ts
|
|
1627
|
-
var inFlightRequests = /* @__PURE__ */ new Map();
|
|
1628
|
-
function generateDeduplicationKey(input) {
|
|
1629
|
-
return RBACCache.generatePermissionKey({
|
|
1630
|
-
userId: input.userId,
|
|
1631
|
-
organisationId: input.scope.organisationId,
|
|
1632
|
-
// Can be undefined for page-level permissions
|
|
1633
|
-
eventId: input.scope.eventId,
|
|
1634
|
-
appId: input.scope.appId,
|
|
1635
|
-
permission: input.permission,
|
|
1636
|
-
pageId: input.pageId
|
|
1637
|
-
});
|
|
1638
|
-
}
|
|
1639
|
-
async function getOrCreateRequest(input, checkFn) {
|
|
1640
|
-
const key = generateDeduplicationKey(input);
|
|
1641
|
-
const existingRequest = inFlightRequests.get(key);
|
|
1642
|
-
if (existingRequest) {
|
|
1643
|
-
return existingRequest;
|
|
1644
|
-
}
|
|
1645
|
-
const requestPromise = checkFn(input).finally(() => {
|
|
1646
|
-
inFlightRequests.delete(key);
|
|
1647
|
-
});
|
|
1648
|
-
inFlightRequests.set(key, requestPromise);
|
|
1649
|
-
return requestPromise;
|
|
1650
|
-
}
|
|
1651
|
-
function clearInFlightRequests() {
|
|
1652
|
-
inFlightRequests.clear();
|
|
1653
|
-
}
|
|
1654
|
-
function getInFlightRequestCount() {
|
|
1655
|
-
return inFlightRequests.size;
|
|
1656
|
-
}
|
|
1657
|
-
|
|
1658
|
-
// src/rbac/utils/eventContext.ts
|
|
1659
|
-
var orgDerivationCache = /* @__PURE__ */ new Map();
|
|
1660
|
-
var MAX_CACHE_SIZE = 100;
|
|
1661
|
-
async function getOrganisationFromEvent(supabase, eventId) {
|
|
1662
|
-
if (orgDerivationCache.has(eventId)) {
|
|
1663
|
-
return orgDerivationCache.get(eventId) ?? null;
|
|
1664
|
-
}
|
|
1665
|
-
const { data, error } = await supabase.from("core_events").select("organisation_id").eq("event_id", eventId).single();
|
|
1666
|
-
let organisationId = null;
|
|
1667
|
-
if (error || !data) {
|
|
1668
|
-
organisationId = null;
|
|
1669
|
-
} else if (data.organisation_id) {
|
|
1670
|
-
organisationId = data.organisation_id;
|
|
1671
|
-
} else {
|
|
1672
|
-
organisationId = null;
|
|
1673
|
-
}
|
|
1674
|
-
if (orgDerivationCache.size >= MAX_CACHE_SIZE) {
|
|
1675
|
-
const firstKey = orgDerivationCache.keys().next().value;
|
|
1676
|
-
if (firstKey) {
|
|
1677
|
-
orgDerivationCache.delete(firstKey);
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
orgDerivationCache.set(eventId, organisationId);
|
|
1681
|
-
return organisationId;
|
|
1682
|
-
}
|
|
1683
|
-
|
|
1684
|
-
// src/rbac/utils/contextValidator.ts
|
|
1685
|
-
var log3 = createLogger("ContextValidator");
|
|
1686
|
-
function allowsOptionalContexts(appName) {
|
|
1687
|
-
return appName === "PORTAL" || appName === "ADMIN";
|
|
1688
|
-
}
|
|
1689
|
-
var ContextValidator = class {
|
|
1690
|
-
/**
|
|
1691
|
-
* Derive organisation ID from event ID
|
|
1692
|
-
*
|
|
1693
|
-
* @param supabase - Supabase client
|
|
1694
|
-
* @param eventId - Event ID
|
|
1695
|
-
* @returns Organisation ID or null
|
|
1696
|
-
*/
|
|
1697
|
-
static async deriveOrgFromEvent(supabase, eventId) {
|
|
1698
|
-
return getOrganisationFromEvent(supabase, eventId);
|
|
1699
|
-
}
|
|
1700
|
-
/**
|
|
1701
|
-
* Resolve scope based on page-level scope_type
|
|
1702
|
-
*
|
|
1703
|
-
* This method handles page-level scoping. All pages have explicit scope_type set.
|
|
1704
|
-
* Used for hybrid apps that have both event and organisation pages.
|
|
1705
|
-
*
|
|
1706
|
-
* @param scope - Current scope
|
|
1707
|
-
* @param pageScopeType - Page scope type ('event', 'organisation', or 'both')
|
|
1708
|
-
* @param appName - App name (for PORTAL/ADMIN special case)
|
|
1709
|
-
* @param supabase - Supabase client (for deriving org from event, only if not already provided)
|
|
1710
|
-
* @param immediateOrganisationId - Optional immediate organisation ID (from selectedEvent.organisation_id) - avoids querying
|
|
1711
|
-
* @returns Resolved scope with all required context
|
|
1712
|
-
*/
|
|
1713
|
-
static async resolveScopeForPage(scope, pageScopeType, appName, supabase, immediateOrganisationId) {
|
|
1714
|
-
const effectiveScopeType = pageScopeType;
|
|
1715
|
-
if (effectiveScopeType === "both") {
|
|
1716
|
-
if (!scope.organisationId && !scope.eventId) {
|
|
1717
|
-
if (allowsOptionalContexts(appName)) {
|
|
1718
|
-
return {
|
|
1719
|
-
isValid: true,
|
|
1720
|
-
resolvedScope: {
|
|
1721
|
-
organisationId: void 0,
|
|
1722
|
-
eventId: void 0,
|
|
1723
|
-
appId: scope.appId
|
|
1724
|
-
},
|
|
1725
|
-
error: null
|
|
1726
|
-
};
|
|
1727
|
-
}
|
|
1728
|
-
return {
|
|
1729
|
-
isValid: false,
|
|
1730
|
-
resolvedScope: null,
|
|
1731
|
-
error: new Error("Page requires either organisation or event context")
|
|
1732
|
-
};
|
|
1733
|
-
}
|
|
1734
|
-
let organisationId = scope.organisationId || immediateOrganisationId || void 0;
|
|
1735
|
-
if (!organisationId && scope.eventId && supabase) {
|
|
1736
|
-
try {
|
|
1737
|
-
const derivedOrgId = await this.deriveOrgFromEvent(supabase, scope.eventId);
|
|
1738
|
-
organisationId = derivedOrgId || void 0;
|
|
1739
|
-
} catch (error) {
|
|
1740
|
-
log3.warn("Failed to derive org from event for both-scope page:", error);
|
|
1741
|
-
}
|
|
1742
|
-
}
|
|
1743
|
-
return {
|
|
1744
|
-
isValid: true,
|
|
1745
|
-
resolvedScope: {
|
|
1746
|
-
organisationId,
|
|
1747
|
-
eventId: scope.eventId,
|
|
1748
|
-
appId: scope.appId
|
|
1749
|
-
},
|
|
1750
|
-
error: null
|
|
1751
|
-
};
|
|
1752
|
-
}
|
|
1753
|
-
if (effectiveScopeType === "event") {
|
|
1754
|
-
if (!scope.eventId) {
|
|
1755
|
-
if (allowsOptionalContexts(appName)) {
|
|
1756
|
-
return {
|
|
1757
|
-
isValid: true,
|
|
1758
|
-
resolvedScope: {
|
|
1759
|
-
organisationId: scope.organisationId,
|
|
1760
|
-
eventId: void 0,
|
|
1761
|
-
appId: scope.appId
|
|
1762
|
-
},
|
|
1763
|
-
error: null
|
|
1764
|
-
};
|
|
1765
|
-
}
|
|
1766
|
-
return {
|
|
1767
|
-
isValid: false,
|
|
1768
|
-
resolvedScope: null,
|
|
1769
|
-
error: new EventContextRequiredError()
|
|
1770
|
-
};
|
|
1771
|
-
}
|
|
1772
|
-
let organisationId = scope.organisationId || immediateOrganisationId || void 0;
|
|
1773
|
-
if (!organisationId && supabase && scope.eventId) {
|
|
1774
|
-
try {
|
|
1775
|
-
const derivedOrgId = await this.deriveOrgFromEvent(supabase, scope.eventId);
|
|
1776
|
-
organisationId = derivedOrgId || void 0;
|
|
1777
|
-
if (!organisationId) {
|
|
1778
|
-
return {
|
|
1779
|
-
isValid: false,
|
|
1780
|
-
resolvedScope: null,
|
|
1781
|
-
error: new Error("Could not resolve organisation from event context")
|
|
1782
|
-
};
|
|
1783
|
-
}
|
|
1784
|
-
} catch (error) {
|
|
1785
|
-
log3.error("Failed to derive org from event:", error);
|
|
1786
|
-
return {
|
|
1787
|
-
isValid: false,
|
|
1788
|
-
resolvedScope: null,
|
|
1789
|
-
error: error instanceof Error ? error : new Error("Failed to derive organisation from event")
|
|
1790
|
-
};
|
|
1791
|
-
}
|
|
1792
|
-
}
|
|
1793
|
-
return {
|
|
1794
|
-
isValid: true,
|
|
1795
|
-
resolvedScope: {
|
|
1796
|
-
organisationId,
|
|
1797
|
-
eventId: scope.eventId,
|
|
1798
|
-
appId: scope.appId
|
|
1799
|
-
},
|
|
1800
|
-
error: null
|
|
1801
|
-
};
|
|
1802
|
-
}
|
|
1803
|
-
if (effectiveScopeType === "organisation") {
|
|
1804
|
-
if (!scope.organisationId) {
|
|
1805
|
-
if (allowsOptionalContexts(appName)) {
|
|
1806
|
-
return {
|
|
1807
|
-
isValid: true,
|
|
1808
|
-
resolvedScope: {
|
|
1809
|
-
organisationId: void 0,
|
|
1810
|
-
eventId: scope.eventId,
|
|
1811
|
-
appId: scope.appId
|
|
1812
|
-
},
|
|
1813
|
-
error: null
|
|
1814
|
-
};
|
|
1815
|
-
}
|
|
1816
|
-
return {
|
|
1817
|
-
isValid: false,
|
|
1818
|
-
resolvedScope: null,
|
|
1819
|
-
error: new OrganisationContextRequiredError()
|
|
1820
|
-
};
|
|
1821
|
-
}
|
|
1822
|
-
return {
|
|
1823
|
-
isValid: true,
|
|
1824
|
-
resolvedScope: {
|
|
1825
|
-
organisationId: scope.organisationId,
|
|
1826
|
-
eventId: scope.eventId,
|
|
1827
|
-
// Event is optional for org-scoped pages
|
|
1828
|
-
appId: scope.appId
|
|
1829
|
-
},
|
|
1830
|
-
error: null
|
|
1831
|
-
};
|
|
1832
|
-
}
|
|
1833
|
-
return {
|
|
1834
|
-
isValid: false,
|
|
1835
|
-
resolvedScope: null,
|
|
1836
|
-
error: new Error("Invalid scope type")
|
|
1837
|
-
};
|
|
1838
|
-
}
|
|
1839
|
-
};
|
|
1840
|
-
|
|
1841
|
-
// src/rbac/api.ts
|
|
1842
|
-
var log4 = createLogger("RBACAPI");
|
|
1843
|
-
var globalEngine = null;
|
|
1844
|
-
function setupRBAC(supabase, config) {
|
|
1845
|
-
getRBACLogger();
|
|
1846
|
-
const isDevelopment = import.meta.env.MODE === "development";
|
|
1847
|
-
const fullConfig = {
|
|
1848
|
-
supabase,
|
|
1849
|
-
debug: isDevelopment,
|
|
1850
|
-
logLevel: "warn",
|
|
1851
|
-
developmentMode: isDevelopment,
|
|
1852
|
-
...config
|
|
1853
|
-
};
|
|
1854
|
-
createRBACConfig(fullConfig);
|
|
1855
|
-
const securityConfig = config === void 0 && !isDevelopment ? void 0 : {
|
|
1856
|
-
// Default: disable rate limiting in development
|
|
1857
|
-
...isDevelopment && config?.security?.enableRateLimiting === void 0 ? { enableRateLimiting: false } : {},
|
|
1858
|
-
// Explicit config overrides defaults
|
|
1859
|
-
...config?.security
|
|
1860
|
-
};
|
|
1861
|
-
globalEngine = createRBACEngine(supabase, securityConfig);
|
|
1862
|
-
const useBatchedAudit = config?.audit?.batched !== false && config?.performance?.enableBatchedAuditLogging !== false;
|
|
1863
|
-
const batchConfig = useBatchedAudit ? {
|
|
1864
|
-
batchWindow: config?.audit?.batchWindow,
|
|
1865
|
-
batchSize: config?.audit?.batchSize
|
|
1866
|
-
} : void 0;
|
|
1867
|
-
const auditManager = createAuditManager(supabase, useBatchedAudit, batchConfig);
|
|
1868
|
-
setGlobalAuditManager(auditManager);
|
|
1869
|
-
if (config?.performance?.enablePerformanceTracking) {
|
|
1870
|
-
enablePerformanceMonitoring();
|
|
1871
|
-
}
|
|
1872
|
-
}
|
|
1873
|
-
function isRBACInitialized() {
|
|
1874
|
-
return globalEngine !== null;
|
|
1875
|
-
}
|
|
1876
|
-
function getEngine() {
|
|
1877
|
-
if (!globalEngine) {
|
|
1878
|
-
throw new RBACNotInitializedError();
|
|
1879
|
-
}
|
|
1880
|
-
return globalEngine;
|
|
1881
|
-
}
|
|
1882
|
-
async function getAccessLevel(input, appName) {
|
|
1883
|
-
try {
|
|
1884
|
-
const engine = getEngine();
|
|
1885
|
-
const isSuperAdminUser = await engine["checkSuperAdmin"](input.userId);
|
|
1886
|
-
if (isSuperAdminUser) {
|
|
1887
|
-
return "super";
|
|
1888
|
-
}
|
|
1889
|
-
const validation = await ContextValidator.resolveScopeForPage(
|
|
1890
|
-
input.scope,
|
|
1891
|
-
"organisation",
|
|
1892
|
-
// Default to organisation scope when no page context
|
|
1893
|
-
appName,
|
|
1894
|
-
engine["supabase"]
|
|
1895
|
-
);
|
|
1896
|
-
if (!validation.isValid || !validation.resolvedScope) {
|
|
1897
|
-
throw validation.error || new OrganisationContextRequiredError();
|
|
1898
|
-
}
|
|
1899
|
-
return engine.getAccessLevel({
|
|
1900
|
-
...input,
|
|
1901
|
-
scope: validation.resolvedScope
|
|
1902
|
-
});
|
|
1903
|
-
} catch (error) {
|
|
1904
|
-
throw error;
|
|
1905
|
-
}
|
|
1906
|
-
}
|
|
1907
|
-
async function getPermissionMap(input, appName) {
|
|
1908
|
-
try {
|
|
1909
|
-
const engine = getEngine();
|
|
1910
|
-
const validation = await ContextValidator.resolveScopeForPage(
|
|
1911
|
-
input.scope,
|
|
1912
|
-
"organisation",
|
|
1913
|
-
// Default to organisation scope when no page context
|
|
1914
|
-
appName,
|
|
1915
|
-
engine["supabase"]
|
|
1916
|
-
);
|
|
1917
|
-
if (!validation.isValid || !validation.resolvedScope) {
|
|
1918
|
-
throw validation.error || new OrganisationContextRequiredError();
|
|
1919
|
-
}
|
|
1920
|
-
return engine.getPermissionMap({
|
|
1921
|
-
...input,
|
|
1922
|
-
scope: validation.resolvedScope
|
|
1923
|
-
});
|
|
1924
|
-
} catch (error) {
|
|
1925
|
-
throw error;
|
|
1926
|
-
}
|
|
1927
|
-
}
|
|
1928
|
-
async function resolveAppContext(input) {
|
|
1929
|
-
try {
|
|
1930
|
-
const engine = getEngine();
|
|
1931
|
-
return await engine.resolveAppContext(input);
|
|
1932
|
-
} catch (error) {
|
|
1933
|
-
throw error;
|
|
1934
|
-
}
|
|
1935
|
-
}
|
|
1936
|
-
async function getRoleContext(input, appName) {
|
|
1937
|
-
const engine = getEngine();
|
|
1938
|
-
const validation = await ContextValidator.resolveScopeForPage(
|
|
1939
|
-
input.scope,
|
|
1940
|
-
"organisation",
|
|
1941
|
-
// Default to organisation scope when no page context
|
|
1942
|
-
appName,
|
|
1943
|
-
engine["supabase"]
|
|
1944
|
-
);
|
|
1945
|
-
if (!validation.isValid || !validation.resolvedScope) {
|
|
1946
|
-
throw validation.error || new OrganisationContextRequiredError();
|
|
1947
|
-
}
|
|
1948
|
-
return engine.getRoleContext({
|
|
1949
|
-
...input,
|
|
1950
|
-
scope: validation.resolvedScope
|
|
1951
|
-
});
|
|
1952
|
-
}
|
|
1953
|
-
async function isPermitted(input, appName, precomputedSuperAdmin = null) {
|
|
1954
|
-
const engine = getEngine();
|
|
1955
|
-
if (precomputedSuperAdmin === true) {
|
|
1956
|
-
return true;
|
|
1957
|
-
}
|
|
1958
|
-
if (precomputedSuperAdmin === null) {
|
|
1959
|
-
const isSuperAdminUser = await engine["checkSuperAdmin"](input.userId);
|
|
1960
|
-
if (isSuperAdminUser) {
|
|
1961
|
-
return true;
|
|
1962
|
-
}
|
|
1963
|
-
}
|
|
1964
|
-
let resolvedAppName = appName;
|
|
1965
|
-
if (!resolvedAppName && input.scope.appId) {
|
|
1966
|
-
try {
|
|
1967
|
-
const { data } = await engine["supabase"].from("rbac_apps").select("name").eq("id", input.scope.appId).eq("is_active", true).single();
|
|
1968
|
-
if (data) {
|
|
1969
|
-
resolvedAppName = data.name;
|
|
1970
|
-
}
|
|
1971
|
-
} catch (err) {
|
|
1972
|
-
}
|
|
1973
|
-
}
|
|
1974
|
-
let pageScopeType;
|
|
1975
|
-
if (input.pageId) {
|
|
1976
|
-
try {
|
|
1977
|
-
const scopeType = await getPageScopeType(
|
|
1978
|
-
input.pageId,
|
|
1979
|
-
input.scope.appId,
|
|
1980
|
-
resolvedAppName
|
|
1981
|
-
);
|
|
1982
|
-
if (!scopeType) {
|
|
1983
|
-
throw new Error(`Page ${input.pageId} does not have scope_type set`);
|
|
1984
|
-
}
|
|
1985
|
-
pageScopeType = scopeType;
|
|
1986
|
-
} catch (err) {
|
|
1987
|
-
log4.error("Failed to get page scope type:", err);
|
|
1988
|
-
throw new Error(`Failed to determine page scope type: ${err instanceof Error ? err.message : String(err)}`);
|
|
1989
|
-
}
|
|
1990
|
-
} else {
|
|
1991
|
-
pageScopeType = "organisation";
|
|
1992
|
-
}
|
|
1993
|
-
const validation = await ContextValidator.resolveScopeForPage(
|
|
1994
|
-
input.scope,
|
|
1995
|
-
pageScopeType,
|
|
1996
|
-
resolvedAppName,
|
|
1997
|
-
engine["supabase"]
|
|
1998
|
-
);
|
|
1999
|
-
if (!validation.isValid || !validation.resolvedScope) {
|
|
2000
|
-
throw validation.error || new OrganisationContextRequiredError();
|
|
2001
|
-
}
|
|
2002
|
-
const validatedScope = validation.resolvedScope;
|
|
2003
|
-
if (pageScopeType === "both" && input.pageId) {
|
|
2004
|
-
const eventScope = {
|
|
2005
|
-
organisationId: validatedScope.organisationId,
|
|
2006
|
-
// Org derived from event
|
|
2007
|
-
eventId: validatedScope.eventId,
|
|
2008
|
-
appId: validatedScope.appId
|
|
2009
|
-
};
|
|
2010
|
-
const eventSecurityContext = {
|
|
2011
|
-
userId: input.userId,
|
|
2012
|
-
organisationId: eventScope.organisationId || null,
|
|
2013
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
2014
|
-
};
|
|
2015
|
-
const eventInput = {
|
|
2016
|
-
...input,
|
|
2017
|
-
scope: eventScope
|
|
2018
|
-
};
|
|
2019
|
-
const hasEventPermission = await engine.isPermitted(eventInput, eventSecurityContext);
|
|
2020
|
-
if (validatedScope.organisationId && validatedScope.eventId) {
|
|
2021
|
-
const orgScope = {
|
|
2022
|
-
organisationId: validatedScope.organisationId,
|
|
2023
|
-
eventId: void 0,
|
|
2024
|
-
// Clear event for org-only check
|
|
2025
|
-
appId: validatedScope.appId
|
|
2026
|
-
};
|
|
2027
|
-
const orgSecurityContext = {
|
|
2028
|
-
userId: input.userId,
|
|
2029
|
-
organisationId: orgScope.organisationId || null,
|
|
2030
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
2031
|
-
};
|
|
2032
|
-
const orgInput = {
|
|
2033
|
-
...input,
|
|
2034
|
-
scope: orgScope
|
|
2035
|
-
};
|
|
2036
|
-
const hasOrgPermission = await engine.isPermitted(orgInput, orgSecurityContext);
|
|
2037
|
-
return hasEventPermission || hasOrgPermission;
|
|
2038
|
-
}
|
|
2039
|
-
return hasEventPermission;
|
|
2040
|
-
}
|
|
2041
|
-
const securityContext = {
|
|
2042
|
-
userId: input.userId,
|
|
2043
|
-
organisationId: validatedScope.organisationId || null,
|
|
2044
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
2045
|
-
// Optional fields can be omitted
|
|
2046
|
-
};
|
|
2047
|
-
const validatedInput = {
|
|
2048
|
-
...input,
|
|
2049
|
-
scope: validatedScope
|
|
2050
|
-
};
|
|
2051
|
-
return engine.isPermitted(validatedInput, securityContext);
|
|
2052
|
-
}
|
|
2053
|
-
async function isPermittedCached(input, appName) {
|
|
2054
|
-
const { userId, scope, permission, pageId } = input;
|
|
2055
|
-
const cacheKey = RBACCache.generatePermissionKey({
|
|
2056
|
-
userId,
|
|
2057
|
-
organisationId: scope.organisationId,
|
|
2058
|
-
eventId: scope.eventId,
|
|
2059
|
-
appId: scope.appId,
|
|
2060
|
-
permission,
|
|
2061
|
-
pageId
|
|
2062
|
-
});
|
|
2063
|
-
const cached = rbacCache.get(cacheKey, true);
|
|
2064
|
-
if (cached !== null) {
|
|
2065
|
-
return cached;
|
|
2066
|
-
}
|
|
2067
|
-
return getOrCreateRequest(input, async (checkInput) => {
|
|
2068
|
-
const result = await isPermitted(checkInput, appName, null);
|
|
2069
|
-
const isPageLevelCheck = !!pageId || permission.includes("page.");
|
|
2070
|
-
rbacCache.set(cacheKey, result, void 0, isPageLevelCheck);
|
|
2071
|
-
return result;
|
|
2072
|
-
});
|
|
2073
|
-
}
|
|
2074
|
-
async function hasAnyPermission(input) {
|
|
2075
|
-
const { permissions, ...baseInput } = input;
|
|
2076
|
-
for (const permission of permissions) {
|
|
2077
|
-
const hasPermission = await isPermitted({
|
|
2078
|
-
...baseInput,
|
|
2079
|
-
permission
|
|
2080
|
-
});
|
|
2081
|
-
if (hasPermission) {
|
|
2082
|
-
return true;
|
|
2083
|
-
}
|
|
2084
|
-
}
|
|
2085
|
-
return false;
|
|
2086
|
-
}
|
|
2087
|
-
async function hasAllPermissions(input) {
|
|
2088
|
-
const { permissions, ...baseInput } = input;
|
|
2089
|
-
for (const permission of permissions) {
|
|
2090
|
-
const hasPermission = await isPermitted({
|
|
2091
|
-
...baseInput,
|
|
2092
|
-
permission
|
|
2093
|
-
});
|
|
2094
|
-
if (!hasPermission) {
|
|
2095
|
-
return false;
|
|
2096
|
-
}
|
|
2097
|
-
}
|
|
2098
|
-
return true;
|
|
2099
|
-
}
|
|
2100
|
-
async function isSuperAdmin(userId) {
|
|
2101
|
-
const engine = getEngine();
|
|
2102
|
-
return engine["checkSuperAdmin"](userId);
|
|
2103
|
-
}
|
|
2104
|
-
async function getPageScopeType(pageId, appId, appName) {
|
|
2105
|
-
const engine = getEngine();
|
|
2106
|
-
try {
|
|
2107
|
-
let resolvedAppId = appId;
|
|
2108
|
-
if (!resolvedAppId && appName) {
|
|
2109
|
-
const { data: app } = await engine["supabase"].from("rbac_apps").select("id").eq("name", appName).eq("is_active", true).single();
|
|
2110
|
-
resolvedAppId = app?.id;
|
|
2111
|
-
}
|
|
2112
|
-
if (!resolvedAppId) {
|
|
2113
|
-
throw new Error(`Could not resolve appId for page ${pageId}`);
|
|
2114
|
-
}
|
|
2115
|
-
let resolvedPageId = pageId;
|
|
2116
|
-
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2117
|
-
if (!uuidRegex.test(pageId)) {
|
|
2118
|
-
const { data: page } = await engine["supabase"].from("rbac_app_pages").select("id").eq("app_id", resolvedAppId).eq("page_name", pageId).maybeSingle();
|
|
2119
|
-
resolvedPageId = page?.id || pageId;
|
|
2120
|
-
}
|
|
2121
|
-
if (!uuidRegex.test(resolvedPageId)) {
|
|
2122
|
-
throw new Error(`Could not resolve pageId ${pageId} to a valid UUID`);
|
|
2123
|
-
}
|
|
2124
|
-
const { data: pageData, error } = await engine["supabase"].from("rbac_app_pages").select("scope_type").eq("id", resolvedPageId).single();
|
|
2125
|
-
if (error) {
|
|
2126
|
-
log4.error("Error fetching page scope type:", { pageId, appId, error });
|
|
2127
|
-
throw new Error(`Failed to get page scope type: ${error.message}`);
|
|
2128
|
-
}
|
|
2129
|
-
if (!pageData || !pageData.scope_type) {
|
|
2130
|
-
throw new Error(`Page ${resolvedPageId} does not have scope_type set`);
|
|
2131
|
-
}
|
|
2132
|
-
return pageData.scope_type;
|
|
2133
|
-
} catch (err) {
|
|
2134
|
-
log4.error("Error fetching page scope type:", err);
|
|
2135
|
-
throw err instanceof Error ? err : new Error(`Failed to get page scope type: ${String(err)}`);
|
|
2136
|
-
}
|
|
2137
|
-
}
|
|
2138
|
-
async function isOrganisationAdmin(userId, organisationId) {
|
|
2139
|
-
const accessLevel = await getAccessLevel({
|
|
2140
|
-
userId,
|
|
2141
|
-
scope: { organisationId }
|
|
2142
|
-
});
|
|
2143
|
-
return accessLevel === "admin" || accessLevel === "super";
|
|
2144
|
-
}
|
|
2145
|
-
async function isEventAdmin(userId, scope) {
|
|
2146
|
-
if (!scope.eventId || !scope.appId) {
|
|
2147
|
-
return false;
|
|
2148
|
-
}
|
|
2149
|
-
const accessLevel = await getAccessLevel({ userId, scope });
|
|
2150
|
-
return accessLevel === "admin" || accessLevel === "super";
|
|
2151
|
-
}
|
|
2152
|
-
function invalidateUserCache(userId, organisationId) {
|
|
2153
|
-
const patterns = organisationId ? [
|
|
2154
|
-
CACHE_PATTERNS.PERMISSION(userId, organisationId),
|
|
2155
|
-
`access:${userId}:${organisationId}:`,
|
|
2156
|
-
`map:${userId}:${organisationId}:`
|
|
2157
|
-
] : [
|
|
2158
|
-
`perm:${userId}:`,
|
|
2159
|
-
`access:${userId}:`,
|
|
2160
|
-
`map:${userId}:`
|
|
2161
|
-
];
|
|
2162
|
-
patterns.forEach((pattern) => rbacCache.invalidate(pattern));
|
|
2163
|
-
}
|
|
2164
|
-
function invalidateOrganisationCache(organisationId) {
|
|
2165
|
-
rbacCache.invalidate(CACHE_PATTERNS.ORGANISATION(organisationId));
|
|
2166
|
-
}
|
|
2167
|
-
function invalidateEventCache(eventId) {
|
|
2168
|
-
rbacCache.invalidate(CACHE_PATTERNS.EVENT(eventId));
|
|
2169
|
-
}
|
|
2170
|
-
function invalidateAppCache(appId) {
|
|
2171
|
-
rbacCache.invalidate(CACHE_PATTERNS.APP(appId));
|
|
2172
|
-
}
|
|
2173
|
-
function clearCache() {
|
|
2174
|
-
rbacCache.clear();
|
|
2175
|
-
}
|
|
2176
|
-
|
|
2177
|
-
export { CACHE_PATTERNS, ContextValidator, EventContextRequiredError, OrganisationContextRequiredError, RBACCache, RBACEngine, RBACNotInitializedError, clearCache, clearInFlightRequests, createRBACConfig, createRBACEngine, disablePerformanceMonitoring, enablePerformanceMonitoring, getAccessLevel, getInFlightRequestCount, getPageScopeType, getPerformanceMetrics, getPerformanceSummary, getPermissionMap, getRBACConfig, getRBACLogger, getRoleContext, hasAllPermissions, hasAnyPermission, invalidateAppCache, invalidateEventCache, invalidateOrganisationCache, invalidateUserCache, isDebugMode, isDevelopmentMode, isEventAdmin, isOrganisationAdmin, isPerformanceMonitoringEnabled, isPermitted, isPermittedCached, isRBACInitialized, isSuperAdmin, rbacCache, recordAuditEvent, recordPermissionCheck, resetPerformanceMetrics, resolveAppContext, setupRBAC };
|