summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Zolnierz <nicholas.zolnierz@mongodb.com>2018-08-27 16:59:22 -0400
committerNick Zolnierz <nicholas.zolnierz@mongodb.com>2018-08-29 16:01:52 -0400
commitaa36a9e8ad8c98e828f1b53966672b368d973380 (patch)
treed9c3bdb754fd34c0dd5e24fe289bcea8b0563f18
parentebb653a9bdcb6ba1f1d4cc8113d32cdb86e6d2bb (diff)
downloadmongo-aa36a9e8ad8c98e828f1b53966672b368d973380.tar.gz
SERVER-36081: Write auth tests for $out and bypassDocumentValidation
-rw-r--r--jstests/aggregation/extras/utils.js6
-rw-r--r--jstests/aggregation/sources/out/bypass_doc_validation.js135
-rw-r--r--jstests/auth/lib/commands_lib.js97
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: {}}]);