Skip to content

Commit dbc28df

Browse files
committed
fix(api): handle unauthenticated user in data routes
- Update authenticated user type from `User` to `User?` in GET and DELETE handlers - Add check for authentication when updating user content preferences - Adjust user ID retrieval logic for repository calls
1 parent 2c42f93 commit dbc28df

File tree

1 file changed

+17
-3
lines changed

1 file changed

+17
-3
lines changed

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

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ Future<Response> _handleGet(RequestContext context, String id) async {
5555
Future<Response> _handlePut(RequestContext context, String id) async {
5656
final modelName = context.read<String>();
5757
final modelConfig = context.read<ModelConfig<dynamic>>();
58-
final authenticatedUser = context.read<User>();
58+
final authenticatedUser = context.read<User?>();
5959
final permissionService = context.read<PermissionService>();
6060
final userPreferenceLimitService = context.read<UserPreferenceLimitService>();
6161

@@ -92,6 +92,12 @@ Future<Response> _handlePut(RequestContext context, String id) async {
9292
}
9393

9494
if (modelName == 'user_content_preferences') {
95+
// User content preferences can only be updated by an authenticated user.
96+
if (authenticatedUser == null) {
97+
throw const UnauthorizedException(
98+
'Authentication required to update user content preferences.',
99+
);
100+
}
95101
if (itemToUpdate is UserContentPreferences) {
96102
await userPreferenceLimitService.checkUpdatePreferences(
97103
authenticatedUser,
@@ -133,7 +139,7 @@ Future<Response> _handlePut(RequestContext context, String id) async {
133139
Future<Response> _handleDelete(RequestContext context, String id) async {
134140
final modelName = context.read<String>();
135141
final modelConfig = context.read<ModelConfig<dynamic>>();
136-
final authenticatedUser = context.read<User>();
142+
final authenticatedUser = context.read<User?>();
137143
final permissionService = context.read<PermissionService>();
138144

139145
_logger.info('Handling DELETE request for model "$modelName", id "$id".');
@@ -155,12 +161,20 @@ Future<Response> _handleDelete(RequestContext context, String id) async {
155161

156162
/// Determines the `userId` to be used for a repository call based on user
157163
/// role and model configuration.
164+
///
165+
/// If the model is user-owned and the authenticated user is not an admin,
166+
/// the authenticated user's ID is returned. Otherwise, `null` is returned,
167+
/// indicating a global operation or an admin-level bypass.
158168
String? _getUserIdForRepoCall({
159169
required ModelConfig<dynamic> modelConfig,
160170
required PermissionService permissionService,
161-
required User authenticatedUser,
171+
required User? authenticatedUser,
162172
}) {
173+
// If the model is user-owned and the user is authenticated and not an admin,
174+
// then the operation should be scoped to the authenticated user's ID.
175+
// Otherwise, it's a global operation or an admin bypass.
163176
return (modelConfig.getOwnerId != null &&
177+
authenticatedUser != null &&
164178
!permissionService.isAdmin(authenticatedUser))
165179
? authenticatedUser.id
166180
: null;

0 commit comments

Comments
 (0)