diff options
author | Nick Zolnierz <nicholas.zolnierz@mongodb.com> | 2018-08-27 16:59:22 -0400 |
---|---|---|
committer | Nick Zolnierz <nicholas.zolnierz@mongodb.com> | 2018-08-29 16:01:52 -0400 |
commit | aa36a9e8ad8c98e828f1b53966672b368d973380 (patch) | |
tree | d9c3bdb754fd34c0dd5e24fe289bcea8b0563f18 | |
parent | ebb653a9bdcb6ba1f1d4cc8113d32cdb86e6d2bb (diff) | |
download | mongo-aa36a9e8ad8c98e828f1b53966672b368d973380.tar.gz |
SERVER-36081: Write auth tests for $out and bypassDocumentValidation
-rw-r--r-- | jstests/aggregation/extras/utils.js | 6 | ||||
-rw-r--r-- | jstests/aggregation/sources/out/bypass_doc_validation.js | 135 | ||||
-rw-r--r-- | jstests/auth/lib/commands_lib.js | 97 |
3 files changed, 234 insertions, 4 deletions
diff --git a/jstests/aggregation/extras/utils.js b/jstests/aggregation/extras/utils.js index be246362ac8..bdc644d109e 100644 --- a/jstests/aggregation/extras/utils.js +++ b/jstests/aggregation/extras/utils.js @@ -238,7 +238,7 @@ function orderedArrayEq(al, ar, verbose = false) { /** * Asserts that the given aggregation fails with a specific code. Error message is optional. */ -function assertErrorCode(coll, pipe, code, errmsg) { +function assertErrorCode(coll, pipe, code, errmsg, options = {}) { if (!Array.isArray(pipe)) { pipe = [pipe]; } @@ -246,6 +246,10 @@ function assertErrorCode(coll, pipe, code, errmsg) { let cmd = {pipeline: pipe}; cmd.cursor = {batchSize: 0}; + for (let opt of Object.keys(options)) { + cmd[opt] = options[opt]; + } + let cursorRes = coll.runCommand("aggregate", cmd); if (cursorRes.ok) { let followupBatchSize = 0; // default diff --git a/jstests/aggregation/sources/out/bypass_doc_validation.js b/jstests/aggregation/sources/out/bypass_doc_validation.js new file mode 100644 index 00000000000..3b67647ef14 --- /dev/null +++ b/jstests/aggregation/sources/out/bypass_doc_validation.js @@ -0,0 +1,135 @@ +/** + * Tests for $out with bypassDocumentValidation. + * + * @tags: [assumes_unsharded_collection] + */ +(function() { + "use strict"; + + load("jstests/aggregation/extras/utils.js"); // For assertErrorCode. + + const testDB = db.getSiblingDB("out_bypass_doc_val"); + const sourceColl = testDB.getCollection("source"); + const targetColl = testDB.getCollection("target"); + + targetColl.drop(); + assert.commandWorked(testDB.createCollection(targetColl.getName(), {validator: {a: 2}})); + + sourceColl.drop(); + assert.commandWorked(sourceColl.insert({_id: 0, a: 1})); + + // Test that the bypassDocumentValidation flag is passed through to the writes on the output + // collection. + (function testBypassDocValidationTrue() { + sourceColl.aggregate([{$out: targetColl.getName()}], {bypassDocumentValidation: true}); + assert.eq([{_id: 0, a: 1}], targetColl.find().toArray()); + + sourceColl.aggregate([{$out: {to: targetColl.getName(), mode: "replaceCollection"}}], + {bypassDocumentValidation: true}); + assert.eq([{_id: 0, a: 1}], targetColl.find().toArray()); + + sourceColl.aggregate( + [{$addFields: {a: 3}}, {$out: {to: targetColl.getName(), mode: "replaceDocuments"}}], + {bypassDocumentValidation: true}); + assert.eq([{_id: 0, a: 3}], targetColl.find().toArray()); + + sourceColl.aggregate( + [ + {$replaceRoot: {newRoot: {_id: 1, a: 4}}}, + {$out: {to: targetColl.getName(), mode: "insertDocuments"}} + ], + {bypassDocumentValidation: true}); + assert.eq([{_id: 0, a: 3}, {_id: 1, a: 4}], targetColl.find().sort({_id: 1}).toArray()); + }()); + + // Test that mode "replaceDocuments" passes without the bypassDocumentValidation flag if the + // updated doc is valid. + (function testReplacementStyleUpdateWithoutBypass() { + sourceColl.aggregate( + [{$addFields: {a: 2}}, {$out: {to: targetColl.getName(), mode: "replaceDocuments"}}]); + assert.eq([{_id: 0, a: 2}], targetColl.find({_id: 0}).toArray()); + sourceColl.aggregate( + [{$addFields: {a: 2}}, {$out: {to: targetColl.getName(), mode: "replaceDocuments"}}], + {bypassDocumentValidation: false}); + assert.eq([{_id: 0, a: 2}], targetColl.find({_id: 0}).toArray()); + }()); + + function assertDocValidationFailure(cmdOptions) { + targetColl.remove({}); + assertErrorCode(sourceColl, + [{$out: targetColl.getName()}], + ErrorCodes.DocumentValidationFailure, + "Expected failure without bypass set", + cmdOptions); + + assertErrorCode(sourceColl, + [{$out: {to: targetColl.getName(), mode: "replaceCollection"}}], + ErrorCodes.DocumentValidationFailure, + "Expected failure without bypass set", + cmdOptions); + + assertErrorCode( + sourceColl, + [{$addFields: {a: 3}}, {$out: {to: targetColl.getName(), mode: "replaceDocuments"}}], + ErrorCodes.DocumentValidationFailure, + "Expected failure without bypass set", + cmdOptions); + + assertErrorCode(sourceColl, + [ + {$replaceRoot: {newRoot: {_id: 1, a: 4}}}, + {$out: {to: targetColl.getName(), mode: "insertDocuments"}} + ], + ErrorCodes.DocumentValidationFailure, + "Expected failure without bypass set", + cmdOptions); + assert.eq(0, targetColl.find().itcount()); + } + + // Test that $out fails if the output document is not valid, and the bypassDocumentValidation + // flag is not set. + assertDocValidationFailure({}); + + // Test that $out fails if the output document is not valid, and the bypassDocumentValidation + // flag is explicitly set to false. + assertDocValidationFailure({bypassDocumentValidation: false}); + + // Test that bypassDocumentValidation is *not* needed if the source collection has a + // validator but the output collection does not. + (function testDocValidatorOnSourceCollection() { + targetColl.drop(); + assert.commandWorked(testDB.runCommand({collMod: sourceColl.getName(), validator: {a: 1}})); + + sourceColl.aggregate([{$out: targetColl.getName()}]); + assert.eq([{_id: 0, a: 1}], targetColl.find().toArray()); + + sourceColl.aggregate([{$out: {to: targetColl.getName(), mode: "replaceCollection"}}]); + assert.eq([{_id: 0, a: 1}], targetColl.find().toArray()); + + sourceColl.aggregate( + [{$addFields: {a: 3}}, {$out: {to: targetColl.getName(), mode: "replaceDocuments"}}]); + assert.eq([{_id: 0, a: 3}], targetColl.find().toArray()); + + sourceColl.aggregate([ + {$replaceRoot: {newRoot: {_id: 1, a: 4}}}, + {$out: {to: targetColl.getName(), mode: "insertDocuments"}} + ]); + assert.eq([{_id: 0, a: 3}, {_id: 1, a: 4}], targetColl.find().sort({_id: 1}).toArray()); + }()); + + // Test that the bypassDocumentValidation is casted to true if the value is non-boolean. + (function testNonBooleanBypassDocValidationFlag() { + targetColl.remove({}); + assert.commandWorked(testDB.runCommand({collMod: targetColl.getName(), validator: {a: 1}})); + sourceColl.drop(); + assert.commandWorked(sourceColl.insert({_id: 0, a: 1})); + + sourceColl.aggregate([{$out: targetColl.getName()}], {bypassDocumentValidation: 5}); + assert.eq([{_id: 0, a: 1}], targetColl.find().toArray()); + + sourceColl.aggregate( + [{$addFields: {a: 3}}, {$out: {to: targetColl.getName(), mode: "replaceDocuments"}}], + {bypassDocumentValidation: "false"}); + assert.eq([{_id: 0, a: 3}], targetColl.find().toArray()); + }()); +}()); diff --git a/jstests/auth/lib/commands_lib.js b/jstests/auth/lib/commands_lib.js index cbe3891be69..c50fc1c9d99 100644 --- a/jstests/auth/lib/commands_lib.js +++ b/jstests/auth/lib/commands_lib.js @@ -6,9 +6,8 @@ of all database commands. This file contains an array of test definitions, as well as some shared test logic. -See jstests/auth/commands_builtinRoles.js and -jstests/auth/commands_userDefinedRoles.js for two separate implementations -of the test logic, respectively to test authorization with builtin roles +See jstests/auth/commands_builtin_roles.js and jstests/auth/commands_user_defined_roles.js for two +separate implementations of the test logic, respectively to test authorization with builtin roles and authorization with user-defined roles. Example test definition: @@ -1158,6 +1157,98 @@ var authCommandsLib = { ] }, { + testname: "aggregate_out_legacy_bypass_doc_validation", + command: { + aggregate: "foo", + pipeline: [{$out: "foo_out"}], + cursor: {}, + bypassDocumentValidation: true, + }, + testcases: [ + { + runOnDb: firstDbName, + // Note that the built-in role must have 'bypassDocumentValidation' for this test. + roles: {dbOwner: 1, root: 1, __system: 1}, + privileges: [ + {resource: {db: firstDbName, collection: "foo"}, actions: ["find"]}, + { + resource: {db: firstDbName, collection: "foo_out"}, + actions: ["insert", "remove", "bypassDocumentValidation"] + }, + ] + }, + ] + }, + { + testname: "aggregate_out_replace_collection_bypass_doc_validation", + command: { + aggregate: "foo", + pipeline: [{$out: {to: "foo_out", mode: "replaceCollection"}}], + cursor: {}, + bypassDocumentValidation: true, + }, + testcases: [ + { + runOnDb: firstDbName, + // Note that the built-in role must have 'bypassDocumentValidation' for this test. + roles: {dbOwner: 1, root: 1, __system: 1}, + privileges: [ + {resource: {db: firstDbName, collection: "foo"}, actions: ["find"]}, + { + resource: {db: firstDbName, collection: "foo_out"}, + actions: ["insert", "remove", "bypassDocumentValidation"] + }, + ] + }, + ] + }, + { + testname: "aggregate_out_replace_documents_bypass_doc_validation", + command: { + aggregate: "foo", + pipeline: [{$out: {to: "foo_out", mode: "replaceDocuments"}}], + cursor: {}, + bypassDocumentValidation: true, + }, + testcases: [ + { + runOnDb: firstDbName, + // Note that the built-in role must have 'bypassDocumentValidation' for this test. + roles: {dbOwner: 1, root: 1, __system: 1}, + privileges: [ + {resource: {db: firstDbName, collection: "foo"}, actions: ["find"]}, + { + resource: {db: firstDbName, collection: "foo_out"}, + actions: ["insert", "update", "bypassDocumentValidation"] + }, + ] + }, + ] + }, + { + testname: "aggregate_out_insert_documents_bypass_doc_validation", + command: { + aggregate: "foo", + pipeline: [{$out: {to: "foo_out", mode: "insertDocuments"}}], + cursor: {}, + bypassDocumentValidation: true, + }, + testcases: [ + { + runOnDb: firstDbName, + // Note that the built-in role must have 'bypassDocumentValidation' for this test. + roles: {dbOwner: 1, root: 1, __system: 1}, + privileges: [ + {resource: {db: firstDbName, collection: "foo"}, actions: ["find"]}, + { + resource: {db: firstDbName, collection: "foo_out"}, + actions: ["insert", "bypassDocumentValidation"] + }, + ] + }, + ] + }, + { testname: "aggregate_readView_writeCollection", setup: function(db) { db.createView("view", "collection", [{$match: {}}]); |