@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
|
@@ -0,0 +1,724 @@
|
|
|
1
|
+
---
|
|
2
|
+
lastUpdated: 2025-01-04T00:00:00+00:00
|
|
3
|
+
version: 2.0.0
|
|
4
|
+
reviewedBy: rbac-architecture-review
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# RBAC Contract
|
|
8
|
+
|
|
9
|
+
> **📋 The Definitive RBAC Contract** | [← Back to RBAC Documentation](./README.md) | [Quick Start](./quick-start.md) | [Migration Guide](./MIGRATION_GUIDE.md)
|
|
10
|
+
|
|
11
|
+
This document defines the **mandatory contract** between `pace-core` and consuming applications for RBAC (Role-Based Access Control) integration. This contract is **enforced** by ESLint rules.
|
|
12
|
+
|
|
13
|
+
**⚠️ CRITICAL**: This contract is **non-negotiable**. Violations will result in build errors.
|
|
14
|
+
|
|
15
|
+
## Table of Contents
|
|
16
|
+
|
|
17
|
+
1. [The Contract](#the-contract)
|
|
18
|
+
2. [Architecture Boundary](#architecture-boundary)
|
|
19
|
+
3. [Compliance Checklist](#compliance-checklist)
|
|
20
|
+
4. [Anti-Patterns](#anti-patterns)
|
|
21
|
+
5. [Enforcement](#enforcement)
|
|
22
|
+
6. [enforcePermissions Configuration](#enforcepermissions-configuration)
|
|
23
|
+
|
|
24
|
+
## The Contract
|
|
25
|
+
|
|
26
|
+
### What Consuming Apps MUST Do
|
|
27
|
+
|
|
28
|
+
#### 1. **MUST Use pace-core RBAC Components**
|
|
29
|
+
|
|
30
|
+
**MUST use `PagePermissionGuard` for all protected pages:**
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
// ✅ CORRECT
|
|
34
|
+
import { PagePermissionGuard } from '@jmruthers/pace-core/rbac';
|
|
35
|
+
|
|
36
|
+
function DashboardPage() {
|
|
37
|
+
return (
|
|
38
|
+
<PagePermissionGuard pageName="dashboard" operation="read">
|
|
39
|
+
<DashboardContent />
|
|
40
|
+
</PagePermissionGuard>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**MUST use pace-core hooks for permission checks:**
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
// ✅ CORRECT
|
|
49
|
+
import { useCan, useResourcePermissions } from '@jmruthers/pace-core/rbac';
|
|
50
|
+
|
|
51
|
+
function MyComponent() {
|
|
52
|
+
const canEdit = useCan('update:users');
|
|
53
|
+
const permissions = useResourcePermissions('users');
|
|
54
|
+
// ...
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**MUST use pace-core API functions for programmatic checks:**
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
// ✅ CORRECT
|
|
62
|
+
import { isPermitted } from '@jmruthers/pace-core/rbac';
|
|
63
|
+
|
|
64
|
+
const hasAccess = await isPermitted({
|
|
65
|
+
permission: 'read:dashboard',
|
|
66
|
+
pageName: 'dashboard'
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
#### 2. **MUST Use Standard AccessDenied Component**
|
|
71
|
+
|
|
72
|
+
**MUST use `AccessDenied` from pace-core for all access denied scenarios:**
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
// ✅ CORRECT
|
|
76
|
+
import { AccessDenied } from '@jmruthers/pace-core/rbac';
|
|
77
|
+
|
|
78
|
+
<PagePermissionGuard
|
|
79
|
+
pageName="dashboard"
|
|
80
|
+
operation="read"
|
|
81
|
+
fallback={<AccessDenied />}
|
|
82
|
+
>
|
|
83
|
+
<DashboardContent />
|
|
84
|
+
</PagePermissionGuard>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
#### 3. **MUST Configure enforcePermissions Correctly**
|
|
88
|
+
|
|
89
|
+
**For event-based apps (where pages handle their own checks):**
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
// ✅ CORRECT - Event-based app pattern
|
|
93
|
+
<PaceAppLayout
|
|
94
|
+
appName="MyApp"
|
|
95
|
+
enforcePermissions={false} // Pages handle checks via PagePermissionGuard
|
|
96
|
+
// ...
|
|
97
|
+
>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**For organisation-based apps (where layout handles checks):**
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
// ✅ CORRECT - Organisation-based app pattern
|
|
104
|
+
<PaceAppLayout
|
|
105
|
+
appName="MyApp"
|
|
106
|
+
enforcePermissions={true} // Layout handles checks
|
|
107
|
+
defaultPermission="read"
|
|
108
|
+
// ...
|
|
109
|
+
>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### 4. **MUST Use useSecureSupabase for RBAC Table Queries**
|
|
113
|
+
|
|
114
|
+
**If you must query RBAC tables (rare), MUST use `useSecureSupabase`:**
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
// ✅ CORRECT - Using secure client
|
|
118
|
+
import { useSecureSupabase } from '@jmruthers/pace-core/rbac';
|
|
119
|
+
|
|
120
|
+
function MyComponent() {
|
|
121
|
+
const secureSupabase = useSecureSupabase(supabase);
|
|
122
|
+
const { data } = await secureSupabase
|
|
123
|
+
.from('rbac_user_profiles') // Allowed through secure client
|
|
124
|
+
.select('*');
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### What Consuming Apps MUST NOT Do
|
|
129
|
+
|
|
130
|
+
#### 1. **MUST NOT Create Wrapper Components**
|
|
131
|
+
|
|
132
|
+
**MUST NOT create wrapper components around `PagePermissionGuard`:**
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
// ❌ FORBIDDEN - Wrapper component
|
|
136
|
+
function EventPageGuard({ pageName, children }) {
|
|
137
|
+
// Event validation logic...
|
|
138
|
+
return (
|
|
139
|
+
<PagePermissionGuard pageName={pageName}>
|
|
140
|
+
{children}
|
|
141
|
+
</PagePermissionGuard>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Instead, handle validation directly in page components:**
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
// ✅ CORRECT - Direct usage
|
|
150
|
+
function DashboardPage() {
|
|
151
|
+
const { selectedEvent } = useEvents();
|
|
152
|
+
|
|
153
|
+
if (!selectedEvent) {
|
|
154
|
+
return <EventSelectionPrompt />;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<PagePermissionGuard pageName="dashboard" operation="read">
|
|
159
|
+
<DashboardContent />
|
|
160
|
+
</PagePermissionGuard>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
#### 2. **MUST NOT Query RBAC Tables Directly**
|
|
166
|
+
|
|
167
|
+
**MUST NOT query RBAC tables without using `useSecureSupabase`:**
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
// ❌ FORBIDDEN - Direct query
|
|
171
|
+
const { data } = await supabase
|
|
172
|
+
.from('rbac_user_profiles') // ERROR: Direct RBAC table query
|
|
173
|
+
.select('*');
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**MUST NOT query these tables directly:**
|
|
177
|
+
- `rbac_organisation_roles`
|
|
178
|
+
- `rbac_event_app_roles`
|
|
179
|
+
- `rbac_global_roles`
|
|
180
|
+
- `rbac_apps`
|
|
181
|
+
- `rbac_app_pages`
|
|
182
|
+
- `rbac_page_permissions`
|
|
183
|
+
- `rbac_user_profiles`
|
|
184
|
+
|
|
185
|
+
#### 3. **MUST NOT Call RBAC RPC Functions Directly**
|
|
186
|
+
|
|
187
|
+
**MUST NOT call `rbac_check_permission_simplified` directly:**
|
|
188
|
+
|
|
189
|
+
```tsx
|
|
190
|
+
// ❌ FORBIDDEN - Direct RPC call
|
|
191
|
+
const { data } = await supabase.rpc('rbac_check_permission_simplified', {
|
|
192
|
+
p_user_id: userId,
|
|
193
|
+
p_permission: 'read:dashboard'
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Instead, use pace-core API:**
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
// ✅ CORRECT - Using pace-core API
|
|
201
|
+
import { isPermitted } from '@jmruthers/pace-core/rbac';
|
|
202
|
+
const hasAccess = await isPermitted({
|
|
203
|
+
permission: 'read:dashboard',
|
|
204
|
+
pageName: 'dashboard'
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
#### 4. **MUST NOT Create Custom Access Denied Components**
|
|
209
|
+
|
|
210
|
+
**MUST NOT create custom access denied components:**
|
|
211
|
+
|
|
212
|
+
```tsx
|
|
213
|
+
// ❌ FORBIDDEN - Custom component
|
|
214
|
+
function CustomAccessDenied() {
|
|
215
|
+
return <div>Access Denied</div>;
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**MUST use standard component:**
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
// ✅ CORRECT - Standard component
|
|
223
|
+
import { AccessDenied } from '@jmruthers/pace-core/rbac';
|
|
224
|
+
<AccessDenied />
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
#### 5. **MUST NOT Bypass PagePermissionGuard**
|
|
228
|
+
|
|
229
|
+
**MUST NOT render protected content without `PagePermissionGuard`:**
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
// ❌ FORBIDDEN - No guard
|
|
233
|
+
function DashboardPage() {
|
|
234
|
+
return <DashboardContent />; // ERROR: Unprotected page
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**MUST protect all pages:**
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
// ✅ CORRECT - Protected page
|
|
242
|
+
function DashboardPage() {
|
|
243
|
+
return (
|
|
244
|
+
<PagePermissionGuard pageName="dashboard" operation="read">
|
|
245
|
+
<DashboardContent />
|
|
246
|
+
</PagePermissionGuard>
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
#### 6. **MUST NOT Implement Custom Permission Logic**
|
|
252
|
+
|
|
253
|
+
**MUST NOT create custom permission checking functions:**
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
// ❌ FORBIDDEN - Custom permission logic
|
|
257
|
+
function checkPermission(userId, permission) {
|
|
258
|
+
// Custom logic...
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**MUST use pace-core functions:**
|
|
263
|
+
|
|
264
|
+
```tsx
|
|
265
|
+
// ✅ CORRECT - Using pace-core
|
|
266
|
+
import { isPermitted } from '@jmruthers/pace-core/rbac';
|
|
267
|
+
const hasAccess = await isPermitted({ permission: 'read:dashboard' });
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
#### 7. **MUST NOT Use Hardcoded Role Checks**
|
|
271
|
+
|
|
272
|
+
**MUST NOT compare roles directly:**
|
|
273
|
+
|
|
274
|
+
```tsx
|
|
275
|
+
// ❌ FORBIDDEN - Hardcoded role check
|
|
276
|
+
if (user.role === 'admin') {
|
|
277
|
+
// ...
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**MUST use pace-core hooks/APIs:**
|
|
282
|
+
|
|
283
|
+
```tsx
|
|
284
|
+
// ✅ CORRECT - Using pace-core
|
|
285
|
+
import { useAccessLevel } from '@jmruthers/pace-core/rbac';
|
|
286
|
+
const { accessLevel } = useAccessLevel();
|
|
287
|
+
if (accessLevel === 'admin') {
|
|
288
|
+
// ...
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
#### 8. **MUST NOT Use Resource Permission String Literals**
|
|
293
|
+
|
|
294
|
+
**MUST NOT use string literals in `useResourcePermissions`:**
|
|
295
|
+
|
|
296
|
+
```tsx
|
|
297
|
+
// ❌ FORBIDDEN - String literal
|
|
298
|
+
const { canCreate } = useResourcePermissions('journal');
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**MUST use RESOURCE_NAMES constant:**
|
|
302
|
+
|
|
303
|
+
```tsx
|
|
304
|
+
// ✅ CORRECT - Using constant
|
|
305
|
+
import { RESOURCE_NAMES } from '@/config/resource-names';
|
|
306
|
+
const { canCreate } = useResourcePermissions(RESOURCE_NAMES.JOURNAL);
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
#### 9. **MUST NOT Create Permission Wrapper Functions**
|
|
310
|
+
|
|
311
|
+
**MUST NOT create wrapper functions around permission checks:**
|
|
312
|
+
|
|
313
|
+
```tsx
|
|
314
|
+
// ❌ FORBIDDEN - Wrapper function
|
|
315
|
+
const canEdit = (postId: string) => {
|
|
316
|
+
const hasPermission = canUpdate('journal');
|
|
317
|
+
const post = posts.find(p => p.id === postId);
|
|
318
|
+
return hasPermission && !!post;
|
|
319
|
+
};
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**MUST use pace-core hooks directly:**
|
|
323
|
+
|
|
324
|
+
```tsx
|
|
325
|
+
// ✅ CORRECT - Direct usage
|
|
326
|
+
const { canUpdate } = useResourcePermissions(RESOURCE_NAMES.JOURNAL);
|
|
327
|
+
// Use canUpdate directly in components
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### What Consuming Apps MAY Do
|
|
331
|
+
|
|
332
|
+
#### 1. **MAY Configure Navigation**
|
|
333
|
+
|
|
334
|
+
**MAY define navigation configuration:**
|
|
335
|
+
|
|
336
|
+
```tsx
|
|
337
|
+
// ✅ ALLOWED - Navigation configuration
|
|
338
|
+
const navItems = [
|
|
339
|
+
{ label: 'Dashboard', path: '/dashboard' },
|
|
340
|
+
{ label: 'Users', path: '/users' }
|
|
341
|
+
];
|
|
342
|
+
|
|
343
|
+
const routePermissions = {
|
|
344
|
+
'/dashboard': 'read:dashboard',
|
|
345
|
+
'/users': 'read:users'
|
|
346
|
+
};
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
#### 2. **MAY Customize AccessDenied Props**
|
|
350
|
+
|
|
351
|
+
**MAY customize `AccessDenied` component with props:**
|
|
352
|
+
|
|
353
|
+
```tsx
|
|
354
|
+
// ✅ ALLOWED - Customized AccessDenied
|
|
355
|
+
<AccessDenied
|
|
356
|
+
message="You don't have permission to view this page."
|
|
357
|
+
onGoBack={() => navigate('/dashboard')}
|
|
358
|
+
showSignOut={true}
|
|
359
|
+
/>
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
#### 3. **MAY Handle Event Context Validation**
|
|
363
|
+
|
|
364
|
+
**MAY validate event context in page components:**
|
|
365
|
+
|
|
366
|
+
```tsx
|
|
367
|
+
// ✅ ALLOWED - Event context validation
|
|
368
|
+
function DashboardPage() {
|
|
369
|
+
const { selectedEvent } = useEvents();
|
|
370
|
+
|
|
371
|
+
if (!selectedEvent) {
|
|
372
|
+
return <EventSelectionPrompt />;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return (
|
|
376
|
+
<PagePermissionGuard pageName="dashboard" operation="read">
|
|
377
|
+
<DashboardContent />
|
|
378
|
+
</PagePermissionGuard>
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## Architecture Boundary
|
|
384
|
+
|
|
385
|
+
### What Lives in pace-core
|
|
386
|
+
|
|
387
|
+
**Core Permission Logic:**
|
|
388
|
+
- `RBACEngine.isPermitted` - Permission checking algorithm
|
|
389
|
+
- Database RPC calls (`rbac_check_permission_simplified`)
|
|
390
|
+
- Super admin detection
|
|
391
|
+
- Scope resolution logic
|
|
392
|
+
- Cache management
|
|
393
|
+
|
|
394
|
+
**Components:**
|
|
395
|
+
- `PagePermissionGuard` - Standard page protection (single canonical component)
|
|
396
|
+
- `NavigationGuard` - Navigation item protection
|
|
397
|
+
- `AccessDenied` - Standard access denied component
|
|
398
|
+
|
|
399
|
+
**Hooks:**
|
|
400
|
+
- `useCan` - Single permission check
|
|
401
|
+
- `usePermissions` - Permission map retrieval
|
|
402
|
+
- `useMultiplePermissions` - Multiple permission checks (covers any/all logic)
|
|
403
|
+
- `useAccessLevel` - Access level checks
|
|
404
|
+
- `useResourcePermissions` - Resource CRUD permissions
|
|
405
|
+
- `useResolvedScope` - Scope resolution
|
|
406
|
+
- `useSecureSupabase` - Secure Supabase client access
|
|
407
|
+
|
|
408
|
+
**API Functions:**
|
|
409
|
+
- `isPermitted` - Core permission check
|
|
410
|
+
- `getRoleContext` - Role information
|
|
411
|
+
- `setupRBAC` - Initialization
|
|
412
|
+
|
|
413
|
+
**Types and Utilities:**
|
|
414
|
+
- All RBAC types (`Permission`, `Scope`, `RBACRoleContext`)
|
|
415
|
+
- Security validators
|
|
416
|
+
- Cache utilities
|
|
417
|
+
|
|
418
|
+
### What Lives in Consuming Apps
|
|
419
|
+
|
|
420
|
+
**Configuration:**
|
|
421
|
+
- Navigation configuration (`navItems`, `routePermissions`, `pageIdMapping`)
|
|
422
|
+
- App-specific page names and constants
|
|
423
|
+
|
|
424
|
+
**UI Integration:**
|
|
425
|
+
- Permission-aware UI rendering (using pace-core hooks)
|
|
426
|
+
- Custom styling of standard `AccessDenied` component (via props)
|
|
427
|
+
- Event context validation (if needed)
|
|
428
|
+
|
|
429
|
+
## Compliance Checklist
|
|
430
|
+
|
|
431
|
+
Before deploying your application, verify:
|
|
432
|
+
|
|
433
|
+
- [ ] All pages are wrapped with `PagePermissionGuard`
|
|
434
|
+
- [ ] No direct queries to RBAC tables (except through `useSecureSupabase`)
|
|
435
|
+
- [ ] No direct RPC calls to `rbac_check_permission_simplified`
|
|
436
|
+
- [ ] No wrapper components around `PagePermissionGuard`
|
|
437
|
+
- [ ] No custom access denied components
|
|
438
|
+
- [ ] No hardcoded role checks (use `useAccessLevel` or `getRoleContext`)
|
|
439
|
+
- [ ] No custom permission utility functions
|
|
440
|
+
- [ ] No resource permission string literals (use `RESOURCE_NAMES` constants)
|
|
441
|
+
- [ ] No permission wrapper functions (use pace-core hooks directly)
|
|
442
|
+
- [ ] `enforcePermissions` configured correctly for your app type
|
|
443
|
+
- [ ] All access denied scenarios use `AccessDenied` from pace-core
|
|
444
|
+
- [ ] ESLint rules pass without errors
|
|
445
|
+
|
|
446
|
+
## Anti-Patterns
|
|
447
|
+
|
|
448
|
+
### Anti-Pattern 1: Wrapper Components
|
|
449
|
+
|
|
450
|
+
```tsx
|
|
451
|
+
// ❌ ANTI-PATTERN
|
|
452
|
+
function EventPageGuard({ pageName, children }) {
|
|
453
|
+
const { selectedEvent } = useEvents();
|
|
454
|
+
if (!selectedEvent) return <EventSelectionPrompt />;
|
|
455
|
+
return <PagePermissionGuard pageName={pageName}>{children}</PagePermissionGuard>;
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
**Why it's wrong:**
|
|
460
|
+
- Decreases maintainability
|
|
461
|
+
- Adds unnecessary abstraction
|
|
462
|
+
- Duplicates logic that should be in pages
|
|
463
|
+
|
|
464
|
+
**Correct pattern:**
|
|
465
|
+
|
|
466
|
+
```tsx
|
|
467
|
+
// ✅ CORRECT
|
|
468
|
+
function DashboardPage() {
|
|
469
|
+
const { selectedEvent } = useEvents();
|
|
470
|
+
if (!selectedEvent) return <EventSelectionPrompt />;
|
|
471
|
+
return (
|
|
472
|
+
<PagePermissionGuard pageName="dashboard" operation="read">
|
|
473
|
+
<DashboardContent />
|
|
474
|
+
</PagePermissionGuard>
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Anti-Pattern 2: Direct Database Queries
|
|
480
|
+
|
|
481
|
+
```tsx
|
|
482
|
+
// ❌ ANTI-PATTERN
|
|
483
|
+
const { data } = await supabase
|
|
484
|
+
.from('rbac_user_profiles')
|
|
485
|
+
.select('*');
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
**Why it's wrong:**
|
|
489
|
+
- Bypasses security controls
|
|
490
|
+
- Violates architecture boundary
|
|
491
|
+
- Not future-proof
|
|
492
|
+
|
|
493
|
+
**Correct pattern:**
|
|
494
|
+
|
|
495
|
+
```tsx
|
|
496
|
+
// ✅ CORRECT
|
|
497
|
+
import { useSecureSupabase } from '@jmruthers/pace-core/rbac';
|
|
498
|
+
const secureSupabase = useSecureSupabase(supabase);
|
|
499
|
+
const { data } = await secureSupabase
|
|
500
|
+
.from('rbac_user_profiles')
|
|
501
|
+
.select('*');
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### Anti-Pattern 3: Custom Access Denied
|
|
505
|
+
|
|
506
|
+
```tsx
|
|
507
|
+
// ❌ ANTI-PATTERN
|
|
508
|
+
function CustomAccessDenied() {
|
|
509
|
+
return <div>Access Denied</div>;
|
|
510
|
+
}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
**Why it's wrong:**
|
|
514
|
+
- Inconsistent UX across apps
|
|
515
|
+
- Duplicates standard functionality
|
|
516
|
+
- Not maintainable
|
|
517
|
+
|
|
518
|
+
**Correct pattern:**
|
|
519
|
+
|
|
520
|
+
```tsx
|
|
521
|
+
// ✅ CORRECT
|
|
522
|
+
import { AccessDenied } from '@jmruthers/pace-core/rbac';
|
|
523
|
+
<AccessDenied />
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
### Anti-Pattern 4: Hardcoded Role Checks
|
|
527
|
+
|
|
528
|
+
```tsx
|
|
529
|
+
// ❌ ANTI-PATTERN
|
|
530
|
+
if (user.role === 'admin') {
|
|
531
|
+
// ...
|
|
532
|
+
}
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
**Why it's wrong:**
|
|
536
|
+
- Bypasses RBAC system
|
|
537
|
+
- Not maintainable (role names can change)
|
|
538
|
+
- Doesn't respect permission hierarchy
|
|
539
|
+
|
|
540
|
+
**Correct pattern:**
|
|
541
|
+
|
|
542
|
+
```tsx
|
|
543
|
+
// ✅ CORRECT
|
|
544
|
+
import { useAccessLevel } from '@jmruthers/pace-core/rbac';
|
|
545
|
+
const { accessLevel } = useAccessLevel();
|
|
546
|
+
if (accessLevel === 'admin') {
|
|
547
|
+
// ...
|
|
548
|
+
}
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
### Anti-Pattern 5: Custom Permission Utilities
|
|
552
|
+
|
|
553
|
+
```tsx
|
|
554
|
+
// ❌ ANTI-PATTERN
|
|
555
|
+
function checkPermission(userId, permission) {
|
|
556
|
+
// Custom logic...
|
|
557
|
+
}
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
**Why it's wrong:**
|
|
561
|
+
- Duplicates pace-core functionality
|
|
562
|
+
- Bypasses centralized RBAC logic
|
|
563
|
+
- Not future-proof
|
|
564
|
+
|
|
565
|
+
**Correct pattern:**
|
|
566
|
+
|
|
567
|
+
```tsx
|
|
568
|
+
// ✅ CORRECT
|
|
569
|
+
import { isPermitted } from '@jmruthers/pace-core/rbac';
|
|
570
|
+
const hasAccess = await isPermitted({ permission: 'read:dashboard' });
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
### Anti-Pattern 6: Resource Permission String Literals
|
|
574
|
+
|
|
575
|
+
```tsx
|
|
576
|
+
// ❌ ANTI-PATTERN
|
|
577
|
+
const { canCreate } = useResourcePermissions('journal');
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
**Why it's wrong:**
|
|
581
|
+
- Not type-safe (typos possible)
|
|
582
|
+
- No compile-time validation
|
|
583
|
+
- Maintenance burden
|
|
584
|
+
|
|
585
|
+
**Correct pattern:**
|
|
586
|
+
|
|
587
|
+
```tsx
|
|
588
|
+
// ✅ CORRECT
|
|
589
|
+
import { RESOURCE_NAMES } from '@/config/resource-names';
|
|
590
|
+
const { canCreate } = useResourcePermissions(RESOURCE_NAMES.JOURNAL);
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
### Anti-Pattern 7: Permission Wrapper Functions
|
|
594
|
+
|
|
595
|
+
```tsx
|
|
596
|
+
// ❌ ANTI-PATTERN
|
|
597
|
+
const canEdit = (postId: string) => {
|
|
598
|
+
const hasPermission = canUpdate('journal');
|
|
599
|
+
const post = posts.find(p => p.id === postId);
|
|
600
|
+
return hasPermission && !!post;
|
|
601
|
+
};
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
**Why it's wrong:**
|
|
605
|
+
- Unnecessary abstraction layer
|
|
606
|
+
- Encourages bypassing direct pace-core usage
|
|
607
|
+
- Creates maintenance burden
|
|
608
|
+
|
|
609
|
+
**Correct pattern:**
|
|
610
|
+
|
|
611
|
+
```tsx
|
|
612
|
+
// ✅ CORRECT
|
|
613
|
+
const { canUpdate } = useResourcePermissions(RESOURCE_NAMES.JOURNAL);
|
|
614
|
+
// Use canUpdate directly in components
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
## Enforcement
|
|
618
|
+
|
|
619
|
+
### ESLint Rules
|
|
620
|
+
|
|
621
|
+
The following ESLint rules enforce this contract (all as **ERROR** severity):
|
|
622
|
+
|
|
623
|
+
1. **`no-direct-rbac-rpc`** - Detects direct calls to `rbac_check_permission_simplified`
|
|
624
|
+
2. **`no-direct-rbac-tables`** - Detects direct queries to RBAC tables
|
|
625
|
+
3. **`no-bypass-page-guard`** - Detects routes without `PagePermissionGuard`
|
|
626
|
+
4. **`no-custom-access-denied`** - Detects custom access denied components
|
|
627
|
+
5. **`no-hardcoded-role-checks`** - Detects hardcoded role comparisons
|
|
628
|
+
6. **`no-custom-permission-utilities`** - Detects custom permission utility functions
|
|
629
|
+
7. **`no-resource-permission-string-literals`** - Detects string literals in `useResourcePermissions` calls
|
|
630
|
+
8. **`no-permission-wrapper-functions`** - Detects wrapper functions around pace-core permission hooks
|
|
631
|
+
|
|
632
|
+
### Runtime Compliance
|
|
633
|
+
|
|
634
|
+
Use runtime compliance checking in development:
|
|
635
|
+
|
|
636
|
+
```tsx
|
|
637
|
+
import { checkRuntimeCompliance } from '@jmruthers/pace-core/rbac/compliance';
|
|
638
|
+
|
|
639
|
+
// In development mode
|
|
640
|
+
if (import.meta.env.DEV) {
|
|
641
|
+
checkRuntimeCompliance();
|
|
642
|
+
}
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
## enforcePermissions Configuration
|
|
646
|
+
|
|
647
|
+
### When to Use `enforcePermissions={true}`
|
|
648
|
+
|
|
649
|
+
**Use for organisation-based apps** where the layout handles permission checks:
|
|
650
|
+
|
|
651
|
+
```tsx
|
|
652
|
+
<PaceAppLayout
|
|
653
|
+
appName="MyApp"
|
|
654
|
+
enforcePermissions={true} // Layout validates all routes
|
|
655
|
+
defaultPermission="read"
|
|
656
|
+
routePermissions={{
|
|
657
|
+
'/dashboard': 'read:dashboard',
|
|
658
|
+
'/users': 'read:users'
|
|
659
|
+
}}
|
|
660
|
+
>
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
**Benefits:**
|
|
664
|
+
- Automatic route protection
|
|
665
|
+
- Safety net for forgotten permission checks
|
|
666
|
+
- Centralized permission configuration
|
|
667
|
+
|
|
668
|
+
### When to Use `enforcePermissions={false}`
|
|
669
|
+
|
|
670
|
+
**Use for event-based apps** where pages handle their own checks:
|
|
671
|
+
|
|
672
|
+
```tsx
|
|
673
|
+
<PaceAppLayout
|
|
674
|
+
appName="MyApp"
|
|
675
|
+
enforcePermissions={false} // Pages handle checks via PagePermissionGuard
|
|
676
|
+
showEvents={true}
|
|
677
|
+
>
|
|
678
|
+
{/* Pages use PagePermissionGuard directly */}
|
|
679
|
+
</PaceAppLayout>
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
**Why `false` for event-based apps:**
|
|
683
|
+
- Pages need event context validation before permission checks
|
|
684
|
+
- `PagePermissionGuard` handles both event validation and permission checks
|
|
685
|
+
- Avoids redundant checks and timing issues
|
|
686
|
+
|
|
687
|
+
**Pattern:**
|
|
688
|
+
|
|
689
|
+
```tsx
|
|
690
|
+
// Each page handles its own checks
|
|
691
|
+
function DashboardPage() {
|
|
692
|
+
const { selectedEvent } = useEvents();
|
|
693
|
+
if (!selectedEvent) return <EventSelectionPrompt />;
|
|
694
|
+
|
|
695
|
+
return (
|
|
696
|
+
<PagePermissionGuard pageName="dashboard" operation="read">
|
|
697
|
+
<DashboardContent />
|
|
698
|
+
</PagePermissionGuard>
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
## Related Documentation
|
|
704
|
+
|
|
705
|
+
- [RBAC README](./README.md) - Overview and quick start
|
|
706
|
+
- [Quick Start Guide](./quick-start.md) - Step-by-step setup
|
|
707
|
+
- [Event-Based Apps](./event-based-apps.md) - Event-based app patterns
|
|
708
|
+
- [Permission Enforcement](../implementation-guides/permission-enforcement.md) - Detailed enforcement patterns
|
|
709
|
+
- [Migration Guide](./MIGRATION_GUIDE.md) - Migrating to this contract
|
|
710
|
+
|
|
711
|
+
## Support
|
|
712
|
+
|
|
713
|
+
If you have questions about this contract or need clarification:
|
|
714
|
+
|
|
715
|
+
1. Review the [Troubleshooting Guide](./troubleshooting.md)
|
|
716
|
+
2. Check [Examples](./examples.md) for common patterns
|
|
717
|
+
3. Review [API Reference](./api-reference.md) for detailed API documentation
|
|
718
|
+
|
|
719
|
+
---
|
|
720
|
+
|
|
721
|
+
**Last Updated**: 2025-01-04
|
|
722
|
+
**Version**: 2.0.0
|
|
723
|
+
**Status**: Enforced
|
|
724
|
+
|