diff options
-rw-r--r-- | src/mongo/db/auth/role_graph.cpp | 82 | ||||
-rw-r--r-- | src/mongo/db/auth/role_graph.h | 10 | ||||
-rw-r--r-- | src/mongo/db/auth/role_graph_test.cpp | 24 | ||||
-rw-r--r-- | src/mongo/db/auth/role_name.h | 2 |
4 files changed, 101 insertions, 17 deletions
diff --git a/src/mongo/db/auth/role_graph.cpp b/src/mongo/db/auth/role_graph.cpp index 17b28e502c4..a34fa853260 100644 --- a/src/mongo/db/auth/role_graph.cpp +++ b/src/mongo/db/auth/role_graph.cpp @@ -40,6 +40,41 @@ namespace mongo { namespace { PrivilegeVector emptyPrivilegeVector; + + // RoleNameIterator for iterating over an unordered_set of RoleNames. + class RoleNameSetIterator : public RoleNameIterator::Impl { + MONGO_DISALLOW_COPYING(RoleNameSetIterator); + + public: + RoleNameSetIterator(const unordered_set<RoleName>::const_iterator& begin, + const unordered_set<RoleName>::const_iterator& end) : + _begin(begin), _end(end) {} + + virtual ~RoleNameSetIterator() {} + + virtual bool more() const { + return _begin != _end; + } + + virtual const RoleName& next() { + const RoleName& toReturn = get(); + ++_begin; + return toReturn; + } + + + virtual const RoleName& get() const { + return *_begin; + } + + private: + virtual Impl* doClone() const { + return new RoleNameSetIterator(_begin, _end); + } + + unordered_set<RoleName>::const_iterator _begin; + unordered_set<RoleName>::const_iterator _end; + }; } // namespace RoleGraph::RoleGraph() {}; @@ -148,6 +183,13 @@ namespace { return RoleNameIterator(new RoleNameVectorIterator(edges.begin(), edges.end())); } + RoleNameIterator RoleGraph::getIndirectSubordinates(const RoleName& role) { + if (!roleExists(role)) + return RoleNameIterator(NULL); + const unordered_set<RoleName>& subs = _roleToIndirectSubordinates.find(role)->second; + return RoleNameIterator(new RoleNameSetIterator(subs.begin(), subs.end())); + } + RoleNameIterator RoleGraph::getDirectMembers(const RoleName& role) { if (!roleExists(role)) return RoleNameIterator(NULL); @@ -171,20 +213,17 @@ namespace { if (!roleExists(recipient)) { return Status(ErrorCodes::RoleNotFound, mongoutils::str::stream() << "Role: " << recipient.getFullName() << - " does not exist", - 0); + " does not exist"); } if (isBuiltinRole(recipient)) { return Status(ErrorCodes::InvalidRoleModification, mongoutils::str::stream() << "Cannot grant roles to built-in role: " << - role.getFullName(), - 0); + role.getFullName()); } if (!roleExists(role)) { return Status(ErrorCodes::RoleNotFound, mongoutils::str::stream() << "Role: " << role.getFullName() << - " does not exist", - 0); + " does not exist"); } if (std::find(_roleToSubordinates[recipient].begin(), @@ -438,17 +477,22 @@ namespace { // Need to clear out the "all privileges" vector for the current role, and re-fill it with // just the direct privileges for this role. PrivilegeVector& currentRoleAllPrivileges = _allPrivilegesForRole[currentRole]; - const PrivilegeVector& currentRoleDirectPrivileges = _directPrivilegesForRole[currentRole]; - currentRoleAllPrivileges.clear(); - for (PrivilegeVector::const_iterator it = currentRoleDirectPrivileges.begin(); - it != currentRoleDirectPrivileges.end(); ++it) { - currentRoleAllPrivileges.push_back(*it); + currentRoleAllPrivileges = _directPrivilegesForRole[currentRole]; + + // Need to do the same thing for the indirect roles + unordered_set<RoleName>& currentRoleIndirectRoles = + _roleToIndirectSubordinates[currentRole]; + currentRoleIndirectRoles.clear(); + const std::vector<RoleName>& currentRoleDirectRoles = _roleToSubordinates[currentRole]; + for (std::vector<RoleName>::const_iterator it = currentRoleDirectRoles.begin(); + it != currentRoleDirectRoles.end(); ++it) { + currentRoleIndirectRoles.insert(*it); } - // Recursively add children's privileges to current role's "all privileges" vector. - const std::vector<RoleName>& children = _roleToSubordinates[currentRole]; - for (std::vector<RoleName>::const_iterator roleIt = children.begin(); - roleIt != children.end(); ++roleIt) { + // Recursively add children's privileges to current role's "all privileges" vector, and + // children's roles to current roles's "indirect roles" vector. + for (std::vector<RoleName>::const_iterator roleIt = currentRoleDirectRoles.begin(); + roleIt != currentRoleDirectRoles.end(); ++roleIt) { const RoleName& childRole = *roleIt; Status status = _recomputePrivilegeDataHelper(childRole, inProgressRoles, visitedRoles); if (status != Status::OK()) { @@ -462,6 +506,14 @@ namespace { privIt != childsPrivileges.end(); ++privIt) { Privilege::addPrivilegeToPrivilegeVector(¤tRoleAllPrivileges, *privIt); } + + // We also know that the "indirect roles" for the child is also correct, so we can add + // those roles to our "indirect roles" set. + const unordered_set<RoleName>& childsRoles = _roleToIndirectSubordinates[childRole]; + for (unordered_set<RoleName>::const_iterator childsRoleIt = childsRoles.begin(); + childsRoleIt != childsRoles.end(); ++childsRoleIt) { + currentRoleIndirectRoles.insert(*childsRoleIt); + } } if (inProgressRoles.back() != currentRole) diff --git a/src/mongo/db/auth/role_graph.h b/src/mongo/db/auth/role_graph.h index be6c12a93c0..b7dcbbaa80f 100644 --- a/src/mongo/db/auth/role_graph.h +++ b/src/mongo/db/auth/role_graph.h @@ -89,6 +89,13 @@ namespace mongo { RoleNameIterator getDirectSubordinates(const RoleName& role); /** + * Returns an iterator that can be used to get a full list of roles that this role inherits + * privileges from. This includes its direct subordinate roles as well as the subordinates + * of its subordinates, and so on. + */ + RoleNameIterator getIndirectSubordinates(const RoleName& role); + + /** * 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. @@ -232,11 +239,12 @@ namespace mongo { // Represents all the outgoing edges to other roles from any given role. - typedef unordered_map<RoleName, vector<RoleName> > EdgeSet; + typedef unordered_map<RoleName, std::vector<RoleName> > EdgeSet; // Maps a role name to a list of privileges associated with that role. typedef unordered_map<RoleName, PrivilegeVector> RolePrivilegeMap; EdgeSet _roleToSubordinates; + unordered_map<RoleName, unordered_set<RoleName> > _roleToIndirectSubordinates; EdgeSet _roleToMembers; RolePrivilegeMap _directPrivilegesForRole; RolePrivilegeMap _allPrivilegesForRole; diff --git a/src/mongo/db/auth/role_graph_test.cpp b/src/mongo/db/auth/role_graph_test.cpp index 6f907cc67d3..3abb14974a1 100644 --- a/src/mongo/db/auth/role_graph_test.cpp +++ b/src/mongo/db/auth/role_graph_test.cpp @@ -108,6 +108,30 @@ namespace { } ASSERT_FALSE(it.more()); + it = graph.getIndirectSubordinates(roleA); // should have roleB, roleC and roleD + bool hasB = false; + bool hasC = false; + bool hasD = false; + int num = 0; + while (it.more()) { + ++num; + RoleName cur = it.next(); + if (cur == roleB) { + hasB = true; + } else if (cur == roleC) { + hasC = true; + } else if (cur == roleD) { + hasD = true; + } else { + FAIL(mongoutils::str::stream() << "unexpected role returned: " << + cur.getFullName()); + } + } + ASSERT_EQUALS(3, num); + ASSERT(hasB); + ASSERT(hasC); + ASSERT(hasD); + it = graph.getDirectSubordinates(roleB); // should be roleC and roleD, order doesn't matter cur = it.next(); if (cur == roleC) { diff --git a/src/mongo/db/auth/role_name.h b/src/mongo/db/auth/role_name.h index 45fb8a007fd..93942e14c67 100644 --- a/src/mongo/db/auth/role_name.h +++ b/src/mongo/db/auth/role_name.h @@ -149,7 +149,7 @@ MONGO_HASH_NAMESPACE_END namespace mongo { - // RoleNameIterator for iterating over an unordered_set of RoleNames. + // RoleNameIterator for iterating over a vector of RoleNames. class RoleNameVectorIterator : public RoleNameIterator::Impl { MONGO_DISALLOW_COPYING(RoleNameVectorIterator); |