Skip to content

Commit 5e0d8bc

Browse files
DmitryLukyanovrstam
authored andcommitted
CSHARP-3196: Mongodb 4.4: Got MongoAuthenticationException : Server sent an invalid nonce.
1 parent 74771c3 commit 5e0d8bc

File tree

13 files changed

+192
-68
lines changed

13 files changed

+192
-68
lines changed

src/MongoDB.Driver.Core/Core/Authentication/AuthenticationHelper.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
using System.Runtime.InteropServices;
1919
using System.Security;
2020
using System.Security.Cryptography;
21-
using System.Text;
2221
using System.Threading;
2322
using System.Threading.Tasks;
2423
using MongoDB.Bson;
@@ -30,30 +29,32 @@ namespace MongoDB.Driver.Core.Authentication
3029
{
3130
internal static class AuthenticationHelper
3231
{
33-
public static void Authenticate(IConnection connection, ConnectionDescription description, CancellationToken cancellationToken)
32+
public static void Authenticate(IConnection connection, ConnectionDescription description, IReadOnlyList<IAuthenticator> authenticators, CancellationToken cancellationToken)
3433
{
3534
Ensure.IsNotNull(connection, nameof(connection));
3635
Ensure.IsNotNull(description, nameof(description));
36+
Ensure.IsNotNull(authenticators, nameof(authenticators));
3737

3838
// authentication is currently broken on arbiters
3939
if (!description.IsMasterResult.IsArbiter)
4040
{
41-
foreach (var authenticator in connection.Settings.Authenticators)
41+
foreach (var authenticator in authenticators)
4242
{
4343
authenticator.Authenticate(connection, description, cancellationToken);
4444
}
4545
}
4646
}
4747

48-
public static async Task AuthenticateAsync(IConnection connection, ConnectionDescription description, CancellationToken cancellationToken)
48+
public static async Task AuthenticateAsync(IConnection connection, ConnectionDescription description, IReadOnlyList<IAuthenticator> authenticators, CancellationToken cancellationToken)
4949
{
5050
Ensure.IsNotNull(connection, nameof(connection));
5151
Ensure.IsNotNull(description, nameof(description));
52+
Ensure.IsNotNull(authenticators, nameof(authenticators));
5253

5354
// authentication is currently broken on arbiters
5455
if (!description.IsMasterResult.IsArbiter)
5556
{
56-
foreach (var authenticator in connection.Settings.Authenticators)
57+
foreach (var authenticator in authenticators)
5758
{
5859
await authenticator.AuthenticateAsync(connection, description, cancellationToken).ConfigureAwait(false);
5960
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/* Copyright 2020-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using MongoDB.Driver.Core.Misc;
18+
19+
namespace MongoDB.Driver.Core.Authentication
20+
{
21+
/// <summary>
22+
/// Represents an authenticator factory.
23+
/// </summary>
24+
public class AuthenticatorFactory : IAuthenticatorFactory
25+
{
26+
private readonly Func<IAuthenticator> _authenticatorFactoryFunc;
27+
28+
/// <summary>
29+
/// Create an authenticatorFactory.
30+
/// </summary>
31+
/// <param name="authenticatorFactoryFunc">The authenticatorFactoryFunc.</param>
32+
public AuthenticatorFactory(Func<IAuthenticator> authenticatorFactoryFunc)
33+
{
34+
_authenticatorFactoryFunc = Ensure.IsNotNull(authenticatorFactoryFunc, nameof(authenticatorFactoryFunc));
35+
}
36+
37+
/// <inheritdoc/>
38+
public IAuthenticator Create()
39+
{
40+
return _authenticatorFactoryFunc();
41+
}
42+
}
43+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/* Copyright 2020-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
namespace MongoDB.Driver.Core.Authentication
17+
{
18+
/// <summary>
19+
/// Represents an authenticator factory.
20+
/// </summary>
21+
public interface IAuthenticatorFactory
22+
{
23+
/// <summary>
24+
/// Create an authenticator.
25+
/// </summary>
26+
/// <returns>The authenticator.</returns>
27+
IAuthenticator Create();
28+
}
29+
}

src/MongoDB.Driver.Core/Core/Configuration/ClusterBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ private ServerFactory CreateServerFactory()
252252
private IServerMonitorFactory CreateServerMonitorFactory()
253253
{
254254
var serverMonitorConnectionSettings = _connectionSettings
255-
.With(authenticators: new IAuthenticator[] { });
255+
.With(authenticatorFactories: new IAuthenticatorFactory[] { });
256256

257257
var heartbeatConnectTimeout = _tcpStreamSettings.ConnectTimeout;
258258
if (heartbeatConnectTimeout == TimeSpan.Zero || heartbeatConnectTimeout == Timeout.InfiniteTimeSpan)

src/MongoDB.Driver.Core/Core/Configuration/ClusterBuilderExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,8 @@ public static ClusterBuilder ConfigureWithConnectionString(this ClusterBuilder b
111111
// Connection
112112
if (connectionString.Username != null)
113113
{
114-
var authenticator = CreateAuthenticator(connectionString);
115-
builder = builder.ConfigureConnection(s => s.With(authenticators: new[] { authenticator }));
114+
var authenticatorFactory = new AuthenticatorFactory(() => CreateAuthenticator(connectionString));
115+
builder = builder.ConfigureConnection(s => s.With(authenticatorFactories: new[] { authenticatorFactory }));
116116
}
117117
if (connectionString.ApplicationName != null)
118118
{

src/MongoDB.Driver.Core/Core/Configuration/ConnectionSettings.cs

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,9 @@ namespace MongoDB.Driver.Core.Configuration
2727
/// </summary>
2828
public class ConnectionSettings
2929
{
30-
#region static
31-
// static fields
32-
private static readonly IReadOnlyList<IAuthenticator> __noAuthenticators = new IAuthenticator[0];
33-
#endregion
34-
3530
// fields
3631
private readonly string _applicationName;
37-
private readonly IReadOnlyList<IAuthenticator> _authenticators;
32+
private readonly IReadOnlyList<IAuthenticatorFactory> _authenticatorFactories;
3833
private readonly IReadOnlyList<CompressorConfiguration> _compressors;
3934
private readonly TimeSpan _maxIdleTime;
4035
private readonly TimeSpan _maxLifeTime;
@@ -43,19 +38,19 @@ public class ConnectionSettings
4338
/// <summary>
4439
/// Initializes a new instance of the <see cref="ConnectionSettings" /> class.
4540
/// </summary>
46-
/// <param name="authenticators">The authenticators.</param>
41+
/// <param name="authenticatorFactories">The authenticator factories.</param>
4742
/// <param name="compressors">The compressors.</param>
4843
/// <param name="maxIdleTime">The maximum idle time.</param>
4944
/// <param name="maxLifeTime">The maximum life time.</param>
5045
/// <param name="applicationName">The application name.</param>
5146
public ConnectionSettings(
52-
Optional<IEnumerable<IAuthenticator>> authenticators = default(Optional<IEnumerable<IAuthenticator>>),
47+
Optional<IEnumerable<IAuthenticatorFactory>> authenticatorFactories = default,
5348
Optional<IEnumerable<CompressorConfiguration>> compressors = default(Optional<IEnumerable<CompressorConfiguration>>),
5449
Optional<TimeSpan> maxIdleTime = default(Optional<TimeSpan>),
5550
Optional<TimeSpan> maxLifeTime = default(Optional<TimeSpan>),
5651
Optional<string> applicationName = default(Optional<string>))
5752
{
58-
_authenticators = Ensure.IsNotNull(authenticators.WithDefault(__noAuthenticators), "authenticators").ToList();
53+
_authenticatorFactories = Ensure.IsNotNull(authenticatorFactories.WithDefault(Enumerable.Empty<IAuthenticatorFactory>()), nameof(authenticatorFactories)).ToList().AsReadOnly();
5954
_compressors = Ensure.IsNotNull(compressors.WithDefault(Enumerable.Empty<CompressorConfiguration>()), nameof(compressors)).ToList();
6055
_maxIdleTime = Ensure.IsGreaterThanZero(maxIdleTime.WithDefault(TimeSpan.FromMinutes(10)), "maxIdleTime");
6156
_maxLifeTime = Ensure.IsGreaterThanZero(maxLifeTime.WithDefault(TimeSpan.FromMinutes(30)), "maxLifeTime");
@@ -75,14 +70,14 @@ public string ApplicationName
7570
}
7671

7772
/// <summary>
78-
/// Gets the authenticators.
73+
/// Gets the authenticator factories.
7974
/// </summary>
8075
/// <value>
81-
/// The authenticators.
76+
/// The authenticator factories.
8277
/// </value>
83-
public IReadOnlyList<IAuthenticator> Authenticators
78+
public IReadOnlyList<IAuthenticatorFactory> AuthenticatorFactories
8479
{
85-
get { return _authenticators; }
80+
get { return _authenticatorFactories; }
8681
}
8782

8883
/// <summary>
@@ -122,21 +117,21 @@ public TimeSpan MaxLifeTime
122117
/// <summary>
123118
/// Returns a new ConnectionSettings instance with some settings changed.
124119
/// </summary>
125-
/// <param name="authenticators">The authenticators.</param>
120+
/// <param name="authenticatorFactories">The authenticator factories.</param>
126121
/// <param name="compressors">The compressors.</param>
127122
/// <param name="maxIdleTime">The maximum idle time.</param>
128123
/// <param name="maxLifeTime">The maximum life time.</param>
129124
/// <param name="applicationName">The application name.</param>
130125
/// <returns>A new ConnectionSettings instance.</returns>
131126
public ConnectionSettings With(
132-
Optional<IEnumerable<IAuthenticator>> authenticators = default(Optional<IEnumerable<IAuthenticator>>),
127+
Optional<IEnumerable<IAuthenticatorFactory>> authenticatorFactories = default,
133128
Optional<IEnumerable<CompressorConfiguration>> compressors = default(Optional<IEnumerable<CompressorConfiguration>>),
134129
Optional<TimeSpan> maxIdleTime = default(Optional<TimeSpan>),
135130
Optional<TimeSpan> maxLifeTime = default(Optional<TimeSpan>),
136131
Optional<string> applicationName = default(Optional<string>))
137132
{
138133
return new ConnectionSettings(
139-
authenticators: Optional.Enumerable(authenticators.WithDefault(_authenticators)),
134+
authenticatorFactories: Optional.Enumerable(authenticatorFactories.WithDefault(_authenticatorFactories)),
140135
compressors: Optional.Enumerable(compressors.WithDefault(_compressors)),
141136
maxIdleTime: maxIdleTime.WithDefault(_maxIdleTime),
142137
maxLifeTime: maxLifeTime.WithDefault(_maxLifeTime),

src/MongoDB.Driver.Core/Core/Connections/ConnectionInitializer.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
using System.Collections.Generic;
17+
using System.Linq;
1718
using System.Threading;
1819
using System.Threading.Tasks;
1920
using MongoDB.Bson;
@@ -42,7 +43,8 @@ public ConnectionInitializer(string applicationName, IReadOnlyList<CompressorCon
4243
public ConnectionDescription InitializeConnection(IConnection connection, CancellationToken cancellationToken)
4344
{
4445
Ensure.IsNotNull(connection, nameof(connection));
45-
var isMasterCommand = CreateInitialIsMasterCommand(connection.Settings.Authenticators);
46+
var authenticators = connection.Settings.AuthenticatorFactories.Select(f => f.Create()).ToList();
47+
var isMasterCommand = CreateInitialIsMasterCommand(authenticators);
4648
var isMasterProtocol = IsMasterHelper.CreateProtocol(isMasterCommand);
4749
var isMasterResult = IsMasterHelper.GetResult(connection, isMasterProtocol, cancellationToken);
4850

@@ -51,7 +53,7 @@ public ConnectionDescription InitializeConnection(IConnection connection, Cancel
5153

5254
var description = new ConnectionDescription(connection.ConnectionId, isMasterResult, buildInfoResult);
5355

54-
AuthenticationHelper.Authenticate(connection, description, cancellationToken);
56+
AuthenticationHelper.Authenticate(connection, description, authenticators, cancellationToken);
5557

5658
var connectionIdServerValue = isMasterResult.ConnectionIdServerValue;
5759
if (connectionIdServerValue.HasValue)
@@ -79,7 +81,8 @@ public ConnectionDescription InitializeConnection(IConnection connection, Cancel
7981
public async Task<ConnectionDescription> InitializeConnectionAsync(IConnection connection, CancellationToken cancellationToken)
8082
{
8183
Ensure.IsNotNull(connection, nameof(connection));
82-
var isMasterCommand = CreateInitialIsMasterCommand(connection.Settings.Authenticators);
84+
var authenticators = connection.Settings.AuthenticatorFactories.Select(f => f.Create()).ToList();
85+
var isMasterCommand = CreateInitialIsMasterCommand(authenticators);
8386
var isMasterProtocol = IsMasterHelper.CreateProtocol(isMasterCommand);
8487
var isMasterResult = await IsMasterHelper.GetResultAsync(connection, isMasterProtocol, cancellationToken).ConfigureAwait(false);
8588

@@ -88,7 +91,7 @@ public async Task<ConnectionDescription> InitializeConnectionAsync(IConnection c
8891

8992
var description = new ConnectionDescription(connection.ConnectionId, isMasterResult, buildInfoResult);
9093

91-
await AuthenticationHelper.AuthenticateAsync(connection, description, cancellationToken).ConfigureAwait(false);
94+
await AuthenticationHelper.AuthenticateAsync(connection, description, authenticators, cancellationToken).ConfigureAwait(false);
9295

9396
var connectionIdServerValue = isMasterResult.ConnectionIdServerValue;
9497
if (connectionIdServerValue.HasValue)

src/MongoDB.Driver/ClusterRegistry.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
using System.Net.Security;
1919
using System.Net.Sockets;
2020
using System.Security.Cryptography.X509Certificates;
21+
using MongoDB.Driver.Core.Authentication;
2122
using MongoDB.Driver.Core.Clusters;
22-
using MongoDB.Driver.Core.Clusters.ServerSelectors;
2323
using MongoDB.Driver.Core.Configuration;
2424
using MongoDB.Driver.Core.Misc;
2525

@@ -105,9 +105,9 @@ private ConnectionPoolSettings ConfigureConnectionPool(ConnectionPoolSettings se
105105

106106
private ConnectionSettings ConfigureConnection(ConnectionSettings settings, ClusterKey clusterKey)
107107
{
108-
var authenticators = clusterKey.Credentials.Select(c => c.ToAuthenticator());
108+
var authenticatorFactories = clusterKey.Credentials.Select(c => new AuthenticatorFactory(() => c.ToAuthenticator()));
109109
return settings.With(
110-
authenticators: Optional.Enumerable(authenticators),
110+
authenticatorFactories: Optional.Enumerable<IAuthenticatorFactory>(authenticatorFactories),
111111
compressors: Optional.Enumerable(clusterKey.Compressors),
112112
maxIdleTime: clusterKey.MaxConnectionIdleTime,
113113
maxLifeTime: clusterKey.MaxConnectionLifeTime,

tests/MongoDB.Driver.Core.Tests/Core/Authentication/AuthenticationHelperTests.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
* limitations under the License.
1414
*/
1515

16+
using System.Linq;
1617
using System.Net;
1718
using System.Security;
1819
using System.Threading;
19-
using System.Threading.Tasks;
2020
using FluentAssertions;
2121
using MongoDB.Bson;
2222
using MongoDB.Bson.TestHelpers.XunitExtensions;
@@ -59,21 +59,22 @@ public void Authenticate_should_invoke_authenticators_when_they_exist(
5959
new BuildInfoResult(new BsonDocument("version", "2.8.0")));
6060

6161
var mockAuthenticator = new Mock<IAuthenticator>();
62-
var settings = new ConnectionSettings(authenticators: new[] { mockAuthenticator.Object });
62+
var settings = new ConnectionSettings(authenticatorFactories: new[] { new AuthenticatorFactory(() => mockAuthenticator.Object) });
63+
var authenticators = settings.AuthenticatorFactories.Select(a => a.Create()).ToList();
6364

6465
var mockConnection = new Mock<IConnection>();
6566
mockConnection.SetupGet(c => c.Description).Returns(description);
6667
mockConnection.SetupGet(c => c.Settings).Returns(settings);
6768

6869
if (async)
6970
{
70-
AuthenticationHelper.AuthenticateAsync(mockConnection.Object, description, CancellationToken.None).GetAwaiter().GetResult();
71+
AuthenticationHelper.AuthenticateAsync(mockConnection.Object, description, authenticators, CancellationToken.None).GetAwaiter().GetResult();
7172

7273
mockAuthenticator.Verify(a => a.AuthenticateAsync(mockConnection.Object, description, CancellationToken.None), Times.Once);
7374
}
7475
else
7576
{
76-
AuthenticationHelper.Authenticate(mockConnection.Object, description, CancellationToken.None);
77+
AuthenticationHelper.Authenticate(mockConnection.Object, description, authenticators, CancellationToken.None);
7778

7879
mockAuthenticator.Verify(a => a.Authenticate(mockConnection.Object, description, CancellationToken.None), Times.Once);
7980
}
@@ -91,21 +92,22 @@ public void Authenticate_should_not_invoke_authenticators_when_connected_to_an_a
9192
new BuildInfoResult(new BsonDocument("version", "2.8.0")));
9293

9394
var mockAuthenticator = new Mock<IAuthenticator>();
94-
var settings = new ConnectionSettings(authenticators: new[] { mockAuthenticator.Object });
95+
var settings = new ConnectionSettings(authenticatorFactories: new[] { new AuthenticatorFactory(() => mockAuthenticator.Object) });
96+
var authenticators = settings.AuthenticatorFactories.Select(a => a.Create()).ToList();
9597

9698
var mockConnection = new Mock<IConnection>();
9799
mockConnection.SetupGet(c => c.Description).Returns(description);
98100
mockConnection.SetupGet(c => c.Settings).Returns(settings);
99101

100102
if (async)
101103
{
102-
AuthenticationHelper.AuthenticateAsync(mockConnection.Object, description, CancellationToken.None).GetAwaiter().GetResult();
104+
AuthenticationHelper.AuthenticateAsync(mockConnection.Object, description, authenticators, CancellationToken.None).GetAwaiter().GetResult();
103105

104106
mockAuthenticator.Verify(a => a.AuthenticateAsync(It.IsAny<IConnection>(), It.IsAny<ConnectionDescription>(), It.IsAny<CancellationToken>()), Times.Never);
105107
}
106108
else
107109
{
108-
AuthenticationHelper.Authenticate(mockConnection.Object, description, CancellationToken.None);
110+
AuthenticationHelper.Authenticate(mockConnection.Object, description, authenticators, CancellationToken.None);
109111

110112
mockAuthenticator.Verify(a => a.Authenticate(It.IsAny<IConnection>(), It.IsAny<ConnectionDescription>(), It.IsAny<CancellationToken>()), Times.Never);
111113
}

0 commit comments

Comments
 (0)