diff --git a/CHANGELOG.md b/CHANGELOG.md index 93ad432d..3d65fa20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 2.0.2 +* Fix flaw where clients could remove all records, even if `allowRemoveAll` were `false`. + # 2.0.1 * Override `readMany` and `findOne`. diff --git a/analysis_options.yaml b/analysis_options.yaml index eae1e42a..c230cee7 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,3 +1,4 @@ +include: package:pedantic/analysis_options.yaml analyzer: strong-mode: implicit-casts: false \ No newline at end of file diff --git a/lib/mongo_service.dart b/lib/mongo_service.dart index 38e25951..9a3e5deb 100644 --- a/lib/mongo_service.dart +++ b/lib/mongo_service.dart @@ -12,10 +12,12 @@ class MongoService extends Service> { /// If set to `true`, parameters in `req.query` are applied to the database query. final bool allowQuery; + /// No longer used. Will be removed by `2.1.0`. + @deprecated final bool debug; MongoService(DbCollection this.collection, - {this.allowRemoveAll: false, this.allowQuery: true, this.debug: true}) + {this.allowRemoveAll = false, this.allowQuery = true, this.debug = true}) : super(); SelectorBuilder _makeQuery([Map params_]) { @@ -51,7 +53,7 @@ class MongoService extends Service> { } } else if (key == 'query' && (allowQuery == true || !params.containsKey('provider'))) { - Map query = params[key]; + var query = params[key] as Map; query.forEach((key, v) { var value = v is Map ? _filterNoQuery(v) : v; @@ -99,7 +101,7 @@ class MongoService extends Service> { var item = _removeSensitive(data); try { - String nonce = (await collection.db.getNonce())['nonce']; + var nonce = (await collection.db.getNonce())['nonce'] as String; var result = await collection.findAndModify( query: where.eq(_NONCE_KEY, nonce), update: item, @@ -209,12 +211,16 @@ class MongoService extends Service> { @override Future> remove(String id, [Map params]) async { - if (id == null || - id == 'null' && - (allowRemoveAll == true || - params?.containsKey('provider') != true)) { - await collection.remove(null); - return {}; + if (id == null || id == 'null') { + // Remove everything... + if (!(allowRemoveAll == true || + params?.containsKey('provider') != true)) { + throw AngelHttpException.forbidden( + message: 'Clients are not allowed to delete all items.'); + } else { + await collection.remove(null); + return {}; + } } // var result = await read(id, params); diff --git a/pubspec.yaml b/pubspec.yaml index d34ceacd..512ab659 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_mongo -version: 2.0.1 +version: 2.0.2 description: MongoDB-enabled services for the Angel framework. Well-tested. author: Tobe O homepage: https://github.com/angel-dart/angel_mongo @@ -12,4 +12,5 @@ dependencies: mongo_dart: ">= 0.2.7 < 1.0.0" dev_dependencies: http: ">= 0.11.3 < 0.12.0" + pedantic: ^1.0.0 test: ^1.0.0 diff --git a/test/generic_test.dart b/test/generic_test.dart index e47fdf2c..74d3adba 100644 --- a/test/generic_test.dart +++ b/test/generic_test.dart @@ -80,8 +80,8 @@ main() { response = await client.get("$url/api"); expect(response.statusCode, isIn([200, 201])); - List users = - god.deserialize(response.body, outputType: [].runtimeType); + var users = god.deserialize(response.body, + outputType: [].runtimeType) as List; expect(users.length, equals(1)); }); @@ -89,11 +89,11 @@ main() { var response = await client.post("$url/api", body: god.serialize(testGreeting), headers: headers); expect(response.statusCode, isIn([200, 201])); - Map created = god.deserialize(response.body); + var created = god.deserialize(response.body) as Map; response = await client.get("$url/api/${created['id']}"); expect(response.statusCode, isIn([200, 201])); - Map read = god.deserialize(response.body); + var read = god.deserialize(response.body) as Map; expect(read['id'], equals(created['id'])); expect(read['to'], equals('world')); //expect(read['createdAt'], isNot(null)); @@ -103,7 +103,7 @@ main() { var response = await client.post("$url/api", body: god.serialize(testGreeting), headers: headers); expect(response.statusCode, isIn([200, 201])); - Map created = god.deserialize(response.body); + var created = god.deserialize(response.body) as Map; var id = new ObjectId.fromHexString(created['id'] as String); var read = await greetingService.findOne({'query': where.id(id)}); @@ -116,7 +116,7 @@ main() { var response = await client.post("$url/api", body: god.serialize(testGreeting), headers: headers); expect(response.statusCode, isIn([200, 201])); - Map created = god.deserialize(response.body); + var created = god.deserialize(response.body) as Map; var id = new ObjectId.fromHexString(created['id'] as String); var read = await greetingService.readMany([id.toHexString()]); @@ -128,11 +128,11 @@ main() { var response = await client.post("$url/api", body: god.serialize(testGreeting), headers: headers); expect(response.statusCode, isIn([200, 201])); - Map created = god.deserialize(response.body); + var created = god.deserialize(response.body) as Map; response = await client.patch("$url/api/${created['id']}", body: god.serialize({"to": "Mom"}), headers: headers); - Map modified = god.deserialize(response.body); + var modified = god.deserialize(response.body) as Map; expect(response.statusCode, isIn([200, 201])); expect(modified['id'], equals(created['id'])); expect(modified['to'], equals('Mom')); @@ -143,11 +143,11 @@ main() { var response = await client.post("$url/api", body: god.serialize(testGreeting), headers: headers); expect(response.statusCode, isIn([200, 201])); - Map created = god.deserialize(response.body); + var created = god.deserialize(response.body) as Map; response = await client.post("$url/api/${created['id']}", body: god.serialize({"to": "Updated"}), headers: headers); - Map modified = god.deserialize(response.body); + var modified = god.deserialize(response.body) as Map; expect(response.statusCode, isIn([200, 201])); expect(modified['id'], equals(created['id'])); expect(modified['to'], equals('Updated')); @@ -157,7 +157,7 @@ main() { test('remove item', () async { var response = await client.post("$url/api", body: god.serialize(testGreeting), headers: headers); - Map created = god.deserialize(response.body); + var created = god.deserialize(response.body) as Map; int lastCount = (await greetingService.index()).length; @@ -165,6 +165,11 @@ main() { expect((await greetingService.index()).length, equals(lastCount - 1)); }); + test('cannot remove all unless explicitly set', () async { + var response = await client.delete('$url/api/null'); + expect(response.statusCode, 403); + }); + test('\$sort and query parameters', () async { // Search by where.eq Map world = await greetingService.create({"to": "world"}); @@ -173,8 +178,8 @@ main() { var response = await client.get("$url/api?to=world"); print(response.body); - List queried = - god.deserialize(response.body, outputType: [].runtimeType); + var queried = god.deserialize(response.body, + outputType: [].runtimeType) as List; expect(queried.length, equals(1)); expect(queried[0].keys.length, equals(2)); expect(queried[0]["id"], equals(world["id"]));