summaryrefslogtreecommitdiff
path: root/src/mongo/client/mongo_uri_connect.cpp
diff options
context:
space:
mode:
authorJonathan Reams <jbreams@mongodb.com>2015-09-25 16:19:01 -0400
committerJonathan Reams <jbreams@mongodb.com>2015-10-13 21:26:12 -0400
commit53c52c43a99d14a9e6c47bcc1ca1e7a2fa044ef0 (patch)
tree7fbc2ff93c5ac378c14edaa8e49173c6786af86e /src/mongo/client/mongo_uri_connect.cpp
parent90cd064713dfcf5c82be07742f7377c83ea0a4f2 (diff)
downloadmongo-53c52c43a99d14a9e6c47bcc1ca1e7a2fa044ef0.tar.gz
SERVER-6233 Add URI parsing to mongo shell
Diffstat (limited to 'src/mongo/client/mongo_uri_connect.cpp')
-rw-r--r--src/mongo/client/mongo_uri_connect.cpp172
1 files changed, 172 insertions, 0 deletions
diff --git a/src/mongo/client/mongo_uri_connect.cpp b/src/mongo/client/mongo_uri_connect.cpp
new file mode 100644
index 00000000000..aaee6d2bcc2
--- /dev/null
+++ b/src/mongo/client/mongo_uri_connect.cpp
@@ -0,0 +1,172 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kNetwork
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/client/mongo_uri.h"
+
+#include "mongo/base/status_with.h"
+#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/client/dbclientinterface.h"
+#include "mongo/client/sasl_client_authenticate.h"
+#include "mongo/util/mongoutils/str.h"
+#include "mongo/util/password_digest.h"
+
+#include <boost/algorithm/string/case_conv.hpp>
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/split.hpp>
+
+#include <iterator>
+
+namespace mongo {
+
+namespace {
+const char kAuthMechanismPropertiesKey[] = "mechanism_properties";
+
+// CANONICALIZE_HOST_NAME is currently unsupported
+const char kAuthServiceName[] = "SERVICE_NAME";
+const char kAuthServiceRealm[] = "SERVICE_REALM";
+
+const char kAuthMechMongoCR[] = "MONGODB-CR";
+const char kAuthMechScramSha1[] = "SCRAM-SHA-1";
+const char kAuthMechDefault[] = "DEFAULT";
+
+const char* const kSupportedAuthMechanismProperties[] = {kAuthServiceName, kAuthServiceRealm};
+
+BSONObj parseAuthMechanismProperties(const std::string& propStr) {
+ BSONObjBuilder bob;
+ std::vector<std::string> props;
+ boost::algorithm::split(props, propStr, boost::algorithm::is_any_of(",:"));
+ for (std::vector<std::string>::const_iterator it = props.begin(); it != props.end(); ++it) {
+ std::string prop((boost::algorithm::to_upper_copy(*it))); // normalize case
+ uassert(ErrorCodes::FailedToParse,
+ str::stream() << "authMechanismProperty: " << *it << " is not supported",
+ std::count(kSupportedAuthMechanismProperties,
+ std::end(kSupportedAuthMechanismProperties),
+ prop));
+ ++it;
+ uassert(ErrorCodes::FailedToParse,
+ str::stream() << "authMechanismProperty: " << prop << " must have a value",
+ it != props.end());
+ bob.append(prop, *it);
+ }
+ return bob.obj();
+}
+
+std::string authKeyCopyDBMongoCR(const std::string& username,
+ const std::string& password,
+ const std::string& nonce) {
+ md5digest d;
+ std::string passwordDigest = createPasswordDigest(username, password);
+ {
+ md5_state_t st;
+ md5_init(&st);
+ md5_append(&st, reinterpret_cast<const md5_byte_t*>(nonce.c_str()), nonce.size());
+ md5_append(&st, reinterpret_cast<const md5_byte_t*>(username.data()), username.length());
+ md5_append(&st,
+ reinterpret_cast<const md5_byte_t*>(passwordDigest.c_str()),
+ passwordDigest.size());
+ md5_finish(&st, d);
+ }
+ return digestToString(d);
+}
+
+} // namespace
+
+BSONObj MongoURI::_makeAuthObjFromOptions(int maxWireVersion) const {
+ BSONObjBuilder bob;
+
+ // Add the username and optional password
+ invariant(!_user.empty());
+ std::string username(_user); // may have to tack on service realm before we append
+
+ if (!_password.empty())
+ bob.append(saslCommandPasswordFieldName, _password);
+
+ BSONElement elt = _options.getField("authSource");
+ if (!elt.eoo()) {
+ bob.appendAs(elt, saslCommandUserDBFieldName);
+ } else if (!_database.empty()) {
+ bob.append(saslCommandUserDBFieldName, _database);
+ } else {
+ bob.append(saslCommandUserDBFieldName, "admin");
+ }
+
+ elt = _options.getField("authMechanism");
+ if (!elt.eoo()) {
+ bob.appendAs(elt, saslCommandMechanismFieldName);
+ } else if (maxWireVersion >= 3) {
+ bob.append(saslCommandMechanismFieldName, kAuthMechScramSha1);
+ } else {
+ bob.append(saslCommandMechanismFieldName, kAuthMechMongoCR);
+ }
+
+ elt = _options.getField("authMechanismProperties");
+ if (!elt.eoo()) {
+ BSONObj parsed(parseAuthMechanismProperties(elt.String()));
+
+ bool hasNameProp = parsed.hasField(kAuthServiceName);
+ bool hasRealmProp = parsed.hasField(kAuthServiceRealm);
+
+ uassert(ErrorCodes::FailedToParse,
+ "Cannot specify both gssapiServiceName and SERVICE_NAME",
+ !(hasNameProp && _options.hasField("gssapiServiceName")));
+ // we append the parsed object so that mechanisms that don't accept it can assert.
+ bob.append(kAuthMechanismPropertiesKey, parsed);
+ // we still append using the old way the SASL code expects it
+ if (hasNameProp) {
+ bob.append(saslCommandServiceNameFieldName, parsed[kAuthServiceName].String());
+ }
+ // if we specified a realm, we just append it to the username as the SASL code
+ // expects it that way.
+ if (hasRealmProp) {
+ username.append("@").append(parsed[kAuthServiceRealm].String());
+ }
+ }
+
+ elt = _options.getField("gssapiServiceName");
+ if (!elt.eoo())
+ bob.appendAs(elt, saslCommandServiceNameFieldName);
+
+ bob.append("user", username);
+
+ return bob.obj();
+}
+
+DBClientBase* MongoURI::connect(std::string& errmsg, double socketTimeout) const {
+ auto ret = _connectString.connect(errmsg, socketTimeout);
+ if (!_user.empty()) {
+ ret->auth(_makeAuthObjFromOptions(ret->getMaxWireVersion()));
+ }
+ return ret;
+}
+
+} // namespace mongo