1
1
import 'package:core/core.dart' ;
2
2
import 'package:dart_frog/dart_frog.dart' ;
3
3
import 'package:flutter_news_app_api_server_full_source_code/src/rbac/permission_service.dart' ;
4
- import 'package:flutter_news_app_api_server_full_source_code/src/registry/model_registry.dart' ;
5
4
import 'package:logging/logging.dart' ;
6
5
7
6
final _log = Logger ('AuthorizationMiddleware' );
8
7
9
8
/// {@template authorization_middleware}
10
- /// Middleware to enforce role-based permissions and model-specific access rules .
9
+ /// Middleware to enforce role-based permissions.
11
10
///
12
- /// This middleware reads the authenticated [User] , the requested `modelName` ,
13
- /// the `HttpMethod` , and the `ModelConfig` from the request context. It then
14
- /// determines the required permission based on the `ModelConfig` metadata for
15
- /// the specific HTTP method and checks if the authenticated user has that
11
+ /// This middleware reads the authenticated [User] and a required `permission`
12
+ /// string from the request context. It then checks if the user has that
16
13
/// permission using the [PermissionService] .
17
14
///
15
+ /// The required permission string must be provided into the context by an
16
+ /// earlier middleware, typically one specific to the route group.
17
+ ///
18
18
/// If the user does not have the required permission, it throws a
19
- /// [ForbiddenException] , which should be caught by the ' errorHandler' middleware.
19
+ /// [ForbiddenException] , which should be caught by the ` errorHandler` middleware.
20
20
///
21
- /// This middleware runs *after* authentication and model validation.
22
- /// It does NOT perform instance-level ownership checks; those are handled
23
- /// by the route handlers (`index.dart` , `[id].dart` ) if required by the
24
- /// `ModelActionPermission.requiresOwnershipCheck` flag.
21
+ /// This middleware runs *after* authentication.
25
22
/// {@endtemplate}
26
23
Middleware authorizationMiddleware () {
27
24
return (handler) {
@@ -30,90 +27,22 @@ Middleware authorizationMiddleware() {
30
27
// User is guaranteed non-null by requireAuthentication() middleware.
31
28
final user = context.read <User >();
32
29
final permissionService = context.read <PermissionService >();
33
- final modelName = context.read <String >(); // Provided by data/_middleware
34
- final modelConfig = context
35
- .read <ModelConfig <dynamic >>(); // Provided by data/_middleware
36
- final method = context.request.method;
37
-
38
- // Determine if the request is for the collection or an item
39
- // The collection path is /api/v1/data
40
- // Item paths are /api/v1/data/[id]
41
- final isCollectionRequest = context.request.uri.path == '/api/v1/data' ;
30
+ final permission = context.read <String >();
42
31
43
- // Determine the required permission configuration based on the HTTP method
44
- ModelActionPermission requiredPermissionConfig;
45
- switch (method) {
46
- case HttpMethod .get :
47
- // Differentiate GET based on whether it's a collection or item request
48
- if (isCollectionRequest) {
49
- requiredPermissionConfig = modelConfig.getCollectionPermission;
50
- } else {
51
- requiredPermissionConfig = modelConfig.getItemPermission;
52
- }
53
- case HttpMethod .post:
54
- requiredPermissionConfig = modelConfig.postPermission;
55
- case HttpMethod .put:
56
- requiredPermissionConfig = modelConfig.putPermission;
57
- case HttpMethod .delete:
58
- requiredPermissionConfig = modelConfig.deletePermission;
59
- default :
60
- // Should ideally be caught earlier by Dart Frog's routing,
61
- // but as a safeguard, deny unsupported methods.
62
- throw const ForbiddenException (
63
- 'Method not supported for this resource.' ,
64
- );
32
+ if (! permissionService.hasPermission (user, permission)) {
33
+ _log.warning (
34
+ 'User ${user .id } denied access to permission "$permission ".' ,
35
+ );
36
+ throw const ForbiddenException (
37
+ 'You do not have permission to perform this action.' ,
38
+ );
65
39
}
66
40
67
- // Perform the permission check based on the configuration type
68
- switch (requiredPermissionConfig.type) {
69
- case RequiredPermissionType .none:
70
- // No specific permission required (beyond authentication if applicable)
71
- // This case is primarily for documentation/completeness if a route
72
- // group didn't require authentication, but the /data route does.
73
- // For the /data route, 'none' effectively means 'authenticated users allowed'.
74
- break ;
75
- case RequiredPermissionType .adminOnly:
76
- // Requires the user to be an admin
77
- if (! permissionService.isAdmin (user)) {
78
- throw const ForbiddenException (
79
- 'Only administrators can perform this action.' ,
80
- );
81
- }
82
- case RequiredPermissionType .specificPermission:
83
- // Requires a specific permission string
84
- final permission = requiredPermissionConfig.permission;
85
- if (permission == null ) {
86
- // This indicates a configuration error in ModelRegistry
87
- _log.severe (
88
- 'Configuration Error: specificPermission type requires a '
89
- 'permission string for model "$modelName ", method "$method ".' ,
90
- );
91
- throw const OperationFailedException (
92
- 'Internal Server Error: Authorization configuration error.' ,
93
- );
94
- }
95
- if (! permissionService.hasPermission (user, permission)) {
96
- throw const ForbiddenException (
97
- 'You do not have permission to perform this action.' ,
98
- );
99
- }
100
- case RequiredPermissionType .unsupported:
101
- // This action is explicitly marked as not supported via this generic route.
102
- // Return Method Not Allowed.
103
- _log.warning (
104
- 'Action for model "$modelName ", method "$method " is marked as '
105
- 'unsupported via generic route.' ,
106
- );
107
- // Throw ForbiddenException to be caught by the errorHandler
108
- throw ForbiddenException (
109
- 'Method "$method " is not supported for model "$modelName " '
110
- 'via this generic data endpoint.' ,
111
- );
112
- }
41
+ _log.finer (
42
+ 'User ${user .id } granted access to permission "$permission ".' ,
43
+ );
113
44
114
- // If all checks pass, proceed to the next handler in the chain.
115
- // Instance-level ownership checks (if requiredPermissionConfig.requiresOwnershipCheck is true)
116
- // are handled by the route handlers themselves.
45
+ // If the check passes, proceed to the next handler.
117
46
return handler (context);
118
47
};
119
48
};
0 commit comments