summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorTyler Kaye <tyler.kaye@mongodb.com>2018-10-29 13:06:49 -0400
committerTyler Kaye <tyler.kaye@mongodb.com>2018-11-14 13:30:44 -0500
commitddcf9f0572755a456632d036744276a09baf5760 (patch)
treed417e74a1fa34b835916422aaf1ddcdf7879d58c /src/mongo
parent106eba1584b61497c133896b5dab7a3cea49296d (diff)
downloadmongo-ddcf9f0572755a456632d036744276a09baf5760.tar.gz
SERVER-35212: Refactor shell code to enable default authentication database as admin
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/client/mongo_uri.cpp54
-rw-r--r--src/mongo/client/mongo_uri.h45
-rw-r--r--src/mongo/client/replica_set_monitor.cpp2
-rw-r--r--src/mongo/scripting/mozjs/mongo.cpp5
-rw-r--r--src/mongo/shell/dbshell.cpp149
-rw-r--r--src/mongo/shell/mongo.js8
-rw-r--r--src/mongo/shell/shell_options.cpp1
-rw-r--r--src/mongo/shell/shell_options.h1
8 files changed, 169 insertions, 96 deletions
diff --git a/src/mongo/client/mongo_uri.cpp b/src/mongo/client/mongo_uri.cpp
index 597933712df..c8b76e776a8 100644
--- a/src/mongo/client/mongo_uri.cpp
+++ b/src/mongo/client/mongo_uri.cpp
@@ -40,6 +40,7 @@
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/find_iterator.hpp>
#include <boost/algorithm/string/predicate.hpp>
+#include <boost/range/algorithm/count.hpp>
#include "mongo/base/status_with.h"
#include "mongo/bson/bsonobjbuilder.h"
@@ -56,8 +57,6 @@ using namespace std::literals::string_literals;
namespace {
constexpr std::array<char, 16> hexits{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
-const mongo::StringData kURIPrefix{"mongodb://"};
-const mongo::StringData kURISRVPrefix{"mongodb+srv://"};
// This vector must remain sorted. It is over pairs to facilitate a call to `std::includes` using
// a `std::map<std::string, std::string>` as the other parameter.
@@ -112,6 +111,10 @@ namespace mongo {
namespace {
+constexpr StringData kURIPrefix = "mongodb://"_sd;
+constexpr StringData kURISRVPrefix = "mongodb+srv://"_sd;
+constexpr StringData kDefaultMongoHost = "127.0.0.1:27017"_sd;
+
/**
* Helper Method for MongoURI::parse() to split a string into exactly 2 pieces by a char
* delimiter.
@@ -519,8 +522,53 @@ const boost::optional<std::string> MongoURI::getAppName() const {
const auto optIter = _options.find("appName");
if (optIter != end(_options)) {
return optIter->second;
+ }
+ return boost::none;
+}
+
+std::string MongoURI::canonicalizeURIAsString() const {
+ StringBuilder uri;
+ uri << kURIPrefix;
+ if (!_user.empty()) {
+ uri << uriEncode(_user);
+ if (!_password.empty()) {
+ uri << ":" << uriEncode(_password);
+ }
+ uri << "@";
+ }
+
+ const auto& servers = _connectString.getServers();
+ if (!servers.empty()) {
+ auto delimeter = "";
+ for (auto& hostAndPort : servers) {
+ if (boost::count(hostAndPort.host(), ':') > 1) {
+ uri << delimeter << "[" << uriEncode(hostAndPort.host()) << "]"
+ << ":" << uriEncode(std::to_string(hostAndPort.port()));
+ } else if (StringData(hostAndPort.host()).endsWith(".sock")) {
+ uri << delimeter << uriEncode(hostAndPort.host());
+ } else {
+ uri << delimeter << uriEncode(hostAndPort.host()) << ":"
+ << uriEncode(std::to_string(hostAndPort.port()));
+ }
+ delimeter = ",";
+ }
} else {
- return boost::none;
+ uri << kDefaultMongoHost;
+ }
+
+ uri << "/";
+ if (!_database.empty()) {
+ uri << uriEncode(_database);
+ }
+
+ if (!_options.empty()) {
+ auto delimeter = "";
+ uri << "?";
+ for (const auto& pair : _options) {
+ uri << delimeter << uriEncode(pair.first) << "=" << uriEncode(pair.second);
+ delimeter = "&";
+ }
}
+ return uri.str();
}
} // namespace mongo
diff --git a/src/mongo/client/mongo_uri.h b/src/mongo/client/mongo_uri.h
index 9b61dcaebd9..c121fa21801 100644
--- a/src/mongo/client/mongo_uri.h
+++ b/src/mongo/client/mongo_uri.h
@@ -130,22 +130,64 @@ public:
return _user;
}
+ void setUser(std::string newUsername) {
+ _user = std::move(newUsername);
+ }
+
const std::string& getPassword() const {
return _password;
}
+ void setPassword(std::string newPassword) {
+ _password = std::move(newPassword);
+ }
+
const OptionsMap& getOptions() const {
return _options;
}
+ void addOption(std::string newKey, std::string newValue) {
+ _options[std::move(newKey)] = std::move(newValue);
+ }
+
+ void setOptionIfNecessary(std::string uriParamKey, std::string value) {
+ const auto key = _options.find(uriParamKey);
+ if (key == end(_options) && !value.empty()) {
+ addOption(uriParamKey, value);
+ }
+ }
+
+ boost::optional<std::string> getOption(const std::string& key) const {
+ const auto optIter = _options.find(key);
+ if (optIter != end(_options)) {
+ return optIter->second;
+ }
+ return boost::none;
+ }
+
const std::string& getDatabase() const {
return _database;
}
+ std::string getAuthenticationDatabase() {
+ auto authDB = _options.find("authSource");
+ if (authDB != _options.end()) {
+ return authDB->second;
+ } else if (!_database.empty()) {
+ return _database;
+ } else {
+ return "admin";
+ }
+ }
+
bool isValid() const {
return _connectString.isValid();
}
+ const ConnectionString& connectionString() const {
+ return _connectString;
+ }
+
const std::string& toString() const {
return _connectString.toString();
}
@@ -161,6 +203,9 @@ public:
const boost::optional<std::string> getAppName() const;
+ std::string canonicalizeURIAsString() const;
+
+
boost::optional<bool> getRetryWrites() const {
return _retryWrites;
}
diff --git a/src/mongo/client/replica_set_monitor.cpp b/src/mongo/client/replica_set_monitor.cpp
index 769358dc9b0..0488b9100ec 100644
--- a/src/mongo/client/replica_set_monitor.cpp
+++ b/src/mongo/client/replica_set_monitor.cpp
@@ -854,6 +854,8 @@ HostAndPort Refresher::_refreshUntilMatches(const ReadPreferenceSetting* criteri
if (_set->setUri.isValid()) {
targetURI = _set->setUri.cloneURIForServer(ns.host);
+ targetURI.setUser("");
+ targetURI.setPassword("");
} else {
targetURI = MongoURI(ConnectionString(ns.host));
}
diff --git a/src/mongo/scripting/mozjs/mongo.cpp b/src/mongo/scripting/mozjs/mongo.cpp
index 08c0375e384..92d60d4eee0 100644
--- a/src/mongo/scripting/mozjs/mongo.cpp
+++ b/src/mongo/scripting/mozjs/mongo.cpp
@@ -715,8 +715,7 @@ void MongoExternalInfo::construct(JSContext* cx, JS::CallArgs args) {
host = ValueWriter(cx, args.get(0)).toString();
}
- auto statusWithHost = MongoURI::parse(host);
- auto cs = uassertStatusOK(statusWithHost);
+ auto cs = uassertStatusOK(MongoURI::parse(host));
boost::optional<std::string> appname = cs.getAppName();
std::string errmsg;
@@ -735,7 +734,7 @@ void MongoExternalInfo::construct(JSContext* cx, JS::CallArgs args) {
JS_SetPrivate(thisv, scope->trackedNew<std::shared_ptr<DBClientBase>>(conn.release()));
o.setBoolean(InternedString::slaveOk, false);
- o.setString(InternedString::host, cs.toString());
+ o.setString(InternedString::host, cs.connectionString().toString());
auto defaultDB = cs.getDatabase() == "" ? "test" : cs.getDatabase();
o.setString(InternedString::defaultDB, defaultDB);
diff --git a/src/mongo/shell/dbshell.cpp b/src/mongo/shell/dbshell.cpp
index e53e09d6ed5..3347c2fa0b1 100644
--- a/src/mongo/shell/dbshell.cpp
+++ b/src/mongo/shell/dbshell.cpp
@@ -92,7 +92,9 @@ bool inMultiLine = false;
static AtomicBool atPrompt(false); // can eval before getting to prompt
namespace {
-const auto kDefaultMongoURL = "mongodb://127.0.0.1:27017"_sd;
+const std::string kDefaultMongoHost = "127.0.0.1"s;
+const std::string kDefaultMongoPort = "27017"s;
+const std::string kDefaultMongoURL = "mongodb://"s + kDefaultMongoHost + ":"s + kDefaultMongoPort;
// Initialize the featureCompatibilityVersion server parameter since the mongo shell does not have a
// featureCompatibilityVersion document from which to initialize the parameter. The parameter is set
@@ -239,7 +241,7 @@ void setupSignals() {
string getURIFromArgs(const std::string& arg, const std::string& host, const std::string& port) {
if (host.empty() && arg.empty() && port.empty()) {
// Nothing provided, just play the default.
- return kDefaultMongoURL.toString();
+ return kDefaultMongoURL;
}
if ((str::startsWith(arg, "mongodb://") || str::startsWith(arg, "mongodb+srv://")) &&
@@ -247,7 +249,7 @@ string getURIFromArgs(const std::string& arg, const std::string& host, const std
// mongo mongodb://blah
return arg;
}
- if ((str::startsWith(host, "mongodb://") || str::startsWith(arg, "mongodb+srv://")) &&
+ if ((str::startsWith(host, "mongodb://") || str::startsWith(host, "mongodb+srv://")) &&
arg.empty() && port.empty()) {
// mongo --host mongodb://blah
return host;
@@ -726,16 +728,17 @@ static void edit(const string& whatToEdit) {
}
namespace {
-bool mechanismRequiresPassword() {
- using std::begin;
- using std::end;
- const std::string passwordlessMechanisms[] = {"GSSAPI", "MONGODB-X509"};
- auto isInShellParameters = [](const auto& mech) {
- return mech == shellGlobalParams.authenticationMechanism;
- };
-
- return std::none_of(
- begin(passwordlessMechanisms), end(passwordlessMechanisms), isInShellParameters);
+bool mechanismRequiresPassword(const MongoURI& uri) {
+ if (const auto authMechanisms = uri.getOption("authMechanism")) {
+ constexpr std::array<StringData, 2> passwordlessMechanisms{"GSSAPI"_sd, "MONGODB-X509"_sd};
+ const std::string& authMechanism = authMechanisms.get();
+ for (const auto& mechanism : passwordlessMechanisms) {
+ if (mechanism.toString() == authMechanism) {
+ return false;
+ }
+ }
+ }
+ return true;
}
} // namespace
@@ -780,30 +783,42 @@ int _main(int argc, char* argv[], char** envp) {
->attachAppender(std::make_unique<logger::ConsoleAppender<logger::MessageEventEphemeral>>(
std::make_unique<logger::MessageEventUnadornedEncoder>()));
+ // Get the URL passed to the shell
std::string& cmdlineURI = shellGlobalParams.url;
+
+ // Parse the output of getURIFromArgs which will determine if --host passed in a URI
MongoURI parsedURI;
- if (!cmdlineURI.empty()) {
- parsedURI = uassertStatusOK(MongoURI::parse(stdx::as_const(cmdlineURI)));
- }
+ parsedURI = uassertStatusOK(MongoURI::parse(getURIFromArgs(
+ cmdlineURI, escape(shellGlobalParams.dbhost), escape(shellGlobalParams.port))));
- // We create an altered URI from the one passed so that we can pass that to replica set
- // monitors. This is to avoid making potentially breaking changes to the replica set monitor
- // code.
- std::string processedURI = cmdlineURI;
- auto pos = cmdlineURI.find('@');
- auto protocolLength = processedURI.find("://");
- if (pos != std::string::npos && protocolLength != std::string::npos) {
- processedURI =
- processedURI.substr(0, protocolLength) + "://" + processedURI.substr(pos + 1);
- }
+ // TODO: add in all of the relevant shellGlobalParams to parsedURI
+ parsedURI.setOptionIfNecessary("compressors"s, shellGlobalParams.networkMessageCompressors);
+ parsedURI.setOptionIfNecessary("authMechanism"s, shellGlobalParams.authenticationMechanism);
+ parsedURI.setOptionIfNecessary("authSource"s, shellGlobalParams.authenticationDatabase);
+ parsedURI.setOptionIfNecessary("gssapiServiceName"s, shellGlobalParams.gssapiServiceName);
+ parsedURI.setOptionIfNecessary("gssapiHostName"s, shellGlobalParams.gssapiHostName);
+ bool usingPassword = !shellGlobalParams.password.empty();
if (!shellGlobalParams.nodb) { // connect to db
+ if (mechanismRequiresPassword(parsedURI) &&
+ (parsedURI.getUser().size() || shellGlobalParams.username.size())) {
+ usingPassword = true;
+ }
+ if (usingPassword && parsedURI.getPassword().empty()) {
+ if (!shellGlobalParams.password.empty()) {
+ parsedURI.setPassword(stdx::as_const(shellGlobalParams.password));
+ } else {
+ parsedURI.setPassword(mongo::askPassword());
+ }
+ }
+ if (parsedURI.getUser().empty() && !shellGlobalParams.username.empty()) {
+ parsedURI.setUser(stdx::as_const(shellGlobalParams.username));
+ }
+
stringstream ss;
if (mongo::serverGlobalParams.quiet.load())
ss << "__quiet = true;";
- ss << "db = connect( \""
- << getURIFromArgs(processedURI, shellGlobalParams.dbhost, shellGlobalParams.port)
- << "\");";
+ ss << "db = connect( \"" << parsedURI.canonicalizeURIAsString() << "\");";
if (shellGlobalParams.shouldRetryWrites || parsedURI.getRetryWrites()) {
// If the --retryWrites cmdline argument or retryWrites URI param was specified, then
@@ -813,46 +828,8 @@ int _main(int argc, char* argv[], char** envp) {
}
mongo::shell_utils::_dbConnect = ss.str();
-
- if (cmdlineURI.size()) {
- const auto compressionKey = parsedURI.getOptions().find("compressors");
- if (compressionKey != end(parsedURI.getOptions()) &&
- shellGlobalParams.networkMessageCompressors.empty()) {
- shellGlobalParams.networkMessageCompressors = compressionKey->second;
- }
- const auto mechanismKey = parsedURI.getOptions().find("authMechanism");
- if (mechanismKey != end(parsedURI.getOptions()) &&
- shellGlobalParams.authenticationMechanism.empty()) {
- shellGlobalParams.authenticationMechanism = mechanismKey->second;
- }
-
- if (mechanismRequiresPassword() &&
- (parsedURI.getUser().size() || shellGlobalParams.username.size())) {
- shellGlobalParams.usingPassword = true;
- }
- if (shellGlobalParams.usingPassword && shellGlobalParams.password.empty()) {
- shellGlobalParams.password =
- parsedURI.getPassword().size() ? parsedURI.getPassword() : mongo::askPassword();
- }
- if (parsedURI.getUser().size() && shellGlobalParams.username.empty()) {
- shellGlobalParams.username = parsedURI.getUser();
- }
- auto authParam = parsedURI.getOptions().find(kAuthParam);
- if (authParam != end(parsedURI.getOptions()) &&
- shellGlobalParams.authenticationDatabase.empty()) {
- shellGlobalParams.authenticationDatabase = authParam->second;
- }
- } else if (shellGlobalParams.usingPassword && shellGlobalParams.password.empty()) {
- shellGlobalParams.password = mongo::askPassword();
- }
}
- // We now substitute the altered URI to permit the replica set monitors to see it without
- // usernames. This is to avoid making potentially breaking changes to the replica set monitor
- // code.
- cmdlineURI = processedURI;
-
-
// Construct the authentication-related code to execute on shell startup.
//
// This constructs and immediately executes an anonymous function, to avoid
@@ -865,41 +842,39 @@ int _main(int argc, char* argv[], char** envp) {
// }())
stringstream authStringStream;
authStringStream << "(function() { " << endl;
- if (!shellGlobalParams.authenticationMechanism.empty()) {
+
+
+ if (const auto authMechanisms = parsedURI.getOption("authMechanism")) {
authStringStream << "DB.prototype._defaultAuthenticationMechanism = \""
- << escape(shellGlobalParams.authenticationMechanism) << "\";" << endl;
+ << escape(authMechanisms.get()) << "\";" << endl;
}
- if (!shellGlobalParams.gssapiServiceName.empty()) {
+ if (const auto gssapiServiveName = parsedURI.getOption("gssapiServiceName")) {
authStringStream << "DB.prototype._defaultGssapiServiceName = \""
- << escape(shellGlobalParams.gssapiServiceName) << "\";" << endl;
+ << escape(gssapiServiveName.get()) << "\";" << endl;
}
- if (!shellGlobalParams.nodb && (!shellGlobalParams.username.empty() ||
- shellGlobalParams.authenticationMechanism == "MONGODB-X509")) {
- authStringStream << "var username = \"" << escape(shellGlobalParams.username) << "\";"
- << endl;
- if (shellGlobalParams.usingPassword) {
- authStringStream << "var password = \"" << escape(shellGlobalParams.password) << "\";"
+ if (!shellGlobalParams.nodb &&
+ (!parsedURI.getUser().empty() ||
+ parsedURI.getOption("authMechanism").get_value_or("") == "MONGODB-X509")) {
+ authStringStream << "var username = \"" << escape(parsedURI.getUser()) << "\";" << endl;
+ if (usingPassword) {
+ authStringStream << "var password = \"" << escape(parsedURI.getPassword()) << "\";"
<< endl;
}
- if (shellGlobalParams.authenticationDatabase.empty()) {
- authStringStream << "var authDb = db;" << endl;
- } else {
- authStringStream << "var authDb = db.getSiblingDB(\""
- << escape(shellGlobalParams.authenticationDatabase) << "\");" << endl;
- }
+ authStringStream << "var authDb = db.getSiblingDB(\""
+ << escape(parsedURI.getAuthenticationDatabase()) << "\");" << endl;
authStringStream << "authDb._authOrThrow({ ";
- if (!shellGlobalParams.username.empty()) {
+ if (!parsedURI.getUser().empty()) {
authStringStream << saslCommandUserFieldName << ": username ";
}
- if (shellGlobalParams.usingPassword) {
+ if (usingPassword) {
authStringStream << ", " << saslCommandPasswordFieldName << ": password ";
}
- if (!shellGlobalParams.gssapiHostName.empty()) {
+ if (const auto gssapiHostNameKey = parsedURI.getOption("gssapiHostName")) {
authStringStream << ", " << saslCommandServiceHostnameFieldName << ": \""
- << escape(shellGlobalParams.gssapiHostName) << '"' << endl;
+ << escape(gssapiHostNameKey.get()) << '"' << endl;
}
authStringStream << "});" << endl;
}
diff --git a/src/mongo/shell/mongo.js b/src/mongo/shell/mongo.js
index 9a2f21544ef..eb5a1c13843 100644
--- a/src/mongo/shell/mongo.js
+++ b/src/mongo/shell/mongo.js
@@ -318,7 +318,13 @@ connect = function(url, user, pass) {
}
}
- chatty("connecting to: " + url);
+ var atPos = url.indexOf("@");
+ var protocolPos = url.indexOf("://");
+ var safeURL = url;
+ if (atPos != -1 && protocolPos != -1) {
+ safeURL = url.substring(0, protocolPos + 3) + url.substring(atPos + 1);
+ }
+ chatty("connecting to: " + safeURL);
var m = new Mongo(url);
var db = m.getDB(m.defaultDB);
diff --git a/src/mongo/shell/shell_options.cpp b/src/mongo/shell/shell_options.cpp
index b92ad6f5637..b847e55d711 100644
--- a/src/mongo/shell/shell_options.cpp
+++ b/src/mongo/shell/shell_options.cpp
@@ -323,7 +323,6 @@ Status storeMongoShellOptions(const moe::Environment& params,
}
if (params.count("password")) {
- shellGlobalParams.usingPassword = true;
shellGlobalParams.password = params["password"].as<string>();
}
diff --git a/src/mongo/shell/shell_options.h b/src/mongo/shell/shell_options.h
index d28bf44d55f..76b4f25a913 100644
--- a/src/mongo/shell/shell_options.h
+++ b/src/mongo/shell/shell_options.h
@@ -54,7 +54,6 @@ struct ShellGlobalParams {
std::string username;
std::string password;
- bool usingPassword;
std::string authenticationMechanism;
std::string authenticationDatabase;
std::string gssapiServiceName;