summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSpencer T Brody <spencer@10gen.com>2013-07-07 17:56:41 -0400
committerSpencer T Brody <spencer@10gen.com>2013-07-17 17:36:28 -0400
commit752f704bc0fb5c771b74a033364eaa045eda2040 (patch)
treef069415ec9b4e3672e704a13604037bd5071701e /src
parent67957614060f635bb0b6dfc3340d8ff64a680156 (diff)
downloadmongo-752f704bc0fb5c771b74a033364eaa045eda2040.tar.gz
SERVER-9518 Function to initialize User cache from V1 user documents.
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/auth/authorization_manager.cpp74
-rw-r--r--src/mongo/db/auth/authorization_manager.h19
-rw-r--r--src/mongo/db/auth/authorization_manager_test.cpp97
-rw-r--r--src/mongo/db/auth/authz_manager_external_state.h12
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_d.cpp13
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_d.h4
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_mock.cpp36
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_mock.h8
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_s.cpp13
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_s.h4
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,