diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/auth/authorization_manager.cpp | 74 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_manager.h | 19 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_manager_test.cpp | 97 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state.h | 12 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_d.cpp | 13 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_d.h | 4 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_mock.cpp | 36 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_mock.h | 8 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_s.cpp | 13 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_s.h | 4 |
10 files changed, 272 insertions, 8 deletions
diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp index 6da4fceaa81..6f24d562c1b 100644 --- a/src/mongo/db/auth/authorization_manager.cpp +++ b/src/mongo/db/auth/authorization_manager.cpp @@ -33,6 +33,7 @@ #include "mongo/db/auth/user_name_hash.h" #include "mongo/db/jsobj.h" #include "mongo/platform/unordered_map.h" +#include "mongo/util/map_util.h" #include "mongo/util/mongoutils/str.h" namespace mongo { @@ -694,7 +695,10 @@ namespace { boost::lock_guard<boost::mutex> lk(_lock); user->decrementRefCount(); if (user->getRefCount() == 0) { - _userCache.erase(user->getName()); + // If it's been invalidated then it's not in the _userCache anymore. + if (user->isValid()) { + _userCache.erase(user->getName()); + } delete user; } } @@ -806,4 +810,72 @@ namespace { _initializeUserPrivilegesFromRoles(user); return Status::OK(); } + + void AuthorizationManager::_invalidateUserCache_inlock() { + for (unordered_map<UserName, User*>::iterator it = _userCache.begin(); + it != _userCache.end(); ++it) { + it->second->invalidate(); + } + _userCache.clear(); + } + + Status AuthorizationManager::initilizeAllV1UserData() { + boost::lock_guard<boost::mutex> lk(_lock); + _invalidateUserCache_inlock(); + + try { + std::vector<std::string> dbNames; + _externalState->getAllDatabaseNames(&dbNames); + + for (std::vector<std::string>::iterator dbIt = dbNames.begin(); + dbIt != dbNames.end(); ++dbIt) { + std::string dbname = *dbIt; + std::vector<BSONObj> privDocs = _externalState->getAllV1PrivilegeDocsForDB(dbname); + + for (std::vector<BSONObj>::iterator docIt = privDocs.begin(); + docIt != privDocs.end(); ++docIt) { + const BSONObj& privDoc = *docIt; + + std::string source; + if (privDoc.hasField("userSource")) { + source = privDoc["userSource"].String(); + } else { + source = dbname; + } + UserName userName(privDoc["user"].String(), source); + + User* user = mapFindWithDefault(_userCache, userName, static_cast<User*>(NULL)); + if (!user) { + user = new User(userName); + // Make sure the user always has a refCount of at least 1 so it's + // effectively "pinned" and will never be removed from the _userCache + // unless the whole cache is invalidated. + user->incrementRefCount(); + _userCache.insert(make_pair(userName, user)); + } + + if (source == dbname || source == "$external") { + Status status = _initializeUserCredentialsFromPrivilegeDocument(user, + privDoc); + if (!status.isOK()) { + return status; + } + } + Status status = _initializeUserRolesFromPrivilegeDocument(user, + privDoc, + dbname); + if (!status.isOK()) { + return status; + } + _initializeUserPrivilegesFromRoles(user); + } + } + } catch (const DBException& e) { + return e.toStatus(); + } catch (const std::exception& e) { + return Status(ErrorCodes::InternalError, e.what()); + } + + return Status::OK(); + } } // namespace mongo diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h index 6170bea849e..48758ace7b7 100644 --- a/src/mongo/db/auth/authorization_manager.h +++ b/src/mongo/db/auth/authorization_manager.h @@ -143,6 +143,19 @@ namespace mongo { */ void releaseUser(User* user); + /** + * Initializes the user cache with User objects for every v0 and v1 user document in the + * system, by reading the system.users collection of every database. If this function + * returns a non-ok Status, the _userCache should be considered corrupt and must be + * discarded. This function should be called once at startup (only if the system hasn't yet + * been upgraded to V2 user data format) and never again after that. + * TODO(spencer): This function will temporarily be called every time user data is changed + * as part of the transition period to the new User data structures. This should be changed + * once we have all the code necessary to upgrade to the V2 user data format, as at that + * point we'll only be able to user V1 user data as read-only. + */ + Status initilizeAllV1UserData(); + private: // Parses the old-style (pre 2.4) privilege document and returns a PrivilegeSet of all the @@ -171,6 +184,12 @@ namespace mongo { Status _initializeUserFromPrivilegeDocument(User* user, const BSONObj& privDoc) const; + /** + * Invalidates all User objects in the cache and removes them from the cache. + * Should only be called when already holding _lock. + */ + void _invalidateUserCache_inlock(); + static bool _doesSupportOldStylePrivileges; diff --git a/src/mongo/db/auth/authorization_manager_test.cpp b/src/mongo/db/auth/authorization_manager_test.cpp index fd68c9e6652..f359e000284 100644 --- a/src/mongo/db/auth/authorization_manager_test.cpp +++ b/src/mongo/db/auth/authorization_manager_test.cpp @@ -589,5 +589,102 @@ namespace { ASSERT(actions.contains(ActionType::dropDatabase)); ASSERT_FALSE(actions.contains(ActionType::find)); } + + TEST_F(AuthorizationManagerTest, initilizeAllV1UserData) { + externalState->insertPrivilegeDocument("test", + BSON("user" << "readOnly" << + "pwd" << "password" << + "roles" << BSON_ARRAY("read"))); + externalState->insertPrivilegeDocument("admin", + BSON("user" << "clusterAdmin" << + "userSource" << "$external" << + "roles" << BSON_ARRAY("clusterAdmin"))); + externalState->insertPrivilegeDocument("test", + BSON("user" << "readWriteMultiDB" << + "pwd" << "password" << + "roles" << BSON_ARRAY("readWrite"))); + externalState->insertPrivilegeDocument("test2", + BSON("user" << "readWriteMultiDB" << + "userSource" << "test" << + "roles" << BSON_ARRAY("readWrite"))); + + Status status = authzManager->initilizeAllV1UserData(); + ASSERT_OK(status); + + User* readOnly; + ASSERT_OK(authzManager->acquireUser(UserName("readOnly", "test"), &readOnly)); + ASSERT(UserName("readOnly", "test") == readOnly->getName()); + ASSERT(readOnly->isValid()); + ASSERT_EQUALS((uint32_t)2, readOnly->getRefCount()); + RoleNameIterator it = readOnly->getRoles(); + ASSERT(it.more()); + RoleName roleName = it.next(); + ASSERT_EQUALS("test", roleName.getDB()); + ASSERT_EQUALS("read", roleName.getRole()); + ASSERT_FALSE(it.more()); + ActionSet actions = readOnly->getActionsForResource("test"); + ASSERT(actions.contains(ActionType::find)); + ASSERT_FALSE(actions.contains(ActionType::insert)); + actions = readOnly->getActionsForResource("test2"); + ASSERT(actions.empty()); + actions = readOnly->getActionsForResource("admin"); + ASSERT(actions.empty()); + + User* clusterAdmin; + ASSERT_OK(authzManager->acquireUser(UserName("clusterAdmin", "$external"), &clusterAdmin)); + ASSERT(UserName("clusterAdmin", "$external") == clusterAdmin->getName()); + ASSERT(clusterAdmin->isValid()); + ASSERT_EQUALS((uint32_t)2, clusterAdmin->getRefCount()); + it = clusterAdmin->getRoles(); + ASSERT(it.more()); + roleName = it.next(); + ASSERT_EQUALS("admin", roleName.getDB()); + ASSERT_EQUALS("clusterAdmin", roleName.getRole()); + ASSERT_FALSE(it.more()); + actions = clusterAdmin->getActionsForResource("*"); + ASSERT(actions.contains(ActionType::listDatabases)); + ASSERT(actions.contains(ActionType::dropDatabase)); + ASSERT_FALSE(actions.contains(ActionType::find)); + + User* multiDB; + status = authzManager->acquireUser(UserName("readWriteMultiDB", "test2"), &multiDB); + ASSERT_NOT_OK(status); + ASSERT(status.code() == ErrorCodes::UserNotFound); + + ASSERT_OK(authzManager->acquireUser(UserName("readWriteMultiDB", "test"), &multiDB)); + ASSERT(UserName("readWriteMultiDB", "test") == multiDB->getName()); + ASSERT(multiDB->isValid()); + ASSERT_EQUALS((uint32_t)2, multiDB->getRefCount()); + it = multiDB->getRoles(); + + bool hasRoleOnTest = false; + bool hasRoleOnTest2 = false; + int numRoles = 0; + while(it.more()) { + ++numRoles; + RoleName roleName = it.next(); + ASSERT_EQUALS("readWrite", roleName.getRole()); + if (roleName.getDB() == "test") { + hasRoleOnTest = true; + } else { + ASSERT_EQUALS("test2", roleName.getDB()); + hasRoleOnTest2 = true; + } + } + ASSERT_EQUALS(2, numRoles); + ASSERT(hasRoleOnTest); + ASSERT(hasRoleOnTest2); + + actions = multiDB->getActionsForResource("test"); + ASSERT(actions.contains(ActionType::find)); + ASSERT(actions.contains(ActionType::insert)); + ASSERT_FALSE(actions.contains(ActionType::shutdown)); + actions = multiDB->getActionsForResource("test2"); + ASSERT(actions.contains(ActionType::find)); + ASSERT(actions.contains(ActionType::insert)); + ASSERT_FALSE(actions.contains(ActionType::shutdown)); + actions = multiDB->getActionsForResource("admin"); + ASSERT(actions.empty()); + } } // namespace } // namespace mongo diff --git a/src/mongo/db/auth/authz_manager_external_state.h b/src/mongo/db/auth/authz_manager_external_state.h index a9af078e675..df66afd28ac 100644 --- a/src/mongo/db/auth/authz_manager_external_state.h +++ b/src/mongo/db/auth/authz_manager_external_state.h @@ -17,6 +17,7 @@ #pragma once #include <string> +#include <vector> #include "mongo/base/disallow_copying.h" #include "mongo/base/status.h" @@ -57,6 +58,17 @@ namespace mongo { virtual Status updatePrivilegeDocument(const UserName& user, const BSONObj& updateObj) const = 0; + /** + * Puts into the *dbnames vector the name of every database in the cluster. + */ + virtual void getAllDatabaseNames(std::vector<std::string>* dbnames) const = 0; + + /** + * Returns a vector of every privilege document from the given database's + * system.users collection. + */ + virtual std::vector<BSONObj> getAllV1PrivilegeDocsForDB(const std::string& dbname) const = 0; + protected: AuthzManagerExternalState(); // This class should never be instantiated directly. diff --git a/src/mongo/db/auth/authz_manager_external_state_d.cpp b/src/mongo/db/auth/authz_manager_external_state_d.cpp index 994d4d56c1a..e4bb80e0572 100644 --- a/src/mongo/db/auth/authz_manager_external_state_d.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_d.cpp @@ -100,4 +100,17 @@ namespace mongo { return Helpers::findOne(usersNamespace, query, *result); } + void AuthzManagerExternalStateMongod::getAllDatabaseNames( + std::vector<std::string>* dbnames) const { + getDatabaseNames(*dbnames); + } + + std::vector<BSONObj> AuthzManagerExternalStateMongod::getAllV1PrivilegeDocsForDB( + const std::string& dbname) const { + Client::GodScope gs; + Client::ReadContext ctx(dbname); + + return Helpers::findAll(dbname, BSONObj()); + } + } // namespace mongo diff --git a/src/mongo/db/auth/authz_manager_external_state_d.h b/src/mongo/db/auth/authz_manager_external_state_d.h index 2158b642648..e083800e30a 100644 --- a/src/mongo/db/auth/authz_manager_external_state_d.h +++ b/src/mongo/db/auth/authz_manager_external_state_d.h @@ -41,6 +41,10 @@ namespace mongo { virtual Status updatePrivilegeDocument(const UserName& user, const BSONObj& updateObj) const; + virtual void getAllDatabaseNames(std::vector<std::string>* dbnames) const; + + virtual std::vector<BSONObj> getAllV1PrivilegeDocsForDB(const std::string& dbname) const; + protected: virtual bool _findUser(const string& usersNamespace, const BSONObj& query, diff --git a/src/mongo/db/auth/authz_manager_external_state_mock.cpp b/src/mongo/db/auth/authz_manager_external_state_mock.cpp index 456902277ff..bec798f0a9b 100644 --- a/src/mongo/db/auth/authz_manager_external_state_mock.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_mock.cpp @@ -38,7 +38,7 @@ namespace mongo { Status AuthzManagerExternalStateMock::insertPrivilegeDocument(const std::string& dbname, const BSONObj& userObj) { - _userDocuments.insert(make_pair(dbname, userObj)); + _userDocuments[dbname].push_back(userObj); return Status::OK(); } @@ -46,6 +46,26 @@ namespace mongo { _userDocuments.clear(); } + void AuthzManagerExternalStateMock::getAllDatabaseNames( + std::vector<std::string>* dbnames) const { + unordered_map<std::string, std::vector<BSONObj> >::const_iterator it; + for (it = _userDocuments.begin(); it != _userDocuments.end(); ++it) { + dbnames->push_back(it->first); + } + } + + std::vector<BSONObj> AuthzManagerExternalStateMock::getAllV1PrivilegeDocsForDB( + const std::string& dbname) const { + std::vector<BSONObj> out; + + const std::vector<BSONObj>& dbDocs = _userDocuments.find(dbname)->second; + for (std::vector<BSONObj>::const_iterator it = dbDocs.begin(); it != dbDocs.end(); ++it) { + out.push_back(*it); + } + return out; + } + + bool AuthzManagerExternalStateMock::_findUser(const std::string& usersNamespace, const BSONObj& query, BSONObj* result) const { @@ -55,11 +75,15 @@ namespace mongo { } MatchExpression* matcher = parseResult.getValue(); - for (unordered_map<std::string, BSONObj>::const_iterator it = _userDocuments.begin(); - it != _userDocuments.end(); ++it) { - if (nsToDatabase(usersNamespace) == it->first && matcher->matchesBSON(it->second)) { - *result = it->second; - return true; + unordered_map<std::string, std::vector<BSONObj> >::const_iterator mapIt; + for (mapIt = _userDocuments.begin(); mapIt != _userDocuments.end(); ++mapIt) { + for (std::vector<BSONObj>::const_iterator vecIt = mapIt->second.begin(); + vecIt != mapIt->second.end(); ++vecIt) { + if (nsToDatabase(usersNamespace) == mapIt->first && + matcher->matchesBSON(*vecIt)) { + *result = *vecIt; + return true; + } } } return false; diff --git a/src/mongo/db/auth/authz_manager_external_state_mock.h b/src/mongo/db/auth/authz_manager_external_state_mock.h index dd026c2764f..dfb92ae6be0 100644 --- a/src/mongo/db/auth/authz_manager_external_state_mock.h +++ b/src/mongo/db/auth/authz_manager_external_state_mock.h @@ -17,6 +17,7 @@ #pragma once #include <string> +#include <vector> #include "mongo/base/disallow_copying.h" #include "mongo/base/status.h" @@ -49,12 +50,17 @@ namespace mongo { void clearPrivilegeDocuments(); + virtual void getAllDatabaseNames(std::vector<std::string>* dbnames) const; + + virtual std::vector<BSONObj> getAllV1PrivilegeDocsForDB(const std::string& dbname) const; + virtual bool _findUser(const std::string& usersNamespace, const BSONObj& query, BSONObj* result) const; + private: - unordered_map<std::string, BSONObj> _userDocuments; // dbname to user document + unordered_map<std::string, std::vector<BSONObj> > _userDocuments; // dbname to user docs }; } // namespace mongo diff --git a/src/mongo/db/auth/authz_manager_external_state_s.cpp b/src/mongo/db/auth/authz_manager_external_state_s.cpp index b4ead4041bf..e723dc4e707 100644 --- a/src/mongo/db/auth/authz_manager_external_state_s.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_s.cpp @@ -102,4 +102,17 @@ namespace mongo { return Status::OK(); } + void AuthzManagerExternalStateMongos::getAllDatabaseNames( + std::vector<std::string>* dbnames) const { + // TODO(spencer): NOT YET IMPLEMENTED + fassertFailed(16964); + } + + std::vector<BSONObj> AuthzManagerExternalStateMongos::getAllV1PrivilegeDocsForDB( + const std::string& dbname) const { + // TODO(spencer): NOT YET IMPLEMENTED + fassertFailed(16965); + } + + } // namespace mongo diff --git a/src/mongo/db/auth/authz_manager_external_state_s.h b/src/mongo/db/auth/authz_manager_external_state_s.h index 2ff3d7fa4fb..7c1fe3653dc 100644 --- a/src/mongo/db/auth/authz_manager_external_state_s.h +++ b/src/mongo/db/auth/authz_manager_external_state_s.h @@ -41,6 +41,10 @@ namespace mongo { virtual Status updatePrivilegeDocument(const UserName& user, const BSONObj& updateObj) const; + virtual void getAllDatabaseNames(std::vector<std::string>* dbnames) const; + + virtual std::vector<BSONObj> getAllV1PrivilegeDocsForDB(const std::string& dbname) const; + protected: virtual bool _findUser(const string& usersNamespace, const BSONObj& query, |