summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorSpencer T Brody <spencer@10gen.com>2013-09-09 15:33:45 -0400
committerSpencer T Brody <spencer@10gen.com>2013-09-12 13:58:46 -0400
commit5d0cb99278bcd6a476fd85b901f37170ce64c2af (patch)
tree00a660b8ba8213e40ed8f2789df6cf755131be91 /src/mongo
parentb7565690f4f3e40df4d959cfcac095d7d85b7a48 (diff)
downloadmongo-5d0cb99278bcd6a476fd85b901f37170ce64c2af.tar.gz
SERVER-9980 Add write concern to user management commands
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/auth/authorization_manager.cpp33
-rw-r--r--src/mongo/db/auth/authorization_manager.h35
-rw-r--r--src/mongo/db/auth/authorization_manager_test.cpp53
-rw-r--r--src/mongo/db/auth/authorization_session_test.cpp27
-rw-r--r--src/mongo/db/auth/authz_manager_external_state.h31
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_d.cpp51
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_d.h31
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_mock.cpp34
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_mock.h33
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_s.cpp50
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_s.h31
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp50
-rw-r--r--src/mongo/db/commands/user_management_commands_parser.cpp2
-rw-r--r--src/mongo/shell/db.js4
14 files changed, 333 insertions, 132 deletions
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) {