@jmruthers/pace-core 0.6.5 → 0.6.7
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 +104 -0
- package/README.md +5 -403
- package/audit-tool/00-dependencies.cjs +394 -0
- package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
- package/audit-tool/audits/02-project-structure.cjs +255 -0
- package/audit-tool/audits/03-architecture.cjs +196 -0
- package/audit-tool/audits/04-code-quality.cjs +149 -0
- package/audit-tool/audits/05-styling.cjs +224 -0
- package/audit-tool/audits/06-security-rbac.cjs +544 -0
- package/audit-tool/audits/07-api-tech-stack.cjs +301 -0
- package/audit-tool/audits/08-testing-documentation.cjs +202 -0
- package/audit-tool/audits/09-operations.cjs +208 -0
- package/audit-tool/index.cjs +291 -0
- package/audit-tool/utils/code-utils.cjs +218 -0
- package/audit-tool/utils/file-utils.cjs +230 -0
- package/audit-tool/utils/report-utils.cjs +241 -0
- package/core-usage-manifest.json +93 -0
- package/cursor-rules/00-standards-overview.mdc +156 -0
- package/cursor-rules/01-pace-core-compliance.mdc +586 -0
- package/cursor-rules/02-project-structure.mdc +42 -4
- package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +126 -10
- package/cursor-rules/04-code-quality.mdc +419 -0
- package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +104 -34
- package/cursor-rules/06-security-rbac.mdc +518 -0
- package/cursor-rules/07-api-tech-stack.mdc +377 -0
- package/cursor-rules/08-testing-documentation.mdc +324 -0
- package/cursor-rules/09-operations.mdc +365 -0
- package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
- package/dist/DataTable-7PMH7XN7.js +15 -0
- package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
- package/dist/{PublicPageProvider-QTFVrL-Z.d.ts → PublicPageProvider-DlsCaR5v.d.ts} +33 -72
- package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
- package/dist/api-Y4MQWOFW.js +4 -0
- package/dist/audit-MYQXYZFU.js +3 -0
- package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
- package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
- package/dist/{chunk-UPPMRMYG.js → chunk-5X4QLXRG.js} +73 -151
- package/dist/chunk-6F3IILHI.js +62 -0
- package/dist/{chunk-E66EQZE6.js → chunk-6GLLNA6U.js} +3 -9
- package/dist/{chunk-ZSAAAMVR.js → chunk-6QYDGKQY.js} +1 -4
- package/dist/{chunk-FMUCXFII.js → chunk-7ILTDCL2.js} +9 -5
- package/dist/{chunk-M43Y4SSO.js → chunk-A3W6LW53.js} +15 -13
- package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
- package/dist/{chunk-HU2C6SSC.js → chunk-BM4CQ5P3.js} +606 -559
- package/dist/chunk-C7NSAPTL.js +1 -0
- package/dist/{chunk-J36DSWQK.js → chunk-FEJLJNWA.js} +7 -41
- package/dist/{chunk-IHB5DR3H.js → chunk-FTCRZOG2.js} +188 -387
- package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
- package/dist/chunk-GHYHJTYV.js +994 -0
- package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
- package/dist/{chunk-FFQEQTNW.js → chunk-IUBRCBSY.js} +134 -45
- package/dist/{chunk-6COVEUS7.js → chunk-JGWDVX64.js} +983 -1034
- package/dist/{chunk-RGAWHO7N.js → chunk-L4XMVJKY.js} +77 -222
- package/dist/chunk-MBADTM7L.js +64 -0
- package/dist/{chunk-M7MPQISP.js → chunk-OJ4SKRSV.js} +3 -16
- package/dist/{chunk-IVOFDYWT.js → chunk-Q7Q7V5NV.js} +2109 -1604
- package/dist/{chunk-JGRYX5UX.js → chunk-S7DKJPLT.js} +29 -58
- package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
- package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
- package/dist/{chunk-NTM7ZSB6.js → chunk-VBCS3DUA.js} +261 -168
- package/dist/{chunk-EFN2EIMK.js → chunk-ZFYPMX46.js} +271 -87
- package/dist/{chunk-L4OXEN46.js → chunk-ZKAWKYT4.js} +10 -24
- package/dist/components.d.ts +7 -5
- package/dist/components.js +46 -257
- package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
- package/dist/eslint-rules/index.cjs +35 -0
- package/{src/eslint-rules/pace-core-compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +234 -235
- package/dist/eslint-rules/rules/04-code-quality.cjs +290 -0
- package/dist/eslint-rules/rules/05-styling.cjs +61 -0
- package/dist/eslint-rules/rules/06-security-rbac.cjs +806 -0
- package/dist/eslint-rules/rules/07-api-tech-stack.cjs +263 -0
- package/dist/eslint-rules/rules/08-testing.cjs +94 -0
- package/dist/eslint-rules/utils/helpers.cjs +42 -0
- package/dist/eslint-rules/utils/manifest-loader.cjs +75 -0
- package/dist/hooks.d.ts +6 -6
- package/dist/hooks.js +62 -172
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +12 -11
- package/dist/index.js +67 -660
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +8 -35
- package/dist/rbac/eslint-rules.d.ts +46 -44
- package/dist/rbac/eslint-rules.js +7 -4
- package/dist/rbac/index.d.ts +109 -586
- package/dist/rbac/index.js +14 -207
- package/dist/styles/index.js +2 -12
- package/dist/theming/runtime.d.ts +14 -1
- package/dist/theming/runtime.js +3 -19
- package/dist/{timezone-CHhWg6b4.d.ts → timezone-BZe_eUxx.d.ts} +175 -1
- package/dist/{types-CkbwOr4Y.d.ts → types-DXstZpNI.d.ts} +4 -17
- package/dist/types-t9H8qKRw.d.ts +55 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +7 -94
- package/dist/{usePublicRouteParams-ClnV4tnv.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +20 -20
- package/dist/utils.d.ts +24 -117
- package/dist/utils.js +54 -392
- package/docs/README.md +17 -7
- package/docs/api/README.md +4 -402
- package/docs/api/modules.md +301 -871
- package/docs/api-reference/components.md +21 -21
- package/docs/api-reference/deprecated.md +31 -6
- package/docs/api-reference/hooks.md +80 -80
- package/docs/api-reference/rpc-functions.md +78 -3
- package/docs/api-reference/types.md +1 -1
- package/docs/api-reference/utilities.md +1 -1
- package/docs/architecture/README.md +1 -1
- package/docs/core-concepts/events.md +3 -3
- package/docs/core-concepts/organisations.md +6 -6
- package/docs/core-concepts/permissions.md +6 -6
- package/docs/documentation-index.md +12 -18
- package/docs/getting-started/cursor-rules.md +3 -23
- package/docs/getting-started/dependencies.md +650 -0
- package/docs/getting-started/documentation-index.md +1 -1
- package/docs/getting-started/examples/README.md +4 -4
- package/docs/getting-started/examples/full-featured-app.md +1 -1
- package/docs/getting-started/faq.md +2 -2
- package/docs/getting-started/installation-guide.md +20 -7
- package/docs/getting-started/quick-reference.md +4 -4
- package/docs/getting-started/quick-start.md +23 -12
- package/docs/implementation-guides/authentication.md +15 -15
- package/docs/implementation-guides/component-styling.md +1 -1
- package/docs/implementation-guides/data-tables.md +126 -33
- package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
- package/docs/implementation-guides/dynamic-colors.md +3 -3
- package/docs/implementation-guides/file-upload-storage.md +2 -2
- package/docs/implementation-guides/hierarchical-datatable.md +40 -60
- package/docs/implementation-guides/inactivity-tracking.md +3 -3
- package/docs/implementation-guides/large-datasets.md +3 -2
- package/docs/implementation-guides/organisation-security.md +2 -2
- package/docs/implementation-guides/performance.md +2 -2
- package/docs/implementation-guides/permission-enforcement.md +5 -1
- package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
- package/docs/migration/V0.4.0_rbac-migration.md +6 -6
- package/docs/rbac/MIGRATION_GUIDE.md +819 -0
- package/docs/rbac/RBAC_CONTRACT.md +724 -0
- package/docs/rbac/README.md +17 -8
- package/docs/rbac/advanced-patterns.md +6 -6
- package/docs/rbac/api-reference.md +20 -20
- package/docs/rbac/edge-functions-guide.md +376 -0
- package/docs/rbac/event-based-apps.md +3 -3
- package/docs/rbac/examples.md +41 -41
- package/docs/rbac/getting-started.md +37 -37
- package/docs/rbac/performance.md +1 -1
- package/docs/rbac/quick-start.md +52 -52
- package/docs/rbac/secure-client-protection.md +1 -35
- package/docs/rbac/troubleshooting.md +1 -1
- package/docs/security/README.md +5 -5
- package/docs/standards/0-standards-overview.md +220 -0
- package/docs/standards/1-pace-core-compliance-standards.md +986 -0
- package/docs/standards/2-project-structure-standards.md +949 -0
- package/docs/standards/3-architecture-standards.md +606 -0
- package/docs/standards/4-code-quality-standards.md +728 -0
- package/docs/standards/5-styling-standards.md +348 -0
- package/docs/standards/{07-rbac-and-rls-standard.md → 6-security-rbac-standards.md} +269 -66
- package/docs/standards/7-api-tech-stack-standards.md +662 -0
- package/docs/standards/8-testing-documentation-standards.md +401 -0
- package/docs/standards/9-operations-standards.md +1102 -0
- package/docs/standards/README.md +185 -57
- package/docs/troubleshooting/README.md +4 -4
- package/docs/troubleshooting/common-issues.md +2 -2
- package/docs/troubleshooting/debugging.md +9 -9
- package/docs/troubleshooting/migration.md +4 -4
- package/docs/troubleshooting/organisation-context-setup.md +42 -19
- package/eslint-config-pace-core.cjs +33 -6
- package/package.json +35 -23
- package/scripts/install-cursor-rules.cjs +25 -6
- package/scripts/install-eslint-config.cjs +284 -0
- package/src/__tests__/fixtures/supabase.ts +1 -1
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +3 -3
- package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +1 -1
- package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +1 -1
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +13 -13
- package/src/__tests__/helpers/component-test-utils.tsx +1 -1
- package/src/__tests__/helpers/supabaseMock.ts +2 -2
- package/src/__tests__/integration/UserProfile.test.tsx +14 -14
- package/src/__tests__/public-recipe-view.test.ts +38 -9
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
- package/src/__tests__/templates/accessibility.test.template.tsx +9 -9
- package/src/__tests__/templates/component.test.template.tsx +18 -15
- package/src/components/Button/Button.tsx +5 -1
- package/src/components/Calendar/Calendar.tsx +201 -47
- package/src/components/ContextSelector/ContextSelector.tsx +106 -119
- package/src/components/DataTable/AUDIT_REPORT.md +293 -0
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +10 -2
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +10 -4
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
- package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
- package/src/components/DataTable/components/DataTableCore.tsx +186 -13
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
- package/src/components/DataTable/components/DataTableLayout.tsx +35 -21
- package/src/components/DataTable/components/EditFields.tsx +23 -3
- package/src/components/DataTable/components/EditableRow.tsx +12 -9
- package/src/components/DataTable/components/EmptyState.tsx +10 -9
- package/src/components/DataTable/components/FilterRow.tsx +2 -4
- package/src/components/DataTable/components/ImportModal.tsx +124 -126
- package/src/components/DataTable/components/LoadingState.tsx +5 -6
- package/src/components/DataTable/components/RowComponent.tsx +12 -0
- package/src/components/DataTable/components/SortIndicator.tsx +50 -0
- package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +41 -27
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
- package/src/components/DataTable/components/index.ts +2 -1
- package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +51 -47
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +24 -21
- package/src/components/DataTable/hooks/useDataTableState.ts +125 -9
- package/src/components/DataTable/hooks/useTableColumns.ts +40 -2
- package/src/components/DataTable/hooks/useTableHandlers.ts +11 -0
- package/src/components/DataTable/types.ts +5 -18
- package/src/components/DataTable/utils/a11yUtils.ts +17 -0
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +2 -1
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
- package/src/components/DateTimeField/DateTimeField.tsx +10 -9
- package/src/components/Dialog/Dialog.test.tsx +128 -104
- package/src/components/Dialog/Dialog.tsx +742 -24
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
- package/src/components/FileDisplay/FileDisplay.test.tsx +4 -2
- package/src/components/FileDisplay/FileDisplay.tsx +23 -17
- package/src/components/FileUpload/FileUpload.test.tsx +52 -14
- package/src/components/FileUpload/FileUpload.tsx +112 -130
- package/src/components/Form/Form.test.tsx +6 -8
- package/src/components/Form/Form.tsx +365 -4
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
- package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +11 -15
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
- package/src/components/Progress/Progress.tsx +2 -4
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
- package/src/components/Select/Select.tsx +109 -98
- package/src/components/Select/types.ts +4 -1
- package/src/components/UserMenu/UserMenu.tsx +9 -6
- package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
- package/src/hooks/__tests__/hooks.integration.test.tsx +55 -57
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +97 -97
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +149 -67
- package/src/hooks/__tests__/usePublicEvent.test.ts +149 -79
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +158 -109
- package/src/hooks/__tests__/useSessionDraft.test.ts +163 -0
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +10 -5
- package/src/hooks/public/usePublicEvent.ts +67 -195
- package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
- package/src/hooks/public/usePublicEventLogo.ts +24 -14
- package/src/hooks/public/usePublicFileDisplay.ts +2 -2
- package/src/hooks/public/usePublicRouteParams.ts +5 -5
- package/src/hooks/useAppConfig.ts +28 -26
- package/src/hooks/useEventTheme.test.ts +217 -239
- package/src/hooks/useEventTheme.ts +16 -28
- package/src/hooks/useFileDisplay.ts +2 -2
- package/src/hooks/useOrganisationPermissions.ts +5 -7
- package/src/hooks/useQueryCache.ts +0 -1
- package/src/hooks/useSessionDraft.ts +380 -0
- package/src/hooks/useSessionRestoration.ts +3 -1
- package/src/icons/index.ts +27 -0
- package/src/index.ts +5 -0
- package/src/providers/OrganisationProvider.tsx +23 -14
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
- package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
- package/src/providers/__tests__/EventProvider.test.tsx +61 -61
- package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
- package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +37 -37
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
- package/src/providers/services/EventServiceProvider.tsx +1 -24
- package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +13 -10
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +7 -457
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +33 -7
- package/src/rbac/adapters.tsx +7 -295
- package/src/rbac/api.test.ts +44 -56
- package/src/rbac/api.ts +10 -17
- package/src/rbac/cache-invalidation.ts +0 -1
- package/src/rbac/compliance/index.ts +10 -0
- package/src/rbac/compliance/pattern-detector.ts +553 -0
- package/src/rbac/compliance/runtime-compliance.ts +22 -0
- package/src/rbac/components/AccessDenied.tsx +150 -0
- package/src/rbac/components/NavigationGuard.tsx +12 -20
- package/src/rbac/components/PagePermissionGuard.tsx +4 -24
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +21 -8
- package/src/rbac/components/index.ts +3 -41
- package/src/rbac/eslint-rules.js +1 -1
- package/src/rbac/hooks/index.ts +0 -3
- package/src/rbac/hooks/permissions/index.ts +0 -3
- package/src/rbac/hooks/permissions/useAccessLevel.ts +4 -8
- package/src/rbac/hooks/usePermissions.ts +0 -3
- package/src/rbac/hooks/useResolvedScope.test.ts +57 -47
- package/src/rbac/hooks/useResolvedScope.ts +58 -140
- package/src/rbac/hooks/useResourcePermissions.test.ts +124 -38
- package/src/rbac/hooks/useResourcePermissions.ts +139 -48
- package/src/rbac/hooks/useRoleManagement.test.ts +65 -22
- package/src/rbac/hooks/useRoleManagement.ts +147 -19
- package/src/rbac/hooks/useSecureSupabase.ts +4 -8
- package/src/rbac/index.ts +7 -9
- package/src/rbac/utils/contextValidator.ts +9 -7
- package/src/services/AuthService.ts +130 -18
- package/src/services/EventService.ts +4 -97
- package/src/services/InactivityService.ts +16 -0
- package/src/services/OrganisationService.ts +7 -44
- package/src/services/__tests__/OrganisationService.test.ts +26 -8
- package/src/services/base/BaseService.ts +0 -3
- package/src/styles/core.css +7 -0
- package/src/theming/__tests__/parseEventColours.test.ts +9 -3
- package/src/theming/parseEventColours.ts +22 -10
- package/src/types/database.generated.ts +4733 -3809
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
- package/src/utils/__tests__/organisationContext.unit.test.ts +9 -10
- package/src/utils/context/organisationContext.test.ts +13 -28
- package/src/utils/context/organisationContext.ts +21 -52
- package/src/utils/dynamic/dynamicUtils.ts +1 -1
- package/src/utils/file-reference/index.ts +39 -15
- package/src/utils/formatting/formatDateTime.test.ts +3 -2
- package/src/utils/google-places/loadGoogleMapsScript.ts +29 -4
- package/src/utils/index.ts +4 -1
- package/src/utils/persistence/__tests__/keyDerivation.test.ts +135 -0
- package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +123 -0
- package/src/utils/persistence/keyDerivation.ts +304 -0
- package/src/utils/persistence/sensitiveFieldDetection.ts +212 -0
- package/src/utils/security/secureStorage.ts +5 -5
- package/src/utils/storage/README.md +1 -1
- package/src/utils/storage/helpers.ts +3 -3
- package/src/utils/supabase/createBaseClient.ts +147 -0
- package/src/utils/timezone/timezone.test.ts +1 -2
- package/src/utils/timezone/timezone.ts +1 -1
- package/src/utils/validation/csrf.ts +4 -4
- package/cursor-rules/00-pace-core-compliance.mdc +0 -331
- package/cursor-rules/01-standards-compliance.mdc +0 -244
- package/cursor-rules/04-testing-standards.mdc +0 -268
- package/cursor-rules/05-bug-reports-and-features.mdc +0 -246
- package/cursor-rules/06-code-quality.mdc +0 -309
- package/cursor-rules/07-tech-stack-compliance.mdc +0 -214
- package/cursor-rules/CHANGELOG.md +0 -119
- package/cursor-rules/README.md +0 -192
- package/dist/DataTable-AOVNCPTX.js +0 -175
- package/dist/DataTable-AOVNCPTX.js.map +0 -1
- package/dist/UnifiedAuthProvider-4SBX4LU5.js +0 -18
- package/dist/UnifiedAuthProvider-4SBX4LU5.js.map +0 -1
- package/dist/api-O6HTBX5Y.js +0 -52
- package/dist/api-O6HTBX5Y.js.map +0 -1
- package/dist/audit-V53FV5AG.js +0 -17
- package/dist/audit-V53FV5AG.js.map +0 -1
- package/dist/chunk-5DRSZLL2.js.map +0 -1
- package/dist/chunk-63FOKYGO.js.map +0 -1
- package/dist/chunk-6COVEUS7.js.map +0 -1
- package/dist/chunk-AFVQODI2.js +0 -263
- package/dist/chunk-AFVQODI2.js.map +0 -1
- package/dist/chunk-DGUM43GV.js.map +0 -1
- package/dist/chunk-E66EQZE6.js.map +0 -1
- package/dist/chunk-EFN2EIMK.js.map +0 -1
- package/dist/chunk-FFQEQTNW.js.map +0 -1
- package/dist/chunk-FMUCXFII.js.map +0 -1
- package/dist/chunk-G37KK66H.js.map +0 -1
- package/dist/chunk-G7QEZTYQ.js +0 -2053
- package/dist/chunk-G7QEZTYQ.js.map +0 -1
- package/dist/chunk-HU2C6SSC.js.map +0 -1
- package/dist/chunk-IHB5DR3H.js.map +0 -1
- package/dist/chunk-IVOFDYWT.js.map +0 -1
- package/dist/chunk-J36DSWQK.js.map +0 -1
- package/dist/chunk-JGRYX5UX.js.map +0 -1
- package/dist/chunk-KQCRWDSA.js +0 -1
- package/dist/chunk-KQCRWDSA.js.map +0 -1
- package/dist/chunk-L4OXEN46.js.map +0 -1
- package/dist/chunk-LMC26NLJ.js +0 -84
- package/dist/chunk-LMC26NLJ.js.map +0 -1
- package/dist/chunk-M43Y4SSO.js.map +0 -1
- package/dist/chunk-M7MPQISP.js.map +0 -1
- package/dist/chunk-NTM7ZSB6.js.map +0 -1
- package/dist/chunk-PWLANIRT.js.map +0 -1
- package/dist/chunk-QXHPKYJV.js.map +0 -1
- package/dist/chunk-RGAWHO7N.js.map +0 -1
- package/dist/chunk-UPPMRMYG.js.map +0 -1
- package/dist/chunk-VBXEHIUJ.js.map +0 -1
- package/dist/chunk-ZSAAAMVR.js.map +0 -1
- package/dist/components.js.map +0 -1
- package/dist/contextValidator-5OGXSPKS.js +0 -9
- package/dist/contextValidator-5OGXSPKS.js.map +0 -1
- package/dist/eslint-rules/pace-core-compliance.cjs +0 -510
- package/dist/hooks.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/providers.js.map +0 -1
- package/dist/rbac/eslint-rules.js.map +0 -1
- package/dist/rbac/index.js.map +0 -1
- package/dist/styles/index.js.map +0 -1
- package/dist/theming/runtime.js.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils.js.map +0 -1
- package/docs/best-practices/README.md +0 -472
- package/docs/best-practices/accessibility.md +0 -601
- package/docs/best-practices/common-patterns.md +0 -516
- package/docs/best-practices/deployment.md +0 -1103
- package/docs/best-practices/performance.md +0 -1328
- package/docs/best-practices/security.md +0 -940
- package/docs/best-practices/testing.md +0 -1034
- package/docs/rbac/compliance/compliance-guide.md +0 -544
- package/docs/standards/01-architecture-standard.md +0 -44
- package/docs/standards/02-api-and-rpc-standard.md +0 -39
- package/docs/standards/03-component-standard.md +0 -32
- package/docs/standards/04-code-style-standard.md +0 -32
- package/docs/standards/05-security-standard.md +0 -44
- package/docs/standards/06-testing-and-docs-standard.md +0 -29
- package/docs/standards/pace-core-compliance.md +0 -432
- package/scripts/audit/core/checks/accessibility.cjs +0 -197
- package/scripts/audit/core/checks/api-usage.cjs +0 -191
- package/scripts/audit/core/checks/bundle.cjs +0 -142
- package/scripts/audit/core/checks/compliance.cjs +0 -2706
- package/scripts/audit/core/checks/config.cjs +0 -54
- package/scripts/audit/core/checks/coverage.cjs +0 -84
- package/scripts/audit/core/checks/dependencies.cjs +0 -994
- package/scripts/audit/core/checks/documentation.cjs +0 -268
- package/scripts/audit/core/checks/environment.cjs +0 -116
- package/scripts/audit/core/checks/error-handling.cjs +0 -340
- package/scripts/audit/core/checks/forms.cjs +0 -172
- package/scripts/audit/core/checks/heuristics.cjs +0 -68
- package/scripts/audit/core/checks/hooks.cjs +0 -334
- package/scripts/audit/core/checks/imports.cjs +0 -244
- package/scripts/audit/core/checks/performance.cjs +0 -325
- package/scripts/audit/core/checks/routes.cjs +0 -117
- package/scripts/audit/core/checks/state.cjs +0 -130
- package/scripts/audit/core/checks/structure.cjs +0 -65
- package/scripts/audit/core/checks/style.cjs +0 -584
- package/scripts/audit/core/checks/testing.cjs +0 -122
- package/scripts/audit/core/checks/typescript.cjs +0 -61
- package/scripts/audit/core/scanner.cjs +0 -199
- package/scripts/audit/core/utils.cjs +0 -137
- package/scripts/audit/index.cjs +0 -223
- package/scripts/audit/reporters/console.cjs +0 -151
- package/scripts/audit/reporters/json.cjs +0 -54
- package/scripts/audit/reporters/markdown.cjs +0 -124
- package/scripts/audit-consuming-app.cjs +0 -86
- package/src/components/DataTable/components/DataTableBody.tsx +0 -454
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
- package/src/components/DataTable/components/ExpandButton.tsx +0 -113
- package/src/components/DataTable/components/GroupHeader.tsx +0 -54
- package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
- package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
- package/src/components/DataTable/core/DataTableContext.tsx +0 -216
- package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
- package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
- package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
- package/src/components/DataTable/utils/debugTools.ts +0 -514
- package/src/eslint-rules/pace-core-compliance.js +0 -638
- package/src/rbac/components/EnhancedNavigationMenu.test.tsx +0 -555
- package/src/rbac/components/EnhancedNavigationMenu.tsx +0 -293
- package/src/rbac/components/NavigationProvider.test.tsx +0 -481
- package/src/rbac/components/NavigationProvider.tsx +0 -345
- package/src/rbac/components/PagePermissionProvider.test.tsx +0 -476
- package/src/rbac/components/PagePermissionProvider.tsx +0 -279
- package/src/rbac/components/PermissionEnforcer.tsx +0 -312
- package/src/rbac/components/RoleBasedRouter.tsx +0 -440
- package/src/rbac/components/SecureDataProvider.test.tsx +0 -543
- package/src/rbac/components/SecureDataProvider.tsx +0 -339
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +0 -620
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +0 -726
- package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +0 -661
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +0 -881
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +0 -783
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +0 -645
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +0 -659
- package/src/rbac/hooks/permissions/useCachedPermissions.ts +0 -79
- package/src/rbac/hooks/permissions/useHasAllPermissions.ts +0 -90
- package/src/rbac/hooks/permissions/useHasAnyPermission.ts +0 -90
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Page Permission Provider Component
|
|
3
|
-
* @package @jmruthers/pace-core
|
|
4
|
-
* @module RBAC/Components/PagePermissionProvider
|
|
5
|
-
* @since 2.0.0
|
|
6
|
-
*
|
|
7
|
-
* A context provider that manages page-level permissions across the entire application.
|
|
8
|
-
* This component ensures that all pages are properly protected and provides centralized
|
|
9
|
-
* page permission management.
|
|
10
|
-
*
|
|
11
|
-
* Features:
|
|
12
|
-
* - App-wide page permission management
|
|
13
|
-
* - Strict mode to prevent bypassing
|
|
14
|
-
* - Automatic audit logging
|
|
15
|
-
* - Integration with existing RBAC system
|
|
16
|
-
* - Page permission tracking
|
|
17
|
-
* - Error handling and recovery
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```tsx
|
|
21
|
-
* // Basic app setup with page permissions
|
|
22
|
-
* <PagePermissionProvider strictMode={true} auditLog={true}>
|
|
23
|
-
* <App />
|
|
24
|
-
* </PagePermissionProvider>
|
|
25
|
-
*
|
|
26
|
-
* // With custom configuration
|
|
27
|
-
* <PagePermissionProvider
|
|
28
|
-
* strictMode={true}
|
|
29
|
-
* auditLog={true}
|
|
30
|
-
* onPageAccess={(pageName, operation, allowed) => {
|
|
31
|
-
* console.log(`Page access: ${pageName} ${operation} - ${allowed ? 'allowed' : 'denied'}`);
|
|
32
|
-
* }}
|
|
33
|
-
* >
|
|
34
|
-
* <App />
|
|
35
|
-
* </PagePermissionProvider>
|
|
36
|
-
* ```
|
|
37
|
-
*
|
|
38
|
-
* @security
|
|
39
|
-
* - Enforces page-level permissions across the app
|
|
40
|
-
* - Prevents apps from bypassing permission checks
|
|
41
|
-
* - Automatic audit logging for all page access attempts
|
|
42
|
-
* - Integration with existing RBAC system
|
|
43
|
-
* - Page permission tracking and monitoring
|
|
44
|
-
*
|
|
45
|
-
* @performance
|
|
46
|
-
* - Optimized with useMemo and useCallback
|
|
47
|
-
* - Efficient context updates
|
|
48
|
-
* - Minimal re-renders
|
|
49
|
-
* - Cached permission checks
|
|
50
|
-
*
|
|
51
|
-
* @dependencies
|
|
52
|
-
* - React 19+ - Context and hooks
|
|
53
|
-
* - useUnifiedAuth - Authentication context
|
|
54
|
-
* - RBAC types - Type definitions
|
|
55
|
-
*/
|
|
56
|
-
|
|
57
|
-
import React, { createContext, useContext, useState, useCallback, useMemo, useEffect } from 'react';
|
|
58
|
-
import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
|
|
59
|
-
import { useResolvedScope } from '../hooks/useResolvedScope';
|
|
60
|
-
import { UUID, Scope, Permission } from '../types';
|
|
61
|
-
import { createLogger } from '../../utils/core/logger';
|
|
62
|
-
|
|
63
|
-
const log = createLogger('PagePermissionProvider');
|
|
64
|
-
|
|
65
|
-
export interface PagePermissionContextType {
|
|
66
|
-
/** Check if user has permission for a page */
|
|
67
|
-
hasPagePermission: (pageName: string, operation: string, pageId?: string, scope?: Scope) => boolean;
|
|
68
|
-
|
|
69
|
-
/** Get all page permissions for current user */
|
|
70
|
-
getPagePermissions: () => Record<string, string[]>;
|
|
71
|
-
|
|
72
|
-
/** Check if page permission checking is enabled */
|
|
73
|
-
isEnabled: boolean;
|
|
74
|
-
|
|
75
|
-
/** Check if strict mode is enabled */
|
|
76
|
-
isStrictMode: boolean;
|
|
77
|
-
|
|
78
|
-
/** Check if audit logging is enabled */
|
|
79
|
-
isAuditLogEnabled: boolean;
|
|
80
|
-
|
|
81
|
-
/** Get page access history */
|
|
82
|
-
getPageAccessHistory: () => PageAccessRecord[];
|
|
83
|
-
|
|
84
|
-
/** Clear page access history */
|
|
85
|
-
clearPageAccessHistory: () => void;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export interface PageAccessRecord {
|
|
89
|
-
pageName: string;
|
|
90
|
-
operation: string;
|
|
91
|
-
userId: UUID;
|
|
92
|
-
scope: Scope;
|
|
93
|
-
allowed: boolean;
|
|
94
|
-
timestamp: string;
|
|
95
|
-
pageId?: string;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export interface PagePermissionProviderProps {
|
|
99
|
-
/** Child components */
|
|
100
|
-
children: React.ReactNode;
|
|
101
|
-
|
|
102
|
-
/** Enable strict mode to prevent bypassing (default: true) */
|
|
103
|
-
strictMode?: boolean;
|
|
104
|
-
|
|
105
|
-
/** Enable audit logging (default: true) */
|
|
106
|
-
auditLog?: boolean;
|
|
107
|
-
|
|
108
|
-
/** Callback when page access is attempted */
|
|
109
|
-
onPageAccess?: (pageName: string, operation: string, allowed: boolean, record: PageAccessRecord) => void;
|
|
110
|
-
|
|
111
|
-
/** Callback when strict mode violation occurs */
|
|
112
|
-
onStrictModeViolation?: (pageName: string, operation: string, record: PageAccessRecord) => void;
|
|
113
|
-
|
|
114
|
-
/** Maximum number of access records to keep in history */
|
|
115
|
-
maxHistorySize?: number;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const PagePermissionContext = createContext<PagePermissionContextType | null>(null);
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* PagePermissionProvider - Manages page-level permissions across the app
|
|
122
|
-
*
|
|
123
|
-
* This provider ensures that all pages are properly protected and provides
|
|
124
|
-
* centralized page permission management with strict enforcement.
|
|
125
|
-
*
|
|
126
|
-
* @param props - Provider props
|
|
127
|
-
* @returns React element with page permission context
|
|
128
|
-
*/
|
|
129
|
-
export function PagePermissionProvider({
|
|
130
|
-
children,
|
|
131
|
-
strictMode = true,
|
|
132
|
-
auditLog = true,
|
|
133
|
-
onPageAccess,
|
|
134
|
-
onStrictModeViolation,
|
|
135
|
-
maxHistorySize = 1000
|
|
136
|
-
}: PagePermissionProviderProps) {
|
|
137
|
-
const { user, selectedOrganisation, selectedEvent, supabase } = useUnifiedAuth();
|
|
138
|
-
const [pageAccessHistory, setPageAccessHistory] = useState<PageAccessRecord[]>([]);
|
|
139
|
-
const [isEnabled, setIsEnabled] = useState(true);
|
|
140
|
-
|
|
141
|
-
// Use useResolvedScope to get proper scope (org derived from event if needed)
|
|
142
|
-
const { resolvedScope } = useResolvedScope({
|
|
143
|
-
supabase,
|
|
144
|
-
selectedOrganisationId: selectedOrganisation?.id || null,
|
|
145
|
-
selectedEventId: selectedEvent?.event_id || null
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
// Get current scope from resolved scope
|
|
149
|
-
// For event-required apps: org is derived from event
|
|
150
|
-
// For org-required apps: org comes from selectedOrganisation
|
|
151
|
-
const currentScope = resolvedScope;
|
|
152
|
-
|
|
153
|
-
// Check if user has permission for a page
|
|
154
|
-
const hasPagePermission = useCallback((
|
|
155
|
-
pageName: string,
|
|
156
|
-
operation: string,
|
|
157
|
-
pageId?: string,
|
|
158
|
-
scope?: Scope
|
|
159
|
-
): boolean => {
|
|
160
|
-
if (!isEnabled) return true;
|
|
161
|
-
if (!user?.id) return false;
|
|
162
|
-
|
|
163
|
-
const effectiveScope = scope || currentScope;
|
|
164
|
-
if (!effectiveScope) return false;
|
|
165
|
-
|
|
166
|
-
// Use the existing RBAC system to check permissions
|
|
167
|
-
// This is a synchronous check for the context - actual permission checking
|
|
168
|
-
// happens in the PagePermissionGuard component using useCan hook
|
|
169
|
-
const permission = `${operation}:page.${pageName}` as Permission;
|
|
170
|
-
|
|
171
|
-
// Return false by default (secure by default) - let individual PagePermissionGuard
|
|
172
|
-
// components handle the actual permission checking asynchronously
|
|
173
|
-
// This context is mainly for tracking and audit purposes
|
|
174
|
-
return false;
|
|
175
|
-
}, [isEnabled, user?.id, currentScope]);
|
|
176
|
-
|
|
177
|
-
// Get all page permissions for current user
|
|
178
|
-
const getPagePermissions = useCallback((): Record<string, string[]> => {
|
|
179
|
-
if (!isEnabled || !user?.id) return {};
|
|
180
|
-
|
|
181
|
-
// For now, return empty object - this will be enhanced with actual permission checking
|
|
182
|
-
// when we integrate with the existing RBAC system
|
|
183
|
-
return {};
|
|
184
|
-
}, [isEnabled, user?.id]);
|
|
185
|
-
|
|
186
|
-
// Get page access history
|
|
187
|
-
const getPageAccessHistory = useCallback((): PageAccessRecord[] => {
|
|
188
|
-
return [...pageAccessHistory];
|
|
189
|
-
}, [pageAccessHistory]);
|
|
190
|
-
|
|
191
|
-
// Clear page access history
|
|
192
|
-
const clearPageAccessHistory = useCallback(() => {
|
|
193
|
-
setPageAccessHistory([]);
|
|
194
|
-
}, []);
|
|
195
|
-
|
|
196
|
-
// Record page access attempt
|
|
197
|
-
const recordPageAccess = useCallback((
|
|
198
|
-
pageName: string,
|
|
199
|
-
operation: string,
|
|
200
|
-
allowed: boolean,
|
|
201
|
-
pageId?: string,
|
|
202
|
-
scope?: Scope
|
|
203
|
-
) => {
|
|
204
|
-
if (!auditLog || !user?.id) return;
|
|
205
|
-
|
|
206
|
-
const record: PageAccessRecord = {
|
|
207
|
-
pageName,
|
|
208
|
-
operation,
|
|
209
|
-
userId: user.id,
|
|
210
|
-
scope: scope || currentScope || { organisationId: '' },
|
|
211
|
-
allowed,
|
|
212
|
-
timestamp: new Date().toISOString(),
|
|
213
|
-
pageId
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
setPageAccessHistory(prev => {
|
|
217
|
-
const newHistory = [record, ...prev];
|
|
218
|
-
return newHistory.slice(0, maxHistorySize);
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
if (onPageAccess) {
|
|
222
|
-
onPageAccess(pageName, operation, allowed, record);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (strictMode && !allowed && onStrictModeViolation) {
|
|
226
|
-
onStrictModeViolation(pageName, operation, record);
|
|
227
|
-
}
|
|
228
|
-
}, [auditLog, user?.id, currentScope, maxHistorySize, onPageAccess, onStrictModeViolation, strictMode]);
|
|
229
|
-
|
|
230
|
-
// Context value
|
|
231
|
-
const contextValue = useMemo((): PagePermissionContextType => ({
|
|
232
|
-
hasPagePermission,
|
|
233
|
-
getPagePermissions,
|
|
234
|
-
isEnabled,
|
|
235
|
-
isStrictMode: strictMode,
|
|
236
|
-
isAuditLogEnabled: auditLog,
|
|
237
|
-
getPageAccessHistory,
|
|
238
|
-
clearPageAccessHistory
|
|
239
|
-
}), [
|
|
240
|
-
hasPagePermission,
|
|
241
|
-
getPagePermissions,
|
|
242
|
-
isEnabled,
|
|
243
|
-
strictMode,
|
|
244
|
-
auditLog,
|
|
245
|
-
getPageAccessHistory,
|
|
246
|
-
clearPageAccessHistory
|
|
247
|
-
]);
|
|
248
|
-
|
|
249
|
-
// Log strict mode violations
|
|
250
|
-
useEffect(() => {
|
|
251
|
-
if (strictMode && auditLog) {
|
|
252
|
-
log.debug('Strict mode enabled - all page access attempts will be logged and enforced');
|
|
253
|
-
}
|
|
254
|
-
}, [strictMode, auditLog]);
|
|
255
|
-
|
|
256
|
-
return (
|
|
257
|
-
<PagePermissionContext.Provider value={contextValue}>
|
|
258
|
-
{children}
|
|
259
|
-
</PagePermissionContext.Provider>
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Hook to use page permission context
|
|
265
|
-
*
|
|
266
|
-
* @returns Page permission context
|
|
267
|
-
* @throws Error if used outside of PagePermissionProvider
|
|
268
|
-
*/
|
|
269
|
-
export function usePagePermissions(): PagePermissionContextType {
|
|
270
|
-
const context = useContext(PagePermissionContext);
|
|
271
|
-
|
|
272
|
-
if (!context) {
|
|
273
|
-
throw new Error('usePagePermissions must be used within a PagePermissionProvider');
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
return context;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
export default PagePermissionProvider;
|
|
@@ -1,312 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Permission Enforcer Component
|
|
3
|
-
* @package @jmruthers/pace-core
|
|
4
|
-
* @module RBAC/Components/PermissionEnforcer
|
|
5
|
-
* @since 2.0.0
|
|
6
|
-
*
|
|
7
|
-
* A component that enforces permissions and prevents apps from bypassing permission checks.
|
|
8
|
-
* This is a critical security component that provides centralized permission enforcement.
|
|
9
|
-
*
|
|
10
|
-
* Features:
|
|
11
|
-
* - Centralized permission enforcement
|
|
12
|
-
* - Strict mode to prevent bypassing
|
|
13
|
-
* - Automatic audit logging
|
|
14
|
-
* - Integration with existing RBAC system
|
|
15
|
-
* - Multiple permission checking
|
|
16
|
-
* - Clear error messages for unauthorized access
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* ```tsx
|
|
20
|
-
* // Basic permission enforcement
|
|
21
|
-
* <PermissionEnforcer
|
|
22
|
-
* permissions={['read:events', 'update:events']}
|
|
23
|
-
* operation="event-management"
|
|
24
|
-
* fallback={<AccessDeniedPage />}
|
|
25
|
-
* >
|
|
26
|
-
* <EventManagementPage />
|
|
27
|
-
* </PermissionEnforcer>
|
|
28
|
-
*
|
|
29
|
-
* // Strict mode (prevents bypassing)
|
|
30
|
-
* <PermissionEnforcer
|
|
31
|
-
* permissions={['admin:system']}
|
|
32
|
-
* operation="system-administration"
|
|
33
|
-
* strictMode={true}
|
|
34
|
-
* fallback={<AccessDeniedPage />}
|
|
35
|
-
* >
|
|
36
|
-
* <SystemAdminPage />
|
|
37
|
-
* </PermissionEnforcer>
|
|
38
|
-
*
|
|
39
|
-
* // With custom fallback
|
|
40
|
-
* <PermissionEnforcer
|
|
41
|
-
* permissions={['update:settings']}
|
|
42
|
-
* operation="settings-update"
|
|
43
|
-
* fallback={<div>You don't have permission to update settings</div>}
|
|
44
|
-
* >
|
|
45
|
-
* <SettingsUpdatePage />
|
|
46
|
-
* </PermissionEnforcer>
|
|
47
|
-
* ```
|
|
48
|
-
*
|
|
49
|
-
* @security
|
|
50
|
-
* - Enforces permissions for all operations
|
|
51
|
-
* - Prevents apps from bypassing permission checks
|
|
52
|
-
* - Automatic audit logging for all permission checks
|
|
53
|
-
* - Integration with existing RBAC system
|
|
54
|
-
* - Clear error messages for unauthorized access
|
|
55
|
-
*
|
|
56
|
-
* @performance
|
|
57
|
-
* - Optimized with useMemo and useCallback
|
|
58
|
-
* - Cached permission checks
|
|
59
|
-
* - Minimal re-renders
|
|
60
|
-
* - Efficient error handling
|
|
61
|
-
*
|
|
62
|
-
* @dependencies
|
|
63
|
-
* - React 19+ - Component framework
|
|
64
|
-
* - useCan hook - Permission checking
|
|
65
|
-
* - useUnifiedAuth - Authentication context
|
|
66
|
-
* - RBAC types - Type definitions
|
|
67
|
-
*/
|
|
68
|
-
|
|
69
|
-
import React, { useMemo, useCallback, useEffect, useState, useRef } from 'react';
|
|
70
|
-
import { useMultiplePermissions } from '../hooks/usePermissions';
|
|
71
|
-
import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
|
|
72
|
-
import { useResolvedScope } from '../hooks/useResolvedScope';
|
|
73
|
-
import { UUID, Permission, Scope } from '../types';
|
|
74
|
-
import { getRBACLogger } from '../config';
|
|
75
|
-
import { createLogger } from '../../utils/core/logger';
|
|
76
|
-
|
|
77
|
-
const log = createLogger('PermissionEnforcer');
|
|
78
|
-
|
|
79
|
-
export interface PermissionEnforcerProps {
|
|
80
|
-
/** Permissions required for access */
|
|
81
|
-
permissions: Permission[];
|
|
82
|
-
|
|
83
|
-
/** Operation being performed */
|
|
84
|
-
operation: string;
|
|
85
|
-
|
|
86
|
-
/** Content to render when user has permission */
|
|
87
|
-
children: React.ReactNode;
|
|
88
|
-
|
|
89
|
-
/** Content to render when user lacks permission */
|
|
90
|
-
fallback?: React.ReactNode;
|
|
91
|
-
|
|
92
|
-
/** Enable strict mode to prevent bypassing (default: true) */
|
|
93
|
-
strictMode?: boolean;
|
|
94
|
-
|
|
95
|
-
/** Force audit logging for this operation (default: true) */
|
|
96
|
-
auditLog?: boolean;
|
|
97
|
-
|
|
98
|
-
/** Custom scope for permission checking */
|
|
99
|
-
scope?: Scope;
|
|
100
|
-
|
|
101
|
-
/** Callback when access is denied */
|
|
102
|
-
onDenied?: (permissions: Permission[], operation: string) => void;
|
|
103
|
-
|
|
104
|
-
/** Loading state content */
|
|
105
|
-
loading?: React.ReactNode;
|
|
106
|
-
|
|
107
|
-
/** Require all permissions (AND) or any permission (OR) */
|
|
108
|
-
requireAll?: boolean;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* PermissionEnforcer - Enforces permissions for operations
|
|
113
|
-
*
|
|
114
|
-
* This component ensures that users can only perform operations they have permission for.
|
|
115
|
-
* It integrates with the existing RBAC system and provides strict enforcement to
|
|
116
|
-
* prevent apps from bypassing permission checks.
|
|
117
|
-
*
|
|
118
|
-
* @param props - Component props
|
|
119
|
-
* @returns React element with permission enforcement
|
|
120
|
-
*/
|
|
121
|
-
export function PermissionEnforcer({
|
|
122
|
-
permissions,
|
|
123
|
-
operation,
|
|
124
|
-
children,
|
|
125
|
-
fallback = <DefaultAccessDenied />,
|
|
126
|
-
strictMode = true,
|
|
127
|
-
auditLog = true,
|
|
128
|
-
scope,
|
|
129
|
-
onDenied,
|
|
130
|
-
loading = <DefaultLoading />,
|
|
131
|
-
requireAll = true
|
|
132
|
-
}: PermissionEnforcerProps) {
|
|
133
|
-
const { user, selectedOrganisation, selectedEvent, supabase } = useUnifiedAuth();
|
|
134
|
-
const [hasChecked, setHasChecked] = useState(false);
|
|
135
|
-
|
|
136
|
-
// Use useResolvedScope hook for consistent scope resolution
|
|
137
|
-
// For event-required apps: selectedOrganisation is null, org derived from event
|
|
138
|
-
// For org-required apps: selectedOrganisation is available, event optional
|
|
139
|
-
const { resolvedScope, isLoading: scopeLoading, error: scopeError } = useResolvedScope({
|
|
140
|
-
supabase,
|
|
141
|
-
selectedOrganisationId: selectedOrganisation?.id || null,
|
|
142
|
-
selectedEventId: selectedEvent?.event_id || null
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// Use provided scope if available, otherwise use resolved scope
|
|
146
|
-
// Extract primitive values to ensure stable reference comparison
|
|
147
|
-
// This prevents useMultiplePermissions from re-checking when scope object reference changes but values are the same
|
|
148
|
-
const scopeToUse = scope || resolvedScope;
|
|
149
|
-
const scopeOrgId = scopeToUse?.organisationId || '';
|
|
150
|
-
const scopeEventId = scopeToUse?.eventId || undefined;
|
|
151
|
-
const scopeAppId = scopeToUse?.appId || undefined;
|
|
152
|
-
|
|
153
|
-
// Memoize effectiveScope using primitive values to ensure stable reference
|
|
154
|
-
// Always create a new scope object from primitive values to prevent reference changes
|
|
155
|
-
const effectiveScope = useMemo(() => {
|
|
156
|
-
const newScope: Scope = {};
|
|
157
|
-
if (scopeOrgId) {
|
|
158
|
-
newScope.organisationId = scopeOrgId;
|
|
159
|
-
}
|
|
160
|
-
if (scopeEventId) {
|
|
161
|
-
newScope.eventId = scopeEventId;
|
|
162
|
-
}
|
|
163
|
-
if (scopeAppId) {
|
|
164
|
-
newScope.appId = scopeAppId;
|
|
165
|
-
}
|
|
166
|
-
return newScope;
|
|
167
|
-
}, [scopeOrgId, scopeEventId, scopeAppId]);
|
|
168
|
-
const checkError = scopeError;
|
|
169
|
-
|
|
170
|
-
// Check all permissions using useMultiplePermissions hook
|
|
171
|
-
const { results: permissionResults, isLoading: permissionsLoading, error: permissionsError } = useMultiplePermissions(
|
|
172
|
-
user?.id || '',
|
|
173
|
-
effectiveScope || { eventId: selectedEvent?.event_id || undefined },
|
|
174
|
-
permissions,
|
|
175
|
-
true // Use cache
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
const isLoading = scopeLoading || permissionsLoading;
|
|
179
|
-
const error = checkError || permissionsError;
|
|
180
|
-
|
|
181
|
-
// Determine if user has required permissions based on requireAll prop
|
|
182
|
-
const hasRequiredPermissions = useMemo((): boolean => {
|
|
183
|
-
if (permissions.length === 0) return true;
|
|
184
|
-
|
|
185
|
-
// If permissionResults is not yet available or empty, deny access
|
|
186
|
-
if (!permissionResults || Object.keys(permissionResults).length === 0) {
|
|
187
|
-
return false;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (requireAll) {
|
|
191
|
-
// User must have ALL permissions
|
|
192
|
-
return Object.values(permissionResults).every(result => result === true);
|
|
193
|
-
} else {
|
|
194
|
-
// User must have ANY permission (default behavior)
|
|
195
|
-
return Object.values(permissionResults).some(result => result === true);
|
|
196
|
-
}
|
|
197
|
-
}, [permissions, permissionResults, requireAll]);
|
|
198
|
-
|
|
199
|
-
// Handle permission check completion
|
|
200
|
-
useEffect(() => {
|
|
201
|
-
if (!isLoading && !error) {
|
|
202
|
-
setHasChecked(true);
|
|
203
|
-
|
|
204
|
-
if (!hasRequiredPermissions && onDenied) {
|
|
205
|
-
onDenied(permissions, operation);
|
|
206
|
-
}
|
|
207
|
-
} else if (error) {
|
|
208
|
-
setHasChecked(true);
|
|
209
|
-
}
|
|
210
|
-
}, [hasRequiredPermissions, isLoading, error, permissions, operation, onDenied]);
|
|
211
|
-
|
|
212
|
-
// Log permission check attempt for audit
|
|
213
|
-
// Only log once per unique permission check result to prevent spam
|
|
214
|
-
// Use the scope primitive values already extracted above
|
|
215
|
-
const permissionsKey = permissions.join(',');
|
|
216
|
-
|
|
217
|
-
const lastLoggedKeyRef = useRef<string | null>(null);
|
|
218
|
-
useEffect(() => {
|
|
219
|
-
if (auditLog && hasChecked && !isLoading) {
|
|
220
|
-
// Create a stable key based on the permission check result values (not object references)
|
|
221
|
-
const logKey = `${operation}-${user?.id}-${permissionsKey}-${hasRequiredPermissions}-${scopeOrgId}-${scopeEventId}-${scopeAppId}`;
|
|
222
|
-
|
|
223
|
-
// Only log if this is a new result (different from last logged)
|
|
224
|
-
if (lastLoggedKeyRef.current !== logKey) {
|
|
225
|
-
lastLoggedKeyRef.current = logKey;
|
|
226
|
-
log.debug('Permission check attempt:', {
|
|
227
|
-
permissions,
|
|
228
|
-
operation,
|
|
229
|
-
userId: user?.id,
|
|
230
|
-
scope: effectiveScope,
|
|
231
|
-
allowed: hasRequiredPermissions,
|
|
232
|
-
requireAll,
|
|
233
|
-
timestamp: new Date().toISOString()
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}, [auditLog, hasChecked, isLoading, permissionsKey, operation, user?.id, scopeOrgId, scopeEventId, scopeAppId, hasRequiredPermissions]);
|
|
238
|
-
|
|
239
|
-
// Handle strict mode violations
|
|
240
|
-
useEffect(() => {
|
|
241
|
-
if (strictMode && hasChecked && !isLoading && !hasRequiredPermissions) {
|
|
242
|
-
const logger = getRBACLogger();
|
|
243
|
-
logger.error(`STRICT MODE VIOLATION: User attempted to perform operation without permission`, {
|
|
244
|
-
permissions,
|
|
245
|
-
operation,
|
|
246
|
-
userId: user?.id,
|
|
247
|
-
scope: effectiveScope,
|
|
248
|
-
requireAll,
|
|
249
|
-
timestamp: new Date().toISOString()
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
}, [strictMode, hasChecked, isLoading, hasRequiredPermissions, permissions, operation, user?.id, effectiveScope, requireAll]);
|
|
253
|
-
|
|
254
|
-
// Show loading state
|
|
255
|
-
if (isLoading || !hasChecked) {
|
|
256
|
-
return <>{loading}</>;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// Show error state
|
|
260
|
-
if (checkError) {
|
|
261
|
-
const logger = getRBACLogger();
|
|
262
|
-
logger.error(`Permission check failed for operation ${operation}:`, checkError);
|
|
263
|
-
return <>{fallback}</>;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// Show access denied
|
|
267
|
-
if (!hasRequiredPermissions) {
|
|
268
|
-
return <>{fallback}</>;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// Show protected content
|
|
272
|
-
return <>{children}</>;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Default access denied component
|
|
277
|
-
*/
|
|
278
|
-
function DefaultAccessDenied() {
|
|
279
|
-
return (
|
|
280
|
-
<div className="flex flex-col items-center justify-center min-h-[200px] p-8 text-center">
|
|
281
|
-
<div className="mb-4">
|
|
282
|
-
<svg className="w-16 h-16 text-acc-500 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
283
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z" />
|
|
284
|
-
</svg>
|
|
285
|
-
</div>
|
|
286
|
-
<h2 className="text-xl font-semibold text-sec-900 mb-2">Access Denied</h2>
|
|
287
|
-
<p className="text-sec-600 mb-4">You don't have permission to perform this operation.</p>
|
|
288
|
-
<button
|
|
289
|
-
onClick={() => window.history.back()}
|
|
290
|
-
className="px-4 py-2 bg-main-600 text-main-50 rounded-md hover:bg-main-700 transition-colors"
|
|
291
|
-
>
|
|
292
|
-
Go Back
|
|
293
|
-
</button>
|
|
294
|
-
</div>
|
|
295
|
-
);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Default loading component
|
|
300
|
-
*/
|
|
301
|
-
function DefaultLoading() {
|
|
302
|
-
return (
|
|
303
|
-
<div className="flex items-center justify-center min-h-[200px] p-8">
|
|
304
|
-
<div className="flex items-center space-x-2">
|
|
305
|
-
<div className="animate-spin rounded-full size-8 border-b-2 border-main-600"></div>
|
|
306
|
-
<span className="text-sec-600">Checking permissions...</span>
|
|
307
|
-
</div>
|
|
308
|
-
</div>
|
|
309
|
-
);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
export default PermissionEnforcer;
|