summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSpencer T Brody <spencer@10gen.com>2013-09-30 18:40:39 -0400
committerSpencer T Brody <spencer@10gen.com>2013-10-04 17:17:51 -0400
commitd09e608691aae000f3176b27cc67a7900229cd1e (patch)
tree8740c59feef65ab28b7b8dc55fc85996a71acaed
parente0f70cdb67a36c831a23b452e7b9fba41f512ae0 (diff)
downloadmongo-d09e608691aae000f3176b27cc67a7900229cd1e.tar.gz
SERVER-9515 Implement several remaining role manipulation commands
-rw-r--r--src/mongo/db/auth/authorization_manager.cpp42
-rw-r--r--src/mongo/db/auth/authorization_manager.h28
-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_test.cpp134
-rw-r--r--src/mongo/db/auth/user_management_commands_parser.cpp133
-rw-r--r--src/mongo/db/auth/user_management_commands_parser.h30
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp628
8 files changed, 822 insertions, 205 deletions
diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp
index a1f60eaf956..9b0b07c9086 100644
--- a/src/mongo/db/auth/authorization_manager.cpp
+++ b/src/mongo/db/auth/authorization_manager.cpp
@@ -163,6 +163,19 @@ namespace mongo {
return _externalState->removePrivilegeDocuments(query, writeConcern, numRemoved);
}
+ Status AuthorizationManager::removeRoleDocuments(const BSONObj& query,
+ const BSONObj& writeConcern,
+ int* numRemoved) const {
+ Status status = _externalState->remove(NamespaceString("admin.system.roles"),
+ query,
+ writeConcern,
+ numRemoved);
+ if (status.code() == ErrorCodes::UnknownError) {
+ return Status(ErrorCodes::RoleModificationFailed, status.reason());
+ }
+ return status;
+ }
+
Status AuthorizationManager::insertRoleDocument(const BSONObj& roleObj,
const BSONObj& writeConcern) const {
Status status = _externalState->insert(NamespaceString("admin.system.roles"),
@@ -215,6 +228,22 @@ namespace mongo {
return _externalState->query(collectionName, query, resultProcessor);
}
+ Status AuthorizationManager::updateAuthzDocuments(const NamespaceString& collectionName,
+ const BSONObj& query,
+ const BSONObj& updatePattern,
+ bool upsert,
+ bool multi,
+ const BSONObj& writeConcern,
+ int* numUpdated) const {
+ return _externalState->update(collectionName,
+ query,
+ updatePattern,
+ upsert,
+ multi,
+ writeConcern,
+ numUpdated);
+ }
+
bool AuthorizationManager::roleExists(const RoleName& role) {
boost::lock_guard<boost::mutex> lk(_lock);
return _roleGraph.roleExists(role);
@@ -230,6 +259,12 @@ namespace mongo {
return _roleGraph.getDirectPrivileges(role);
}
+ std::vector<RoleName> AuthorizationManager::getSubordinateRolesForRole(
+ const RoleName& role) {
+ boost::lock_guard<boost::mutex> lk(_lock);
+ return _roleGraph.getDirectSubordinates(role);
+ }
+
Status AuthorizationManager::getBSONForPrivileges(const PrivilegeVector& privileges,
mutablebson::Element resultArray) {
for (PrivilegeVector::const_iterator it = privileges.begin();
@@ -270,9 +305,10 @@ namespace mongo {
// Build roles array
mutablebson::Element rolesArrayElement = result.getDocument().makeElementArray("roles");
result.pushBack(rolesArrayElement);
- RoleNameIterator nameIt = graph->getDirectSubordinates(roleName);
- while (nameIt.more()) {
- const RoleName& subRole = nameIt.next();
+ const std::vector<RoleName> roles = graph->getDirectSubordinates(roleName);
+ for (std::vector<RoleName>::const_iterator it = roles.begin();
+ it != roles.end(); ++it) {
+ const RoleName& subRole = *it;
mutablebson::Element roleObj = result.getDocument().makeElementObject("");
roleObj.appendString("name", subRole.getRole());
roleObj.appendString("source", subRole.getDB());
diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h
index 5220ee11a45..1f9a7ef2200 100644
--- a/src/mongo/db/auth/authorization_manager.h
+++ b/src/mongo/db/auth/authorization_manager.h
@@ -183,6 +183,29 @@ namespace mongo {
const BSONObj& writeConcern) const;
/**
+ * Updates documents matching "query" according to "updatePattern" in "collectionName".
+ * Should only be called on collections with authorization documents in them
+ * (ie admin.system.users and admin.system.roles).
+ */
+ Status updateAuthzDocuments(const NamespaceString& collectionName,
+ const BSONObj& query,
+ const BSONObj& updatePattern,
+ bool upsert,
+ bool multi,
+ const BSONObj& writeConcern,
+ int* numUpdated) const;
+
+ /*
+ * Removes roles matching the given query.
+ * Writes into *numRemoved the number of role documents that were modified.
+ * 'writeConcern' contains the arguments to be passed to getLastError to block for
+ * successful completion of the write.
+ */
+ Status removeRoleDocuments(const BSONObj& query,
+ const BSONObj& writeConcern,
+ int* numRemoved) const;
+
+ /**
* Finds all documents matching "query" in "collectionName". For each document returned,
* calls the function resultProcessor on it.
* Should only be called on collections with authorization documents in them
@@ -257,6 +280,11 @@ namespace mongo {
PrivilegeVector getDirectPrivilegesForRole(const RoleName& role);
/**
+ * Returns the direct subordinate roles of the given role.
+ */
+ std::vector<RoleName> getSubordinateRolesForRole(const RoleName& role);
+
+ /**
* Initializes the authorization manager. Depending on what version the authorization
* system is at, this may involve building up the user cache and/or the roles graph.
* This function should be called once at startup and never again after that.
diff --git a/src/mongo/db/auth/role_graph.cpp b/src/mongo/db/auth/role_graph.cpp
index d42354aa577..a010326f57c 100644
--- a/src/mongo/db/auth/role_graph.cpp
+++ b/src/mongo/db/auth/role_graph.cpp
@@ -40,6 +40,7 @@ namespace mongo {
namespace {
PrivilegeVector emptyPrivilegeVector;
+ std::vector<RoleName> emptyRoleVector;
// RoleNameIterator for iterating over an unordered_set of RoleNames.
class RoleNameSetIterator : public RoleNameIterator::Impl {
@@ -75,6 +76,7 @@ namespace {
unordered_set<RoleName>::const_iterator _begin;
unordered_set<RoleName>::const_iterator _end;
};
+
} // namespace
RoleGraph::RoleGraph() {};
@@ -180,11 +182,10 @@ namespace {
return Status::OK();
}
- RoleNameIterator RoleGraph::getDirectSubordinates(const RoleName& role) {
+ const std::vector<RoleName>& RoleGraph::getDirectSubordinates(const RoleName& role) {
if (!roleExists(role))
- return RoleNameIterator(NULL);
- const std::vector<RoleName>& edges = _roleToSubordinates.find(role)->second;
- return RoleNameIterator(new RoleNameVectorIterator(edges.begin(), edges.end()));
+ return emptyRoleVector;
+ return _roleToSubordinates.find(role)->second;
}
RoleNameIterator RoleGraph::getIndirectSubordinates(const RoleName& role) {
@@ -194,11 +195,10 @@ namespace {
return RoleNameIterator(new RoleNameSetIterator(subs.begin(), subs.end()));
}
- RoleNameIterator RoleGraph::getDirectMembers(const RoleName& role) {
+ const std::vector<RoleName>& RoleGraph::getDirectMembers(const RoleName& role) {
if (!roleExists(role))
- return RoleNameIterator(NULL);
- const std::vector<RoleName>& edges = _roleToMembers.find(role)->second;
- return RoleNameIterator(new RoleNameVectorIterator(edges.begin(), edges.end()));
+ return emptyRoleVector;
+ return _roleToMembers.find(role)->second;
}
const PrivilegeVector& RoleGraph::getDirectPrivileges(const RoleName& role) {
diff --git a/src/mongo/db/auth/role_graph.h b/src/mongo/db/auth/role_graph.h
index b7dcbbaa80f..f30f62a104d 100644
--- a/src/mongo/db/auth/role_graph.h
+++ b/src/mongo/db/auth/role_graph.h
@@ -71,22 +71,20 @@ namespace mongo {
static void generateUniversalPrivileges(PrivilegeVector* privileges);
/**
- * Returns an iterator that can be used to get a list of the members of the given role.
+ * Returns a vector of the RoleNames of the "members" of the given role.
* Members of a role are roles that have been granted this role directly (roles that are
* members transitively through another role are not included). These are the "parents" of
- * this node in the graph. The iterator is valid until the next call to addRole or
- * removeRole.
+ * this node in the graph.
*/
- RoleNameIterator getDirectMembers(const RoleName& role);
+ const std::vector<RoleName>& getDirectMembers(const RoleName& role);
/**
- * Returns an iterator that can be used to get a list of "subordinate" roles of the given
- * role. Subordinate roles are the roles that this role has been granted directly (roles
+ * Returns a vector of the RoleNames of the "subordninates" 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. The iterator is valid until the next call to
- * addRole or removeRole.
+ * the "children" of this node in the graph.
*/
- RoleNameIterator getDirectSubordinates(const RoleName& role);
+ const std::vector<RoleName>& getDirectSubordinates(const RoleName& role);
/**
* Returns an iterator that can be used to get a full list of roles that this role inherits
diff --git a/src/mongo/db/auth/role_graph_test.cpp b/src/mongo/db/auth/role_graph_test.cpp
index e4e34f302c2..42d933fc76e 100644
--- a/src/mongo/db/auth/role_graph_test.cpp
+++ b/src/mongo/db/auth/role_graph_test.cpp
@@ -52,32 +52,27 @@ namespace {
ASSERT_OK(graph.createRole(roleB));
ASSERT_OK(graph.createRole(roleC));
- RoleNameIterator it;
- it = graph.getDirectSubordinates(roleA);
- ASSERT_FALSE(it.more());
- it = graph.getDirectMembers(roleA);
- ASSERT_FALSE(it.more());
+ std::vector<RoleName> roles = graph.getDirectSubordinates(roleA);
+ ASSERT_EQUALS(0U, roles.size());
+ roles = graph.getDirectMembers(roleA);
+ ASSERT_EQUALS(0U, roles.size());
ASSERT_OK(graph.addRoleToRole(roleA, roleB));
// A -> B
- it = graph.getDirectSubordinates(roleA);
- ASSERT_TRUE(it.more());
- // should not advance the iterator
- ASSERT_EQUALS(it.get().getFullName(), roleB.getFullName());
- ASSERT_EQUALS(it.get().getFullName(), roleB.getFullName());
- ASSERT_EQUALS(it.next().getFullName(), roleB.getFullName());
- ASSERT_FALSE(it.more());
+ roles = graph.getDirectSubordinates(roleA);
+ ASSERT_EQUALS(1U, roles.size());
+ ASSERT_EQUALS(roles[0].getFullName(), roleB.getFullName());
- it = graph.getDirectMembers(roleA);
- ASSERT_FALSE(it.more());
+ roles = graph.getDirectMembers(roleA);
+ ASSERT_EQUALS(0U, roles.size());
- it = graph.getDirectMembers(roleB);
- ASSERT_EQUALS(it.next().getFullName(), roleA.getFullName());
- ASSERT_FALSE(it.more());
+ roles = graph.getDirectMembers(roleB);
+ ASSERT_EQUALS(1U, roles.size());
+ ASSERT_EQUALS(roles[0].getFullName(), roleA.getFullName());
- it = graph.getDirectSubordinates(roleB);
- ASSERT_FALSE(it.more());
+ roles= graph.getDirectSubordinates(roleB);
+ ASSERT_EQUALS(0U, roles.size());
ASSERT_OK(graph.addRoleToRole(roleA, roleC));
ASSERT_OK(graph.addRoleToRole(roleB, roleC));
@@ -96,20 +91,9 @@ namespace {
* D
*/
-
- it = graph.getDirectSubordinates(roleA); // should be roleB and roleC, order doesn't matter
- RoleName cur = it.next();
- if (cur == roleB) {
- ASSERT_EQUALS(it.next().getFullName(), roleC.getFullName());
- } else if (cur == roleC) {
- ASSERT_EQUALS(it.next().getFullName(), roleB.getFullName());
- } else {
- FAIL(mongoutils::str::stream() << "unexpected role returned: " << cur.getFullName());
- }
- ASSERT_FALSE(it.more());
-
+ // Check indirect roles for roleA. should have roleB, roleC and roleD, order doesn't matter.
ASSERT_OK(graph.recomputePrivilegeData());
- it = graph.getIndirectSubordinates(roleA); // should have roleB, roleC and roleD
+ RoleNameIterator it = graph.getIndirectSubordinates(roleA);
bool hasB = false;
bool hasC = false;
bool hasD = false;
@@ -133,39 +117,32 @@ namespace {
ASSERT(hasC);
ASSERT(hasD);
- it = graph.getDirectSubordinates(roleB); // should be roleC and roleD, order doesn't matter
- cur = it.next();
- if (cur == roleC) {
- ASSERT_EQUALS(it.next().getFullName(), roleD.getFullName());
- } else if (cur == roleD) {
- ASSERT_EQUALS(it.next().getFullName(), roleC.getFullName());
- } else {
- FAIL(mongoutils::str::stream() << "unexpected role returned: " << cur.getFullName());
- }
- ASSERT_FALSE(it.more());
-
- it = graph.getDirectSubordinates(roleC);
- ASSERT_FALSE(it.more());
-
- it = graph.getDirectMembers(roleA);
- ASSERT_FALSE(it.more());
-
- it = graph.getDirectMembers(roleB);
- ASSERT_EQUALS(it.next().getFullName(), roleA.getFullName());
- ASSERT_FALSE(it.more());
-
- it = graph.getDirectMembers(roleC); // should be role A and role B, order doesn't matter
- cur = it.next();
- if (cur == roleA) {
- ASSERT_EQUALS(it.next().getFullName(), roleB.getFullName());
- } else if (cur == roleB) {
- ASSERT_EQUALS(it.next().getFullName(), roleA.getFullName());
- } else {
- FAIL(mongoutils::str::stream() << "unexpected role returned: " << cur.getFullName());
- }
- ASSERT_FALSE(it.more());
+ roles = graph.getDirectSubordinates(roleA); // should be roleB then roleC
+ ASSERT_EQUALS(2U, roles.size());
+ ASSERT_EQUALS(roleB.getFullName(), roles[0].getFullName());
+ ASSERT_EQUALS(roleC.getFullName(), roles[1].getFullName());
+
+ roles = graph.getDirectSubordinates(roleB); // should be roleC then roleD
+ ASSERT_EQUALS(2U, roles.size());
+ ASSERT_EQUALS(roleC.getFullName(), roles[0].getFullName());
+ ASSERT_EQUALS(roleD.getFullName(), roles[1].getFullName());
+
+ roles = graph.getDirectSubordinates(roleC);
+ ASSERT_EQUALS(0U, roles.size());
+
+ roles = graph.getDirectMembers(roleA);
+ ASSERT_EQUALS(0U, roles.size());
+
+ roles = graph.getDirectMembers(roleB);
+ ASSERT_EQUALS(1U, roles.size());
+ ASSERT_EQUALS(roleA.getFullName(), roles[0].getFullName());
+
+ roles = graph.getDirectMembers(roleC); // should be role A then role B
+ ASSERT_EQUALS(2U, roles.size());
+ ASSERT_EQUALS(roleA.getFullName(), roles[0].getFullName());
+ ASSERT_EQUALS(roleB.getFullName(), roles[1].getFullName());
- // Now remove roleD from roleB and make sure graph is update correctly
+ // Now remove roleD from roleB and make sure graph is updated correctly
ASSERT_OK(graph.removeRoleFromRole(roleB, roleD));
/*
@@ -175,23 +152,23 @@ namespace {
* v v
* B -> C
*/
- it = graph.getDirectSubordinates(roleB); // should be just roleC
- ASSERT_EQUALS(it.next().getFullName(), roleC.getFullName());
- ASSERT_FALSE(it.more());
+ roles = graph.getDirectSubordinates(roleB); // should be just roleC
+ ASSERT_EQUALS(1U, roles.size());
+ ASSERT_EQUALS(roleC.getFullName(), roles[0].getFullName());
- it = graph.getDirectSubordinates(roleD); // should be empty
- ASSERT_FALSE(it.more());
+ roles = graph.getDirectSubordinates(roleD); // should be empty
+ ASSERT_EQUALS(0U, roles.size());
// Now delete roleB entirely and make sure that the other roles are updated properly
ASSERT_OK(graph.deleteRole(roleB));
ASSERT_NOT_OK(graph.deleteRole(roleB));
- it = graph.getDirectSubordinates(roleA);
- ASSERT_EQUALS(it.next().getFullName(), roleC.getFullName());
- ASSERT_FALSE(it.more());
- it = graph.getDirectMembers(roleC);
- ASSERT_EQUALS(it.next().getFullName(), roleA.getFullName());
- ASSERT_FALSE(it.more());
+ roles = graph.getDirectSubordinates(roleA);
+ ASSERT_EQUALS(1U, roles.size());
+ ASSERT_EQUALS(roleC.getFullName(), roles[0].getFullName());
+ roles = graph.getDirectMembers(roleC);
+ ASSERT_EQUALS(1U, roles.size());
+ ASSERT_EQUALS(roleA.getFullName(), roles[0].getFullName());
}
const ResourcePattern collectionAFooResource(ResourcePattern::forExactNamespace(
@@ -526,10 +503,9 @@ namespace {
// properly.
swap(tempGraph, graph);
- RoleNameIterator it = graph.getDirectSubordinates(roleB);
- ASSERT_TRUE(it.more());
- ASSERT_EQUALS(it.next().getFullName(), roleC.getFullName());
- ASSERT_FALSE(it.more());
+ std::vector<RoleName> roles = graph.getDirectSubordinates(roleB);
+ ASSERT_EQUALS(1U, roles.size());
+ ASSERT_EQUALS(roleC.getFullName(), roles[0].getFullName());
graph.getAllPrivileges(roleA); // should have privileges from roleB *and* role C
PrivilegeVector privileges = graph.getAllPrivileges(roleA);
diff --git a/src/mongo/db/auth/user_management_commands_parser.cpp b/src/mongo/db/auth/user_management_commands_parser.cpp
index 2678cbd9b0b..88b8c90bee6 100644
--- a/src/mongo/db/auth/user_management_commands_parser.cpp
+++ b/src/mongo/db/auth/user_management_commands_parser.cpp
@@ -35,7 +35,6 @@
#include "mongo/bson/util/bson_extract.h"
#include "mongo/client/auth_helpers.h"
#include "mongo/db/auth/action_type.h"
-#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/auth/privilege_parser.h"
#include "mongo/db/auth/user_document_parser.h"
@@ -176,7 +175,7 @@ namespace auth {
BSONObj* parsedWriteConcern) {
unordered_set<std::string> validFieldNames;
validFieldNames.insert(cmdName.toString());
- validFieldNames.insert("roles");
+ validFieldNames.insert(rolesFieldName.toString());
validFieldNames.insert("writeConcern");
Status status = _checkNoExtraFields(cmdObj, cmdName, validFieldNames);
@@ -189,15 +188,13 @@ namespace auth {
return status;
}
- std::string userNameStr;
- status = bsonExtractStringField(cmdObj, cmdName, &userNameStr);
+ status = bsonExtractStringField(cmdObj, cmdName, parsedName);
if (!status.isOK()) {
return status;
}
- *parsedName = userNameStr;
BSONElement rolesElement;
- status = bsonExtractTypedField(cmdObj, "roles", Array, &rolesElement);
+ status = bsonExtractTypedField(cmdObj, rolesFieldName, Array, &rolesElement);
if (!status.isOK()) {
return status;
}
@@ -212,85 +209,12 @@ namespace auth {
if (!parsedRoleNames->size()) {
return Status(ErrorCodes::BadValue,
- mongoutils::str::stream() << cmdName << " command requires a non-empty" <<
- " roles array");
+ mongoutils::str::stream() << cmdName << " command requires a non-empty \""
+ << rolesFieldName << "\" array");
}
return Status::OK();
}
- /**
- * Validates that the roles array described by rolesElement is valid.
- * Also returns a new roles array (via the modifiedRolesArray output param) where any roles
- * from the input array that were listed as strings have been expanded to a full role document.
- * If includePossessionBools is true then the expanded roles documents will have "hasRole"
- * and "canDelegate" boolean fields (in addition to the "name" and "source" fields which are
- * there either way).
- */
- Status _validateAndModifyRolesArray(const BSONElement& rolesElement,
- const std::string& dbname,
- AuthorizationManager* authzManager,
- bool includePossessionBools,
- BSONArray* modifiedRolesArray) {
- BSONArrayBuilder rolesBuilder;
-
- for (BSONObjIterator it(rolesElement.Obj()); it.more(); it.next()) {
- BSONElement element = *it;
- if (element.type() == String) {
- RoleName roleName(element.String(), dbname);
- if (!authzManager->roleExists(roleName)) {
- return Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << roleName.toString() <<
- " does not name an existing role");
- }
-
- if (includePossessionBools) {
- rolesBuilder.append(BSON("name" << element.String() <<
- "source" << dbname <<
- "hasRole" << true <<
- "canDelegate" << false));
- } else {
- rolesBuilder.append(BSON("name" << element.String() <<
- "source" << dbname));
- }
- } else if (element.type() == Object) {
- // Check that the role object is valid
- V2UserDocumentParser parser;
- BSONObj roleObj = element.Obj();
- Status status = parser.checkValidRoleObject(roleObj, includePossessionBools);
- if (!status.isOK()) {
- return status;
- }
-
- // Check that the role actually exists
- std::string roleNameString;
- std::string roleSource;
- status = bsonExtractStringField(roleObj, "name", &roleNameString);
- if (!status.isOK()) {
- return status;
- }
- status = bsonExtractStringField(roleObj, "source", &roleSource);
- if (!status.isOK()) {
- return status;
- }
-
- RoleName roleName(roleNameString, roleSource);
- if (!authzManager->roleExists(roleName)) {
- return Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << roleName.toString() <<
- " does not name an existing role");
- }
-
- rolesBuilder.append(element);
- } else {
- return Status(ErrorCodes::UnsupportedFormat,
- "Values in 'roles' array must be sub-documents or strings");
- }
- }
-
- *modifiedRolesArray = rolesBuilder.arr();
- return Status::OK();
- }
-
Status parseCreateOrUpdateUserCommands(const BSONObj& cmdObj,
const StringData& cmdName,
const std::string& dbname,
@@ -591,5 +515,52 @@ namespace auth {
return Status::OK();
}
+ Status parseRemoveRoleCommand(const BSONObj& cmdObj,
+ const std::string& dbname,
+ RoleName* parsedRoleName,
+ BSONObj* parsedWriteConcern) {
+ unordered_set<std::string> validFieldNames;
+ validFieldNames.insert("removeRole");
+ validFieldNames.insert("writeConcern");
+
+ Status status = _checkNoExtraFields(cmdObj, "removeRole", validFieldNames);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ std::string user;
+ status = bsonExtractStringField(cmdObj, "removeRole", &user);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ status = _extractWriteConcern(cmdObj, parsedWriteConcern);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ *parsedRoleName = RoleName(user, dbname);
+ return Status::OK();
+ }
+
+ Status parseRemoveRolesFromDatabaseCommand(const BSONObj& cmdObj,
+ const std::string& dbname,
+ BSONObj* parsedWriteConcern) {
+ unordered_set<std::string> validFieldNames;
+ validFieldNames.insert("removeRolesFromDatabase");
+ validFieldNames.insert("writeConcern");
+
+ Status status = _checkNoExtraFields(cmdObj, "removeRolesFromDatabase", validFieldNames);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ status = _extractWriteConcern(cmdObj, parsedWriteConcern);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ return Status::OK();
+ }
} // namespace auth
} // namespace mongo
diff --git a/src/mongo/db/auth/user_management_commands_parser.h b/src/mongo/db/auth/user_management_commands_parser.h
index d1bca97c5ad..4d49ebb77a3 100644
--- a/src/mongo/db/auth/user_management_commands_parser.h
+++ b/src/mongo/db/auth/user_management_commands_parser.h
@@ -39,9 +39,6 @@
#include "mongo/db/jsobj.h"
namespace mongo {
-
- class AuthorizationManager;
-
namespace auth {
struct CreateOrUpdateUserArgs {
@@ -69,9 +66,11 @@ namespace auth {
/**
* Takes a command object describing an invocation of one of "grantRolesToUser",
- * "revokeRolesFromUser", "grantDelegateRolesToUser", and "revokeDelegateRolesFromUser" (which
- * command it is is specified in the "cmdName" argument), and parses out the user name of the
- * user being modified, the roles being granted or revoked, and the write concern to use.
+ * "revokeRolesFromUser", "grantDelegateRolesToUser", "revokeDelegateRolesFromUser",
+ * "grantRolesToRole", and "revokeRolesFromRoles" (which command it is is specified in the
+ * "cmdName" argument), and parses out (into the parsedName out param) the user/role name of
+ * the user/roles being modified, the roles being granted or revoked, and the write concern to
+ * use.
*/
Status parseRolePossessionManipulationCommands(const BSONObj& cmdObj,
const StringData& cmdName,
@@ -83,7 +82,7 @@ namespace auth {
/**
* Takes a command object describing an invocation of the "removeUser" command and parses out
- * the userName of the user to be removed and the writeConcern.
+ * the UserName of the user to be removed and the writeConcern.
* Also validates the input and returns a non-ok Status if there is anything wrong.
*/
Status parseAndValidateRemoveUserCommand(const BSONObj& cmdObj,
@@ -144,5 +143,22 @@ namespace auth {
PrivilegeVector* parsedPrivileges,
BSONObj* parsedWriteConcern);
+ /**
+ * Takes a command object describing an invocation of the "removeRole" command and parses out
+ * the RoleName of the role to be removed and the writeConcern.
+ */
+ Status parseRemoveRoleCommand(const BSONObj& cmdObj,
+ const std::string& dbname,
+ RoleName* parsedRoleName,
+ BSONObj* parsedWriteConcern);
+
+ /**
+ * Takes a command object describing an invocation of the "removeRolesFromDatabase" command and
+ * parses out the write concern.
+ */
+ Status parseRemoveRolesFromDatabaseCommand(const BSONObj& cmdObj,
+ const std::string& dbname,
+ BSONObj* parsedWriteConcern);
+
} // namespace auth
} // namespace mongo
diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp
index d778e705ac1..826cd0ff10c 100644
--- a/src/mongo/db/commands/user_management_commands.cpp
+++ b/src/mongo/db/commands/user_management_commands.cpp
@@ -53,7 +53,7 @@
namespace mongo {
- using mongoutils::str::stream;
+ namespace str = mongoutils::str;
static void addStatus(const Status& status, BSONObjBuilder& builder) {
builder.append("ok", status.isOK() ? 1.0: 0.0);
@@ -87,15 +87,15 @@ namespace mongo {
}
// Should only be called inside the AuthzUpdateLock
- static Status getBSONForRoleVectorIfRolesExist(const std::vector<RoleName>& roles,
- AuthorizationManager* authzManager,
- BSONArray* result) {
+ static Status rolesVectorToBSONArrayIfRolesExist(const std::vector<RoleName>& roles,
+ AuthorizationManager* authzManager,
+ BSONArray* result) {
BSONArrayBuilder rolesArrayBuilder;
for (std::vector<RoleName>::const_iterator it = roles.begin(); it != roles.end(); ++it) {
const RoleName& role = *it;
if (!authzManager->roleExists(role)) {
return Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << role.getFullName() <<
+ str::stream() << role.getFullName() <<
" does not name an existing role");
}
rolesArrayBuilder.append(BSON("name" << role.getRole() << "source" << role.getDB()));
@@ -114,7 +114,7 @@ namespace mongo {
const User::RoleData& role = *it;
if (!authzManager->roleExists(role.name)) {
return Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << it->name.getFullName() <<
+ str::stream() << it->name.getFullName() <<
" does not name an existing role");
}
if (!role.hasRole && !role.canDelegate) {
@@ -234,7 +234,7 @@ namespace mongo {
BSONObjBuilder userObjBuilder;
userObjBuilder.append("_id",
- stream() << args.userName.getDB() << "." <<
+ str::stream() << args.userName.getDB() << "." <<
args.userName.getUser());
userObjBuilder.append(AuthorizationManager::USER_NAME_FIELD_NAME,
args.userName.getUser());
@@ -459,7 +459,7 @@ namespace mongo {
if (numUpdated == 0) {
addStatus(Status(ErrorCodes::UserNotFound,
- mongoutils::str::stream() << "User '" << userName.getFullName() <<
+ str::stream() << "User '" << userName.getFullName() <<
"' not found"),
result);
return false;
@@ -612,7 +612,7 @@ namespace mongo {
RoleName& roleName = *it;
if (!authzManager->roleExists(roleName)) {
addStatus(Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << roleName.getFullName() <<
+ str::stream() << roleName.getFullName() <<
" does not name an existing role"),
result);
return false;
@@ -710,7 +710,7 @@ namespace mongo {
RoleName& roleName = *it;
if (!authzManager->roleExists(roleName)) {
addStatus(Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << roleName.getFullName() <<
+ str::stream() << roleName.getFullName() <<
" does not name an existing role"),
result);
return false;
@@ -816,7 +816,7 @@ namespace mongo {
RoleName& roleName = *it;
if (!authzManager->roleExists(roleName)) {
addStatus(Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << roleName.getFullName() <<
+ str::stream() << roleName.getFullName() <<
" does not name an existing role"),
result);
return false;
@@ -915,7 +915,7 @@ namespace mongo {
RoleName& roleName = *it;
if (!authzManager->roleExists(roleName)) {
addStatus(Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << roleName.getFullName() <<
+ str::stream() << roleName.getFullName() <<
" does not name an existing role"),
result);
return false;
@@ -1089,7 +1089,7 @@ namespace mongo {
BSONObjBuilder roleObjBuilder;
- roleObjBuilder.append("_id", stream() << args.roleName.getDB() << "." <<
+ roleObjBuilder.append("_id", str::stream() << args.roleName.getDB() << "." <<
args.roleName.getRole());
roleObjBuilder.append(AuthorizationManager::ROLE_NAME_FIELD_NAME,
args.roleName.getRole());
@@ -1114,7 +1114,7 @@ namespace mongo {
// Role existence has to be checked after acquiring the update lock
BSONArray roles;
- status = getBSONForRoleVectorIfRolesExist(args.roles, authzManager, &roles);
+ status = rolesVectorToBSONArrayIfRolesExist(args.roles, authzManager, &roles);
if (!status.isOK()) {
addStatus(status, result);
return false;
@@ -1131,10 +1131,107 @@ namespace mongo {
} cmdCreateRole;
- class CmdGrantPrivilegeToRole: public Command {
+ class CmdUpdateRole: public Command {
public:
- CmdGrantPrivilegeToRole() : Command("grantPrivilegesToRole") {}
+ CmdUpdateRole() : Command("updateRole") {}
+
+ virtual bool logTheOp() {
+ return false;
+ }
+
+ virtual bool slaveOk() const {
+ return false;
+ }
+
+ virtual LockType locktype() const {
+ return NONE;
+ }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Used to update a role" << endl;
+ }
+
+ virtual void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) {
+ // TODO: update this with the new rules around user creation in 2.6.
+ ActionSet actions;
+ actions.addAction(ActionType::userAdmin);
+ out->push_back(Privilege(ResourcePattern::forDatabaseName(dbname), actions));
+ }
+
+ bool run(const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result,
+ bool fromRepl) {
+ auth::CreateOrUpdateRoleArgs args;
+ Status status = auth::parseCreateOrUpdateRoleCommands(cmdObj,
+ "createRole",
+ dbname,
+ &args);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+
+ if (!args.hasPrivileges && !args.hasRoles) {
+ addStatus(Status(ErrorCodes::BadValue,
+ "Must specify at least one field to update in updateRole"),
+ result);
+ return false;
+ }
+
+ BSONObjBuilder updateSetBuilder;
+
+ if (args.hasPrivileges) {
+ BSONArray privileges;
+ status = privilegeVectorToBSONArray(args.privileges, &privileges);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+ updateSetBuilder.append("privileges", privileges);
+ }
+
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ AuthzDocumentsUpdateGuard updateGuard(authzManager);
+ if (!updateGuard.tryLock("Update role")) {
+ addStatus(Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."),
+ result);
+ return false;
+ }
+
+ // Role existence has to be checked after acquiring the update lock
+ if (args.hasRoles) {
+ BSONArray roles;
+ status = rolesVectorToBSONArrayIfRolesExist(args.roles, authzManager, &roles);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+
+ updateSetBuilder.append("roles", roles);
+ }
+
+ status = authzManager->updateRoleDocument(args.roleName,
+ BSON("$set" << updateSetBuilder.done()),
+ args.writeConcern);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+
+ return true;
+ }
+ } cmdUpdateRole;
+
+ class CmdGrantPrivilegesToRole: public Command {
+ public:
+
+ CmdGrantPrivilegesToRole() : Command("grantPrivilegesToRole") {}
virtual bool logTheOp() {
return false;
@@ -1192,7 +1289,7 @@ namespace mongo {
if (!authzManager->roleExists(roleName)) {
addStatus(Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << roleName.getFullName() <<
+ str::stream() << roleName.getFullName() <<
" does not name an existing role"),
result);
return false;
@@ -1200,7 +1297,7 @@ namespace mongo {
if (authzManager->isBuiltinRole(roleName)) {
addStatus(Status(ErrorCodes::InvalidRoleModification,
- mongoutils::str::stream() << roleName.getFullName() <<
+ str::stream() << roleName.getFullName() <<
" is a built-in role and cannot be modified."),
result);
return false;
@@ -1248,6 +1345,501 @@ namespace mongo {
} cmdGrantPrivilegesToRole;
+ class CmdRevokePrivilegesFromRole: public Command {
+ public:
+
+ CmdRevokePrivilegesFromRole() : Command("revokePrivilegesFromRole") {}
+
+ virtual bool logTheOp() {
+ return false;
+ }
+
+ virtual bool slaveOk() const {
+ return false;
+ }
+
+ virtual LockType locktype() const {
+ return NONE;
+ }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Revokes privileges from a role" << endl;
+ }
+
+ virtual void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) {
+ // TODO: update this with the new rules around user creation in 2.6.
+ ActionSet actions;
+ actions.addAction(ActionType::userAdmin);
+ out->push_back(Privilege(ResourcePattern::forDatabaseName(dbname), actions));
+ }
+
+ bool run(const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result,
+ bool fromRepl) {
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ AuthzDocumentsUpdateGuard updateGuard(authzManager);
+ if (!updateGuard.tryLock("Revoke privileges from role")) {
+ addStatus(Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."),
+ result);
+ return false;
+ }
+
+ RoleName roleName;
+ PrivilegeVector privilegesToRemove;
+ BSONObj writeConcern;
+ Status status = auth::parseAndValidateRolePrivilegeManipulationCommands(
+ cmdObj,
+ "revokePrivilegesFromRole",
+ dbname,
+ &roleName,
+ &privilegesToRemove,
+ &writeConcern);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+
+ if (!authzManager->roleExists(roleName)) {
+ addStatus(Status(ErrorCodes::RoleNotFound,
+ str::stream() << roleName.getFullName() <<
+ " does not name an existing role"),
+ result);
+ return false;
+ }
+
+ if (authzManager->isBuiltinRole(roleName)) {
+ addStatus(Status(ErrorCodes::InvalidRoleModification,
+ str::stream() << roleName.getFullName() <<
+ " is a built-in role and cannot be modified."),
+ result);
+ return false;
+ }
+
+ PrivilegeVector privileges = authzManager->getDirectPrivilegesForRole(roleName);
+ for (PrivilegeVector::iterator itToRm = privilegesToRemove.begin();
+ itToRm != privilegesToRemove.end(); ++itToRm) {
+ for (PrivilegeVector::iterator curIt = privileges.begin();
+ curIt != privileges.end(); ++curIt) {
+ if (curIt->getResourcePattern() == itToRm->getResourcePattern()) {
+ curIt->removeActions(itToRm->getActions());
+ if (curIt->getActions().empty()) {
+ privileges.erase(curIt);
+ }
+ break;
+ }
+ }
+ }
+
+ // Build up update modifier object to $set privileges.
+ mutablebson::Document updateObj;
+ mutablebson::Element setElement = updateObj.makeElementObject("$set");
+ status = updateObj.root().pushBack(setElement);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+ mutablebson::Element privilegesElement = updateObj.makeElementArray("privileges");
+ status = setElement.pushBack(privilegesElement);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+ status = authzManager->getBSONForPrivileges(privileges, privilegesElement);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+
+ BSONObjBuilder updateBSONBuilder;
+ updateObj.writeTo(&updateBSONBuilder);
+ status = authzManager->updateRoleDocument(
+ roleName,
+ updateBSONBuilder.done(),
+ writeConcern);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+
+ return true;
+ }
+
+ } cmdRevokePrivilegesFromRole;
+
+ class CmdGrantRolesToRole: public Command {
+ public:
+
+ CmdGrantRolesToRole() : Command("grantRolesToRole") {}
+
+ virtual bool logTheOp() {
+ return false;
+ }
+
+ virtual bool slaveOk() const {
+ return false;
+ }
+
+ virtual LockType locktype() const {
+ return NONE;
+ }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Grants roles to another role." << endl;
+ }
+
+ virtual void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) {
+ // TODO: update this with the new rules around user creation in 2.6.
+ ActionSet actions;
+ actions.addAction(ActionType::userAdmin);
+ out->push_back(Privilege(ResourcePattern::forDatabaseName(dbname), actions));
+ }
+
+ bool run(const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result,
+ bool fromRepl) {
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ AuthzDocumentsUpdateGuard updateGuard(authzManager);
+ if (!updateGuard.tryLock("Grant roles to role")) {
+ addStatus(Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."),
+ result);
+ return false;
+ }
+
+ std::string roleNameString;
+ std::vector<RoleName> rolesToAdd;
+ BSONObj writeConcern;
+ Status status = auth::parseRolePossessionManipulationCommands(cmdObj,
+ "grantRolesToRole",
+ "grantedRoles",
+ dbname,
+ &roleNameString,
+ &rolesToAdd,
+ &writeConcern);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+
+ RoleName roleName(roleNameString, dbname);
+ if (!authzManager->roleExists(roleName)) {
+ addStatus(Status(ErrorCodes::RoleNotFound,
+ str::stream() << roleName.getFullName() <<
+ " does not name an existing role"),
+ result);
+ return false;
+ }
+
+ if (authzManager->isBuiltinRole(roleName)) {
+ addStatus(Status(ErrorCodes::InvalidRoleModification,
+ str::stream() << roleName.getFullName() <<
+ " is a built-in role and cannot be modified."),
+ result);
+ return false;
+ }
+
+ // TODO(spencer): Make sure that this update doesn't introduce a cycle
+ std::vector<RoleName> roles = authzManager->getSubordinateRolesForRole(roleName);
+ for (vector<RoleName>::iterator it = rolesToAdd.begin(); it != rolesToAdd.end(); ++it) {
+ RoleName& roleToAdd = *it;
+ if (std::find(roles.begin(), roles.end(), roleToAdd) == roles.end()) {
+ // Only add role if it's not already present
+ roles.push_back(roleToAdd);
+ }
+ }
+
+ BSONArray newRolesBSONArray;
+ status = rolesVectorToBSONArrayIfRolesExist(roles,
+ authzManager,
+ &newRolesBSONArray);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+ status = authzManager->updateRoleDocument(
+ roleName, BSON("$set" << BSON("roles" << newRolesBSONArray)), writeConcern);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+ return true;
+ }
+
+ } cmdGrantRolesToRole;
+
+ class CmdRevokeRolesFromRole: public Command {
+ public:
+
+ CmdRevokeRolesFromRole() : Command("revokeRolesFromRole") {}
+
+ virtual bool logTheOp() {
+ return false;
+ }
+
+ virtual bool slaveOk() const {
+ return false;
+ }
+
+ virtual LockType locktype() const {
+ return NONE;
+ }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Revokes roles from another role." << endl;
+ }
+
+ virtual void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) {
+ // TODO: update this with the new rules around user creation in 2.6.
+ ActionSet actions;
+ actions.addAction(ActionType::userAdmin);
+ out->push_back(Privilege(ResourcePattern::forDatabaseName(dbname), actions));
+ }
+
+ bool run(const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result,
+ bool fromRepl) {
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ AuthzDocumentsUpdateGuard updateGuard(authzManager);
+ if (!updateGuard.tryLock("Revoke roles from role")) {
+ addStatus(Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."),
+ result);
+ return false;
+ }
+
+ std::string roleNameString;
+ std::vector<RoleName> rolesToRemove;
+ BSONObj writeConcern;
+ Status status = auth::parseRolePossessionManipulationCommands(cmdObj,
+ "revokeRolesFromRole",
+ "revokedRoles",
+ dbname,
+ &roleNameString,
+ &rolesToRemove,
+ &writeConcern);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+
+ RoleName roleName(roleNameString, dbname);
+ if (!authzManager->roleExists(roleName)) {
+ addStatus(Status(ErrorCodes::RoleNotFound,
+ str::stream() << roleName.getFullName() <<
+ " does not name an existing role"),
+ result);
+ return false;
+ }
+
+ if (authzManager->isBuiltinRole(roleName)) {
+ addStatus(Status(ErrorCodes::InvalidRoleModification,
+ str::stream() << roleName.getFullName() <<
+ " is a built-in role and cannot be modified."),
+ result);
+ return false;
+ }
+
+ std::vector<RoleName> roles = authzManager->getSubordinateRolesForRole(roleName);
+ for (vector<RoleName>::iterator it = rolesToRemove.begin();
+ it != rolesToRemove.end(); ++it) {
+ vector<RoleName>::iterator itToRm = std::find(roles.begin(), roles.end(), *it);
+ if (itToRm != roles.end()) {
+ roles.erase(itToRm);
+ }
+ }
+
+ BSONArray newRolesBSONArray;
+ status = rolesVectorToBSONArrayIfRolesExist(roles,
+ authzManager,
+ &newRolesBSONArray);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+ status = authzManager->updateRoleDocument(
+ roleName, BSON("$set" << BSON("roles" << newRolesBSONArray)), writeConcern);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+ return true;
+ }
+
+ } cmdRevokeRolesFromRole;
+
+ class CmdRemoveRole: public Command {
+ public:
+
+ CmdRemoveRole() : Command("removeRole") {}
+
+ virtual bool logTheOp() {
+ return false;
+ }
+
+ virtual bool slaveOk() const {
+ return false;
+ }
+
+ virtual LockType locktype() const {
+ return NONE;
+ }
+
+ virtual void help(stringstream& ss) const {
+ ss << "Removes a single role. Before deleting the role completely it must remove it "
+ "from any users or roles that reference it. If any errors occur in the middle "
+ "of that process it's possible to be left in a state where the role has been "
+ "removed from some user/roles but otherwise still exists."<< endl;
+ }
+
+ virtual void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) {
+ // TODO: update this with the new rules around user creation in 2.6.
+ ActionSet actions;
+ actions.addAction(ActionType::userAdmin);
+ out->push_back(Privilege(ResourcePattern::forDatabaseName(dbname), actions));
+ }
+
+ bool run(const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result,
+ bool fromRepl) {
+ AuthorizationManager* authzManager = getGlobalAuthorizationManager();
+ AuthzDocumentsUpdateGuard updateGuard(authzManager);
+ if (!updateGuard.tryLock("Remove role")) {
+ addStatus(Status(ErrorCodes::LockBusy, "Could not lock auth data update lock."),
+ result);
+ return false;
+ }
+
+ RoleName roleName;
+ BSONObj writeConcern;
+
+ Status status = auth::parseRemoveRoleCommand(cmdObj,
+ dbname,
+ &roleName,
+ &writeConcern);
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+
+ if (!authzManager->roleExists(roleName)) {
+ addStatus(Status(ErrorCodes::RoleNotFound,
+ str::stream() << roleName.getFullName() <<
+ " does not name an existing role"),
+ result);
+ return false;
+ }
+
+ if (authzManager->isBuiltinRole(roleName)) {
+ addStatus(Status(ErrorCodes::InvalidRoleModification,
+ str::stream() << roleName.getFullName() <<
+ " is a built-in role and cannot be modified."),
+ result);
+ return false;
+ }
+
+ // Remove this role from all users
+ int numUpdated;
+ status = authzManager->updateAuthzDocuments(
+ NamespaceString("admin.system.users"),
+ BSON("roles" << BSON("$elemMatch" <<
+ BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME <<
+ roleName.getRole() <<
+ AuthorizationManager::ROLE_SOURCE_FIELD_NAME <<
+ roleName.getDB()))),
+ BSON("$pull" << BSON("roles" <<
+ BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME <<
+ roleName.getRole() <<
+ AuthorizationManager::ROLE_SOURCE_FIELD_NAME <<
+ roleName.getDB()))),
+ false,
+ true,
+ writeConcern,
+ &numUpdated);
+ if (!status.isOK()) {
+ ErrorCodes::Error code = status.code() == ErrorCodes::UnknownError ?
+ ErrorCodes::UserModificationFailed : status.code();
+ addStatus(Status(code,
+ str::stream() << "Failed to remove role " << roleName.getFullName()
+ << " from all users: " << status.reason()),
+ result);
+ return false;
+ }
+
+ // Remove this role from all other roles
+ status = authzManager->updateAuthzDocuments(
+ NamespaceString("admin.system.roles"),
+ BSON("roles" << BSON("$elemMatch" <<
+ BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME <<
+ roleName.getRole() <<
+ AuthorizationManager::ROLE_SOURCE_FIELD_NAME <<
+ roleName.getDB()))),
+ BSON("$pull" << BSON("roles" <<
+ BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME <<
+ roleName.getRole() <<
+ AuthorizationManager::ROLE_SOURCE_FIELD_NAME <<
+ roleName.getDB()))),
+ false,
+ true,
+ writeConcern,
+ &numUpdated);
+ if (!status.isOK()) {
+ ErrorCodes::Error code = status.code() == ErrorCodes::UnknownError ?
+ ErrorCodes::RoleModificationFailed : status.code();
+ addStatus(Status(code,
+ str::stream() << "Removed role " << roleName.getFullName() <<
+ " from all users but failed to remove from all roles: " <<
+ status.reason()),
+ result);
+ return false;
+ }
+
+ // Finally, remove the actual role document
+ status = authzManager->removeRoleDocuments(
+ BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME << roleName.getRole() <<
+ AuthorizationManager::ROLE_SOURCE_FIELD_NAME << roleName.getDB()),
+ writeConcern,
+ &numUpdated);
+ if (!status.isOK()) {
+ addStatus(Status(status.code(),
+ str::stream() << "Removed role " << roleName.getFullName() <<
+ " from all users and roles but failed to actually delete"
+ " the role itself: " << status.reason()),
+ result);
+ return false;
+ }
+
+ dassert(numUpdated == 0 || numUpdated == 1);
+ if (numUpdated == 0) {
+ addStatus(Status(ErrorCodes::RoleNotFound,
+ str::stream() << "Role '" << roleName.getFullName() <<
+ "' not found"),
+ result);
+ return false;
+ }
+
+ return true;
+ }
+
+ } cmdRemoveRole;
+
class CmdRolesInfo: public Command {
public: