From 9919096bd2173a9d25a5f977020e78b541b87b13 Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 1 Nov 2025 16:02:40 +0100 Subject: [PATCH 1/2] refactor(router): enhance role-based access control - Improve readability and maintainability of RBAC logic - Implement single source of truth for top-level route paths - Redirect unauthorized users based on role permissions --- lib/router/router.dart | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/lib/router/router.dart b/lib/router/router.dart index d3b148b2..1d4a4151 100644 --- a/lib/router/router.dart +++ b/lib/router/router.dart @@ -86,24 +86,45 @@ GoRouter createRouter({ // --- Role-Based Access Control (RBAC) --- final userRole = context.read().state.user?.dashboardRole; - final destinationRouteName = state.topRoute?.name; - // Allow navigation if role is not yet determined or route is unknown. - if (userRole == null || destinationRouteName == null) { + // Allow navigation if the user role isn't determined yet. + if (userRole == null) { return null; } - final allowedRoutes = routePermissions[userRole]; + // A local map to resolve top-level route names to their base paths. + // This is the single source for this mapping within the redirect logic. + const topLevelPaths = { + Routes.overviewName: Routes.overview, + Routes.contentManagementName: Routes.contentManagement, + Routes.userManagementName: Routes.userManagement, + Routes.appConfigurationName: Routes.appConfiguration, + }; - // Check if the user is trying to access a route they are not - // permitted to view. + // Get the set of authorized route *names* for the user's role from + // the single source of truth: route_permissions.dart. + final allowedRouteNames = routePermissions[userRole] ?? {}; + + // Convert the allowed route names into a list of their base paths. + final authorizedPaths = allowedRouteNames + .map((name) => topLevelPaths[name]) + .whereType< + String + >() // Filter out any nulls if a name is not in the map. + .toList(); + + // Check if the destination path starts with any of the authorized base + // paths, or if it's the universally accessible settings page. final isAuthorized = - allowedRoutes?.contains(destinationRouteName) ?? false; + authorizedPaths.any( + currentLocation.startsWith, + ) || + currentLocation.startsWith(Routes.settings); - // Universally allowed routes like 'settings' are exempt from this check. - if (!isAuthorized && destinationRouteName != Routes.settingsName) { + if (!isAuthorized) { print( - ' Action: Unauthorized access to "$destinationRouteName". ' + ' Action: Unauthorized access to "$currentLocation" for role ' + '$userRole. Authorized base paths: $authorizedPaths. ' 'Redirecting to $overviewPath.', ); // Redirect unauthorized users to the overview page. This is a safe From 1b709b2f2f566c18f1c6c8695c17ea2db9e866fe Mon Sep 17 00:00:00 2001 From: fulleni Date: Sat, 1 Nov 2025 16:08:17 +0100 Subject: [PATCH 2/2] refactor(router): improve route permission checking and error handling - Enhance the process of converting allowed route names to their base paths - Add an assertion to catch configuration errors when a route name is not defined in topLevelPaths - Maintain the functionality of filtering out null values --- lib/router/router.dart | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/router/router.dart b/lib/router/router.dart index 1d4a4151..09a55334 100644 --- a/lib/router/router.dart +++ b/lib/router/router.dart @@ -107,12 +107,17 @@ GoRouter createRouter({ // Convert the allowed route names into a list of their base paths. final authorizedPaths = allowedRouteNames - .map((name) => topLevelPaths[name]) - .whereType< - String - >() // Filter out any nulls if a name is not in the map. + .map((name) { + final path = topLevelPaths[name]; + assert( + path != null, + 'Configuration error: Route name "$name" from routePermissions is not defined in topLevelPaths.', + ); + return path; + }) + .whereType() .toList(); - + // Check if the destination path starts with any of the authorized base // paths, or if it's the universally accessible settings page. final isAuthorized =