@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
package/docs/rbac/README.md
CHANGED
|
@@ -12,6 +12,8 @@ The PACE Core RBAC (Role-Based Access Control) system provides comprehensive per
|
|
|
12
12
|
|
|
13
13
|
## 🚨 Critical Rules (Follow These or It Won't Work)
|
|
14
14
|
|
|
15
|
+
> **📋 IMPORTANT**: See [RBAC Contract](./RBAC_CONTRACT.md) for the complete, enforceable contract. This section provides a quick overview.
|
|
16
|
+
|
|
15
17
|
**MANDATORY Setup Steps (in order):**
|
|
16
18
|
|
|
17
19
|
1. **Call `setupRBAC(supabase)` FIRST** - Must be called before any RBAC components or hooks
|
|
@@ -42,6 +44,8 @@ The PACE Core RBAC (Role-Based Access Control) system provides comprehensive per
|
|
|
42
44
|
6. **App name must match exactly** - Environment variable must match `rbac_apps.name` (case-sensitive)
|
|
43
45
|
7. **Never query RBAC tables directly** - Always use `PagePermissionGuard` or RBAC API functions
|
|
44
46
|
|
|
47
|
+
**⚠️ Contract Compliance**: All consuming apps must comply with the [RBAC Contract](./RBAC_CONTRACT.md). Violations will result in build errors.
|
|
48
|
+
|
|
45
49
|
## 🚀 Quick Start
|
|
46
50
|
|
|
47
51
|
```tsx
|
|
@@ -52,7 +56,7 @@ function MyComponent() {
|
|
|
52
56
|
<PagePermissionGuard
|
|
53
57
|
pageName="users"
|
|
54
58
|
operation="read"
|
|
55
|
-
fallback={<
|
|
59
|
+
fallback={<p>Access Denied</p>}
|
|
56
60
|
>
|
|
57
61
|
<UserList />
|
|
58
62
|
</PagePermissionGuard>
|
|
@@ -62,6 +66,8 @@ function MyComponent() {
|
|
|
62
66
|
|
|
63
67
|
## 📖 Core Concepts
|
|
64
68
|
|
|
69
|
+
- **[RBAC Contract](./RBAC_CONTRACT.md)** - ⚠️ **START HERE** - The definitive, enforceable contract (MUST read)
|
|
70
|
+
- **[Migration Guide](./MIGRATION_GUIDE.md)** - Migrating to RBAC Contract v2.0.0
|
|
65
71
|
- **[Quick Start](./quick-start.md)** - Get up and running in 10 minutes (foolproof guide for organisation-based apps)
|
|
66
72
|
- **[Event-Based Apps](./event-based-apps.md)** - Get up and running in 10 minutes (foolproof guide for event-based apps)
|
|
67
73
|
- **[API Reference](./api-reference.md)** - Complete API documentation
|
|
@@ -138,11 +144,14 @@ graph TD
|
|
|
138
144
|
- `useCan` - Check individual permissions
|
|
139
145
|
- `usePermissions` - Get all permissions for a scope
|
|
140
146
|
- `useAccessLevel` - Get user's access level
|
|
141
|
-
- `useMultiplePermissions` - Check multiple permissions efficiently
|
|
147
|
+
- `useMultiplePermissions` - Check multiple permissions efficiently (covers any/all logic)
|
|
148
|
+
- `useResourcePermissions` - Resource CRUD permissions
|
|
149
|
+
- `useSecureSupabase` - Secure Supabase client access
|
|
142
150
|
|
|
143
151
|
### Components
|
|
144
|
-
- `
|
|
145
|
-
- `
|
|
152
|
+
- `PagePermissionGuard` - Page-level permission protection (single canonical component)
|
|
153
|
+
- `NavigationGuard` - Navigation item protection
|
|
154
|
+
- `AccessDenied` - Standard access denied component
|
|
146
155
|
|
|
147
156
|
### Utilities
|
|
148
157
|
- `isPermitted` - Server-side permission checking
|
|
@@ -223,9 +232,9 @@ function UsersPage() {
|
|
|
223
232
|
<PagePermissionGuard
|
|
224
233
|
pageName="users"
|
|
225
234
|
operation="read"
|
|
226
|
-
fallback={<
|
|
235
|
+
fallback={<p>You don't have permission to view this page</p>}
|
|
227
236
|
>
|
|
228
|
-
<
|
|
237
|
+
<section>
|
|
229
238
|
<h1>User Management</h1>
|
|
230
239
|
|
|
231
240
|
{/* Multiple operations on same page */}
|
|
@@ -252,7 +261,7 @@ function UsersPage() {
|
|
|
252
261
|
>
|
|
253
262
|
<DeleteUserButtons />
|
|
254
263
|
</PagePermissionGuard>
|
|
255
|
-
</
|
|
264
|
+
</section>
|
|
256
265
|
</PagePermissionGuard>
|
|
257
266
|
);
|
|
258
267
|
}
|
|
@@ -273,7 +282,7 @@ function AdminPanel() {
|
|
|
273
282
|
<PagePermissionGuard
|
|
274
283
|
pageName="admin"
|
|
275
284
|
operation="read"
|
|
276
|
-
fallback={<
|
|
285
|
+
fallback={<p>Access denied</p>}
|
|
277
286
|
>
|
|
278
287
|
<UserManagement />
|
|
279
288
|
</PagePermissionGuard>
|
|
@@ -379,7 +379,7 @@ export function withPermission(permission: string, fallback?: React.ComponentTyp
|
|
|
379
379
|
|
|
380
380
|
// Usage
|
|
381
381
|
const ProtectedUserList = withPermission('read:users', () => (
|
|
382
|
-
<
|
|
382
|
+
<p>Access denied to user list</p>
|
|
383
383
|
))(UserList);
|
|
384
384
|
|
|
385
385
|
// In component
|
|
@@ -436,16 +436,16 @@ export const useOrganisationPermissions = createPermissionHook('organisations');
|
|
|
436
436
|
function UserManagement({ userId, scope }) {
|
|
437
437
|
const { permissions, isLoading } = useUserPermissions(userId, scope);
|
|
438
438
|
|
|
439
|
-
if (isLoading) return <
|
|
439
|
+
if (isLoading) return <p>Loading permissions...</p>;
|
|
440
440
|
|
|
441
441
|
return (
|
|
442
|
-
<
|
|
442
|
+
<section>
|
|
443
443
|
{permissions.read && <UserList />}
|
|
444
444
|
{permissions.create && <CreateUserButton />}
|
|
445
445
|
{permissions.update && <EditUserButton />}
|
|
446
446
|
{permissions.delete && <DeleteUserButton />}
|
|
447
447
|
{permissions.manage && <UserManagementPanel />}
|
|
448
|
-
</
|
|
448
|
+
</section>
|
|
449
449
|
);
|
|
450
450
|
}
|
|
451
451
|
```
|
|
@@ -647,7 +647,7 @@ import { withPermission } from './permission-decorators';
|
|
|
647
647
|
|
|
648
648
|
// Mock component
|
|
649
649
|
const TestComponent = ({ data }: { data: string }) => (
|
|
650
|
-
<
|
|
650
|
+
<p>Test Component: {data}</p>
|
|
651
651
|
);
|
|
652
652
|
|
|
653
653
|
describe('Permission Decorators', () => {
|
|
@@ -684,7 +684,7 @@ describe('Permission Decorators', () => {
|
|
|
684
684
|
})
|
|
685
685
|
}));
|
|
686
686
|
|
|
687
|
-
const FallbackComponent = () => <
|
|
687
|
+
const FallbackComponent = () => <p>Access Denied</p>;
|
|
688
688
|
const ProtectedComponent = withPermission('read:users', FallbackComponent)(TestComponent);
|
|
689
689
|
|
|
690
690
|
render(
|
|
@@ -44,13 +44,13 @@ function UserActions() {
|
|
|
44
44
|
undefined // No pageId for event-app permissions
|
|
45
45
|
);
|
|
46
46
|
|
|
47
|
-
if (error) return <
|
|
48
|
-
if (isLoading) return <
|
|
47
|
+
if (error) return <p>Error: {error.message}</p>;
|
|
48
|
+
if (isLoading) return <p>Checking permissions...</p>;
|
|
49
49
|
|
|
50
50
|
return (
|
|
51
|
-
<
|
|
51
|
+
<section>
|
|
52
52
|
{can && <EventActions />}
|
|
53
|
-
</
|
|
53
|
+
</section>
|
|
54
54
|
);
|
|
55
55
|
}
|
|
56
56
|
```
|
|
@@ -87,19 +87,19 @@ function PermissionDisplay() {
|
|
|
87
87
|
}
|
|
88
88
|
);
|
|
89
89
|
|
|
90
|
-
if (isLoading) return <
|
|
91
|
-
if (error) return <
|
|
90
|
+
if (isLoading) return <p>Loading permissions...</p>;
|
|
91
|
+
if (error) return <p>Error: {error.message}</p>;
|
|
92
92
|
|
|
93
93
|
return (
|
|
94
|
-
<
|
|
94
|
+
<section>
|
|
95
95
|
<h3>User Permissions</h3>
|
|
96
96
|
{Object.entries(permissions).map(([pageId, operations]) => (
|
|
97
|
-
<
|
|
97
|
+
<p key={pageId}>
|
|
98
98
|
<strong>{pageId}:</strong> {operations.join(', ')}
|
|
99
|
-
</
|
|
99
|
+
</p>
|
|
100
100
|
))}
|
|
101
101
|
<button onClick={refetch}>Refresh</button>
|
|
102
|
-
</
|
|
102
|
+
</section>
|
|
103
103
|
);
|
|
104
104
|
}
|
|
105
105
|
```
|
|
@@ -128,13 +128,13 @@ function AccessLevelDisplay({ userId, scope }) {
|
|
|
128
128
|
scope
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
-
if (isLoading) return <
|
|
132
|
-
if (error) return <
|
|
131
|
+
if (isLoading) return <p>Loading access level...</p>;
|
|
132
|
+
if (error) return <p>Error: {error.message}</p>;
|
|
133
133
|
|
|
134
134
|
return (
|
|
135
|
-
<
|
|
135
|
+
<section>
|
|
136
136
|
<p>Access Level: {accessLevel || 'None'}</p>
|
|
137
|
-
</
|
|
137
|
+
</section>
|
|
138
138
|
);
|
|
139
139
|
}
|
|
140
140
|
```
|
|
@@ -183,12 +183,12 @@ function UserManagement({ userId, scope }) {
|
|
|
183
183
|
}, [checkMultiple, isLoading, userId, scope]);
|
|
184
184
|
|
|
185
185
|
return (
|
|
186
|
-
<
|
|
186
|
+
<section>
|
|
187
187
|
{permissions['read:users'] && <UserList />}
|
|
188
188
|
{permissions['create:users'] && <CreateUserButton />}
|
|
189
189
|
{permissions['update:users'] && <EditUserButton />}
|
|
190
190
|
{permissions['delete:users'] && <DeleteUserButton />}
|
|
191
|
-
</
|
|
191
|
+
</section>
|
|
192
192
|
);
|
|
193
193
|
}
|
|
194
194
|
```
|
|
@@ -221,12 +221,12 @@ import { PermissionEnforcer } from '@jmruthers/pace-core/rbac';
|
|
|
221
221
|
|
|
222
222
|
function UserActions() {
|
|
223
223
|
return (
|
|
224
|
-
<
|
|
224
|
+
<section>
|
|
225
225
|
{/* Pattern 1: PermissionEnforcer - automatic scope resolution */}
|
|
226
226
|
<PermissionEnforcer
|
|
227
227
|
permissions={['read:users']}
|
|
228
228
|
operation="user-management"
|
|
229
|
-
fallback={<
|
|
229
|
+
fallback={<p>Access Denied</p>}
|
|
230
230
|
>
|
|
231
231
|
<UserList />
|
|
232
232
|
</PermissionEnforcer>
|
|
@@ -236,11 +236,11 @@ function UserActions() {
|
|
|
236
236
|
permissions={['create:users', 'update:users']}
|
|
237
237
|
operation="user-crud"
|
|
238
238
|
requireAll={true} // User needs ALL permissions
|
|
239
|
-
fallback={<
|
|
239
|
+
fallback={<p>You need create and update permissions</p>}
|
|
240
240
|
>
|
|
241
241
|
<UserManagementPanel />
|
|
242
242
|
</PermissionEnforcer>
|
|
243
|
-
</
|
|
243
|
+
</section>
|
|
244
244
|
);
|
|
245
245
|
}
|
|
246
246
|
```
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
---
|
|
2
|
+
lastUpdated: 2025-01-28T00:00:00+11:00
|
|
3
|
+
version: 0.6.0
|
|
4
|
+
reviewedBy: rbac-compliance-audit
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Edge Functions RBAC Guide
|
|
8
|
+
|
|
9
|
+
**📚 Related**: [RBAC Compliance Guide](../standards/09-rbac-compliance.md) | [RBAC API Reference](./api-reference.md)
|
|
10
|
+
|
|
11
|
+
This guide explains how to use pace-core RBAC APIs in Edge Functions (Deno serverless functions). **There are NO exceptions** - Edge Functions MUST use pace-core APIs, not custom RBAC helpers.
|
|
12
|
+
|
|
13
|
+
## Why No Exceptions?
|
|
14
|
+
|
|
15
|
+
Edge Functions cannot use React hooks (they're Deno-based serverless functions), but pace-core provides programmatic APIs that work outside React:
|
|
16
|
+
|
|
17
|
+
- ✅ `isPermitted()` - Programmatic permission checking API
|
|
18
|
+
- ✅ `setupRBAC()` - Initialize RBAC engine with Supabase client
|
|
19
|
+
- ✅ `getAccessLevel()` - Get user access level
|
|
20
|
+
- ✅ `getRoleContext()` - Get user role context
|
|
21
|
+
|
|
22
|
+
**Custom RBAC helpers are FORBIDDEN** because they:
|
|
23
|
+
- Bypass security validation
|
|
24
|
+
- Skip audit logging
|
|
25
|
+
- Miss caching optimizations
|
|
26
|
+
- Duplicate functionality that pace-core already provides
|
|
27
|
+
|
|
28
|
+
## Correct Pattern
|
|
29
|
+
|
|
30
|
+
### Step 1: Create Supabase Client
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { createClient } from 'jsr:@supabase/supabase-js@2';
|
|
34
|
+
|
|
35
|
+
Deno.serve(async (req: Request) => {
|
|
36
|
+
// Extract auth token from request
|
|
37
|
+
const authHeader = req.headers.get('Authorization');
|
|
38
|
+
if (!authHeader) {
|
|
39
|
+
return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401 });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Create Supabase client with auth header
|
|
43
|
+
const supabase = createClient(
|
|
44
|
+
Deno.env.get('SUPABASE_URL') ?? '',
|
|
45
|
+
Deno.env.get('SUPABASE_ANON_KEY') ?? '',
|
|
46
|
+
{
|
|
47
|
+
global: {
|
|
48
|
+
headers: { Authorization: authHeader },
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Step 2: Get User from Session
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// Get authenticated user
|
|
58
|
+
const { data: { user }, error: authError } = await supabase.auth.getUser();
|
|
59
|
+
if (authError || !user) {
|
|
60
|
+
return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401 });
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Step 3: Setup RBAC
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// Import pace-core RBAC functions
|
|
68
|
+
import { setupRBAC, isPermitted } from 'npm:@jmruthers/pace-core@^0.6.0/rbac';
|
|
69
|
+
|
|
70
|
+
// Setup RBAC (MUST be called before using isPermitted)
|
|
71
|
+
setupRBAC(supabase);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Step 4: Extract Organisation Context
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
// Extract organisation context from request
|
|
78
|
+
// Option 1: From headers
|
|
79
|
+
const organisationId = req.headers.get('x-organisation-id');
|
|
80
|
+
|
|
81
|
+
// Option 2: From request body
|
|
82
|
+
if (!organisationId) {
|
|
83
|
+
const body = await req.json();
|
|
84
|
+
organisationId = body.organisationId;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Option 3: From query parameters
|
|
88
|
+
if (!organisationId) {
|
|
89
|
+
const url = new URL(req.url);
|
|
90
|
+
organisationId = url.searchParams.get('organisationId');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!organisationId) {
|
|
94
|
+
return new Response(
|
|
95
|
+
JSON.stringify({ error: 'Organisation context required' }),
|
|
96
|
+
{ status: 400 }
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Step 5: Check Permission
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// Check permission using pace-core API
|
|
105
|
+
const hasPermission = await isPermitted({
|
|
106
|
+
userId: user.id,
|
|
107
|
+
scope: {
|
|
108
|
+
organisationId,
|
|
109
|
+
eventId: req.headers.get('x-event-id'), // Optional
|
|
110
|
+
appId: req.headers.get('x-app-id'), // Optional
|
|
111
|
+
},
|
|
112
|
+
permission: 'read:dashboard',
|
|
113
|
+
pageId: 'dashboard' // Optional, but recommended
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
if (!hasPermission) {
|
|
117
|
+
return new Response(
|
|
118
|
+
JSON.stringify({ error: 'Permission denied' }),
|
|
119
|
+
{ status: 403 }
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Proceed with function logic
|
|
124
|
+
return new Response(JSON.stringify({ success: true }));
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Complete Example
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
// supabase/functions/protected-endpoint/index.ts
|
|
132
|
+
import { createClient } from 'jsr:@supabase/supabase-js@2';
|
|
133
|
+
import { setupRBAC, isPermitted } from 'npm:@jmruthers/pace-core@^0.6.0/rbac';
|
|
134
|
+
|
|
135
|
+
Deno.serve(async (req: Request) => {
|
|
136
|
+
try {
|
|
137
|
+
// 1. Extract auth token
|
|
138
|
+
const authHeader = req.headers.get('Authorization');
|
|
139
|
+
if (!authHeader) {
|
|
140
|
+
return new Response(
|
|
141
|
+
JSON.stringify({ error: 'Unauthorized' }),
|
|
142
|
+
{ status: 401, headers: { 'Content-Type': 'application/json' } }
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 2. Create Supabase client
|
|
147
|
+
const supabase = createClient(
|
|
148
|
+
Deno.env.get('SUPABASE_URL') ?? '',
|
|
149
|
+
Deno.env.get('SUPABASE_ANON_KEY') ?? '',
|
|
150
|
+
{
|
|
151
|
+
global: {
|
|
152
|
+
headers: { Authorization: authHeader },
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
// 3. Get authenticated user
|
|
158
|
+
const { data: { user }, error: authError } = await supabase.auth.getUser();
|
|
159
|
+
if (authError || !user) {
|
|
160
|
+
return new Response(
|
|
161
|
+
JSON.stringify({ error: 'Unauthorized' }),
|
|
162
|
+
{ status: 401, headers: { 'Content-Type': 'application/json' } }
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// 4. Setup RBAC
|
|
167
|
+
setupRBAC(supabase);
|
|
168
|
+
|
|
169
|
+
// 5. Extract organisation context
|
|
170
|
+
const organisationId = req.headers.get('x-organisation-id') ||
|
|
171
|
+
(await req.json().catch(() => ({}))).organisationId;
|
|
172
|
+
|
|
173
|
+
if (!organisationId) {
|
|
174
|
+
return new Response(
|
|
175
|
+
JSON.stringify({ error: 'Organisation context required' }),
|
|
176
|
+
{ status: 400, headers: { 'Content-Type': 'application/json' } }
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 6. Check permission
|
|
181
|
+
const hasPermission = await isPermitted({
|
|
182
|
+
userId: user.id,
|
|
183
|
+
scope: { organisationId },
|
|
184
|
+
permission: 'read:dashboard',
|
|
185
|
+
pageId: 'dashboard'
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
if (!hasPermission) {
|
|
189
|
+
return new Response(
|
|
190
|
+
JSON.stringify({ error: 'Permission denied' }),
|
|
191
|
+
{ status: 403, headers: { 'Content-Type': 'application/json' } }
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// 7. Execute function logic
|
|
196
|
+
const result = {
|
|
197
|
+
message: 'Access granted',
|
|
198
|
+
userId: user.id,
|
|
199
|
+
organisationId
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
return new Response(
|
|
203
|
+
JSON.stringify(result),
|
|
204
|
+
{
|
|
205
|
+
status: 200,
|
|
206
|
+
headers: { 'Content-Type': 'application/json' }
|
|
207
|
+
}
|
|
208
|
+
);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error('Edge Function error:', error);
|
|
211
|
+
return new Response(
|
|
212
|
+
JSON.stringify({ error: 'Internal server error' }),
|
|
213
|
+
{ status: 500, headers: { 'Content-Type': 'application/json' } }
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Migration Guide: Removing Custom RBAC Helpers
|
|
220
|
+
|
|
221
|
+
If you have existing Edge Functions with custom RBAC helpers, follow these steps:
|
|
222
|
+
|
|
223
|
+
### Step 1: Identify Custom Helpers
|
|
224
|
+
|
|
225
|
+
Search for custom RBAC helpers:
|
|
226
|
+
```bash
|
|
227
|
+
# Find files with custom RBAC helpers
|
|
228
|
+
grep -r "rbac_check_permission_simplified" supabase/functions/
|
|
229
|
+
grep -r "function.*checkPermission" supabase/functions/
|
|
230
|
+
grep -r "pace-core-compliance-exception" supabase/functions/
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Step 2: Replace Custom Helper with pace-core API
|
|
234
|
+
|
|
235
|
+
**Before (❌ FORBIDDEN):**
|
|
236
|
+
```typescript
|
|
237
|
+
// supabase/functions/_shared/rbac.ts
|
|
238
|
+
export async function checkPermission(
|
|
239
|
+
supabase: SupabaseClient,
|
|
240
|
+
userId: string,
|
|
241
|
+
permission: string,
|
|
242
|
+
organisationId: string
|
|
243
|
+
): Promise<boolean> {
|
|
244
|
+
const { data, error } = await supabase.rpc('rbac_check_permission_simplified', {
|
|
245
|
+
p_user_id: userId,
|
|
246
|
+
p_permission: permission,
|
|
247
|
+
p_organisation_id: organisationId
|
|
248
|
+
});
|
|
249
|
+
return data === true;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// supabase/functions/my-function/index.ts
|
|
253
|
+
import { checkPermission } from '../_shared/rbac.ts';
|
|
254
|
+
|
|
255
|
+
const hasPermission = await checkPermission(supabase, user.id, 'read:dashboard', orgId);
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**After (✅ CORRECT):**
|
|
259
|
+
```typescript
|
|
260
|
+
// supabase/functions/my-function/index.ts
|
|
261
|
+
import { setupRBAC, isPermitted } from 'npm:@jmruthers/pace-core@^0.6.0/rbac';
|
|
262
|
+
|
|
263
|
+
// Setup RBAC once
|
|
264
|
+
setupRBAC(supabase);
|
|
265
|
+
|
|
266
|
+
// Use pace-core API directly
|
|
267
|
+
const hasPermission = await isPermitted({
|
|
268
|
+
userId: user.id,
|
|
269
|
+
scope: { organisationId: orgId },
|
|
270
|
+
permission: 'read:dashboard',
|
|
271
|
+
pageId: 'dashboard'
|
|
272
|
+
});
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Step 3: Remove Custom Helper Files
|
|
276
|
+
|
|
277
|
+
After migrating all Edge Functions:
|
|
278
|
+
```bash
|
|
279
|
+
# Remove custom RBAC helper file
|
|
280
|
+
rm supabase/functions/_shared/rbac.ts
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Step 4: Update All Edge Functions
|
|
284
|
+
|
|
285
|
+
Update each Edge Function to:
|
|
286
|
+
1. Import `setupRBAC` and `isPermitted` from pace-core
|
|
287
|
+
2. Call `setupRBAC(supabase)` before permission checks
|
|
288
|
+
3. Use `isPermitted()` with complete `PermissionCheck` input
|
|
289
|
+
4. Remove imports of custom RBAC helpers
|
|
290
|
+
|
|
291
|
+
## Common Mistakes
|
|
292
|
+
|
|
293
|
+
### ❌ Mistake 1: Calling RPC Directly
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
// ❌ FORBIDDEN
|
|
297
|
+
const { data } = await supabase.rpc('rbac_check_permission_simplified', {
|
|
298
|
+
p_user_id: userId,
|
|
299
|
+
p_permission: 'read:dashboard'
|
|
300
|
+
});
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**Fix:**
|
|
304
|
+
```typescript
|
|
305
|
+
// ✅ CORRECT
|
|
306
|
+
setupRBAC(supabase);
|
|
307
|
+
const hasPermission = await isPermitted({
|
|
308
|
+
userId,
|
|
309
|
+
scope: { organisationId },
|
|
310
|
+
permission: 'read:dashboard'
|
|
311
|
+
});
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### ❌ Mistake 2: Forgetting to Setup RBAC
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
// ❌ FORBIDDEN - Missing setupRBAC
|
|
318
|
+
import { isPermitted } from 'npm:@jmruthers/pace-core@^0.6.0/rbac';
|
|
319
|
+
|
|
320
|
+
const hasPermission = await isPermitted({ ... }); // Will fail!
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**Fix:**
|
|
324
|
+
```typescript
|
|
325
|
+
// ✅ CORRECT
|
|
326
|
+
import { setupRBAC, isPermitted } from 'npm:@jmruthers/pace-core@^0.6.0/rbac';
|
|
327
|
+
|
|
328
|
+
setupRBAC(supabase); // MUST be called first
|
|
329
|
+
const hasPermission = await isPermitted({ ... });
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### ❌ Mistake 3: Missing Organisation Context
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
// ❌ FORBIDDEN - Missing organisationId
|
|
336
|
+
const hasPermission = await isPermitted({
|
|
337
|
+
userId: user.id,
|
|
338
|
+
scope: {}, // Missing organisationId
|
|
339
|
+
permission: 'read:dashboard'
|
|
340
|
+
});
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
**Fix:**
|
|
344
|
+
```typescript
|
|
345
|
+
// ✅ CORRECT
|
|
346
|
+
const organisationId = req.headers.get('x-organisation-id');
|
|
347
|
+
if (!organisationId) {
|
|
348
|
+
return new Response(JSON.stringify({ error: 'Organisation context required' }), { status: 400 });
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const hasPermission = await isPermitted({
|
|
352
|
+
userId: user.id,
|
|
353
|
+
scope: { organisationId },
|
|
354
|
+
permission: 'read:dashboard'
|
|
355
|
+
});
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## Verification Checklist
|
|
359
|
+
|
|
360
|
+
Before deploying Edge Functions, verify:
|
|
361
|
+
|
|
362
|
+
- [ ] `setupRBAC(supabase)` is called before any permission checks
|
|
363
|
+
- [ ] `isPermitted()` is used instead of custom helpers
|
|
364
|
+
- [ ] No direct calls to `rbac_check_permission_simplified` RPC
|
|
365
|
+
- [ ] Organisation context is extracted from request
|
|
366
|
+
- [ ] User authentication is verified before permission checks
|
|
367
|
+
- [ ] Error handling for missing auth/context
|
|
368
|
+
- [ ] No custom RBAC helper files exist
|
|
369
|
+
- [ ] All Edge Functions follow the correct pattern
|
|
370
|
+
|
|
371
|
+
## Related Documentation
|
|
372
|
+
|
|
373
|
+
- [RBAC Compliance Guide](../standards/09-rbac-compliance.md) - Complete compliance rules
|
|
374
|
+
- [RBAC API Reference](./api-reference.md) - Complete API documentation
|
|
375
|
+
- [RBAC Quick Start](./quick-start.md) - Getting started guide
|
|
376
|
+
|
|
@@ -491,7 +491,7 @@ export function Dashboard() {
|
|
|
491
491
|
const { user, selectedEventId, selectedOrganisationId } = useUnifiedAuth()
|
|
492
492
|
|
|
493
493
|
if (!user) {
|
|
494
|
-
return <
|
|
494
|
+
return <p>Please log in</p>
|
|
495
495
|
}
|
|
496
496
|
|
|
497
497
|
return (
|
|
@@ -580,7 +580,7 @@ export function Participants() {
|
|
|
580
580
|
const { user, selectedEventId, selectedOrganisationId } = useUnifiedAuth()
|
|
581
581
|
|
|
582
582
|
if (!user) {
|
|
583
|
-
return <
|
|
583
|
+
return <p>Please log in</p>
|
|
584
584
|
}
|
|
585
585
|
|
|
586
586
|
return (
|
|
@@ -748,7 +748,7 @@ setupRBAC(supabase) // Must be called BEFORE rendering app
|
|
|
748
748
|
setSelectedEventId('your-event-id');
|
|
749
749
|
}, []);
|
|
750
750
|
|
|
751
|
-
return <
|
|
751
|
+
return <p>...</p>;
|
|
752
752
|
}
|
|
753
753
|
```
|
|
754
754
|
|