fix:added more rolelabel logging to see why missing pages

This commit is contained in:
Ida Dittrich 2026-01-05 06:56:23 +01:00
parent 05508cc76c
commit 826eead605
6 changed files with 209 additions and 64 deletions

View file

@ -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;
}
/**

View file

@ -28,28 +28,70 @@ const PageManager: React.FC<PageManagerProps> = ({
// Check if user has access to a page using backend RBAC permissions
const checkPageAccess = async (pageData: GenericPageData): Promise<boolean> => {
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<PageManagerProps> = ({
// 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<PageManagerProps> = ({
};
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

View file

@ -160,15 +160,28 @@ export const SidebarProvider: React.FC<SidebarProviderProps> = ({ 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<SidebarProviderProps> = ({ 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<SidebarProviderProps> = ({ 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<SidebarProviderProps> = ({ 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<SidebarProviderProps> = ({ 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);

View file

@ -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<boolean> => {
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]);
/**

View file

@ -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

View file

@ -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) {