diff options
author | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2015-06-20 00:22:50 -0400 |
---|---|---|
committer | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2015-06-20 10:56:02 -0400 |
commit | 9c2ed42daa8fbbef4a919c21ec564e2db55e8d60 (patch) | |
tree | 3814f79c10d7b490948d8cb7b112ac1dd41ceff1 /src/mongo/db/auth/role_graph_test.cpp | |
parent | 01965cf52bce6976637ecb8f4a622aeb05ab256a (diff) | |
download | mongo-9c2ed42daa8fbbef4a919c21ec564e2db55e8d60.tar.gz |
SERVER-18579: Clang-Format - reformat code, no comment reflow
Diffstat (limited to 'src/mongo/db/auth/role_graph_test.cpp')
-rw-r--r-- | src/mongo/db/auth/role_graph_test.cpp | 1343 |
1 files changed, 673 insertions, 670 deletions
diff --git a/src/mongo/db/auth/role_graph_test.cpp b/src/mongo/db/auth/role_graph_test.cpp index 4e99f584276..745207c4b6d 100644 --- a/src/mongo/db/auth/role_graph_test.cpp +++ b/src/mongo/db/auth/role_graph_test.cpp @@ -40,693 +40,696 @@ namespace mongo { namespace { - // Tests adding and removing roles from other roles, the RoleNameIterator, and the - // getDirectMembers and getDirectSubordinates methods - TEST(RoleGraphTest, AddRemoveRoles) { - RoleName roleA("roleA", "dbA"); - RoleName roleB("roleB", "dbB"); - RoleName roleC("roleC", "dbC"); - RoleName roleD("readWrite", "dbD"); // built-in role - - RoleGraph graph; - ASSERT_OK(graph.createRole(roleA)); - 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()); - - 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()); +// Tests adding and removing roles from other roles, the RoleNameIterator, and the +// getDirectMembers and getDirectSubordinates methods +TEST(RoleGraphTest, AddRemoveRoles) { + RoleName roleA("roleA", "dbA"); + RoleName roleB("roleB", "dbB"); + RoleName roleC("roleC", "dbC"); + RoleName roleD("readWrite", "dbD"); // built-in role + + RoleGraph graph; + ASSERT_OK(graph.createRole(roleA)); + 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()); + + 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()); + + 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.getDirectSubordinates(roleB); + ASSERT_FALSE(it.more()); + + ASSERT_OK(graph.addRoleToRole(roleA, roleC)); + ASSERT_OK(graph.addRoleToRole(roleB, roleC)); + ASSERT_OK(graph.addRoleToRole(roleB, roleD)); + // Adding the same role twice should be a no-op, duplicate roles should be de-duped. + ASSERT_OK(graph.addRoleToRole(roleB, roleD)); + + /* + * Graph now looks like: + * A + * / \ + * v v + * B -> C + * | + * v + * 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()); - 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.getDirectSubordinates(roleB); - ASSERT_FALSE(it.more()); - - ASSERT_OK(graph.addRoleToRole(roleA, roleC)); - ASSERT_OK(graph.addRoleToRole(roleB, roleC)); - ASSERT_OK(graph.addRoleToRole(roleB, roleD)); - // Adding the same role twice should be a no-op, duplicate roles should be de-duped. - ASSERT_OK(graph.addRoleToRole(roleB, roleD)); - - /* - * Graph now looks like: - * A - * / \ - * v v - * B -> C - * | - * v - * D - */ - - - it = graph.getDirectSubordinates(roleA); // should be roleB and roleC, order doesn't matter + } else { + FAIL(mongoutils::str::stream() << "unexpected role returned: " << cur.getFullName()); + } + ASSERT_FALSE(it.more()); + + ASSERT_OK(graph.recomputePrivilegeData()); + 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) { - ASSERT_EQUALS(it.next().getFullName(), roleC.getFullName()); + hasB = true; } 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()); - - ASSERT_OK(graph.recomputePrivilegeData()); - 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) { - ASSERT_EQUALS(it.next().getFullName(), roleD.getFullName()); + hasC = true; } 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()); + hasD = true; } else { FAIL(mongoutils::str::stream() << "unexpected role returned: " << cur.getFullName()); } - ASSERT_FALSE(it.more()); - - // Now remove roleD from roleB and make sure graph is update correctly - ASSERT_OK(graph.removeRoleFromRole(roleB, roleD)); - - /* - * Graph now looks like: - * A - * / \ - * v v - * B -> C - */ - it = graph.getDirectSubordinates(roleB); // should be just roleC - ASSERT_EQUALS(it.next().getFullName(), roleC.getFullName()); - ASSERT_FALSE(it.more()); - - it = graph.getDirectSubordinates(roleD); // should be empty - ASSERT_FALSE(it.more()); - - - // 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()); } - - const ResourcePattern collectionAFooResource(ResourcePattern::forExactNamespace( - NamespaceString("dbA.foo"))); - const ResourcePattern db1Resource(ResourcePattern::forDatabaseName("db1")); - const ResourcePattern db2Resource(ResourcePattern::forDatabaseName("db2")); - const ResourcePattern dbAResource(ResourcePattern::forDatabaseName("dbA")); - const ResourcePattern dbBResource(ResourcePattern::forDatabaseName("dbB")); - const ResourcePattern dbCResource(ResourcePattern::forDatabaseName("dbC")); - const ResourcePattern dbDResource(ResourcePattern::forDatabaseName("dbD")); - const ResourcePattern dbResource(ResourcePattern::forDatabaseName("db")); - - // Tests that adding multiple privileges on the same resource correctly collapses those to one - // privilege - TEST(RoleGraphTest, AddPrivileges) { - RoleName roleA("roleA", "dbA"); - - RoleGraph graph; - ASSERT_OK(graph.createRole(roleA)); - - // Test adding a single privilege - ActionSet actions; - actions.addAction(ActionType::find); - ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(dbAResource, actions))); - - PrivilegeVector privileges = graph.getDirectPrivileges(roleA); - ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); - ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); - ASSERT_EQUALS(actions.toString(), privileges[0].getActions().toString()); - - // Add a privilege on a different resource - ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(collectionAFooResource, actions))); - privileges = graph.getDirectPrivileges(roleA); - ASSERT_EQUALS(static_cast<size_t>(2), privileges.size()); - ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); - ASSERT_EQUALS(actions.toString(), privileges[0].getActions().toString()); - ASSERT_EQUALS(collectionAFooResource, privileges[1].getResourcePattern()); - ASSERT_EQUALS(actions.toString(), privileges[1].getActions().toString()); - - - // Add different privileges on an existing resource and make sure they get de-duped - actions.removeAllActions(); - actions.addAction(ActionType::insert); - - PrivilegeVector privilegesToAdd; - privilegesToAdd.push_back(Privilege(dbAResource, actions)); - - actions.removeAllActions(); - actions.addAction(ActionType::update); - privilegesToAdd.push_back(Privilege(dbAResource, actions)); - - ASSERT_OK(graph.addPrivilegesToRole(roleA, privilegesToAdd)); - - privileges = graph.getDirectPrivileges(roleA); - ASSERT_EQUALS(static_cast<size_t>(2), privileges.size()); - ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); - ASSERT_NOT_EQUALS(actions.toString(), privileges[0].getActions().toString()); - actions.addAction(ActionType::find); - actions.addAction(ActionType::insert); - ASSERT_EQUALS(actions.toString(), privileges[0].getActions().toString()); - actions.removeAction(ActionType::insert); - actions.removeAction(ActionType::update); - ASSERT_EQUALS(collectionAFooResource, privileges[1].getResourcePattern()); - ASSERT_EQUALS(actions.toString(), privileges[1].getActions().toString()); - } - - // Tests that recomputePrivilegeData correctly detects cycles in the graph. - TEST(RoleGraphTest, DetectCycles) { - RoleName roleA("roleA", "dbA"); - RoleName roleB("roleB", "dbB"); - RoleName roleC("roleC", "dbC"); - RoleName roleD("roleD", "dbD"); - - RoleGraph graph; - ASSERT_OK(graph.createRole(roleA)); - ASSERT_OK(graph.createRole(roleB)); - ASSERT_OK(graph.createRole(roleC)); - ASSERT_OK(graph.createRole(roleD)); - - // Add a role to itself - ASSERT_OK(graph.recomputePrivilegeData()); - ASSERT_OK(graph.addRoleToRole(roleA, roleA)); - ASSERT_NOT_OK(graph.recomputePrivilegeData()); - ASSERT_OK(graph.removeRoleFromRole(roleA, roleA)); - ASSERT_OK(graph.recomputePrivilegeData()); - - ASSERT_OK(graph.addRoleToRole(roleA, roleB)); - ASSERT_OK(graph.recomputePrivilegeData()); - ASSERT_OK(graph.addRoleToRole(roleA, roleC)); - ASSERT_OK(graph.addRoleToRole(roleB, roleC)); - ASSERT_OK(graph.recomputePrivilegeData()); - /* - * Graph now looks like: - * A - * / \ - * v v - * B -> C - */ - ASSERT_OK(graph.addRoleToRole(roleC, roleD)); - ASSERT_OK(graph.addRoleToRole(roleD, roleB)); // Add a cycle - /* - * Graph now looks like: - * A - * / \ - * v v - * B -> C - * ^ / - * \ v - * D - */ - ASSERT_NOT_OK(graph.recomputePrivilegeData()); - ASSERT_OK(graph.removeRoleFromRole(roleD, roleB)); - ASSERT_OK(graph.recomputePrivilegeData()); - } - - // Tests that recomputePrivilegeData correctly updates transitive privilege data for all roles. - TEST(RoleGraphTest, RecomputePrivilegeData) { - // We create 4 roles and give each of them a unique privilege. After that the direct - // privileges for all the roles are not touched. The only thing that is changed is the - // role membership graph, and we test how that affects the set of all transitive privileges - // for each role. - RoleName roleA("roleA", "dbA"); - RoleName roleB("roleB", "dbB"); - RoleName roleC("roleC", "dbC"); - RoleName roleD("readWrite", "dbD"); // built-in role - - ActionSet actions; - actions.addAllActions(); - - RoleGraph graph; - ASSERT_OK(graph.createRole(roleA)); - ASSERT_OK(graph.createRole(roleB)); - ASSERT_OK(graph.createRole(roleC)); - - ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(dbAResource, actions))); - ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbBResource, actions))); - ASSERT_OK(graph.addPrivilegeToRole(roleC, Privilege(dbCResource, actions))); - - ASSERT_OK(graph.recomputePrivilegeData()); - - PrivilegeVector privileges = graph.getAllPrivileges(roleA); - ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); - ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); - - // At this point we have all 4 roles set up, each with their own privilege, but no - // roles have been granted to each other. - - ASSERT_OK(graph.addRoleToRole(roleA, roleB)); - // Role graph: A->B - ASSERT_OK(graph.recomputePrivilegeData()); - privileges = graph.getAllPrivileges(roleA); - ASSERT_EQUALS(static_cast<size_t>(2), privileges.size()); - ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); - ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern()); - - // Add's roleC's privileges to roleB and make sure roleA gets them as well. - ASSERT_OK(graph.addRoleToRole(roleB, roleC)); - // Role graph: A->B->C - ASSERT_OK(graph.recomputePrivilegeData()); - privileges = graph.getAllPrivileges(roleA); - ASSERT_EQUALS(static_cast<size_t>(3), privileges.size()); - ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); - ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern()); - ASSERT_EQUALS(dbCResource, privileges[2].getResourcePattern()); - privileges = graph.getAllPrivileges(roleB); - ASSERT_EQUALS(static_cast<size_t>(2), privileges.size()); - ASSERT_EQUALS(dbBResource, privileges[0].getResourcePattern()); - ASSERT_EQUALS(dbCResource, privileges[1].getResourcePattern()); - - // Add's roleD's privileges to roleC and make sure that roleA and roleB get them as well. - ASSERT_OK(graph.addRoleToRole(roleC, roleD)); - // Role graph: A->B->C->D - ASSERT_OK(graph.recomputePrivilegeData()); - privileges = graph.getAllPrivileges(roleA); - const size_t readWriteRolePrivilegeCount = graph.getAllPrivileges(roleD).size(); - ASSERT_EQUALS(readWriteRolePrivilegeCount + 3, privileges.size()); - ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); - ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern()); - ASSERT_EQUALS(dbCResource, privileges[2].getResourcePattern()); - privileges = graph.getAllPrivileges(roleB); - ASSERT_EQUALS(readWriteRolePrivilegeCount + 2, privileges.size()); - ASSERT_EQUALS(dbBResource, privileges[0].getResourcePattern()); - ASSERT_EQUALS(dbCResource, privileges[1].getResourcePattern()); - privileges = graph.getAllPrivileges(roleC); - ASSERT_EQUALS(readWriteRolePrivilegeCount + 1, privileges.size()); - ASSERT_EQUALS(dbCResource, privileges[0].getResourcePattern()); - - // Remove roleC from roleB, make sure that roleA then loses both roleC's and roleD's - // privileges - ASSERT_OK(graph.removeRoleFromRole(roleB, roleC)); - // Role graph: A->B C->D - ASSERT_OK(graph.recomputePrivilegeData()); - privileges = graph.getAllPrivileges(roleA); - ASSERT_EQUALS(static_cast<size_t>(2), privileges.size()); - ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); - ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern()); - privileges = graph.getAllPrivileges(roleB); - ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); - ASSERT_EQUALS(dbBResource, privileges[0].getResourcePattern()); - privileges = graph.getAllPrivileges(roleC); - ASSERT_EQUALS(readWriteRolePrivilegeCount + 1, privileges.size()); - ASSERT_EQUALS(dbCResource, privileges[0].getResourcePattern()); - privileges = graph.getAllPrivileges(roleD); - ASSERT_EQUALS(readWriteRolePrivilegeCount, privileges.size()); - - // Make sure direct privileges were untouched - privileges = graph.getDirectPrivileges(roleA); - ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); - ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); - privileges = graph.getDirectPrivileges(roleB); - ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); - ASSERT_EQUALS(dbBResource, privileges[0].getResourcePattern()); - privileges = graph.getDirectPrivileges(roleC); - ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); - ASSERT_EQUALS(dbCResource, privileges[0].getResourcePattern()); - privileges = graph.getDirectPrivileges(roleD); - ASSERT_EQUALS(readWriteRolePrivilegeCount, privileges.size()); - } - - // Test that if you grant 1 role to another, then remove it and change it's privileges, then - // re-grant it, the receiving role sees the new privileges and not the old ones. - TEST(RoleGraphTest, ReAddRole) { - RoleName roleA("roleA", "dbA"); - RoleName roleB("roleB", "dbB"); - RoleName roleC("roleC", "dbC"); - - ActionSet actionsA, actionsB, actionsC; - actionsA.addAction(ActionType::find); - actionsB.addAction(ActionType::insert); - actionsC.addAction(ActionType::update); - - RoleGraph graph; - ASSERT_OK(graph.createRole(roleA)); - ASSERT_OK(graph.createRole(roleB)); - ASSERT_OK(graph.createRole(roleC)); - - ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(dbResource, actionsA))); - ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbResource, actionsB))); - ASSERT_OK(graph.addPrivilegeToRole(roleC, Privilege(dbResource, actionsC))); - - ASSERT_OK(graph.addRoleToRole(roleA, roleB)); - ASSERT_OK(graph.addRoleToRole(roleB, roleC)); // graph: A <- B <- C - - ASSERT_OK(graph.recomputePrivilegeData()); - - // roleA should have privileges from roleB and roleC - PrivilegeVector privileges = graph.getAllPrivileges(roleA); - ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); - ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find)); - ASSERT_TRUE(privileges[0].getActions().contains(ActionType::insert)); - ASSERT_TRUE(privileges[0].getActions().contains(ActionType::update)); - - // Now remove roleB from roleA. B still is a member of C, but A no longer should have - // privileges from B or C. - ASSERT_OK(graph.removeRoleFromRole(roleA, roleB)); - ASSERT_OK(graph.recomputePrivilegeData()); - - // roleA should no longer have the privileges from roleB or roleC - privileges = graph.getAllPrivileges(roleA); - ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); - ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find)); - ASSERT_FALSE(privileges[0].getActions().contains(ActionType::insert)); - ASSERT_FALSE(privileges[0].getActions().contains(ActionType::update)); - - // Change the privileges that roleB grants - ASSERT_OK(graph.removeAllPrivilegesFromRole(roleB)); - ActionSet newActionsB; - newActionsB.addAction(ActionType::remove); - ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbResource, newActionsB))); - - // Grant roleB back to roleA, make sure roleA has roleB's new privilege but not its old one. - ASSERT_OK(graph.addRoleToRole(roleA, roleB)); - ASSERT_OK(graph.recomputePrivilegeData()); - - privileges = graph.getAllPrivileges(roleA); - ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); - ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find)); - ASSERT_TRUE(privileges[0].getActions().contains(ActionType::update)); // should get roleC's actions again - ASSERT_TRUE(privileges[0].getActions().contains(ActionType::remove)); // roleB should grant this to roleA - ASSERT_FALSE(privileges[0].getActions().contains(ActionType::insert)); // no roles have this action anymore - - // Now delete roleB completely. A should once again lose the privileges from both B and C. - ASSERT_OK(graph.deleteRole(roleB)); - ASSERT_OK(graph.recomputePrivilegeData()); - - // roleA should no longer have the privileges from roleB or roleC - privileges = graph.getAllPrivileges(roleA); - ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); - ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find)); - ASSERT_FALSE(privileges[0].getActions().contains(ActionType::update)); - ASSERT_FALSE(privileges[0].getActions().contains(ActionType::remove)); - ASSERT_FALSE(privileges[0].getActions().contains(ActionType::insert)); - - // Now re-create roleB and give it a new privilege, then grant it back to roleA. - // RoleA should get its new privilege but not roleC's privilege this time nor either of - // roleB's old privileges. - ASSERT_OK(graph.createRole(roleB)); - actionsB.removeAllActions(); - actionsB.addAction(ActionType::shutdown); - ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbResource, actionsB))); - ASSERT_OK(graph.addRoleToRole(roleA, roleB)); - ASSERT_OK(graph.recomputePrivilegeData()); - - privileges = graph.getAllPrivileges(roleA); - ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); - ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find)); - ASSERT_TRUE(privileges[0].getActions().contains(ActionType::shutdown)); - ASSERT_FALSE(privileges[0].getActions().contains(ActionType::update)); - ASSERT_FALSE(privileges[0].getActions().contains(ActionType::remove)); - ASSERT_FALSE(privileges[0].getActions().contains(ActionType::insert)); - } - - // Tests copy constructor and swap functionality. - TEST(RoleGraphTest, CopySwap) { - RoleName roleA("roleA", "dbA"); - RoleName roleB("roleB", "dbB"); - RoleName roleC("roleC", "dbC"); - - RoleGraph graph; - ASSERT_OK(graph.createRole(roleA)); - ASSERT_OK(graph.createRole(roleB)); - ASSERT_OK(graph.createRole(roleC)); - - ActionSet actions; - actions.addAction(ActionType::find); - ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(dbAResource, actions))); - ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbBResource, actions))); - ASSERT_OK(graph.addPrivilegeToRole(roleC, Privilege(dbCResource, actions))); - - ASSERT_OK(graph.addRoleToRole(roleA, roleB)); - - // Make a copy of the graph to do further modifications on. - RoleGraph tempGraph(graph); - ASSERT_OK(tempGraph.addRoleToRole(roleB, roleC)); - tempGraph.recomputePrivilegeData(); - - // Now swap the copy back with the original graph and make sure the original was updated - // properly. - swap(tempGraph, graph); - - RoleNameIterator it = graph.getDirectSubordinates(roleB); - ASSERT_TRUE(it.more()); + 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) { + ASSERT_EQUALS(it.next().getFullName(), roleD.getFullName()); + } else if (cur == roleD) { ASSERT_EQUALS(it.next().getFullName(), roleC.getFullName()); - ASSERT_FALSE(it.more()); - - graph.getAllPrivileges(roleA); // should have privileges from roleB *and* role C - PrivilegeVector privileges = graph.getAllPrivileges(roleA); - ASSERT_EQUALS(static_cast<size_t>(3), privileges.size()); - ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); - ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern()); - ASSERT_EQUALS(dbCResource, privileges[2].getResourcePattern()); + } else { + FAIL(mongoutils::str::stream() << "unexpected role returned: " << cur.getFullName()); } + ASSERT_FALSE(it.more()); - // Tests error handling - TEST(RoleGraphTest, ErrorHandling) { - RoleName roleA("roleA", "dbA"); - RoleName roleB("roleB", "dbB"); - RoleName roleC("roleC", "dbC"); - - ActionSet actions; - actions.addAction(ActionType::find); - Privilege privilege1(db1Resource, actions); - Privilege privilege2(db2Resource, actions); - PrivilegeVector privileges; - privileges.push_back(privilege1); - privileges.push_back(privilege2); - - RoleGraph graph; - // None of the roles exist yet. - ASSERT_NOT_OK(graph.addPrivilegeToRole(roleA, privilege1)); - ASSERT_NOT_OK(graph.addPrivilegesToRole(roleA, privileges)); - ASSERT_NOT_OK(graph.removePrivilegeFromRole(roleA, privilege1)); - ASSERT_NOT_OK(graph.removePrivilegesFromRole(roleA, privileges)); - ASSERT_NOT_OK(graph.removeAllPrivilegesFromRole(roleA)); - ASSERT_NOT_OK(graph.addRoleToRole(roleA, roleB)); - ASSERT_NOT_OK(graph.removeRoleFromRole(roleA, roleB)); - - // One of the roles exists - ASSERT_OK(graph.createRole(roleA)); - ASSERT_NOT_OK(graph.createRole(roleA)); // Can't create same role twice - ASSERT_NOT_OK(graph.addRoleToRole(roleA, roleB)); - ASSERT_NOT_OK(graph.addRoleToRole(roleB, roleA)); - ASSERT_NOT_OK(graph.removeRoleFromRole(roleA, roleB)); - ASSERT_NOT_OK(graph.removeRoleFromRole(roleB, roleA)); - - // Should work now that both exist. - ASSERT_OK(graph.createRole(roleB)); - ASSERT_OK(graph.addRoleToRole(roleA, roleB)); - ASSERT_OK(graph.removeRoleFromRole(roleA, roleB)); - ASSERT_NOT_OK(graph.removeRoleFromRole(roleA, roleB)); // roleA isn't actually a member of roleB - - // Can't remove a privilege from a role that doesn't have it. - ASSERT_NOT_OK(graph.removePrivilegeFromRole(roleA, privilege1)); - ASSERT_OK(graph.addPrivilegeToRole(roleA, privilege1)); - ASSERT_OK(graph.removePrivilegeFromRole(roleA, privilege1)); // now should work - - // Test that removing a vector of privileges fails if *any* of the privileges are missing. - ASSERT_OK(graph.addPrivilegeToRole(roleA, privilege1)); - ASSERT_OK(graph.addPrivilegeToRole(roleA, privilege2)); - // Removing both privileges should work since it has both - ASSERT_OK(graph.removePrivilegesFromRole(roleA, privileges)); - // Now add only 1 back and this time removing both should fail - ASSERT_OK(graph.addPrivilegeToRole(roleA, privilege1)); - ASSERT_NOT_OK(graph.removePrivilegesFromRole(roleA, privileges)); - } + it = graph.getDirectSubordinates(roleC); + ASSERT_FALSE(it.more()); + it = graph.getDirectMembers(roleA); + ASSERT_FALSE(it.more()); - TEST(RoleGraphTest, BuiltinRoles) { - RoleName userRole("userDefined", "dbA"); - RoleName builtinRole("read", "dbA"); - - ActionSet actions; - actions.addAction(ActionType::insert); - Privilege privilege(dbAResource, actions); - - RoleGraph graph; - - ASSERT(graph.roleExists(builtinRole)); - ASSERT_NOT_OK(graph.createRole(builtinRole)); - ASSERT_NOT_OK(graph.deleteRole(builtinRole)); - ASSERT(graph.roleExists(builtinRole)); - ASSERT(!graph.roleExists(userRole)); - ASSERT_OK(graph.createRole(userRole)); - ASSERT(graph.roleExists(userRole)); - - ASSERT_NOT_OK(graph.addPrivilegeToRole(builtinRole, privilege)); - ASSERT_NOT_OK(graph.removePrivilegeFromRole(builtinRole, privilege)); - ASSERT_NOT_OK(graph.addRoleToRole(builtinRole, userRole)); - ASSERT_NOT_OK(graph.removeRoleFromRole(builtinRole, userRole)); - - ASSERT_OK(graph.addPrivilegeToRole(userRole, privilege)); - ASSERT_OK(graph.addRoleToRole(userRole, builtinRole)); - ASSERT_OK(graph.recomputePrivilegeData()); - - PrivilegeVector privileges = graph.getDirectPrivileges(userRole); - ASSERT_EQUALS(1U, privileges.size()); - ASSERT(privileges[0].getActions().equals(actions)); - ASSERT(!privileges[0].getActions().contains(ActionType::find)); - ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); - - privileges = graph.getAllPrivileges(userRole); - size_t i; - for (i = 0; i < privileges.size(); ++i) { - if (dbAResource == privileges[i].getResourcePattern()) - break; - } - ASSERT_NOT_EQUALS(privileges.size(), i); - ASSERT(privileges[i].getActions().isSupersetOf(actions)); - ASSERT(privileges[i].getActions().contains(ActionType::insert)); - ASSERT(privileges[i].getActions().contains(ActionType::find)); + it = graph.getDirectMembers(roleB); + ASSERT_EQUALS(it.next().getFullName(), roleA.getFullName()); + ASSERT_FALSE(it.more()); - ASSERT_OK(graph.deleteRole(userRole)); - ASSERT(!graph.roleExists(userRole)); - } - - TEST(RoleGraphTest, BuiltinRolesOnlyOnAppropriateDatabases) { - RoleGraph graph; - ASSERT(graph.roleExists(RoleName("read", "test"))); - ASSERT(graph.roleExists(RoleName("readWrite", "test"))); - ASSERT(graph.roleExists(RoleName("userAdmin", "test"))); - ASSERT(graph.roleExists(RoleName("dbAdmin", "test"))); - ASSERT(graph.roleExists(RoleName("dbOwner", "test"))); - ASSERT(!graph.roleExists(RoleName("readAnyDatabase", "test"))); - ASSERT(!graph.roleExists(RoleName("readWriteAnyDatabase", "test"))); - ASSERT(!graph.roleExists(RoleName("userAdminAnyDatabase", "test"))); - ASSERT(!graph.roleExists(RoleName("dbAdminAnyDatabase", "test"))); - ASSERT(!graph.roleExists(RoleName("clusterAdmin", "test"))); - ASSERT(!graph.roleExists(RoleName("root", "test"))); - ASSERT(!graph.roleExists(RoleName("__system", "test"))); - ASSERT(!graph.roleExists(RoleName("MyRole", "test"))); - - ASSERT(graph.roleExists(RoleName("read", "admin"))); - ASSERT(graph.roleExists(RoleName("readWrite", "admin"))); - ASSERT(graph.roleExists(RoleName("userAdmin", "admin"))); - ASSERT(graph.roleExists(RoleName("dbAdmin", "admin"))); - ASSERT(graph.roleExists(RoleName("dbOwner", "admin"))); - ASSERT(graph.roleExists(RoleName("readAnyDatabase", "admin"))); - ASSERT(graph.roleExists(RoleName("readWriteAnyDatabase", "admin"))); - ASSERT(graph.roleExists(RoleName("userAdminAnyDatabase", "admin"))); - ASSERT(graph.roleExists(RoleName("dbAdminAnyDatabase", "admin"))); - ASSERT(graph.roleExists(RoleName("clusterAdmin", "admin"))); - ASSERT(graph.roleExists(RoleName("root", "admin"))); - ASSERT(graph.roleExists(RoleName("__system", "admin"))); - ASSERT(!graph.roleExists(RoleName("MyRole", "admin"))); + 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()); } - - 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()); + ASSERT_FALSE(it.more()); + + // Now remove roleD from roleB and make sure graph is update correctly + ASSERT_OK(graph.removeRoleFromRole(roleB, roleD)); + + /* + * Graph now looks like: + * A + * / \ + * v v + * B -> C + */ + it = graph.getDirectSubordinates(roleB); // should be just roleC + ASSERT_EQUALS(it.next().getFullName(), roleC.getFullName()); + ASSERT_FALSE(it.more()); + + it = graph.getDirectSubordinates(roleD); // should be empty + ASSERT_FALSE(it.more()); + + + // 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()); +} + +const ResourcePattern collectionAFooResource( + ResourcePattern::forExactNamespace(NamespaceString("dbA.foo"))); +const ResourcePattern db1Resource(ResourcePattern::forDatabaseName("db1")); +const ResourcePattern db2Resource(ResourcePattern::forDatabaseName("db2")); +const ResourcePattern dbAResource(ResourcePattern::forDatabaseName("dbA")); +const ResourcePattern dbBResource(ResourcePattern::forDatabaseName("dbB")); +const ResourcePattern dbCResource(ResourcePattern::forDatabaseName("dbC")); +const ResourcePattern dbDResource(ResourcePattern::forDatabaseName("dbD")); +const ResourcePattern dbResource(ResourcePattern::forDatabaseName("db")); + +// Tests that adding multiple privileges on the same resource correctly collapses those to one +// privilege +TEST(RoleGraphTest, AddPrivileges) { + RoleName roleA("roleA", "dbA"); + + RoleGraph graph; + ASSERT_OK(graph.createRole(roleA)); + + // Test adding a single privilege + ActionSet actions; + actions.addAction(ActionType::find); + ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(dbAResource, actions))); + + PrivilegeVector privileges = graph.getDirectPrivileges(roleA); + ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); + ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); + ASSERT_EQUALS(actions.toString(), privileges[0].getActions().toString()); + + // Add a privilege on a different resource + ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(collectionAFooResource, actions))); + privileges = graph.getDirectPrivileges(roleA); + ASSERT_EQUALS(static_cast<size_t>(2), privileges.size()); + ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); + ASSERT_EQUALS(actions.toString(), privileges[0].getActions().toString()); + ASSERT_EQUALS(collectionAFooResource, privileges[1].getResourcePattern()); + ASSERT_EQUALS(actions.toString(), privileges[1].getActions().toString()); + + + // Add different privileges on an existing resource and make sure they get de-duped + actions.removeAllActions(); + actions.addAction(ActionType::insert); + + PrivilegeVector privilegesToAdd; + privilegesToAdd.push_back(Privilege(dbAResource, actions)); + + actions.removeAllActions(); + actions.addAction(ActionType::update); + privilegesToAdd.push_back(Privilege(dbAResource, actions)); + + ASSERT_OK(graph.addPrivilegesToRole(roleA, privilegesToAdd)); + + privileges = graph.getDirectPrivileges(roleA); + ASSERT_EQUALS(static_cast<size_t>(2), privileges.size()); + ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); + ASSERT_NOT_EQUALS(actions.toString(), privileges[0].getActions().toString()); + actions.addAction(ActionType::find); + actions.addAction(ActionType::insert); + ASSERT_EQUALS(actions.toString(), privileges[0].getActions().toString()); + actions.removeAction(ActionType::insert); + actions.removeAction(ActionType::update); + ASSERT_EQUALS(collectionAFooResource, privileges[1].getResourcePattern()); + ASSERT_EQUALS(actions.toString(), privileges[1].getActions().toString()); +} + +// Tests that recomputePrivilegeData correctly detects cycles in the graph. +TEST(RoleGraphTest, DetectCycles) { + RoleName roleA("roleA", "dbA"); + RoleName roleB("roleB", "dbB"); + RoleName roleC("roleC", "dbC"); + RoleName roleD("roleD", "dbD"); + + RoleGraph graph; + ASSERT_OK(graph.createRole(roleA)); + ASSERT_OK(graph.createRole(roleB)); + ASSERT_OK(graph.createRole(roleC)); + ASSERT_OK(graph.createRole(roleD)); + + // Add a role to itself + ASSERT_OK(graph.recomputePrivilegeData()); + ASSERT_OK(graph.addRoleToRole(roleA, roleA)); + ASSERT_NOT_OK(graph.recomputePrivilegeData()); + ASSERT_OK(graph.removeRoleFromRole(roleA, roleA)); + ASSERT_OK(graph.recomputePrivilegeData()); + + ASSERT_OK(graph.addRoleToRole(roleA, roleB)); + ASSERT_OK(graph.recomputePrivilegeData()); + ASSERT_OK(graph.addRoleToRole(roleA, roleC)); + ASSERT_OK(graph.addRoleToRole(roleB, roleC)); + ASSERT_OK(graph.recomputePrivilegeData()); + /* + * Graph now looks like: + * A + * / \ + * v v + * B -> C + */ + ASSERT_OK(graph.addRoleToRole(roleC, roleD)); + ASSERT_OK(graph.addRoleToRole(roleD, roleB)); // Add a cycle + /* + * Graph now looks like: + * A + * / \ + * v v + * B -> C + * ^ / + * \ v + * D + */ + ASSERT_NOT_OK(graph.recomputePrivilegeData()); + ASSERT_OK(graph.removeRoleFromRole(roleD, roleB)); + ASSERT_OK(graph.recomputePrivilegeData()); +} + +// Tests that recomputePrivilegeData correctly updates transitive privilege data for all roles. +TEST(RoleGraphTest, RecomputePrivilegeData) { + // We create 4 roles and give each of them a unique privilege. After that the direct + // privileges for all the roles are not touched. The only thing that is changed is the + // role membership graph, and we test how that affects the set of all transitive privileges + // for each role. + RoleName roleA("roleA", "dbA"); + RoleName roleB("roleB", "dbB"); + RoleName roleC("roleC", "dbC"); + RoleName roleD("readWrite", "dbD"); // built-in role + + ActionSet actions; + actions.addAllActions(); + + RoleGraph graph; + ASSERT_OK(graph.createRole(roleA)); + ASSERT_OK(graph.createRole(roleB)); + ASSERT_OK(graph.createRole(roleC)); + + ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(dbAResource, actions))); + ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbBResource, actions))); + ASSERT_OK(graph.addPrivilegeToRole(roleC, Privilege(dbCResource, actions))); + + ASSERT_OK(graph.recomputePrivilegeData()); + + PrivilegeVector privileges = graph.getAllPrivileges(roleA); + ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); + ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); + + // At this point we have all 4 roles set up, each with their own privilege, but no + // roles have been granted to each other. + + ASSERT_OK(graph.addRoleToRole(roleA, roleB)); + // Role graph: A->B + ASSERT_OK(graph.recomputePrivilegeData()); + privileges = graph.getAllPrivileges(roleA); + ASSERT_EQUALS(static_cast<size_t>(2), privileges.size()); + ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); + ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern()); + + // Add's roleC's privileges to roleB and make sure roleA gets them as well. + ASSERT_OK(graph.addRoleToRole(roleB, roleC)); + // Role graph: A->B->C + ASSERT_OK(graph.recomputePrivilegeData()); + privileges = graph.getAllPrivileges(roleA); + ASSERT_EQUALS(static_cast<size_t>(3), privileges.size()); + ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); + ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern()); + ASSERT_EQUALS(dbCResource, privileges[2].getResourcePattern()); + privileges = graph.getAllPrivileges(roleB); + ASSERT_EQUALS(static_cast<size_t>(2), privileges.size()); + ASSERT_EQUALS(dbBResource, privileges[0].getResourcePattern()); + ASSERT_EQUALS(dbCResource, privileges[1].getResourcePattern()); + + // Add's roleD's privileges to roleC and make sure that roleA and roleB get them as well. + ASSERT_OK(graph.addRoleToRole(roleC, roleD)); + // Role graph: A->B->C->D + ASSERT_OK(graph.recomputePrivilegeData()); + privileges = graph.getAllPrivileges(roleA); + const size_t readWriteRolePrivilegeCount = graph.getAllPrivileges(roleD).size(); + ASSERT_EQUALS(readWriteRolePrivilegeCount + 3, privileges.size()); + ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); + ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern()); + ASSERT_EQUALS(dbCResource, privileges[2].getResourcePattern()); + privileges = graph.getAllPrivileges(roleB); + ASSERT_EQUALS(readWriteRolePrivilegeCount + 2, privileges.size()); + ASSERT_EQUALS(dbBResource, privileges[0].getResourcePattern()); + ASSERT_EQUALS(dbCResource, privileges[1].getResourcePattern()); + privileges = graph.getAllPrivileges(roleC); + ASSERT_EQUALS(readWriteRolePrivilegeCount + 1, privileges.size()); + ASSERT_EQUALS(dbCResource, privileges[0].getResourcePattern()); + + // Remove roleC from roleB, make sure that roleA then loses both roleC's and roleD's + // privileges + ASSERT_OK(graph.removeRoleFromRole(roleB, roleC)); + // Role graph: A->B C->D + ASSERT_OK(graph.recomputePrivilegeData()); + privileges = graph.getAllPrivileges(roleA); + ASSERT_EQUALS(static_cast<size_t>(2), privileges.size()); + ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); + ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern()); + privileges = graph.getAllPrivileges(roleB); + ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); + ASSERT_EQUALS(dbBResource, privileges[0].getResourcePattern()); + privileges = graph.getAllPrivileges(roleC); + ASSERT_EQUALS(readWriteRolePrivilegeCount + 1, privileges.size()); + ASSERT_EQUALS(dbCResource, privileges[0].getResourcePattern()); + privileges = graph.getAllPrivileges(roleD); + ASSERT_EQUALS(readWriteRolePrivilegeCount, privileges.size()); + + // Make sure direct privileges were untouched + privileges = graph.getDirectPrivileges(roleA); + ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); + ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); + privileges = graph.getDirectPrivileges(roleB); + ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); + ASSERT_EQUALS(dbBResource, privileges[0].getResourcePattern()); + privileges = graph.getDirectPrivileges(roleC); + ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); + ASSERT_EQUALS(dbCResource, privileges[0].getResourcePattern()); + privileges = graph.getDirectPrivileges(roleD); + ASSERT_EQUALS(readWriteRolePrivilegeCount, privileges.size()); +} + +// Test that if you grant 1 role to another, then remove it and change it's privileges, then +// re-grant it, the receiving role sees the new privileges and not the old ones. +TEST(RoleGraphTest, ReAddRole) { + RoleName roleA("roleA", "dbA"); + RoleName roleB("roleB", "dbB"); + RoleName roleC("roleC", "dbC"); + + ActionSet actionsA, actionsB, actionsC; + actionsA.addAction(ActionType::find); + actionsB.addAction(ActionType::insert); + actionsC.addAction(ActionType::update); + + RoleGraph graph; + ASSERT_OK(graph.createRole(roleA)); + ASSERT_OK(graph.createRole(roleB)); + ASSERT_OK(graph.createRole(roleC)); + + ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(dbResource, actionsA))); + ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbResource, actionsB))); + ASSERT_OK(graph.addPrivilegeToRole(roleC, Privilege(dbResource, actionsC))); + + ASSERT_OK(graph.addRoleToRole(roleA, roleB)); + ASSERT_OK(graph.addRoleToRole(roleB, roleC)); // graph: A <- B <- C + + ASSERT_OK(graph.recomputePrivilegeData()); + + // roleA should have privileges from roleB and roleC + PrivilegeVector privileges = graph.getAllPrivileges(roleA); + ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); + ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find)); + ASSERT_TRUE(privileges[0].getActions().contains(ActionType::insert)); + ASSERT_TRUE(privileges[0].getActions().contains(ActionType::update)); + + // Now remove roleB from roleA. B still is a member of C, but A no longer should have + // privileges from B or C. + ASSERT_OK(graph.removeRoleFromRole(roleA, roleB)); + ASSERT_OK(graph.recomputePrivilegeData()); + + // roleA should no longer have the privileges from roleB or roleC + privileges = graph.getAllPrivileges(roleA); + ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); + ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find)); + ASSERT_FALSE(privileges[0].getActions().contains(ActionType::insert)); + ASSERT_FALSE(privileges[0].getActions().contains(ActionType::update)); + + // Change the privileges that roleB grants + ASSERT_OK(graph.removeAllPrivilegesFromRole(roleB)); + ActionSet newActionsB; + newActionsB.addAction(ActionType::remove); + ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbResource, newActionsB))); + + // Grant roleB back to roleA, make sure roleA has roleB's new privilege but not its old one. + ASSERT_OK(graph.addRoleToRole(roleA, roleB)); + ASSERT_OK(graph.recomputePrivilegeData()); + + privileges = graph.getAllPrivileges(roleA); + ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); + ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find)); + ASSERT_TRUE(privileges[0].getActions().contains( + ActionType::update)); // should get roleC's actions again + ASSERT_TRUE(privileges[0].getActions().contains( + ActionType::remove)); // roleB should grant this to roleA + ASSERT_FALSE(privileges[0].getActions().contains( + ActionType::insert)); // no roles have this action anymore + + // Now delete roleB completely. A should once again lose the privileges from both B and C. + ASSERT_OK(graph.deleteRole(roleB)); + ASSERT_OK(graph.recomputePrivilegeData()); + + // roleA should no longer have the privileges from roleB or roleC + privileges = graph.getAllPrivileges(roleA); + ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); + ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find)); + ASSERT_FALSE(privileges[0].getActions().contains(ActionType::update)); + ASSERT_FALSE(privileges[0].getActions().contains(ActionType::remove)); + ASSERT_FALSE(privileges[0].getActions().contains(ActionType::insert)); + + // Now re-create roleB and give it a new privilege, then grant it back to roleA. + // RoleA should get its new privilege but not roleC's privilege this time nor either of + // roleB's old privileges. + ASSERT_OK(graph.createRole(roleB)); + actionsB.removeAllActions(); + actionsB.addAction(ActionType::shutdown); + ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbResource, actionsB))); + ASSERT_OK(graph.addRoleToRole(roleA, roleB)); + ASSERT_OK(graph.recomputePrivilegeData()); + + privileges = graph.getAllPrivileges(roleA); + ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); + ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find)); + ASSERT_TRUE(privileges[0].getActions().contains(ActionType::shutdown)); + ASSERT_FALSE(privileges[0].getActions().contains(ActionType::update)); + ASSERT_FALSE(privileges[0].getActions().contains(ActionType::remove)); + ASSERT_FALSE(privileges[0].getActions().contains(ActionType::insert)); +} + +// Tests copy constructor and swap functionality. +TEST(RoleGraphTest, CopySwap) { + RoleName roleA("roleA", "dbA"); + RoleName roleB("roleB", "dbB"); + RoleName roleC("roleC", "dbC"); + + RoleGraph graph; + ASSERT_OK(graph.createRole(roleA)); + ASSERT_OK(graph.createRole(roleB)); + ASSERT_OK(graph.createRole(roleC)); + + ActionSet actions; + actions.addAction(ActionType::find); + ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(dbAResource, actions))); + ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbBResource, actions))); + ASSERT_OK(graph.addPrivilegeToRole(roleC, Privilege(dbCResource, actions))); + + ASSERT_OK(graph.addRoleToRole(roleA, roleB)); + + // Make a copy of the graph to do further modifications on. + RoleGraph tempGraph(graph); + ASSERT_OK(tempGraph.addRoleToRole(roleB, roleC)); + tempGraph.recomputePrivilegeData(); + + // Now swap the copy back with the original graph and make sure the original was updated + // properly. + swap(tempGraph, graph); + + RoleNameIterator it = graph.getDirectSubordinates(roleB); + ASSERT_TRUE(it.more()); + ASSERT_EQUALS(it.next().getFullName(), roleC.getFullName()); + ASSERT_FALSE(it.more()); + + graph.getAllPrivileges(roleA); // should have privileges from roleB *and* role C + PrivilegeVector privileges = graph.getAllPrivileges(roleA); + ASSERT_EQUALS(static_cast<size_t>(3), privileges.size()); + ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); + ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern()); + ASSERT_EQUALS(dbCResource, privileges[2].getResourcePattern()); +} + +// Tests error handling +TEST(RoleGraphTest, ErrorHandling) { + RoleName roleA("roleA", "dbA"); + RoleName roleB("roleB", "dbB"); + RoleName roleC("roleC", "dbC"); + + ActionSet actions; + actions.addAction(ActionType::find); + Privilege privilege1(db1Resource, actions); + Privilege privilege2(db2Resource, actions); + PrivilegeVector privileges; + privileges.push_back(privilege1); + privileges.push_back(privilege2); + + RoleGraph graph; + // None of the roles exist yet. + ASSERT_NOT_OK(graph.addPrivilegeToRole(roleA, privilege1)); + ASSERT_NOT_OK(graph.addPrivilegesToRole(roleA, privileges)); + ASSERT_NOT_OK(graph.removePrivilegeFromRole(roleA, privilege1)); + ASSERT_NOT_OK(graph.removePrivilegesFromRole(roleA, privileges)); + ASSERT_NOT_OK(graph.removeAllPrivilegesFromRole(roleA)); + ASSERT_NOT_OK(graph.addRoleToRole(roleA, roleB)); + ASSERT_NOT_OK(graph.removeRoleFromRole(roleA, roleB)); + + // One of the roles exists + ASSERT_OK(graph.createRole(roleA)); + ASSERT_NOT_OK(graph.createRole(roleA)); // Can't create same role twice + ASSERT_NOT_OK(graph.addRoleToRole(roleA, roleB)); + ASSERT_NOT_OK(graph.addRoleToRole(roleB, roleA)); + ASSERT_NOT_OK(graph.removeRoleFromRole(roleA, roleB)); + ASSERT_NOT_OK(graph.removeRoleFromRole(roleB, roleA)); + + // Should work now that both exist. + ASSERT_OK(graph.createRole(roleB)); + ASSERT_OK(graph.addRoleToRole(roleA, roleB)); + ASSERT_OK(graph.removeRoleFromRole(roleA, roleB)); + ASSERT_NOT_OK( + graph.removeRoleFromRole(roleA, roleB)); // roleA isn't actually a member of roleB + + // Can't remove a privilege from a role that doesn't have it. + ASSERT_NOT_OK(graph.removePrivilegeFromRole(roleA, privilege1)); + ASSERT_OK(graph.addPrivilegeToRole(roleA, privilege1)); + ASSERT_OK(graph.removePrivilegeFromRole(roleA, privilege1)); // now should work + + // Test that removing a vector of privileges fails if *any* of the privileges are missing. + ASSERT_OK(graph.addPrivilegeToRole(roleA, privilege1)); + ASSERT_OK(graph.addPrivilegeToRole(roleA, privilege2)); + // Removing both privileges should work since it has both + ASSERT_OK(graph.removePrivilegesFromRole(roleA, privileges)); + // Now add only 1 back and this time removing both should fail + ASSERT_OK(graph.addPrivilegeToRole(roleA, privilege1)); + ASSERT_NOT_OK(graph.removePrivilegesFromRole(roleA, privileges)); +} + + +TEST(RoleGraphTest, BuiltinRoles) { + RoleName userRole("userDefined", "dbA"); + RoleName builtinRole("read", "dbA"); + + ActionSet actions; + actions.addAction(ActionType::insert); + Privilege privilege(dbAResource, actions); + + RoleGraph graph; + + ASSERT(graph.roleExists(builtinRole)); + ASSERT_NOT_OK(graph.createRole(builtinRole)); + ASSERT_NOT_OK(graph.deleteRole(builtinRole)); + ASSERT(graph.roleExists(builtinRole)); + ASSERT(!graph.roleExists(userRole)); + ASSERT_OK(graph.createRole(userRole)); + ASSERT(graph.roleExists(userRole)); + + ASSERT_NOT_OK(graph.addPrivilegeToRole(builtinRole, privilege)); + ASSERT_NOT_OK(graph.removePrivilegeFromRole(builtinRole, privilege)); + ASSERT_NOT_OK(graph.addRoleToRole(builtinRole, userRole)); + ASSERT_NOT_OK(graph.removeRoleFromRole(builtinRole, userRole)); + + ASSERT_OK(graph.addPrivilegeToRole(userRole, privilege)); + ASSERT_OK(graph.addRoleToRole(userRole, builtinRole)); + ASSERT_OK(graph.recomputePrivilegeData()); + + PrivilegeVector privileges = graph.getDirectPrivileges(userRole); + ASSERT_EQUALS(1U, privileges.size()); + ASSERT(privileges[0].getActions().equals(actions)); + ASSERT(!privileges[0].getActions().contains(ActionType::find)); + ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); + + privileges = graph.getAllPrivileges(userRole); + size_t i; + for (i = 0; i < privileges.size(); ++i) { + if (dbAResource == privileges[i].getResourcePattern()) + break; } + ASSERT_NOT_EQUALS(privileges.size(), i); + ASSERT(privileges[i].getActions().isSupersetOf(actions)); + ASSERT(privileges[i].getActions().contains(ActionType::insert)); + ASSERT(privileges[i].getActions().contains(ActionType::find)); + + ASSERT_OK(graph.deleteRole(userRole)); + ASSERT(!graph.roleExists(userRole)); +} + +TEST(RoleGraphTest, BuiltinRolesOnlyOnAppropriateDatabases) { + RoleGraph graph; + ASSERT(graph.roleExists(RoleName("read", "test"))); + ASSERT(graph.roleExists(RoleName("readWrite", "test"))); + ASSERT(graph.roleExists(RoleName("userAdmin", "test"))); + ASSERT(graph.roleExists(RoleName("dbAdmin", "test"))); + ASSERT(graph.roleExists(RoleName("dbOwner", "test"))); + ASSERT(!graph.roleExists(RoleName("readAnyDatabase", "test"))); + ASSERT(!graph.roleExists(RoleName("readWriteAnyDatabase", "test"))); + ASSERT(!graph.roleExists(RoleName("userAdminAnyDatabase", "test"))); + ASSERT(!graph.roleExists(RoleName("dbAdminAnyDatabase", "test"))); + ASSERT(!graph.roleExists(RoleName("clusterAdmin", "test"))); + ASSERT(!graph.roleExists(RoleName("root", "test"))); + ASSERT(!graph.roleExists(RoleName("__system", "test"))); + ASSERT(!graph.roleExists(RoleName("MyRole", "test"))); + + ASSERT(graph.roleExists(RoleName("read", "admin"))); + ASSERT(graph.roleExists(RoleName("readWrite", "admin"))); + ASSERT(graph.roleExists(RoleName("userAdmin", "admin"))); + ASSERT(graph.roleExists(RoleName("dbAdmin", "admin"))); + ASSERT(graph.roleExists(RoleName("dbOwner", "admin"))); + ASSERT(graph.roleExists(RoleName("readAnyDatabase", "admin"))); + ASSERT(graph.roleExists(RoleName("readWriteAnyDatabase", "admin"))); + ASSERT(graph.roleExists(RoleName("userAdminAnyDatabase", "admin"))); + ASSERT(graph.roleExists(RoleName("dbAdminAnyDatabase", "admin"))); + ASSERT(graph.roleExists(RoleName("clusterAdmin", "admin"))); + ASSERT(graph.roleExists(RoleName("root", "admin"))); + ASSERT(graph.roleExists(RoleName("__system", "admin"))); + 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 |