Skip to content

Commit f33667d

Browse files
authored
Merge pull request #35 from flutter-news-app-full-source-code/enhanceuser-route-put
Enhanceuser route put
2 parents a575011 + 8c05e11 commit f33667d

File tree

2 files changed

+42
-2
lines changed

2 files changed

+42
-2
lines changed

lib/src/rbac/role_permissions.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,16 @@ final Set<String> _appGuestUserPermissions = {
1414
Permissions.userContentPreferencesReadOwned,
1515
Permissions.userContentPreferencesUpdateOwned,
1616
Permissions.remoteConfigRead,
17+
// Allows a user to update their own User object. This is essential for
18+
// features like updating the `feedActionStatus` (e.g., when a user
19+
// dismisses an in-feed prompt, etc). The endpoint handler ensures only
20+
// non-sensitive fields can be modified.
21+
Permissions.userUpdateOwned,
1722
};
1823

1924
final Set<String> _appStandardUserPermissions = {
2025
..._appGuestUserPermissions,
2126
Permissions.userReadOwned,
22-
Permissions.userUpdateOwned,
2327
Permissions.userDeleteOwned,
2428
};
2529

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

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,9 +324,45 @@ Future<Response> _handlePut(
324324
case 'user':
325325
{
326326
final repo = context.read<DataRepository<User>>();
327+
328+
// --- Safe User Update Logic ---
329+
// To prevent security vulnerabilities like privilege escalation, we do not
330+
// simply save the entire request body. Instead, we perform a safe,
331+
// partial update.
332+
333+
// 1. Fetch the existing, trusted user object from the database.
334+
// This ensures we have the current, authoritative state of the user,
335+
// including their correct roles and ID.
336+
final existingUser = await repo.read(
337+
id: id,
338+
userId: userIdForRepoCall,
339+
);
340+
341+
// 2. Create a new User object by merging only the allowed, safe-to-update
342+
// fields from the incoming request (`itemToUpdate`) into the
343+
// existing user data.
344+
// This is the most critical step. It guarantees that a user cannot
345+
// change their own `appRole`, `dashboardRole`, `id`, or `createdAt`
346+
// fields, even if they include them in the request payload.
347+
// The `email` field is also protected here, as changing it requires a
348+
// separate, secure verification flow (e.g., via a dedicated endpoint)
349+
// and should not be done through this generic data endpoint.
350+
final updatedUser = existingUser.copyWith(
351+
// `feedActionStatus` is considered safe for a user to update as it
352+
// only tracks their interaction with UI elements.
353+
feedActionStatus: (itemToUpdate as User).feedActionStatus,
354+
355+
// FUTURE: If a `displayName` field were added to the User model,
356+
// it would also be considered safe and could be updated here:
357+
// displayName: itemToUpdate.displayName,
358+
);
359+
360+
// 3. Save the securely merged user object back to the database.
361+
// The repository will now update the user record with our safely
362+
// constructed `updatedUser` object.
327363
updatedItem = await repo.update(
328364
id: id,
329-
item: itemToUpdate as User,
365+
item: updatedUser,
330366
userId: userIdForRepoCall,
331367
);
332368
}

0 commit comments

Comments
 (0)