Skip to content

Commit 5e33c62

Browse files
MikalaiMazurenkaDmitryLukyanov
authored andcommitted
CSHARP-2116: Implement auth tests
1 parent 84d429e commit 5e33c62

File tree

7 files changed

+533
-626
lines changed

7 files changed

+533
-626
lines changed

src/MongoDB.Driver/MongoCredential.cs

Lines changed: 58 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
using System;
1717
using System.Collections.Generic;
1818
using System.Linq;
19-
using System.Runtime.InteropServices;
2019
using System.Security;
2120
using MongoDB.Driver.Core.Authentication;
2221
using MongoDB.Shared;
@@ -165,7 +164,9 @@ public string Username
165164
/// <returns>A default credential.</returns>
166165
public static MongoCredential CreateCredential(string databaseName, string username, string password)
167166
{
168-
return FromComponents(null,
167+
return FromComponents(
168+
mechanism: null,
169+
source: null,
169170
databaseName,
170171
username,
171172
new PasswordEvidence(password));
@@ -186,7 +187,9 @@ public static MongoCredential CreateCredential(string databaseName, string usern
186187
/// <returns>A default credential.</returns>
187188
public static MongoCredential CreateCredential(string databaseName, string username, SecureString password)
188189
{
189-
return FromComponents(null,
190+
return FromComponents(
191+
mechanism: null,
192+
source: null,
190193
databaseName,
191194
username,
192195
new PasswordEvidence(password));
@@ -200,8 +203,10 @@ public static MongoCredential CreateCredential(string databaseName, string usern
200203
/// <remarks>This overload is used primarily on linux.</remarks>
201204
public static MongoCredential CreateGssapiCredential(string username)
202205
{
203-
return FromComponents("GSSAPI",
204-
"$external",
206+
return FromComponents(
207+
mechanism: "GSSAPI",
208+
source: "$external",
209+
databaseName: null,
205210
username,
206211
new ExternalEvidence());
207212
}
@@ -214,8 +219,10 @@ public static MongoCredential CreateGssapiCredential(string username)
214219
/// <returns>A credential for GSSAPI.</returns>
215220
public static MongoCredential CreateGssapiCredential(string username, string password)
216221
{
217-
return FromComponents("GSSAPI",
218-
"$external",
222+
return FromComponents(
223+
mechanism: "GSSAPI",
224+
source: "$external",
225+
databaseName: null,
219226
username,
220227
new PasswordEvidence(password));
221228
}
@@ -228,8 +235,10 @@ public static MongoCredential CreateGssapiCredential(string username, string pas
228235
/// <returns>A credential for GSSAPI.</returns>
229236
public static MongoCredential CreateGssapiCredential(string username, SecureString password)
230237
{
231-
return FromComponents("GSSAPI",
232-
"$external",
238+
return FromComponents(
239+
mechanism: "GSSAPI",
240+
source: "$external",
241+
databaseName: null,
233242
username,
234243
new PasswordEvidence(password));
235244
}
@@ -244,7 +253,9 @@ public static MongoCredential CreateGssapiCredential(string username, SecureStri
244253
[Obsolete("MONGODB-CR was replaced by SCRAM-SHA-1 in MongoDB 3.0, and is now deprecated.")]
245254
public static MongoCredential CreateMongoCRCredential(string databaseName, string username, string password)
246255
{
247-
return FromComponents("MONGODB-CR",
256+
return FromComponents(
257+
mechanism: "MONGODB-CR",
258+
source: null,
248259
databaseName,
249260
username,
250261
new PasswordEvidence(password));
@@ -260,7 +271,9 @@ public static MongoCredential CreateMongoCRCredential(string databaseName, strin
260271
[Obsolete("MONGODB-CR was replaced by SCRAM-SHA-1 in MongoDB 3.0, and is now deprecated.")]
261272
public static MongoCredential CreateMongoCRCredential(string databaseName, string username, SecureString password)
262273
{
263-
return FromComponents("MONGODB-CR",
274+
return FromComponents(
275+
mechanism: "MONGODB-CR",
276+
source: null,
264277
databaseName,
265278
username,
266279
new PasswordEvidence(password));
@@ -273,8 +286,10 @@ public static MongoCredential CreateMongoCRCredential(string databaseName, strin
273286
/// <returns>A credential for MONGODB-X509.</returns>
274287
public static MongoCredential CreateMongoX509Credential(string username)
275288
{
276-
return FromComponents("MONGODB-X509",
277-
"$external",
289+
return FromComponents(
290+
mechanism: "MONGODB-X509",
291+
source: "$external",
292+
databaseName: null,
278293
username,
279294
new ExternalEvidence());
280295
}
@@ -288,7 +303,9 @@ public static MongoCredential CreateMongoX509Credential(string username)
288303
/// <returns>A credential for PLAIN.</returns>
289304
public static MongoCredential CreatePlainCredential(string databaseName, string username, string password)
290305
{
291-
return FromComponents("PLAIN",
306+
return FromComponents(
307+
mechanism: "PLAIN",
308+
source: null,
292309
databaseName,
293310
username,
294311
new PasswordEvidence(password));
@@ -303,7 +320,9 @@ public static MongoCredential CreatePlainCredential(string databaseName, string
303320
/// <returns>A credential for PLAIN.</returns>
304321
public static MongoCredential CreatePlainCredential(string databaseName, string username, SecureString password)
305322
{
306-
return FromComponents("PLAIN",
323+
return FromComponents(
324+
mechanism: "PLAIN",
325+
source: null,
307326
databaseName,
308327
username,
309328
new PasswordEvidence(password));
@@ -452,9 +471,14 @@ internal IAuthenticator ToAuthenticator()
452471

453472
// internal static methods
454473
internal static MongoCredential FromComponents(string mechanism, string source, string username, string password)
474+
{
475+
return FromComponents(mechanism, source, databaseName: null, username, password);
476+
}
477+
478+
internal static MongoCredential FromComponents(string mechanism, string source, string databaseName, string username, string password)
455479
{
456480
var evidence = password == null ? (MongoIdentityEvidence)new ExternalEvidence() : new PasswordEvidence(password);
457-
return FromComponents(mechanism, source, username, evidence);
481+
return FromComponents(mechanism, source, databaseName, username, evidence);
458482
}
459483

460484
// private methods
@@ -471,7 +495,15 @@ private void ValidatePassword(string password)
471495
}
472496

473497
// private static methods
474-
private static MongoCredential FromComponents(string mechanism, string source, string username, MongoIdentityEvidence evidence)
498+
private static void EnsureNullOrExternalSource(string mechanism, string source)
499+
{
500+
if (source != null && source != "$external")
501+
{
502+
throw new ArgumentException($"A {mechanism} source must be $external.", nameof(source));
503+
}
504+
}
505+
506+
private static MongoCredential FromComponents(string mechanism, string source, string databaseName, string username, MongoIdentityEvidence evidence)
475507
{
476508
var defaultedMechanism = (mechanism ?? "DEFAULT").Trim().ToUpperInvariant();
477509
switch (defaultedMechanism)
@@ -481,7 +513,7 @@ private static MongoCredential FromComponents(string mechanism, string source, s
481513
case "SCRAM-SHA-1":
482514
case "SCRAM-SHA-256":
483515
// it is allowed for a password to be an empty string, but not a username
484-
source = source ?? "admin";
516+
source = source ?? databaseName ?? "admin";
485517
if (evidence == null || !(evidence is PasswordEvidence))
486518
{
487519
var message = string.Format("A {0} credential must have a password.", defaultedMechanism);
@@ -493,8 +525,8 @@ private static MongoCredential FromComponents(string mechanism, string source, s
493525
new MongoInternalIdentity(source, username),
494526
evidence);
495527
case "MONGODB-X509":
496-
// always $external for X509.
497-
source = "$external";
528+
// MUST be "$external". Defaults to $external.
529+
EnsureNullOrExternalSource(mechanism, source);
498530
if (evidence == null || !(evidence is ExternalEvidence))
499531
{
500532
throw new ArgumentException("A MONGODB-X509 does not support a password.");
@@ -505,15 +537,15 @@ private static MongoCredential FromComponents(string mechanism, string source, s
505537
new MongoX509Identity(username),
506538
evidence);
507539
case "GSSAPI":
508-
// always $external for GSSAPI.
509-
source = "$external";
540+
// MUST be "$external". Defaults to $external.
541+
EnsureNullOrExternalSource(mechanism, source);
510542

511543
return new MongoCredential(
512-
"GSSAPI",
513-
new MongoExternalIdentity(source, username),
544+
mechanism,
545+
new MongoExternalIdentity(username),
514546
evidence);
515547
case "PLAIN":
516-
source = source ?? "admin";
548+
source = source ?? databaseName ?? "$external";
517549
if (evidence == null || !(evidence is PasswordEvidence))
518550
{
519551
throw new ArgumentException("A PLAIN credential must have a password.");
@@ -522,7 +554,7 @@ private static MongoCredential FromComponents(string mechanism, string source, s
522554
MongoIdentity identity;
523555
if (source == "$external")
524556
{
525-
identity = new MongoExternalIdentity(source, username);
557+
identity = new MongoExternalIdentity(username);
526558
}
527559
else
528560
{

src/MongoDB.Driver/MongoUrl.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,8 @@ public MongoCredential GetCredential()
596596
{
597597
return MongoCredential.FromComponents(
598598
_authenticationMechanism,
599-
_authenticationSource ?? _databaseName,
599+
_authenticationSource,
600+
_databaseName,
600601
_username,
601602
_password);
602603
}
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/* Copyright 2018-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 FluentAssertions;
18+
using MongoDB.Bson;
19+
using MongoDB.Bson.TestHelpers;
20+
using MongoDB.Bson.TestHelpers.JsonDrivenTests;
21+
using MongoDB.Bson.TestHelpers.XunitExtensions;
22+
using MongoDB.Driver.Core.Authentication;
23+
using Xunit;
24+
25+
namespace MongoDB.Driver.Tests.Specifications.auth
26+
{
27+
public class AuthTestRunner
28+
{
29+
[SkippableTheory]
30+
[ClassData(typeof(TestCaseFactory))]
31+
public void RunTestDefinition(JsonDrivenTestCase testCase)
32+
{
33+
var definition = testCase.Test;
34+
JsonDrivenHelper.EnsureAllFieldsAreValid(definition, "description", "uri", "valid", "credential");
35+
36+
MongoCredential mongoCredential = null;
37+
Exception parseException = null;
38+
try
39+
{
40+
var connectionString = (string)definition["uri"];
41+
mongoCredential = MongoClientSettings.FromConnectionString(connectionString).Credential;
42+
}
43+
catch (Exception ex)
44+
{
45+
parseException = ex;
46+
}
47+
48+
if (parseException == null)
49+
{
50+
AssertValid(mongoCredential, definition);
51+
}
52+
else
53+
{
54+
AssertInvalid(parseException, definition);
55+
}
56+
}
57+
58+
private void AssertValid(MongoCredential mongoCredential, BsonDocument definition)
59+
{
60+
if (!definition["valid"].ToBoolean())
61+
{
62+
throw new AssertionException($"The connection string '{definition["uri"]}' should be invalid.");
63+
}
64+
65+
var expectedCredential = definition["credential"] as BsonDocument;
66+
if (expectedCredential != null)
67+
{
68+
JsonDrivenHelper.EnsureAllFieldsAreValid(expectedCredential, "username", "password", "source", "mechanism", "mechanism_properties");
69+
mongoCredential.Username.Should().Be(ValueToString(expectedCredential["username"]));
70+
#pragma warning disable 618
71+
mongoCredential.Password.Should().Be(ValueToString(expectedCredential["password"]));
72+
#pragma warning restore 618
73+
mongoCredential.Source.Should().Be(ValueToString(expectedCredential["source"]));
74+
mongoCredential.Mechanism.Should().Be(ValueToString(expectedCredential["mechanism"]));
75+
76+
var authenticator = mongoCredential.ToAuthenticator();
77+
if (authenticator is GssapiAuthenticator gssapiAuthenticator)
78+
{
79+
expectedCredential.TryGetValue("mechanism_properties", out var expectedMechanismProperties);
80+
if (expectedMechanismProperties.IsBsonNull)
81+
{
82+
var serviceName = gssapiAuthenticator._mechanism_serviceName();
83+
serviceName.Should().Be("mongodb"); // The default is "mongodb".
84+
var canonicalizeHostName = gssapiAuthenticator._mechanism_canonicalizeHostName();
85+
canonicalizeHostName.Should().BeFalse(); // The default is "false".
86+
}
87+
else
88+
{
89+
foreach (var expectedMechanismProperty in expectedMechanismProperties.AsBsonDocument)
90+
{
91+
var mechanismName = expectedMechanismProperty.Name;
92+
switch (mechanismName)
93+
{
94+
case "SERVICE_NAME":
95+
var serviceName = gssapiAuthenticator._mechanism_serviceName();
96+
serviceName.Should().Be(ValueToString(expectedMechanismProperty.Value));
97+
break;
98+
case "CANONICALIZE_HOST_NAME":
99+
var canonicalizeHostName = gssapiAuthenticator._mechanism_canonicalizeHostName();
100+
canonicalizeHostName.Should().Be(expectedMechanismProperty.Value.ToBoolean());
101+
break;
102+
default:
103+
throw new Exception($"Invalid mechanism property '{mechanismName}'.");
104+
}
105+
}
106+
}
107+
}
108+
else
109+
{
110+
// Other authenticators do not contain mechanism properties
111+
}
112+
}
113+
}
114+
115+
private void AssertInvalid(Exception ex, BsonDocument definition)
116+
{
117+
if (definition["valid"].ToBoolean())
118+
{
119+
throw new AssertionException($"The connection string '{definition["uri"]}' should be valid.", ex);
120+
}
121+
}
122+
123+
private string ValueToString(BsonValue value)
124+
{
125+
return value == BsonNull.Value ? null : value.ToString();
126+
}
127+
128+
// nested types
129+
private class TestCaseFactory : JsonDrivenTestCaseFactory
130+
{
131+
protected override string PathPrefix => "MongoDB.Driver.Tests.Specifications.auth.tests.";
132+
}
133+
}
134+
135+
internal static class GssapiAuthenticatorReflector
136+
{
137+
public static bool _mechanism_canonicalizeHostName(this GssapiAuthenticator obj)
138+
{
139+
var mechanism = _mechanism(obj);
140+
return (bool)Reflector.GetFieldValue(mechanism, "_canonicalizeHostName");
141+
}
142+
143+
public static string _mechanism_serviceName(this GssapiAuthenticator obj)
144+
{
145+
var mechanism = _mechanism(obj);
146+
return (string)Reflector.GetFieldValue(mechanism, "_serviceName");
147+
}
148+
149+
private static object _mechanism(GssapiAuthenticator obj)
150+
{
151+
return Reflector.GetFieldValue(obj, nameof(_mechanism));
152+
}
153+
}
154+
}

0 commit comments

Comments
 (0)