summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSpencer T Brody <spencer@10gen.com>2013-06-11 15:17:05 -0400
committerSpencer T Brody <spencer@10gen.com>2013-06-14 16:08:33 -0400
commit85a5413d651d00849d63b96386584e36d5d845e3 (patch)
tree3b322fb6f2491bd38d735bca01ad471ce27a6883
parentfcc103ba403fd35d96675f217c0e56d73eb14469 (diff)
downloadmongo-85a5413d651d00849d63b96386584e36d5d845e3.tar.gz
SERVER-6246 Add updateUser command and shell helper
-rw-r--r--src/mongo/client/dbclientinterface.h2
-rw-r--r--src/mongo/db/auth/authorization_manager.cpp5
-rw-r--r--src/mongo/db/auth/authorization_manager.h3
-rw-r--r--src/mongo/db/auth/authz_manager_external_state.h4
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_d.cpp30
-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.h4
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_s.cpp28
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_s.h4
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp100
-rw-r--r--src/mongo/shell/db.js20
-rw-r--r--src/mongo/shell/dbshell.cpp5
12 files changed, 199 insertions, 10 deletions
diff --git a/src/mongo/client/dbclientinterface.h b/src/mongo/client/dbclientinterface.h
index 04c44cdb0fc..0ece5b3a2a1 100644
--- a/src/mongo/client/dbclientinterface.h
+++ b/src/mongo/client/dbclientinterface.h
@@ -658,7 +658,7 @@ namespace mongo {
*/
virtual unsigned long long count(const string &ns, const BSONObj& query = BSONObj(), int options=0, int limit=0, int skip=0 );
- string createPasswordDigest( const string &username , const string &clearTextPassword );
+ static string createPasswordDigest(const string &username, const string &clearTextPassword);
/** returns true in isMaster parm if this db is the current master
of a replica pair.
diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp
index 593c5e72b19..85e64c68a8d 100644
--- a/src/mongo/db/auth/authorization_manager.cpp
+++ b/src/mongo/db/auth/authorization_manager.cpp
@@ -287,6 +287,11 @@ namespace {
return _externalState->insertPrivilegeDocument(dbname, userObj);
}
+ Status AuthorizationManager::updatePrivilegeDocument(const UserName& user,
+ const BSONObj& updateObj) const {
+ return _externalState->updatePrivilegeDocument(user, updateObj);
+ }
+
ActionSet AuthorizationManager::getAllUserActions() const {
ActionSet allActions;
allActions.addAllActionsFromSet(readRoleActions);
diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h
index 4b9c906ff89..730f72f6a11 100644
--- a/src/mongo/db/auth/authorization_manager.h
+++ b/src/mongo/db/auth/authorization_manager.h
@@ -95,6 +95,9 @@ namespace mongo {
// 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;
+
// 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/authz_manager_external_state.h b/src/mongo/db/auth/authz_manager_external_state.h
index d5c8d92aa20..a9af078e675 100644
--- a/src/mongo/db/auth/authz_manager_external_state.h
+++ b/src/mongo/db/auth/authz_manager_external_state.h
@@ -53,6 +53,10 @@ namespace mongo {
virtual Status insertPrivilegeDocument(const std::string& dbname,
const BSONObj& userObj) const = 0;
+ // Updates the given user object with the given update modifier.
+ virtual Status updatePrivilegeDocument(const UserName& user,
+ const BSONObj& updateObj) 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 e3ac620cb0b..5147fe826a5 100644
--- a/src/mongo/db/auth/authz_manager_external_state_d.cpp
+++ b/src/mongo/db/auth/authz_manager_external_state_d.cpp
@@ -17,6 +17,7 @@
#include "mongo/db/auth/authz_manager_external_state_d.h"
#include "mongo/base/status.h"
+#include "mongo/db/auth/user_name.h"
#include "mongo/db/client.h"
#include "mongo/db/dbhelpers.h"
#include "mongo/db/instance.h"
@@ -52,6 +53,35 @@ namespace mongo {
return Status(ErrorCodes::UserModificationFailed, errstr);
}
+ Status AuthzManagerExternalStateMongod::updatePrivilegeDocument(
+ const UserName& user, const BSONObj& updateObj) const {
+ string userNS = mongoutils::str::stream() << user.getDB() << ".system.users";
+ Client::GodScope gs;
+ Client::WriteContext ctx(userNS);
+
+ DBDirectClient client;
+ client.update(userNS,
+ QUERY("user" << user.getUser() << "userSource" << BSONNULL),
+ updateObj);
+
+ // 30 second timeout for w:majority
+ BSONObj res = client.getLastErrorDetailed(false, false, -1, 30*1000);
+ string err = client.getLastErrorString(res);
+ if (!err.empty()) {
+ return Status(ErrorCodes::UserModificationFailed, err);
+ }
+
+ int numUpdated = res["n"].numberInt();
+ dassert(numUpdated <= 1 && numUpdated >= 0);
+ if (numUpdated == 0) {
+ return Status(ErrorCodes::UserNotFound,
+ mongoutils::str::stream() << "User " << user.getFullName() <<
+ " not found");
+ }
+
+ return Status::OK();
+ }
+
bool AuthzManagerExternalStateMongod::_findUser(const string& usersNamespace,
const BSONObj& query,
BSONObj* result) const {
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 15982c27305..2158b642648 100644
--- a/src/mongo/db/auth/authz_manager_external_state_d.h
+++ b/src/mongo/db/auth/authz_manager_external_state_d.h
@@ -21,6 +21,7 @@
#include "mongo/base/disallow_copying.h"
#include "mongo/base/status.h"
#include "mongo/db/auth/authz_manager_external_state.h"
+#include "mongo/db/auth/user_name.h"
namespace mongo {
@@ -37,6 +38,9 @@ namespace mongo {
virtual Status insertPrivilegeDocument(const std::string& dbname,
const BSONObj& userObj) const;
+ virtual Status updatePrivilegeDocument(const UserName& user,
+ const BSONObj& updateObj) const;
+
protected:
virtual bool _findUser(const string& usersNamespace,
const BSONObj& query,
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 4b40a57de01..3a2eadd5456 100644
--- a/src/mongo/db/auth/authz_manager_external_state_mock.h
+++ b/src/mongo/db/auth/authz_manager_external_state_mock.h
@@ -40,6 +40,10 @@ namespace mongo {
return Status::OK();
}
+ virtual Status updatePrivilegeDocument(const UserName& user,
+ const BSONObj& updateObj) const {
+ return Status::OK();
+ }
virtual bool _findUser(const std::string& usersNamespace,
const BSONObj& query,
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 05cb28be292..15bfd9ec7c4 100644
--- a/src/mongo/db/auth/authz_manager_external_state_s.cpp
+++ b/src/mongo/db/auth/authz_manager_external_state_s.cpp
@@ -19,6 +19,7 @@
#include <string>
#include "mongo/client/dbclientinterface.h"
+#include "mongo/db/auth/user_name.h"
#include "mongo/db/jsobj.h"
#include "mongo/s/grid.h"
#include "mongo/util/assert_util.h"
@@ -74,4 +75,31 @@ namespace mongo {
return Status(ErrorCodes::UserModificationFailed, errstr);
}
+ Status AuthzManagerExternalStateMongos::updatePrivilegeDocument(
+ const UserName& user, const BSONObj& updateObj) const {
+ string userNS = mongoutils::str::stream() << user.getDB() << ".system.users";
+ scoped_ptr<ScopedDbConnection> conn(getConnectionForUsersCollection(userNS));
+
+ conn->get()->update(userNS,
+ QUERY("user" << user.getUser() << "userSource" << BSONNULL),
+ updateObj);
+
+ // 30 second timeout for w:majority
+ BSONObj res = conn->get()->getLastErrorDetailed(false, false, -1, 30*1000);
+ string err = conn->get()->getLastErrorString(res);
+ if (!err.empty()) {
+ return Status(ErrorCodes::UserModificationFailed, err);
+ }
+
+ int numUpdated = res["n"].numberInt();
+ dassert(numUpdated <= 1 && numUpdated >= 0);
+ if (numUpdated == 0) {
+ return Status(ErrorCodes::UserNotFound,
+ mongoutils::str::stream() << "User " << user.getFullName() <<
+ " not found");
+ }
+
+ return Status::OK();
+ }
+
} // 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 cf57205033c..2ff3d7fa4fb 100644
--- a/src/mongo/db/auth/authz_manager_external_state_s.h
+++ b/src/mongo/db/auth/authz_manager_external_state_s.h
@@ -21,6 +21,7 @@
#include "mongo/base/disallow_copying.h"
#include "mongo/base/status.h"
#include "mongo/db/auth/authz_manager_external_state.h"
+#include "mongo/db/auth/user_name.h"
namespace mongo {
@@ -37,6 +38,9 @@ namespace mongo {
virtual Status insertPrivilegeDocument(const std::string& dbname,
const BSONObj& userObj) const;
+ virtual Status updatePrivilegeDocument(const UserName& user,
+ const BSONObj& updateObj) const;
+
protected:
virtual bool _findUser(const string& usersNamespace,
const BSONObj& query,
diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp
index 6221ffc2a08..acf482e29b6 100644
--- a/src/mongo/db/commands/user_management_commands.cpp
+++ b/src/mongo/db/commands/user_management_commands.cpp
@@ -19,6 +19,7 @@
#include "mongo/base/status.h"
#include "mongo/bson/util/bson_extract.h"
+#include "mongo/client/dbclientinterface.h"
#include "mongo/db/auth/action_set.h"
#include "mongo/db/auth/action_type.h"
#include "mongo/db/auth/authorization_manager.h"
@@ -165,7 +166,7 @@ namespace mongo {
}
if (cmdObj.hasField("extraData")) {
- userObjBuilder.append("extraData", extraData);
+ userObjBuilder.append(extraData);
}
if (cmdObj.hasField("roles")) {
@@ -182,4 +183,101 @@ namespace mongo {
return true;
}
} cmdCreateUser;
+
+ class CmdUpdateUser : public Command {
+ public:
+
+ virtual bool logTheOp() {
+ return false;
+ }
+
+ virtual bool slaveOk() const {
+ return false;
+ }
+
+ virtual LockType locktype() const {
+ return NONE;
+ }
+
+ CmdUpdateUser() : Command("updateUser") {}
+
+ virtual void help(stringstream& ss) const {
+ ss << "Used to update a user, for example to change its password" << endl;
+ }
+
+ virtual void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) {
+ // TODO: update this with the new rules around user creation in 2.6.
+ ActionSet actions;
+ actions.addAction(ActionType::userAdmin);
+ out->push_back(Privilege(dbname, actions));
+ }
+
+ bool run(const string& dbname,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result,
+ bool fromRepl) {
+ std::string userName;
+ std::string clearTextPassword;
+ std::string password;
+ BSONElement extraData;
+
+ if (!cmdObj.hasField("pwd") && !cmdObj.hasField("extraData")) {
+ errmsg = "updateUser: must specify at least one of 'pwd' and 'extraData'";
+ return false;
+ }
+
+ Status status = bsonExtractStringField(cmdObj, "user", &userName);
+ if (!status.isOK()) {
+ addStatus(Status(ErrorCodes::UserModificationFailed,
+ "\"user\" string not specified"),
+ result);
+ return false;
+ }
+
+
+ status = bsonExtractStringFieldWithDefault(cmdObj, "pwd", "", &clearTextPassword);
+ if (!status.isOK()) {
+ addStatus(Status(ErrorCodes::UserModificationFailed,
+ "invalid \"pwd\" string"),
+ result);
+ return false;
+ }
+ password = DBClientWithCommands::createPasswordDigest(userName, clearTextPassword);
+
+ if (cmdObj.hasField("extraData")) {
+ status = bsonExtractField(cmdObj, "extraData", &extraData);
+ if (!status.isOK()) {
+ addStatus(Status(ErrorCodes::UserModificationFailed,
+ "invalid \"extraData\" string"),
+ result);
+ return false;
+ }
+ }
+
+ // TODO: This update will have to change once we're using the new v2 user
+ // storage format.
+ BSONObjBuilder setBuilder;
+ if (cmdObj.hasField("pwd")) {
+ setBuilder.append("pwd", password);
+ }
+ if (cmdObj.hasField("extraData")) {
+ setBuilder.append(extraData);
+ }
+ BSONObj updateObj = BSON("$set" << setBuilder.obj());
+
+ status = getGlobalAuthorizationManager()->updatePrivilegeDocument(
+ UserName(userName, dbname), updateObj);
+
+ if (!status.isOK()) {
+ addStatus(status, result);
+ return false;
+ }
+
+ return true;
+ }
+ } cmdUpdateUser;
}
diff --git a/src/mongo/shell/db.js b/src/mongo/shell/db.js
index a41172ecf1e..06c3a45971a 100644
--- a/src/mongo/shell/db.js
+++ b/src/mongo/shell/db.js
@@ -178,14 +178,20 @@ DB.prototype.addUser = function() {
}
}
-DB.prototype.changeUserPassword = function(username, password) {
- var hashedPassword = _hashPassword(username, password);
- db.system.users.update({user : username, userSource : null}, {$set : {pwd : hashedPassword}});
- var err = db.getLastError();
- if (err) {
- throw "Changing password failed: " + err;
+DB.prototype.updateUser = function(updateObject) {
+ var cmdObj = {updateUser:1};
+ cmdObj = Object.extend(cmdObj, updateObject);
+ var res = this.runCommand(cmdObj);
+ if (res.ok) {
+ return;
}
-}
+ throw Error("Updating user failed: " + res.errmsg);
+};
+
+DB.prototype.changeUserPassword = function(username, password) {
+ var updateObject = { user: username, pwd: password};
+ this.updateUser(updateObject);
+};
DB.prototype.logout = function(){
return this.getMongo().logout(this.getName());
diff --git a/src/mongo/shell/dbshell.cpp b/src/mongo/shell/dbshell.cpp
index cfaeef04433..7fb986b8928 100644
--- a/src/mongo/shell/dbshell.cpp
+++ b/src/mongo/shell/dbshell.cpp
@@ -129,7 +129,10 @@ void shellHistoryAdd( const char * line ) {
// We don't want any .auth() or .addUser() commands added, but we want to
// be able to add things like `.author`, so be smart about how this is
// detected by using regular expresions.
- static pcrecpp::RE hiddenCommands("\\.(auth|addUser)\\s*\\(");
+ // TODO(spencer): If the createUser or updateUser commands are run directly via runCommand they
+ // are not filtered from the history.
+ static pcrecpp::RE hiddenCommands(
+ "\\.(auth|addUser|createUser|updateUser|changeUserPassword)\\s*\\(");
if (!hiddenCommands.PartialMatch(line))
{
linenoiseHistoryAdd( line );