Skip to content

Commit 1da10e8

Browse files
committed
SERVER-9515 Add shell helpers for role management commands
1 parent 18a4798 commit 1da10e8

File tree

3 files changed

+235
-11
lines changed

3 files changed

+235
-11
lines changed

jstests/role_management_helpers.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// This test is a basic sanity check of the shell helpers for manipulating role objects
2+
// It is not a comprehensive test of the functionality of the role manipulation commands
3+
4+
function assertHasRole(rolesArray, roleName, roleDB) {
5+
for (i in rolesArray) {
6+
var curRole = rolesArray[i];
7+
if (curRole.name == roleName && curRole.db == roleDB) {
8+
return;
9+
}
10+
}
11+
assert(false, "role " + roleName + "@" + roleDB + " not found in array: " + tojson(rolesArray));
12+
}
13+
14+
function assertHasPrivilege(privilegeArray, privilege) {
15+
for (i in privilegeArray) {
16+
var curPriv = privilegeArray[i];
17+
if (curPriv.resource.cluster == privilege.resource.cluster &&
18+
curPriv.resource.anyResource == privilege.resource.anyResource &&
19+
curPriv.resource.db == privilege.resource.db &&
20+
curPriv.resource.collection == privilege.resource.collection) {
21+
// Same resource
22+
assert.eq(curPriv.actions.length, privilege.actions.length);
23+
for (k in curPriv.actions) {
24+
assert.eq(curPriv.actions[k], privilege.actions[k]);
25+
}
26+
return;
27+
}
28+
}
29+
assert(false, "Privilege " + tojson(privilege) + " not found in privilege array: " +
30+
tojson(privilegeArray));
31+
}
32+
33+
(function(db) {
34+
var db = db.getSiblingDB("role_management_helpers");
35+
db.dropDatabase();
36+
db.dropAllRoles();
37+
38+
db.addRole({name:'roleA', roles: [], privileges: [{resource: {db:db.getName(), collection: ""},
39+
actions: ['find']}]});
40+
db.addRole({name:'roleB', privileges: [], roles: ["roleA"]});
41+
db.addRole({name:'roleC', privileges: [], roles: []});
42+
43+
// Test getRole
44+
var roleObj = db.getRole("roleA");
45+
assert.eq(0, roleObj.roles.length);
46+
assert.eq(1, roleObj.privileges.length);
47+
assertHasPrivilege(roleObj.privileges,
48+
{resource: {db:db.getName(), collection:""}, actions:['find']});
49+
roleObj = db.getRole("roleB");
50+
assert.eq(1, roleObj.privileges.length); // inherited from roleA
51+
assertHasPrivilege(roleObj.privileges,
52+
{resource: {db:db.getName(), collection:""}, actions:['find']});
53+
assert.eq(1, roleObj.roles.length);
54+
assertHasRole(roleObj.roles, "roleA", db.getName());
55+
56+
// Granting roles to nonexistent role fails
57+
assert.throws(function() { db.grantRolesToRole("fakeRole", ['dbAdmin']); });
58+
// Granting roles to built-in role fails
59+
assert.throws(function() { db.grantRolesToRole("readWrite", ['dbAdmin']); });
60+
// Granting non-existant role fails
61+
assert.throws(function() { db.grantRolesToRole("roleB", ['dbAdmin', 'fakeRole']); });
62+
63+
roleObj = db.getRole("roleB");
64+
assert.eq(1, roleObj.privileges.length);
65+
assert.eq(1, roleObj.roles.length);
66+
assertHasRole(roleObj.roles, "roleA", db.getName());
67+
68+
// Granting a role you already have is no problem
69+
db.grantRolesToRole("roleB", ['readWrite', 'roleC']);
70+
roleObj = db.getRole("roleB");
71+
assert.gt(roleObj.privileges.length, 1); // Got privileges from readWrite role
72+
assert.eq(3, roleObj.roles.length);
73+
assertHasRole(roleObj.roles, "readWrite", db.getName());
74+
assertHasRole(roleObj.roles, "roleA", db.getName());
75+
assertHasRole(roleObj.roles, "roleC", db.getName());
76+
77+
// Revoking roles the role doesn't have is fine
78+
db.revokeRolesFromRole("roleB", ['roleA', 'readWrite', 'dbAdmin']);
79+
roleObj = db.getRole("roleB");
80+
assert.eq(0, roleObj.privileges.length);
81+
assert.eq(1, roleObj.roles.length);
82+
assertHasRole(roleObj.roles, "roleC", db.getName());
83+
84+
// Privileges on the same resource get collapsed
85+
db.grantPrivilegesToRole("roleA",
86+
[{resource: {cluster:true}, actions:['listDatabases']},
87+
{resource: {db:db.getName(), collection:""}, actions:['insert']}]);
88+
roleObj = db.getRole("roleA");
89+
assert.eq(0, roleObj.roles.length);
90+
assert.eq(2, roleObj.privileges.length);
91+
assertHasPrivilege(roleObj.privileges,
92+
{resource: {db:db.getName(), collection:""}, actions:['find', 'insert']});
93+
assertHasPrivilege(roleObj.privileges,
94+
{resource: {cluster:true}, actions:['listDatabases']});
95+
96+
97+
// Test dropRole
98+
db.dropRole('roleC');
99+
assert.throws(function() {db.getRole('roleC')});
100+
roleObj = db.getRole("roleB");
101+
assert.eq(0, roleObj.privileges.length);
102+
assert.eq(0, roleObj.roles.length);
103+
104+
// Test dropAllRoles
105+
db.dropAllRoles();
106+
assert.throws(function() {db.getRole('roleA')});
107+
assert.throws(function() {db.getRole('roleB')});
108+
assert.throws(function() {db.getRole('roleC')});
109+
110+
}(db));

jstests/user_management_helpers.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// This test is a basic sanity check of the shell helpers for manipulating user objects
2+
// It is not a comprehensive test of the functionality of the user manipulation commands
13
function assertHasRole(rolesArray, roleName, roleDB, hasRole, canDelegate) {
24
for (i in rolesArray) {
35
var curRole = rolesArray[i];
@@ -7,7 +9,7 @@ function assertHasRole(rolesArray, roleName, roleDB, hasRole, canDelegate) {
79
return;
810
}
911
}
10-
assert(false, "role " + roleName + "@" + roleDB + " not found in array: " + rolesArray);
12+
assert(false, "role " + roleName + "@" + roleDB + " not found in array: " + tojson(rolesArray));
1113
}
1214

1315

@@ -72,4 +74,11 @@ function assertHasRole(rolesArray, roleName, roleDB, hasRole, canDelegate) {
7274
assert.eq(1, userObj.roles.length);
7375
assertHasRole(userObj.roles, "readWrite", db.getName(), false, true);
7476

77+
// Test dropUser
78+
db.dropUser('andy');
79+
assert.throws(function() {printjson(db.getUser('andy'))});
80+
81+
// Test dropAllUsers
82+
db.dropAllUsers()
83+
assert.eq(0, db.getUsers().length);
7584
}(db));

src/mongo/shell/db.js

Lines changed: 115 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,16 +1087,15 @@ DB.prototype.dropUser = function( username, writeConcern ){
10871087
return true;
10881088
}
10891089

1090-
var notFoundErrmsg = "User '" + username + "@" + this.getName() + "' not found";
1091-
if (res.errmsg == notFoundErrmsg) {
1090+
if (res.code == 11) { // Code 11 = UserNotFound
10921091
return false;
10931092
}
10941093

10951094
if (res.errmsg == "no such cmd: dropUsers") {
10961095
return this._removeUserV1(username, cmdObj['writeConcern']);
10971096
}
10981097

1099-
throw "Couldn't drop user: " + res.errmsg;
1098+
throw Error(res.errmsg);
11001099
}
11011100

11021101
DB.prototype._removeUserV1 = function(username, writeConcern) {
@@ -1119,15 +1118,11 @@ DB.prototype.dropAllUsers = function(writeConcern) {
11191118
var res = this.runCommand({dropUsersFromDatabase:1,
11201119
writeConcern: writeConcern ? writeConcern : _defaultWriteConcern});
11211120

1122-
if (res.n == 0) {
1123-
return false;
1124-
}
1125-
1126-
if (res.ok) {
1127-
return true;
1121+
if (!res.ok) {
1122+
throw Error(res.errmsg);
11281123
}
11291124

1130-
throw "Couldn't drop users: " + res.errmsg;
1125+
return res.n;
11311126
}
11321127

11331128
DB.prototype.__pwHash = function( nonce, username, pass ) {
@@ -1230,6 +1225,9 @@ DB.prototype.getUser = function(username) {
12301225
throw Error(res.errmsg);
12311226
}
12321227

1228+
if (res.users.length == 0) {
1229+
throw Error("User " + username + "@" + db.getName() + " not found");
1230+
}
12331231
return res.users[0];
12341232
}
12351233

@@ -1242,4 +1240,111 @@ DB.prototype.getUsers = function() {
12421240
return res.users;
12431241
}
12441242

1243+
DB.prototype.addRole = function(roleObj, writeConcern) {
1244+
var name = roleObj["name"];
1245+
var cmdObj = {createRole:name};
1246+
cmdObj = Object.extend(cmdObj, roleObj);
1247+
delete cmdObj["name"];
1248+
cmdObj["writeConcern"] = writeConcern ? writeConcern : _defaultWriteConcern;
1249+
1250+
var res = this.runCommand(cmdObj);
1251+
1252+
if (!res.ok) {
1253+
throw Error(res.errmsg);
1254+
}
1255+
printjson(roleObj);
1256+
}
1257+
1258+
DB.prototype.updateRole = function(name, updateObject, writeConcern) {
1259+
var cmdObj = {updateRole:name};
1260+
cmdObj = Object.extend(cmdObj, updateObject);
1261+
cmdObj['writeConcern'] = writeConcern ? writeConcern : _defaultWriteConcern;
1262+
var res = this.runCommand(cmdObj);
1263+
if (!res.ok) {
1264+
throw Error(res.errmsg);
1265+
}
1266+
};
1267+
1268+
DB.prototype.dropRole = function(name, writeConcern) {
1269+
var cmdObj = {dropRole:name,
1270+
writeConcern: writeConcern ? writeConcern : _defaultWriteConcern};
1271+
var res = this.runCommand(cmdObj);
1272+
1273+
if (res.ok) {
1274+
return true;
1275+
}
1276+
1277+
if (res.code == 31) { // Code 31 = RoleNotFound
1278+
return false;
1279+
}
1280+
1281+
throw Error(res.errmsg);
1282+
};
1283+
1284+
DB.prototype.dropAllRoles = function(writeConcern) {
1285+
var res = this.runCommand({dropRolesFromDatabase:1,
1286+
writeConcern: writeConcern ? writeConcern : _defaultWriteConcern});
1287+
1288+
if (!res.ok) {
1289+
throw Error(res.errmsg);
1290+
}
1291+
1292+
return res.n;
1293+
}
1294+
1295+
DB.prototype.grantRolesToRole = function(rolename, roles, writeConcern) {
1296+
var cmdObj = {grantRolesToRole: rolename,
1297+
grantedRoles: roles,
1298+
writeConcern: writeConcern ? writeConcern : _defaultWriteConcern};
1299+
var res = this.runCommand(cmdObj);
1300+
if (!res.ok) {
1301+
throw Error(res.errmsg);
1302+
}
1303+
}
1304+
1305+
DB.prototype.revokeRolesFromRole = function(rolename, roles, writeConcern) {
1306+
var cmdObj = {revokeRolesFromRole: rolename,
1307+
revokedRoles: roles,
1308+
writeConcern: writeConcern ? writeConcern : _defaultWriteConcern};
1309+
var res = this.runCommand(cmdObj);
1310+
if (!res.ok) {
1311+
throw Error(res.errmsg);
1312+
}
1313+
}
1314+
1315+
DB.prototype.grantPrivilegesToRole = function(rolename, privileges, writeConcern) {
1316+
var cmdObj = {grantPrivilegesToRole: rolename,
1317+
privileges: privileges,
1318+
writeConcern: writeConcern ? writeConcern : _defaultWriteConcern};
1319+
var res = this.runCommand(cmdObj);
1320+
if (!res.ok) {
1321+
throw Error(res.errmsg);
1322+
}
1323+
}
1324+
1325+
DB.prototype.revokePrivilegesFromRole = function(rolename, privileges, writeConcern) {
1326+
var cmdObj = {revokePrivilegesFromRole: rolename,
1327+
privileges: privileges,
1328+
writeConcern: writeConcern ? writeConcern : _defaultWriteConcern};
1329+
var res = this.runCommand(cmdObj);
1330+
if (!res.ok) {
1331+
throw Error(res.errmsg);
1332+
}
1333+
}
1334+
1335+
DB.prototype.getRole = function(rolename) {
1336+
if (typeof rolename != "string") {
1337+
throw Error("Role name for getRole shell helper must be a string");
1338+
}
1339+
var res = this.runCommand({rolesInfo: rolename});
1340+
if (!res.ok) {
1341+
throw Error(res.errmsg);
1342+
}
1343+
1344+
if (res.roles.length == 0) {
1345+
throw Error("Role " + rolename + "@" + db.getName() + " not found");
1346+
}
1347+
return res.roles[0];
1348+
}
1349+
12451350
}());

0 commit comments

Comments
 (0)