Skip to content

Commit d75febf

Browse files
committed
JAVA-827: Support SASL PLAIN authentication mechanism
1 parent b2656b0 commit d75febf

20 files changed

Lines changed: 858 additions & 114 deletions

driver-compat/src/main/com/mongodb/MongoClientURI.java

Lines changed: 103 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -18,50 +18,114 @@
1818

1919
import java.util.List;
2020

21-
2221
/**
23-
* Represents a <a href="http://www.mongodb.org/display/DOCS/Connections">URI</a> which can be used to create a
24-
* MongoClient instance. The URI describes the hosts to be used and options. <p>The format of the URI is:
22+
* Represents a <a href="http://www.mongodb.org/display/DOCS/Connections">URI</a>
23+
* which can be used to create a MongoClient instance. The URI describes the hosts to
24+
* be used and options.
25+
* <p>The format of the URI is:
2526
* <pre>
2627
* mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]
2728
* </pre>
28-
* <ul> <li>{@code mongodb://} is a required prefix to identify that this is a string in the standard connection
29-
* format.</li> <li>{@code username:password@} are optional. If given, the driver will attempt to login to a database
30-
* after connecting to a database server.</li> <li>{@code host1} is the only required part of the URI. It identifies a
31-
* server address to connect to.</li> <li>{@code :portX} is optional and defaults to :27017 if not provided.</li>
32-
* <li>{@code /database} is the name of the database to login to and thus is only relevant if the {@code
33-
* username:password@} syntax is used. If not specified the "admin" database will be used by default.</li> <li>{@code
34-
* ?options} are connection options. Note that if {@code database} is absent there is still a {@code /} required between
35-
* the last host and the {@code ?} introducing the options. Options are name=value pairs and the pairs are separated by
36-
* "&amp;". For backwards compatibility, ";" is accepted as a separator in addition to "&amp;", but should be considered
37-
* as deprecated.</li> </ul> <p> The Java driver supports the following options (case insensitive): <p> Replica set
38-
* configuration: </p> <ul> <li>{@code replicaSet=name}: Implies that the hosts given are a seed list, and the driver
39-
* will attempt to find all members of the set.</li> </ul> <p>Connection Configuration:</p> <ul> <li>{@code
40-
* connectTimeoutMS=ms}: How long a connection can take to be opened before timing out.</li> <li>{@code
41-
* socketTimeoutMS=ms}: How long a send or receive on a socket can take before timing out.</li> </ul> <p>Connection pool
42-
* configuration:</p> <ul> <li>{@code maxPoolSize=n}: The maximum number of connections in the connection pool.</li>
43-
* <li>{@code waitQueueMultiple=n} : this multiplier, multiplied with the maxPoolSize setting, gives the maximum number
44-
* of threads that may be waiting for a connection to become available from the pool. All further threads will get an
45-
* exception right away.</li> <li>{@code waitQueueTimeoutMS=ms}: The maximum wait time in milliseconds that a thread may
46-
* wait for a connection to become available.</li> </ul> <p>Write concern configuration:</p> <ul> <li>{@code
47-
* safe=true|false} <ul> <li>{@code true}: the driver sends a getLastError command after every update to ensure that the
48-
* update succeeded (see also {@code w} and {@code wtimeoutMS}).</li> <li>{@code false}: the driver does not send a
49-
* getLastError command after every update.</li> </ul> </li> <li>{@code w=wValue} <ul> <li>The driver adds { w : wValue
50-
* } to the getLastError command. Implies {@code safe=true}.</li> <li>wValue is typically a number, but can be any
51-
* string in order to allow for specifications like {@code "majority"}</li> </ul> </li> <li>{@code wtimeoutMS=ms} <ul>
52-
* <li>The driver adds { wtimeout : ms } to the getlasterror command. Implies {@code safe=true}.</li> <li>Used in
53-
* combination with {@code w}</li> </ul> </li> </ul> <p>Read preference configuration:</p> <ul> <li>{@code
54-
* slaveOk=true|false}: Whether a driver connected to a replica set will send reads to slaves/secondaries.</li>
29+
* <ul>
30+
* <li>{@code mongodb://} is a required prefix to identify that this is a string in the standard connection format.</li>
31+
* <li>{@code username:password@} are optional. If given, the driver will attempt to login to a database after
32+
* connecting to a database server. For some authentication mechanisms, only the username is specified and the password is not,
33+
* in which case the ":" after the username is left off as well</li>
34+
* <li>{@code host1} is the only required part of the URI. It identifies a server address to connect to.</li>
35+
* <li>{@code :portX} is optional and defaults to :27017 if not provided.</li>
36+
* <li>{@code /database} is the name of the database to login to and thus is only relevant if the
37+
* {@code username:password@} syntax is used. If not specified the "admin" database will be used by default.</li>
38+
* <li>{@code ?options} are connection options. Note that if {@code database} is absent there is still a {@code /}
39+
* required between the last host and the {@code ?} introducing the options. Options are name=value pairs and the pairs
40+
* are separated by "&amp;". For backwards compatibility, ";" is accepted as a separator in addition to "&amp;",
41+
* but should be considered as deprecated.</li>
42+
* </ul>
43+
* <p>
44+
* The following options are supported (case insensitive):
45+
* <p>
46+
* Replica set configuration:
47+
* </p>
48+
* <ul>
49+
* <li>{@code replicaSet=name}: Implies that the hosts given are a seed list, and the driver will attempt to find
50+
* all members of the set.</li>
51+
* </ul>
52+
* <p>Connection Configuration:</p>
53+
* <ul>
54+
* <li>{@code ssl=true|false}: Whether to connect using SSL.</li>
55+
* <li>{@code connectTimeoutMS=ms}: How long a connection can take to be opened before timing out.</li>
56+
* <li>{@code socketTimeoutMS=ms}: How long a send or receive on a socket can take before timing out.</li>
57+
* </ul>
58+
* <p>Connection pool configuration:</p>
59+
* <ul>
60+
* <li>{@code maxPoolSize=n}: The maximum number of connections in the connection pool.</li>
61+
* <li>{@code waitQueueMultiple=n} : this multiplier, multiplied with the maxPoolSize setting, gives the maximum number of
62+
* threads that may be waiting for a connection to become available from the pool. All further threads will get an
63+
* exception right away.</li>
64+
* <li>{@code waitQueueTimeoutMS=ms}: The maximum wait time in milliseconds that a thread may wait for a connection to
65+
* become available.</li>
66+
* </ul>
67+
* <p>Write concern configuration:</p>
68+
* <ul>
69+
* <li>{@code safe=true|false}
70+
* <ul>
71+
* <li>{@code true}: the driver sends a getLastError command after every update to ensure that the update succeeded
72+
* (see also {@code w} and {@code wtimeoutMS}).</li>
73+
* <li>{@code false}: the driver does not send a getLastError command after every update.</li>
74+
* </ul>
75+
* </li>
76+
* <li>{@code w=wValue}
77+
* <ul>
78+
* <li>The driver adds { w : wValue } to the getLastError command. Implies {@code safe=true}.</li>
79+
* <li>wValue is typically a number, but can be any string in order to allow for specifications like
80+
* {@code "majority"}</li>
81+
* </ul>
82+
* </li>
83+
* <li>{@code wtimeoutMS=ms}
84+
* <ul>
85+
* <li>The driver adds { wtimeout : ms } to the getlasterror command. Implies {@code safe=true}.</li>
86+
* <li>Used in combination with {@code w}</li>
87+
* </ul>
88+
* </li>
89+
* </ul>
90+
* <p>Read preference configuration:</p>
91+
* <ul>
92+
* <li>{@code slaveOk=true|false}: Whether a driver connected to a replica set will send reads to slaves/secondaries.</li>
5593
* <li>{@code readPreference=enum}: The read preference for this connection. If set, it overrides any slaveOk value.
56-
* <ul> <li>Enumerated values: <ul> <li>{@code primary}</li> <li>{@code primaryPreferred}</li> <li>{@code
57-
* secondary}</li> <li>{@code secondaryPreferred}</li> <li>{@code nearest}</li> </ul> </li> </ul> </li> <li>{@code
58-
* readPreferenceTags=string}. A representation of a tag set as a comma-separated list of colon-separated key-value
59-
* pairs, e.g. {@code "dc:ny,rack:1}". Spaces are stripped from beginning and end of all keys and values. To specify a
60-
* list of tag sets, using multiple readPreferenceTags, e.g. {@code readPreferenceTags=dc:ny,
61-
* rack:1;readPreferenceTags=dc:ny;readPreferenceTags=}
62-
* <ul> <li>Note the empty value for the last one, which means match any secondary as a last resort.</li> <li>Order
63-
* matters when using multiple readPreferenceTags.</li> </ul> </li> </ul>
64-
* <p/>
94+
* <ul>
95+
* <li>Enumerated values:
96+
* <ul>
97+
* <li>{@code primary}</li>
98+
* <li>{@code primaryPreferred}</li>
99+
* <li>{@code secondary}</li>
100+
* <li>{@code secondaryPreferred}</li>
101+
* <li>{@code nearest}</li>
102+
* </ul>
103+
* </li>
104+
* </ul>
105+
* </li>
106+
* <li>{@code readPreferenceTags=string}. A representation of a tag set as a comma-separated list of colon-separated
107+
* key-value pairs, e.g. {@code "dc:ny,rack:1}". Spaces are stripped from beginning and end of all keys and values.
108+
* To specify a list of tag sets, using multiple readPreferenceTags,
109+
* e.g. {@code readPreferenceTags=dc:ny,rack:1;readPreferenceTags=dc:ny;readPreferenceTags=}
110+
* <ul>
111+
* <li>Note the empty value for the last one, which means match any secondary as a last resort.</li>
112+
* <li>Order matters when using multiple readPreferenceTags.</li>
113+
* </ul>
114+
* </li>
115+
* </ul>
116+
* <p>Authentication configuration:</p>
117+
* <ul>
118+
* <li>{@code authMechanism=MONGO-CR|GSSAPI|PLAIN}: The authentication mechanism to use if a credential was supplied.
119+
* The default is MONGODB-CR, which is the native MongoDB Challenge Response mechanism. For the GSSAPI mechanism, no password is accepted,
120+
* only the username.
121+
* </li>
122+
* <li>{@code authSource=string}: The source of the authentication credentials. This is typically the database that
123+
* the credentials have been created. The value defaults to the database specified in the path portion of the URI.
124+
* If the database is specified in neither place, the default value is "admin". This option is only respected when using the MONGO-CR
125+
* mechanism (the default).
126+
* </li>
127+
* <ul>
128+
* <p>
65129
* Note: This class is a replacement for {@code MongoURI}, to be used with {@code MongoClient}. The main difference in
66130
* behavior is that the default write concern is {@code WriteConcern.ACKNOWLEDGED}.
67131
*

driver-compat/src/main/com/mongodb/MongoCredential.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
7-
*
7+
*
88
* http://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
1212
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
15-
*
1615
*/
1716

1817
package com.mongodb;
@@ -28,6 +27,11 @@
2827
@Immutable
2928
public final class MongoCredential {
3029

30+
/**
31+
* The PLAIN mechanism. See the <a href="http://www.ietf.org/rfc/rfc4616.txt">RFC</a>.
32+
*/
33+
public static final String PLAIN_MECHANISM = "PLAIN";
34+
3135
/**
3236
* The GSSAPI mechanism. See the <a href="http://tools.ietf.org/html/rfc4752">RFC</a>.
3337
*/
@@ -52,6 +56,18 @@ public static MongoCredential createMongoCRCredential(String userName, String da
5256
return new MongoCredential(org.mongodb.MongoCredential.createMongoCRCredential(userName, database, password));
5357
}
5458

59+
/**
60+
* Creates a MongoCredential instance for the PLAIN SASL mechanism.
61+
*
62+
* @param userName the non-null user name
63+
* @param password the non-null user password
64+
* @return the credential
65+
*/
66+
public static MongoCredential createPlainCredential(final String userName, final char[] password) {
67+
return new MongoCredential(org.mongodb.MongoCredential.createPlainCredential(userName, password));
68+
}
69+
70+
5571
/**
5672
* Creates a MongoCredential instance for the GSSAPI SASL mechanism.
5773
*
@@ -77,7 +93,7 @@ public static MongoCredential createGSSAPICredential(String userName) {
7793
* @return the mechanism.
7894
*/
7995
public String getMechanism() {
80-
return proxied.getMechanism();
96+
return proxied.getMechanism().getMechanismName();
8197
}
8298

8399
/**

driver-compat/src/test/unit/com/mongodb/MongoCredentialTest.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
7-
*
7+
*
88
* http://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
1212
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
15-
*
1615
*/
1716

1817
package com.mongodb;
@@ -27,7 +26,7 @@ public class MongoCredentialTest {
2726
public void testMongoChallengeResponseMechanism() {
2827
MongoCredential credential;
2928

30-
final String mechanism = org.mongodb.MongoCredential.MONGODB_CR_MECHANISM;
29+
final String mechanism = MongoCredential.MONGODB_CR_MECHANISM;
3130
final String userName = "user";
3231
final String database = "test";
3332
final char[] password = "pwd".toCharArray();
@@ -37,7 +36,6 @@ public void testMongoChallengeResponseMechanism() {
3736
assertEquals(userName, credential.getUserName());
3837
assertEquals(database, credential.getSource());
3938
assertArrayEquals(password, credential.getPassword());
40-
assertEquals(org.mongodb.MongoCredential.MONGODB_CR_MECHANISM, credential.getMechanism());
4139
}
4240

4341
@Test
@@ -52,7 +50,21 @@ public void testGSSAPIMechanism() {
5250
assertEquals(userName, credential.getUserName());
5351
assertEquals("$external", credential.getSource());
5452
assertArrayEquals(null, credential.getPassword());
55-
assertEquals(org.mongodb.MongoCredential.GSSAPI_MECHANISM, credential.getMechanism());
53+
}
54+
55+
@Test
56+
public void testPlainMechanism() {
57+
MongoCredential credential;
58+
59+
final String mechanism = MongoCredential.PLAIN_MECHANISM;
60+
final String userName = "user";
61+
final char[] password = "pwd".toCharArray();
62+
credential = MongoCredential.createPlainCredential(userName, password);
63+
64+
assertEquals(mechanism, credential.getMechanism());
65+
assertEquals(userName, credential.getUserName());
66+
assertEquals("$external", credential.getSource());
67+
assertArrayEquals(password, credential.getPassword());
5668
}
5769

5870
@Test
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright (c) 2008 - 2013 10gen, Inc. <http://10gen.com>
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.mongodb;
18+
19+
import java.util.HashMap;
20+
import java.util.Map;
21+
22+
/**
23+
* An enumeration of the MongodDB-supported authentication mechanisms.
24+
*
25+
* @since 3.0
26+
*/
27+
public enum AuthenticationMechanism {
28+
/**
29+
* The GSSAPI mechanism. See the <a href="http://tools.ietf.org/html/rfc4752">RFC</a>.
30+
*/
31+
GSSAPI("GSSAPI"),
32+
33+
/**
34+
* The PLAIN mechanism. See the <a href="http://www.ietf.org/rfc/rfc4616.txt">RFC</a>.
35+
*/
36+
PLAIN("PLAIN"),
37+
38+
/**
39+
* The MongoDB Challenge Response mechanism.
40+
*/
41+
MONGODB_CR("MONGODB-CR");
42+
43+
private static final Map<String, AuthenticationMechanism> AUTH_MAP = new HashMap<String, AuthenticationMechanism>();
44+
private final String mechanismName;
45+
46+
AuthenticationMechanism(final String mechanismName) {
47+
this.mechanismName = mechanismName;
48+
}
49+
50+
/**
51+
* Get the mechanism name.
52+
*
53+
* @return the mechanism name
54+
*/
55+
public String getMechanismName() {
56+
return mechanismName;
57+
}
58+
59+
@Override
60+
public String toString() {
61+
return mechanismName;
62+
}
63+
64+
static {
65+
for (AuthenticationMechanism value : values()) {
66+
AUTH_MAP.put(value.getMechanismName(), value);
67+
}
68+
}
69+
70+
/**
71+
* Gets the mechanism by its name.
72+
*
73+
* @param mechanismName the mechanism name
74+
* @return the mechanism
75+
* @see #getMechanismName()
76+
*/
77+
public static AuthenticationMechanism fromMechanismName(final String mechanismName) {
78+
AuthenticationMechanism mechanism = AUTH_MAP.get(mechanismName);
79+
if (mechanism == null) {
80+
throw new IllegalArgumentException("Unsupported authMechanism: " + mechanismName);
81+
}
82+
return mechanism;
83+
}
84+
}

0 commit comments

Comments
 (0)