summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSpencer T Brody <spencer@10gen.com>2013-11-08 18:02:05 -0500
committerSpencer T Brody <spencer@10gen.com>2013-11-13 19:01:57 -0500
commit4d5ba6b01fa2d38640ba5bc8bf5c99f305e619bf (patch)
tree4a2483cda1f7f8abf250bac9cda6c83d75613856
parent7688ccdf103191f89ed3c285889f9264dba48d8a (diff)
downloadmongo-4d5ba6b01fa2d38640ba5bc8bf5c99f305e619bf.tar.gz
SERVER-11411 Give rolesInfo command the ability to list all roles defined for a given database.
This commit also introduces the "showPrivileges" option, which defaults to false, to rolesInfo to control whether or not privilege information is included in the result.
-rw-r--r--jstests/role_management_helpers.js29
-rw-r--r--src/mongo/db/auth/authorization_manager.cpp16
-rw-r--r--src/mongo/db/auth/authorization_manager.h27
-rw-r--r--src/mongo/db/auth/authz_manager_external_state.h29
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_local.cpp47
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_local.h11
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_s.cpp33
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_s.h8
-rw-r--r--src/mongo/db/auth/role_graph.cpp16
-rw-r--r--src/mongo/db/auth/role_graph.h16
-rw-r--r--src/mongo/db/auth/role_graph_builtin_roles.cpp23
-rw-r--r--src/mongo/db/auth/role_graph_test.cpp50
-rw-r--r--src/mongo/db/auth/role_name.h5
-rw-r--r--src/mongo/db/auth/user_management_commands_parser.cpp28
-rw-r--r--src/mongo/db/auth/user_management_commands_parser.h12
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp83
-rw-r--r--src/mongo/shell/db.js17
17 files changed, 382 insertions, 68 deletions
diff --git a/jstests/role_management_helpers.js b/jstests/role_management_helpers.js
index 3bf773e480a..08403a209da 100644
--- a/jstests/role_management_helpers.js
+++ b/jstests/role_management_helpers.js
@@ -45,16 +45,31 @@ function assertHasPrivilege(privilegeArray, privilege) {
// Test getRole
var roleObj = db.getRole("roleA");
assert.eq(0, roleObj.roles.length);
+ assert.eq(null, roleObj.privileges);
+ roleObj = db.getRole("roleA", {showPrivileges: true});
assert.eq(1, roleObj.privileges.length);
assertHasPrivilege(roleObj.privileges,
{resource: {db:db.getName(), collection:"foo"}, actions:['find']});
- roleObj = db.getRole("roleB");
+ roleObj = db.getRole("roleB", {showPrivileges: true});
assert.eq(1, roleObj.privileges.length); // inherited from roleA
assertHasPrivilege(roleObj.privileges,
{resource: {db:db.getName(), collection:"foo"}, actions:['find']});
assert.eq(1, roleObj.roles.length);
assertHasRole(roleObj.roles, "roleA", db.getName());
+ // Test getRoles
+ var roles = db.getRoles();
+ assert.eq(3, roles.length);
+ printjson(roles);
+ assert(roles[0].role == 'roleA' || roles[1].role == 'roleA' || roles[2].role == 'roleA');
+ assert(roles[0].role == 'roleB' || roles[1].role == 'roleB' || roles[2].role == 'roleB');
+ assert(roles[0].role == 'roleC' || roles[1].role == 'roleC' || roles[2].role == 'roleC');
+ assert.eq(null, roles[0].privileges);
+ var roles = db.getRoles({showPrivileges: true, showBuiltinRoles: true});
+ assert.eq(8, roles.length);
+ assert.neq(null, roles[0].privileges);
+
+
// Granting roles to nonexistent role fails
assert.throws(function() { db.grantRolesToRole("fakeRole", ['dbAdmin']); });
// Granting roles to built-in role fails
@@ -62,14 +77,14 @@ function assertHasPrivilege(privilegeArray, privilege) {
// Granting non-existant role fails
assert.throws(function() { db.grantRolesToRole("roleB", ['dbAdmin', 'fakeRole']); });
- roleObj = db.getRole("roleB");
+ roleObj = db.getRole("roleB", {showPrivileges: true});
assert.eq(1, roleObj.privileges.length);
assert.eq(1, roleObj.roles.length);
assertHasRole(roleObj.roles, "roleA", db.getName());
// Granting a role you already have is no problem
db.grantRolesToRole("roleB", ['readWrite', 'roleC']);
- roleObj = db.getRole("roleB");
+ roleObj = db.getRole("roleB", {showPrivileges: true});
assert.gt(roleObj.privileges.length, 1); // Got privileges from readWrite role
assert.eq(3, roleObj.roles.length);
assertHasRole(roleObj.roles, "readWrite", db.getName());
@@ -78,7 +93,7 @@ function assertHasPrivilege(privilegeArray, privilege) {
// Revoking roles the role doesn't have is fine
db.revokeRolesFromRole("roleB", ['roleA', 'readWrite', 'dbAdmin']);
- roleObj = db.getRole("roleB");
+ roleObj = db.getRole("roleB", {showPrivileges: true});
assert.eq(0, roleObj.privileges.length);
assert.eq(1, roleObj.roles.length);
assertHasRole(roleObj.roles, "roleC", db.getName());
@@ -87,7 +102,7 @@ function assertHasPrivilege(privilegeArray, privilege) {
db.grantPrivilegesToRole("roleA",
[{resource: {db:db.getName(), collection:""}, actions:['dropDatabase']},
{resource: {db:db.getName(), collection:"foo"}, actions:['insert']}]);
- roleObj = db.getRole("roleA");
+ roleObj = db.getRole("roleA", {showPrivileges: true});
assert.eq(0, roleObj.roles.length);
assert.eq(2, roleObj.privileges.length);
assertHasPrivilege(roleObj.privileges,
@@ -99,7 +114,7 @@ function assertHasPrivilege(privilegeArray, privilege) {
db.updateRole("roleA", {roles:['roleB'],
privileges:[{resource: {db: db.getName(), collection:"foo"},
actions:['find']}]});
- roleObj = db.getRole("roleA");
+ roleObj = db.getRole("roleA", {showPrivileges: true});
assert.eq(1, roleObj.roles.length);
assertHasRole(roleObj.roles, "roleB", db.getName());
assert.eq(1, roleObj.privileges.length);
@@ -109,7 +124,7 @@ function assertHasPrivilege(privilegeArray, privilege) {
// Test dropRole
db.dropRole('roleC');
assert.throws(function() {db.getRole('roleC')});
- roleObj = db.getRole("roleB");
+ roleObj = db.getRole("roleB", {showPrivileges: true});
assert.eq(0, roleObj.privileges.length);
assert.eq(0, roleObj.roles.length);
diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp
index d21f5f9a9f7..19ecbd1ad81 100644
--- a/src/mongo/db/auth/authorization_manager.cpp
+++ b/src/mongo/db/auth/authorization_manager.cpp
@@ -530,8 +530,20 @@ namespace mongo {
return _externalState->getUserDescription(userName, result);
}
- Status AuthorizationManager::getRoleDescription(const RoleName& roleName, BSONObj* result) {
- return _externalState->getRoleDescription(roleName, result);
+ Status AuthorizationManager::getRoleDescription(const RoleName& roleName,
+ bool showPrivileges,
+ BSONObj* result) {
+ return _externalState->getRoleDescription(roleName, showPrivileges, result);
+ }
+
+ Status AuthorizationManager::getRoleDescriptionsForDB(const std::string dbname,
+ bool showPrivileges,
+ bool showBuiltinRoles,
+ vector<BSONObj>* result) {
+ return _externalState->getRoleDescriptionsForDB(dbname,
+ showPrivileges,
+ showBuiltinRoles,
+ result);
}
Status AuthorizationManager::acquireUser(const UserName& userName, User** acquiredUser) {
diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h
index 6f078ca15e6..976dd7aa232 100644
--- a/src/mongo/db/auth/authorization_manager.h
+++ b/src/mongo/db/auth/authorization_manager.h
@@ -290,15 +290,34 @@ namespace mongo {
/**
* Writes into "result" a document describing the named role and returns Status::OK(). The
- * description includes the role's in which the named role has membership, a full list of
- * the role's privileges, and a full list of the roles of which the named role is a member,
- * including those roles memberships held implicitly through other roles (indirect roles).
+ * description includes the roles in which the named role has membership and a full list of
+ * the roles of which the named role is a member, including those roles memberships held
+ * implicitly through other roles (indirect roles). If "showPrivileges" is true, then the
+ * description documents will also include a full list of the role's privileges.
* In the event that some of this information is inconsistent, the document will contain a
* "warnings" array, with string messages describing inconsistencies.
*
* If the role does not exist, returns ErrorCodes::RoleNotFound.
*/
- Status getRoleDescription(const RoleName& roleName, BSONObj* result);
+ Status getRoleDescription(const RoleName& roleName, bool showPrivileges, BSONObj* result);
+
+ /**
+ * Writes into "result" documents describing the roles that are defined on the given
+ * database. Each role description document includes the other roles in which the role has
+ * membership and a full list of the roles of which the named role is a member,
+ * including those roles memberships held implicitly through other roles (indirect roles).
+ * If showPrivileges is true, then the description documents will also include a full list
+ * of the role's privileges. If showBuiltinRoles is true, then the result array will
+ * contain description documents for all the builtin roles for the given database, if it
+ * is false the result will just include user defined roles.
+ * In the event that some of the information in a given role description is inconsistent,
+ * the document will contain a "warnings" array, with string messages describing
+ * inconsistencies.
+ */
+ Status getRoleDescriptionsForDB(const std::string dbname,
+ bool showPrivileges,
+ bool showBuiltinRoles,
+ vector<BSONObj>* result);
/**
* Returns the User object for the given userName in the out parameter "acquiredUser".
diff --git a/src/mongo/db/auth/authz_manager_external_state.h b/src/mongo/db/auth/authz_manager_external_state.h
index a885c4589be..f233d8dc39c 100644
--- a/src/mongo/db/auth/authz_manager_external_state.h
+++ b/src/mongo/db/auth/authz_manager_external_state.h
@@ -79,15 +79,36 @@ namespace mongo {
/**
* Writes into "result" a document describing the named role and returns Status::OK(). The
- * description includes the role's in which the named role has membership, a full list of
- * the role's privileges, and a full list of the roles of which the named role is a member,
- * including those roles memberships held implicitly through other roles (indirect roles).
+ * description includes the roles in which the named role has membership and a full list of
+ * the roles of which the named role is a member, including those roles memberships held
+ * implicitly through other roles (indirect roles). If "showPrivileges" is true, then the
+ * description documents will also include a full list of the role's privileges.
* In the event that some of this information is inconsistent, the document will contain a
* "warnings" array, with string messages describing inconsistencies.
*
* If the role does not exist, returns ErrorCodes::RoleNotFound.
*/
- virtual Status getRoleDescription(const RoleName& roleName, BSONObj* result) = 0;
+ virtual Status getRoleDescription(const RoleName& roleName,
+ bool showPrivileges,
+ BSONObj* result) = 0;
+
+ /**
+ * Writes into "result" documents describing the roles that are defined on the given
+ * database. Each role description document includes the other roles in which the role has
+ * membership and a full list of the roles of which the named role is a member,
+ * including those roles memberships held implicitly through other roles (indirect roles).
+ * If showPrivileges is true, then the description documents will also include a full list
+ * of the role's privileges. If showBuiltinRoles is true, then the result array will
+ * contain description documents for all the builtin roles for the given database, if it
+ * is false the result will just include user defined roles.
+ * In the event that some of the information in a given role description is inconsistent,
+ * the document will contain a "warnings" array, with string messages describing
+ * inconsistencies.
+ */
+ virtual Status getRoleDescriptionsForDB(const std::string dbname,
+ bool showPrivileges,
+ bool showBuiltinRoles,
+ vector<BSONObj>* result) = 0;
/**
* Gets the privilege document for "userName" stored in the system.users collection of
diff --git a/src/mongo/db/auth/authz_manager_external_state_local.cpp b/src/mongo/db/auth/authz_manager_external_state_local.cpp
index 13cf991bda7..458612d2acd 100644
--- a/src/mongo/db/auth/authz_manager_external_state_local.cpp
+++ b/src/mongo/db/auth/authz_manager_external_state_local.cpp
@@ -204,10 +204,16 @@ namespace {
return Status::OK();
}
- Status AuthzManagerExternalStateLocal::getRoleDescription(
- const RoleName& roleName,
- BSONObj* result) {
+ Status AuthzManagerExternalStateLocal::getRoleDescription(const RoleName& roleName,
+ bool showPrivileges,
+ BSONObj* result) {
boost::lock_guard<boost::mutex> lk(_roleGraphMutex);
+ return _getRoleDescription_inlock(roleName, showPrivileges, result);
+ }
+
+ Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(const RoleName& roleName,
+ bool showPrivileges,
+ BSONObj* result) {
if (!_roleGraph.roleExists(roleName))
return Status(ErrorCodes::RoleNotFound, "No role named " + roleName.toString());
@@ -221,17 +227,23 @@ namespace {
mutablebson::Element indirectRolesElement = resultDoc.makeElementArray("indirectRoles");
fassert(17165, resultDoc.root().pushBack(indirectRolesElement));
mutablebson::Element privilegesElement = resultDoc.makeElementArray("privileges");
- fassert(17166, resultDoc.root().pushBack(privilegesElement));
+ if (showPrivileges) {
+ fassert(17166, resultDoc.root().pushBack(privilegesElement));
+ }
+ fassert(17267,
+ resultDoc.root().appendBool("isBuiltin", _roleGraph.isBuiltinRole(roleName)));
mutablebson::Element warningsElement = resultDoc.makeElementArray("warnings");
addRoleNameObjectsToArrayElement(rolesElement, _roleGraph.getDirectSubordinates(roleName));
if (_roleGraphState == roleGraphStateConsistent) {
addRoleNameObjectsToArrayElement(
indirectRolesElement, _roleGraph.getIndirectSubordinates(roleName));
- addPrivilegeObjectsOrWarningsToArrayElement(
- privilegesElement, warningsElement, _roleGraph.getAllPrivileges(roleName));
+ if (showPrivileges) {
+ addPrivilegeObjectsOrWarningsToArrayElement(
+ privilegesElement, warningsElement, _roleGraph.getAllPrivileges(roleName));
+ }
}
- else {
+ else if (showPrivileges) {
warningsElement.appendString(
"", "Role graph state inconsistent; only direct privileges available.");
addPrivilegeObjectsOrWarningsToArrayElement(
@@ -244,6 +256,27 @@ namespace {
return Status::OK();
}
+ Status AuthzManagerExternalStateLocal::getRoleDescriptionsForDB(const std::string dbname,
+ bool showPrivileges,
+ bool showBuiltinRoles,
+ vector<BSONObj>* result) {
+ boost::lock_guard<boost::mutex> lk(_roleGraphMutex);
+
+ for (RoleNameIterator it = _roleGraph.getRolesForDatabase(dbname);
+ it.more(); it.next()) {
+ if (!showBuiltinRoles && _roleGraph.isBuiltinRole(it.get())) {
+ continue;
+ }
+ BSONObj roleDoc;
+ Status status = _getRoleDescription_inlock(it.get(), showPrivileges, &roleDoc);
+ if (!status.isOK()) {
+ return status;
+ }
+ result->push_back(roleDoc);
+ }
+ return Status::OK();
+ }
+
namespace {
/**
diff --git a/src/mongo/db/auth/authz_manager_external_state_local.h b/src/mongo/db/auth/authz_manager_external_state_local.h
index 14016dacbc5..f9c02dbd2ce 100644
--- a/src/mongo/db/auth/authz_manager_external_state_local.h
+++ b/src/mongo/db/auth/authz_manager_external_state_local.h
@@ -55,7 +55,13 @@ namespace mongo {
virtual Status getStoredAuthorizationVersion(int* outVersion);
virtual Status getUserDescription(const UserName& userName, BSONObj* result);
- virtual Status getRoleDescription(const RoleName& roleName, BSONObj* result);
+ virtual Status getRoleDescription(const RoleName& roleName,
+ bool showPrivileges,
+ BSONObj* result);
+ virtual Status getRoleDescriptionsForDB(const std::string dbname,
+ bool showPrivileges,
+ bool showBuiltinRoles,
+ vector<BSONObj>* result);
virtual void logOp(
const char* op,
@@ -84,6 +90,9 @@ namespace mongo {
*/
virtual Status _getUserDocument(const UserName& userName, BSONObj* result) = 0;
+ Status _getRoleDescription_inlock(const RoleName& roleName,
+ bool showPrivileges,
+ BSONObj* result);
/**
* Eventually consistent, in-memory representation of all roles in the system (both
* user-defined and built-in). Synchronized via _roleGraphMutex.
diff --git a/src/mongo/db/auth/authz_manager_external_state_s.cpp b/src/mongo/db/auth/authz_manager_external_state_s.cpp
index 450356c7aa8..926992a273d 100644
--- a/src/mongo/db/auth/authz_manager_external_state_s.cpp
+++ b/src/mongo/db/auth/authz_manager_external_state_s.cpp
@@ -146,6 +146,7 @@ namespace mongo {
}
Status AuthzManagerExternalStateMongos::getRoleDescription(const RoleName& roleName,
+ bool showPrivileges,
BSONObj* result) {
try {
scoped_ptr<ScopedDbConnection> conn(getConnectionForAuthzCollection(
@@ -157,7 +158,8 @@ namespace mongo {
BSON_ARRAY(BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME <<
roleName.getRole() <<
AuthorizationManager::ROLE_SOURCE_FIELD_NAME <<
- roleName.getDB()))),
+ roleName.getDB())) <<
+ "showPrivileges" << showPrivileges),
cmdResult);
if (!cmdResult["ok"].trueValue()) {
int code = cmdResult["code"].numberInt();
@@ -172,6 +174,35 @@ namespace mongo {
}
}
+ Status AuthzManagerExternalStateMongos::getRoleDescriptionsForDB(const std::string dbname,
+ bool showPrivileges,
+ bool showBuiltinRoles,
+ vector<BSONObj>* result) {
+ try {
+ scoped_ptr<ScopedDbConnection> conn(getConnectionForAuthzCollection(
+ AuthorizationManager::rolesCollectionNamespace));
+ BSONObj cmdResult;
+ conn->get()->runCommand(
+ dbname,
+ BSON("rolesInfo" << 1 <<
+ "showPrivileges" << showPrivileges <<
+ "showBuiltinRoles" << showBuiltinRoles),
+ cmdResult);
+ if (!cmdResult["ok"].trueValue()) {
+ int code = cmdResult["code"].numberInt();
+ if (code == 0) code = ErrorCodes::UnknownError;
+ return Status(ErrorCodes::Error(code), cmdResult["errmsg"].str());
+ }
+ for (BSONObjIterator it(cmdResult["roles"].Obj()); it.more(); it.next()) {
+ result->push_back((*it).Obj().getOwned());
+ }
+ conn->done();
+ return Status::OK();
+ } catch (const DBException& e) {
+ return e.toStatus();
+ }
+ }
+
Status AuthzManagerExternalStateMongos::findOne(
const NamespaceString& collectionName,
const BSONObj& query,
diff --git a/src/mongo/db/auth/authz_manager_external_state_s.h b/src/mongo/db/auth/authz_manager_external_state_s.h
index 3341bf19d68..4c17443be52 100644
--- a/src/mongo/db/auth/authz_manager_external_state_s.h
+++ b/src/mongo/db/auth/authz_manager_external_state_s.h
@@ -54,7 +54,13 @@ namespace mongo {
virtual Status initialize();
virtual Status getStoredAuthorizationVersion(int* outVersion);
virtual Status getUserDescription(const UserName& userName, BSONObj* result);
- virtual Status getRoleDescription(const RoleName& roleName, BSONObj* result);
+ virtual Status getRoleDescription(const RoleName& roleName,
+ bool showPrivileges,
+ BSONObj* result);
+ virtual Status getRoleDescriptionsForDB(const std::string dbname,
+ bool showPrivileges,
+ bool showBuiltinRoles,
+ vector<BSONObj>* result);
virtual Status getAllDatabaseNames(std::vector<std::string>* dbnames);
diff --git a/src/mongo/db/auth/role_graph.cpp b/src/mongo/db/auth/role_graph.cpp
index 8937874ca1d..7202c0c19d5 100644
--- a/src/mongo/db/auth/role_graph.cpp
+++ b/src/mongo/db/auth/role_graph.cpp
@@ -29,6 +29,7 @@
#include "mongo/db/auth/role_graph.h"
#include <algorithm>
+#include <set>
#include <vector>
#include "mongo/db/auth/privilege.h"
@@ -47,7 +48,8 @@ namespace {
_roleToIndirectSubordinates(other._roleToIndirectSubordinates),
_roleToMembers(other._roleToMembers),
_directPrivilegesForRole(other._directPrivilegesForRole),
- _allPrivilegesForRole(other._allPrivilegesForRole) {}
+ _allPrivilegesForRole(other._allPrivilegesForRole),
+ _allRoles(other._allRoles) {}
RoleGraph::~RoleGraph() {};
void RoleGraph::swap(RoleGraph& other) {
@@ -57,6 +59,7 @@ namespace {
swap(this->_roleToMembers, other._roleToMembers);
swap(this->_directPrivilegesForRole, other._directPrivilegesForRole);
swap(this->_allPrivilegesForRole, other._allPrivilegesForRole);
+ swap(this->_allRoles, other._allRoles);
}
void swap(RoleGraph& lhs, RoleGraph& rhs) {
@@ -103,6 +106,7 @@ namespace {
_roleToMembers[role];
_directPrivilegesForRole[role];
_allPrivilegesForRole[role];
+ _allRoles.insert(role);
}
Status RoleGraph::deleteRole(const RoleName& role) {
@@ -136,6 +140,7 @@ namespace {
_roleToMembers.erase(role);
_directPrivilegesForRole.erase(role);
_allPrivilegesForRole.erase(role);
+ _allRoles.erase(role);
return Status::OK();
}
@@ -527,5 +532,14 @@ namespace {
return Status::OK();
}
+ RoleNameIterator RoleGraph::getRolesForDatabase(const std::string& dbname) {
+ _createBuiltinRolesForDBIfNeeded(dbname);
+
+ std::set<RoleName>::const_iterator lower = _allRoles.lower_bound(RoleName("", dbname));
+ std::string afterDB = dbname;
+ afterDB.push_back('\0');
+ std::set<RoleName>::const_iterator upper = _allRoles.lower_bound(RoleName("", afterDB));
+ return makeRoleNameIterator(lower, upper);
+ }
} // namespace mongo
diff --git a/src/mongo/db/auth/role_graph.h b/src/mongo/db/auth/role_graph.h
index e6ee302f051..4d7de167a15 100644
--- a/src/mongo/db/auth/role_graph.h
+++ b/src/mongo/db/auth/role_graph.h
@@ -29,6 +29,7 @@
#pragma once
#include <algorithm>
+#include <set>
#include <vector>
#include "mongo/base/status.h"
@@ -88,7 +89,7 @@ namespace mongo {
RoleNameIterator getDirectMembers(const RoleName& role);
/**
- * Returns an iterator over the RoleNames of the "subordninates" of the given role.
+ * Returns an iterator over the RoleNames of the "subordinates" of the given role.
* Subordinate roles are the roles that this role has been granted directly (roles
* that have been granted transitively through another role are not included). These are
* the "children" of this node in the graph.
@@ -103,6 +104,12 @@ namespace mongo {
RoleNameIterator getIndirectSubordinates(const RoleName& role);
/**
+ * Returns an iterator that can be used to get a full list of roles (in lexicographical
+ * order) that are defined on the given database.
+ */
+ RoleNameIterator getRolesForDatabase(const std::string& dbname);
+
+ /**
* Returns a vector of the privileges that the given role has been directly granted.
* Privileges that have been granted transitively through this role's subordinate roles are
* not included.
@@ -266,6 +273,12 @@ namespace mongo {
void _createBuiltinRoleIfNeeded(const RoleName& role);
/**
+ * Adds the built-in roles for the given database name to the role graph if they aren't
+ * already present.
+ */
+ void _createBuiltinRolesForDBIfNeeded(const std::string& dbname);
+
+ /**
* Returns whether or not the given role exists strictly within the role graph.
*/
bool _roleExistsDontCreateBuiltin(const RoleName& role);
@@ -293,6 +306,7 @@ namespace mongo {
EdgeSet _roleToMembers;
RolePrivilegeMap _directPrivilegesForRole;
RolePrivilegeMap _allPrivilegesForRole;
+ set<RoleName> _allRoles;
};
void swap(RoleGraph& lhs, RoleGraph& rhs);
diff --git a/src/mongo/db/auth/role_graph_builtin_roles.cpp b/src/mongo/db/auth/role_graph_builtin_roles.cpp
index 54d5671b741..45b70a9fbb4 100644
--- a/src/mongo/db/auth/role_graph_builtin_roles.cpp
+++ b/src/mongo/db/auth/role_graph_builtin_roles.cpp
@@ -709,6 +709,29 @@ namespace {
return false;
}
+ void RoleGraph::_createBuiltinRolesForDBIfNeeded(const std::string& dbname) {
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_READ, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_READ_WRITE, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_USER_ADMIN, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_DB_ADMIN, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_DB_OWNER, dbname));
+
+ if (dbname == "admin") {
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_READ_ANY_DB, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_READ_WRITE_ANY_DB, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_USER_ADMIN_ANY_DB, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_DB_ADMIN_ANY_DB, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_CLUSTER_MONITOR, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_HOST_MANAGEMENT, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_CLUSTER_MANAGEMENT, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_CLUSTER_ADMIN, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_BACKUP, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_RESTORE, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_ROOT, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_INTERNAL, dbname));
+ }
+ }
+
void RoleGraph::_createBuiltinRoleIfNeeded(const RoleName& role) {
if (!isBuiltinRole(role) || _roleExistsDontCreateBuiltin(role)) {
return;
diff --git a/src/mongo/db/auth/role_graph_test.cpp b/src/mongo/db/auth/role_graph_test.cpp
index e4e34f302c2..4ace76a761f 100644
--- a/src/mongo/db/auth/role_graph_test.cpp
+++ b/src/mongo/db/auth/role_graph_test.cpp
@@ -35,6 +35,7 @@
#include "mongo/db/auth/role_graph.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/mongoutils/str.h"
+#include "mongo/util/sequence_util.h"
namespace mongo {
namespace {
@@ -672,5 +673,54 @@ namespace {
ASSERT(!graph.roleExists(RoleName("MyRole", "admin")));
}
+ TEST(RoleGraphTest, getRolesForDatabase) {
+ RoleGraph graph;
+ graph.createRole(RoleName("myRole", "test"));
+ // Make sure that a role on "test2" doesn't show up in the roles list for "test"
+ graph.createRole(RoleName("anotherRole", "test2"));
+ graph.createRole(RoleName("myAdminRole", "admin"));
+
+ // Non-admin DB with no user-defined roles
+ RoleNameIterator it = graph.getRolesForDatabase("fakedb");
+ ASSERT_EQUALS(RoleName("dbAdmin", "fakedb"), it.next());
+ ASSERT_EQUALS(RoleName("dbOwner", "fakedb"), it.next());
+ ASSERT_EQUALS(RoleName("read", "fakedb"), it.next());
+ ASSERT_EQUALS(RoleName("readWrite", "fakedb"), it.next());
+ ASSERT_EQUALS(RoleName("userAdmin", "fakedb"), it.next());
+ ASSERT_FALSE(it.more());
+
+ // Non-admin DB with a user-defined role
+ it = graph.getRolesForDatabase("test");
+ ASSERT_EQUALS(RoleName("dbAdmin", "test"), it.next());
+ ASSERT_EQUALS(RoleName("dbOwner", "test"), it.next());
+ ASSERT_EQUALS(RoleName("myRole", "test"), it.next());
+ ASSERT_EQUALS(RoleName("read", "test"), it.next());
+ ASSERT_EQUALS(RoleName("readWrite", "test"), it.next());
+ ASSERT_EQUALS(RoleName("userAdmin", "test"), it.next());
+ ASSERT_FALSE(it.more());
+
+ // Admin DB
+ it = graph.getRolesForDatabase("admin");
+ ASSERT_EQUALS(RoleName("__system", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("backup", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("clusterAdmin", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("clusterManager", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("clusterMonitor", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("dbAdmin", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("dbAdminAnyDatabase", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("dbOwner", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("hostManager", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("myAdminRole", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("read", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("readAnyDatabase", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("readWrite", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("readWriteAnyDatabase", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("restore", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("root", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("userAdmin", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("userAdminAnyDatabase", "admin"), it.next());
+ ASSERT_FALSE(it.more());
+ }
+
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/auth/role_name.h b/src/mongo/db/auth/role_name.h
index 7f7de642c9b..63f7da816c7 100644
--- a/src/mongo/db/auth/role_name.h
+++ b/src/mongo/db/auth/role_name.h
@@ -89,7 +89,10 @@ namespace mongo {
}
static inline bool operator<(const RoleName& lhs, const RoleName& rhs) {
- return lhs.getFullName() < rhs.getFullName();
+ if (lhs.getDB() == rhs.getDB()) {
+ return lhs.getRole() < rhs.getRole();
+ }
+ return lhs.getDB() < rhs.getDB();
}
std::ostream& operator<<(std::ostream& os, const RoleName& name);
diff --git a/src/mongo/db/auth/user_management_commands_parser.cpp b/src/mongo/db/auth/user_management_commands_parser.cpp
index eb79a95f64e..6ad54f13724 100644
--- a/src/mongo/db/auth/user_management_commands_parser.cpp
+++ b/src/mongo/db/auth/user_management_commands_parser.cpp
@@ -398,19 +398,23 @@ namespace auth {
Status parseRolesInfoCommand(const BSONObj& cmdObj,
const StringData& dbname,
- std::vector<RoleName>* parsedRoleNames) {
+ RolesInfoArgs* parsedArgs) {
unordered_set<std::string> validFieldNames;
validFieldNames.insert("rolesInfo");
+ validFieldNames.insert("showPrivileges");
+ validFieldNames.insert("showBuiltinRoles");
Status status = _checkNoExtraFields(cmdObj, "rolesInfo", validFieldNames);
if (!status.isOK()) {
return status;
}
- if (cmdObj["rolesInfo"].type() == Array) {
+ if (cmdObj["rolesInfo"].numberInt() == 1) {
+ parsedArgs->allForDB = true;
+ } else if (cmdObj["rolesInfo"].type() == Array) {
status = parseRoleNamesFromBSONArray(BSONArray(cmdObj["rolesInfo"].Obj()),
dbname,
- parsedRoleNames);
+ &parsedArgs->roleNames);
if (!status.isOK()) {
return status;
}
@@ -424,7 +428,23 @@ namespace auth {
if (!status.isOK()) {
return status;
}
- parsedRoleNames->push_back(name);
+ parsedArgs->roleNames.push_back(name);
+ }
+
+ status = bsonExtractBooleanFieldWithDefault(cmdObj,
+ "showPrivileges",
+ false,
+ &parsedArgs->showPrivileges);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ status = bsonExtractBooleanFieldWithDefault(cmdObj,
+ "showBuiltinRoles",
+ false,
+ &parsedArgs->showBuiltinRoles);
+ if (!status.isOK()) {
+ return status;
}
return Status::OK();
diff --git a/src/mongo/db/auth/user_management_commands_parser.h b/src/mongo/db/auth/user_management_commands_parser.h
index 60d92e6ae13..d5e10f7574d 100644
--- a/src/mongo/db/auth/user_management_commands_parser.h
+++ b/src/mongo/db/auth/user_management_commands_parser.h
@@ -114,13 +114,21 @@ namespace auth {
const StringData& dbname,
UsersInfoArgs* parsedArgs);
+ struct RolesInfoArgs {
+ std::vector<RoleName> roleNames;
+ bool allForDB;
+ bool showPrivileges;
+ bool showBuiltinRoles;
+ RolesInfoArgs() : allForDB(false), showPrivileges(false), showBuiltinRoles(false) {}
+ };
+
/**
* Takes a command object describing an invocation of the "rolesInfo" command and parses out
- * the role names requested into the "parsedRoleNames" output param.
+ * the arguments into the "parsedArgs" output param.
*/
Status parseRolesInfoCommand(const BSONObj& cmdObj,
const StringData& dbname,
- std::vector<RoleName>* parsedRoleNames);
+ RolesInfoArgs* parsedArgs);
struct CreateOrUpdateRoleArgs {
RoleName roleName;
diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp
index 6340059db25..25ce3e41d75 100644
--- a/src/mongo/db/commands/user_management_commands.cpp
+++ b/src/mongo/db/commands/user_management_commands.cpp
@@ -214,7 +214,7 @@ namespace mongo {
}
BSONObj roleToAddDoc;
- Status status = authzManager->getRoleDescription(roleToAdd, &roleToAddDoc);
+ Status status = authzManager->getRoleDescription(roleToAdd, false, &roleToAddDoc);
if (status == ErrorCodes::RoleNotFound) {
return Status(ErrorCodes::RoleNotFound,
"Cannot grant nonexistent role " + roleToAdd.toString());
@@ -433,7 +433,7 @@ namespace mongo {
// Role existence has to be checked after acquiring the update lock
for (size_t i = 0; i < args.roles.size(); ++i) {
BSONObj ignored;
- status = authzManager->getRoleDescription(args.roles[i], &ignored);
+ status = authzManager->getRoleDescription(args.roles[i], false, &ignored);
if (!status.isOK()) {
addStatus(status, result);
return false;
@@ -585,7 +585,7 @@ namespace mongo {
if (args.hasRoles) {
for (size_t i = 0; i < args.roles.size(); ++i) {
BSONObj ignored;
- status = authzManager->getRoleDescription(args.roles[i], &ignored);
+ status = authzManager->getRoleDescription(args.roles[i], false, &ignored);
if (!status.isOK()) {
addStatus(status, result);
return false;
@@ -893,7 +893,7 @@ namespace mongo {
for (vector<RoleName>::iterator it = roles.begin(); it != roles.end(); ++it) {
RoleName& roleName = *it;
BSONObj roleDoc;
- status = authzManager->getRoleDescription(roleName, &roleDoc);
+ status = authzManager->getRoleDescription(roleName, false, &roleDoc);
if (!status.isOK()) {
addStatus(status, result);
return false;
@@ -1006,7 +1006,7 @@ namespace mongo {
for (vector<RoleName>::iterator it = roles.begin(); it != roles.end(); ++it) {
RoleName& roleName = *it;
BSONObj roleDoc;
- status = authzManager->getRoleDescription(roleName, &roleDoc);
+ status = authzManager->getRoleDescription(roleName, false, &roleDoc);
if (!status.isOK()) {
addStatus(status, result);
return false;
@@ -1440,7 +1440,7 @@ namespace mongo {
// Role existence has to be checked after acquiring the update lock
BSONObj ignored;
- status = authzManager->getRoleDescription(args.roleName, &ignored);
+ status = authzManager->getRoleDescription(args.roleName, false, &ignored);
if (!status.isOK()) {
addStatus(status, result);
return false;
@@ -1571,7 +1571,7 @@ namespace mongo {
}
BSONObj roleDoc;
- status = authzManager->getRoleDescription(roleName, &roleDoc);
+ status = authzManager->getRoleDescription(roleName, true, &roleDoc);
if (!status.isOK()) {
addStatus(status, result);
return false;
@@ -1718,7 +1718,7 @@ namespace mongo {
}
BSONObj roleDoc;
- status = authzManager->getRoleDescription(roleName, &roleDoc);
+ status = authzManager->getRoleDescription(roleName, true, &roleDoc);
if (!status.isOK()) {
addStatus(status, result);
return false;
@@ -1873,7 +1873,7 @@ namespace mongo {
// Role existence has to be checked after acquiring the update lock
BSONObj roleDoc;
- status = authzManager->getRoleDescription(roleName, &roleDoc);
+ status = authzManager->getRoleDescription(roleName, false, &roleDoc);
if (!status.isOK()) {
addStatus(status, result);
return false;
@@ -2003,7 +2003,7 @@ namespace mongo {
}
BSONObj roleDoc;
- status = authzManager->getRoleDescription(roleName, &roleDoc);
+ status = authzManager->getRoleDescription(roleName, false, &roleDoc);
if (!status.isOK()) {
addStatus(status, result);
return false;
@@ -2130,7 +2130,7 @@ namespace mongo {
}
BSONObj roleDoc;
- status = authzManager->getRoleDescription(roleName, &roleDoc);
+ status = authzManager->getRoleDescription(roleName, false, &roleDoc);
if (!status.isOK()) {
addStatus(status, result);
return false;
@@ -2382,24 +2382,33 @@ namespace mongo {
const std::string& dbname,
const BSONObj& cmdObj) {
AuthorizationSession* authzSession = client->getAuthorizationSession();
- std::vector<RoleName> roleNames;
- Status status = auth::parseRolesInfoCommand(cmdObj, dbname, &roleNames);
+ auth::RolesInfoArgs args;
+ Status status = auth::parseRolesInfoCommand(cmdObj, dbname, &args);
if (!status.isOK()) {
return status;
}
- for (size_t i = 0; i < roleNames.size(); ++i) {
- if (authzSession->isAuthenticatedAsUserWithRole(roleNames[i])) {
- continue; // Can always see roles that you are a member of
- }
-
+ if (args.allForDB) {
if (!authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(roleNames[i].getDB()),
- ActionType::viewRole)) {
+ ResourcePattern::forDatabaseName(dbname), ActionType::viewRole)) {
return Status(ErrorCodes::Unauthorized,
str::stream() << "Not authorized to view roles from the " <<
dbname << " database");
}
+ } else {
+ for (size_t i = 0; i < args.roleNames.size(); ++i) {
+ if (authzSession->isAuthenticatedAsUserWithRole(args.roleNames[i])) {
+ continue; // Can always see roles that you are a member of
+ }
+
+ if (!authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forDatabaseName(args.roleNames[i].getDB()),
+ ActionType::viewRole)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "Not authorized to view roles from the " <<
+ args.roleNames[i].getDB() << " database");
+ }
+ }
}
return Status::OK();
@@ -2412,8 +2421,8 @@ namespace mongo {
BSONObjBuilder& result,
bool fromRepl) {
- std::vector<RoleName> roleNames;
- Status status = auth::parseRolesInfoCommand(cmdObj, dbname, &roleNames);
+ auth::RolesInfoArgs args;
+ Status status = auth::parseRolesInfoCommand(cmdObj, dbname, &args);
if (!status.isOK()) {
addStatus(status, result);
return false;
@@ -2426,18 +2435,32 @@ namespace mongo {
}
BSONArrayBuilder rolesArrayBuilder;
- for (size_t i = 0; i < roleNames.size(); ++i) {
- BSONObj roleDetails;
- status = getGlobalAuthorizationManager()->getRoleDescription(
- roleNames[i], &roleDetails);
- if (status.code() == ErrorCodes::RoleNotFound) {
- continue;
- }
+ if (args.allForDB) {
+ std::vector<BSONObj> rolesDocs;
+ status = getGlobalAuthorizationManager()->getRoleDescriptionsForDB(
+ dbname, args.showPrivileges, args.showBuiltinRoles, &rolesDocs);
if (!status.isOK()) {
addStatus(status, result);
return false;
}
- rolesArrayBuilder.append(roleDetails);
+
+ for (size_t i = 0; i < rolesDocs.size(); ++i) {
+ rolesArrayBuilder.append(rolesDocs[i]);
+ }
+ } else {
+ for (size_t i = 0; i < args.roleNames.size(); ++i) {
+ BSONObj roleDetails;
+ status = getGlobalAuthorizationManager()->getRoleDescription(
+ args.roleNames[i], args.showPrivileges, &roleDetails);
+ if (status.code() == ErrorCodes::RoleNotFound) {
+ continue;
+ }
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+ rolesArrayBuilder.append(roleDetails);
+ }
}
result.append("roles", rolesArrayBuilder.arr());
return true;
diff --git a/src/mongo/shell/db.js b/src/mongo/shell/db.js
index 1a32c091258..c74a65e2bf7 100644
--- a/src/mongo/shell/db.js
+++ b/src/mongo/shell/db.js
@@ -1373,11 +1373,13 @@ DB.prototype.revokePrivilegesFromRole = function(rolename, privileges, writeConc
}
}
-DB.prototype.getRole = function(rolename) {
+DB.prototype.getRole = function(rolename, args) {
if (typeof rolename != "string") {
throw Error("Role name for getRole shell helper must be a string");
}
- var res = this.runCommand({rolesInfo: rolename});
+ var cmdObj = {rolesInfo:rolename};
+ Object.extend(cmdObj, args);
+ var res = this.runCommand(cmdObj);
if (!res.ok) {
throw Error(res.errmsg);
}
@@ -1388,4 +1390,15 @@ DB.prototype.getRole = function(rolename) {
return res.roles[0];
}
+DB.prototype.getRoles = function(args) {
+ var cmdObj = {rolesInfo:1};
+ Object.extend(cmdObj, args);
+ var res = this.runCommand(cmdObj);
+ if (!res.ok) {
+ throw Error(res.errmsg);
+ }
+
+ return res.roles;
+}
+
}());