diff options
author | Spencer T Brody <spencer@10gen.com> | 2013-09-09 15:33:45 -0400 |
---|---|---|
committer | Spencer T Brody <spencer@10gen.com> | 2013-09-12 13:58:46 -0400 |
commit | 5d0cb99278bcd6a476fd85b901f37170ce64c2af (patch) | |
tree | 00a660b8ba8213e40ed8f2789df6cf755131be91 | |
parent | b7565690f4f3e40df4d959cfcac095d7d85b7a48 (diff) | |
download | mongo-5d0cb99278bcd6a476fd85b901f37170ce64c2af.tar.gz |
SERVER-9980 Add write concern to user management commands
-rw-r--r-- | jstests/sharding/auth_repl.js | 2 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_manager.cpp | 33 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_manager.h | 35 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_manager_test.cpp | 53 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_session_test.cpp | 27 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state.h | 31 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_d.cpp | 51 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_d.h | 31 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_mock.cpp | 34 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_mock.h | 33 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_s.cpp | 50 | ||||
-rw-r--r-- | src/mongo/db/auth/authz_manager_external_state_s.h | 31 | ||||
-rw-r--r-- | src/mongo/db/commands/user_management_commands.cpp | 50 | ||||
-rw-r--r-- | src/mongo/db/commands/user_management_commands_parser.cpp | 2 | ||||
-rw-r--r-- | src/mongo/shell/db.js | 4 |
15 files changed, 333 insertions, 134 deletions
diff --git a/jstests/sharding/auth_repl.js b/jstests/sharding/auth_repl.js index 837381f6f58..871ca699bba 100644 --- a/jstests/sharding/auth_repl.js +++ b/jstests/sharding/auth_repl.js @@ -27,12 +27,10 @@ assert(doc != null); // Add admin user using direct connection to primary to simulate connection from remote host var adminDB = primary.getDB('admin'); adminDB.addUser('user', 'user', jsTest.adminUserRoles, nodeCount); -adminDB.runCommand({ getLastError: 1, w: nodeCount }); // TODO: remove after fixing SERVER-9980 adminDB.auth('user', 'user'); var priTestDB = primary.getDB('test'); priTestDB.addUser('a', 'a', jsTest.basicUserRoles, nodeCount); -priTestDB.runCommand({ getLastError: 1, w: nodeCount }); // TODO: remove after fixing SERVER-9980 // Authenticate the replSet connection assert.eq(1, testDB.auth('a', 'a')); diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp index 2e58c3f51c4..73012ed2eff 100644 --- a/src/mongo/db/auth/authorization_manager.cpp +++ b/src/mongo/db/auth/authorization_manager.cpp @@ -143,18 +143,21 @@ namespace mongo { } Status AuthorizationManager::insertPrivilegeDocument(const std::string& dbname, - const BSONObj& userObj) const { - return _externalState->insertPrivilegeDocument(dbname, userObj); + const BSONObj& userObj, + const BSONObj& writeConcern) const { + return _externalState->insertPrivilegeDocument(dbname, userObj, writeConcern); } Status AuthorizationManager::updatePrivilegeDocument(const UserName& user, - const BSONObj& updateObj) const { - return _externalState->updatePrivilegeDocument(user, updateObj); + const BSONObj& updateObj, + const BSONObj& writeConcern) const { + return _externalState->updatePrivilegeDocument(user, updateObj, writeConcern); } Status AuthorizationManager::removePrivilegeDocuments(const BSONObj& query, + const BSONObj& writeConcern, int* numRemoved) const { - return _externalState->removePrivilegeDocuments(query, numRemoved); + return _externalState->removePrivilegeDocuments(query, writeConcern, numRemoved); } ActionSet AuthorizationManager::getAllUserActions() { @@ -561,17 +564,21 @@ namespace mongo { durableVersion); } + BSONObj writeConcern; // Upgrade from v1 to v2. - status = _externalState->copyCollection(usersCollectionName, backupUsersCollectionName); + status = _externalState->copyCollection(usersCollectionName, + backupUsersCollectionName, + writeConcern); if (!status.isOK()) return status; - status = _externalState->dropCollection(newusersCollectionName); + status = _externalState->dropCollection(newusersCollectionName, writeConcern); if (!status.isOK()) return status; status = _externalState->createIndex( newusersCollectionName, BSON(USER_NAME_FIELD_NAME << 1 << USER_SOURCE_FIELD_NAME << 1), - true // unique + true, // unique + writeConcern ); if (!status.isOK()) return status; @@ -583,18 +590,22 @@ namespace mongo { continue; status = _externalState->insert( - newusersCollectionName, userAsV2PrivilegeDocument(*iter->second)); + newusersCollectionName, userAsV2PrivilegeDocument(*iter->second), + writeConcern); if (!status.isOK()) return status; } - status = _externalState->renameCollection(newusersCollectionName, usersCollectionName); + status = _externalState->renameCollection(newusersCollectionName, + usersCollectionName, + writeConcern); if (!status.isOK()) return status; status = _externalState->updateOne( versionCollectionName, versionDocumentQuery, BSON("$set" << BSON("currentVersion" << 2)), - true); + true, + writeConcern); if (!status.isOK()) return status; _version = 2; diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h index baef0f8882e..844ab7e6bff 100644 --- a/src/mongo/db/auth/authorization_manager.h +++ b/src/mongo/db/auth/authorization_manager.h @@ -120,15 +120,34 @@ namespace mongo { // Returns true if there exists at least one privilege document in the system. bool hasAnyPrivilegeDocuments() const; - // Creates the given user object in the given database. - Status insertPrivilegeDocument(const std::string& dbname, const BSONObj& userObj) const; - - // Updates the given user object with the given update modifier. - Status updatePrivilegeDocument(const UserName& user, const BSONObj& updateObj) const; + /** + * Creates the given user object in the given database. + * 'writeConcern' contains the arguments to be passed to getLastError to block for + * successful completion of the write. + */ + Status insertPrivilegeDocument(const std::string& dbname, + const BSONObj& userObj, + const BSONObj& writeConcern) const; - // Removes users for the given database matching the given query. - // Writes into *numRemoved the number of user documents that were modified. - Status removePrivilegeDocuments(const BSONObj& query, int* numRemoved) const; + /** + * Updates the given user object with the given update modifier. + * 'writeConcern' contains the arguments to be passed to getLastError to block for + * successful completion of the write. + */ + Status updatePrivilegeDocument(const UserName& user, + const BSONObj& updateObj, + const BSONObj& writeConcern) const; + + /* + * Removes users for the given database matching the given query. + * Writes into *numRemoved the number of user documents that were modified. + * 'writeConcern' contains the arguments to be passed to getLastError to block for + * successful completion of the write. + */ + // + Status removePrivilegeDocuments(const BSONObj& query, + const BSONObj& writeConcern, + int* numRemoved) const; // Checks to see if "doc" is a valid privilege document, assuming it is stored in the // "system.users" collection of database "dbname". diff --git a/src/mongo/db/auth/authorization_manager_test.cpp b/src/mongo/db/auth/authorization_manager_test.cpp index b5d86841923..f6e18983aec 100644 --- a/src/mongo/db/auth/authorization_manager_test.cpp +++ b/src/mongo/db/auth/authorization_manager_test.cpp @@ -330,11 +330,13 @@ namespace { authzManager->setAuthorizationVersion(1); ASSERT_OK(externalState->insert(NamespaceString("test.system.users"), - BSON("user" << "v0RW" << "pwd" << "password"))); + BSON("user" << "v0RW" << "pwd" << "password"), + BSONObj())); ASSERT_OK(externalState->insert(NamespaceString("admin.system.users"), BSON("user" << "v0AdminRO" << "pwd" << "password" << - "readOnly" << true))); + "readOnly" << true), + BSONObj())); User* v0RW; ASSERT_OK(authzManager->acquireUser(UserName("v0RW", "test"), &v0RW)); @@ -384,11 +386,13 @@ namespace { ASSERT_OK(externalState->insert(NamespaceString("test.system.users"), BSON("user" << "v1read" << "pwd" << "password" << - "roles" << BSON_ARRAY("read")))); + "roles" << BSON_ARRAY("read")), + BSONObj())); ASSERT_OK(externalState->insert(NamespaceString("admin.system.users"), BSON("user" << "v1cluster" << "pwd" << "password" << - "roles" << BSON_ARRAY("clusterAdmin")))); + "roles" << BSON_ARRAY("clusterAdmin")), + BSONObj())); User* v1read; ASSERT_OK(authzManager->acquireUser(UserName("v1read", "test"), &v1read)); @@ -436,19 +440,23 @@ namespace { ASSERT_OK(externalState->insert(NamespaceString("test.system.users"), BSON("user" << "readOnly" << "pwd" << "password" << - "roles" << BSON_ARRAY("read")))); + "roles" << BSON_ARRAY("read")), + BSONObj())); ASSERT_OK(externalState->insert(NamespaceString("admin.system.users"), BSON("user" << "clusterAdmin" << "userSource" << "$external" << - "roles" << BSON_ARRAY("clusterAdmin")))); + "roles" << BSON_ARRAY("clusterAdmin")), + BSONObj())); ASSERT_OK(externalState->insert(NamespaceString("test.system.users"), BSON("user" << "readWriteMultiDB" << "pwd" << "password" << - "roles" << BSON_ARRAY("readWrite")))); + "roles" << BSON_ARRAY("readWrite")), + BSONObj())); ASSERT_OK(externalState->insert(NamespaceString("test2.system.users"), BSON("user" << "readWriteMultiDB" << "userSource" << "test" << - "roles" << BSON_ARRAY("readWrite")))); + "roles" << BSON_ARRAY("readWrite")), + BSONObj())); Status status = authzManager->initialize(); ASSERT_OK(status); @@ -554,7 +562,8 @@ namespace { "roles" << BSON_ARRAY(BSON("name" << "read" << "source" << "test" << "canDelegate" << false << - "hasRole" << true))))); + "hasRole" << true))), + BSONObj())); ASSERT_OK(externalState->insertPrivilegeDocument( "admin", BSON("name" << "v2cluster" << @@ -563,7 +572,8 @@ namespace { "roles" << BSON_ARRAY(BSON("name" << "clusterAdmin" << "source" << "admin" << "canDelegate" << true << - "hasRole" << true))))); + "hasRole" << true))), + BSONObj())); User* v2read; ASSERT_OK(authzManager->acquireUser(UserName("v2read", "test"), &v2read)); @@ -624,19 +634,23 @@ namespace { ASSERT_OK(externalState->insert(NamespaceString("test.system.users"), BSON("user" << "readOnly" << "pwd" << "password" << - "roles" << BSON_ARRAY("read")))); + "roles" << BSON_ARRAY("read")), + BSONObj())); ASSERT_OK(externalState->insert(NamespaceString("admin.system.users"), BSON("user" << "clusterAdmin" << "userSource" << "$external" << - "roles" << BSON_ARRAY("clusterAdmin")))); + "roles" << BSON_ARRAY("clusterAdmin")), + BSONObj())); ASSERT_OK(externalState->insert(NamespaceString("test.system.users"), BSON("user" << "readWriteMultiDB" << "pwd" << "password" << - "roles" << BSON_ARRAY("readWrite")))); + "roles" << BSON_ARRAY("readWrite")), + BSONObj())); ASSERT_OK(externalState->insert(NamespaceString("test2.system.users"), BSON("user" << "readWriteMultiDB" << "userSource" << "test" << - "roles" << BSON_ARRAY("readWrite")))); + "roles" << BSON_ARRAY("readWrite")), + BSONObj())); ASSERT_OK(authzManager->initialize()); } @@ -717,7 +731,8 @@ namespace { authzManager->setAuthorizationVersion(1); setUpV1UserData(); ASSERT_OK(externalState->insert(versionCollectionName, - BSON("_id" << 1 << "currentVersion" << 1))); + BSON("_id" << 1 << "currentVersion" << 1), + BSONObj())); ASSERT_OK(authzManager->upgradeAuthCollections()); validateV1AdminUserData(backupUsersCollectionName); @@ -728,10 +743,11 @@ namespace { authzManager->setAuthorizationVersion(1); setUpV1UserData(); ASSERT_OK(externalState->insert(versionCollectionName, - BSON("_id" << 1 << "currentVersion" << 3))); + BSON("_id" << 1 << "currentVersion" << 3), + BSONObj())); ASSERT_NOT_OK(authzManager->upgradeAuthCollections()); validateV1AdminUserData(usersCollectionName); - ASSERT_OK(externalState->remove(versionCollectionName, BSONObj())); + ASSERT_OK(externalState->remove(versionCollectionName, BSONObj(), BSONObj())); ASSERT_OK(authzManager->upgradeAuthCollections()); validateV1AdminUserData(backupUsersCollectionName); validateV2UserData(); @@ -741,7 +757,8 @@ namespace { authzManager->setAuthorizationVersion(1); setUpV1UserData(); ASSERT_OK(externalState->insert(versionCollectionName, - BSON("_id" << 1 << "currentVersion" << 2))); + BSON("_id" << 1 << "currentVersion" << 2), + BSONObj())); ASSERT_NOT_OK(authzManager->upgradeAuthCollections()); validateV1AdminUserData(usersCollectionName); } diff --git a/src/mongo/db/auth/authorization_session_test.cpp b/src/mongo/db/auth/authorization_session_test.cpp index 5ca4ff21550..837db1fb1f0 100644 --- a/src/mongo/db/auth/authorization_session_test.cpp +++ b/src/mongo/db/auth/authorization_session_test.cpp @@ -96,7 +96,8 @@ namespace { BSON("name" << "dbAdmin" << "source" << "test" << "hasRole" << true << - "canDelegate" << false))))); + "canDelegate" << false))), + BSONObj())); ASSERT_OK(authzSession->addAndAuthorizeUser(UserName("spencer", "test"))); ASSERT_TRUE(authzSession->checkAuthorization("test", ActionType::insert)); @@ -113,7 +114,8 @@ namespace { "roles" << BSON_ARRAY(BSON("name" << "readWriteAnyDatabase" << "source" << "admin" << "hasRole" << true << - "canDelegate" << false))))); + "canDelegate" << false))), + BSONObj())); ASSERT_OK(authzSession->addAndAuthorizeUser(UserName("admin", "admin"))); ASSERT_TRUE(authzSession->checkAuthorization("*", ActionType::insert)); @@ -142,7 +144,8 @@ namespace { "roles" << BSON_ARRAY(BSON("name" << "readWrite" << "source" << "test" << "hasRole" << true << - "canDelegate" << false))))); + "canDelegate" << false))), + BSONObj())); ASSERT_OK(authzSession->addAndAuthorizeUser(UserName("spencer", "test"))); ASSERT_TRUE(authzSession->checkAuthorization("test", ActionType::find)); @@ -160,7 +163,8 @@ namespace { "roles" << BSON_ARRAY(BSON("name" << "read" << "source" << "test" << "hasRole" << true << - "canDelegate" << false))))); + "canDelegate" << false))), + BSONObj())); // Make sure that invalidating the user causes the session to reload its privileges. authzManager->invalidateUser(user); @@ -188,7 +192,8 @@ namespace { "roles" << BSON_ARRAY(BSON("name" << "readWrite" << "source" << "test" << "hasRole" << true << - "canDelegate" << false))))); + "canDelegate" << false))), + BSONObj())); ASSERT_OK(authzSession->addAndAuthorizeUser(UserName("spencer", "test"))); ASSERT_TRUE(authzSession->checkAuthorization("test", ActionType::find)); @@ -207,7 +212,8 @@ namespace { "roles" << BSON_ARRAY(BSON("name" << "read" << "source" << "test" << "hasRole" << true << - "canDelegate" << false))))); + "canDelegate" << false))), + BSONObj())); // Even though the user's privileges have been reduced, since we've configured user // document lookup to fail, the authz session should continue to use its known out-of-date @@ -224,16 +230,19 @@ namespace { managerState->insert(NamespaceString("test.system.users"), BSON("user" << "andy" << "pwd" << "a" << - "roles" << BSON_ARRAY("readWrite"))); + "roles" << BSON_ARRAY("readWrite")), + BSONObj()); managerState->insert(NamespaceString("test2.system.users"), BSON("user" << "andy" << "userSource" << "test" << - "roles" << BSON_ARRAY("read"))); + "roles" << BSON_ARRAY("read")), + BSONObj()); managerState->insert(NamespaceString("admin.system.users"), BSON("user" << "andy" << "userSource" << "test" << "roles" << BSON_ARRAY("clusterAdmin") << - "otherDBRoles" << BSON("test3" << BSON_ARRAY("dbAdmin")))); + "otherDBRoles" << BSON("test3" << BSON_ARRAY("dbAdmin"))), + BSONObj()); ASSERT_OK(authzManager->initialize()); ASSERT(!authzSession->checkAuthorization("test.foo", ActionType::find)); diff --git a/src/mongo/db/auth/authz_manager_external_state.h b/src/mongo/db/auth/authz_manager_external_state.h index 37d26f854ae..0762e5f075e 100644 --- a/src/mongo/db/auth/authz_manager_external_state.h +++ b/src/mongo/db/auth/authz_manager_external_state.h @@ -67,15 +67,19 @@ namespace mongo { // Creates the given user object in the given database. // TODO(spencer): remove dbname argument once users are only written into the admin db virtual Status insertPrivilegeDocument(const std::string& dbname, - const BSONObj& userObj) = 0; + const BSONObj& userObj, + const BSONObj& writeConcern) = 0; // Updates the given user object with the given update modifier. virtual Status updatePrivilegeDocument(const UserName& user, - const BSONObj& updateObj) = 0; + const BSONObj& updateObj, + const BSONObj& writeConcern) = 0; // Removes users for the given database matching the given query. // Writes into *numRemoved the number of user documents that were modified. - virtual Status removePrivilegeDocuments(const BSONObj& query, int* numRemoved) = 0; + virtual Status removePrivilegeDocuments(const BSONObj& query, + const BSONObj& writeConcern, + int* numRemoved) = 0; /** * Puts into the *dbnames vector the name of every database in the cluster. @@ -104,7 +108,8 @@ namespace mongo { * Inserts "document" into "collectionName". */ virtual Status insert(const NamespaceString& collectionName, - const BSONObj& document) = 0; + const BSONObj& document, + const BSONObj& writeConcern) = 0; /** * Update one document matching "query" according to "updatePattern" in "collectionName". @@ -115,39 +120,45 @@ namespace mongo { virtual Status updateOne(const NamespaceString& collectionName, const BSONObj& query, const BSONObj& updatePattern, - bool upsert) = 0; + bool upsert, + const BSONObj& writeConcern) = 0; /** * Removes all documents matching "query" from "collectionName". */ virtual Status remove(const NamespaceString& collectionName, - const BSONObj& query) = 0; + const BSONObj& query, + const BSONObj& writeConcern) = 0; /** * Creates an index with the given pattern on "collectionName". */ virtual Status createIndex(const NamespaceString& collectionName, const BSONObj& pattern, - bool unique) = 0; + bool unique, + const BSONObj& writeConcern) = 0; /** * Drops the named collection. */ - virtual Status dropCollection(const NamespaceString& collectionName) = 0; + virtual Status dropCollection(const NamespaceString& collectionName, + const BSONObj& writeConcern) = 0; /** * Renames collection "oldName" to "newName", possibly dropping the previous * collection named "newName". */ virtual Status renameCollection(const NamespaceString& oldName, - const NamespaceString& newName) = 0; + const NamespaceString& newName, + const BSONObj& writeConcern) = 0; /** * Copies the contents of collection "fromName" into "toName". Fails * if "toName" is already a collection. */ virtual Status copyCollection(const NamespaceString& fromName, - const NamespaceString& toName) = 0; + const NamespaceString& toName, + const BSONObj& writeConcern) = 0; /** * Tries to acquire the global lock guarding modifications to all persistent data related 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 e7d2f060e59..2fb6f49be5a 100644 --- a/src/mongo/db/auth/authz_manager_external_state_d.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_d.cpp @@ -52,7 +52,8 @@ namespace { AuthzManagerExternalStateMongod::~AuthzManagerExternalStateMongod() {} Status AuthzManagerExternalStateMongod::insertPrivilegeDocument(const string& dbname, - const BSONObj& userObj) { + const BSONObj& userObj, + const BSONObj& writeConcern) { try { const std::string userNS = "admin.system.users"; DBDirectClient client; @@ -66,8 +67,12 @@ namespace { client.insert(userNS, userObj); } - // 30 second timeout for w:majority - BSONObj res = client.getLastErrorDetailed(false, false, -1, 30*1000); + // Handle write concern + BSONObjBuilder gleBuilder; + gleBuilder.append("getLastError", 1); + gleBuilder.appendElements(writeConcern); + BSONObj res; + client.runCommand("admin", gleBuilder.done(), res); string errstr = client.getLastErrorString(res); if (errstr.empty()) { return Status::OK(); @@ -86,7 +91,7 @@ namespace { } Status AuthzManagerExternalStateMongod::updatePrivilegeDocument( - const UserName& user, const BSONObj& updateObj) { + const UserName& user, const BSONObj& updateObj, const BSONObj& writeConcern) { try { const std::string userNS = "admin.system.users"; DBDirectClient client; @@ -103,8 +108,12 @@ namespace { updateObj); } - // 30 second timeout for w:majority - BSONObj res = client.getLastErrorDetailed(false, false, -1, 30*1000); + // Handle write concern + BSONObjBuilder gleBuilder; + gleBuilder.append("getLastError", 1); + gleBuilder.appendElements(writeConcern); + BSONObj res; + client.runCommand("admin", gleBuilder.done(), res); string err = client.getLastErrorString(res); if (!err.empty()) { return Status(ErrorCodes::UserModificationFailed, err); @@ -125,6 +134,7 @@ namespace { } Status AuthzManagerExternalStateMongod::removePrivilegeDocuments(const BSONObj& query, + const BSONObj& writeConcern, int* numRemoved) { try { const std::string userNS = "admin.system.users"; @@ -139,8 +149,12 @@ namespace { client.remove(userNS, query); } - // 30 second timeout for w:majority - BSONObj res = client.getLastErrorDetailed(false, false, -1, 30*1000); + // Handle write concern + BSONObjBuilder gleBuilder; + gleBuilder.append("getLastError", 1); + gleBuilder.appendElements(writeConcern); + BSONObj res; + client.runCommand("admin", gleBuilder.done(), res); string errstr = client.getLastErrorString(res); if (!errstr.empty()) { return Status(ErrorCodes::UserModificationFailed, errstr); @@ -192,7 +206,8 @@ namespace { Status AuthzManagerExternalStateMongod::insert( const NamespaceString& collectionName, - const BSONObj& document) { + const BSONObj& document, + const BSONObj& writeConcern) { fassertFailed(17092); } @@ -200,37 +215,43 @@ namespace { const NamespaceString& collectionName, const BSONObj& query, const BSONObj& updatePattern, - bool upsert) { + bool upsert, + const BSONObj& writeConcern) { fassertFailed(17093); } Status AuthzManagerExternalStateMongod::remove( const NamespaceString& collectionName, - const BSONObj& query) { + const BSONObj& query, + const BSONObj& writeConcern) { fassertFailed(17094); } Status AuthzManagerExternalStateMongod::createIndex( const NamespaceString& collectionName, const BSONObj& pattern, - bool unique) { + bool unique, + const BSONObj& writeConcern) { fassertFailed(17095); } Status AuthzManagerExternalStateMongod::dropCollection( - const NamespaceString& collectionName) { + const NamespaceString& collectionName, + const BSONObj& writeConcern) { fassertFailed(17096); } Status AuthzManagerExternalStateMongod::renameCollection( const NamespaceString& oldName, - const NamespaceString& newName) { + const NamespaceString& newName, + const BSONObj& writeConcern) { fassertFailed(17097); } Status AuthzManagerExternalStateMongod::copyCollection( const NamespaceString& fromName, - const NamespaceString& toName) { + const NamespaceString& toName, + const BSONObj& writeConcern) { fassertFailed(17098); } 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 f94ef40e0d8..cef5a8aa883 100644 --- a/src/mongo/db/auth/authz_manager_external_state_d.h +++ b/src/mongo/db/auth/authz_manager_external_state_d.h @@ -49,12 +49,16 @@ namespace mongo { virtual ~AuthzManagerExternalStateMongod(); virtual Status insertPrivilegeDocument(const std::string& dbname, - const BSONObj& userObj); + const BSONObj& userObj, + const BSONObj& writeConcern); virtual Status updatePrivilegeDocument(const UserName& user, - const BSONObj& updateObj); + const BSONObj& updateObj, + const BSONObj& writeConcern); - virtual Status removePrivilegeDocuments(const BSONObj& query, int* numRemoved); + virtual Status removePrivilegeDocuments(const BSONObj& query, + const BSONObj& writeConcern, + int* numRemoved); virtual Status getAllDatabaseNames(std::vector<std::string>* dbnames); @@ -65,21 +69,28 @@ namespace mongo { const BSONObj& query, BSONObj* result); virtual Status insert(const NamespaceString& collectionName, - const BSONObj& document); + const BSONObj& document, + const BSONObj& writeConcern); virtual Status updateOne(const NamespaceString& collectionName, const BSONObj& query, const BSONObj& updatePattern, - bool upsert); + bool upsert, + const BSONObj& writeConcern); virtual Status remove(const NamespaceString& collectionName, - const BSONObj& query); + const BSONObj& query, + const BSONObj& writeConcern); virtual Status createIndex(const NamespaceString& collectionName, const BSONObj& pattern, - bool unique); - virtual Status dropCollection(const NamespaceString& collectionName); + bool unique, + const BSONObj& writeConcern); + virtual Status dropCollection(const NamespaceString& collectionName, + const BSONObj& writeConcern); virtual Status renameCollection(const NamespaceString& oldName, - const NamespaceString& newName); + const NamespaceString& newName, + const BSONObj& writeConcern); virtual Status copyCollection(const NamespaceString& fromName, - const NamespaceString& toName); + const NamespaceString& toName, + const BSONObj& writeConcern); virtual bool tryAcquireAuthzUpdateLock(const StringData& why); virtual void releaseAuthzUpdateLock(); 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 833606032d3..0c4dd2add49 100644 --- a/src/mongo/db/auth/authz_manager_external_state_mock.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_mock.cpp @@ -44,19 +44,22 @@ namespace mongo { Status AuthzManagerExternalStateMock::updatePrivilegeDocument(const UserName& user, - const BSONObj& updateObj) { + const BSONObj& updateObj, + const BSONObj&) { return Status(ErrorCodes::InternalError, "Not implemented in mock."); } Status AuthzManagerExternalStateMock::removePrivilegeDocuments(const BSONObj& query, + const BSONObj&, int* numRemoved) { return Status(ErrorCodes::InternalError, "Not implemented in mock."); } Status AuthzManagerExternalStateMock::insertPrivilegeDocument(const std::string& dbname, - const BSONObj& userObj) { + const BSONObj& userObj, + const BSONObj& writeConcern) { NamespaceString usersCollection("admin.system.users"); - return insert(usersCollection, userObj); + return insert(usersCollection, userObj, writeConcern); } void AuthzManagerExternalStateMock::clearPrivilegeDocuments() { @@ -112,7 +115,8 @@ namespace mongo { Status AuthzManagerExternalStateMock::insert( const NamespaceString& collectionName, - const BSONObj& document) { + const BSONObj& document, + const BSONObj&) { _documents[collectionName].push_back(document.copy()); return Status::OK(); } @@ -121,7 +125,8 @@ namespace mongo { const NamespaceString& collectionName, const BSONObj& query, const BSONObj& updatePattern, - bool upsert) { + bool upsert, + const BSONObj& writeConcern) { namespace mmb = mutablebson; UpdateDriver::Options updateOptions; @@ -154,7 +159,7 @@ namespace mongo { if (!status.isOK()) { return status; } - return insert(collectionName, document.getObject()); + return insert(collectionName, document.getObject(), writeConcern); } else { return status; @@ -163,7 +168,8 @@ namespace mongo { Status AuthzManagerExternalStateMock::remove( const NamespaceString& collectionName, - const BSONObj& query) { + const BSONObj& query, + const BSONObj&) { BSONObjCollection::iterator iter; while (_findOneIter(collectionName, query, &iter).isOK()) { _documents[collectionName].erase(iter); @@ -174,27 +180,31 @@ namespace mongo { Status AuthzManagerExternalStateMock::createIndex( const NamespaceString& collectionName, const BSONObj& pattern, - bool unique) { + bool unique, + const BSONObj&) { return Status::OK(); } - Status AuthzManagerExternalStateMock::dropCollection(const NamespaceString& collectionName) { + Status AuthzManagerExternalStateMock::dropCollection(const NamespaceString& collectionName, + const BSONObj&) { _documents.erase(collectionName); return Status::OK(); } Status AuthzManagerExternalStateMock::renameCollection(const NamespaceString& oldName, - const NamespaceString& newName) { + const NamespaceString& newName, + const BSONObj& writeConcern) { if (_documents.count(oldName) == 0) { return Status(ErrorCodes::NamespaceNotFound, "No collection to rename named " + oldName.ns()); } std::swap(_documents[newName], _documents[oldName]); - return dropCollection(oldName); + return dropCollection(oldName, writeConcern); } Status AuthzManagerExternalStateMock::copyCollection(const NamespaceString& fromName, - const NamespaceString& toName) { + const NamespaceString& toName, + const BSONObj&) { if (_documents.count(fromName) == 0) { return Status(ErrorCodes::NamespaceNotFound, "No collection to copy named " + fromName.ns()); 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 62d053df346..6b19164f1cc 100644 --- a/src/mongo/db/auth/authz_manager_external_state_mock.h +++ b/src/mongo/db/auth/authz_manager_external_state_mock.h @@ -51,13 +51,17 @@ namespace mongo { AuthzManagerExternalStateMock() {}; virtual Status insertPrivilegeDocument(const std::string& dbname, - const BSONObj& userObj); + const BSONObj& userObj, + const BSONObj& writeConcern); virtual Status updatePrivilegeDocument(const UserName& user, - const BSONObj& updateObj); + const BSONObj& updateObj, + const BSONObj& writeConcern); // no-op for the mock - virtual Status removePrivilegeDocuments(const BSONObj& query, int* numRemoved); + virtual Status removePrivilegeDocuments(const BSONObj& query, + const BSONObj& writeConcern, + int* numRemoved); void clearPrivilegeDocuments(); @@ -76,24 +80,31 @@ namespace mongo { // This implementation does not understand uniqueness constraints. virtual Status insert(const NamespaceString& collectionName, - const BSONObj& document); + const BSONObj& document, + const BSONObj& writeConcern); - // This implementation does not understand uniqueness constraints, + // This implementation does not understand uniqueness constraints, ignores writeConcern, // and only correctly handles some upsert behaviors. virtual Status updateOne(const NamespaceString& collectionName, const BSONObj& query, const BSONObj& updatePattern, - bool upsert); + bool upsert, + const BSONObj& writeConcern); virtual Status remove(const NamespaceString& collectionName, - const BSONObj& query); + const BSONObj& query, + const BSONObj& writeConcern); virtual Status createIndex(const NamespaceString& collectionName, const BSONObj& pattern, - bool unique); - virtual Status dropCollection(const NamespaceString& collectionName); + bool unique, + const BSONObj& writeConcern); + virtual Status dropCollection(const NamespaceString& collectionName, + const BSONObj& writeConcern); virtual Status renameCollection(const NamespaceString& oldName, - const NamespaceString& newName); + const NamespaceString& newName, + const BSONObj& writeConcern); virtual Status copyCollection(const NamespaceString& fromName, - const NamespaceString& toName); + const NamespaceString& toName, + const BSONObj& writeConcern); virtual bool tryAcquireAuthzUpdateLock(const StringData& why); virtual void releaseAuthzUpdateLock(); 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 b87ba80eadf..8c38923f3c7 100644 --- a/src/mongo/db/auth/authz_manager_external_state_s.cpp +++ b/src/mongo/db/auth/authz_manager_external_state_s.cpp @@ -84,15 +84,20 @@ namespace { } Status AuthzManagerExternalStateMongos::insertPrivilegeDocument(const string& dbname, - const BSONObj& userObj) { + const BSONObj& userObj, + const BSONObj& writeConcern) { try { const std::string userNS = "admin.system.users"; scoped_ptr<ScopedDbConnection> conn(getConnectionForUsersCollection(userNS)); conn->get()->insert(userNS, userObj); - // 30 second timeout for w:majority - BSONObj res = conn->get()->getLastErrorDetailed(false, false, -1, 30*1000); + // Handle write concern + BSONObjBuilder gleBuilder; + gleBuilder.append("getLastError", 1); + gleBuilder.appendElements(writeConcern); + BSONObj res; + conn->get()->runCommand("admin", gleBuilder.done(), res); string errstr = conn->get()->getLastErrorString(res); conn->done(); @@ -113,7 +118,7 @@ namespace { } Status AuthzManagerExternalStateMongos::updatePrivilegeDocument( - const UserName& user, const BSONObj& updateObj) { + const UserName& user, const BSONObj& updateObj, const BSONObj& writeConcern) { try { const std::string userNS = "admin.system.users"; scoped_ptr<ScopedDbConnection> conn(getConnectionForUsersCollection(userNS)); @@ -124,8 +129,12 @@ namespace { AuthorizationManager::USER_SOURCE_FIELD_NAME << user.getDB()), updateObj); - // 30 second timeout for w:majority - BSONObj res = conn->get()->getLastErrorDetailed(false, false, -1, 30*1000); + // Handle write concern + BSONObjBuilder gleBuilder; + gleBuilder.append("getLastError", 1); + gleBuilder.appendElements(writeConcern); + BSONObj res; + conn->get()->runCommand("admin", gleBuilder.done(), res); string err = conn->get()->getLastErrorString(res); conn->done(); @@ -148,6 +157,7 @@ namespace { } Status AuthzManagerExternalStateMongos::removePrivilegeDocuments(const BSONObj& query, + const BSONObj& writeConcern, int* numRemoved) { try { string userNS = "admin.system.users"; @@ -155,8 +165,12 @@ namespace { conn->get()->remove(userNS, query); - // 30 second timeout for w:majority - BSONObj res = conn->get()->getLastErrorDetailed(false, false, -1, 30*1000); + // Handle write concern + BSONObjBuilder gleBuilder; + gleBuilder.append("getLastError", 1); + gleBuilder.appendElements(writeConcern); + BSONObj res; + conn->get()->runCommand("admin", gleBuilder.done(), res); string err = conn->get()->getLastErrorString(res); conn->done(); @@ -220,7 +234,8 @@ namespace { Status AuthzManagerExternalStateMongos::insert( const NamespaceString& collectionName, - const BSONObj& document) { + const BSONObj& document, + const BSONObj& writeConcern) { fassertFailed(17102); } @@ -228,37 +243,42 @@ namespace { const NamespaceString& collectionName, const BSONObj& query, const BSONObj& updatePattern, - bool upsert) { + bool upsert, + const BSONObj& writeConcern) { fassertFailed(17103); } Status AuthzManagerExternalStateMongos::remove( const NamespaceString& collectionName, - const BSONObj& query) { + const BSONObj& query, + const BSONObj& writeConcern) { fassertFailed(17104); } Status AuthzManagerExternalStateMongos::createIndex( const NamespaceString& collectionName, const BSONObj& pattern, - bool unique) { + bool unique, + const BSONObj& writeConcern) { fassertFailed(17105); } Status AuthzManagerExternalStateMongos::dropCollection( - const NamespaceString& collectionName) { + const NamespaceString& collectionName, const BSONObj& writeConcern) { fassertFailed(17106); } Status AuthzManagerExternalStateMongos::renameCollection( const NamespaceString& oldName, - const NamespaceString& newName) { + const NamespaceString& newName, + const BSONObj& writeConcern) { fassertFailed(17107); } Status AuthzManagerExternalStateMongos::copyCollection( const NamespaceString& fromName, - const NamespaceString& toName) { + const NamespaceString& toName, + const BSONObj& writeConcern) { fassertFailed(17108); } 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 406204bfd73..5d47b462fc1 100644 --- a/src/mongo/db/auth/authz_manager_external_state_s.h +++ b/src/mongo/db/auth/authz_manager_external_state_s.h @@ -50,12 +50,16 @@ namespace mongo { virtual ~AuthzManagerExternalStateMongos(); virtual Status insertPrivilegeDocument(const std::string& dbname, - const BSONObj& userObj); + const BSONObj& userObj, + const BSONObj& writeConcern); virtual Status updatePrivilegeDocument(const UserName& user, - const BSONObj& updateObj); + const BSONObj& updateObj, + const BSONObj& writeConcern); - virtual Status removePrivilegeDocuments(const BSONObj& query, int* numRemoved); + virtual Status removePrivilegeDocuments(const BSONObj& query, + const BSONObj& writeConcern, + int* numRemoved); virtual Status getAllDatabaseNames(std::vector<std::string>* dbnames); @@ -66,21 +70,28 @@ namespace mongo { const BSONObj& query, BSONObj* result); virtual Status insert(const NamespaceString& collectionName, - const BSONObj& document); + const BSONObj& document, + const BSONObj& writeConcern); virtual Status updateOne(const NamespaceString& collectionName, const BSONObj& query, const BSONObj& updatePattern, - bool upsert); + bool upsert, + const BSONObj& writeConcern); virtual Status remove(const NamespaceString& collectionName, - const BSONObj& query); + const BSONObj& query, + const BSONObj& writeConcern); virtual Status createIndex(const NamespaceString& collectionName, const BSONObj& pattern, - bool unique); - virtual Status dropCollection(const NamespaceString& collectionName); + bool unique, + const BSONObj& writeConcern); + virtual Status dropCollection(const NamespaceString& collectionName, + const BSONObj& writeConcern); virtual Status renameCollection(const NamespaceString& oldName, - const NamespaceString& newName); + const NamespaceString& newName, + const BSONObj& writeConcern); virtual Status copyCollection(const NamespaceString& fromName, - const NamespaceString& toName); + const NamespaceString& toName, + const BSONObj& writeConcern); virtual bool tryAcquireAuthzUpdateLock(const StringData& why); virtual void releaseAuthzUpdateLock(); diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp index 8739320b876..10382f06f62 100644 --- a/src/mongo/db/commands/user_management_commands.cpp +++ b/src/mongo/db/commands/user_management_commands.cpp @@ -67,6 +67,20 @@ namespace mongo { } } + static Status extractWriteConcern(const BSONObj cmdObj, BSONObj* writeConcern) { + BSONElement writeConcernElement; + Status status = bsonExtractTypedField(cmdObj, "writeConcern", Object, &writeConcernElement); + if (!status.isOK()) { + if (status.code() == ErrorCodes::NoSuchKey) { + *writeConcern = BSONObj(); + return Status::OK(); + } + return status; + } + *writeConcern = writeConcernElement.Obj(); + return Status::OK(); + } + class CmdCreateUser : public Command { public: @@ -114,7 +128,14 @@ namespace mongo { return false; } - status = authzManager->insertPrivilegeDocument(dbname, userObj); + BSONObj writeConcern; + status = extractWriteConcern(cmdObj, &writeConcern); + if (!status.isOK()) { + addStatus(status, result); + return false; + } + + status = authzManager->insertPrivilegeDocument(dbname, userObj, writeConcern); if (!status.isOK()) { addStatus(status, result); return false; @@ -177,7 +198,14 @@ namespace mongo { return false; } - status = authzManager->updatePrivilegeDocument(userName, updateObj); + BSONObj writeConcern; + status = extractWriteConcern(cmdObj, &writeConcern); + if (!status.isOK()) { + addStatus(status, result); + return false; + } + + status = authzManager->updatePrivilegeDocument(userName, updateObj, writeConcern); if (!status.isOK()) { addStatus(status, result); return false; @@ -237,11 +265,19 @@ namespace mongo { return false; } + BSONObj writeConcern; + status = extractWriteConcern(cmdObj, &writeConcern); + if (!status.isOK()) { + addStatus(status, result); + return false; + } + int numUpdated; AuthorizationManager* authzManager = getGlobalAuthorizationManager(); status = authzManager->removePrivilegeDocuments( BSON(AuthorizationManager::USER_NAME_FIELD_NAME << user << AuthorizationManager::USER_SOURCE_FIELD_NAME << dbname), + writeConcern, &numUpdated); if (!status.isOK()) { addStatus(status, result); @@ -298,10 +334,18 @@ namespace mongo { string& errmsg, BSONObjBuilder& result, bool fromRepl) { + BSONObj writeConcern; + Status status = extractWriteConcern(cmdObj, &writeConcern); + if (!status.isOK()) { + addStatus(status, result); + return false; + } + int numRemoved; AuthorizationManager* authzManager = getGlobalAuthorizationManager(); - Status status = authzManager->removePrivilegeDocuments( + status = authzManager->removePrivilegeDocuments( BSON(AuthorizationManager::USER_SOURCE_FIELD_NAME << dbname), + writeConcern, &numRemoved); if (!status.isOK()) { addStatus(status, result); diff --git a/src/mongo/db/commands/user_management_commands_parser.cpp b/src/mongo/db/commands/user_management_commands_parser.cpp index 2e4f42211fd..ff17d046653 100644 --- a/src/mongo/db/commands/user_management_commands_parser.cpp +++ b/src/mongo/db/commands/user_management_commands_parser.cpp @@ -116,6 +116,7 @@ namespace auth { validFieldNames.insert("customData"); validFieldNames.insert("pwd"); validFieldNames.insert("roles"); + validFieldNames.insert("writeConcern"); // Iterate through all fields in command object and make sure there are no unexpected // ones. @@ -218,6 +219,7 @@ namespace auth { validFieldNames.insert("customData"); validFieldNames.insert("pwd"); validFieldNames.insert("roles"); + validFieldNames.insert("writeConcern"); // Iterate through all fields in command object and make sure there are no unexpected // ones. diff --git a/src/mongo/shell/db.js b/src/mongo/shell/db.js index 8b54e9f2430..e6562684879 100644 --- a/src/mongo/shell/db.js +++ b/src/mongo/shell/db.js @@ -168,6 +168,10 @@ DB.prototype._createUser = function(userObj, replicatedTo, timeout) { delete cmdObj["name"]; delete cmdObj["user"]; + replicatedTo = replicatedTo != undefined && replicatedTo != null ? replicatedTo : "majority" + timeout = timeout || 30 * 1000; + cmdObj["writeConcern"] = { w: replicatedTo, wtimeout: timeout } + var res = this.runCommand(cmdObj); if (res.ok) { |