From 826eead6055795f28e9e4952a1199d415f231221 Mon Sep 17 00:00:00 2001 From: Ida Dittrich Date: Mon, 5 Jan 2026 06:56:23 +0100 Subject: [PATCH] fix:added more rolelabel logging to see why missing pages --- src/api/userApi.ts | 18 +++++- src/core/PageManager/PageManager.tsx | 58 ++++++++++++++++-- src/core/PageManager/SidebarProvider.tsx | 77 ++++++++++++++++++++++-- src/hooks/usePermissions.ts | 35 ++++++++++- src/hooks/useUsers.ts | 72 +++++++++------------- src/utils/userCache.ts | 13 ++-- 6 files changed, 209 insertions(+), 64 deletions(-) diff --git a/src/api/userApi.ts b/src/api/userApi.ts index 126adeb..8c70535 100644 --- a/src/api/userApi.ts +++ b/src/api/userApi.ts @@ -11,7 +11,8 @@ export interface User { fullName: string; language: string; enabled: boolean; - privilege: string; + privilege?: string; // Deprecated - use roleLabels instead + roleLabels?: string[]; // Array of role labels from backend (e.g., ["user"]) authenticationAuthority: string; mandateId: string; [key: string]: any; // Allow additional properties @@ -80,10 +81,23 @@ export async function fetchCurrentUser( endpoint = '/api/google/me'; } - return await request({ + console.log('📡 fetchCurrentUser: Requesting user data from:', endpoint); + const response = await request({ url: endpoint, method: 'get' }); + + console.log('📥 fetchCurrentUser: Received response:', { + endpoint, + hasData: !!response, + username: response?.username, + roleLabels: response?.roleLabels, + privilege: response?.privilege, + allKeys: response ? Object.keys(response) : [], + fullResponse: response + }); + + return response; } /** diff --git a/src/core/PageManager/PageManager.tsx b/src/core/PageManager/PageManager.tsx index 6a07c96..32f6088 100644 --- a/src/core/PageManager/PageManager.tsx +++ b/src/core/PageManager/PageManager.tsx @@ -28,28 +28,70 @@ const PageManager: React.FC = ({ // Check if user has access to a page using backend RBAC permissions const checkPageAccess = async (pageData: GenericPageData): Promise => { + console.log('🔍 PageManager: Checking page access:', { + path: pageData.path, + label: pageData.label, + hide: pageData.hide, + moduleEnabled: pageData.moduleEnabled + }); + try { - return await canView('UI', pageData.path); + const hasAccess = await canView('UI', pageData.path); + console.log('🔍 PageManager: Page access result:', { + path: pageData.path, + hasAccess + }); + return hasAccess; } catch (error) { - console.error(`Error checking RBAC access for ${pageData.path}:`, error); + console.error(`❌ PageManager: Error checking RBAC access for ${pageData.path}:`, error); return false; } }; useEffect(() => { + console.log('🔄 PageManager: useEffect triggered for path:', currentPath); const pageData = getPageDataByPath(currentPath); + console.log('📄 PageManager: Page data found:', { + path: currentPath, + hasPageData: !!pageData, + hide: pageData?.hide, + moduleEnabled: pageData?.moduleEnabled, + label: pageData?.label + }); + if (!pageData || pageData.hide || !pageData.moduleEnabled) { + console.log('⛔ PageManager: Page not rendered:', { + path: currentPath, + reason: !pageData ? 'not found' : pageData.hide ? 'hidden' : 'module disabled' + }); return; } // Check page access + console.log('🔍 PageManager: Checking access before rendering:', currentPath); checkPageAccess(pageData).then(hasAccess => { + console.log('🔍 PageManager: Access check complete:', { + path: currentPath, + hasAccess + }); + if (!hasAccess) { + console.log('⛔ PageManager: Page not rendered due to access check:', currentPath); return; } + + console.log('✅ PageManager: Rendering page:', { + path: currentPath, + label: pageData.label + }); setPageInstances(prev => { + console.log('📦 PageManager: Creating/updating page instance:', { + path: currentPath, + existingInstances: Array.from(prev.keys()), + willCreateNew: !prev.has(currentPath) + }); const newInstances = new Map(prev); // Update active states @@ -59,6 +101,10 @@ const PageManager: React.FC = ({ // Create instance if it doesn't exist if (!newInstances.has(currentPath)) { + console.log('📦 PageManager: Creating new page instance:', { + path: currentPath, + label: pageData.label + }); const shouldPreserve = pageData.preserveState || false; const pageInstance: PageInstance = { @@ -84,9 +130,13 @@ const PageManager: React.FC = ({ }; newInstances.set(currentPath, pageInstance); - - + console.log('✅ PageManager: Page instance created:', { + path: currentPath, + totalInstances: newInstances.size, + allPaths: Array.from(newInstances.keys()) + }); } else { + console.log('🔄 PageManager: Page instance already exists, updating active state:', currentPath); if (import.meta.env.DEV) { const _instance = newInstances.get(currentPath); void _instance; // Intentionally unused, for debugging purposes diff --git a/src/core/PageManager/SidebarProvider.tsx b/src/core/PageManager/SidebarProvider.tsx index fa175dd..a0e836c 100644 --- a/src/core/PageManager/SidebarProvider.tsx +++ b/src/core/PageManager/SidebarProvider.tsx @@ -160,15 +160,28 @@ export const SidebarProvider: React.FC = ({ children }) => .sort((a, b) => (a.order || 0) - (b.order || 0)); // Process each main page + console.log('📋 SidebarProvider: Processing pages, total:', mainPages.length); for (const pageData of mainPages) { + console.log('🔍 SidebarProvider: Checking access for page:', { + path: pageData.path, + label: pageData.label, + hasSubpages: pageData.hasSubpages + }); + // Check RBAC permissions try { const hasRBACAccess = await canView('UI', pageData.path); + console.log('🔍 SidebarProvider: RBAC check result:', { + path: pageData.path, + hasAccess: hasRBACAccess + }); + if (!hasRBACAccess) { + console.log('⛔ SidebarProvider: Page hidden due to RBAC:', pageData.path); continue; } } catch (error) { - console.error(`Error checking RBAC access for ${pageData.path}:`, error); + console.error(`❌ SidebarProvider: Error checking RBAC access for ${pageData.path}:`, error); continue; } @@ -183,18 +196,49 @@ export const SidebarProvider: React.FC = ({ children }) => // Filter subpages by RBAC access const accessibleSubpages = []; + console.log('📋 SidebarProvider: Checking subpages for:', { + parentPath: pageData.path, + totalSubpages: allSubpages.length + }); + for (const subpage of allSubpages) { try { + console.log('🔍 SidebarProvider: Checking subpage access:', { + parentPath: pageData.path, + subpagePath: subpage.path, + subpageLabel: subpage.label + }); + const hasSubpageRBACAccess = await canView('UI', subpage.path); + console.log('🔍 SidebarProvider: Subpage RBAC result:', { + subpagePath: subpage.path, + hasAccess: hasSubpageRBACAccess + }); + if (hasSubpageRBACAccess) { accessibleSubpages.push(subpage); + console.log('✅ SidebarProvider: Subpage added:', subpage.path); + } else { + console.log('⛔ SidebarProvider: Subpage hidden due to RBAC:', subpage.path); } } catch (error) { - console.error(`Error checking RBAC access for subpage ${subpage.path}:`, error); + console.error(`❌ SidebarProvider: Error checking RBAC access for subpage ${subpage.path}:`, error); } } + console.log('📋 SidebarProvider: Subpage filtering complete:', { + parentPath: pageData.path, + totalSubpages: allSubpages.length, + accessibleSubpages: accessibleSubpages.length, + accessiblePaths: accessibleSubpages.map(s => s.path) + }); + if (accessibleSubpages.length > 0) { + console.log('✅ SidebarProvider: Adding parent page with subpages:', { + path: pageData.path, + label: pageData.label, + subpagesCount: accessibleSubpages.length + }); // Create expandable item with submenu items.push({ id: pageData.id, @@ -212,6 +256,10 @@ export const SidebarProvider: React.FC = ({ children }) => }); } else { // No accessible subpages, show as regular item + console.log('✅ SidebarProvider: Adding parent page without accessible subpages:', { + path: pageData.path, + label: pageData.label + }); items.push({ id: pageData.id, name: resolveLanguageText(pageData.name, t), @@ -223,6 +271,10 @@ export const SidebarProvider: React.FC = ({ children }) => } } else { // Regular items without subpages + console.log('✅ SidebarProvider: Adding regular page:', { + path: pageData.path, + label: pageData.label + }); items.push({ id: pageData.id, name: resolveLanguageText(pageData.name, t), @@ -235,19 +287,36 @@ export const SidebarProvider: React.FC = ({ children }) => } // Sort all items by order - return items.sort((a, b) => (a.order || 0) - (b.order || 0)); + const sortedItems = items.sort((a, b) => (a.order || 0) - (b.order || 0)); + console.log('📊 SidebarProvider: Final sidebar items built and sorted:', { + totalItems: sortedItems.length, + sortedPaths: sortedItems.map(item => item.link), + items: sortedItems.map(item => ({ + id: item.id, + link: item.link, + name: item.name, + hasSubmenu: !!item.submenu, + submenuCount: item.submenu?.length || 0 + })) + }); + return sortedItems; }; // Refresh sidebar items const refreshSidebar = async () => { + console.log('🔄 SidebarProvider: Refreshing sidebar items...'); setLoading(true); setError(null); try { const items = await getSidebarItems(); + console.log('✅ SidebarProvider: Setting sidebar items:', { + count: items.length, + items: items.map(item => ({ id: item.id, link: item.link, name: item.name })) + }); setSidebarItems(items); } catch (err) { - console.error('Error refreshing sidebar:', err); + console.error('❌ SidebarProvider: Error refreshing sidebar:', err); setError(err instanceof Error ? err.message : 'Failed to load sidebar items'); } finally { setLoading(false); diff --git a/src/hooks/usePermissions.ts b/src/hooks/usePermissions.ts index 67b1868..e82f832 100644 --- a/src/hooks/usePermissions.ts +++ b/src/hooks/usePermissions.ts @@ -90,10 +90,31 @@ export const usePermissions = () => { try { // Use retry logic for 429 errors // Note: We wrap the API call in retry logic since useApiRequest doesn't handle 429 retries + console.log('🔐 usePermissions: Checking permissions for:', { context, item, cacheKey: key }); + const permissions = await retryWithBackoff(async () => { try { - return await fetchPermissionsApi(request, context, item); + const result = await fetchPermissionsApi(request, context, item); + console.log('✅ usePermissions: Received permissions response:', { + context, + item, + permissions: result, + hasView: result?.view, + hasCreate: result?.create, + hasUpdate: result?.update, + hasDelete: result?.delete, + fullResponse: result + }); + return result; } catch (error: any) { + console.error('❌ usePermissions: Error fetching permissions:', { + context, + item, + error: error.message, + status: error.response?.status, + statusText: error.response?.statusText, + fullError: error + }); // If useApiRequest throws, we need to check if it's a 429 // For now, we'll let the retry logic handle it throw error; @@ -104,6 +125,7 @@ export const usePermissions = () => { setCache(prev => { const newCache = { ...prev, [key]: permissions }; cacheRef.current = newCache; + console.log('💾 usePermissions: Cached permissions:', { context, item, permissions }); return newCache; }); @@ -170,8 +192,17 @@ export const usePermissions = () => { context: PermissionContext, item: string ): Promise => { + console.log('👁️ canView: Checking view access for:', { context, item }); const permissions = await checkPermission(context, item); - return permissions.view; + const hasAccess = permissions.view === true; + console.log('👁️ canView: Result:', { + context, + item, + hasAccess, + permissions: permissions, + viewPermission: permissions.view + }); + return hasAccess; }, [checkPermission]); /** diff --git a/src/hooks/useUsers.ts b/src/hooks/useUsers.ts index 7169390..88fc71c 100644 --- a/src/hooks/useUsers.ts +++ b/src/hooks/useUsers.ts @@ -29,23 +29,15 @@ export function useCurrentUser() { try { // Check if we already have user data in sessionStorage cache const cachedUser = getUserDataCache(); - if (cachedUser) { - // Validate cached user data - if privilege is missing, refetch from API - if (cachedUser.privilege === undefined || cachedUser.privilege === null) { - console.warn('⚠️ Cached user data missing privilege, refetching from API...', { - username: cachedUser.username, - privilege: cachedUser.privilege - }); - // Clear incomplete cache and continue to fetch from API - clearUserDataCache(); - } else { - setUser(cachedUser); - console.log('✅ Using cached user data from sessionStorage (persists during session):', { - username: cachedUser.username, - privilege: cachedUser.privilege - }); - return; - } + if (cachedUser && cachedUser.username) { + // Use cached user data - permissions are checked via RBAC API, not client-side + setUser(cachedUser); + console.log('✅ Using cached user data from sessionStorage (persists during session):', { + username: cachedUser.username, + roleLabels: cachedUser.roleLabels, + privilege: cachedUser.privilege + }); + return; } // JWT tokens are now stored in httpOnly cookies, so we fetch user data from API @@ -78,33 +70,33 @@ export function useCurrentUser() { // Log full response for debugging console.log('📦 User data received from API:', { username: data?.username, + roleLabels: data?.roleLabels, privilege: data?.privilege, + hasRoleLabels: !!data?.roleLabels, hasPrivilege: !!data?.privilege, allKeys: data ? Object.keys(data) : [], fullData: data }); - // Validate user data before caching - ensure privilege is present - if (!data || !data.privilege) { - console.error('❌ User data from API missing privilege field - this may cause permission issues:', { + // Always cache user data - permissions are checked via RBAC API, not client-side + // roleLabels/privilege are optional metadata for display/logging purposes + if (!data || !data.username) { + console.error('❌ User data from API is invalid:', { username: data?.username, - privilege: data?.privilege, dataKeys: data ? Object.keys(data) : [], fullResponse: data }); - // Don't cache incomplete data - it will cause permission issues on next load - // But still set user so the app can function (permissions are checked via RBAC API) - setUser(data); - console.warn('⚠️ User data set but not cached due to missing privilege - will refetch on next load'); - } else { - // Only cache if privilege is present - setUserDataCache(data); - console.log('✅ User data fetched from API and cached in sessionStorage (secure):', { - username: data.username, - privilege: data.privilege - }); - setUser(data); + throw new Error('Invalid user data received from API'); } + + // Cache user data (permissions are checked via RBAC API) + setUserDataCache(data); + console.log('✅ User data fetched from API and cached in sessionStorage (secure):', { + username: data.username, + roleLabels: data.roleLabels, + privilege: data.privilege + }); + setUser(data); } catch (error: any) { console.error('❌ Failed to fetch user data:', error); @@ -272,16 +264,10 @@ export function useCurrentUser() { useEffect(() => { // Try to load user from sessionStorage cache first for faster initial load const cachedUser = getUserDataCache(); - if (cachedUser) { - // Validate cached user data - if privilege is missing, don't use cache - if (cachedUser.privilege === undefined || cachedUser.privilege === null) { - console.warn('⚠️ Cached user data missing privilege on mount, will refetch from API'); - clearUserDataCache(); - // Don't set user - let fetchCurrentUser handle it - } else { - setUser(cachedUser); - console.log('✅ Using cached user data from sessionStorage on mount (persists during session)'); - } + if (cachedUser && cachedUser.username) { + // Use cached user data - permissions are checked via RBAC API + setUser(cachedUser); + console.log('✅ Using cached user data from sessionStorage on mount (persists during session)'); } // For OAuth authentication, wait a bit longer before fetching user data diff --git a/src/utils/userCache.ts b/src/utils/userCache.ts index e746f1c..9b461c8 100644 --- a/src/utils/userCache.ts +++ b/src/utils/userCache.ts @@ -17,7 +17,8 @@ export interface CachedUserData { username: string; email: string; fullName: string; - privilege: string; + privilege?: string; // Deprecated - use roleLabels instead + roleLabels?: string[]; // Array of role labels from backend (e.g., ["user"]) mandateId: string; language: string; enabled: boolean; @@ -30,14 +31,8 @@ export interface CachedUserData { */ export const setUserDataCache = (userData: CachedUserData): void => { if (userData) { - // Validate that privilege is present before caching - if (!userData.privilege) { - console.warn('⚠️ Attempted to cache user data without privilege, skipping cache:', { - username: userData.username, - hasPrivilege: !!userData.privilege - }); - return; - } + // Always cache user data - permissions are checked via RBAC API, not client-side + // roleLabels/privilege are optional metadata, not required for app functionality try { sessionStorage.setItem(USER_CACHE_KEY, JSON.stringify(userData)); } catch (error) {