Skip to content

Commit f98db03

Browse files
committed
style: misc
1 parent 16007a9 commit f98db03

File tree

6 files changed

+68
-71
lines changed

6 files changed

+68
-71
lines changed

lib/src/middlewares/authorization_middleware.dart

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import 'package:ht_shared/ht_shared.dart'; // For User, ForbiddenException
1313
/// permission using the [PermissionService].
1414
///
1515
/// If the user does not have the required permission, it throws a
16-
/// [ForbiddenException], which should be caught by the [errorHandler] middleware.
16+
/// [ForbiddenException], which should be caught by the 'errorHandler' middleware.
1717
///
1818
/// This middleware runs *after* authentication and model validation.
1919
/// It does NOT perform instance-level ownership checks; those are handled
@@ -28,7 +28,8 @@ Middleware authorizationMiddleware() {
2828
final user = context.read<User>();
2929
final permissionService = context.read<PermissionService>();
3030
final modelName = context.read<String>(); // Provided by data/_middleware
31-
final modelConfig = context.read<ModelConfig<dynamic>>(); // Provided by data/_middleware
31+
final modelConfig =
32+
context.read<ModelConfig<dynamic>>(); // Provided by data/_middleware
3233
final method = context.request.method;
3334

3435
// Determine the required permission configuration based on the HTTP method
@@ -45,7 +46,9 @@ Middleware authorizationMiddleware() {
4546
default:
4647
// Should ideally be caught earlier by Dart Frog's routing,
4748
// but as a safeguard, deny unsupported methods.
48-
throw const ForbiddenException('Method not supported for this resource.');
49+
throw const ForbiddenException(
50+
'Method not supported for this resource.',
51+
);
4952
}
5053

5154
// Perform the permission check based on the configuration type
@@ -67,8 +70,8 @@ Middleware authorizationMiddleware() {
6770
// Requires a specific permission string
6871
final permission = requiredPermissionConfig.permission;
6972
if (permission == null) {
70-
// This indicates a configuration error in ModelRegistry
71-
print(
73+
// This indicates a configuration error in ModelRegistry
74+
print(
7275
'[AuthorizationMiddleware] Configuration Error: specificPermission '
7376
'type requires a permission string for model "$modelName", method "$method".',
7477
);

lib/src/rbac/permissions.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ abstract class Permissions {
4444

4545
// User Preferences Permissions (User-owned)
4646
static const String userPreferencesReadOwned = 'user_preferences.read_owned';
47-
static const String userPreferencesUpdateOwned = 'user_preferences.update_owned';
47+
static const String userPreferencesUpdateOwned =
48+
'user_preferences.update_owned';
4849

4950
// Remote Config Permissions (Admin-owned/managed)
5051
static const String remoteConfigReadAdmin = 'remote_config.read_admin';

lib/src/registry/model_registry.dart

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22
// ignore_for_file: strict_raw_type, lines_longer_than_80_chars
33

44
import 'package:dart_frog/dart_frog.dart';
5-
import 'package:ht_app_settings_client/ht_app_settings_client.dart';
5+
import 'package:ht_api/src/rbac/permissions.dart'; // Import permissions
66
import 'package:ht_data_client/ht_data_client.dart';
77
import 'package:ht_shared/ht_shared.dart';
88

9-
import 'package:ht_api/src/rbac/permissions.dart'; // Import permissions
10-
119
/// Defines the type of permission check required for a specific action.
1210
enum RequiredPermissionType {
1311
/// No specific permission check is required (e.g., public access).

lib/src/services/jwt_auth_token_service.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ class JwtAuthTokenService implements AuthTokenService {
7373

7474
// Custom claims (optional, include what's useful)
7575
'email': user.email,
76-
'role': _userRoleToString(user.role), // Include the user's role as a string
76+
'role': _userRoleToString(
77+
user.role,
78+
), // Include the user's role as a string
7779
},
7880
issuer: _issuer,
7981
subject: user.id,

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

Lines changed: 41 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ Future<Response> onRequest(RequestContext context, String id) async {
1717
final requestId = context.read<RequestId>().id;
1818
// User is guaranteed non-null by requireAuthentication() middleware
1919
final authenticatedUser = context.read<User>();
20-
final permissionService = context.read<PermissionService>(); // Read PermissionService
20+
final permissionService =
21+
context.read<PermissionService>(); // Read PermissionService
2122

2223
// The main try/catch block here is removed to let the errorHandler middleware
2324
// handle all exceptions thrown by the handlers below.
@@ -82,12 +83,11 @@ Future<Response> _handleGet(
8283
// Note: This is for data *scoping* by the repository, not the permission check.
8384
// We infer user-owned based on the presence of getOwnerId function.
8485
if (modelConfig.getOwnerId != null) {
85-
userIdForRepoCall = authenticatedUser.id;
86+
userIdForRepoCall = authenticatedUser.id;
8687
} else {
87-
userIdForRepoCall = null;
88+
userIdForRepoCall = null;
8889
}
8990

90-
9191
// Repository exceptions (like NotFoundException) will propagate up to the
9292
// main onRequest try/catch (which is now removed, so they go to errorHandler).
9393
switch (modelName) {
@@ -104,8 +104,8 @@ Future<Response> _handleGet(
104104
final repo = context.read<HtDataRepository<Country>>();
105105
item = await repo.read(id: id, userId: userIdForRepoCall);
106106
case 'user': // Handle User model specifically if needed, or rely on generic
107-
final repo = context.read<HtDataRepository<User>>();
108-
item = await repo.read(id: id, userId: userIdForRepoCall);
107+
final repo = context.read<HtDataRepository<User>>();
108+
item = await repo.read(id: id, userId: userIdForRepoCall);
109109
// Add cases for other models as they are added to ModelRegistry
110110
default:
111111
// This case should ideally be caught by middleware, but added for safety
@@ -122,7 +122,7 @@ Future<Response> _handleGet(
122122
!permissionService.isAdmin(authenticatedUser)) {
123123
// Ensure getOwnerId is provided for models requiring ownership check
124124
if (modelConfig.getOwnerId == null) {
125-
print(
125+
print(
126126
'[ReqID: $requestId] Configuration Error: Model "$modelName" requires '
127127
'ownership check for GET but getOwnerId is not provided.',
128128
);
@@ -142,7 +142,6 @@ Future<Response> _handleGet(
142142
}
143143
}
144144

145-
146145
// Create metadata including the request ID and current timestamp
147146
final metadata = ResponseMetadata(
148147
requestId: requestId,
@@ -204,32 +203,30 @@ Future<Response> _handlePut(
204203
// Ensure the ID in the path matches the ID in the request body (if present)
205204
// This is a data integrity check, not an authorization check.
206205
try {
207-
final bodyItemId = modelConfig.getId(itemToUpdate);
208-
if (bodyItemId != id) {
209-
// Throw BadRequestException to be caught by the errorHandler
210-
throw BadRequestException(
211-
'Bad Request: ID in request body ("$bodyItemId") does not match ID in path ("$id").',
212-
);
213-
}
206+
final bodyItemId = modelConfig.getId(itemToUpdate);
207+
if (bodyItemId != id) {
208+
// Throw BadRequestException to be caught by the errorHandler
209+
throw BadRequestException(
210+
'Bad Request: ID in request body ("$bodyItemId") does not match ID in path ("$id").',
211+
);
212+
}
214213
} catch (e) {
215-
// Ignore if getId throws, means ID might not be in the body,
216-
// which is acceptable depending on the model/client.
217-
// Log for debugging if needed.
218-
print('[ReqID: $requestId] Warning: Could not get ID from PUT body: $e');
214+
// Ignore if getId throws, means ID might not be in the body,
215+
// which is acceptable depending on the model/client.
216+
// Log for debugging if needed.
217+
print('[ReqID: $requestId] Warning: Could not get ID from PUT body: $e');
219218
}
220219

221-
222220
// Determine userId for repository call based on ModelConfig (for data scoping/ownership enforcement)
223221
String? userIdForRepoCall;
224222
// If the model is user-owned, pass the authenticated user's ID to the repository
225223
// for ownership enforcement. Otherwise, pass null.
226-
if (modelConfig.getOwnerId != null) {
227-
userIdForRepoCall = authenticatedUser.id;
224+
if (modelConfig.getOwnerId != null) {
225+
userIdForRepoCall = authenticatedUser.id;
228226
} else {
229-
userIdForRepoCall = null;
227+
userIdForRepoCall = null;
230228
}
231229

232-
233230
dynamic updatedItem;
234231

235232
// Repository exceptions (like NotFoundException, BadRequestException)
@@ -247,7 +244,7 @@ Future<Response> _handlePut(
247244
case 'category':
248245
{
249246
final repo = context.read<HtDataRepository<Category>>();
250-
updatedItem = await repo.update(
247+
updatedItem = await repo.update(
251248
id: id,
252249
item: itemToUpdate as Category,
253250
userId: userIdForRepoCall,
@@ -256,7 +253,7 @@ Future<Response> _handlePut(
256253
case 'source':
257254
{
258255
final repo = context.read<HtDataRepository<Source>>();
259-
updatedItem = await repo.update(
256+
updatedItem = await repo.update(
260257
id: id,
261258
item: itemToUpdate as Source,
262259
userId: userIdForRepoCall,
@@ -265,16 +262,16 @@ Future<Response> _handlePut(
265262
case 'country':
266263
{
267264
final repo = context.read<HtDataRepository<Country>>();
268-
updatedItem = await repo.update(
265+
updatedItem = await repo.update(
269266
id: id,
270267
item: itemToUpdate as Country,
271268
userId: userIdForRepoCall,
272269
);
273270
}
274-
case 'user':
271+
case 'user':
275272
{
276273
final repo = context.read<HtDataRepository<User>>();
277-
updatedItem = await repo.update(
274+
updatedItem = await repo.update(
278275
id: id,
279276
item: itemToUpdate as User,
280277
userId: userIdForRepoCall,
@@ -289,7 +286,7 @@ Future<Response> _handlePut(
289286
);
290287
}
291288

292-
// --- Handler-Level Ownership Check (for PUT) ---
289+
// --- Handler-Level Ownership Check (for PUT) ---
293290
// This check is needed if the ModelConfig for PUT requires ownership
294291
// AND the user is NOT an admin (admins can bypass ownership checks).
295292
// Note: The repository *might* have already enforced ownership if userId was passed.
@@ -298,9 +295,9 @@ Future<Response> _handlePut(
298295
// (e.g., if the repo update method allows admins to update any item even if userId is passed).
299296
if (modelConfig.putPermission.requiresOwnershipCheck &&
300297
!permissionService.isAdmin(authenticatedUser)) {
301-
// Ensure getOwnerId is provided for models requiring ownership check
298+
// Ensure getOwnerId is provided for models requiring ownership check
302299
if (modelConfig.getOwnerId == null) {
303-
print(
300+
print(
304301
'[ReqID: $requestId] Configuration Error: Model "$modelName" requires '
305302
'ownership check for PUT but getOwnerId is not provided.',
306303
);
@@ -313,7 +310,7 @@ Future<Response> _handlePut(
313310
// after the update, or ideally, the update method returns the item with owner ID.
314311
// Assuming the updatedItem returned by the repo has the owner ID:
315312
final itemOwnerId = modelConfig.getOwnerId!(updatedItem);
316-
if (itemOwnerId != authenticatedUser.id) {
313+
if (itemOwnerId != authenticatedUser.id) {
317314
// This scenario should ideally not happen if the repository correctly
318315
// enforced ownership during the update call when userId was passed.
319316
// But as a defense-in-depth, we check here.
@@ -328,7 +325,6 @@ Future<Response> _handlePut(
328325
}
329326
}
330327

331-
332328
// Create metadata including the request ID and current timestamp
333329
final metadata = ResponseMetadata(
334330
requestId: requestId,
@@ -368,21 +364,21 @@ Future<Response> _handleDelete(
368364
String? userIdForRepoCall;
369365
// If the model is user-owned, pass the authenticated user's ID to the repository
370366
// for ownership enforcement. Otherwise, pass null.
371-
if (modelConfig.getOwnerId != null) {
372-
userIdForRepoCall = authenticatedUser.id;
367+
if (modelConfig.getOwnerId != null) {
368+
userIdForRepoCall = authenticatedUser.id;
373369
} else {
374-
userIdForRepoCall = null;
370+
userIdForRepoCall = null;
375371
}
376372

377373
// --- Handler-Level Ownership Check (for DELETE) ---
378374
// For DELETE, we need to fetch the item *before* attempting deletion
379375
// to perform the ownership check if required.
380376
dynamic itemToDelete;
381-
if (modelConfig.deletePermission.requiresOwnershipCheck &&
377+
if (modelConfig.deletePermission.requiresOwnershipCheck &&
382378
!permissionService.isAdmin(authenticatedUser)) {
383-
// Ensure getOwnerId is provided for models requiring ownership check
379+
// Ensure getOwnerId is provided for models requiring ownership check
384380
if (modelConfig.getOwnerId == null) {
385-
print(
381+
print(
386382
'[ReqID: $requestId] Configuration Error: Model "$modelName" requires '
387383
'ownership check for DELETE but getOwnerId is not provided.',
388384
);
@@ -406,12 +402,12 @@ Future<Response> _handleDelete(
406402
case 'country':
407403
final repo = context.read<HtDataRepository<Country>>();
408404
itemToDelete = await repo.read(id: id, userId: userIdForRepoCall);
409-
case 'user':
405+
case 'user':
410406
final repo = context.read<HtDataRepository<User>>();
411407
itemToDelete = await repo.read(id: id, userId: userIdForRepoCall);
412408
// Add cases for other models
413409
default:
414-
print(
410+
print(
415411
'[ReqID: $requestId] Error: Unsupported model type "$modelName" reached _handleDelete ownership check.',
416412
);
417413
// Throw an exception to be caught by the errorHandler
@@ -431,11 +427,10 @@ Future<Response> _handleDelete(
431427
);
432428
}
433429
}
434-
// If itemToDelete is null here, it means the item wasn't found during the read.
435-
// The subsequent delete call will likely throw NotFoundException, which is correct.
430+
// If itemToDelete is null here, it means the item wasn't found during the read.
431+
// The subsequent delete call will likely throw NotFoundException, which is correct.
436432
}
437433

438-
439434
// Allow repository exceptions (e.g., NotFoundException) to propagate
440435
// upwards to be handled by the standard error handling mechanism.
441436
switch (modelName) {
@@ -455,7 +450,7 @@ Future<Response> _handleDelete(
455450
await context
456451
.read<HtDataRepository<Country>>()
457452
.delete(id: id, userId: userIdForRepoCall);
458-
case 'user':
453+
case 'user':
459454
await context
460455
.read<HtDataRepository<User>>()
461456
.delete(id: id, userId: userIdForRepoCall);
@@ -472,7 +467,6 @@ Future<Response> _handleDelete(
472467
);
473468
}
474469

475-
476470
// Return 204 No Content for successful deletion (no body, no metadata)
477471
return Response(statusCode: HttpStatus.noContent);
478472
}

routes/api/v1/data/index.dart

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ Future<Response> onRequest(RequestContext context) async {
1717
final requestId = context.read<RequestId>().id;
1818
// User is guaranteed non-null by requireAuthentication() middleware
1919
final authenticatedUser = context.read<User>();
20-
final permissionService = context.read<PermissionService>(); // Read PermissionService
20+
final permissionService =
21+
context.read<PermissionService>(); // Read PermissionService
2122

2223
// The main try/catch block here is removed to let the errorHandler middleware
2324
// handle all exceptions thrown by the handlers below.
@@ -81,9 +82,9 @@ Future<Response> _handleGet(
8182
// Note: This is for data *scoping* by the repository, not the permission check.
8283
// We infer user-owned based on the presence of getOwnerId function.
8384
if (modelConfig.getOwnerId != null) {
84-
userIdForRepoCall = authenticatedUser.id;
85+
userIdForRepoCall = authenticatedUser.id;
8586
} else {
86-
userIdForRepoCall = null;
87+
userIdForRepoCall = null;
8788
}
8889

8990
// Repository exceptions (like NotFoundException, BadRequestException)
@@ -146,11 +147,11 @@ Future<Response> _handleGet(
146147
limit: limit,
147148
);
148149
case 'user': // Handle User model specifically if needed, or rely on generic
149-
final repo = context.read<HtDataRepository<User>>();
150-
// Note: readAll/readAllByQuery on User repo might need special handling
151-
// depending on whether non-admins can list *all* users or just their own.
152-
// Assuming for now readAll/readAllByQuery with userId scopes to owned.
153-
paginatedResponse = specificQuery.isNotEmpty
150+
final repo = context.read<HtDataRepository<User>>();
151+
// Note: readAll/readAllByQuery on User repo might need special handling
152+
// depending on whether non-admins can list *all* users or just their own.
153+
// Assuming for now readAll/readAllByQuery with userId scopes to owned.
154+
paginatedResponse = specificQuery.isNotEmpty
154155
? await repo.readAllByQuery(
155156
specificQuery,
156157
userId: userIdForRepoCall,
@@ -238,7 +239,6 @@ Future<Response> _handlePost(
238239
userIdForRepoCall = null;
239240
}
240241

241-
242242
// Process based on model type
243243
dynamic createdItem;
244244

@@ -270,12 +270,11 @@ Future<Response> _handlePost(
270270
userId: userIdForRepoCall,
271271
);
272272
case 'user': // Handle User model specifically if needed, or rely on generic
273-
final repo = context.read<HtDataRepository<User>>();
274-
// User creation is typically handled by auth routes, not generic data POST.
275-
// Throw Forbidden or BadRequest if attempted here.
276-
throw const ForbiddenException(
273+
// User creation is typically handled by auth routes, not generic data POST.
274+
// Throw Forbidden or BadRequest if attempted here.
275+
throw const ForbiddenException(
277276
'User creation is not allowed via the generic data endpoint.',
278-
);
277+
);
279278
// Add cases for other models as they are added to ModelRegistry
280279
default:
281280
// This case should ideally be caught by middleware, but added for safety

0 commit comments

Comments
 (0)