From 80e28c5d8bc1c4f3bb42863851d0e52aba258988 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Thu, 24 Sep 2026 00:21:06 +0300 Subject: [PATCH] fix(mongodb): handle unauthorized listDatabases on admin by falling back to connection database --- lib/core/database/mongodb_connection.dart | 24 +++++++++++++ .../mongodb/mongo_databases_view.dart | 13 +++++++ .../mongodb_connection_fallback_test.dart | 35 +++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 test/core/database/mongodb_connection_fallback_test.dart diff --git a/lib/core/database/mongodb_connection.dart b/lib/core/database/mongodb_connection.dart index c49b0e1..7f96222 100644 --- a/lib/core/database/mongodb_connection.dart +++ b/lib/core/database/mongodb_connection.dart @@ -45,6 +45,24 @@ class MongoConnection { Db? _db; bool _isConnected = false; + /// Effective database name from [database] configuration or connection URI path. + String? get effectiveDatabase { + if (database != null && database!.trim().isNotEmpty) { + return database!.trim(); + } + final rawUri = _sessionUri ?? _connectionString; + if (rawUri != null && rawUri.isNotEmpty) { + try { + final parsed = Uri.parse(rawUri); + final path = parsed.path.replaceFirst('/', '').trim(); + if (path.isNotEmpty) { + return path; + } + } catch (_) {} + } + return null; + } + /// Scrubs sensitive in-memory credentials once the network handshake completes. /// /// Getters [password] and [connectionString] become null. The live session @@ -298,6 +316,12 @@ class MongoConnection { .where((name) => name.isNotEmpty) .toList(); } catch (e) { + final fallback = effectiveDatabase; + if (fallback != null && + fallback.isNotEmpty && + fallback.toLowerCase() != 'admin') { + return [fallback]; + } rethrow; } } diff --git a/lib/features/mongodb/mongo_databases_view.dart b/lib/features/mongodb/mongo_databases_view.dart index 31aeaf1..1e3a8dc 100644 --- a/lib/features/mongodb/mongo_databases_view.dart +++ b/lib/features/mongodb/mongo_databases_view.dart @@ -135,6 +135,19 @@ class _MongoDatabasesViewState extends State { }); } } catch (e) { + final fallback = _connection?.effectiveDatabase; + if (fallback != null && + fallback.isNotEmpty && + fallback.toLowerCase() != 'admin') { + if (mounted) { + setState(() { + _databases = [_DatabaseInfo(name: fallback, sizeOnDisk: null)]; + _isLoading = false; + _error = null; + }); + } + return; + } if (mounted) { setState(() { _error = 'Failed to list databases: $e'; diff --git a/test/core/database/mongodb_connection_fallback_test.dart b/test/core/database/mongodb_connection_fallback_test.dart new file mode 100644 index 0000000..7abebe1 --- /dev/null +++ b/test/core/database/mongodb_connection_fallback_test.dart @@ -0,0 +1,35 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:querya_desktop/core/database/mongodb_connection.dart'; + +void main() { + group('MongoConnection.effectiveDatabase', () { + test('returns database from property if configured', () { + final conn = MongoConnection( + id: 1, + name: 'test', + host: 'localhost', + database: ' analytics_prod ', + ); + expect(conn.effectiveDatabase, 'analytics_prod'); + }); + + test('returns database from connectionString URI path if database property is omitted', () { + final conn = MongoConnection( + id: 1, + name: 'test', + host: 'localhost', + connectionString: 'mongodb://user:secret@mongo.cloud.com:27017/tenant_db?ssl=true', + ); + expect(conn.effectiveDatabase, 'tenant_db'); + }); + + test('returns null when neither property nor URI has a database', () { + final conn = MongoConnection( + id: 1, + name: 'test', + host: 'localhost', + ); + expect(conn.effectiveDatabase, isNull); + }); + }); +}