@jmruthers/pace-core 0.5.184 → 0.5.186
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 +38 -0
- package/README.md +60 -1
- package/core-usage-manifest.json +312 -0
- package/dist/{DataTable-QAB34V6K.js → DataTable-IX2NBUTP.js} +6 -6
- package/dist/{DataTable-Bz8ffqyA.d.ts → DataTable-Z9NLVJh0.d.ts} +1 -1
- package/dist/{index-Bl--n7-T.d.ts → PublicPageProvider-DIzEzwKl.d.ts} +23 -10
- package/dist/{UnifiedAuthProvider-7F6T4B6K.js → UnifiedAuthProvider-A4BCQRJY.js} +4 -2
- package/dist/{UnifiedAuthProvider-F86d7dSi.d.ts → UnifiedAuthProvider-BG0AL5eE.d.ts} +2 -1
- package/dist/{api-ROMBCNKU.js → api-BMFCXVQX.js} +2 -2
- package/dist/{chunk-RA3JUFMW.js → chunk-445GEP27.js} +154 -4
- package/dist/{chunk-RA3JUFMW.js.map → chunk-445GEP27.js.map} +1 -1
- package/dist/{chunk-W22JP75J.js → chunk-DAGICKHT.js} +9 -7
- package/dist/chunk-DAGICKHT.js.map +1 -0
- package/dist/{chunk-FUEYYMX5.js → chunk-FXFJRTKI.js} +24 -3
- package/dist/chunk-FXFJRTKI.js.map +1 -0
- package/dist/{chunk-CSOFYHAG.js → chunk-GRIQLQ52.js} +374 -60
- package/dist/chunk-GRIQLQ52.js.map +1 -0
- package/dist/{chunk-NQPMQGS2.js → chunk-HDCUMOOI.js} +497 -399
- package/dist/chunk-HDCUMOOI.js.map +1 -0
- package/dist/chunk-HESYZWZW.js +388 -0
- package/dist/chunk-HESYZWZW.js.map +1 -0
- package/dist/{chunk-QUVSNGIP.js → chunk-HGPQUCBC.js} +34 -9
- package/dist/{chunk-QUVSNGIP.js.map → chunk-HGPQUCBC.js.map} +1 -1
- package/dist/{chunk-PWAHJW4G.js → chunk-OALXJH4Y.js} +86 -33
- package/dist/chunk-OALXJH4Y.js.map +1 -0
- package/dist/{chunk-MI7HBHN3.js → chunk-TC7D3CR3.js} +89 -9
- package/dist/chunk-TC7D3CR3.js.map +1 -0
- package/dist/chunk-THRPYOFK.js +215 -0
- package/dist/chunk-THRPYOFK.js.map +1 -0
- package/dist/{chunk-M7W4CP3M.js → chunk-U6WNSFX5.js} +2 -1
- package/dist/chunk-U6WNSFX5.js.map +1 -0
- package/dist/{chunk-UHNYIBXL.js → chunk-UQWSHFVX.js} +1 -1
- package/dist/chunk-UQWSHFVX.js.map +1 -0
- package/dist/{chunk-QCDXODCA.js → chunk-XAUHJD3L.js} +2 -2
- package/dist/components.d.ts +182 -6
- package/dist/components.js +157 -11
- package/dist/components.js.map +1 -1
- package/dist/{database.generated-CBmg2950.d.ts → database.generated-DI89OQeI.d.ts} +63 -9
- package/dist/eslint-rules/pace-core-compliance.cjs +406 -0
- package/dist/{file-reference-D06mEEWW.d.ts → file-reference-PRTSLxKx.d.ts} +10 -1
- package/dist/hooks.d.ts +52 -15
- package/dist/hooks.js +12 -22
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +12 -12
- package/dist/index.js +82 -18
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +3 -1
- package/dist/rbac/index.d.ts +206 -15
- package/dist/rbac/index.js +28 -6
- package/dist/timezone-_pgH8qrY.d.ts +530 -0
- package/dist/{types-_x1f4QBF.d.ts → types-DUyCRSTj.d.ts} +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/dist/{usePublicRouteParams-JJczomYq.d.ts → usePublicRouteParams-D71QLlg4.d.ts} +114 -3
- package/dist/utils.d.ts +110 -152
- package/dist/utils.js +128 -138
- package/dist/utils.js.map +1 -1
- package/docs/api/README.md +60 -1
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/Logger.md +178 -0
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +2 -2
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +2 -2
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +5 -5
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/enums/LogLevel.md +54 -0
- package/docs/api/enums/RBACErrorCode.md +1 -1
- package/docs/api/enums/RPCFunction.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CalendarProps.md +18 -2
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/ComplianceResult.md +30 -0
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/DatabaseComplianceResult.md +85 -0
- package/docs/api/interfaces/DatabaseIssue.md +41 -0
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +6 -6
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +48 -8
- package/docs/api/interfaces/FileUploadProps.md +46 -13
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/FormFieldProps.md +1 -1
- package/docs/api/interfaces/FormProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.md +9 -9
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoggerConfig.md +62 -0
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +36 -23
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +11 -11
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProgressProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +6 -6
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/QuickFix.md +52 -0
- package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
- package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
- package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +4 -4
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
- package/docs/api/interfaces/RBACResult.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
- package/docs/api/interfaces/ResourcePermissions.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +7 -7
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +5 -5
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/RuntimeComplianceResult.md +55 -0
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
- package/docs/api/interfaces/SetupIssue.md +41 -0
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/TabsContentProps.md +1 -1
- package/docs/api/interfaces/TabsListProps.md +1 -1
- package/docs/api/interfaces/TabsProps.md +1 -1
- package/docs/api/interfaces/TabsTriggerProps.md +1 -1
- package/docs/api/interfaces/TextareaProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseFormDialogOptions.md +62 -0
- package/docs/api/interfaces/UseFormDialogReturn.md +117 -0
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +746 -50
- package/docs/api-reference/components.md +26 -12
- package/docs/api-reference/hooks.md +111 -0
- package/docs/api-reference/rpc-functions.md +1 -1
- package/docs/api-reference/utilities.md +184 -0
- package/docs/getting-started/installation-guide.md +75 -16
- package/docs/getting-started/quick-start.md +61 -11
- package/docs/implementation-guides/authentication.md +88 -12
- package/docs/implementation-guides/file-reference-system.md +26 -3
- package/docs/implementation-guides/file-upload-storage.md +30 -1
- package/docs/rbac/README.md +1 -0
- package/docs/rbac/compliance/compliance-guide.md +544 -0
- package/docs/rbac/getting-started.md +158 -33
- package/docs/standards/pace-core-compliance.md +432 -0
- package/eslint-config-pace-core.cjs +93 -0
- package/package.json +15 -3
- package/scripts/analyze-bundle.js +232 -0
- package/scripts/build-css.js +56 -0
- package/scripts/build-docs-incremental.js +1015 -0
- package/scripts/check-pace-core-compliance.cjs +2353 -0
- package/scripts/check-pace-core-compliance.js +512 -0
- package/scripts/generate-docs.js +157 -0
- package/scripts/setup-build-cache.js +73 -0
- package/scripts/utils/command-runner.js +131 -0
- package/scripts/utils/env.js +33 -0
- package/scripts/utils/index.js +10 -0
- package/scripts/utils/logger.js +88 -0
- package/scripts/utils/path-helpers.js +37 -0
- package/scripts/validate-formats.js +133 -0
- package/scripts/validate-master.js +155 -0
- package/scripts/validate-pre-publish.js +140 -0
- package/scripts/validate-theme.js +142 -0
- package/src/components/Calendar/Calendar.tsx +8 -1
- package/src/components/Card/Card.tsx +47 -8
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +314 -0
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +126 -0
- package/src/components/DatePickerWithTimezone/README.md +135 -0
- package/src/components/DatePickerWithTimezone/index.ts +10 -0
- package/src/components/DateTimeField/DateTimeField.test.tsx +358 -0
- package/src/components/DateTimeField/DateTimeField.tsx +232 -0
- package/src/components/DateTimeField/README.md +148 -0
- package/src/components/DateTimeField/index.ts +10 -0
- package/src/components/FileUpload/FileUpload.test.tsx +2 -0
- package/src/components/FileUpload/FileUpload.tsx +10 -1
- package/src/components/Header/Header.test.tsx +47 -18
- package/src/components/Header/Header.tsx +22 -7
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +29 -20
- package/src/components/PaceAppLayout/README.md +9 -0
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +37 -8
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +146 -5
- package/src/components/index.ts +8 -0
- package/src/eslint-rules/pace-core-compliance.cjs +406 -0
- package/src/eslint-rules/pace-core-compliance.js +640 -0
- package/src/hooks/__tests__/useFormDialog.test.ts +478 -0
- package/src/hooks/index.ts +5 -0
- package/src/hooks/useFileReference.test.ts +2 -0
- package/src/hooks/useFormDialog.ts +147 -0
- package/src/hooks/usePreventTabReload.ts +106 -0
- package/src/hooks/useSecureDataAccess.ts +2 -2
- package/src/index.ts +27 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +6 -5
- package/src/providers/services/UnifiedAuthProvider.tsx +24 -3
- package/src/rbac/__tests__/rbac-role-isolation.test.ts +456 -0
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +3 -0
- package/src/rbac/compliance/database-validator.ts +165 -0
- package/src/rbac/compliance/index.ts +38 -0
- package/src/rbac/compliance/quick-fix-suggestions.ts +209 -0
- package/src/rbac/compliance/runtime-compliance.ts +77 -0
- package/src/rbac/compliance/setup-validator.ts +131 -0
- package/src/rbac/components/PagePermissionGuard.tsx +8 -64
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +35 -21
- package/src/rbac/docs/event-based-apps.md +285 -0
- package/src/rbac/errors.ts +11 -0
- package/src/rbac/hooks/useRoleManagement.ts +292 -12
- package/src/rbac/index.ts +30 -0
- package/src/services/OrganisationService.ts +4 -0
- package/src/styles/core.css +5 -5
- package/src/types/database.generated.ts +63 -9
- package/src/types/file-reference.ts +9 -0
- package/src/utils/__tests__/timezone.test.ts +345 -0
- package/src/utils/file-reference/__tests__/file-reference.test.ts +60 -4
- package/src/utils/file-reference/index.ts +13 -2
- package/src/utils/formatting/formatDateTimeTimezone.test.ts +167 -0
- package/src/utils/formatting/formatting.ts +179 -0
- package/src/utils/index.ts +27 -1
- package/src/utils/location/index.ts +16 -0
- package/src/utils/location/location.test.ts +286 -0
- package/src/utils/location/location.ts +175 -0
- package/src/utils/security/secureDataAccess.ts +1 -1
- package/src/utils/storage/helpers.ts +68 -0
- package/src/utils/timezone/index.ts +17 -0
- package/src/utils/timezone/timezone.test.ts +349 -0
- package/src/utils/timezone/timezone.ts +281 -0
- package/dist/chunk-CSOFYHAG.js.map +0 -1
- package/dist/chunk-FUEYYMX5.js.map +0 -1
- package/dist/chunk-HKIT6O7W.js +0 -198
- package/dist/chunk-HKIT6O7W.js.map +0 -1
- package/dist/chunk-KUEN3HFB.js +0 -94
- package/dist/chunk-KUEN3HFB.js.map +0 -1
- package/dist/chunk-M7W4CP3M.js.map +0 -1
- package/dist/chunk-MI7HBHN3.js.map +0 -1
- package/dist/chunk-NQPMQGS2.js.map +0 -1
- package/dist/chunk-PWAHJW4G.js.map +0 -1
- package/dist/chunk-UHNYIBXL.js.map +0 -1
- package/dist/chunk-W22JP75J.js.map +0 -1
- package/dist/formatting-5wETwiGF.d.ts +0 -162
- /package/dist/{DataTable-QAB34V6K.js.map → DataTable-IX2NBUTP.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-7F6T4B6K.js.map → UnifiedAuthProvider-A4BCQRJY.js.map} +0 -0
- /package/dist/{api-ROMBCNKU.js.map → api-BMFCXVQX.js.map} +0 -0
- /package/dist/{chunk-QCDXODCA.js.map → chunk-XAUHJD3L.js.map} +0 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quick Fix Suggestions for RBAC/Auth Compliance
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module RBAC/Compliance/QuickFixSuggestions
|
|
5
|
+
* @since 1.0.0
|
|
6
|
+
*
|
|
7
|
+
* This module provides auto-suggest fixes for common RBAC/auth compliance issues.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export interface QuickFix {
|
|
11
|
+
issue: string;
|
|
12
|
+
suggestion: string;
|
|
13
|
+
codeExample?: string;
|
|
14
|
+
migrationSteps?: string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get quick fix suggestions for custom auth code
|
|
19
|
+
*/
|
|
20
|
+
export function getCustomAuthCodeFixes(customCodeName: string, type: 'hook' | 'component' | 'util'): QuickFix {
|
|
21
|
+
const fixes: Record<string, QuickFix> = {
|
|
22
|
+
'useAuth': {
|
|
23
|
+
issue: `Custom ${type} '${customCodeName}' detected`,
|
|
24
|
+
suggestion: `Replace with useUnifiedAuth from pace-core`,
|
|
25
|
+
codeExample: `// Before
|
|
26
|
+
import { useAuth } from './hooks/useAuth';
|
|
27
|
+
|
|
28
|
+
// After
|
|
29
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core';`,
|
|
30
|
+
migrationSteps: [
|
|
31
|
+
'Remove custom useAuth hook',
|
|
32
|
+
'Import useUnifiedAuth from @jmruthers/pace-core',
|
|
33
|
+
'Update all usages to use useUnifiedAuth',
|
|
34
|
+
'Ensure UnifiedAuthProvider wraps your app'
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
'usePermissions': {
|
|
38
|
+
issue: `Custom ${type} '${customCodeName}' detected`,
|
|
39
|
+
suggestion: `Replace with usePermissions from pace-core`,
|
|
40
|
+
codeExample: `// Before
|
|
41
|
+
import { usePermissions } from './hooks/usePermissions';
|
|
42
|
+
|
|
43
|
+
// After
|
|
44
|
+
import { usePermissions } from '@jmruthers/pace-core/rbac';`,
|
|
45
|
+
migrationSteps: [
|
|
46
|
+
'Remove custom usePermissions hook',
|
|
47
|
+
'Import usePermissions from @jmruthers/pace-core/rbac',
|
|
48
|
+
'Update all usages - ensure setupRBAC() has been called',
|
|
49
|
+
'Verify provider hierarchy is correct'
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
'PermissionGuard': {
|
|
53
|
+
issue: `Custom ${type} '${customCodeName}' detected`,
|
|
54
|
+
suggestion: `Replace with PagePermissionGuard from pace-core`,
|
|
55
|
+
codeExample: `// Before
|
|
56
|
+
import { PermissionGuard } from './components/PermissionGuard';
|
|
57
|
+
|
|
58
|
+
// After
|
|
59
|
+
import { PagePermissionGuard } from '@jmruthers/pace-core/rbac';`,
|
|
60
|
+
migrationSteps: [
|
|
61
|
+
'Remove custom PermissionGuard component',
|
|
62
|
+
'Import PagePermissionGuard from @jmruthers/pace-core/rbac',
|
|
63
|
+
'Wrap pages with PagePermissionGuard',
|
|
64
|
+
'Use pageName and operation props instead of custom permission strings'
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
'checkPermission': {
|
|
68
|
+
issue: `Custom ${type} '${customCodeName}' detected`,
|
|
69
|
+
suggestion: `Replace with isPermitted from pace-core`,
|
|
70
|
+
codeExample: `// Before
|
|
71
|
+
import { checkPermission } from './utils/permissions';
|
|
72
|
+
|
|
73
|
+
// After
|
|
74
|
+
import { isPermitted } from '@jmruthers/pace-core/rbac';`,
|
|
75
|
+
migrationSteps: [
|
|
76
|
+
'Remove custom checkPermission utility',
|
|
77
|
+
'Import isPermitted from @jmruthers/pace-core/rbac',
|
|
78
|
+
'Update all usages to use isPermitted with proper scope',
|
|
79
|
+
'Ensure setupRBAC() has been called'
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return fixes[customCodeName] || {
|
|
85
|
+
issue: `Custom ${type} '${customCodeName}' detected`,
|
|
86
|
+
suggestion: `Use pace-core's equivalent instead. Check @jmruthers/pace-core documentation for the correct import.`,
|
|
87
|
+
migrationSteps: [
|
|
88
|
+
`Remove custom ${customCodeName} ${type}`,
|
|
89
|
+
'Find equivalent in pace-core',
|
|
90
|
+
'Import from @jmruthers/pace-core or @jmruthers/pace-core/rbac',
|
|
91
|
+
'Update all usages'
|
|
92
|
+
]
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get quick fix suggestions for duplicate Supabase config
|
|
98
|
+
*/
|
|
99
|
+
export function getDuplicateConfigFixes(): QuickFix {
|
|
100
|
+
return {
|
|
101
|
+
issue: 'Multiple Supabase client instantiations found',
|
|
102
|
+
suggestion: 'Consolidate to a single Supabase client configuration',
|
|
103
|
+
codeExample: `// Before - Multiple createClient calls
|
|
104
|
+
// src/lib/supabase.ts
|
|
105
|
+
export const supabase = createClient(url, key);
|
|
106
|
+
|
|
107
|
+
// src/utils/api.ts
|
|
108
|
+
export const supabase = createClient(url, key);
|
|
109
|
+
|
|
110
|
+
// After - Single configuration
|
|
111
|
+
// src/lib/supabase.ts
|
|
112
|
+
export const supabase = createClient(
|
|
113
|
+
import.meta.env.VITE_SUPABASE_URL,
|
|
114
|
+
import.meta.env.VITE_SUPABASE_ANON_KEY
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
// src/utils/api.ts
|
|
118
|
+
import { supabase } from '../lib/supabase';`,
|
|
119
|
+
migrationSteps: [
|
|
120
|
+
'Create a single supabase.ts file in a shared location (e.g., src/lib/supabase.ts)',
|
|
121
|
+
'Move all Supabase client creation to this file',
|
|
122
|
+
'Export the client instance',
|
|
123
|
+
'Update all files to import from the shared location',
|
|
124
|
+
'Remove duplicate createClient calls'
|
|
125
|
+
]
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get quick fix suggestions for unprotected pages
|
|
131
|
+
*/
|
|
132
|
+
export function getUnprotectedPageFixes(): QuickFix {
|
|
133
|
+
return {
|
|
134
|
+
issue: 'Route/page found without PagePermissionGuard',
|
|
135
|
+
suggestion: 'Wrap all routes with PagePermissionGuard',
|
|
136
|
+
codeExample: `// Before
|
|
137
|
+
<Route path="/dashboard" element={<Dashboard />} />
|
|
138
|
+
|
|
139
|
+
// After
|
|
140
|
+
<Route
|
|
141
|
+
path="/dashboard"
|
|
142
|
+
element={
|
|
143
|
+
<PagePermissionGuard pageName="dashboard" operation="read">
|
|
144
|
+
<Dashboard />
|
|
145
|
+
</PagePermissionGuard>
|
|
146
|
+
}
|
|
147
|
+
/>`,
|
|
148
|
+
migrationSteps: [
|
|
149
|
+
'Import PagePermissionGuard from @jmruthers/pace-core/rbac',
|
|
150
|
+
'Wrap each route/page component with PagePermissionGuard',
|
|
151
|
+
'Set pageName prop to match your page name in rbac_app_pages table',
|
|
152
|
+
'Set operation prop (read, create, update, or delete)',
|
|
153
|
+
'Ensure setupRBAC() has been called and providers are set up correctly'
|
|
154
|
+
]
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get quick fix suggestions for direct Supabase auth usage
|
|
160
|
+
*/
|
|
161
|
+
export function getDirectSupabaseAuthFixes(): QuickFix {
|
|
162
|
+
return {
|
|
163
|
+
issue: 'Direct Supabase auth usage detected',
|
|
164
|
+
suggestion: 'Use UnifiedAuthProvider and useUnifiedAuth from pace-core',
|
|
165
|
+
codeExample: `// Before
|
|
166
|
+
import { createClient } from '@supabase/supabase-js';
|
|
167
|
+
const supabase = createClient(url, key);
|
|
168
|
+
await supabase.auth.signInWithPassword({ email, password });
|
|
169
|
+
|
|
170
|
+
// After
|
|
171
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core';
|
|
172
|
+
const { signIn } = useUnifiedAuth();
|
|
173
|
+
await signIn({ email, password });`,
|
|
174
|
+
migrationSteps: [
|
|
175
|
+
'Remove direct Supabase auth calls',
|
|
176
|
+
'Import useUnifiedAuth from @jmruthers/pace-core',
|
|
177
|
+
'Use the auth methods from useUnifiedAuth hook',
|
|
178
|
+
'Ensure UnifiedAuthProvider wraps your app',
|
|
179
|
+
'Update all auth-related code to use pace-core hooks'
|
|
180
|
+
]
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Get all quick fix suggestions for a compliance issue
|
|
186
|
+
*/
|
|
187
|
+
export function getQuickFixes(issueType: string, details?: Record<string, any>): QuickFix[] {
|
|
188
|
+
const fixes: QuickFix[] = [];
|
|
189
|
+
|
|
190
|
+
switch (issueType) {
|
|
191
|
+
case 'custom-auth-code':
|
|
192
|
+
if (details?.name && details?.type) {
|
|
193
|
+
fixes.push(getCustomAuthCodeFixes(details.name, details.type));
|
|
194
|
+
}
|
|
195
|
+
break;
|
|
196
|
+
case 'duplicate-config':
|
|
197
|
+
fixes.push(getDuplicateConfigFixes());
|
|
198
|
+
break;
|
|
199
|
+
case 'unprotected-pages':
|
|
200
|
+
fixes.push(getUnprotectedPageFixes());
|
|
201
|
+
break;
|
|
202
|
+
case 'direct-supabase-auth':
|
|
203
|
+
fixes.push(getDirectSupabaseAuthFixes());
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return fixes;
|
|
208
|
+
}
|
|
209
|
+
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime Compliance Checking
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module RBAC/Compliance/RuntimeCompliance
|
|
5
|
+
* @since 1.0.0
|
|
6
|
+
*
|
|
7
|
+
* This module provides runtime compliance checking utilities.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { validateRBACSetup, SetupIssue } from './setup-validator';
|
|
11
|
+
import { getRBACLogger } from '../config';
|
|
12
|
+
|
|
13
|
+
export interface RuntimeComplianceResult {
|
|
14
|
+
setup: {
|
|
15
|
+
isCompliant: boolean;
|
|
16
|
+
issues: SetupIssue[];
|
|
17
|
+
};
|
|
18
|
+
warnings: string[];
|
|
19
|
+
providerContext?: {
|
|
20
|
+
available: boolean;
|
|
21
|
+
message?: string;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check runtime compliance
|
|
27
|
+
*
|
|
28
|
+
* This function checks if the RBAC system is properly set up and logs warnings
|
|
29
|
+
* to the console if issues are found. This is intended for development-time
|
|
30
|
+
* validation only.
|
|
31
|
+
*
|
|
32
|
+
* @returns Runtime compliance result
|
|
33
|
+
*/
|
|
34
|
+
export function checkRuntimeCompliance(): RuntimeComplianceResult {
|
|
35
|
+
const logger = getRBACLogger();
|
|
36
|
+
const setupValidation = validateRBACSetup();
|
|
37
|
+
const warnings: string[] = [];
|
|
38
|
+
|
|
39
|
+
if (!setupValidation.isCompliant) {
|
|
40
|
+
setupValidation.issues.forEach(issue => {
|
|
41
|
+
const warning = `[RBAC Compliance] ${issue.message}\n Recommendation: ${issue.recommendation}`;
|
|
42
|
+
warnings.push(warning);
|
|
43
|
+
logger.warn(warning);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Check for provider context issues
|
|
48
|
+
const providerContextIssues = setupValidation.issues.filter(
|
|
49
|
+
issue => issue.type === 'missing-provider-context' || issue.type === 'not-initialized'
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const providerContext = providerContextIssues.length > 0 ? {
|
|
53
|
+
available: false,
|
|
54
|
+
message: 'UnifiedAuthProvider context may not be available. Ensure your app is wrapped with UnifiedAuthProvider from @jmruthers/pace-core.'
|
|
55
|
+
} : {
|
|
56
|
+
available: true
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
setup: setupValidation,
|
|
61
|
+
warnings,
|
|
62
|
+
providerContext
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Validate and warn about RBAC setup issues
|
|
68
|
+
*
|
|
69
|
+
* This is a convenience function that checks compliance and logs warnings.
|
|
70
|
+
* Call this in development mode to get early warnings about setup issues.
|
|
71
|
+
*/
|
|
72
|
+
export function validateAndWarn(): void {
|
|
73
|
+
if (import.meta.env.MODE === 'development' || import.meta.env.DEV) {
|
|
74
|
+
checkRuntimeCompliance();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RBAC Setup Validator
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module RBAC/Compliance/SetupValidator
|
|
5
|
+
* @since 1.0.0
|
|
6
|
+
*
|
|
7
|
+
* This module provides utilities to validate RBAC setup state.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { getRBACConfig } from '../config';
|
|
11
|
+
import { RBACNotInitializedError } from '../errors';
|
|
12
|
+
|
|
13
|
+
export interface SetupIssue {
|
|
14
|
+
type: 'not-initialized' | 'missing-config' | 'invalid-config' | 'missing-provider-context';
|
|
15
|
+
message: string;
|
|
16
|
+
recommendation: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ComplianceResult {
|
|
20
|
+
isCompliant: boolean;
|
|
21
|
+
issues: SetupIssue[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Check if RBAC system is initialized
|
|
26
|
+
*
|
|
27
|
+
* @returns true if RBAC is initialized, false otherwise
|
|
28
|
+
*/
|
|
29
|
+
export function isRBACInitialized(): boolean {
|
|
30
|
+
try {
|
|
31
|
+
const config = getRBACConfig();
|
|
32
|
+
return config !== null && config.supabase !== null;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
if (error instanceof RBACNotInitializedError) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
// Re-throw unexpected errors
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get setup issues
|
|
44
|
+
*
|
|
45
|
+
* @returns Array of setup issues
|
|
46
|
+
*/
|
|
47
|
+
export function getSetupIssues(): SetupIssue[] {
|
|
48
|
+
const issues: SetupIssue[] = [];
|
|
49
|
+
|
|
50
|
+
const config = getRBACConfig();
|
|
51
|
+
|
|
52
|
+
if (!config) {
|
|
53
|
+
issues.push({
|
|
54
|
+
type: 'not-initialized',
|
|
55
|
+
message: 'RBAC system has not been initialized. setupRBAC() has not been called.',
|
|
56
|
+
recommendation: 'Call setupRBAC(supabase) before using any RBAC features. This should be done in your main entry point (main.tsx or App.tsx) before rendering the app.'
|
|
57
|
+
});
|
|
58
|
+
return issues;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!config.supabase) {
|
|
62
|
+
issues.push({
|
|
63
|
+
type: 'missing-config',
|
|
64
|
+
message: 'RBAC configuration is missing Supabase client.',
|
|
65
|
+
recommendation: 'Ensure setupRBAC() is called with a valid Supabase client instance.'
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return issues;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Check if UnifiedAuthProvider context is available
|
|
74
|
+
*
|
|
75
|
+
* This function can be called from React components to check if the context
|
|
76
|
+
* is available. It uses React's useContext hook, so it must be called from
|
|
77
|
+
* within a React component.
|
|
78
|
+
*
|
|
79
|
+
* @returns true if context is available, false otherwise
|
|
80
|
+
* @throws Error if called outside React component context
|
|
81
|
+
*/
|
|
82
|
+
export function isUnifiedAuthContextAvailable(): boolean {
|
|
83
|
+
try {
|
|
84
|
+
// This will only work if called from within a React component
|
|
85
|
+
// We can't import useContext here directly as it would require React
|
|
86
|
+
// Instead, we'll check if the context can be accessed
|
|
87
|
+
// Note: This is a best-effort check and may not work in all scenarios
|
|
88
|
+
return true; // Context availability is checked by useUnifiedAuth hook itself
|
|
89
|
+
} catch (error) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get provider context setup issues
|
|
96
|
+
*
|
|
97
|
+
* This provides guidance on common provider setup problems.
|
|
98
|
+
* Actual context availability must be checked at component level.
|
|
99
|
+
*
|
|
100
|
+
* @returns Array of setup issues related to provider context
|
|
101
|
+
*/
|
|
102
|
+
export function getProviderContextIssues(): SetupIssue[] {
|
|
103
|
+
const issues: SetupIssue[] = [];
|
|
104
|
+
|
|
105
|
+
// Check if RBAC is initialized (prerequisite for provider context)
|
|
106
|
+
if (!isRBACInitialized()) {
|
|
107
|
+
issues.push({
|
|
108
|
+
type: 'not-initialized',
|
|
109
|
+
message: 'RBAC system must be initialized before provider context can be used.',
|
|
110
|
+
recommendation: 'Call setupRBAC(supabase) before rendering UnifiedAuthProvider.'
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return issues;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Validate RBAC setup
|
|
119
|
+
*
|
|
120
|
+
* @returns Compliance result with issues and recommendations
|
|
121
|
+
*/
|
|
122
|
+
export function validateRBACSetup(): ComplianceResult {
|
|
123
|
+
const issues = getSetupIssues();
|
|
124
|
+
const providerIssues = getProviderContextIssues();
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
isCompliant: issues.length === 0 && providerIssues.length === 0,
|
|
128
|
+
issues: [...issues, ...providerIssues]
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
@@ -72,7 +72,6 @@ import { useCan } from '../hooks';
|
|
|
72
72
|
import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
|
|
73
73
|
import { UUID, Permission, Scope } from '../types';
|
|
74
74
|
import { createScopeFromEvent } from '../utils/eventContext';
|
|
75
|
-
import { getCurrentAppName } from '../../utils/app/appNameResolver';
|
|
76
75
|
import { getRBACLogger } from '../config';
|
|
77
76
|
|
|
78
77
|
export interface PagePermissionGuardProps {
|
|
@@ -129,16 +128,16 @@ const PagePermissionGuardComponent = ({
|
|
|
129
128
|
onDenied,
|
|
130
129
|
loading = <DefaultLoading />
|
|
131
130
|
}: PagePermissionGuardProps) => {
|
|
132
|
-
// Generate a unique instance ID for debugging
|
|
133
|
-
const instanceId = useMemo(() => Math.random().toString(36).substr(2, 9), []);
|
|
134
|
-
|
|
135
131
|
// Track render count for debugging
|
|
136
132
|
const renderCountRef = useRef(0);
|
|
137
133
|
renderCountRef.current += 1;
|
|
138
134
|
|
|
135
|
+
// Generate a unique instance ID for debugging (must be called before any conditional returns)
|
|
136
|
+
const instanceId = useMemo(() => Math.random().toString(36).substr(2, 9), []);
|
|
139
137
|
|
|
140
|
-
|
|
141
|
-
|
|
138
|
+
// Use UnifiedAuth hook - if context is not available, it will throw and ErrorBoundary will handle it
|
|
139
|
+
// This is better than checking for context and returning early, which causes infinite loops
|
|
140
|
+
const { user, selectedOrganisation, selectedEvent, supabase, appId: contextAppId } = useUnifiedAuth();
|
|
142
141
|
|
|
143
142
|
const [hasChecked, setHasChecked] = useState(false);
|
|
144
143
|
const [checkError, setCheckError] = useState<Error | null>(null);
|
|
@@ -179,64 +178,9 @@ const PagePermissionGuardComponent = ({
|
|
|
179
178
|
return;
|
|
180
179
|
}
|
|
181
180
|
|
|
182
|
-
// Get app ID from
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
// Try to resolve from database
|
|
186
|
-
if (supabaseRef.current) {
|
|
187
|
-
const appName = getCurrentAppName();
|
|
188
|
-
if (appName) {
|
|
189
|
-
try {
|
|
190
|
-
const { data: app, error } = await supabaseRef.current
|
|
191
|
-
.from('rbac_apps')
|
|
192
|
-
.select('id, name, is_active')
|
|
193
|
-
.eq('name', appName)
|
|
194
|
-
.eq('is_active', true)
|
|
195
|
-
.single() as { data: { id: string; name: string; is_active: boolean } | null; error: any };
|
|
196
|
-
|
|
197
|
-
if (signal.aborted) {
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (error) {
|
|
202
|
-
const logger = getRBACLogger();
|
|
203
|
-
logger.error('Database error resolving app ID:', error);
|
|
204
|
-
if (signal.aborted) {
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
const { data: inactiveApp } = await supabaseRef.current
|
|
208
|
-
.from('rbac_apps')
|
|
209
|
-
.select('id, name, is_active')
|
|
210
|
-
.eq('name', appName)
|
|
211
|
-
.single() as { data: { id: string; name: string; is_active: boolean } | null };
|
|
212
|
-
|
|
213
|
-
if (signal.aborted) {
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (inactiveApp) {
|
|
218
|
-
logger.error(`App "${appName}" exists but is inactive (is_active: ${inactiveApp.is_active})`);
|
|
219
|
-
} else {
|
|
220
|
-
logger.error(`App "${appName}" not found in rbac_apps table`);
|
|
221
|
-
}
|
|
222
|
-
} else if (app) {
|
|
223
|
-
appId = app.id;
|
|
224
|
-
} else {
|
|
225
|
-
const logger = getRBACLogger();
|
|
226
|
-
logger.error('No app data returned for:', appName);
|
|
227
|
-
}
|
|
228
|
-
} catch (error) {
|
|
229
|
-
if (signal.aborted) {
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
const logger = getRBACLogger();
|
|
233
|
-
logger.error('Unexpected error resolving app ID:', error);
|
|
234
|
-
}
|
|
235
|
-
} else {
|
|
236
|
-
const logger = getRBACLogger();
|
|
237
|
-
logger.error('No app name found. Make sure to call setRBACAppName() in your app setup.');
|
|
238
|
-
}
|
|
239
|
-
}
|
|
181
|
+
// Get app ID from UnifiedAuth context (already resolved on login)
|
|
182
|
+
// This is much faster than querying the database
|
|
183
|
+
const appId = contextAppId;
|
|
240
184
|
|
|
241
185
|
if (signal.aborted) {
|
|
242
186
|
return;
|
|
@@ -80,6 +80,7 @@ describe('PagePermissionGuard Component', () => {
|
|
|
80
80
|
user: mockUser,
|
|
81
81
|
selectedOrganisation: { id: 'org-123' },
|
|
82
82
|
selectedEvent: { event_id: 'event-123' },
|
|
83
|
+
appId: 'app-123', // Required for scope resolution
|
|
83
84
|
supabase: {
|
|
84
85
|
from: vi.fn().mockReturnValue({
|
|
85
86
|
select: vi.fn().mockReturnValue({
|
|
@@ -343,14 +344,17 @@ describe('PagePermissionGuard Component', () => {
|
|
|
343
344
|
</PagePermissionGuard>
|
|
344
345
|
);
|
|
345
346
|
|
|
347
|
+
// When there's an error and no permission, component shows fallback
|
|
346
348
|
await waitFor(() => {
|
|
347
|
-
expect(screen.
|
|
348
|
-
}, { interval: 10 });
|
|
349
|
+
expect(screen.getByTestId('test-fallback')).toBeInTheDocument();
|
|
350
|
+
}, { interval: 10, timeout: 2000 });
|
|
349
351
|
});
|
|
350
352
|
});
|
|
351
353
|
|
|
352
354
|
describe('App ID Resolution', () => {
|
|
353
355
|
it('resolves app ID from database', async () => {
|
|
356
|
+
// When appId is already provided from useUnifiedAuth, getCurrentAppName is not called
|
|
357
|
+
// The component uses appId from context directly
|
|
354
358
|
mockUseCan.mockReturnValue({
|
|
355
359
|
can: true,
|
|
356
360
|
isLoading: false,
|
|
@@ -368,9 +372,11 @@ describe('PagePermissionGuard Component', () => {
|
|
|
368
372
|
|
|
369
373
|
await waitFor(() => {
|
|
370
374
|
expect(screen.getByTestId('test-component')).toBeInTheDocument();
|
|
371
|
-
}, { interval: 10 });
|
|
375
|
+
}, { interval: 10, timeout: 2000 });
|
|
372
376
|
|
|
373
|
-
|
|
377
|
+
// appId is provided from useUnifiedAuth context, so getCurrentAppName is not called
|
|
378
|
+
// The component uses contextAppId directly (line 183 of PagePermissionGuard)
|
|
379
|
+
expect(mockGetCurrentAppName).not.toHaveBeenCalled();
|
|
374
380
|
});
|
|
375
381
|
|
|
376
382
|
it('handles app resolution errors in test environment', async () => {
|
|
@@ -432,10 +438,12 @@ describe('PagePermissionGuard Component', () => {
|
|
|
432
438
|
mockGetCurrentAppName.mockReturnValue('test-app');
|
|
433
439
|
|
|
434
440
|
// Mock database returning invalid app ID
|
|
441
|
+
// In test mode, validation is skipped, so component should work normally
|
|
435
442
|
mockUseUnifiedAuthFn.mockReturnValue({
|
|
436
443
|
user: mockUser,
|
|
437
444
|
selectedOrganisation: { id: 'org-123' },
|
|
438
445
|
selectedEvent: { event_id: 'event-123' },
|
|
446
|
+
appId: 'app-123',
|
|
439
447
|
supabase: {
|
|
440
448
|
from: vi.fn().mockReturnValue({
|
|
441
449
|
select: vi.fn().mockReturnValue({
|
|
@@ -452,9 +460,11 @@ describe('PagePermissionGuard Component', () => {
|
|
|
452
460
|
} as any
|
|
453
461
|
});
|
|
454
462
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
463
|
+
mockUseCan.mockReturnValue({
|
|
464
|
+
can: true,
|
|
465
|
+
isLoading: false,
|
|
466
|
+
error: null
|
|
467
|
+
});
|
|
458
468
|
|
|
459
469
|
render(
|
|
460
470
|
<PagePermissionGuard
|
|
@@ -466,12 +476,10 @@ describe('PagePermissionGuard Component', () => {
|
|
|
466
476
|
</PagePermissionGuard>
|
|
467
477
|
);
|
|
468
478
|
|
|
479
|
+
// In test mode, validation is skipped, so component should render normally
|
|
469
480
|
await waitFor(() => {
|
|
470
|
-
expect(screen.
|
|
471
|
-
}, { interval: 10 });
|
|
472
|
-
|
|
473
|
-
// Restore NODE_ENV
|
|
474
|
-
process.env.NODE_ENV = originalEnv;
|
|
481
|
+
expect(screen.getByTestId('test-component')).toBeInTheDocument();
|
|
482
|
+
}, { interval: 10, timeout: 2000 });
|
|
475
483
|
});
|
|
476
484
|
});
|
|
477
485
|
|
|
@@ -550,6 +558,7 @@ describe('PagePermissionGuard Component', () => {
|
|
|
550
558
|
user: mockUser,
|
|
551
559
|
selectedOrganisation: { id: 'org-123' },
|
|
552
560
|
selectedEvent: null,
|
|
561
|
+
appId: 'app-123',
|
|
553
562
|
supabase: {
|
|
554
563
|
from: vi.fn().mockReturnValue({
|
|
555
564
|
select: vi.fn().mockReturnValue({
|
|
@@ -603,6 +612,7 @@ describe('PagePermissionGuard Component', () => {
|
|
|
603
612
|
user: mockUser,
|
|
604
613
|
selectedOrganisation: null,
|
|
605
614
|
selectedEvent: { event_id: 'event-123' },
|
|
615
|
+
appId: 'app-123',
|
|
606
616
|
supabase: {
|
|
607
617
|
from: vi.fn().mockReturnValue({
|
|
608
618
|
select: vi.fn().mockReturnValue({
|
|
@@ -653,7 +663,7 @@ describe('PagePermissionGuard Component', () => {
|
|
|
653
663
|
expect.objectContaining({
|
|
654
664
|
organisationId: 'resolved-org',
|
|
655
665
|
eventId: 'event-123',
|
|
656
|
-
appId: 'app-123'
|
|
666
|
+
appId: 'app-123' // Component uses appId from context, not from resolved scope
|
|
657
667
|
}),
|
|
658
668
|
'read:page.dashboard',
|
|
659
669
|
'dashboard',
|
|
@@ -666,6 +676,7 @@ describe('PagePermissionGuard Component', () => {
|
|
|
666
676
|
user: mockUser,
|
|
667
677
|
selectedOrganisation: null,
|
|
668
678
|
selectedEvent: { event_id: 'event-123' },
|
|
679
|
+
appId: undefined, // Not available for error case
|
|
669
680
|
supabase: {} as any
|
|
670
681
|
});
|
|
671
682
|
|
|
@@ -692,6 +703,7 @@ describe('PagePermissionGuard Component', () => {
|
|
|
692
703
|
user: mockUser,
|
|
693
704
|
selectedOrganisation: null,
|
|
694
705
|
selectedEvent: null,
|
|
706
|
+
appId: undefined,
|
|
695
707
|
supabase: null
|
|
696
708
|
});
|
|
697
709
|
|
|
@@ -809,8 +821,8 @@ describe('PagePermissionGuard Component', () => {
|
|
|
809
821
|
);
|
|
810
822
|
|
|
811
823
|
await waitFor(() => {
|
|
812
|
-
expect(screen.
|
|
813
|
-
}, { interval: 10 });
|
|
824
|
+
expect(screen.getByTestId('test-fallback')).toBeInTheDocument();
|
|
825
|
+
}, { interval: 10, timeout: 2000 });
|
|
814
826
|
|
|
815
827
|
expect(onDeniedSpy).toHaveBeenCalledWith(mockPageName, mockOperation);
|
|
816
828
|
});
|
|
@@ -864,8 +876,8 @@ describe('PagePermissionGuard Component', () => {
|
|
|
864
876
|
);
|
|
865
877
|
|
|
866
878
|
await waitFor(() => {
|
|
867
|
-
expect(screen.
|
|
868
|
-
}, { interval: 10 });
|
|
879
|
+
expect(screen.getByTestId('test-fallback')).toBeInTheDocument();
|
|
880
|
+
}, { interval: 10, timeout: 2000 });
|
|
869
881
|
|
|
870
882
|
expect(consoleSpy).not.toHaveBeenCalledWith(
|
|
871
883
|
expect.stringContaining('STRICT MODE VIOLATION')
|
|
@@ -895,8 +907,8 @@ describe('PagePermissionGuard Component', () => {
|
|
|
895
907
|
);
|
|
896
908
|
|
|
897
909
|
await waitFor(() => {
|
|
898
|
-
expect(screen.
|
|
899
|
-
}, { interval: 10 });
|
|
910
|
+
expect(screen.getByTestId('test-fallback')).toBeInTheDocument();
|
|
911
|
+
}, { interval: 10, timeout: 2000 });
|
|
900
912
|
|
|
901
913
|
expect(consoleSpy).not.toHaveBeenCalledWith(
|
|
902
914
|
expect.stringContaining('Page access attempt')
|
|
@@ -910,8 +922,9 @@ describe('PagePermissionGuard Component', () => {
|
|
|
910
922
|
it('handles missing user gracefully', async () => {
|
|
911
923
|
mockUseUnifiedAuthFn.mockReturnValue({
|
|
912
924
|
user: null,
|
|
913
|
-
|
|
914
|
-
|
|
925
|
+
selectedOrganisation: { id: 'org-123' },
|
|
926
|
+
selectedEvent: { event_id: 'event-123' },
|
|
927
|
+
appId: 'app-123',
|
|
915
928
|
supabase: {
|
|
916
929
|
from: vi.fn().mockReturnValue({
|
|
917
930
|
select: vi.fn().mockReturnValue({
|
|
@@ -966,6 +979,7 @@ describe('PagePermissionGuard Component', () => {
|
|
|
966
979
|
user: mockUser,
|
|
967
980
|
selectedOrganisation: { id: 'org-123' },
|
|
968
981
|
selectedEvent: { event_id: 'event-123' },
|
|
982
|
+
appId: undefined, // Not resolved due to database error
|
|
969
983
|
supabase: {
|
|
970
984
|
from: vi.fn().mockReturnValue({
|
|
971
985
|
select: vi.fn().mockReturnValue({
|