Skip to content

Commit 039a078

Browse files
committed
CXX-360 add support for authMechanismProperties
1 parent 2824c8a commit 039a078

File tree

3 files changed

+142
-1
lines changed

3 files changed

+142
-1
lines changed

src/mongo/client/connection_string_test.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ namespace {
114114
{ "mongodb://user@[::1]:1234,127.0.0.2:1234/?replicaSet=replName", "user", "", kSet, "replName", 2, 1, "" },
115115

116116
{ "mongodb://[::1]:1234,[::1]:1234/dbName?foo=a&c=b&replicaSet=replName", "", "", kSet, "replName", 2, 3, "dbName"},
117+
118+
{ "mongodb://user:pwd@[::1]/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:foobar", "user", "pwd", kMaster, "", 1, 2, "" },
119+
120+
{ "mongodb://user:pwd@[::1]/?authMechanism=GSSAPI&gssapiServiceName=foobar", "user", "pwd", kMaster, "", 1, 2, "" }
117121
};
118122

119123
const InvalidURITestCase invalidCases[] = {

src/mongo/client/dbclient.cpp

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "mongo/platform/basic.h"
2121

22+
#include "mongo/base/init.h"
2223
#include "mongo/bson/util/bson_extract.h"
2324
#include "mongo/bson/util/builder.h"
2425
#include "mongo/client/constants.h"
@@ -44,6 +45,7 @@
4445
#include "mongo/util/net/ssl_manager.h"
4546
#include "mongo/util/password_digest.h"
4647

48+
#include <boost/algorithm/string/case_conv.hpp>
4749
#include <boost/algorithm/string/classification.hpp>
4850
#include <boost/algorithm/string/predicate.hpp>
4951
#include <boost/algorithm/string/split.hpp>
@@ -139,12 +141,53 @@ namespace mongo {
139141
_string = ss.str();
140142
}
141143

144+
namespace {
145+
const char kAuthMechanismPropertiesKey[] = "mechanism_properties";
146+
147+
// CANONICALIZE_HOST_NAME is currently unsupported
148+
const char kAuthServiceName[] = "SERVICE_NAME";
149+
const char kAuthServiceRealm[] = "SERVICE_REALM";
150+
151+
const char* const kSupportedAuthMechanismProperties[] = {
152+
kAuthServiceName,
153+
kAuthServiceRealm
154+
};
155+
156+
// bootleg std::end for c-style arrays.
157+
template<class T, std::size_t N>
158+
T* endOf(T (&arr)[N]) { return arr + N; }
159+
160+
BSONObj parseAuthMechanismProperties(const std::string& propStr) {
161+
BSONObjBuilder bob;
162+
std::vector<std::string> props;
163+
boost::algorithm::split(props, propStr, boost::algorithm::is_any_of(",:"));
164+
for (std::vector<std::string>::const_iterator it = props.begin();
165+
it != props.end(); ++it) {
166+
std::string prop((boost::algorithm::to_upper_copy(*it))); // normalize case
167+
uassert(ErrorCodes::FailedToParse,
168+
str::stream() << "authMechanismProperty: " << *it
169+
<< " is not supported",
170+
std::count(kSupportedAuthMechanismProperties,
171+
endOf(kSupportedAuthMechanismProperties),
172+
prop));
173+
++it;
174+
uassert(ErrorCodes::FailedToParse,
175+
str::stream() << "authMechanismProperty: "
176+
<< prop << " must have a value",
177+
it != props.end());
178+
bob.append(prop, *it);
179+
}
180+
return bob.obj();
181+
}
182+
} // namespace
183+
142184
BSONObj ConnectionString::_makeAuthObjFromOptions() const {
143185
BSONObjBuilder bob;
144186

145187
// Add the username and optional password
146188
invariant(!_user.empty());
147-
bob.append("user", _user);
189+
std::string username(_user); // may have to tack on service realm before we append
190+
148191
if (!_password.empty())
149192
bob.append("pwd", _password);
150193

@@ -156,10 +199,35 @@ namespace mongo {
156199
if (!elt.eoo())
157200
bob.appendAs(elt, "mechanism");
158201

202+
elt = _options.getField("authMechanismProperties");
203+
if (!elt.eoo()) {
204+
BSONObj parsed(parseAuthMechanismProperties(elt.String()));
205+
206+
bool hasNameProp = parsed.hasField(kAuthServiceName);
207+
bool hasRealmProp = parsed.hasField(kAuthServiceRealm);
208+
209+
uassert(ErrorCodes::FailedToParse,
210+
"Cannot specify both gssapiServiceName and SERVICE_NAME",
211+
!(hasNameProp && _options.hasField("gssapiServiceName")));
212+
// we append the parsed object so that mechanisms that don't accept it can assert.
213+
bob.append(kAuthMechanismPropertiesKey, parsed);
214+
// we still append using the old way the SASL code expects it
215+
if (hasNameProp) {
216+
bob.append("serviceName", parsed[kAuthServiceName].String());
217+
}
218+
// if we specified a realm, we just append it to the username as the SASL code
219+
// expects it that way.
220+
if (hasRealmProp) {
221+
username.append("@").append(parsed[kAuthServiceRealm].String());
222+
}
223+
}
224+
159225
elt = _options.getField("gssapiServiceName");
160226
if (!elt.eoo())
161227
bob.appendAs(elt, "serviceName");
162228

229+
bob.append("user", username);
230+
163231
return bob.obj();
164232
}
165233

@@ -794,6 +862,10 @@ namespace mongo {
794862
saslCommandDigestPasswordFieldName,
795863
true,
796864
&digestPassword));
865+
uassert(ErrorCodes::AuthenticationFailed,
866+
"Cannot set mechanism_properties when using MONGODB_CR",
867+
!params.hasField(kAuthMechanismPropertiesKey));
868+
797869
BSONObj result;
798870
uassert(result["code"].Int(),
799871
result.toString(),
@@ -828,6 +900,10 @@ namespace mongo {
828900
getSSLManager()->getClientSubjectName() + "\"",
829901
user == getSSLManager()->getClientSubjectName());
830902

903+
uassert(ErrorCodes::AuthenticationFailed,
904+
"Cannot set mechanism_properties when using MONGODB_X509",
905+
!params.hasField(kAuthMechanismPropertiesKey));
906+
831907
BSONObj result;
832908
uassert(result["code"].Int(),
833909
result.toString(),

src/mongo/unittest/sasl_test.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515

1616
#include "mongo/platform/basic.h"
1717

18+
#include <boost/scoped_ptr.hpp>
19+
1820
#include "mongo/unittest/integration_test.h"
21+
#include "mongo/util/mongoutils/str.h"
1922
#include "mongo/client/dbclient.h"
2023

2124
using namespace mongo;
@@ -67,4 +70,62 @@ namespace {
6770
ASSERT_TRUE(result["kerberos"].trueValue());
6871
ASSERT_EQUALS(result["authenticated"].str(), "yeah");
6972
}
73+
74+
TEST(SASLAuthentication, DISABLED_KerberosProperties) {
75+
// You must run kinit -p drivers@LDAPTEST.10GEN.CC before this test
76+
std::string connStr = str::stream()
77+
<< "mongodb://drivers@ldaptest.10gen.cc/?"
78+
<< "authMechanism=GSSAPI" << "&"
79+
<< "authMechanismProperties="
80+
<< "SERVICE_NAME:mongodb" << ","
81+
<< "SERVICE_REALM:LDAPTEST.10GEN.CC";
82+
83+
std::string errmsg;
84+
ConnectionString parsed(ConnectionString::parse(connStr, errmsg));
85+
std::cout << errmsg << std::endl;
86+
boost::scoped_ptr<DBClientConnection> conn;
87+
88+
ASSERT_NO_THROW(conn.reset(dynamic_cast<DBClientConnection*>(parsed.connect(errmsg, 0))));
89+
90+
BSONObj result = conn->findOne("kerberos.test", Query("{}"));
91+
ASSERT_TRUE(result["kerberos"].trueValue());
92+
ASSERT_EQUALS(result["authenticated"].str(), "yeah");
93+
}
94+
95+
TEST(SASLAuthentication, DISABLED_KerberosPropertiesInvalid) {
96+
// You must run kinit -p drivers@LDAPTEST.10GEN.CC before this test
97+
std::string connStr = str::stream()
98+
<< "mongodb://drivers@ldaptest.10gen.cc/?"
99+
<< "authMechanism=GSSAPI" << "&"
100+
<< "authMechanismProperties="
101+
<< "SERVICE_NAME:mongodb" << ","
102+
<< "SERVICE_REALM:LDAPTEST.10GEN.CC" << ","
103+
<< "I_AM_NOT_VALID:meow";
104+
105+
std::string errmsg;
106+
ConnectionString parsed(ConnectionString::parse(connStr, errmsg));
107+
std::cout << errmsg << std::endl;
108+
boost::scoped_ptr<DBClientConnection> conn;
109+
ASSERT_THROW(conn.reset(dynamic_cast<DBClientConnection*>(parsed.connect(errmsg, 0))),
110+
UserException);
111+
}
112+
113+
TEST(SASLAuthentication, DISABLED_KerberosPropertiesRedundant) {
114+
// You must run kinit -p drivers@LDAPTEST.10GEN.CC before this test
115+
std::string connStr = str::stream()
116+
<< "mongodb://drivers@ldaptest.10gen.cc/?"
117+
<< "gssapiServiceName=thisshouldnotwork" << "&" // can't set this and SERVICE_NAME
118+
<< "authMechanism=GSSAPI" << "&"
119+
<< "authMechanismProperties="
120+
<< "SERVICE_NAME:mongodb" << ","
121+
<< "SERVICE_REALM:LDAPTEST.10GEN.CC";
122+
123+
std::string errmsg;
124+
ConnectionString parsed(ConnectionString::parse(connStr, errmsg));
125+
std::cout << errmsg << std::endl;
126+
boost::scoped_ptr<DBClientConnection> conn;
127+
ASSERT_THROW(conn.reset(dynamic_cast<DBClientConnection*>(parsed.connect(errmsg, 0))),
128+
UserException);
129+
}
130+
70131
} // namespace

0 commit comments

Comments
 (0)