diff options
author | Andy Schwerin <schwerin@10gen.com> | 2013-09-19 15:46:08 -0400 |
---|---|---|
committer | Andy Schwerin <schwerin@10gen.com> | 2013-09-26 14:33:43 -0400 |
commit | 3bc344db791e74f378880fd4ccd2ea045ba238f9 (patch) | |
tree | 206ad9c8fd4e5d3fcbfa67f981d7c360aecb5983 | |
parent | 25d5d2ae7c22267d942532de72109d3d7bf67a8c (diff) | |
download | mongo-3bc344db791e74f378880fd4ccd2ea045ba238f9.tar.gz |
SERVER-1105 Update AuthorizationSession's logic for collection-level access control.
Also requires changing the privileges of the built-in roles. This patch takes the opportunity to remove the 2.2-style read-only roles in favor of the 2.4-style "read" and "readAnyDatabase" roles, and renames the 2.2-style read-write roles "dbOwner" and "root". The "root" name, at least, is subject to change prior to the next unstable release.
Test harnesses are updated as needed to use the correct builtin roles.
24 files changed, 622 insertions, 837 deletions
diff --git a/buildscripts/smoke.py b/buildscripts/smoke.py index 65ceee5212e..bdfec533473 100755 --- a/buildscripts/smoke.py +++ b/buildscripts/smoke.py @@ -169,10 +169,7 @@ class mongod(object): try: Connection( "localhost" , int(port) ).admin.command("createUser", "admin", pwd="password", - roles=["readWriteAnyDatabase", - "dbAdminAnyDatabase", - "userAdminAnyDatabase", - "clusterAdmin"]) + roles=["__system"]) except OperationFailure, e: if e.message == 'need to login': pass # SERVER-4225 diff --git a/jstests/auth/auth1.js b/jstests/auth/auth1.js index 0d1610a83d5..d77f1544734 100644 --- a/jstests/auth/auth1.js +++ b/jstests/auth/auth1.js @@ -18,7 +18,7 @@ tRO = dbRO[ baseName ]; db.removeAllUsers(); -db.getSisterDB( "admin" ).addUser( "super", "super", jsTest.adminUserRoles ); +db.getSisterDB( "admin" ).addUser( "super", "super", ["__system"] ); db.getSisterDB("admin").auth("super", "super"); db.addUser( "eliot" , "eliot", jsTest.basicUserRoles ); db.addUser( "guest" , "guest", jsTest.readOnlyUserRoles ); @@ -87,5 +87,4 @@ assert.eq( 1000, db.eval( function() { return db[ "jstests_auth_auth1" ].count() db.eval( function() { db[ "jstests_auth_auth1" ].save( {i:1000} ) } ); assert.eq( 1001, db.eval( function() { return db[ "jstests_auth_auth1" ].count(); } ) , "D2" ); - print("SUCCESS auth1.js"); diff --git a/jstests/auth/basic_role_auth.js b/jstests/auth/basic_role_auth.js index ddb1229ec16..60ad58bb9fc 100644 --- a/jstests/auth/basic_role_auth.js +++ b/jstests/auth/basic_role_auth.js @@ -15,8 +15,7 @@ var AUTH_INFO = { admin: { root: { pwd: 'root', - roles: [ 'readWriteAnyDatabase', 'userAdminAnyDatabase', - 'dbAdminAnyDatabase', 'clusterAdmin' ] + roles: [ 'root' ] }, cluster: { pwd: 'cluster', @@ -187,7 +186,10 @@ var testOps = function(db, allowedActions) { }, db); checkErr(allowedActions.hasOwnProperty('user_r'), function() { - db.system.users.findOne(); + var result = db.runCommand({usersInfo: /.*/}); + if (!result.ok) { + throw new Error(tojson(result)); + } }); checkErr(allowedActions.hasOwnProperty('user_w'), function() { @@ -524,6 +526,7 @@ var runTests = function(conn) { testFunc.test(newConn); } catch (x) { failures.push(testFunc.name); + jsTestLog(x); } }); diff --git a/jstests/auth/renameSystemCollections.js b/jstests/auth/renameSystemCollections.js index 4d3e67035b1..e682635c1e3 100644 --- a/jstests/auth/renameSystemCollections.js +++ b/jstests/auth/renameSystemCollections.js @@ -7,17 +7,21 @@ var testDB2 = conn.getDB("testdb2"); var CodeUnauthorized = 13; -adminDB.addUser({user:'userAdmin', +var backdoorUserDoc = { name: 'backdoor', source: 'admin', pwd: 'hashed', roles: ['root'] } + +adminDB.addUser({name:'userAdmin', pwd:'password', roles:['userAdminAnyDatabase']}); adminDB.auth('userAdmin', 'password'); -adminDB.addUser({user:'readWriteAdmin', +adminDB.addUser({name:'readWriteAdmin', pwd:'password', roles:['readWriteAnyDatabase']}); -adminDB.addUser({user:'readWriteAndUserAdmin', +adminDB.addUser({name:'readWriteAndUserAdmin', pwd:'password', roles:['readWriteAnyDatabase', 'userAdminAnyDatabase']}); +adminDB.addUser({name: 'root', pwd: 'password', roles: ['root']}); +adminDB.addUser({name: 'rootier', pwd: 'password', roles: ['__system']}); adminDB.logout(); @@ -36,9 +40,7 @@ assert.eq(0, adminDB.users.count()); jsTestLog("Test that a readWrite user can't use renameCollection to override system.users"); -adminDB.users.insert({user:'backdoor', - pwd:'hashedpassword', - roles:'userAdmin'}); +adminDB.users.insert(backdoorUserDoc); res = adminDB.users.renameCollection("system.users", true); assert.eq(0, res.ok); assert.eq(CodeUnauthorized, res.code); @@ -50,21 +52,58 @@ adminDB.auth('userAdmin', 'password'); var res = adminDB.system.users.renameCollection("users"); assert.eq(0, res.ok); assert.eq(CodeUnauthorized, res.code); -assert.eq(3, adminDB.system.users.count()); +assert.eq(5, adminDB.system.users.count()); adminDB.auth('readWriteAndUserAdmin', 'password'); assert.eq(0, adminDB.users.count()); -jsTestLog("Test that with userAdmin AND dbAdmin you CAN rename to/from system.users"); +jsTestLog("Test that even with userAdmin AND dbAdmin you CANNOT rename to/from system.users"); var res = adminDB.system.users.renameCollection("users"); -assert.eq(1, res.ok); -assert.eq(3, adminDB.users.count()); +assert.eq(0, res.ok); +assert.eq(CodeUnauthorized, res.code); +assert.eq(5, adminDB.system.users.count()); adminDB.users.drop(); -adminDB.users.insert({user:'newUser', - pwd:'hashedPassword', - roles:['readWrite']}); +adminDB.users.insert(backdoorUserDoc); var res = adminDB.users.renameCollection("system.users"); +assert.eq(0, res.ok); +assert.eq(CodeUnauthorized, res.code); + +assert.eq(null, adminDB.system.users.findOne({name: backdoorUserDoc.name})); +assert.neq(null, adminDB.system.users.findOne({name:'userAdmin'})); + +adminDB.auth('root', 'password'); +adminDB.users.drop(); +adminDB.users.insert(backdoorUserDoc); + +jsTestLog("Test that with root you CANNOT rename to/from system.users"); +var res = adminDB.system.users.renameCollection("users"); +assert.eq(0, res.ok); +assert.eq(CodeUnauthorized, res.code); +assert.eq(5, adminDB.system.users.count()); + +adminDB.users.drop(); +adminDB.users.insert(backdoorUserDoc); +var res = adminDB.users.renameCollection("system.users"); +assert.eq(0, res.ok); +assert.eq(CodeUnauthorized, res.code); + +assert.eq(null, adminDB.system.users.findOne({name: backdoorUserDoc.name})); +assert.neq(null, adminDB.system.users.findOne({name:'userAdmin'})); + +adminDB.auth('rootier', 'password'); + +jsTestLog("Test that with __system you CAN rename to/from system.users"); +var res = adminDB.system.users.renameCollection("users", true); +assert.eq(1, res.ok, tojson(res)); +assert.eq(0, adminDB.system.users.count()); +assert.eq(5, adminDB.users.count()); + +adminDB.users.drop(); +adminDB.users.insert(backdoorUserDoc); +var res = adminDB.users.renameCollection("system.users", true); assert.eq(1, res.ok); -assert.neq(null, adminDB.system.users.findOne({user:'newUser'})); -assert.eq(null, adminDB.system.users.findOne({user:'userAdmin'})); +assert.neq(null, adminDB.system.users.findOne({name: backdoorUserDoc.name})); +assert.eq(null, adminDB.system.users.findOne({name:'userAdmin'})); + + diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript index 20486ff8b64..0da1f578d4f 100644 --- a/src/mongo/db/auth/SConscript +++ b/src/mongo/db/auth/SConscript @@ -69,7 +69,6 @@ env.CppUnitTest('authorization_manager_test', 'authorization_manager_test.cpp', LIBDEPS=['authcore', 'authmocks']) env.CppUnitTest('authorization_session_test', 'authorization_session_test.cpp', LIBDEPS=['authcore', 'authmocks']) -env.CppUnitTest('resource_pattern_test', 'resource_pattern_test.cpp', LIBDEPS=['authcore']) env.CppUnitTest('user_management_commands_parser_test', 'user_management_commands_parser_test.cpp', LIBDEPS=['usercommandsparser', 'authmocks'], NO_CRUTCH=True) diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp index 4b4cd1cffc7..835403cd20e 100644 --- a/src/mongo/db/auth/authorization_manager.cpp +++ b/src/mongo/db/auth/authorization_manager.cpp @@ -61,8 +61,9 @@ namespace mongo { user->incrementRefCount(); // Pin this user so the ref count never drops below 1. ActionSet allActions; allActions.addAllActions(); - user->addPrivilege(Privilege(ResourcePattern::forAnyResource(), allActions)); - + PrivilegeVector privileges; + RoleGraph::generateUniversalPrivileges(&privileges); + user->addPrivileges(privileges); internalSecurity.user = user; return Status::OK(); @@ -191,10 +192,6 @@ namespace mongo { return _externalState->query(collectionName, query, resultProcessor); } - ActionSet AuthorizationManager::getAllUserActions() { - return RoleGraph::getAllUserActions(); - } - bool AuthorizationManager::roleExists(const RoleName& role) { boost::lock_guard<boost::mutex> lk(_lock); return _roleGraph.roleExists(role); diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h index 7e3723a1875..9515b97fcb5 100644 --- a/src/mongo/db/auth/authorization_manager.h +++ b/src/mongo/db/auth/authorization_manager.h @@ -37,6 +37,7 @@ #include "mongo/base/status.h" #include "mongo/db/auth/action_set.h" #include "mongo/db/auth/authz_manager_external_state.h" +#include "mongo/db/auth/resource_pattern.h" #include "mongo/db/auth/role_graph.h" #include "mongo/db/auth/user.h" #include "mongo/db/auth/user_name.h" @@ -176,10 +177,6 @@ namespace mongo { // that an old-style user with those attributes should be given. ActionSet getActionsForOldStyleUser(const std::string& dbname, bool readOnly) const; - // Returns an ActionSet of all actions that can be be granted to users. This does not - // include internal-only actions. - ActionSet getAllUserActions(); - /** * Returns the User object for the given userName in the out parameter "acquiredUser". * If the user cache already has a user object for this user, it increments the refcount diff --git a/src/mongo/db/auth/authorization_manager_test.cpp b/src/mongo/db/auth/authorization_manager_test.cpp index 71f1272241d..dc8497ac1e3 100644 --- a/src/mongo/db/auth/authorization_manager_test.cpp +++ b/src/mongo/db/auth/authorization_manager_test.cpp @@ -73,7 +73,7 @@ namespace { const User::RoleDataMap& roles = v0RW->getRoles(); ASSERT_EQUALS(1U, roles.size()); User::RoleData role = roles.begin()->second; - ASSERT_EQUALS(RoleName("oldReadWrite", "test"), role.name); + ASSERT_EQUALS(RoleName("dbOwner", "test"), role.name); ASSERT(role.hasRole); ASSERT(!role.canDelegate); // Make sure user's refCount is 0 at the end of the test to avoid an assertion failure @@ -87,7 +87,7 @@ namespace { const User::RoleDataMap& adminRoles = v0AdminRO->getRoles(); ASSERT_EQUALS(1U, adminRoles.size()); role = adminRoles.begin()->second; - ASSERT_EQUALS(RoleName("oldAdminRead", "admin"), role.name); + ASSERT_EQUALS(RoleName("readAnyDatabase", "admin"), role.name); ASSERT(role.hasRole); ASSERT(!role.canDelegate); // Make sure user's refCount is 0 at the end of the test to avoid an assertion failure diff --git a/src/mongo/db/auth/authorization_session.cpp b/src/mongo/db/auth/authorization_session.cpp index 0c446fdd6f1..ce547214e1d 100644 --- a/src/mongo/db/auth/authorization_session.cpp +++ b/src/mongo/db/auth/authorization_session.cpp @@ -238,60 +238,62 @@ namespace { return isAuthorizedForPrivilege(Privilege(ResourcePattern::forExactNamespace(ns), actions)); } - static const ResourcePattern anyUsersCollectionPattern = ResourcePattern::forCollectionName( - "system.users"); - static const ResourcePattern anyProfileCollectionPattern = ResourcePattern::forCollectionName( - "system.profile"); - static const ResourcePattern anyIndexesCollectionPattern = ResourcePattern::forCollectionName( - "system.indexes"); - - // Returns a new privilege that has replaced the actions needed to handle special casing - // certain namespaces like system.users and system.profile. Note that the special handling - // of system.indexes takes place in checkAuthForInsert, not here. - static Privilege _modifyPrivilegeForSpecialCases(Privilege privilege) { - ActionSet newActions(privilege.getActions()); - const ResourcePattern& target(privilege.getResourcePattern()); - if (anyUsersCollectionPattern.matchesResourcePattern(target)) { - if (newActions.contains(ActionType::insert) || - newActions.contains(ActionType::update) || - newActions.contains(ActionType::remove)) { - // End users can't modify system.users directly, only the system can. - newActions.addAction(ActionType::userAdminV1); - } else { - newActions.addAction(ActionType::userAdmin); + static const int resourceSearchListCapacity = 5; + /** + * Builds from "target" an exhaustive list of all ResourcePatterns that match "target". + * + * Stores the resulting list into resourceSearchList, and returns the length. + * + * The seach lists are as follows, depending on the type of "target": + * + * target is ResourcePattern::forAnyResource(): + * searchList = { ResourcePattern::forAnyResource(), ResourcePattern::forAnyResource() } + * target is the ResourcePattern::forClusterResource(): + * searchList = { ResourcePattern::forAnyResource(), ResourcePattern::forClusterResource() } + * target is a database, db: + * searchList = { ResourcePattern::forAnyResource(), + * ResourcePattern::forAnyNormalResource(), + * db } + * target is a non-system collection, db.coll: + * searchList = { ResourcePattern::forAnyResource(), + * ResourcePattern::forAnyNormalResource(), + * db, + * coll, + * db.coll } + * target is a system collection, db.system.coll: + * searchList = { ResourcePattern::forAnyResource(), + * system.coll, + * db.system.coll } + */ + static int buildResourceSearchList( + const ResourcePattern& target, + ResourcePattern resourceSearchList[resourceSearchListCapacity]) { + + int size = 0; + resourceSearchList[size++] = ResourcePattern::forAnyResource(); + if (target.isExactNamespacePattern()) { + if (!target.ns().isSystem()) { + resourceSearchList[size++] = ResourcePattern::forAnyNormalResource(); + resourceSearchList[size++] = ResourcePattern::forDatabaseName(target.ns().db()); } - newActions.removeAction(ActionType::find); - newActions.removeAction(ActionType::insert); - newActions.removeAction(ActionType::update); - newActions.removeAction(ActionType::remove); - } else if (anyProfileCollectionPattern.matchesResourcePattern(target)) { - newActions.removeAction(ActionType::find); - newActions.addAction(ActionType::profileRead); - } else if (anyIndexesCollectionPattern.matchesResourcePattern(target) - && newActions.contains(ActionType::find)) { - newActions.removeAction(ActionType::find); - newActions.addAction(ActionType::indexRead); + resourceSearchList[size++] = ResourcePattern::forCollectionName(target.ns().coll()); } - - return Privilege(privilege.getResourcePattern(), newActions); + else if (target.isDatabasePattern()) { + resourceSearchList[size++] = ResourcePattern::forAnyNormalResource(); + } + resourceSearchList[size++] = target; + dassert(size <= resourceSearchListCapacity); + return size; } bool AuthorizationSession::_isAuthorizedForPrivilege(const Privilege& privilege) { AuthorizationManager& authMan = getAuthorizationManager(); - Privilege modifiedPrivilege = _modifyPrivilegeForSpecialCases(privilege); - - // Need to check not just the resource of the privilege, but also just the database - // component and the "*" resource. - ResourcePattern resourceSearchList[3]; - resourceSearchList[0] = ResourcePattern::forAnyResource(); - resourceSearchList[1] = modifiedPrivilege.getResourcePattern(); - if (modifiedPrivilege.getResourcePattern().isExactNamespacePattern()) { - resourceSearchList[2] = - ResourcePattern::forDatabaseName(modifiedPrivilege.getResourcePattern().ns().db()); - } + const ResourcePattern& target(privilege.getResourcePattern()); + ResourcePattern resourceSearchList[resourceSearchListCapacity]; + const int resourceSearchListLength = buildResourceSearchList(target, resourceSearchList); - ActionSet unmetRequirements = modifiedPrivilege.getActions(); + ActionSet unmetRequirements = privilege.getActions(); UserSet::iterator it = _authenticatedUsers.begin(); while (it != _authenticatedUsers.end()) { User* user = *it; @@ -329,7 +331,7 @@ namespace { } } - for (int i = 0; i < static_cast<int>(boost::size(resourceSearchList)); ++i) { + for (int i = 0; i < resourceSearchListLength; ++i) { ActionSet userActions = user->getActionsForResource(resourceSearchList[i]); unmetRequirements.removeAllActionsFromSet(userActions); diff --git a/src/mongo/db/auth/authorization_session_test.cpp b/src/mongo/db/auth/authorization_session_test.cpp index 9360d36e788..bf71c8b68ee 100644 --- a/src/mongo/db/auth/authorization_session_test.cpp +++ b/src/mongo/db/auth/authorization_session_test.cpp @@ -153,7 +153,9 @@ namespace { ASSERT_OK(authzSession->addAndAuthorizeUser(UserName("admin", "admin"))); ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource( - ResourcePattern::forAnyResource(), ActionType::insert)); + ResourcePattern::forExactNamespace( + NamespaceString("anydb.somecollection")), + ActionType::insert)); ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource( otherDBResource, ActionType::insert)); ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource( @@ -289,7 +291,7 @@ namespace { ASSERT_OK(authzSession->addAndAuthorizeUser(UserName("useradmin", "test"))); ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource( testUsersCollResource, ActionType::insert)); - ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource( + ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource( testUsersCollResource, ActionType::find)); ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource( otherUsersCollResource, ActionType::insert)); diff --git a/src/mongo/db/auth/privilege.cpp b/src/mongo/db/auth/privilege.cpp index 89261791f98..a1d0dab7bfe 100644 --- a/src/mongo/db/auth/privilege.cpp +++ b/src/mongo/db/auth/privilege.cpp @@ -20,6 +20,18 @@ namespace mongo { + void Privilege::addPrivilegeToPrivilegeVector(PrivilegeVector* privileges, + const Privilege& privilegeToAdd) { + for (PrivilegeVector::iterator it = privileges->begin(); it != privileges->end(); ++it) { + if (it->getResourcePattern() == privilegeToAdd.getResourcePattern()) { + it->addActions(privilegeToAdd.getActions()); + return; + } + } + // No privilege exists yet for this resource + privileges->push_back(privilegeToAdd); + } + Privilege::Privilege(const ResourcePattern& resource, const ActionType& action) : _resource(resource) { diff --git a/src/mongo/db/auth/privilege.h b/src/mongo/db/auth/privilege.h index dda2aca6c09..710e18e6be8 100644 --- a/src/mongo/db/auth/privilege.h +++ b/src/mongo/db/auth/privilege.h @@ -24,11 +24,23 @@ namespace mongo { + class Privilege; + typedef std::vector<Privilege> PrivilegeVector; + /** * A representation of the permission to perform a set of actions on a resource. */ class Privilege { public: + /** + * Adds "privilegeToAdd" to "privileges", de-duping "privilegeToAdd" if the vector already + * contains a privilege on the same resource. + * + * This method is the preferred way to add privileges to privilege vectors. + */ + static void addPrivilegeToPrivilegeVector(PrivilegeVector* privileges, + const Privilege& privilegeToAdd); + Privilege() {}; Privilege(const ResourcePattern& resource, const ActionType& action); @@ -53,6 +65,4 @@ namespace mongo { ActionSet _actions; // bitmask of actions this privilege grants }; - typedef std::vector<Privilege> PrivilegeVector; - } // namespace mongo diff --git a/src/mongo/db/auth/resource_pattern.cpp b/src/mongo/db/auth/resource_pattern.cpp index 954bbc24d0a..9a527f4598e 100644 --- a/src/mongo/db/auth/resource_pattern.cpp +++ b/src/mongo/db/auth/resource_pattern.cpp @@ -24,43 +24,6 @@ namespace mongo { - bool ResourcePattern::matchesDatabaseName(const StringData& dbName) const { - switch (_matchType) { - case matchAnyResource: return true; - case matchAnyNormalResource: return true; - case matchDatabaseName: return dbName == _ns.db(); - default: return false; - } - } - - bool ResourcePattern::matchesNamespaceString(const NamespaceString& ns) const { - switch (_matchType) { - case matchAnyResource: return true; - case matchAnyNormalResource: return !ns.isSystem(); - case matchDatabaseName: return ns.db() == _ns.db() && !ns.isSystem(); - case matchCollectionName: return ns.coll() == _ns.coll(); - case matchExactNamespace: return ns == _ns; - default: return false; - } - } - - bool ResourcePattern::matchesResourcePattern(const ResourcePattern& target) const { - switch (target._matchType) { - case matchAnyResource: - return matchesEverything(); - case matchClusterResource: - return matchesClusterResource(); - case matchDatabaseName: - return matchesDatabaseName(target._ns.db()); - case matchExactNamespace: - return matchesNamespaceString(target._ns); - default: - error() << "Programming error: target of matchesResourcePattern() must be a specific " - "resource or ResourcePattern::forAnyResource(); not a wildcard"; - fassertFailed(17136); - } - } - std::string ResourcePattern::toString() const { switch (_matchType) { case matchNever: diff --git a/src/mongo/db/auth/resource_pattern.h b/src/mongo/db/auth/resource_pattern.h index 24bc1020301..9cbd9255840 100644 --- a/src/mongo/db/auth/resource_pattern.h +++ b/src/mongo/db/auth/resource_pattern.h @@ -26,10 +26,14 @@ namespace mongo { /** - * Representation of patterns that match various kinds of resources used in access control - * checks. + * Representation of names of various kinds of resources targetable by the access control + * system. * - * The resources are databases, collections and the the cluster itself. + * Three of the types of name, "forDatabaseName", "forExactNamespace" and "forClusterResource", + * can represent concrete resources targeted for manipulation by database operations. All of + * the types also act as patterns, useful for matching against groups of concrete resources as + * part of the access control system. See buildResourceSearchList() in + * authorization_session.cpp for details. */ class ResourcePattern { public: @@ -119,57 +123,6 @@ namespace mongo { } /** - * Returns true if this pattern matches nothing at all. - */ - bool matchesNothing() const { return matchNever == _matchType; } - - /** - * Returns true if this pattern matches every possible resource. - */ - bool matchesEverything() const { return matchAnyResource == _matchType; } - - /** - * Returns true if this pattern matches every resource except the cluster resource and - * system collections. - */ - bool matchesAnyNormalResource() const { - return matchesEverything() || matchAnyNormalResource == _matchType; - } - - /** - * Returns true if this pattern matches the system resource. - */ - bool matchesClusterResource() const { - return matchesEverything() || _matchType == matchClusterResource; - } - - /** - * Returns true if this pattern matches the named database. - */ - bool matchesDatabaseName(const StringData& dbName) const; - - /** - * Returns true if this pattern matches the given NamespaceString. - */ - bool matchesNamespaceString(const NamespaceString& ns) const; - - /** - * Returns true if this pattern matches the target. - * - * The target must be a pattern matching some specific instance of a resource, rather than a - * wildcard of some sort. That is, it must be a database name pattern, an exact match - * pattern, or a cluster resource pattern. - * - * As a special exception, the target may also be ResourcePattern::forAnyResource(), since - * this is the target used for providing access control on applyOps and db.eval. If the - * target type is ResourcePattern::forAnyResource(), this method only returns true if this - * instance is ResourcePattern::forAnyResource(). - * - * Behavior for other patterns is undefined. - */ - bool matchesResourcePattern(const ResourcePattern& target) const; - - /** * Returns the namespace that this pattern matches. * * Behavior is undefined unless isExactNamespacePattern() is true. diff --git a/src/mongo/db/auth/resource_pattern_test.cpp b/src/mongo/db/auth/resource_pattern_test.cpp deleted file mode 100644 index 4ac2c467846..00000000000 --- a/src/mongo/db/auth/resource_pattern_test.cpp +++ /dev/null @@ -1,342 +0,0 @@ -/** - * Copyright (C) 2013 10gen Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <string> - -#include "mongo/db/auth/resource_pattern.h" -#include "mongo/db/namespace_string.h" -#include "mongo/unittest/unittest.h" - -namespace mongo { -namespace { - - TEST(ResourcePattern, AnyResourcePattern) { - ResourcePattern pattern = ResourcePattern::forAnyResource(); - ASSERT(pattern.matchesEverything()); - ASSERT(pattern.matchesAnyNormalResource()); - ASSERT(pattern.matchesClusterResource()); - ASSERT(pattern.matchesDatabaseName("admin")); - ASSERT(pattern.matchesDatabaseName("config")); - ASSERT(pattern.matchesDatabaseName("test")); - ASSERT(pattern.matchesDatabaseName("work")); - ASSERT(pattern.matchesNamespaceString(NamespaceString("test.collection"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("test.dotted.collection"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("test.system.profile"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("test.not.system.profile"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("test.system.profile.not"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("test.system.frimfram"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("work.collection"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("work.dotted.collection"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("work.system.profile"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("work.not.system.profile"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("work.system.profile.not"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("work.system.frimfram"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("admin.collection"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("admin.dotted.collection"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("admin.system.profile"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("admin.not.system.profile"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("admin.system.profile.not"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("admin.system.frimfram"))); - } - - TEST(ResourcePattern, AnyNormalResourcePattern) { - ResourcePattern pattern = ResourcePattern::forAnyNormalResource(); - ASSERT(!pattern.matchesEverything()); - ASSERT(pattern.matchesAnyNormalResource()); - ASSERT(!pattern.matchesClusterResource()); - ASSERT(pattern.matchesDatabaseName("admin")); - ASSERT(pattern.matchesDatabaseName("config")); - ASSERT(pattern.matchesDatabaseName("test")); - ASSERT(pattern.matchesDatabaseName("work")); - ASSERT(pattern.matchesNamespaceString(NamespaceString("test.collection"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("test.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.profile"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("test.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.frimfram"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("work.collection"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("work.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.profile"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("work.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.frimfram"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("admin.collection"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("admin.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.profile"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("admin.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.frimfram"))); - } - - TEST(ResourcePattern, ClusterResourcePattern) { - ResourcePattern pattern = ResourcePattern::forClusterResource(); - ASSERT(!pattern.matchesEverything()); - ASSERT(!pattern.matchesAnyNormalResource()); - ASSERT(pattern.matchesClusterResource()); - ASSERT(!pattern.matchesDatabaseName("admin")); - ASSERT(!pattern.matchesDatabaseName("config")); - ASSERT(!pattern.matchesDatabaseName("test")); - ASSERT(!pattern.matchesDatabaseName("work")); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.frimfram"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.frimfram"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.frimfram"))); - } - - TEST(ResourcePattern, DatabaseNameTestPattern) { - ResourcePattern pattern = ResourcePattern::forDatabaseName("test"); - ASSERT(!pattern.matchesEverything()); - ASSERT(!pattern.matchesAnyNormalResource()); - ASSERT(!pattern.matchesClusterResource()); - ASSERT(!pattern.matchesDatabaseName("admin")); - ASSERT(!pattern.matchesDatabaseName("config")); - ASSERT(pattern.matchesDatabaseName("test")); - ASSERT(!pattern.matchesDatabaseName("work")); - ASSERT(pattern.matchesNamespaceString(NamespaceString("test.collection"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("test.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.profile"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("test.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.frimfram"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.frimfram"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.frimfram"))); - - pattern = ResourcePattern::forDatabaseName("admin"); - ASSERT(!pattern.matchesEverything()); - ASSERT(!pattern.matchesClusterResource()); - ASSERT(pattern.matchesDatabaseName("admin")); - ASSERT(!pattern.matchesDatabaseName("config")); - ASSERT(!pattern.matchesDatabaseName("test")); - ASSERT(!pattern.matchesDatabaseName("work")); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.frimfram"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.frimfram"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("admin.collection"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("admin.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.profile"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("admin.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.frimfram"))); - } - - TEST(ResourcePattern, NormalCollectionPattern) { - ResourcePattern pattern = ResourcePattern::forCollectionName("collection"); - ASSERT(!pattern.matchesEverything()); - ASSERT(!pattern.matchesAnyNormalResource()); - ASSERT(!pattern.matchesClusterResource()); - ASSERT(!pattern.matchesDatabaseName("admin")); - ASSERT(!pattern.matchesDatabaseName("config")); - ASSERT(!pattern.matchesDatabaseName("test")); - ASSERT(!pattern.matchesDatabaseName("work")); - ASSERT(pattern.matchesNamespaceString(NamespaceString("test.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.frimfram"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("work.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.frimfram"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("admin.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.frimfram"))); - } - - TEST(ResourcePattern, DottedCollectionPattern) { - ResourcePattern pattern = ResourcePattern::forCollectionName("dotted.collection"); - ASSERT(!pattern.matchesEverything()); - ASSERT(!pattern.matchesAnyNormalResource()); - ASSERT(!pattern.matchesClusterResource()); - ASSERT(!pattern.matchesDatabaseName("admin")); - ASSERT(!pattern.matchesDatabaseName("config")); - ASSERT(!pattern.matchesDatabaseName("test")); - ASSERT(!pattern.matchesDatabaseName("work")); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("config"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.collection"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("test.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.frimfram"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.collection"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("work.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.frimfram"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.collection"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("admin.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.frimfram"))); - } - - TEST(ResourcePattern, SystemCollectionPattern) { - ResourcePattern pattern = ResourcePattern::forCollectionName("system.profile"); - ASSERT(!pattern.matchesEverything()); - ASSERT(!pattern.matchesAnyNormalResource()); - ASSERT(!pattern.matchesClusterResource()); - ASSERT(!pattern.matchesDatabaseName("admin")); - ASSERT(!pattern.matchesDatabaseName("config")); - ASSERT(!pattern.matchesDatabaseName("test")); - ASSERT(!pattern.matchesDatabaseName("work")); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.dotted.collection"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("test.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.frimfram"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.dotted.collection"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("work.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.frimfram"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.dotted.collection"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("admin.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.frimfram"))); - } - - TEST(ResourcePattern, ExactNamespacePattern) { - ResourcePattern pattern = ResourcePattern::forExactNamespace( - NamespaceString("admin.system.profile")); - ASSERT(!pattern.matchesEverything()); - ASSERT(!pattern.matchesAnyNormalResource()); - ASSERT(!pattern.matchesClusterResource()); - ASSERT(!pattern.matchesDatabaseName("admin")); - ASSERT(!pattern.matchesDatabaseName("config")); - ASSERT(!pattern.matchesDatabaseName("test")); - ASSERT(!pattern.matchesDatabaseName("work")); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.frimfram"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.frimfram"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.dotted.collection"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("admin.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.frimfram"))); - - pattern = ResourcePattern::forExactNamespace(NamespaceString("test.system.profile")); - ASSERT(!pattern.matchesEverything()); - ASSERT(!pattern.matchesClusterResource()); - ASSERT(!pattern.matchesDatabaseName("admin")); - ASSERT(!pattern.matchesDatabaseName("config")); - ASSERT(!pattern.matchesDatabaseName("test")); - ASSERT(!pattern.matchesDatabaseName("work")); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.dotted.collection"))); - ASSERT(pattern.matchesNamespaceString(NamespaceString("test.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.frimfram"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.frimfram"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.frimfram"))); - - pattern = ResourcePattern::forExactNamespace(NamespaceString("test.collection")); - ASSERT(!pattern.matchesEverything()); - ASSERT(!pattern.matchesClusterResource()); - ASSERT(!pattern.matchesDatabaseName("admin")); - ASSERT(!pattern.matchesDatabaseName("config")); - ASSERT(!pattern.matchesDatabaseName("test")); - ASSERT(!pattern.matchesDatabaseName("work")); - ASSERT(pattern.matchesNamespaceString(NamespaceString("test.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("test.system.frimfram"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("work.system.frimfram"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.dotted.collection"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.not.system.profile"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.profile.not"))); - ASSERT(!pattern.matchesNamespaceString(NamespaceString("admin.system.frimfram"))); - } - -} // namespace -} // namespace mongo diff --git a/src/mongo/db/auth/role_graph.cpp b/src/mongo/db/auth/role_graph.cpp index 9a76da37223..4f96d073ed0 100644 --- a/src/mongo/db/auth/role_graph.cpp +++ b/src/mongo/db/auth/role_graph.cpp @@ -223,24 +223,6 @@ namespace { return Status::OK(); } -namespace { - // Helper function for adding a privilege to a privilege vector, de-duping the privilege if - // the vector already contains a privilege on the same resource. - void addPrivilegeToPrivilegeVector(PrivilegeVector& currentPrivileges, - const Privilege& privilegeToAdd) { - for (PrivilegeVector::iterator it = currentPrivileges.begin(); - it != currentPrivileges.end(); ++it) { - Privilege& curPrivilege = *it; - if (curPrivilege.getResourcePattern() == privilegeToAdd.getResourcePattern()) { - curPrivilege.addActions(privilegeToAdd.getActions()); - return; - } - } - // No privilege exists yet for this resource - currentPrivileges.push_back(privilegeToAdd); - } -} // namespace - Status RoleGraph::addPrivilegeToRole(const RoleName& role, const Privilege& privilegeToAdd) { if (!roleExists(role)) { return Status(ErrorCodes::RoleNotFound, @@ -261,7 +243,7 @@ namespace { void RoleGraph::_addPrivilegeToRoleNoChecks(const RoleName& role, const Privilege& privilegeToAdd) { - addPrivilegeToPrivilegeVector(_directPrivilegesForRole[role], privilegeToAdd); + Privilege::addPrivilegeToPrivilegeVector(&_directPrivilegesForRole[role], privilegeToAdd); } // NOTE: Current runtime of this is O(n*m) where n is the size of the current PrivilegeVector @@ -444,7 +426,7 @@ namespace { const PrivilegeVector& childsPrivileges = _allPrivilegesForRole[childRole]; for (PrivilegeVector::const_iterator privIt = childsPrivileges.begin(); privIt != childsPrivileges.end(); ++privIt) { - addPrivilegeToPrivilegeVector(currentRoleAllPrivileges, *privIt); + Privilege::addPrivilegeToPrivilegeVector(¤tRoleAllPrivileges, *privIt); } } diff --git a/src/mongo/db/auth/role_graph.h b/src/mongo/db/auth/role_graph.h index c1be6c10b60..7d6b83a3acc 100644 --- a/src/mongo/db/auth/role_graph.h +++ b/src/mongo/db/auth/role_graph.h @@ -66,10 +66,9 @@ namespace mongo { void swap(RoleGraph& other); /** - * Returns an ActionSet of all actions that can be be granted to users. This does not - * include internal-only actions. + * Adds to "privileges" the necessary privileges to do absolutely anything on the system. */ - static ActionSet getAllUserActions(); + static void generateUniversalPrivileges(PrivilegeVector* privileges); /** * Returns an iterator that can be used to get a list of the members of the given role. diff --git a/src/mongo/db/auth/role_graph_builtin_roles.cpp b/src/mongo/db/auth/role_graph_builtin_roles.cpp index 2ca5544c79c..9543bc1b54a 100644 --- a/src/mongo/db/auth/role_graph_builtin_roles.cpp +++ b/src/mongo/db/auth/role_graph_builtin_roles.cpp @@ -24,10 +24,10 @@ namespace mongo { - const std::string RoleGraph::BUILTIN_ROLE_V0_READ = "oldRead"; - const std::string RoleGraph::BUILTIN_ROLE_V0_READ_WRITE= "oldReadWrite"; - const std::string RoleGraph::BUILTIN_ROLE_V0_ADMIN_READ = "oldAdminRead"; - const std::string RoleGraph::BUILTIN_ROLE_V0_ADMIN_READ_WRITE= "oldAdminReadWrite"; + const std::string RoleGraph::BUILTIN_ROLE_V0_READ = "read"; + const std::string RoleGraph::BUILTIN_ROLE_V0_READ_WRITE= "dbOwner"; + const std::string RoleGraph::BUILTIN_ROLE_V0_ADMIN_READ = "readAnyDatabase"; + const std::string RoleGraph::BUILTIN_ROLE_V0_ADMIN_READ_WRITE= "root"; namespace { const std::string ADMIN_DBNAME = "admin"; @@ -41,248 +41,392 @@ namespace { const std::string BUILTIN_ROLE_READ_WRITE_ANY_DB = "readWriteAnyDatabase"; const std::string BUILTIN_ROLE_USER_ADMIN_ANY_DB = "userAdminAnyDatabase"; const std::string BUILTIN_ROLE_DB_ADMIN_ANY_DB = "dbAdminAnyDatabase"; + const std::string BUILTIN_ROLE_ROOT = "root"; + const std::string BUILTIN_ROLE_INTERNAL = "__system"; + const std::string BUILTIN_ROLE_DB_OWNER = "dbOwner"; - // ActionSets for the various built-in roles. These ActionSets contain all the actions that - // a user of each built-in role is granted. + /// Actions that the "read" role may perform on a normal resources of a specific database, and + /// that the "readAnyDatabase" role may perform on normal resources of any database. ActionSet readRoleActions; + + /// Actions that the "readWrite" role may perform on a normal resources of a specific database, + /// and that the "readWriteAnyDatabase" role may perform on normal resources of any database. ActionSet readWriteRoleActions; + + /// Actions that the "userAdmin" role may perform on normal resources of a specific database, + /// and that the "userAdminAnyDatabase" role may perform on normal resources of any database. ActionSet userAdminRoleActions; + + /// Actions that the "dbAdmin" role may perform on normal resources of a specific database, + // and that the "dbAdminAnyDatabase" role may perform on normal resources of any database. ActionSet dbAdminRoleActions; - ActionSet clusterAdminRoleActions; - // Can only be performed by internal connections. Nothing ever explicitly grants these actions, - // but they're included when calling addAllActions on an ActionSet, which is how internal - // connections are granted their privileges. - ActionSet internalActions; - // Old-style user roles - ActionSet compatibilityReadOnlyActions; - ActionSet compatibilityReadWriteActions; - ActionSet compatibilityReadOnlyAdminActions; - ActionSet compatibilityReadWriteAdminActions; + /// Actions that the clusterAdmin role may perform on the cluster resource. + ActionSet clusterAdminRoleClusterActions; + + /// Actions that the clusterAdmin role may perform on any database. + ActionSet clusterAdminRoleDatabaseActions; + + /// Actions that the "dbOwner" role may perform on normal resources of a specific database. + ActionSet dbOwnerRoleActions; + + ActionSet& operator<<(ActionSet& target, ActionType source) { + target.addAction(source); + return target; + } + + void operator+=(ActionSet& target, const ActionSet& source) { + target.addAllActionsFromSet(source); + } // This sets up the built-in role ActionSets. This is what determines what actions each role // is authorized to perform MONGO_INITIALIZER(AuthorizationBuiltinRoles)(InitializerContext* context) { // Read role - readRoleActions.addAction(ActionType::cloneCollectionLocalSource); - readRoleActions.addAction(ActionType::collStats); - readRoleActions.addAction(ActionType::dbHash); - readRoleActions.addAction(ActionType::dbStats); - readRoleActions.addAction(ActionType::find); - readRoleActions.addAction(ActionType::indexRead); - readRoleActions.addAction(ActionType::killCursors); + readRoleActions + << ActionType::cloneCollectionLocalSource + << ActionType::collStats + << ActionType::dbHash + << ActionType::dbStats + << ActionType::find + << ActionType::killCursors; // Read-write role - readWriteRoleActions.addAllActionsFromSet(readRoleActions); - readWriteRoleActions.addAction(ActionType::cloneCollectionTarget); - readWriteRoleActions.addAction(ActionType::convertToCapped); - readWriteRoleActions.addAction(ActionType::createCollection); // db admin gets this also - readWriteRoleActions.addAction(ActionType::dropCollection); - readWriteRoleActions.addAction(ActionType::dropIndexes); - readWriteRoleActions.addAction(ActionType::emptycapped); - readWriteRoleActions.addAction(ActionType::ensureIndex); - readWriteRoleActions.addAction(ActionType::insert); - readWriteRoleActions.addAction(ActionType::remove); - readWriteRoleActions.addAction(ActionType::renameCollectionSameDB); // db admin gets this also - readWriteRoleActions.addAction(ActionType::update); + readWriteRoleActions += readRoleActions; + readWriteRoleActions + << ActionType::cloneCollectionTarget + << ActionType::convertToCapped + << ActionType::createCollection // db admin gets this also + << ActionType::dropCollection + << ActionType::dropIndexes + << ActionType::emptycapped + << ActionType::ensureIndex + << ActionType::insert + << ActionType::remove + << ActionType::renameCollectionSameDB // db admin gets this also + << ActionType::update; // User admin role - userAdminRoleActions.addAction(ActionType::userAdmin); + userAdminRoleActions + << ActionType::userAdmin; // DB admin role - dbAdminRoleActions.addAction(ActionType::clean); - dbAdminRoleActions.addAction(ActionType::cloneCollectionLocalSource); - dbAdminRoleActions.addAction(ActionType::collMod); - dbAdminRoleActions.addAction(ActionType::collStats); - dbAdminRoleActions.addAction(ActionType::compact); - dbAdminRoleActions.addAction(ActionType::convertToCapped); - dbAdminRoleActions.addAction(ActionType::createCollection); // read_write gets this also - dbAdminRoleActions.addAction(ActionType::dbStats); - dbAdminRoleActions.addAction(ActionType::dropCollection); - dbAdminRoleActions.addAction(ActionType::dropIndexes); - dbAdminRoleActions.addAction(ActionType::ensureIndex); - dbAdminRoleActions.addAction(ActionType::indexRead); - dbAdminRoleActions.addAction(ActionType::indexStats); - dbAdminRoleActions.addAction(ActionType::profileEnable); - dbAdminRoleActions.addAction(ActionType::profileRead); - dbAdminRoleActions.addAction(ActionType::reIndex); - dbAdminRoleActions.addAction(ActionType::renameCollectionSameDB); // read_write gets this also - dbAdminRoleActions.addAction(ActionType::storageDetails); - dbAdminRoleActions.addAction(ActionType::validate); - - // We separate clusterAdmin read-only and read-write actions for backwards - // compatibility with old-style read-only admin users. This separation is not exposed to - // the user, and could go away once we stop supporting old-style privilege documents. - ActionSet clusterAdminRoleReadActions; - ActionSet clusterAdminRoleWriteActions; - - // Cluster admin role - clusterAdminRoleReadActions.addAction(ActionType::connPoolStats); - clusterAdminRoleReadActions.addAction(ActionType::connPoolSync); - clusterAdminRoleReadActions.addAction(ActionType::getCmdLineOpts); - clusterAdminRoleReadActions.addAction(ActionType::getLog); - clusterAdminRoleReadActions.addAction(ActionType::getParameter); - clusterAdminRoleReadActions.addAction(ActionType::getShardMap); - clusterAdminRoleReadActions.addAction(ActionType::getShardVersion); - clusterAdminRoleReadActions.addAction(ActionType::hostInfo); - clusterAdminRoleReadActions.addAction(ActionType::listDatabases); - clusterAdminRoleReadActions.addAction(ActionType::listShards); - clusterAdminRoleReadActions.addAction(ActionType::logRotate); - clusterAdminRoleReadActions.addAction(ActionType::netstat); - clusterAdminRoleReadActions.addAction(ActionType::replSetFreeze); - clusterAdminRoleReadActions.addAction(ActionType::replSetGetStatus); - clusterAdminRoleReadActions.addAction(ActionType::replSetMaintenance); - clusterAdminRoleReadActions.addAction(ActionType::replSetStepDown); - clusterAdminRoleReadActions.addAction(ActionType::replSetSyncFrom); - clusterAdminRoleReadActions.addAction(ActionType::setParameter); - clusterAdminRoleReadActions.addAction(ActionType::setShardVersion); // TODO: should this be internal? - clusterAdminRoleReadActions.addAction(ActionType::serverStatus); - clusterAdminRoleReadActions.addAction(ActionType::splitVector); - // Shutdown is in read actions b/c that's how it was in 2.2 - clusterAdminRoleReadActions.addAction(ActionType::shutdown); - clusterAdminRoleReadActions.addAction(ActionType::top); - clusterAdminRoleReadActions.addAction(ActionType::touch); - clusterAdminRoleReadActions.addAction(ActionType::unlock); - clusterAdminRoleReadActions.addAction(ActionType::unsetSharding); - clusterAdminRoleReadActions.addAction(ActionType::writeBacksQueued); - - clusterAdminRoleWriteActions.addAction(ActionType::addShard); - clusterAdminRoleWriteActions.addAction(ActionType::cleanupOrphaned); - clusterAdminRoleWriteActions.addAction(ActionType::closeAllDatabases); - clusterAdminRoleWriteActions.addAction(ActionType::cpuProfiler); - clusterAdminRoleWriteActions.addAction(ActionType::cursorInfo); - clusterAdminRoleWriteActions.addAction(ActionType::diagLogging); - clusterAdminRoleWriteActions.addAction(ActionType::dropDatabase); // TODO: Should there be a CREATE_DATABASE also? - clusterAdminRoleWriteActions.addAction(ActionType::enableSharding); - clusterAdminRoleWriteActions.addAction(ActionType::flushRouterConfig); - clusterAdminRoleWriteActions.addAction(ActionType::fsync); - clusterAdminRoleWriteActions.addAction(ActionType::inprog); - clusterAdminRoleWriteActions.addAction(ActionType::killop); - clusterAdminRoleWriteActions.addAction(ActionType::mergeChunks); - clusterAdminRoleWriteActions.addAction(ActionType::moveChunk); - clusterAdminRoleWriteActions.addAction(ActionType::movePrimary); - clusterAdminRoleWriteActions.addAction(ActionType::removeShard); - clusterAdminRoleWriteActions.addAction(ActionType::repairDatabase); - clusterAdminRoleWriteActions.addAction(ActionType::replSetInitiate); - clusterAdminRoleWriteActions.addAction(ActionType::replSetReconfig); - clusterAdminRoleWriteActions.addAction(ActionType::resync); - clusterAdminRoleWriteActions.addAction(ActionType::shardCollection); - clusterAdminRoleWriteActions.addAction(ActionType::shardingState); - clusterAdminRoleWriteActions.addAction(ActionType::split); - clusterAdminRoleWriteActions.addAction(ActionType::splitChunk); - - clusterAdminRoleActions.addAllActionsFromSet(clusterAdminRoleReadActions); - clusterAdminRoleActions.addAllActionsFromSet(clusterAdminRoleWriteActions); - clusterAdminRoleActions.addAction(ActionType::killCursors); - - // Old-style user actions, for backwards compatibility - compatibilityReadOnlyActions.addAllActionsFromSet(readRoleActions); - - compatibilityReadWriteActions.addAllActionsFromSet(readWriteRoleActions); - compatibilityReadWriteActions.addAllActionsFromSet(dbAdminRoleActions); - compatibilityReadWriteActions.addAllActionsFromSet(userAdminRoleActions); - compatibilityReadWriteActions.addAction(ActionType::clone); - compatibilityReadWriteActions.addAction(ActionType::copyDBTarget); - compatibilityReadWriteActions.addAction(ActionType::dropDatabase); - compatibilityReadWriteActions.addAction(ActionType::repairDatabase); - - compatibilityReadOnlyAdminActions.addAllActionsFromSet(compatibilityReadOnlyActions); - compatibilityReadOnlyAdminActions.addAllActionsFromSet(clusterAdminRoleReadActions); - - compatibilityReadWriteAdminActions.addAllActionsFromSet(compatibilityReadWriteActions); - compatibilityReadWriteAdminActions.addAllActionsFromSet(compatibilityReadOnlyAdminActions); - compatibilityReadWriteAdminActions.addAllActionsFromSet(clusterAdminRoleWriteActions); - - // Internal commands - internalActions.addAction(ActionType::clone); - internalActions.addAction(ActionType::handshake); - internalActions.addAction(ActionType::mapReduceShardedFinish); - internalActions.addAction(ActionType::replSetElect); - internalActions.addAction(ActionType::replSetFresh); - internalActions.addAction(ActionType::replSetGetRBID); - internalActions.addAction(ActionType::replSetHeartbeat); - internalActions.addAction(ActionType::writebacklisten); - internalActions.addAction(ActionType::userAdminV1); - internalActions.addAction(ActionType::_migrateClone); - internalActions.addAction(ActionType::_recvChunkAbort); - internalActions.addAction(ActionType::_recvChunkCommit); - internalActions.addAction(ActionType::_recvChunkStart); - internalActions.addAction(ActionType::_recvChunkStatus); - internalActions.addAction(ActionType::_transferMods); + dbAdminRoleActions + << ActionType::clean + << ActionType::cloneCollectionLocalSource + << ActionType::collMod + << ActionType::collStats + << ActionType::compact + << ActionType::convertToCapped + << ActionType::createCollection // read_write gets this also + << ActionType::dbStats + << ActionType::dropCollection + << ActionType::dropIndexes + << ActionType::ensureIndex + << ActionType::indexStats + << ActionType::profileEnable + << ActionType::reIndex + << ActionType::renameCollectionSameDB // read_write gets this also + << ActionType::storageDetails + << ActionType::validate; + + // Cluster admin role actions that target the cluster resource. + clusterAdminRoleClusterActions + << ActionType::connPoolStats + << ActionType::connPoolSync + << ActionType::getCmdLineOpts + << ActionType::getLog + << ActionType::getParameter + << ActionType::getShardMap + << ActionType::getShardVersion + << ActionType::hostInfo + << ActionType::listDatabases + << ActionType::listShards + << ActionType::logRotate + << ActionType::netstat + << ActionType::replSetFreeze + << ActionType::replSetGetStatus + << ActionType::replSetMaintenance + << ActionType::replSetStepDown + << ActionType::replSetSyncFrom + << ActionType::setParameter + << ActionType::setShardVersion // TODO: should this be internal? + << ActionType::serverStatus + << ActionType::splitVector + << ActionType::shutdown + << ActionType::top + << ActionType::touch + << ActionType::unlock + << ActionType::unsetSharding + << ActionType::writeBacksQueued + << ActionType::addShard + << ActionType::cleanupOrphaned + << ActionType::closeAllDatabases + << ActionType::cpuProfiler + << ActionType::cursorInfo + << ActionType::diagLogging + << ActionType::enableSharding + << ActionType::flushRouterConfig + << ActionType::fsync + << ActionType::inprog + << ActionType::killop + << ActionType::mergeChunks + << ActionType::moveChunk + << ActionType::movePrimary + << ActionType::removeShard + << ActionType::repairDatabase + << ActionType::replSetInitiate + << ActionType::replSetReconfig + << ActionType::resync + << ActionType::shardCollection + << ActionType::shardingState + << ActionType::split + << ActionType::splitChunk; + + clusterAdminRoleDatabaseActions + << ActionType::dropDatabase + << ActionType::killCursors; + + // Database-owner role database actions. + dbOwnerRoleActions += readWriteRoleActions; + dbOwnerRoleActions += dbAdminRoleActions; + dbOwnerRoleActions += userAdminRoleActions; + dbOwnerRoleActions + << ActionType::clone + << ActionType::copyDBTarget + << ActionType::dropDatabase + << ActionType::repairDatabase; return Status::OK(); } + void addReadOnlyDbPrivileges(PrivilegeVector* privileges, const StringData& dbName) { + Privilege::addPrivilegeToPrivilegeVector( + privileges, Privilege(ResourcePattern::forDatabaseName(dbName), readRoleActions)); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forExactNamespace( + NamespaceString(dbName, "system.indexes")), + readRoleActions)); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forExactNamespace(NamespaceString(dbName, "system.js")), + readRoleActions)); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forExactNamespace( + NamespaceString(dbName, "system.namespaces")), + readRoleActions)); + } + + void addReadWriteDbPrivileges(PrivilegeVector* privileges, const StringData& dbName) { + addReadOnlyDbPrivileges(privileges, dbName); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forDatabaseName(dbName), readWriteRoleActions)); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forExactNamespace(NamespaceString(dbName, "system.js")), + readWriteRoleActions)); + } + + void addUserAdminDbPrivileges(PrivilegeVector* privileges, const StringData& dbName) { + privileges->push_back( + Privilege(ResourcePattern::forDatabaseName(dbName), userAdminRoleActions)); + } + + void addDbAdminDbPrivileges(PrivilegeVector* privileges, const StringData& dbName) { + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forDatabaseName(dbName), dbAdminRoleActions)); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forExactNamespace( + NamespaceString(dbName, "system.indexes")), + readRoleActions)); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forExactNamespace( + NamespaceString(dbName, "system.namespaces")), + readRoleActions)); + ActionSet profileActions = readRoleActions; + profileActions.addAction(ActionType::dropCollection); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forExactNamespace( + NamespaceString(dbName, "system.profile")), + profileActions)); + } + + void addDbOwnerPrivileges(PrivilegeVector* privileges, const StringData& dbName) { + + addReadWriteDbPrivileges(privileges, dbName); + addDbAdminDbPrivileges(privileges, dbName); + addUserAdminDbPrivileges(privileges, dbName); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forDatabaseName(dbName), dbOwnerRoleActions)); + } + + + void addReadOnlyAnyDbPrivileges(PrivilegeVector* privileges) { + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forAnyNormalResource(), readRoleActions)); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forClusterResource(), ActionType::listDatabases)); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forCollectionName("system.indexes"), + readRoleActions)); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forCollectionName("system.js"), + readRoleActions)); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forCollectionName("system.namespaces"), + readRoleActions)); + } + + void addReadWriteAnyDbPrivileges(PrivilegeVector* privileges) { + addReadOnlyAnyDbPrivileges(privileges); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forAnyNormalResource(), readWriteRoleActions)); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forCollectionName("system.js"), readWriteRoleActions)); + } + + void addUserAdminAnyDbPrivileges(PrivilegeVector* privileges) { + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forAnyNormalResource(), userAdminRoleActions)); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forCollectionName("system.roles"), + readRoleActions)); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forCollectionName("system.users"), + readRoleActions)); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forExactNamespace( + NamespaceString("admin.system.version")), + readRoleActions)); + } + + void addDbAdminAnyDbPrivileges(PrivilegeVector* privileges) { + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forAnyNormalResource(), dbAdminRoleActions)); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forCollectionName("system.indexes"), + readRoleActions)); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forCollectionName("system.namespaces"), + readRoleActions)); + ActionSet profileActions = readRoleActions; + profileActions.addAction(ActionType::dropCollection); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forCollectionName("system.profile"), + profileActions)); + } + + void addClusterAdminPrivileges(PrivilegeVector* privileges) { + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forClusterResource(), clusterAdminRoleClusterActions)); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forAnyNormalResource(), + clusterAdminRoleDatabaseActions)); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forExactNamespace(NamespaceString("local", + "system.replset")), + readRoleActions)); + } + + void addRootRolePrivileges(PrivilegeVector* privileges) { + addClusterAdminPrivileges(privileges); + addUserAdminAnyDbPrivileges(privileges); + addDbAdminAnyDbPrivileges(privileges); + addReadWriteAnyDbPrivileges(privileges); + Privilege::addPrivilegeToPrivilegeVector( + privileges, + Privilege(ResourcePattern::forAnyNormalResource(), dbOwnerRoleActions)); + } + + void addInternalRolePrivileges(PrivilegeVector* privileges) { + RoleGraph::generateUniversalPrivileges(privileges); + } + /** * Returns the privilege that corresponds with the given built-in role. */ - Privilege getPrivilegeForBuiltinRole(const RoleName& roleName) { + PrivilegeVector getPrivilegesForBuiltinRole(const RoleName& roleName) { + PrivilegeVector result; const bool isAdminDB = (roleName.getDB() == ADMIN_DBNAME); if (roleName.getRole() == BUILTIN_ROLE_READ) { - return Privilege(ResourcePattern::forDatabaseName(roleName.getDB()), readRoleActions); + addReadOnlyDbPrivileges(&result, roleName.getDB()); } - if (roleName.getRole() == BUILTIN_ROLE_READ_WRITE) { - return Privilege(ResourcePattern::forDatabaseName(roleName.getDB()), - readWriteRoleActions); + else if (roleName.getRole() == BUILTIN_ROLE_READ_WRITE) { + addReadWriteDbPrivileges(&result, roleName.getDB()); } - if (roleName.getRole() == BUILTIN_ROLE_USER_ADMIN) { - return Privilege(ResourcePattern::forDatabaseName(roleName.getDB()), - userAdminRoleActions); + else if (roleName.getRole() == BUILTIN_ROLE_USER_ADMIN) { + addUserAdminDbPrivileges(&result, roleName.getDB()); } - if (roleName.getRole() == BUILTIN_ROLE_DB_ADMIN) { - return Privilege(ResourcePattern::forDatabaseName(roleName.getDB()), - dbAdminRoleActions); + else if (roleName.getRole() == BUILTIN_ROLE_DB_ADMIN) { + addDbAdminDbPrivileges(&result, roleName.getDB()); } - if (roleName.getRole() == RoleGraph::BUILTIN_ROLE_V0_READ) { - return Privilege(ResourcePattern::forDatabaseName(roleName.getDB()), - compatibilityReadOnlyActions); + else if (roleName.getRole() == BUILTIN_ROLE_DB_OWNER) { + addDbOwnerPrivileges(&result, roleName.getDB()); } - if (roleName.getRole() == RoleGraph::BUILTIN_ROLE_V0_READ_WRITE) { - return Privilege(ResourcePattern::forDatabaseName(roleName.getDB()), - compatibilityReadWriteActions); + else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_READ_ANY_DB) { + addReadOnlyAnyDbPrivileges(&result); } - if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_READ_ANY_DB) { - return Privilege(ResourcePattern::forAnyResource(), readRoleActions); + else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_READ_WRITE_ANY_DB) { + addReadWriteAnyDbPrivileges(&result); } - if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_READ_WRITE_ANY_DB) { - return Privilege(ResourcePattern::forAnyResource(), readWriteRoleActions); + else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_USER_ADMIN_ANY_DB) { + addUserAdminAnyDbPrivileges(&result); } - if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_USER_ADMIN_ANY_DB) { - return Privilege(ResourcePattern::forAnyResource(), userAdminRoleActions); + else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_DB_ADMIN_ANY_DB) { + addDbAdminAnyDbPrivileges(&result); } - if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_DB_ADMIN_ANY_DB) { - return Privilege(ResourcePattern::forAnyResource(), dbAdminRoleActions); + else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_CLUSTER_ADMIN) { + addClusterAdminPrivileges(&result); } - if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_CLUSTER_ADMIN) { - return Privilege(ResourcePattern::forAnyResource(), clusterAdminRoleActions); + else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_ROOT) { + addRootRolePrivileges(&result); } - if (isAdminDB && roleName.getRole() == RoleGraph::BUILTIN_ROLE_V0_ADMIN_READ) { - return Privilege(ResourcePattern::forAnyResource(), - compatibilityReadOnlyAdminActions); + else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_INTERNAL) { + addInternalRolePrivileges(&result); } - if (isAdminDB && roleName.getRole() == RoleGraph::BUILTIN_ROLE_V0_ADMIN_READ_WRITE) { - return Privilege(ResourcePattern::forAnyResource(), - compatibilityReadWriteAdminActions); + else { + fassertFailed(0); } - - fassertFailed(17116); + return result; } } // namespace - ActionSet RoleGraph::getAllUserActions() { + void RoleGraph::generateUniversalPrivileges(PrivilegeVector* privileges) { ActionSet allActions; - allActions.addAllActionsFromSet(readRoleActions); - allActions.addAllActionsFromSet(readWriteRoleActions); - allActions.addAllActionsFromSet(userAdminRoleActions); - allActions.addAllActionsFromSet(dbAdminRoleActions); - allActions.addAllActionsFromSet(clusterAdminRoleActions); - return allActions; + allActions.addAllActions(); + privileges->push_back(Privilege(ResourcePattern::forAnyResource(), allActions)); } bool RoleGraph::_isBuiltinRole(const RoleName& role) { - bool isAdminDB = role.getDB() == "admin"; + bool isAdminDB = role.getDB() == ADMIN_DBNAME; if (role.getRole() == BUILTIN_ROLE_READ) { return true; @@ -296,10 +440,7 @@ namespace { else if (role.getRole() == BUILTIN_ROLE_DB_ADMIN) { return true; } - else if (role.getRole() == BUILTIN_ROLE_V0_READ) { - return true; - } - else if (role.getRole() == BUILTIN_ROLE_V0_READ_WRITE) { + else if (role.getRole() == BUILTIN_ROLE_DB_OWNER) { return true; } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_READ_ANY_DB) { @@ -317,10 +458,10 @@ namespace { else if (isAdminDB && role.getRole() == BUILTIN_ROLE_CLUSTER_ADMIN) { return true; } - else if (isAdminDB && role.getRole() == BUILTIN_ROLE_V0_ADMIN_READ) { + else if (isAdminDB && role.getRole() == BUILTIN_ROLE_ROOT) { return true; } - else if (isAdminDB && role.getRole() == BUILTIN_ROLE_V0_ADMIN_READ_WRITE) { + else if (isAdminDB && role.getRole() == BUILTIN_ROLE_INTERNAL) { return true; } @@ -333,9 +474,11 @@ namespace { } _createRoleDontCheckIfRoleExists(role); - Privilege privilege = getPrivilegeForBuiltinRole(role); - _addPrivilegeToRoleNoChecks(role, privilege); - _allPrivilegesForRole[role].push_back(privilege); + PrivilegeVector privileges = getPrivilegesForBuiltinRole(role); + for (size_t i = 0; i < privileges.size(); ++i) { + _addPrivilegeToRoleNoChecks(role, privileges[i]); + _allPrivilegesForRole[role].push_back(privileges[i]); + } } } // namespace mongo diff --git a/src/mongo/db/auth/role_graph_test.cpp b/src/mongo/db/auth/role_graph_test.cpp index 27afc61b7ac..5d719779a90 100644 --- a/src/mongo/db/auth/role_graph_test.cpp +++ b/src/mongo/db/auth/role_graph_test.cpp @@ -167,6 +167,16 @@ namespace { 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) { @@ -178,20 +188,20 @@ namespace { // Test adding a single privilege ActionSet actions; actions.addAction(ActionType::find); - ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(ResourcePattern::forDatabaseName("dbA"), actions))); + ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(dbAResource, actions))); PrivilegeVector privileges = graph.getDirectPrivileges(roleA); ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbA"), privileges[0].getResourcePattern()); + 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(ResourcePattern::forExactNamespace(NamespaceString("dbA.foo")), actions))); + ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(collectionAFooResource, actions))); privileges = graph.getDirectPrivileges(roleA); ASSERT_EQUALS(static_cast<size_t>(2), privileges.size()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbA"), privileges[0].getResourcePattern()); + ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); ASSERT_EQUALS(actions.toString(), privileges[0].getActions().toString()); - ASSERT_EQUALS(ResourcePattern::forExactNamespace(NamespaceString("dbA.foo")), privileges[1].getResourcePattern()); + ASSERT_EQUALS(collectionAFooResource, privileges[1].getResourcePattern()); ASSERT_EQUALS(actions.toString(), privileges[1].getActions().toString()); @@ -200,24 +210,24 @@ namespace { actions.addAction(ActionType::insert); PrivilegeVector privilegesToAdd; - privilegesToAdd.push_back(Privilege(ResourcePattern::forDatabaseName("dbA"), actions)); + privilegesToAdd.push_back(Privilege(dbAResource, actions)); actions.removeAllActions(); actions.addAction(ActionType::update); - privilegesToAdd.push_back(Privilege(ResourcePattern::forDatabaseName("dbA"), actions)); + 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(ResourcePattern::forDatabaseName("dbA"), privileges[0].getResourcePattern()); + 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(ResourcePattern::forExactNamespace(NamespaceString("dbA.foo")), privileges[1].getResourcePattern()); + ASSERT_EQUALS(collectionAFooResource, privileges[1].getResourcePattern()); ASSERT_EQUALS(actions.toString(), privileges[1].getActions().toString()); } @@ -283,15 +293,15 @@ namespace { ASSERT_OK(graph.createRole(roleB)); ASSERT_OK(graph.createRole(roleC)); - ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(ResourcePattern::forDatabaseName("dbA"), actions))); - ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(ResourcePattern::forDatabaseName("dbB"), actions))); - ASSERT_OK(graph.addPrivilegeToRole(roleC, Privilege(ResourcePattern::forDatabaseName("dbC"), actions))); + 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(ResourcePattern::forDatabaseName("dbA"), privileges[0].getResourcePattern()); + 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. @@ -301,8 +311,8 @@ namespace { ASSERT_OK(graph.recomputePrivilegeData()); privileges = graph.getAllPrivileges(roleA); ASSERT_EQUALS(static_cast<size_t>(2), privileges.size()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbA"), privileges[0].getResourcePattern()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbB"), privileges[1].getResourcePattern()); + 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)); @@ -310,36 +320,31 @@ namespace { ASSERT_OK(graph.recomputePrivilegeData()); privileges = graph.getAllPrivileges(roleA); ASSERT_EQUALS(static_cast<size_t>(3), privileges.size()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbA"), privileges[0].getResourcePattern()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbB"), privileges[1].getResourcePattern()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbC"), privileges[2].getResourcePattern()); + 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(ResourcePattern::forDatabaseName("dbB"), privileges[0].getResourcePattern()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbC"), privileges[1].getResourcePattern()); + 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); - ASSERT_EQUALS(static_cast<size_t>(4), privileges.size()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbA"), privileges[0].getResourcePattern()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbB"), privileges[1].getResourcePattern()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbC"), privileges[2].getResourcePattern()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbD"), privileges[3].getResourcePattern()); + 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(static_cast<size_t>(3), privileges.size()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbB"), privileges[0].getResourcePattern()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbC"), privileges[1].getResourcePattern()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbD"), privileges[2].getResourcePattern()); + ASSERT_EQUALS(readWriteRolePrivilegeCount + 2, privileges.size()); + ASSERT_EQUALS(dbBResource, privileges[0].getResourcePattern()); + ASSERT_EQUALS(dbCResource, privileges[1].getResourcePattern()); privileges = graph.getAllPrivileges(roleC); - ASSERT_EQUALS(static_cast<size_t>(2), privileges.size()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbC"), privileges[0].getResourcePattern()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbD"), privileges[1].getResourcePattern()); - privileges = graph.getAllPrivileges(roleD); - ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbD"), privileges[0].getResourcePattern()); + 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 @@ -348,32 +353,29 @@ namespace { ASSERT_OK(graph.recomputePrivilegeData()); privileges = graph.getAllPrivileges(roleA); ASSERT_EQUALS(static_cast<size_t>(2), privileges.size()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbA"), privileges[0].getResourcePattern()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbB"), privileges[1].getResourcePattern()); + 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(ResourcePattern::forDatabaseName("dbB"), privileges[0].getResourcePattern()); + ASSERT_EQUALS(dbBResource, privileges[0].getResourcePattern()); privileges = graph.getAllPrivileges(roleC); - ASSERT_EQUALS(static_cast<size_t>(2), privileges.size()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbC"), privileges[0].getResourcePattern()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbD"), privileges[1].getResourcePattern()); + ASSERT_EQUALS(readWriteRolePrivilegeCount + 1, privileges.size()); + ASSERT_EQUALS(dbCResource, privileges[0].getResourcePattern()); privileges = graph.getAllPrivileges(roleD); - ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbD"), privileges[0].getResourcePattern()); + 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(ResourcePattern::forDatabaseName("dbA"), privileges[0].getResourcePattern()); + ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); privileges = graph.getDirectPrivileges(roleB); ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbB"), privileges[0].getResourcePattern()); + ASSERT_EQUALS(dbBResource, privileges[0].getResourcePattern()); privileges = graph.getDirectPrivileges(roleC); ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbC"), privileges[0].getResourcePattern()); + ASSERT_EQUALS(dbCResource, privileges[0].getResourcePattern()); privileges = graph.getDirectPrivileges(roleD); - ASSERT_EQUALS(static_cast<size_t>(1), privileges.size()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbD"), privileges[0].getResourcePattern()); + ASSERT_EQUALS(readWriteRolePrivilegeCount, privileges.size()); } // Test that if you grant 1 role to another, then remove it and change it's privileges, then @@ -393,9 +395,9 @@ namespace { ASSERT_OK(graph.createRole(roleB)); ASSERT_OK(graph.createRole(roleC)); - ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(ResourcePattern::forDatabaseName("db"), actionsA))); - ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(ResourcePattern::forDatabaseName("db"), actionsB))); - ASSERT_OK(graph.addPrivilegeToRole(roleC, Privilege(ResourcePattern::forDatabaseName("db"), actionsC))); + 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 @@ -425,7 +427,7 @@ namespace { ASSERT_OK(graph.removeAllPrivilegesFromRole(roleB)); ActionSet newActionsB; newActionsB.addAction(ActionType::remove); - ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(ResourcePattern::forDatabaseName("db"), newActionsB))); + 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)); @@ -456,7 +458,7 @@ namespace { ASSERT_OK(graph.createRole(roleB)); actionsB.removeAllActions(); actionsB.addAction(ActionType::shutdown); - ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(ResourcePattern::forDatabaseName("db"), actionsB))); + ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbResource, actionsB))); ASSERT_OK(graph.addRoleToRole(roleA, roleB)); ASSERT_OK(graph.recomputePrivilegeData()); @@ -482,9 +484,9 @@ namespace { ActionSet actions; actions.addAction(ActionType::find); - ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(ResourcePattern::forDatabaseName("dbA"), actions))); - ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(ResourcePattern::forDatabaseName("dbB"), actions))); - ASSERT_OK(graph.addPrivilegeToRole(roleC, Privilege(ResourcePattern::forDatabaseName("dbC"), actions))); + 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)); @@ -505,9 +507,9 @@ namespace { 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(ResourcePattern::forDatabaseName("dbA"), privileges[0].getResourcePattern()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbB"), privileges[1].getResourcePattern()); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbC"), privileges[2].getResourcePattern()); + ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); + ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern()); + ASSERT_EQUALS(dbCResource, privileges[2].getResourcePattern()); } // Tests error handling @@ -518,8 +520,8 @@ namespace { ActionSet actions; actions.addAction(ActionType::find); - Privilege privilege1(ResourcePattern::forDatabaseName("db1"), actions); - Privilege privilege2(ResourcePattern::forDatabaseName("db2"), actions); + Privilege privilege1(db1Resource, actions); + Privilege privilege2(db2Resource, actions); PrivilegeVector privileges; privileges.push_back(privilege1); privileges.push_back(privilege2); @@ -570,7 +572,7 @@ namespace { ActionSet actions; actions.addAction(ActionType::insert); - Privilege privilege(ResourcePattern::forDatabaseName("dbA"), actions); + Privilege privilege(dbAResource, actions); RoleGraph graph; @@ -595,18 +597,53 @@ namespace { ASSERT_EQUALS(1U, privileges.size()); ASSERT(privileges[0].getActions().equals(actions)); ASSERT(!privileges[0].getActions().contains(ActionType::find)); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbA"), privileges[0].getResourcePattern()); + ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern()); privileges = graph.getAllPrivileges(userRole); - ASSERT_EQUALS(1U, privileges.size()); - ASSERT(privileges[0].getActions().isSupersetOf(actions)); - ASSERT(privileges[0].getActions().contains(ActionType::insert)); - ASSERT(privileges[0].getActions().contains(ActionType::find)); - ASSERT_EQUALS(ResourcePattern::forDatabaseName("dbA"), privileges[0].getResourcePattern()); + 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"))); + } + } // namespace } // namespace mongo diff --git a/src/mongo/db/auth/user_document_parser_test.cpp b/src/mongo/db/auth/user_document_parser_test.cpp index 023bdc5b566..e61f7d91b3f 100644 --- a/src/mongo/db/auth/user_document_parser_test.cpp +++ b/src/mongo/db/auth/user_document_parser_test.cpp @@ -60,25 +60,25 @@ namespace { ASSERT_OK(v1parser.initializeUserRolesFromUserDocument( user.get(), readOnly, "test")); ASSERT_EQUALS(1U, user->getRoles().size()); - ASSERT_EQUALS(1U, user->getRoles().count(RoleName("oldRead", "test"))); + ASSERT_EQUALS(1U, user->getRoles().count(RoleName("read", "test"))); resetUsers(); ASSERT_OK(v1parser.initializeUserRolesFromUserDocument( user.get(), readWrite, "test")); ASSERT_EQUALS(1U, user->getRoles().size()); - ASSERT_EQUALS(1U, user->getRoles().count(RoleName("oldReadWrite", "test"))); + ASSERT_EQUALS(1U, user->getRoles().count(RoleName("dbOwner", "test"))); resetUsers(); ASSERT_OK(v1parser.initializeUserRolesFromUserDocument( adminUser.get(), readOnlyAdmin, "admin")); ASSERT_EQUALS(1U, adminUser->getRoles().size()); - ASSERT_EQUALS(1U, adminUser->getRoles().count(RoleName("oldAdminRead", "admin"))); + ASSERT_EQUALS(1U, adminUser->getRoles().count(RoleName("readAnyDatabase", "admin"))); resetUsers(); ASSERT_OK(v1parser.initializeUserRolesFromUserDocument( adminUser.get(), readWriteAdmin, "admin")); ASSERT_EQUALS(1U, adminUser->getRoles().size()); - ASSERT_EQUALS(1U, adminUser->getRoles().count(RoleName("oldAdminReadWrite", "admin"))); + ASSERT_EQUALS(1U, adminUser->getRoles().count(RoleName("root", "admin"))); } TEST_F(V1UserDocumentParsing, VerifyRolesFieldMustBeAnArray) { diff --git a/src/mongo/db/commands/apply_ops.cpp b/src/mongo/db/commands/apply_ops.cpp index 0b207c529f3..1f1503fe002 100644 --- a/src/mongo/db/commands/apply_ops.cpp +++ b/src/mongo/db/commands/apply_ops.cpp @@ -55,8 +55,7 @@ namespace mongo { const BSONObj& cmdObj, std::vector<Privilege>* out) { // applyOps can do pretty much anything, so require all privileges. - out->push_back(Privilege(ResourcePattern::forAnyResource(), - getGlobalAuthorizationManager()->getAllUserActions())); + RoleGraph::generateUniversalPrivileges(out); } virtual bool run(const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) { diff --git a/src/mongo/db/dbeval.cpp b/src/mongo/db/dbeval.cpp index b02ded46776..af190d082c3 100644 --- a/src/mongo/db/dbeval.cpp +++ b/src/mongo/db/dbeval.cpp @@ -137,8 +137,7 @@ namespace mongo { const BSONObj& cmdObj, std::vector<Privilege>* out) { - out->push_back(Privilege(ResourcePattern::forAnyResource(), - getGlobalAuthorizationManager()->getAllUserActions())); + RoleGraph::generateUniversalPrivileges(out); } CmdEval() : Command("eval", false, "$eval") { } bool run(const string& dbname , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) { diff --git a/src/mongo/s/commands_public.cpp b/src/mongo/s/commands_public.cpp index 1dadc1bf0e7..f10ba91cab7 100644 --- a/src/mongo/s/commands_public.cpp +++ b/src/mongo/s/commands_public.cpp @@ -1724,8 +1724,7 @@ namespace mongo { const BSONObj& cmdObj, std::vector<Privilege>* out) { // applyOps can do pretty much anything, so require all privileges. - out->push_back(Privilege(ResourcePattern::forAnyResource(), - getGlobalAuthorizationManager()->getAllUserActions())); + RoleGraph::generateUniversalPrivileges(out); } virtual bool run(const string& dbName , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool) { errmsg = "applyOps not allowed through mongos"; @@ -1757,8 +1756,7 @@ namespace mongo { const BSONObj& cmdObj, std::vector<Privilege>* out) { // $eval can do pretty much anything, so require all privileges. - out->push_back(Privilege(ResourcePattern::forAnyResource(), - getGlobalAuthorizationManager()->getAllUserActions())); + RoleGraph::generateUniversalPrivileges(out); } virtual bool run(const string& dbName, BSONObj& cmdObj, diff --git a/src/mongo/shell/utils.js b/src/mongo/shell/utils.js index d45591e4fd3..f62febac867 100644 --- a/src/mongo/shell/utils.js +++ b/src/mongo/shell/utils.js @@ -403,11 +403,8 @@ jsTest.options = jsTestOptions jsTest.setOption = setJsTestOption jsTest.log = jsTestLog jsTest.readOnlyUserRoles = ["read"] -jsTest.basicUserRoles = ["readWrite", "dbAdmin", "userAdmin"] -jsTest.adminUserRoles = ["clusterAdmin", - "userAdminAnyDatabase", - "dbAdminAnyDatabase", - "readWriteAnyDatabase"] +jsTest.basicUserRoles = ["dbOwner"] +jsTest.adminUserRoles = ["root"] jsTest.dir = function(){ return jsTest.path().replace( /\/[^\/]+$/, "/" ) @@ -436,7 +433,7 @@ jsTest.addAuth = function(conn) { } print ("Adding admin user on connection: " + localconn); return localconn.getDB('admin').addUser(jsTestOptions().adminUser, jsTestOptions().adminPassword, - jsTest.adminUserRoles, 'majority', 60000); + ["__system"], 'majority', 60000); } jsTest.authenticate = function(conn) { |