Skip to content

Commit 876a772

Browse files
author
giorgiofran
committed
Secure Connection
2 parents 72cfdc4 + 20553b1 commit 876a772

15 files changed

+288
-52
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
## Recent change notes
44

5+
### 0.4.1
6+
7+
* Secure Connection
8+
* The connection string now accepts more than one server.
9+
* Before: only mongodb://www.example.org/test.
10+
* Now it can be: mongodb://www.example.org,www1.example.org,www2.example.org/test
11+
* It is equivalent to: db.pool([mongodb://www.example.org/test, mongodb://www1.example.org/test, mongodb://www2.example.org/test]);
12+
* Added an "uriList" getter in "Db" class.
13+
514
### 0.4.1-dev.2.2
615

716
* Lint clean-up

README.md

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Server-side driver library for MongoDb implemented in pure Dart.
1111

1212
```dart
1313
14-
Db db = new Db("mongodb://localhost:27017/mongo_dart-blog");
14+
var db = Db("mongodb://localhost:27017/mongo_dart-blog");
1515
await db.open();
1616
```
1717

@@ -91,13 +91,13 @@ Simple app on base of [JSON ZIPS dataset](https://media.mongodb.org/zips.json)
9191
```dart
9292
import 'package:mongo_dart/mongo_dart.dart';
9393
94-
main() async {
94+
void main() async {
9595
void displayZip(Map zip) {
9696
print(
9797
'state: ${zip["state"]}, city: ${zip["city"]}, zip: ${zip["id"]}, population: ${zip["pop"]}');
9898
}
99-
Db db =
100-
new Db("mongodb://reader:[email protected]:37468/samlple");
99+
var db =
100+
Db("mongodb://reader:[email protected]:37468/samlple");
101101
var zips = db.collection('zip');
102102
await db.open();
103103
print('''
@@ -145,12 +145,66 @@ main() async {
145145
}
146146
```
147147

148+
### Secure connection
149+
150+
You can connect using a secured tls/ssl connection in one of this two ways:
151+
152+
* setting the secure connection parameter to true in db.open()
153+
154+
```dart
155+
await db.open(secure: true);
156+
```
157+
158+
* adding a query parameter => "tls=true" (or "ssl=true").
159+
160+
```dart
161+
var db = DB('mongodb://www.example.com:27017/test?tls=true&authSource=admin');
162+
or
163+
var db = DB('mongodb://www.example.com:27017/test?ssl=true&authSource=admin');
164+
```
165+
166+
No certificates can be used.
167+
168+
### Atlas (MongoDb cloud service) connection
169+
170+
Atlas requires a tls connection, so now it is possible to connect to this cloud service.
171+
When creating a cluster Atlas shows you three ways of connecting:
172+
Mongo shell, driver and MongoDb Compass Application.
173+
The connection string is in Seedlist Connection Format (starts with mongodb+srv://).
174+
At present this driver does not support this connection string format.
175+
You can do the following:
176+
connect with the mongo shell to the address given by the site, for example:
177+
178+
```bash
179+
mongo "mongodb+srv://cluster0.xtest.mongodb.net/<database name>" --username <your Atlas user>
180+
```
181+
182+
The shell will ask you the password, enter it.
183+
184+
immediately after, the shell will show you the connection string in Standard Connection Format (starting with "mongodb://") in a line starting with "connecting to", for example.
185+
186+
```code
187+
connecting to: mongodb://cluster0-shard-00-00.xtest.mongodb.net:27017,cluster0-shard-00-02.xtest.mongodb.net:27017,cluster0-shard-00-01.xtest.mongodb.net:27017/<database name>?authSource=admin&compressors=disabled&gssapiServiceName=mongodb&replicaSet=atlas-stcn2i-shard-0&ssl=true
188+
```
189+
190+
Copy that string and add your user and password immediately after the "mongodb://" schema in the format "username:password@", for example
191+
192+
```code
193+
mongodb://<your user>:<your password>@cluster0-shard-00-00.xtest.mongodb.net:27017,cluster0-shard-00-02.xtest.mongodb.net:27017,cluster0-shard-00-01.xtest.mongodb.net:27017/<database name>?authSource=admin&compressors=disabled&gssapiServiceName=mongodb&replicaSet=atlas-stcn2i-shard-0&ssl=true
194+
```
195+
196+
Here we are, you can use the latter Connection String in the Db constructor
197+
198+
```dart
199+
var db = Db("mongodb://dbUser:[email protected]:27017,cluster0-shard-00-02.xtest.mongodb.net:27017,cluster0-shard-00-01.xtest.mongodb.net:27017/test-db?authSource=admin&compressors=disabled&gssapiServiceName=mongodb&replicaSet=atlas-stcn2i-shard-0&ssl=true");
200+
```
201+
148202
### See also
149203

150-
- [API Doc](https://pub.dev/documentation/mongo_dart/latest/)
204+
* [API Doc](https://pub.dev/documentation/mongo_dart/latest/)
151205

152-
- [Feature check list](https://github.com/vadimtsushko/mongo_dart/blob/master/doc/feature_checklist.md)
206+
* [Feature check list](https://github.com/vadimtsushko/mongo_dart/blob/master/doc/feature_checklist.md)
153207

154-
- [Recent change notes](https://github.com/vadimtsushko/mongo_dart/blob/master/changelog.md)
208+
* [Recent change notes](https://github.com/vadimtsushko/mongo_dart/blob/master/changelog.md)
155209

156-
- Additional [examples](https://github.com/vadimtsushko/mongo_dart/tree/master/example) and [tests](https://github.com/vadimtsushko/mongo_dart/tree/master/test)
210+
* Additional [examples](https://github.com/vadimtsushko/mongo_dart/tree/master/example) and [tests](https://github.com/vadimtsushko/mongo_dart/tree/master/test)

example/example.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ String port = Platform.environment['MONGO_DART_DRIVER_PORT'] ?? '27017';
66

77
void main() async {
88
var db = Db('mongodb://$host:$port/mongo_dart-blog');
9+
// Example url for Atlas connection
10+
/* var db = Db('mongodb://<atlas-user>:<atlas-password>@'
11+
'cluster0-shard-00-02.xtest.mongodb.net:27017,'
12+
'cluster0-shard-00-01.xtest.mongodb.net:27017,'
13+
'cluster0-shard-00-00.xtest.mongodb.net:27017/'
14+
'mongo_dart-blog?authSource=admin&compressors=disabled'
15+
'&gssapiServiceName=mongodb&replicaSet=atlas-stcn2i-shard-0'
16+
'&ssl=true'); */
917
var authors = <String, Map>{};
1018
var users = <String, Map>{};
1119
await db.open();

lib/mongo_dart.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ library mongo_dart;
77
import 'dart:async';
88
import 'dart:collection';
99
import 'dart:convert' show base64, utf8;
10-
import 'dart:io' show File, FileMode, IOSink, Socket;
10+
import 'dart:io' show File, FileMode, IOSink, SecureSocket, Socket;
1111
import 'dart:math';
1212
import 'dart:typed_data';
1313
import 'package:collection/collection.dart';

lib/src/database/db.dart

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,12 @@ class WriteConcern {
113113
class _UriParameters {
114114
static const authMechanism = 'authMechanism';
115115
static const authSource = 'authSource';
116+
static const tls = 'tls';
117+
static const ssl = 'ssl';
116118
}
117119

118120
class Db {
119-
final MONGO_DEFAULT_PORT = 27017;
121+
static const mongoDefaultPort = 27017;
120122
final _log = Logger('Db');
121123
final List<String> _uriList = <String>[];
122124

@@ -143,7 +145,11 @@ class Db {
143145
/// And that code direct to MongoLab server on 37637 port, database *testdb*, username *dart*, password *test*
144146
/// var db = new Db('mongodb://dart:[email protected]:37637/objectory_blog');
145147
Db(String uriString, [this._debugInfo]) {
146-
_uriList.add(uriString);
148+
if (uriString.contains(',')) {
149+
_uriList.addAll(_splitServers(uriString));
150+
} else {
151+
_uriList.add(uriString);
152+
}
147153
}
148154

149155
Db.pool(List<String> uriList, [this._debugInfo]) {
@@ -156,19 +162,59 @@ class Db {
156162

157163
_Connection get masterConnection => _connectionManager.masterConnection;
158164

159-
ServerConfig _parseUri(String uriString) {
165+
List<String> get uriList => _uriList.toList();
166+
167+
List<String> _splitServers(String uriString) {
168+
String prefix, suffix;
169+
var startServersIndex, endServersIndex;
170+
if (uriString.startsWith('mongodb://')) {
171+
startServersIndex = 10;
172+
} else {
173+
throw MongoDartError('Unexpected scheme in url $uriString. '
174+
'The url is expected to start with "mongodb://"');
175+
}
176+
endServersIndex = uriString.indexOf('/', startServersIndex);
177+
var serversString = uriString.substring(startServersIndex, endServersIndex);
178+
var credentialsIndex = serversString.indexOf('@');
179+
if (credentialsIndex != -1) {
180+
startServersIndex += credentialsIndex + 1;
181+
serversString = uriString.substring(startServersIndex, endServersIndex);
182+
}
183+
prefix = uriString.substring(0, startServersIndex);
184+
suffix = uriString.substring(endServersIndex);
185+
var parts = serversString.split(',');
186+
return [for (var server in parts) '$prefix${server.trim()}$suffix'];
187+
}
188+
189+
ServerConfig _parseUri(String uriString, {bool isSecure}) {
190+
isSecure ??= false;
160191
var uri = Uri.parse(uriString);
161192

162193
if (uri.scheme != 'mongodb') {
163194
throw MongoDartError('Invalid scheme in uri: $uriString ${uri.scheme}');
164195
}
165196

166-
var serverConfig = ServerConfig();
167-
serverConfig.host = uri.host;
168-
serverConfig.port = uri.port;
197+
uri.queryParameters.forEach((String queryParam, String value) {
198+
if (queryParam == _UriParameters.authMechanism) {
199+
selectAuthenticationMechanism(value);
200+
}
201+
202+
if (queryParam == _UriParameters.authSource) {
203+
authSourceDb = Db._authDb(value);
204+
}
169205

170-
if (serverConfig.port == null || serverConfig.port == 0) {
171-
serverConfig.port = MONGO_DEFAULT_PORT;
206+
if ((queryParam == _UriParameters.tls ||
207+
queryParam == _UriParameters.ssl) &&
208+
value == 'true') {
209+
isSecure = true;
210+
}
211+
});
212+
213+
var serverConfig = ServerConfig(
214+
uri.host ?? '127.0.0.1', uri.port ?? mongoDefaultPort, isSecure);
215+
216+
if (serverConfig.port == 0) {
217+
serverConfig.port = mongoDefaultPort;
172218
}
173219

174220
if (uri.userInfo.isNotEmpty) {
@@ -186,16 +232,6 @@ class Db {
186232
databaseName = uri.path.replaceAll('/', '');
187233
}
188234

189-
uri.queryParameters.forEach((String queryParam, String value) {
190-
if (queryParam == _UriParameters.authMechanism) {
191-
selectAuthenticationMechanism(value);
192-
}
193-
194-
if (queryParam == _UriParameters.authSource) {
195-
authSourceDb = Db._authDb(value);
196-
}
197-
});
198-
199235
return serverConfig;
200236
}
201237

@@ -255,7 +291,9 @@ class Db {
255291
return section.payload.content;
256292
}
257293

258-
Future open({WriteConcern writeConcern = WriteConcern.ACKNOWLEDGED}) {
294+
Future open(
295+
{WriteConcern writeConcern = WriteConcern.ACKNOWLEDGED,
296+
bool secure = false}) {
259297
return Future.sync(() {
260298
if (state == State.OPENING) {
261299
throw MongoDartError('Attempt to open db in state $state');
@@ -266,7 +304,7 @@ class Db {
266304
_connectionManager = _ConnectionManager(this);
267305

268306
_uriList.forEach((uri) {
269-
_connectionManager.addConnection(_parseUri(uri));
307+
_connectionManager.addConnection(_parseUri(uri, isSecure: secure));
270308
});
271309

272310
return _connectionManager.open(writeConcern);
@@ -384,7 +422,7 @@ class Db {
384422
return ListCollectionsCursor(this, filter).stream;
385423
} else {
386424
// Using system collections (pre v3.0 API)
387-
Map selector = <String, dynamic>{};
425+
var selector = <String, dynamic>{};
388426
// If we are limiting the access to a specific collection name
389427
if (filter.containsKey('name')) {
390428
selector['name'] = "${databaseName}.${filter['name']}";
@@ -599,8 +637,7 @@ class Db {
599637
if (!_masterConnection.serverCapabilities.supportsOpMsg) {
600638
return <String, Object>{};
601639
}
602-
var operation =
603-
ServerStatusOperation(this, options: options);
640+
var operation = ServerStatusOperation(this, options: options);
604641
return operation.execute();
605642
}
606643

lib/src/database/info/server_status.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ class ServerStatus {
2424
storageEngineName = serverStatus[keyStorageEngine][keyName];
2525
isPersistent = serverStatus[keyStorageEngine][keyPersistent] ?? true;
2626
if (storageEngineName == keyWiredTiger) {
27-
if (serverStatus[keyWiredTiger][keyLog][keyMaximumLogFileSize] > 0) {
27+
// Atlas service does not return the "wiredTiger" element
28+
if (!serverStatus.containsKey(keyWiredTiger) ||
29+
serverStatus[keyWiredTiger][keyLog][keyMaximumLogFileSize] > 0) {
2830
isJournaled = true;
2931
}
3032
}

lib/src/database/operation/create_index_operation.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,11 @@ const Set keysToOmit = <String>{
1818
};
1919

2020
class CreateIndexOperation extends CommandOperation {
21-
DbCollection collection;
2221
Object fieldOrSpec;
2322
Map<String, Object> indexes;
2423

25-
CreateIndexOperation(
26-
Db db, this.collection, this.fieldOrSpec, CreateIndexOptions indexOptions)
24+
CreateIndexOperation(Db db, DbCollection collection, this.fieldOrSpec,
25+
CreateIndexOptions indexOptions)
2726
: super(db, indexOptions.options,
2827
collection: collection, aspect: Aspect.writeOperation) {
2928
var indexParameters = parseIndexOptions(fieldOrSpec);

lib/src/database/server_config.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@ part of mongo_dart;
33
class ServerConfig {
44
String host;
55
int port;
6+
bool isSecure;
67
String userName;
78
String password;
8-
ServerConfig([this.host = '127.0.0.1', this.port = 27017]);
9+
ServerConfig(
10+
[this.host = '127.0.0.1',
11+
this.port = Db.mongoDefaultPort,
12+
this.isSecure = false]);
913
String get hostUrl => '$host:${port.toString()}';
1014
}

0 commit comments

Comments
 (0)