summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Milkie <milkie@10gen.com>2013-12-19 17:12:12 -0500
committerEric Milkie <milkie@10gen.com>2013-12-24 14:04:22 -0500
commitf148caa11e97727949b5e8594b7439856faff499 (patch)
treebf19ff059f11538c2a87c4e3e685f70e5a026937
parentee0365ac659fa89758263d19327d69c547eb2d50 (diff)
downloadmongo-f148caa11e97727949b5e8594b7439856faff499.tar.gz
SERVER-11192 hook c++ driver to transmit mongos authenticated users to mongod, for auditing
Also, add UserNameIterator as a parallel to RoleIterator.
-rw-r--r--src/mongo/client/dbclient.cpp15
-rw-r--r--src/mongo/client/dbclient_rs.cpp11
-rw-r--r--src/mongo/client/dbclient_rs.h2
-rw-r--r--src/mongo/client/dbclientcursor.cpp9
-rw-r--r--src/mongo/client/dbclientinterface.h18
-rw-r--r--src/mongo/client/syncclusterconnection.cpp8
-rw-r--r--src/mongo/client/syncclusterconnection.h2
-rw-r--r--src/mongo/db/audit.cpp9
-rw-r--r--src/mongo/db/audit.h29
-rw-r--r--src/mongo/db/auth/action_types.txt1
-rw-r--r--src/mongo/db/auth/authorization_session.cpp30
-rw-r--r--src/mongo/db/auth/authorization_session.h22
-rw-r--r--src/mongo/db/auth/user_management_commands_parser.cpp8
-rw-r--r--src/mongo/db/auth/user_management_commands_parser.h11
-rw-r--r--src/mongo/db/auth/user_name.h76
-rw-r--r--src/mongo/db/auth/user_set.cpp24
-rw-r--r--src/mongo/db/auth/user_set.h33
-rw-r--r--src/mongo/db/auth/user_set_test.cpp4
-rw-r--r--src/mongo/db/commands/connection_status.cpp2
-rw-r--r--src/mongo/db/dbcommands.cpp45
-rw-r--r--src/mongo/db/introspect.cpp2
-rw-r--r--src/mongo/db/server_extra_log_context.cpp4
-rw-r--r--src/mongo/s/dbclient_multi_command.cpp10
-rw-r--r--src/mongo/s/shard.cpp6
24 files changed, 330 insertions, 51 deletions
diff --git a/src/mongo/client/dbclient.cpp b/src/mongo/client/dbclient.cpp
index e815bd464c9..261247362e6 100644
--- a/src/mongo/client/dbclient.cpp
+++ b/src/mongo/client/dbclient.cpp
@@ -412,12 +412,25 @@ namespace mongo {
return QueryOptions(0);
}
+ void DBClientWithCommands::setRunCommandHook(RunCommandHookFunc func) {
+ _runCommandHook = func;
+ }
+
bool DBClientWithCommands::runCommand(const string &dbname,
const BSONObj& cmd,
BSONObj &info,
int options) {
string ns = dbname + ".$cmd";
- info = findOne(ns, cmd, 0 , options);
+ if (_runCommandHook) {
+ BSONObjBuilder cmdObj;
+ cmdObj.appendElements(cmd);
+ _runCommandHook(&cmdObj);
+
+ info = findOne(ns, cmdObj.done(), 0 , options);
+ }
+ else {
+ info = findOne(ns, cmd, 0 , options);
+ }
return isOk(info);
}
diff --git a/src/mongo/client/dbclient_rs.cpp b/src/mongo/client/dbclient_rs.cpp
index e70d192a21b..d381f4adb8f 100644
--- a/src/mongo/client/dbclient_rs.cpp
+++ b/src/mongo/client/dbclient_rs.cpp
@@ -1393,6 +1393,15 @@ namespace mongo {
return rsm->getServerAddress();
}
+ void DBClientReplicaSet::setRunCommandHook(DBClientWithCommands::RunCommandHookFunc func) {
+ if (_master) {
+ _master->setRunCommandHook(func);
+ }
+ if (_lastSlaveOkConn) {
+ _lastSlaveOkConn->setRunCommandHook(func);
+ }
+ }
+
// A replica set connection is never disconnected, since it controls its own reconnection
// logic.
//
@@ -1504,6 +1513,7 @@ namespace mongo {
_master.reset(newConn);
_master->setReplSetClientCallback(this);
+ _master->setRunCommandHook(_runCommandHook);
_auth( _master.get() );
return _master.get();
@@ -1888,6 +1898,7 @@ namespace mongo {
_lastSlaveOkConn.reset(newConn);
_lastSlaveOkConn->setReplSetClientCallback(this);
+ _lastSlaveOkConn->setRunCommandHook(_runCommandHook);
_auth(_lastSlaveOkConn.get());
diff --git a/src/mongo/client/dbclient_rs.h b/src/mongo/client/dbclient_rs.h
index 282479b00f5..7bcd2cc2eeb 100644
--- a/src/mongo/client/dbclient_rs.h
+++ b/src/mongo/client/dbclient_rs.h
@@ -531,6 +531,8 @@ namespace mongo {
const BSONObj& queryObj,
int queryOptions );
+ virtual void setRunCommandHook(DBClientWithCommands::RunCommandHookFunc func);
+
protected:
/** Authorize. Authorizes all nodes as needed
*/
diff --git a/src/mongo/client/dbclientcursor.cpp b/src/mongo/client/dbclientcursor.cpp
index f6dc5758579..8ed7b8f5773 100644
--- a/src/mongo/client/dbclientcursor.cpp
+++ b/src/mongo/client/dbclientcursor.cpp
@@ -78,6 +78,15 @@ namespace mongo {
void DBClientCursor::initLazy( bool isRetry ) {
massert( 15875 , "DBClientCursor::initLazy called on a client that doesn't support lazy" , _client->lazySupported() );
+ if (DBClientWithCommands::RunCommandHookFunc hook = _client->getRunCommandHook()) {
+ if (NamespaceString(ns).isCommand()) {
+ BSONObjBuilder bob;
+ bob.appendElements(query);
+ hook(&bob);
+ query = bob.obj();
+ }
+ }
+
Message toSend;
_assembleInit( toSend );
_client->say( toSend, isRetry, &_originalHost );
diff --git a/src/mongo/client/dbclientinterface.h b/src/mongo/client/dbclientinterface.h
index 49226fb03c5..13d2939fba8 100644
--- a/src/mongo/client/dbclientinterface.h
+++ b/src/mongo/client/dbclientinterface.h
@@ -957,6 +957,19 @@ namespace mongo {
virtual string toString() = 0;
+ /**
+ * A function type for runCommand hooking; the function takes a pointer
+ * to a BSONObjBuilder and returns nothing. The builder contains a
+ * runCommand BSON object.
+ * Once such a function is set as the runCommand hook, every time the DBClient
+ * processes a runCommand, the hook will be called just prior to sending it to the server.
+ */
+ typedef boost::function<void(BSONObjBuilder*)> RunCommandHookFunc;
+ virtual void setRunCommandHook(RunCommandHookFunc func);
+ RunCommandHookFunc getRunCommandHook() const {
+ return _runCommandHook;
+ }
+
protected:
/** if the result of a command is ok*/
bool isOk(const BSONObj&);
@@ -996,6 +1009,11 @@ namespace mongo {
const string &username,
BSONObj *info);
+ /**
+ * This function will be executed by the driver on runCommand calls.
+ */
+ RunCommandHookFunc _runCommandHook;
+
private:
enum QueryOptions _cachedAvailableOptions;
bool _haveCachedAvailableOptions;
diff --git a/src/mongo/client/syncclusterconnection.cpp b/src/mongo/client/syncclusterconnection.cpp
index 6e63a7d688d..361b0c83cc7 100644
--- a/src/mongo/client/syncclusterconnection.cpp
+++ b/src/mongo/client/syncclusterconnection.cpp
@@ -161,6 +161,7 @@ namespace mongo {
void SyncClusterConnection::_connect( const std::string& host ) {
log() << "SyncClusterConnection connecting to [" << host << "]" << endl;
DBClientConnection * c = new DBClientConnection( true );
+ c->setRunCommandHook(_runCommandHook);
c->setSoTimeout( _socketTimeout );
string errmsg;
if ( ! c->connect( host , errmsg ) )
@@ -513,4 +514,11 @@ namespace mongo {
if( _conns[i] ) _conns[i]->setSoTimeout( socketTimeout );
}
+ void SyncClusterConnection::setRunCommandHook(DBClientWithCommands::RunCommandHookFunc func) {
+ for (size_t i = 0; i < _conns.size(); ++i) {
+ if (_conns[i]) {
+ _conns[i]->setRunCommandHook(func);
+ }
+ }
+ }
}
diff --git a/src/mongo/client/syncclusterconnection.h b/src/mongo/client/syncclusterconnection.h
index cd81be44c6e..5994f8966cb 100644
--- a/src/mongo/client/syncclusterconnection.h
+++ b/src/mongo/client/syncclusterconnection.h
@@ -112,6 +112,8 @@ namespace mongo {
virtual bool lazySupported() const { return false; }
+ virtual void setRunCommandHook(DBClientWithCommands::RunCommandHookFunc func);
+
protected:
virtual void _auth(const BSONObj& params);
diff --git a/src/mongo/db/audit.cpp b/src/mongo/db/audit.cpp
index 226d76c331d..9b3dd2aa02d 100644
--- a/src/mongo/db/audit.cpp
+++ b/src/mongo/db/audit.cpp
@@ -208,6 +208,15 @@ namespace audit {
const StringData& ns,
const BSONObj& keyPattern,
bool unique) MONGO_AUDIT_STUB
+
+ void appendImpersonatedUsers(BSONObjBuilder* cmd) MONGO_AUDIT_STUB
+
+ void parseAndRemoveImpersonatedUserField(BSONObj cmdObj,
+ AuthorizationSession* authSession,
+ std::vector<UserName>* parsedUserNames,
+ bool* fieldIsPresent)
+ MONGO_AUDIT_STUB
+
} // namespace audit
} // namespace mongo
diff --git a/src/mongo/db/audit.h b/src/mongo/db/audit.h
index 2948c9a4b0e..b939966f168 100644
--- a/src/mongo/db/audit.h
+++ b/src/mongo/db/audit.h
@@ -39,6 +39,7 @@
namespace mongo {
+ class AuthorizationSession;
class BSONObj;
class ClientBasic;
class NamespaceString;
@@ -349,5 +350,33 @@ namespace audit {
const BSONObj& keyPattern,
bool unique);
+
+ /*
+ * Appends an array of user/db pairs to the provided Document.
+ * The users are extracted from the current client. They are to be the
+ * impersonated users for a Command run by an internal user.
+ */
+ void appendImpersonatedUsers(BSONObjBuilder* cmd);
+ const char cmdOptionImpersonatedUsers[] = "impersonatedUsers";
+
+ /*
+ * Looks for an 'impersonatedUsers' field. This field is used by mongos to
+ * transmit the usernames of the currently authenticated user when it runs commands
+ * on a shard using internal user authentication. Auditing uses this information
+ * to properly ascribe users to actions. This is necessary only for implicit actions that
+ * mongos cannot properly audit itself; examples are implicit collection and database creation.
+ * This function requires that the field is the last field in the bson object; it edits the
+ * command BSON to efficiently remove the field before returning.
+ *
+ * cmdObj [in, out]: If any impersonated users field exists, it will be parsed and removed.
+ * authSession [in]: current authorization session
+ * parsedUserNames [out]: populated with parsed usernames
+ * fieldIsPresent [out]: true if impersonatedUsers field was present in the object
+ */
+ void parseAndRemoveImpersonatedUserField(BSONObj cmdObj,
+ AuthorizationSession* authSession,
+ std::vector<UserName>* parsedUserNames,
+ bool* fieldIsPresent);
+
} // namespace audit
} // namespace mongo
diff --git a/src/mongo/db/auth/action_types.txt b/src/mongo/db/auth/action_types.txt
index 9c06abbc2fb..ecf7bf9ad87 100644
--- a/src/mongo/db/auth/action_types.txt
+++ b/src/mongo/db/auth/action_types.txt
@@ -59,6 +59,7 @@
"grantRolesToUser", # Not used for permissions checks, but to id the event in logs.
"handshake",
"hostInfo",
+"impersonate",
"indexStats",
"inprog",
"insert",
diff --git a/src/mongo/db/auth/authorization_session.cpp b/src/mongo/db/auth/authorization_session.cpp
index bbab218f0b1..a9f0887253e 100644
--- a/src/mongo/db/auth/authorization_session.cpp
+++ b/src/mongo/db/auth/authorization_session.cpp
@@ -51,7 +51,8 @@ namespace {
const std::string ADMIN_DBNAME = "admin";
} // namespace
- AuthorizationSession::AuthorizationSession(AuthzSessionExternalState* externalState) {
+ AuthorizationSession::AuthorizationSession(AuthzSessionExternalState* externalState)
+ : _impersonationFlag(false) {
_externalState.reset(externalState);
}
@@ -78,6 +79,9 @@ namespace {
return status;
}
+ // If there are any users in the impersonate list, clear them.
+ clearImpersonatedUserNames();
+
// Calling add() on the UserSet may return a user that was replaced because it was from the
// same database.
User* replacedUser = _authenticatedUsers.add(user);
@@ -97,19 +101,20 @@ namespace {
}
void AuthorizationSession::logoutDatabase(const std::string& dbname) {
+ clearImpersonatedUserNames();
User* removedUser = _authenticatedUsers.removeByDBName(dbname);
if (removedUser) {
getAuthorizationManager().releaseUser(removedUser);
}
}
- UserSet::NameIterator AuthorizationSession::getAuthenticatedUserNames() {
+ UserNameIterator AuthorizationSession::getAuthenticatedUserNames() {
return _authenticatedUsers.getNames();
}
std::string AuthorizationSession::getAuthenticatedUserNamesToken() {
std::string ret;
- for (UserSet::NameIterator nameIter = getAuthenticatedUserNames();
+ for (UserNameIterator nameIter = getAuthenticatedUserNames();
nameIter.more();
nameIter.next()) {
ret += '\0'; // Using a NUL byte which isn't valid in usernames to separate them.
@@ -475,4 +480,23 @@ namespace {
return false;
}
+ void AuthorizationSession::setImpersonatedUserNames(const std::vector<UserName>& names) {
+ _impersonatedUserNames = names;
+ _impersonationFlag = true;
+ }
+
+ // Clear the vector of impersonated UserNames.
+ void AuthorizationSession::clearImpersonatedUserNames() {
+ _impersonatedUserNames.clear();
+ _impersonationFlag = false;
+ }
+
+ UserNameIterator AuthorizationSession::getImpersonatedUserNames() const {
+ return makeUserNameIteratorForContainer(_impersonatedUserNames);
+ }
+
+ bool AuthorizationSession::isImpersonating() const {
+ return _impersonationFlag;
+ }
+
} // namespace mongo
diff --git a/src/mongo/db/auth/authorization_session.h b/src/mongo/db/auth/authorization_session.h
index c9c9178791a..3a64792b611 100644
--- a/src/mongo/db/auth/authorization_session.h
+++ b/src/mongo/db/auth/authorization_session.h
@@ -89,7 +89,7 @@ namespace mongo {
size_t getNumAuthenticatedUsers();
// Gets an iterator over the names of all authenticated users stored in this manager.
- UserSet::NameIterator getAuthenticatedUserNames();
+ UserNameIterator getAuthenticatedUserNames();
// Returns a string representing all logged-in users on the current session.
// WARNING: this string will contain NUL bytes so don't call c_str()!
@@ -180,6 +180,21 @@ namespace mongo {
// isAuthorizedForActionsOnResource(ResourcePattern::forExactNamespace(ns), actions).
bool isAuthorizedForActionsOnNamespace(const NamespaceString& ns, const ActionSet& actions);
+ // Replaces the vector of UserNames that a system user is impersonating with a new vector.
+ // The auditing system adds these to each audit record in the log.
+ void setImpersonatedUserNames(const std::vector<UserName>& names);
+
+ // Returns an iterator to a vector of impersonated usernames.
+ UserNameIterator getImpersonatedUserNames() const;
+
+ // Clears the vector of impersonated UserNames.
+ void clearImpersonatedUserNames();
+
+ // Tells whether impersonation is active or not. This state is set when
+ // setImpersonatedUserNames is called and cleared when clearImpersonatedUserNames is
+ // called.
+ bool isImpersonating() const;
+
private:
// If any users authenticated on this session are marked as invalid this updates them with
@@ -195,6 +210,11 @@ namespace mongo {
// All Users who have been authenticated on this connection
UserSet _authenticatedUsers;
+
+ // A vector of impersonated UserNames. These are used in the auditing system.
+ // They are not used for authz checks.
+ std::vector<UserName> _impersonatedUserNames;
+ bool _impersonationFlag;
};
} // namespace mongo
diff --git a/src/mongo/db/auth/user_management_commands_parser.cpp b/src/mongo/db/auth/user_management_commands_parser.cpp
index 002880c2050..61023fe6a25 100644
--- a/src/mongo/db/auth/user_management_commands_parser.cpp
+++ b/src/mongo/db/auth/user_management_commands_parser.cpp
@@ -137,7 +137,7 @@ namespace auth {
return Status::OK();
}
- Status _parseUserNamesFromBSONArray(const BSONArray& usersArray,
+ Status parseUserNamesFromBSONArray(const BSONArray& usersArray,
const StringData& dbname,
std::vector<UserName>* parsedUserNames) {
return _parseNamesFromBSONArray(usersArray,
@@ -359,9 +359,9 @@ namespace auth {
if (cmdObj["usersInfo"].numberInt() == 1) {
parsedArgs->allForDB = true;
} else if (cmdObj["usersInfo"].type() == Array) {
- status = _parseUserNamesFromBSONArray(BSONArray(cmdObj["usersInfo"].Obj()),
- dbname,
- &parsedArgs->userNames);
+ status = parseUserNamesFromBSONArray(BSONArray(cmdObj["usersInfo"].Obj()),
+ dbname,
+ &parsedArgs->userNames);
if (!status.isOK()) {
return status;
}
diff --git a/src/mongo/db/auth/user_management_commands_parser.h b/src/mongo/db/auth/user_management_commands_parser.h
index d5e10f7574d..befeb2731ba 100644
--- a/src/mongo/db/auth/user_management_commands_parser.h
+++ b/src/mongo/db/auth/user_management_commands_parser.h
@@ -194,7 +194,7 @@ namespace auth {
PrivilegeVector* parsedPrivileges);
/**
- * Takes a BSONArray of name,source pair documents, parses that array and returns (via the
+ * Takes a BSONArray of name,db pair documents, parses that array and returns (via the
* output param parsedRoleNames) a list of the role names in the input array.
* Performs syntactic validation of "rolesArray", only.
*/
@@ -202,5 +202,14 @@ namespace auth {
const StringData& dbname,
std::vector<RoleName>* parsedRoleNames);
+ /**
+ * Takes a BSONArray of name,db pair documents, parses that array and returns (via the
+ * output param parsedUserNames) a list of the usernames in the input array.
+ * Performs syntactic validation of "usersArray", only.
+ */
+ Status parseUserNamesFromBSONArray(const BSONArray& usersArray,
+ const StringData& dbname,
+ std::vector<UserName>* parsedUserNames);
+
} // namespace auth
} // namespace mongo
diff --git a/src/mongo/db/auth/user_name.h b/src/mongo/db/auth/user_name.h
index acee87f8ada..9104fded846 100644
--- a/src/mongo/db/auth/user_name.h
+++ b/src/mongo/db/auth/user_name.h
@@ -18,6 +18,9 @@
#include <iosfwd>
#include <string>
+#include <boost/scoped_ptr.hpp>
+
+#include "mongo/base/disallow_copying.h"
#include "mongo/base/string_data.h"
namespace mongo {
@@ -71,4 +74,77 @@ namespace mongo {
std::ostream& operator<<(std::ostream& os, const UserName& name);
+ /**
+ * Iterator over an unspecified container of UserName objects.
+ */
+ class UserNameIterator {
+ public:
+ class Impl {
+ MONGO_DISALLOW_COPYING(Impl);
+ public:
+ Impl() {};
+ virtual ~Impl() {};
+ static Impl* clone(Impl* orig) { return orig ? orig->doClone(): NULL; }
+ virtual bool more() const = 0;
+ virtual const UserName& get() const = 0;
+
+ virtual const UserName& next() = 0;
+
+ private:
+ virtual Impl* doClone() const = 0;
+ };
+
+ UserNameIterator() : _impl(NULL) {}
+ UserNameIterator(const UserNameIterator& other) : _impl(Impl::clone(other._impl.get())) {}
+ explicit UserNameIterator(Impl* impl) : _impl(impl) {}
+
+ UserNameIterator& operator=(const UserNameIterator& other) {
+ _impl.reset(Impl::clone(other._impl.get()));
+ return *this;
+ }
+
+ bool more() const { return _impl.get() && _impl->more(); }
+ const UserName& get() const { return _impl->get(); }
+
+ const UserName& next() { return _impl->next(); }
+
+ const UserName& operator*() const { return get(); }
+ const UserName* operator->() const { return &get(); }
+
+ private:
+ boost::scoped_ptr<Impl> _impl;
+ };
+
+
+ template <typename ContainerIterator>
+ class UserNameContainerIteratorImpl : public UserNameIterator::Impl {
+ MONGO_DISALLOW_COPYING(UserNameContainerIteratorImpl);
+ public:
+ UserNameContainerIteratorImpl(const ContainerIterator& begin,
+ const ContainerIterator& end) :
+ _curr(begin), _end(end) {}
+ virtual ~UserNameContainerIteratorImpl() {}
+ virtual bool more() const { return _curr != _end; }
+ virtual const UserName& next() { return *(_curr++); }
+ virtual const UserName& get() const { return *_curr; }
+ virtual UserNameIterator::Impl* doClone() const {
+ return new UserNameContainerIteratorImpl(_curr, _end);
+ }
+
+ private:
+ ContainerIterator _curr;
+ ContainerIterator _end;
+ };
+
+ template <typename ContainerIterator>
+ UserNameIterator makeUserNameIterator(const ContainerIterator& begin,
+ const ContainerIterator& end) {
+ return UserNameIterator( new UserNameContainerIteratorImpl<ContainerIterator>(begin, end));
+ }
+
+ template <typename Container>
+ UserNameIterator makeUserNameIteratorForContainer(const Container& container) {
+ return makeUserNameIterator(container.begin(), container.end());
+ }
+
} // namespace mongo
diff --git a/src/mongo/db/auth/user_set.cpp b/src/mongo/db/auth/user_set.cpp
index c36a1c093f7..34c1318ab9d 100644
--- a/src/mongo/db/auth/user_set.cpp
+++ b/src/mongo/db/auth/user_set.cpp
@@ -23,6 +23,27 @@
namespace mongo {
+namespace {
+ class UserSetNameIteratorImpl : public UserNameIterator::Impl {
+ MONGO_DISALLOW_COPYING(UserSetNameIteratorImpl);
+ public:
+ UserSetNameIteratorImpl(const UserSet::iterator& begin,
+ const UserSet::iterator& end) :
+ _curr(begin), _end(end) {}
+ virtual ~UserSetNameIteratorImpl() {}
+ virtual bool more() const { return _curr != _end; }
+ virtual const UserName& next() { return (*(_curr++))->getName(); }
+ virtual const UserName& get() const { return (*_curr)->getName(); }
+ virtual UserNameIterator::Impl* doClone() const {
+ return new UserSetNameIteratorImpl(_curr, _end);
+ }
+
+ private:
+ UserSet::iterator _curr;
+ UserSet::iterator _end;
+ };
+} // namespace
+
UserSet::UserSet() : _users(), _usersEnd(_users.end()) {}
UserSet::~UserSet() {}
@@ -90,4 +111,7 @@ namespace mongo {
return NULL;
}
+ UserNameIterator UserSet::getNames() const {
+ return UserNameIterator(new UserSetNameIteratorImpl(begin(), end()));
+ }
} // namespace mongo
diff --git a/src/mongo/db/auth/user_set.h b/src/mongo/db/auth/user_set.h
index ad193654e52..591a9bdb9fa 100644
--- a/src/mongo/db/auth/user_set.h
+++ b/src/mongo/db/auth/user_set.h
@@ -37,37 +37,6 @@ namespace mongo {
public:
typedef std::vector<User*>::const_iterator iterator;
- /**
- * Forward iterator over the names of the users stored in a UserSet.
- *
- * Instances are valid until the underlying vector<User*> is modified.
- *
- * more() must be the first method called after construction, and must be checked
- * after each call to next() before calling any other methods.
- */
- class NameIterator {
- public:
- explicit NameIterator(iterator begin, iterator end) : _curr(begin), _end(end) {}
-
- NameIterator() {}
-
- bool more() { return _curr != _end; }
- const UserName& next() {
- const UserName& ret = get();
- ++_curr;
- return ret;
- }
-
- const UserName& get() const { return (*_curr)->getName(); }
-
- const UserName& operator*() const { return get(); }
- const UserName* operator->() const { return &get(); }
-
- private:
- std::vector<User*>::const_iterator _curr;
- std::vector<User*>::const_iterator _end;
- };
-
UserSet();
~UserSet();
@@ -121,7 +90,7 @@ namespace mongo {
// Gets an iterator over the names of the users stored in the set. The iterator is
// valid until the next non-const method is called on the UserSet.
- NameIterator getNames() const { return NameIterator(begin(), end()); }
+ UserNameIterator getNames() const;
iterator begin() const { return _users.begin(); }
iterator end() const { return _usersEnd; }
diff --git a/src/mongo/db/auth/user_set_test.cpp b/src/mongo/db/auth/user_set_test.cpp
index fb30d00bee7..5063f727412 100644
--- a/src/mongo/db/auth/user_set_test.cpp
+++ b/src/mongo/db/auth/user_set_test.cpp
@@ -75,7 +75,7 @@ namespace {
ASSERT_EQUALS(p3, set.lookup(UserName("Bob", "test2")));
ASSERT_EQUALS(p3, set.lookupByDBName("test2"));
- UserSet::NameIterator iter = set.getNames();
+ UserNameIterator iter = set.getNames();
ASSERT_TRUE(iter.more());
ASSERT_EQUALS(iter.next(), UserName("Bob", "test2"));
ASSERT_FALSE(iter.more());
@@ -83,7 +83,7 @@ namespace {
TEST(UserSetTest, IterateNames) {
UserSet pset;
- UserSet::NameIterator iter = pset.getNames();
+ UserNameIterator iter = pset.getNames();
ASSERT(!iter.more());
ASSERT_NULL(pset.add(new User(UserName("bob", "test"))));
diff --git a/src/mongo/db/commands/connection_status.cpp b/src/mongo/db/commands/connection_status.cpp
index 680ae1730d0..a4d173c0143 100644
--- a/src/mongo/db/commands/connection_status.cpp
+++ b/src/mongo/db/commands/connection_status.cpp
@@ -56,7 +56,7 @@ namespace mongo {
{
BSONArrayBuilder authenticatedUsers(authInfo.subarrayStart("authenticatedUsers"));
- UserSet::NameIterator nameIter = authSession->getAuthenticatedUserNames();
+ UserNameIterator nameIter = authSession->getAuthenticatedUserNames();
for ( ; nameIter.more(); nameIter.next()) {
BSONObjBuilder userInfoBuilder(authenticatedUsers.subobjStart());
userInfoBuilder.append(AuthorizationManager::USER_NAME_FIELD_NAME,
diff --git a/src/mongo/db/dbcommands.cpp b/src/mongo/db/dbcommands.cpp
index bc277328bbc..7a39783a3aa 100644
--- a/src/mongo/db/dbcommands.cpp
+++ b/src/mongo/db/dbcommands.cpp
@@ -32,6 +32,7 @@
#include <time.h>
+#include "mongo/base/disallow_copying.h"
#include "mongo/base/init.h"
#include "mongo/base/status.h"
#include "mongo/bson/util/builder.h"
@@ -41,6 +42,8 @@
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/auth/privilege.h"
+#include "mongo/db/auth/user_management_commands_parser.h"
+#include "mongo/db/auth/user_name.h"
#include "mongo/db/background.h"
#include "mongo/db/clientcursor.h"
#include "mongo/db/commands.h"
@@ -1312,6 +1315,35 @@ namespace mongo {
bool maintenanceModeSet;
};
+
+ /**
+ * RAII class to optionally set an impersonated username list into the authorization session
+ * for the duration of the life of this object
+ */
+ class ImpersonationSessionGuard {
+ MONGO_DISALLOW_COPYING(ImpersonationSessionGuard);
+ public:
+ ImpersonationSessionGuard(AuthorizationSession* authSession,
+ bool fieldIsPresent,
+ const std::vector<UserName> &parsedUserNames) :
+ _authSession(authSession), _impersonation(false) {
+ if (fieldIsPresent) {
+ massert(17317, "impersonation unexpectedly active",
+ !authSession->isImpersonating());
+ authSession->setImpersonatedUserNames(parsedUserNames);
+ _impersonation = true;
+ }
+ }
+ ~ImpersonationSessionGuard() {
+ if (_impersonation) {
+ _authSession->clearImpersonatedUserNames();
+ }
+ }
+ private:
+ AuthorizationSession* _authSession;
+ bool _impersonation;
+ };
+
/**
* this handles
- auth
@@ -1328,7 +1360,6 @@ namespace mongo {
BSONObj& cmdObj,
BSONObjBuilder& result,
bool fromRepl ) {
-
std::string dbname = nsToDatabase( cmdns );
scoped_ptr<MaintenanceModeSetter> mmSetter;
@@ -1343,6 +1374,18 @@ namespace mongo {
return;
}
+ // Handle command option impersonatedUsers.
+ // This must come before _checkAuthorization(), as there is some command parsing logic
+ // in that code path that must not see the impersonated user array element.
+ std::vector<UserName> parsedUserNames;
+ AuthorizationSession* authSession = client.getAuthorizationSession();
+ bool fieldIsPresent = false;
+ audit::parseAndRemoveImpersonatedUserField(cmdObj, authSession,
+ &parsedUserNames, &fieldIsPresent);
+ ImpersonationSessionGuard impersonationSession(authSession,
+ fieldIsPresent,
+ parsedUserNames);
+
Status status = _checkAuthorization(c, &client, dbname, cmdObj, fromRepl);
if (!status.isOK()) {
appendCommandStatus(result, status);
diff --git a/src/mongo/db/introspect.cpp b/src/mongo/db/introspect.cpp
index 09d1ca58a80..d1dff7b2039 100644
--- a/src/mongo/db/introspect.cpp
+++ b/src/mongo/db/introspect.cpp
@@ -52,7 +52,7 @@ namespace {
void _appendUserInfo(const Client& c,
BSONObjBuilder& builder,
AuthorizationSession* authSession) {
- UserSet::NameIterator nameIter = authSession->getAuthenticatedUserNames();
+ UserNameIterator nameIter = authSession->getAuthenticatedUserNames();
UserName bestUser;
if (nameIter.more())
diff --git a/src/mongo/db/server_extra_log_context.cpp b/src/mongo/db/server_extra_log_context.cpp
index 1552da28d54..824402de5fc 100644
--- a/src/mongo/db/server_extra_log_context.cpp
+++ b/src/mongo/db/server_extra_log_context.cpp
@@ -53,8 +53,8 @@ namespace {
if (!clientBasic->hasAuthorizationSession())
return;
- UserSet::NameIterator users =
- clientBasic->getAuthorizationSession()->getAuthenticatedUserNames();
+ UserNameIterator users =
+ clientBasic->getAuthorizationSession()->getAuthenticatedUserNames();
if (!users.more())
return;
diff --git a/src/mongo/s/dbclient_multi_command.cpp b/src/mongo/s/dbclient_multi_command.cpp
index 3a400f2692d..d6a401c3d6e 100644
--- a/src/mongo/s/dbclient_multi_command.cpp
+++ b/src/mongo/s/dbclient_multi_command.cpp
@@ -28,6 +28,9 @@
#include "mongo/s/dbclient_multi_command.h"
+#include "mongo/bson/mutable/document.h"
+#include "mongo/db/audit.h"
+#include "mongo/db/client_basic.h"
#include "mongo/db/dbmessage.h"
#include "mongo/db/wire_version.h"
#include "mongo/s/shard.h"
@@ -111,14 +114,17 @@ namespace mongo {
// THROWS
static void sayAsCmd( DBClientBase* conn, const StringData& dbName, const BSONObj& cmdObj ) {
Message toSend;
-
+ BSONObjBuilder usersBuilder;
+ usersBuilder.appendElements(cmdObj);
+ audit::appendImpersonatedUsers(&usersBuilder);
+
// see query.h for the protocol we are using here.
BufBuilder bufB;
bufB.appendNum( 0 ); // command/query options
bufB.appendStr( dbName.toString() + ".$cmd" ); // write command ns
bufB.appendNum( 0 ); // ntoskip (0 for command)
bufB.appendNum( 1 ); // ntoreturn (1 for command)
- cmdObj.appendSelfToBufBuilder( bufB );
+ usersBuilder.obj().appendSelfToBufBuilder( bufB );
toSend.setData( dbQuery, bufB.buf(), bufB.len() );
// Send our command
diff --git a/src/mongo/s/shard.cpp b/src/mongo/s/shard.cpp
index 693cdd7b51f..3c8e629ac0b 100644
--- a/src/mongo/s/shard.cpp
+++ b/src/mongo/s/shard.cpp
@@ -38,6 +38,7 @@
#include "mongo/client/dbclient_rs.h"
#include "mongo/client/dbclientcursor.h"
+#include "mongo/db/audit.h"
#include "mongo/db/auth/action_set.h"
#include "mongo/db/auth/action_type.h"
#include "mongo/db/auth/authorization_manager_global.h"
@@ -451,6 +452,11 @@ namespace mongo {
causedBy( (string)"unknown failure : " + result.toString() ) ), ok );
}
+
+ // For every DBClient created by mongos, add a hook that will append impersonated users
+ // to the end of every runCommand. mongod uses this information to produce auditing
+ // records attributed to the proper authenticated user(s).
+ conn->setRunCommandHook(boost::bind(&audit::appendImpersonatedUsers, _1));
}
void ShardingConnectionHook::onDestroy( DBClientBase * conn ) {