Skip to content

Commit c8dbe6c

Browse files
committed
refactor(auth): differentiate GET by collection/item
- Added collection/item permissions - Updated ModelConfig fields - Added request type check
1 parent 81c04ac commit c8dbe6c

File tree

3 files changed

+58
-16
lines changed

3 files changed

+58
-16
lines changed

lib/src/middlewares/authorization_middleware.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,21 @@ Middleware authorizationMiddleware() {
3232
context.read<ModelConfig<dynamic>>(); // Provided by data/_middleware
3333
final method = context.request.method;
3434

35+
// Determine if the request is for the collection or an item
36+
// The collection path is /api/v1/data
37+
// Item paths are /api/v1/data/[id]
38+
final isCollectionRequest = context.request.uri.path == '/api/v1/data';
39+
3540
// Determine the required permission configuration based on the HTTP method
3641
ModelActionPermission requiredPermissionConfig;
3742
switch (method) {
3843
case HttpMethod.get:
39-
requiredPermissionConfig = modelConfig.getPermission;
44+
// Differentiate GET based on whether it's a collection or item request
45+
if (isCollectionRequest) {
46+
requiredPermissionConfig = modelConfig.getCollectionPermission;
47+
} else {
48+
requiredPermissionConfig = modelConfig.getItemPermission;
49+
}
4050
case HttpMethod.post:
4151
requiredPermissionConfig = modelConfig.postPermission;
4252
case HttpMethod.put:

lib/src/registry/model_registry.dart

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ class ModelConfig<T> {
6868
const ModelConfig({
6969
required this.fromJson,
7070
required this.getId,
71-
required this.getPermission,
71+
required this.getCollectionPermission, // New field for GET collection
72+
required this.getItemPermission, // New field for GET item
7273
required this.postPermission,
7374
required this.putPermission,
7475
required this.deletePermission,
@@ -86,8 +87,11 @@ class ModelConfig<T> {
8687
/// is true for any action.
8788
final String? Function(T item)? getOwnerId;
8889

89-
/// Authorization configuration for GET requests.
90-
final ModelActionPermission getPermission;
90+
/// Authorization configuration for GET requests to the collection endpoint.
91+
final ModelActionPermission getCollectionPermission;
92+
93+
/// Authorization configuration for GET requests to a specific item endpoint.
94+
final ModelActionPermission getItemPermission;
9195

9296
/// Authorization configuration for POST requests.
9397
final ModelActionPermission postPermission;
@@ -120,7 +124,11 @@ final modelRegistry = <String, ModelConfig<dynamic>>{
120124
fromJson: Headline.fromJson,
121125
getId: (h) => h.id,
122126
// Headlines: Admin-owned, read allowed by standard/guest users
123-
getPermission: const ModelActionPermission(
127+
getCollectionPermission: const ModelActionPermission(
128+
type: RequiredPermissionType.specificPermission,
129+
permission: Permissions.headlineRead,
130+
),
131+
getItemPermission: const ModelActionPermission(
124132
type: RequiredPermissionType.specificPermission,
125133
permission: Permissions.headlineRead,
126134
),
@@ -138,7 +146,11 @@ final modelRegistry = <String, ModelConfig<dynamic>>{
138146
fromJson: Category.fromJson,
139147
getId: (c) => c.id,
140148
// Categories: Admin-owned, read allowed by standard/guest users
141-
getPermission: const ModelActionPermission(
149+
getCollectionPermission: const ModelActionPermission(
150+
type: RequiredPermissionType.specificPermission,
151+
permission: Permissions.categoryRead,
152+
),
153+
getItemPermission: const ModelActionPermission(
142154
type: RequiredPermissionType.specificPermission,
143155
permission: Permissions.categoryRead,
144156
),
@@ -156,7 +168,11 @@ final modelRegistry = <String, ModelConfig<dynamic>>{
156168
fromJson: Source.fromJson,
157169
getId: (s) => s.id,
158170
// Sources: Admin-owned, read allowed by standard/guest users
159-
getPermission: const ModelActionPermission(
171+
getCollectionPermission: const ModelActionPermission(
172+
type: RequiredPermissionType.specificPermission,
173+
permission: Permissions.sourceRead,
174+
),
175+
getItemPermission: const ModelActionPermission(
160176
type: RequiredPermissionType.specificPermission,
161177
permission: Permissions.sourceRead,
162178
),
@@ -174,7 +190,11 @@ final modelRegistry = <String, ModelConfig<dynamic>>{
174190
fromJson: Country.fromJson,
175191
getId: (c) => c.id, // Assuming Country has an 'id' field
176192
// Countries: Admin-owned, read allowed by standard/guest users
177-
getPermission: const ModelActionPermission(
193+
getCollectionPermission: const ModelActionPermission(
194+
type: RequiredPermissionType.specificPermission,
195+
permission: Permissions.countryRead,
196+
),
197+
getItemPermission: const ModelActionPermission(
178198
type: RequiredPermissionType.specificPermission,
179199
permission: Permissions.countryRead,
180200
),
@@ -193,7 +213,10 @@ final modelRegistry = <String, ModelConfig<dynamic>>{
193213
getId: (u) => u.id,
194214
getOwnerId: (dynamic item) =>
195215
(item as User).id as String?, // User is the owner of their profile
196-
getPermission: const ModelActionPermission(
216+
getCollectionPermission: const ModelActionPermission(
217+
type: RequiredPermissionType.adminOnly, // Only admin can list all users
218+
),
219+
getItemPermission: const ModelActionPermission(
197220
type: RequiredPermissionType.specificPermission,
198221
permission: Permissions.userReadOwned, // User can read their own
199222
requiresOwnershipCheck: true, // Must be the owner
@@ -218,7 +241,10 @@ final modelRegistry = <String, ModelConfig<dynamic>>{
218241
getId: (s) => s.id,
219242
getOwnerId: (dynamic item) =>
220243
(item as UserAppSettings).id as String?, // User ID is the owner ID
221-
getPermission: const ModelActionPermission(
244+
getCollectionPermission: const ModelActionPermission(
245+
type: RequiredPermissionType.unsupported, // Not accessible via collection
246+
),
247+
getItemPermission: const ModelActionPermission(
222248
type: RequiredPermissionType.specificPermission,
223249
permission: Permissions.appSettingsReadOwned,
224250
requiresOwnershipCheck: true,
@@ -244,7 +270,10 @@ final modelRegistry = <String, ModelConfig<dynamic>>{
244270
getId: (p) => p.id,
245271
getOwnerId: (dynamic item) => (item as UserContentPreferences).id
246272
as String?, // User ID is the owner ID
247-
getPermission: const ModelActionPermission(
273+
getCollectionPermission: const ModelActionPermission(
274+
type: RequiredPermissionType.unsupported, // Not accessible via collection
275+
),
276+
getItemPermission: const ModelActionPermission(
248277
type: RequiredPermissionType.specificPermission,
249278
permission: Permissions.userPreferencesReadOwned,
250279
requiresOwnershipCheck: true,
@@ -269,9 +298,12 @@ final modelRegistry = <String, ModelConfig<dynamic>>{
269298
fromJson: AppConfig.fromJson,
270299
getId: (config) => config.id,
271300
getOwnerId: null, // AppConfig is a global resource, not user-owned
272-
getPermission: const ModelActionPermission(
301+
getCollectionPermission: const ModelActionPermission(
302+
type: RequiredPermissionType.unsupported, // Not accessible via collection
303+
),
304+
getItemPermission: const ModelActionPermission(
273305
type: RequiredPermissionType
274-
.none, // Readable by any authenticated user via /api/v1/data
306+
.none, // Readable by any authenticated user via /api/v1/data/[id]
275307
),
276308
postPermission: const ModelActionPermission(
277309
type: RequiredPermissionType.adminOnly, // Only administrators can create

routes/api/v1/data/[id].dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,15 @@ Future<Response> _handleGet(
131131
}
132132

133133
// --- Handler-Level Ownership Check (for GET item) ---
134-
// This check is needed if the ModelConfig for GET requires ownership
134+
// This check is needed if the ModelConfig for GET item requires ownership
135135
// AND the user is NOT an admin (admins can bypass ownership checks).
136-
if (modelConfig.getPermission.requiresOwnershipCheck &&
136+
if (modelConfig.getItemPermission.requiresOwnershipCheck &&
137137
!permissionService.isAdmin(authenticatedUser)) {
138138
// Ensure getOwnerId is provided for models requiring ownership check
139139
if (modelConfig.getOwnerId == null) {
140140
print(
141141
'[ReqID: $requestId] Configuration Error: Model "$modelName" requires '
142-
'ownership check for GET but getOwnerId is not provided.',
142+
'ownership check for GET item but getOwnerId is not provided.',
143143
);
144144
// Throw an exception to be caught by the errorHandler
145145
throw const OperationFailedException(

0 commit comments

Comments
 (0)