// Tests that $$USER_ROLES is able to be used in an "update" and "findAndModify" commands. // @tags: [requires_fcv_70] import {ShardingTest} from "jstests/libs/shardingtest.js"; const dbName = "test"; const collName = "coll"; function initialize(db) { let engDoc = { _id: 0, allowedRoles: ["eng-app-prod", "eng-app-stg", "read"], allowedRole: "read", comment: "only for engineering team", teamMembers: ["John", "Ashley", "Gina"], yearlyEduBudget: 15000, yearlyTnEBudget: 2000, salesWins: 1000 }; let salesDoc = { _id: 1, allowedRoles: ["sales-person"], allowedRole: "observe", comment: "only for sales team", salesWins: 1000 }; let testUpdate = {_id: 2, allowedRole: "test", teamMembersRights: ["testUpdate"]}; let testFindAndModify = { _id: 3, allowedRole: "write", teamMembersRights: ["testFindAndModify"] }; let coll = db.getCollection(collName); assert.commandWorked(coll.insertMany([engDoc, salesDoc, testUpdate, testFindAndModify])); } // Test accessing $$USER_ROLES in the query portion of "update" command. function runUpdateQuery(db) { let coll = db.getCollection(collName); let pre = coll.findOne( {$expr: {$eq: [{$setIntersection: ["$allowedRoles", "$$USER_ROLES.role"]}, []]}}); var preSalesWins = pre.salesWins; assert.commandWorked(coll.update( {$expr: {$eq: [{$setIntersection: ["$allowedRoles", "$$USER_ROLES.role"]}, []]}}, {$inc: {salesWins: 1000}}, {multi: true})); let post = coll.findOne( {$expr: {$eq: [{$setIntersection: ["$allowedRoles", "$$USER_ROLES.role"]}, []]}}); var postSalesWins = post.salesWins; assert.eq(postSalesWins, preSalesWins + 1000); } // Test accessing $$USER_ROLES in the update portion of "update" command. function runUpdateUpdate(db) { let coll = db.getCollection(collName); assert.commandWorked( coll.update({_id: 2}, [{$set: {"teamMembersRights": "$$USER_ROLES.role"}}])); let post = coll.findOne({_id: 2}); let expectedResult = { _id: 2, allowedRole: "test", teamMembersRights: ["readWriteAnyDatabase", "read"] }; assert.eq(post, expectedResult); } // Test accessing $$USER_ROLES in the query portion of "findAndModify" command. function runFindAndModifyQuery(db) { let coll = db.getCollection(collName); let pre = coll.findOne({$expr: {allowedRole: "$$USER_ROLES.role"}}); var preSalesWins = pre.salesWins; db.coll.findAndModify({ query: {allowedRole: "read", $expr: {allowedRole: "$$USER_ROLES.role"}}, update: {$inc: {salesWins: 1000}} }); let post = coll.findOne({$expr: {allowedRole: "$$USER_ROLES.role"}}); var postSalesWins = post.salesWins; assert.eq(postSalesWins, preSalesWins + 1000); } // Test accessing $$USER_ROLES in the update portion of "findAndModify" command. function runFindAndModifyUpdate(db) { let coll = db.getCollection(collName); coll.findAndModify({ query: {allowedRole: "write"}, update: [{$set: {"teamMembersRights": "$$USER_ROLES.role"}}] }); let post = coll.findOne({_id: 3}); let expectedResult = { _id: 3, allowedRole: "write", teamMembersRights: ["readWriteAnyDatabase", "read"] }; assert.eq(post, expectedResult); } function runTest(conn, st = null) { // Create a user on the admin database. let admin = conn.getDB("admin"); assert.commandWorked(admin.runCommand({createUser: "admin", pwd: "admin", roles: ["root"]})); admin.auth("admin", "admin"); if (st) { // Shard the collection that will be used in the update and findAndModify commands. assert.commandWorked(conn.getDB("admin").runCommand({enableSharding: dbName})); st.shardColl(conn.getDB(dbName).getCollection(collName), {allowedRole: 1}); } const db = conn.getDB(dbName); // Create a user that has roles on more than one database. The readWriteAnyDatabase is // necessary for the inserts that follow to work. assert.commandWorked(db.runCommand({ createUser: "user", pwd: "pwd", roles: [{role: "readWriteAnyDatabase", db: "admin"}, {role: "read", db: dbName}] })); // Logout of the admin user so that we can log into the other user so we can access those // roles with $$USER_ROLES below. admin.logout(); db.auth("user", "pwd"); initialize(db); runUpdateQuery(db); runUpdateUpdate(db); runFindAndModifyQuery(db); runFindAndModifyUpdate(db); db.logout(); } jsTest.log("Test standalone"); const mongod = MongoRunner.runMongod({auth: ""}); runTest(mongod); MongoRunner.stopMongod(mongod); jsTest.log("Test sharded cluster"); const st = new ShardingTest({ mongos: 1, config: 1, shards: 2, keyFile: 'jstests/libs/key1', }); runTest(st.s, st); st.stop();