Skip to content

Commit 662bfe5

Browse files
committed
feat(auth): adapt PUT handler for secure user updates
Modifies the _handlePut function to introduce special handling for the 'user' model. When updating a user, the raw request body map is now passed directly to the _updateItem function, bypassing the standard fromJson deserialization. This change enables the custom user updater in the DataOperationRegistry to perform a secure, selective merge of updatable fields (like roles), preventing mass assignment vulnerabilities. For all other models, the behavior remains unchanged.
1 parent e7978b2 commit 662bfe5

File tree

1 file changed

+31
-17
lines changed

1 file changed

+31
-17
lines changed

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

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -68,27 +68,41 @@ Future<Response> _handlePut(RequestContext context, String id) async {
6868

6969
requestBody['updatedAt'] = DateTime.now().toUtc().toIso8601String();
7070

71+
// The item to be passed to the updater function.
72+
// For 'user' updates, this will be the raw request body map to allow for
73+
// secure, selective field merging in the DataOperationRegistry.
74+
// For all other models, it's the deserialized object.
7175
dynamic itemToUpdate;
72-
try {
73-
itemToUpdate = modelConfig.fromJson(requestBody);
74-
} on TypeError catch (e, s) {
75-
_logger.warning('Deserialization TypeError in PUT /data/[id]', e, s);
76-
throw const BadRequestException(
77-
'Invalid request body: Missing or invalid required field(s).',
78-
);
79-
}
8076

81-
try {
82-
final bodyItemId = modelConfig.getId(itemToUpdate);
83-
if (bodyItemId != id) {
84-
throw BadRequestException(
85-
'Bad Request: ID in request body ("$bodyItemId") does not match ID in path ("$id").',
77+
if (modelName == 'user') {
78+
// For user updates, we pass the raw map to the updater.
79+
// This allows the updater to selectively apply fields, preventing mass
80+
// assignment vulnerabilities. The ID check is also skipped as the request
81+
// body for a user role update will not contain an ID.
82+
_logger.finer('User model update: using raw request body for updater.');
83+
itemToUpdate = requestBody;
84+
} else {
85+
// For all other models, deserialize the body into a model instance.
86+
try {
87+
itemToUpdate = modelConfig.fromJson(requestBody);
88+
} on TypeError catch (e, s) {
89+
_logger.warning('Deserialization TypeError in PUT /data/[id]', e, s);
90+
throw const BadRequestException(
91+
'Invalid request body: Missing or invalid required field(s).',
8692
);
8793
}
88-
} catch (e) {
89-
// Ignore if getId throws, as the ID might not be in the body,
90-
// which can be acceptable for some models.
91-
_logger.info('Could not get ID from PUT body: $e');
94+
95+
// Validate that the ID in the body matches the ID in the path.
96+
try {
97+
final bodyItemId = modelConfig.getId(itemToUpdate);
98+
if (bodyItemId != id) {
99+
throw BadRequestException(
100+
'Bad Request: ID in request body ("$bodyItemId") does not match ID in path ("$id").',
101+
);
102+
}
103+
} catch (e) {
104+
_logger.info('Could not get ID from PUT body: $e');
105+
}
92106
}
93107

94108
if (modelName == 'user_content_preferences') {

0 commit comments

Comments
 (0)