@jmruthers/pace-core 0.5.35 → 0.5.36
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/dist/{DataTable-7H63EFYM.js → DataTable-BC4UXGIK.js} +2 -2
- package/dist/{chunk-Q4U5PCOY.js → chunk-6Z3XT4BB.js} +22 -182
- package/dist/chunk-6Z3XT4BB.js.map +1 -0
- package/dist/{chunk-BRWHKGV2.js → chunk-GEJMGKZW.js} +2 -81
- package/dist/chunk-GEJMGKZW.js.map +1 -0
- package/dist/{chunk-MC5E4ILK.js → chunk-KXXVMXZ7.js} +2 -2
- package/dist/components.js +2 -2
- package/dist/index.js +3 -3
- package/dist/rbac/index.d.ts +1 -1
- package/dist/rbac/index.js +1 -1
- package/dist/utils.js +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- 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/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- 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/DataAccessRecord.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/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventContextType.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/EventProviderProps.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- 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/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 +1 -1
- 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 +1 -1
- 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/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- 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/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACContextType.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACProviderProps.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- 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/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/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
- 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/UsePublicRouteParamsReturn.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 +5 -5
- package/package.json +1 -1
- package/src/components/DataTable/components/DataTableCore.tsx +21 -106
- package/src/rbac/components/PagePermissionGuard.tsx +6 -190
- package/dist/chunk-BRWHKGV2.js.map +0 -1
- package/dist/chunk-Q4U5PCOY.js.map +0 -1
- package/docs/troubleshooting/cake-infinite-rerender-debugging.md +0 -284
- package/docs/troubleshooting/cake-infinite-rerender-summary.md +0 -117
- package/docs/troubleshooting/cake-rerender-diagnostic.js +0 -162
- package/docs/troubleshooting/page-permission-guard-diagnostic.js +0 -129
- package/docs/troubleshooting/page-permission-guard-ui-debugging.md +0 -353
- package/docs/troubleshooting/rbac-critical-fixes-summary.md +0 -260
- /package/dist/{DataTable-7H63EFYM.js.map → DataTable-BC4UXGIK.js.map} +0 -0
- /package/dist/{chunk-MC5E4ILK.js.map → chunk-KXXVMXZ7.js.map} +0 -0
package/docs/api/modules.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
[@jmruthers/pace-core - v0.5.
|
|
1
|
+
[@jmruthers/pace-core - v0.5.36](README.md) / Exports
|
|
2
2
|
|
|
3
|
-
# @jmruthers/pace-core - v0.5.
|
|
3
|
+
# @jmruthers/pace-core - v0.5.36
|
|
4
4
|
|
|
5
5
|
**`File`**
|
|
6
6
|
|
|
@@ -5036,7 +5036,7 @@ ___
|
|
|
5036
5036
|
|
|
5037
5037
|
### PagePermissionGuard
|
|
5038
5038
|
|
|
5039
|
-
▸ **PagePermissionGuard**(`props`): `Element`
|
|
5039
|
+
▸ **PagePermissionGuard**(`props`): ``null`` \| `string` \| `number` \| `boolean` \| `Iterable`\<`ReactNode`, `any`, `any`\> \| `Element`
|
|
5040
5040
|
|
|
5041
5041
|
PagePermissionGuard - Enforces page-level permissions
|
|
5042
5042
|
|
|
@@ -5052,13 +5052,13 @@ prevent apps from bypassing permission checks.
|
|
|
5052
5052
|
|
|
5053
5053
|
#### Returns
|
|
5054
5054
|
|
|
5055
|
-
`Element`
|
|
5055
|
+
``null`` \| `string` \| `number` \| `boolean` \| `Iterable`\<`ReactNode`, `any`, `any`\> \| `Element`
|
|
5056
5056
|
|
|
5057
5057
|
React element with permission enforcement
|
|
5058
5058
|
|
|
5059
5059
|
#### Defined in
|
|
5060
5060
|
|
|
5061
|
-
[packages/core/src/rbac/components/PagePermissionGuard.tsx:
|
|
5061
|
+
[packages/core/src/rbac/components/PagePermissionGuard.tsx:489](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/rbac/components/PagePermissionGuard.tsx#L489)
|
|
5062
5062
|
|
|
5063
5063
|
___
|
|
5064
5064
|
|
package/package.json
CHANGED
|
@@ -333,13 +333,6 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
333
333
|
// Get scope from useUnifiedAuth hook instead of user metadata
|
|
334
334
|
const { selectedOrganisationId, selectedEventId, supabase } = useUnifiedAuth();
|
|
335
335
|
|
|
336
|
-
// DEBUG: Log what we get from useUnifiedAuth
|
|
337
|
-
console.log('[DataTable] useUnifiedAuth values:', {
|
|
338
|
-
selectedOrganisationId,
|
|
339
|
-
selectedEventId,
|
|
340
|
-
hasUser: !!user,
|
|
341
|
-
userId: user?.id
|
|
342
|
-
});
|
|
343
336
|
|
|
344
337
|
// Create scope the same way as PagePermissionGuard - with event-based resolution
|
|
345
338
|
const [resolvedScope, setResolvedScope] = useState<Scope | null>(null);
|
|
@@ -385,42 +378,35 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
385
378
|
if (supabase) {
|
|
386
379
|
const appName = getCurrentAppName();
|
|
387
380
|
if (appName) {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
381
|
+
try {
|
|
382
|
+
const { data: app, error } = await supabase
|
|
383
|
+
.from('rbac_apps')
|
|
384
|
+
.select('id, name, is_active')
|
|
385
|
+
.eq('name', appName)
|
|
386
|
+
.eq('is_active', true)
|
|
387
|
+
.single() as { data: { id: string; name: string; is_active: boolean } | null; error: any };
|
|
388
|
+
|
|
389
|
+
if (error) {
|
|
390
|
+
// Check if app exists but is inactive
|
|
391
|
+
const { data: inactiveApp } = await supabase
|
|
391
392
|
.from('rbac_apps')
|
|
392
393
|
.select('id, name, is_active')
|
|
393
394
|
.eq('name', appName)
|
|
394
|
-
.
|
|
395
|
-
.single() as { data: { id: string; name: string; is_active: boolean } | null; error: any };
|
|
395
|
+
.single() as { data: { id: string; name: string; is_active: boolean } | null };
|
|
396
396
|
|
|
397
|
-
if (
|
|
398
|
-
console.error(
|
|
399
|
-
// Check if app exists but is inactive
|
|
400
|
-
const { data: inactiveApp } = await supabase
|
|
401
|
-
.from('rbac_apps')
|
|
402
|
-
.select('id, name, is_active')
|
|
403
|
-
.eq('name', appName)
|
|
404
|
-
.single() as { data: { id: string; name: string; is_active: boolean } | null };
|
|
405
|
-
|
|
406
|
-
if (inactiveApp) {
|
|
407
|
-
console.error(`[DataTable] App "${appName}" exists but is inactive (is_active: ${inactiveApp.is_active})`);
|
|
408
|
-
} else {
|
|
409
|
-
console.error(`[DataTable] App "${appName}" not found in rbac_apps table`);
|
|
410
|
-
}
|
|
411
|
-
} else if (app) {
|
|
412
|
-
appId = app.id;
|
|
413
|
-
console.log('[DataTable] Successfully resolved app ID:', app.id);
|
|
397
|
+
if (inactiveApp) {
|
|
398
|
+
console.error(`[DataTable] App "${appName}" exists but is inactive (is_active: ${inactiveApp.is_active})`);
|
|
414
399
|
} else {
|
|
415
|
-
console.error(
|
|
400
|
+
console.error(`[DataTable] App "${appName}" not found in rbac_apps table`);
|
|
416
401
|
}
|
|
417
|
-
}
|
|
418
|
-
|
|
402
|
+
} else if (app) {
|
|
403
|
+
appId = app.id;
|
|
419
404
|
}
|
|
420
|
-
}
|
|
421
|
-
console.
|
|
405
|
+
} catch (error) {
|
|
406
|
+
console.error('[DataTable] Unexpected error resolving app ID:', error);
|
|
422
407
|
}
|
|
423
408
|
}
|
|
409
|
+
}
|
|
424
410
|
|
|
425
411
|
// If we have both organisation and event, use them directly
|
|
426
412
|
if (selectedOrganisationId && selectedEventId) {
|
|
@@ -472,39 +458,11 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
472
458
|
resolveScope();
|
|
473
459
|
}, [selectedOrganisationId, selectedEventId, supabase]);
|
|
474
460
|
|
|
475
|
-
// DEBUG: Log the exact parameters being passed to useCan
|
|
476
|
-
console.log('[DataTable] useCan parameters:', {
|
|
477
|
-
userId: user?.id || '',
|
|
478
|
-
stableScope,
|
|
479
|
-
permission: `read:page.${effectivePageId}`,
|
|
480
|
-
effectivePageId,
|
|
481
|
-
useCache: true,
|
|
482
|
-
hasValidScope: !!(stableScope && stableScope.organisationId)
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
// DEBUG: Log stableScope details separately to avoid console collapsing
|
|
486
|
-
console.log('[DataTable] stableScope details:');
|
|
487
|
-
console.log(' organisationId:', stableScope?.organisationId);
|
|
488
|
-
console.log(' eventId:', stableScope?.eventId);
|
|
489
|
-
console.log(' appId:', stableScope?.appId);
|
|
490
|
-
console.log(' stableScope type:', typeof stableScope);
|
|
491
|
-
console.log(' stableScope keys:', stableScope ? Object.keys(stableScope) : 'null');
|
|
492
|
-
|
|
493
461
|
// Always call useCan hooks (React rules), but use a fallback scope if needed
|
|
494
462
|
const fallbackScope = { organisationId: '', eventId: undefined, appId: undefined };
|
|
495
463
|
const effectiveScope = stableScope && stableScope.organisationId ? stableScope : fallbackScope;
|
|
496
464
|
|
|
497
|
-
|
|
498
|
-
const testBypass = false; // Set to true to test bypassing useCan
|
|
499
|
-
|
|
500
|
-
const permissions = testBypass ? {
|
|
501
|
-
canRead: { can: true, isLoading: false, error: null, refetch: async () => {} },
|
|
502
|
-
canCreate: { can: true, isLoading: false, error: null, refetch: async () => {} },
|
|
503
|
-
canUpdate: { can: true, isLoading: false, error: null, refetch: async () => {} },
|
|
504
|
-
canDelete: { can: true, isLoading: false, error: null, refetch: async () => {} },
|
|
505
|
-
canExport: { can: true, isLoading: false, error: null, refetch: async () => {} },
|
|
506
|
-
canImport: { can: true, isLoading: false, error: null, refetch: async () => {} },
|
|
507
|
-
} : {
|
|
465
|
+
const permissions = {
|
|
508
466
|
canRead: useCan(user?.id || '', effectiveScope, `read:page.${effectivePageId}` as any, effectivePageId, true),
|
|
509
467
|
canCreate: useCan(user?.id || '', effectiveScope, `create:page.${effectivePageId}` as any, effectivePageId, true),
|
|
510
468
|
canUpdate: useCan(user?.id || '', effectiveScope, `update:page.${effectivePageId}` as any, effectivePageId, true),
|
|
@@ -512,10 +470,6 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
512
470
|
canExport: useCan(user?.id || '', effectiveScope, `manage:page.${effectivePageId}` as any, effectivePageId, true), // Using manage for export/import
|
|
513
471
|
canImport: useCan(user?.id || '', effectiveScope, `manage:page.${effectivePageId}` as any, effectivePageId, true), // Using manage for export/import
|
|
514
472
|
};
|
|
515
|
-
|
|
516
|
-
if (testBypass) {
|
|
517
|
-
console.log('[DataTable] TEST BYPASS: Using hardcoded can: true for all permissions');
|
|
518
|
-
}
|
|
519
473
|
|
|
520
474
|
// ============================================================================
|
|
521
475
|
// CONFIGURATION RESOLUTION - ALWAYS call these hooks
|
|
@@ -949,50 +903,11 @@ function DataTableInternal<TData extends DataRecord>({
|
|
|
949
903
|
throw new Error('DataTable requires authenticated user for RBAC');
|
|
950
904
|
}
|
|
951
905
|
|
|
952
|
-
const scope = {
|
|
953
|
-
organisationId: user?.user_metadata?.organisationId || user?.app_metadata?.organisationId,
|
|
954
|
-
eventId: user?.user_metadata?.eventId || user?.app_metadata?.eventId,
|
|
955
|
-
appId: user?.user_metadata?.appId || user?.app_metadata?.appId,
|
|
956
|
-
};
|
|
957
906
|
|
|
958
907
|
// MANDATORY: No data access without read permission
|
|
959
|
-
console.log('[DataTable] RBAC DEBUG - Basic Info:', {
|
|
960
|
-
effectivePageId,
|
|
961
|
-
pageId,
|
|
962
|
-
pageName,
|
|
963
|
-
userId: user?.id,
|
|
964
|
-
permission: `read:page.${effectivePageId}`
|
|
965
|
-
});
|
|
966
|
-
|
|
967
|
-
console.log('[DataTable] RBAC DEBUG - CanRead Details:');
|
|
968
|
-
console.log(' can:', permissions.canRead.can);
|
|
969
|
-
console.log(' isLoading:', permissions.canRead.isLoading);
|
|
970
|
-
console.log(' error:', permissions.canRead.error);
|
|
971
|
-
console.log(' refetch type:', typeof permissions.canRead.refetch);
|
|
972
|
-
|
|
973
|
-
console.log('[DataTable] RBAC DEBUG - Scope (FIXED):');
|
|
974
|
-
console.log(' resolvedScope:', resolvedScope);
|
|
975
|
-
console.log(' stableScope:', stableScope);
|
|
976
|
-
console.log(' organisationId:', stableScope?.organisationId);
|
|
977
|
-
console.log(' eventId:', stableScope?.eventId);
|
|
978
|
-
console.log(' appId:', stableScope?.appId);
|
|
979
|
-
|
|
980
908
|
if (!permissions.canRead.can) {
|
|
981
|
-
console.error('[DataTable] ACCESS DENIED - DataTable RBAC failing!');
|
|
982
|
-
console.error(' effectivePageId:', effectivePageId);
|
|
983
|
-
console.error(' canRead.can:', permissions.canRead.can);
|
|
984
|
-
console.error(' canRead.isLoading:', permissions.canRead.isLoading);
|
|
985
|
-
console.error(' canRead.error:', permissions.canRead.error);
|
|
986
|
-
console.error(' permission:', `read:page.${effectivePageId}`);
|
|
987
|
-
console.error(' Full canRead object:', permissions.canRead);
|
|
988
909
|
return <AccessDeniedPage resource={effectivePageId || 'unknown-page'} operation="read" />;
|
|
989
910
|
}
|
|
990
|
-
|
|
991
|
-
console.log('[DataTable] ACCESS GRANTED - DataTable RBAC working!');
|
|
992
|
-
console.log(' effectivePageId:', effectivePageId);
|
|
993
|
-
console.log(' canRead.can:', permissions.canRead.can);
|
|
994
|
-
console.log(' canRead.isLoading:', permissions.canRead.isLoading);
|
|
995
|
-
console.log(' canRead.error:', permissions.canRead.error);
|
|
996
911
|
|
|
997
912
|
// ============================================================================
|
|
998
913
|
// RENDER
|
|
@@ -135,28 +135,10 @@ const PagePermissionGuardComponent = ({
|
|
|
135
135
|
const renderCountRef = useRef(0);
|
|
136
136
|
renderCountRef.current += 1;
|
|
137
137
|
|
|
138
|
-
console.log(`[PagePermissionGuard] RENDER #${renderCountRef.current} for ${pageName}:${operation}`, {
|
|
139
|
-
instanceId,
|
|
140
|
-
timestamp: new Date().toISOString()
|
|
141
|
-
});
|
|
142
138
|
|
|
143
|
-
// Track component lifecycle
|
|
144
|
-
useEffect(() => {
|
|
145
|
-
console.log(`[PagePermissionGuard] MOUNTED: ${pageName}:${operation}`, { instanceId });
|
|
146
|
-
return () => {
|
|
147
|
-
console.log(`[PagePermissionGuard] UNMOUNTED: ${pageName}:${operation}`, { instanceId });
|
|
148
|
-
};
|
|
149
|
-
}, [pageName, operation, instanceId]);
|
|
150
139
|
|
|
151
140
|
const { user, selectedOrganisationId, selectedEventId, supabase } = useUnifiedAuth();
|
|
152
141
|
|
|
153
|
-
// DEBUG: Log what we get from useUnifiedAuth
|
|
154
|
-
console.log('[PagePermissionGuard] useUnifiedAuth values:', {
|
|
155
|
-
selectedOrganisationId,
|
|
156
|
-
selectedEventId,
|
|
157
|
-
hasUser: !!user,
|
|
158
|
-
userId: user?.id
|
|
159
|
-
});
|
|
160
142
|
const [hasChecked, setHasChecked] = useState(false);
|
|
161
143
|
const [checkError, setCheckError] = useState<Error | null>(null);
|
|
162
144
|
const [resolvedScope, setResolvedScope] = useState<Scope | null>(null);
|
|
@@ -216,7 +198,6 @@ const PagePermissionGuardComponent = ({
|
|
|
216
198
|
const appName = getCurrentAppName();
|
|
217
199
|
if (appName) {
|
|
218
200
|
try {
|
|
219
|
-
console.log('[PagePermissionGuard] Resolving app name to ID:', appName);
|
|
220
201
|
const { data: app, error } = await supabaseRef.current
|
|
221
202
|
.from('rbac_apps')
|
|
222
203
|
.select('id, name, is_active')
|
|
@@ -240,7 +221,6 @@ const PagePermissionGuardComponent = ({
|
|
|
240
221
|
}
|
|
241
222
|
} else if (app) {
|
|
242
223
|
appId = app.id;
|
|
243
|
-
console.log('[PagePermissionGuard] Successfully resolved app ID:', app.id);
|
|
244
224
|
} else {
|
|
245
225
|
console.error('[PagePermissionGuard] No app data returned for:', appName);
|
|
246
226
|
}
|
|
@@ -281,7 +261,6 @@ const PagePermissionGuardComponent = ({
|
|
|
281
261
|
eventId: selectedEventId,
|
|
282
262
|
appId: appId
|
|
283
263
|
};
|
|
284
|
-
console.log('[PagePermissionGuard] Setting resolved scope:', resolvedScope);
|
|
285
264
|
setResolvedScope(resolvedScope);
|
|
286
265
|
setCheckError(null); // Clear any previous errors
|
|
287
266
|
return;
|
|
@@ -316,7 +295,6 @@ const PagePermissionGuardComponent = ({
|
|
|
316
295
|
eventId: selectedEventId || undefined,
|
|
317
296
|
appId: appId
|
|
318
297
|
};
|
|
319
|
-
console.log('[PagePermissionGuard] Setting resolved scope (org only):', resolvedScope);
|
|
320
298
|
setResolvedScope(resolvedScope);
|
|
321
299
|
setCheckError(null); // Clear any previous errors
|
|
322
300
|
return;
|
|
@@ -373,22 +351,6 @@ const PagePermissionGuardComponent = ({
|
|
|
373
351
|
return `${operation}:page.${pageName}` as Permission;
|
|
374
352
|
}, [operation, pageName]);
|
|
375
353
|
|
|
376
|
-
// DEBUG: Log the exact parameters being passed to useCan
|
|
377
|
-
console.log('[PagePermissionGuard] useCan parameters:', {
|
|
378
|
-
userId: user?.id || '',
|
|
379
|
-
stableScope,
|
|
380
|
-
permission,
|
|
381
|
-
effectivePageId,
|
|
382
|
-
useCache: true
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
// DEBUG: Log stableScope details separately to avoid console collapsing
|
|
386
|
-
console.log('[PagePermissionGuard] stableScope details:');
|
|
387
|
-
console.log(' organisationId:', stableScope?.organisationId);
|
|
388
|
-
console.log(' eventId:', stableScope?.eventId);
|
|
389
|
-
console.log(' appId:', stableScope?.appId);
|
|
390
|
-
console.log(' stableScope type:', typeof stableScope);
|
|
391
|
-
console.log(' stableScope keys:', stableScope ? Object.keys(stableScope) : 'null');
|
|
392
354
|
|
|
393
355
|
// Check if user has permission - only call useCan when we have a resolved scope
|
|
394
356
|
// If resolvedScope is null, we're still resolving, so show loading state
|
|
@@ -400,17 +362,6 @@ const PagePermissionGuardComponent = ({
|
|
|
400
362
|
true // Use cache
|
|
401
363
|
);
|
|
402
364
|
|
|
403
|
-
// Debug useCan results
|
|
404
|
-
console.log(`[PagePermissionGuard] useCan result for ${pageName}:${operation}`, {
|
|
405
|
-
instanceId,
|
|
406
|
-
can,
|
|
407
|
-
canIsLoading,
|
|
408
|
-
canError: !!canError,
|
|
409
|
-
userId: user?.id,
|
|
410
|
-
stableScope,
|
|
411
|
-
permission,
|
|
412
|
-
effectivePageId
|
|
413
|
-
});
|
|
414
365
|
|
|
415
366
|
// Combine loading states - we're loading if either scope is resolving OR permission check is loading
|
|
416
367
|
const isLoading = !resolvedScope || canIsLoading;
|
|
@@ -445,6 +396,7 @@ const PagePermissionGuardComponent = ({
|
|
|
445
396
|
}
|
|
446
397
|
}, [auditLog, hasChecked, isLoading, pageName, operation, user?.id, resolvedScope, can]);
|
|
447
398
|
|
|
399
|
+
|
|
448
400
|
// Handle strict mode violations
|
|
449
401
|
useEffect(() => {
|
|
450
402
|
if (strictMode && hasChecked && !isLoading && !can) {
|
|
@@ -471,166 +423,30 @@ const PagePermissionGuardComponent = ({
|
|
|
471
423
|
const scopeKey = resolvedScope ? `${resolvedScope.organisationId}-${resolvedScope.eventId}-${resolvedScope.appId}` : 'no-scope';
|
|
472
424
|
const permissionKey = `${scopeKey}-${can}-${isLoading}-${!!checkError}-${hasChecked}`;
|
|
473
425
|
|
|
474
|
-
// Debug logging for state transitions
|
|
475
|
-
useEffect(() => {
|
|
476
|
-
console.log('[PagePermissionGuard] State transition:', {
|
|
477
|
-
instanceId,
|
|
478
|
-
isLoading,
|
|
479
|
-
hasChecked,
|
|
480
|
-
resolvedScope: !!resolvedScope,
|
|
481
|
-
checkError: !!checkError,
|
|
482
|
-
can,
|
|
483
|
-
shouldShowAccessDenied,
|
|
484
|
-
shouldShowContent,
|
|
485
|
-
permissionKey
|
|
486
|
-
});
|
|
487
|
-
}, [isLoading, hasChecked, resolvedScope, checkError, can, shouldShowAccessDenied, shouldShowContent, permissionKey, instanceId]);
|
|
488
|
-
|
|
489
|
-
// CRITICAL DEBUG: Log every render decision with full context
|
|
490
|
-
const renderDecision = {
|
|
491
|
-
instanceId,
|
|
492
|
-
timestamp: new Date().toISOString(),
|
|
493
|
-
renderCount: renderCountRef.current,
|
|
494
|
-
// State values
|
|
495
|
-
isLoading,
|
|
496
|
-
hasValidScope,
|
|
497
|
-
hasValidUser,
|
|
498
|
-
hasChecked,
|
|
499
|
-
checkError: !!checkError,
|
|
500
|
-
can,
|
|
501
|
-
// Calculated values
|
|
502
|
-
shouldShowAccessDenied,
|
|
503
|
-
shouldShowContent,
|
|
504
|
-
// Render conditions
|
|
505
|
-
willShowLoading: isLoading || !hasValidScope || !hasValidUser || !hasChecked,
|
|
506
|
-
willShowError: checkError && !can,
|
|
507
|
-
willShowDenied: shouldShowAccessDenied,
|
|
508
|
-
willShowContent: shouldShowContent,
|
|
509
|
-
// Raw data for debugging
|
|
510
|
-
resolvedScope: resolvedScope ? {
|
|
511
|
-
organisationId: resolvedScope.organisationId,
|
|
512
|
-
eventId: resolvedScope.eventId,
|
|
513
|
-
appId: resolvedScope.appId
|
|
514
|
-
} : null,
|
|
515
|
-
user: user ? { id: user.id, email: user.email } : null
|
|
516
|
-
};
|
|
517
|
-
|
|
518
|
-
console.log('[PagePermissionGuard] RENDER DECISION:', renderDecision);
|
|
519
426
|
|
|
520
|
-
// CRITICAL: If we have permission but shouldShowContent is false, this is the bug!
|
|
521
|
-
if (can && !shouldShowContent) {
|
|
522
|
-
console.error('[PagePermissionGuard] CRITICAL BUG DETECTED: Has permission but shouldShowContent is false!', {
|
|
523
|
-
...renderDecision,
|
|
524
|
-
bug: 'PERMISSION_GRANTED_BUT_CONTENT_NOT_SHOWING'
|
|
525
|
-
});
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
// CRITICAL: If shouldShowContent is true but we're not rendering content, this is the bug!
|
|
529
|
-
if (shouldShowContent && !(isLoading || !hasValidScope || !hasValidUser || !hasChecked) && !(checkError && !can) && !shouldShowAccessDenied) {
|
|
530
|
-
console.log('[PagePermissionGuard] SHOULD RENDER CONTENT - This is the correct path!', {
|
|
531
|
-
...renderDecision,
|
|
532
|
-
status: 'CONTENT_SHOULD_BE_RENDERED'
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
427
|
|
|
536
428
|
// Show loading state - if we're still loading or don't have valid state
|
|
537
429
|
if (isLoading || !hasValidScope || !hasValidUser || !hasChecked) {
|
|
538
|
-
|
|
539
|
-
return (
|
|
540
|
-
<div key={`loading-${permissionKey}`} style={{ border: '3px solid orange', padding: '5px', backgroundColor: 'lightyellow' }}>
|
|
541
|
-
<div style={{ color: 'orange', fontWeight: 'bold' }}>🔄 LOADING STATE</div>
|
|
542
|
-
{loading}
|
|
543
|
-
</div>
|
|
544
|
-
);
|
|
430
|
+
return loading || <div>Checking permissions...</div>;
|
|
545
431
|
}
|
|
546
432
|
|
|
547
433
|
// Show error state - only if we have an error AND no permission
|
|
548
434
|
if (checkError && !can) {
|
|
549
|
-
|
|
550
|
-
return (
|
|
551
|
-
<div key={`error-${permissionKey}`} style={{ border: '3px solid red', padding: '5px', backgroundColor: 'lightcoral' }}>
|
|
552
|
-
<div style={{ color: 'red', fontWeight: 'bold' }}>❌ ERROR STATE</div>
|
|
553
|
-
{fallback}
|
|
554
|
-
</div>
|
|
555
|
-
);
|
|
435
|
+
return fallback;
|
|
556
436
|
}
|
|
557
437
|
|
|
558
438
|
// Show access denied - if permission check is complete and user doesn't have permission
|
|
559
439
|
if (shouldShowAccessDenied) {
|
|
560
|
-
|
|
561
|
-
return (
|
|
562
|
-
<div key={`denied-${permissionKey}`} style={{ border: '3px solid red', padding: '5px', backgroundColor: 'lightcoral' }}>
|
|
563
|
-
<div style={{ color: 'red', fontWeight: 'bold' }}>🚫 ACCESS DENIED STATE</div>
|
|
564
|
-
{fallback}
|
|
565
|
-
</div>
|
|
566
|
-
);
|
|
440
|
+
return fallback;
|
|
567
441
|
}
|
|
568
442
|
|
|
569
443
|
// Show protected content - if permission check is complete and user has permission
|
|
570
444
|
if (shouldShowContent) {
|
|
571
|
-
|
|
572
|
-
console.log('[PagePermissionGuard] CONTENT RENDER SUCCESS - This should fix the Access Denied issue!');
|
|
573
|
-
|
|
574
|
-
// FORCE RENDER: Create a very obvious visual indicator that cannot be missed
|
|
575
|
-
const forceRenderContent = (
|
|
576
|
-
<div
|
|
577
|
-
key={`content-${permissionKey}`}
|
|
578
|
-
style={{
|
|
579
|
-
border: '5px solid red',
|
|
580
|
-
padding: '10px',
|
|
581
|
-
margin: '10px',
|
|
582
|
-
backgroundColor: 'yellow',
|
|
583
|
-
position: 'relative',
|
|
584
|
-
zIndex: 9999
|
|
585
|
-
}}
|
|
586
|
-
>
|
|
587
|
-
<div style={{
|
|
588
|
-
background: 'red',
|
|
589
|
-
color: 'white',
|
|
590
|
-
padding: '10px',
|
|
591
|
-
fontSize: '16px',
|
|
592
|
-
fontWeight: 'bold',
|
|
593
|
-
marginBottom: '10px',
|
|
594
|
-
textAlign: 'center'
|
|
595
|
-
}}>
|
|
596
|
-
🚨 FORCE RENDER: PagePermissionGuard Content is ACTUALLY RENDERING! 🚨
|
|
597
|
-
</div>
|
|
598
|
-
<div style={{
|
|
599
|
-
background: 'lightgreen',
|
|
600
|
-
padding: '5px',
|
|
601
|
-
fontSize: '14px',
|
|
602
|
-
marginBottom: '10px',
|
|
603
|
-
border: '2px solid green'
|
|
604
|
-
}}>
|
|
605
|
-
✅ This proves the PagePermissionGuard is working correctly!
|
|
606
|
-
</div>
|
|
607
|
-
<div style={{ border: '2px solid blue', padding: '5px' }}>
|
|
608
|
-
<strong>Original Children:</strong>
|
|
609
|
-
{children}
|
|
610
|
-
</div>
|
|
611
|
-
</div>
|
|
612
|
-
);
|
|
613
|
-
|
|
614
|
-
console.log('[PagePermissionGuard] RETURNING FORCE RENDER CONTENT');
|
|
615
|
-
return forceRenderContent;
|
|
445
|
+
return <>{children}</>;
|
|
616
446
|
}
|
|
617
447
|
|
|
618
448
|
// Fallback: This should never happen, but just in case
|
|
619
|
-
|
|
620
|
-
console.error('[PagePermissionGuard] CRITICAL: UNEXPECTED STATE - FALLING BACK TO LOADING', {
|
|
621
|
-
instanceId,
|
|
622
|
-
isLoading,
|
|
623
|
-
hasValidScope,
|
|
624
|
-
hasValidUser,
|
|
625
|
-
hasChecked,
|
|
626
|
-
checkError: !!checkError,
|
|
627
|
-
can,
|
|
628
|
-
shouldShowAccessDenied,
|
|
629
|
-
shouldShowContent,
|
|
630
|
-
resolvedScope,
|
|
631
|
-
user: user ? { id: user.id, email: user.email } : null
|
|
632
|
-
});
|
|
633
|
-
return <div key={`fallback-${permissionKey}`}>{loading}</div>;
|
|
449
|
+
return fallback;
|
|
634
450
|
}
|
|
635
451
|
|
|
636
452
|
/**
|