Skip to content

Commit 258807e

Browse files
committed
SCRAM-SHA Client & Server Key cache
JAVA-2510
1 parent 8d8dd3b commit 258807e

31 files changed

+336
-262
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ public AuthenticationMechanism getAuthenticationMechanism() {
378378
/**
379379
* Gets the user name
380380
*
381-
* @return the user name. Can never be null.
381+
* @return the user name.
382382
*/
383383
@Nullable
384384
public String getUserName() {

driver-core/src/main/com/mongodb/connection/Authenticator.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,25 @@
2323
import com.mongodb.lang.Nullable;
2424

2525
abstract class Authenticator {
26-
private final MongoCredential credential;
26+
private final MongoCredentialWithCache credential;
2727

28-
Authenticator(@NonNull final MongoCredential credential) {
28+
Authenticator(@NonNull final MongoCredentialWithCache credential) {
2929
this.credential = credential;
3030
}
3131

3232
@NonNull
33-
MongoCredential getCredential() {
33+
MongoCredentialWithCache getMongoCredentialWithCache() {
3434
return credential;
3535
}
3636

37+
@NonNull
38+
MongoCredential getMongoCredential() {
39+
return credential.getCredential();
40+
}
41+
3742
@NonNull
3843
String getUserNameNonNull() {
39-
String userName = credential.getUserName();
44+
String userName = credential.getCredential().getUserName();
4045
if (userName == null) {
4146
throw new MongoInternalException("User name can not be null");
4247
}
@@ -45,16 +50,16 @@ String getUserNameNonNull() {
4550

4651
@NonNull
4752
char[] getPasswordNonNull() {
48-
char[] password = credential.getPassword();
53+
char[] password = credential.getCredential().getPassword();
4954
if (password == null) {
5055
throw new MongoInternalException("Password can not be null");
5156
}
5257
return password;
5358
}
5459

5560
@NonNull
56-
public <T> T getNonNullMechanismProperty(final String key, @Nullable final T defaultValue) {
57-
T mechanismProperty = credential.getMechanismProperty(key, defaultValue);
61+
<T> T getNonNullMechanismProperty(final String key, @Nullable final T defaultValue) {
62+
T mechanismProperty = credential.getCredential().getMechanismProperty(key, defaultValue);
5863
if (mechanismProperty == null) {
5964
throw new MongoInternalException("Mechanism property can not be null");
6065
}

driver-core/src/main/com/mongodb/connection/DefaultAuthenticator.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import static com.mongodb.assertions.Assertions.isTrueArgument;
2323

2424
class DefaultAuthenticator extends Authenticator {
25-
DefaultAuthenticator(final MongoCredential credential) {
25+
DefaultAuthenticator(final MongoCredentialWithCache credential) {
2626
super(credential);
2727
isTrueArgument("unspecified authentication mechanism", credential.getAuthenticationMechanism() == null);
2828
}
@@ -40,9 +40,9 @@ void authenticateAsync(final InternalConnection connection, final ConnectionDesc
4040

4141
Authenticator createAuthenticator(final ConnectionDescription connectionDescription) {
4242
if (connectionDescription.getServerVersion().compareTo(new ServerVersion(2, 7)) >= 0) {
43-
return new ScramSha1Authenticator(getCredential());
43+
return new ScramShaAuthenticator(getMongoCredentialWithCache());
4444
} else {
45-
return new NativeAuthenticator(getCredential());
45+
return new NativeAuthenticator(getMongoCredentialWithCache());
4646
}
4747
}
4848
}

driver-core/src/main/com/mongodb/connection/DefaultClusterFactory.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ public Cluster createCluster(final ClusterSettings clusterSettings, final Server
173173
final List<MongoCompressor> compressorList) {
174174

175175
ClusterId clusterId = new ClusterId(clusterSettings.getDescription());
176+
177+
176178
ClusterableServerFactory serverFactory = new DefaultClusterableServerFactory(clusterId, clusterSettings, serverSettings,
177179
connectionPoolSettings, streamFactory, heartbeatStreamFactory, credentialList, commandListener, applicationName,
178180
mongoDriverInformation != null ? mongoDriverInformation : MongoDriverInformation.builder().build(), compressorList);

driver-core/src/main/com/mongodb/connection/DefaultClusterableServerFactory.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818

1919
import com.mongodb.MongoCompressor;
2020
import com.mongodb.MongoCredential;
21-
import com.mongodb.ServerAddress;
2221
import com.mongodb.MongoDriverInformation;
22+
import com.mongodb.ServerAddress;
2323
import com.mongodb.event.CommandListener;
2424
import com.mongodb.event.ServerListener;
2525

@@ -32,7 +32,7 @@ class DefaultClusterableServerFactory implements ClusterableServerFactory {
3232
private final ServerSettings serverSettings;
3333
private final ConnectionPoolSettings connectionPoolSettings;
3434
private final StreamFactory streamFactory;
35-
private final List<MongoCredential> credentialList;
35+
private final List<MongoCredentialWithCache> credentialList;
3636
private final StreamFactory heartbeatStreamFactory;
3737
private final CommandListener commandListener;
3838
private final String applicationName;
@@ -50,7 +50,7 @@ class DefaultClusterableServerFactory implements ClusterableServerFactory {
5050
this.serverSettings = serverSettings;
5151
this.connectionPoolSettings = connectionPoolSettings;
5252
this.streamFactory = streamFactory;
53-
this.credentialList = credentialList;
53+
this.credentialList = MongoCredentialWithCache.wrapCredentialList(credentialList);
5454
this.heartbeatStreamFactory = heartbeatStreamFactory;
5555
this.commandListener = commandListener;
5656
this.applicationName = applicationName;

driver-core/src/main/com/mongodb/connection/GSSAPIAuthenticator.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,14 @@
4242
class GSSAPIAuthenticator extends SaslAuthenticator {
4343
private static final String GSSAPI_MECHANISM_NAME = "GSSAPI";
4444
private static final String GSSAPI_OID = "1.2.840.113554.1.2.2";
45-
public static final String SERVICE_NAME_DEFAULT_VALUE = "mongodb";
46-
public static final Boolean CANONICALIZE_HOST_NAME_DEFAULT_VALUE = false;
45+
private static final String SERVICE_NAME_DEFAULT_VALUE = "mongodb";
46+
private static final Boolean CANONICALIZE_HOST_NAME_DEFAULT_VALUE = false;
4747

48-
GSSAPIAuthenticator(final MongoCredential credential) {
48+
GSSAPIAuthenticator(final MongoCredentialWithCache credential) {
4949
super(credential);
5050

51-
if (getCredential().getAuthenticationMechanism() != GSSAPI) {
52-
throw new MongoException("Incorrect mechanism: " + this.getCredential().getMechanism());
51+
if (getMongoCredential().getAuthenticationMechanism() != GSSAPI) {
52+
throw new MongoException("Incorrect mechanism: " + getMongoCredential().getMechanism());
5353
}
5454
}
5555

@@ -60,9 +60,9 @@ public String getMechanismName() {
6060

6161
@Override
6262
protected SaslClient createSaslClient(final ServerAddress serverAddress) {
63-
MongoCredential credential = getCredential();
63+
MongoCredential credential = getMongoCredential();
6464
try {
65-
Map<String, Object> saslClientProperties = getCredential().getMechanismProperty(JAVA_SASL_CLIENT_PROPERTIES_KEY, null);
65+
Map<String, Object> saslClientProperties = credential.getMechanismProperty(JAVA_SASL_CLIENT_PROPERTIES_KEY, null);
6666
if (saslClientProperties == null) {
6767
saslClientProperties = new HashMap<String, Object>();
6868
saslClientProperties.put(Sasl.MAX_BUFFER, "0");

driver-core/src/main/com/mongodb/connection/InternalStreamConnectionFactory.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@
1616

1717
package com.mongodb.connection;
1818

19-
import com.mongodb.AuthenticationMechanism;
2019
import com.mongodb.MongoCompressor;
21-
import com.mongodb.MongoCredential;
2220
import com.mongodb.MongoDriverInformation;
2321
import com.mongodb.event.CommandListener;
2422
import org.bson.BsonDocument;
@@ -36,7 +34,7 @@ class InternalStreamConnectionFactory implements InternalConnectionFactory {
3634
private final List<MongoCompressor> compressorList;
3735
private final CommandListener commandListener;
3836

39-
InternalStreamConnectionFactory(final StreamFactory streamFactory, final List<MongoCredential> credentialList,
37+
InternalStreamConnectionFactory(final StreamFactory streamFactory, final List<MongoCredentialWithCache> credentialList,
4038
final String applicationName, final MongoDriverInformation mongoDriverInformation,
4139
final List<MongoCompressor> compressorList,
4240
final CommandListener commandListener) {
@@ -46,7 +44,7 @@ class InternalStreamConnectionFactory implements InternalConnectionFactory {
4644
this.clientMetadataDocument = createClientMetadataDocument(applicationName, mongoDriverInformation);
4745
notNull("credentialList", credentialList);
4846
this.authenticators = new ArrayList<Authenticator>(credentialList.size());
49-
for (MongoCredential credential : credentialList) {
47+
for (MongoCredentialWithCache credential : credentialList) {
5048
authenticators.add(createAuthenticator(credential));
5149
}
5250
}
@@ -58,13 +56,12 @@ public InternalConnection create(final ServerId serverId) {
5856
compressorList));
5957
}
6058

61-
private Authenticator createAuthenticator(final MongoCredential credential) {
62-
AuthenticationMechanism authenticationMechanism = credential.getAuthenticationMechanism();
63-
if (authenticationMechanism == null) {
59+
private Authenticator createAuthenticator(final MongoCredentialWithCache credential) {
60+
if (credential.getAuthenticationMechanism() == null) {
6461
return new DefaultAuthenticator(credential);
6562
}
6663

67-
switch (authenticationMechanism) {
64+
switch (credential.getAuthenticationMechanism()) {
6865
case GSSAPI:
6966
return new GSSAPIAuthenticator(credential);
7067
case PLAIN:
@@ -77,7 +74,7 @@ private Authenticator createAuthenticator(final MongoCredential credential) {
7774
case MONGODB_CR:
7875
return new NativeAuthenticator(credential);
7976
default:
80-
throw new IllegalArgumentException("Unsupported authentication mechanism " + authenticationMechanism);
77+
throw new IllegalArgumentException("Unsupported authentication mechanism " + credential.getAuthenticationMechanism());
8178
}
8279
}
8380
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
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 com.mongodb.connection;
18+
19+
import com.mongodb.AuthenticationMechanism;
20+
import com.mongodb.MongoCredential;
21+
22+
import java.util.ArrayList;
23+
import java.util.List;
24+
25+
class MongoCredentialWithCache {
26+
private final MongoCredential credential;
27+
private final Cache cache;
28+
29+
static List<MongoCredentialWithCache> wrapCredentialList(final List<MongoCredential> credentialList) {
30+
ArrayList<MongoCredentialWithCache> credentialListWithCache = new ArrayList<MongoCredentialWithCache>();
31+
for (MongoCredential credential : credentialList) {
32+
credentialListWithCache.add(new MongoCredentialWithCache(credential));
33+
}
34+
return credentialListWithCache;
35+
}
36+
37+
MongoCredentialWithCache(final MongoCredential credential) {
38+
this(credential, null);
39+
}
40+
41+
MongoCredentialWithCache(final MongoCredential credential, final Cache cache) {
42+
this.credential = credential;
43+
this.cache = cache != null ? cache : new Cache();
44+
}
45+
46+
MongoCredentialWithCache withMechanism(final AuthenticationMechanism mechanism) {
47+
return new MongoCredentialWithCache(credential.withMechanism(mechanism), cache);
48+
}
49+
50+
AuthenticationMechanism getAuthenticationMechanism() {
51+
return credential.getAuthenticationMechanism();
52+
}
53+
54+
public MongoCredential getCredential() {
55+
return credential;
56+
}
57+
58+
@SuppressWarnings("unchecked")
59+
<T> T getFromCache(final Object key, final Class<T> clazz) {
60+
return clazz.cast(cache.get(key));
61+
}
62+
63+
void putInCache(final Object key, final Object value) {
64+
cache.set(key, value);
65+
}
66+
67+
68+
static class Cache {
69+
private Object cacheKey;
70+
private Object cacheValue;
71+
72+
synchronized Object get(final Object key) {
73+
if (cacheKey != null && cacheKey.equals(key)) {
74+
return cacheValue;
75+
}
76+
return null;
77+
}
78+
79+
synchronized void set(final Object key, final Object value) {
80+
cacheKey = key;
81+
cacheValue = value;
82+
}
83+
}
84+
}
85+

driver-core/src/main/com/mongodb/connection/NativeAuthenticator.java

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package com.mongodb.connection;
1818

1919
import com.mongodb.MongoCommandException;
20-
import com.mongodb.MongoCredential;
2120
import com.mongodb.MongoSecurityException;
2221
import com.mongodb.async.SingleResultCallback;
2322
import org.bson.BsonDocument;
@@ -29,37 +28,37 @@
2928
import static com.mongodb.internal.authentication.NativeAuthenticationHelper.getNonceCommand;
3029

3130
class NativeAuthenticator extends Authenticator {
32-
NativeAuthenticator(final MongoCredential credential) {
31+
NativeAuthenticator(final MongoCredentialWithCache credential) {
3332
super(credential);
3433
}
3534

3635
@Override
3736
public void authenticate(final InternalConnection connection, final ConnectionDescription connectionDescription) {
3837
try {
39-
BsonDocument nonceResponse = executeCommand(getCredential().getSource(),
38+
BsonDocument nonceResponse = executeCommand(getMongoCredential().getSource(),
4039
getNonceCommand(),
4140
connection);
4241

4342
BsonDocument authCommand = getAuthCommand(getUserNameNonNull(),
4443
getPasswordNonNull(),
4544
((BsonString) nonceResponse.get("nonce")).getValue());
46-
executeCommand(getCredential().getSource(), authCommand, connection);
45+
executeCommand(getMongoCredential().getSource(), authCommand, connection);
4746
} catch (MongoCommandException e) {
48-
throw new MongoSecurityException(getCredential(), "Exception authenticating", e);
47+
throw new MongoSecurityException(getMongoCredential(), "Exception authenticating", e);
4948
}
5049
}
5150

5251
@Override
5352
void authenticateAsync(final InternalConnection connection, final ConnectionDescription connectionDescription,
5453
final SingleResultCallback<Void> callback) {
55-
executeCommandAsync(getCredential().getSource(), getNonceCommand(), connection,
54+
executeCommandAsync(getMongoCredential().getSource(), getNonceCommand(), connection,
5655
new SingleResultCallback<BsonDocument>() {
5756
@Override
5857
public void onResult(final BsonDocument nonceResult, final Throwable t) {
5958
if (t != null) {
6059
callback.onResult(null, translateThrowable(t));
6160
} else {
62-
executeCommandAsync(getCredential().getSource(),
61+
executeCommandAsync(getMongoCredential().getSource(),
6362
getAuthCommand(getUserNameNonNull(), getPasswordNonNull(),
6463
((BsonString) nonceResult.get("nonce")).getValue()),
6564
connection,
@@ -80,7 +79,7 @@ public void onResult(final BsonDocument result, final Throwable t) {
8079

8180
private Throwable translateThrowable(final Throwable t) {
8281
if (t instanceof MongoCommandException) {
83-
return new MongoSecurityException(getCredential(), "Exception authenticating", t);
82+
return new MongoSecurityException(getMongoCredential(), "Exception authenticating", t);
8483
} else {
8584
return t;
8685
}

driver-core/src/main/com/mongodb/connection/PlainAuthenticator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
class PlainAuthenticator extends SaslAuthenticator {
3737
private static final String DEFAULT_PROTOCOL = "mongodb";
3838

39-
PlainAuthenticator(final MongoCredential credential) {
39+
PlainAuthenticator(final MongoCredentialWithCache credential) {
4040
super(credential);
4141
}
4242

@@ -47,7 +47,7 @@ public String getMechanismName() {
4747

4848
@Override
4949
protected SaslClient createSaslClient(final ServerAddress serverAddress) {
50-
final MongoCredential credential = getCredential();
50+
final MongoCredential credential = getMongoCredential();
5151
isTrue("mechanism is PLAIN", credential.getAuthenticationMechanism() == PLAIN);
5252
try {
5353
return Sasl.createSaslClient(new String[]{PLAIN.getMechanismName()},

0 commit comments

Comments
 (0)