Skip to content

CSHARP-5471: Incorrectly resolving the authentication mechanism param… #1605

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
33 changes: 17 additions & 16 deletions src/MongoDB.Driver/Core/Configuration/ConnectionString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -684,29 +684,30 @@ private ConnectionString BuildResolvedConnectionString(ConnectionStringScheme re
.AllKeys
.SelectMany(key => resolvedOptions
.GetValues(key)
.Select(value => $"{key}={Uri.EscapeDataString(value)}")));
.Select(value => $"{key}={EscapeOptionValue(key, value)}")));

if (mergedOptions.Count > 0)
{
connectionString += "?" + string.Join("&", mergedOptions);
}

return new ConnectionString(connectionString, isResolved: true);

string EscapeOptionValue(string key, string value)
=> string.Equals(key, "authmechanismproperties", StringComparison.OrdinalIgnoreCase) ? value : Uri.EscapeDataString(value);

}

private void ExtractScheme(Match match)
{
var schemeGroup = match.Groups["scheme"];
if (schemeGroup.Success)
if (schemeGroup.Success && schemeGroup.Value == "mongodb+srv")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated change, but I looked at it :)

{
if (schemeGroup.Value == "mongodb+srv")
_scheme = ConnectionStringScheme.MongoDBPlusSrv;
if (!_tls.HasValue)
{
_scheme = ConnectionStringScheme.MongoDBPlusSrv;
if (!_tls.HasValue)
{
_tls = true;
_allOptions.Add("tls", "true");
}
_tls = true;
_allOptions.Add("tls", "true");
}
}
}
Expand Down Expand Up @@ -761,7 +762,13 @@ private void ExtractOptions(Match match)
var parts = option.Value.Split('=');
var name = parts[0].Trim();
var value = parts[1].Trim();
_allOptions.Add(name, Uri.UnescapeDataString(value));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think this change is safe, as we are stopping decoding for all options and that options are available via public API, not just for building the resolved connection string.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest either introduce new private collection to contain the unprocessed options (like a _rawOptions), or DO NOT escape/unescape only authmechanismproperties option to mimic what ParseOption does.

// Should not decode authmechanismproperties before splitting by separator.
if (!string.Equals(name, "authmechanismproperties", StringComparison.OrdinalIgnoreCase))
{
value = Uri.UnescapeDataString(value);
}

_allOptions.Add(name, value);
ParseOption(name, value);
}
}
Expand Down Expand Up @@ -905,12 +912,6 @@ string ProtectConnectionString(string connectionString)

private void ParseOption(string name, string value)
{
// Should not decode authmechanismproperties before splitting by separator.
if (!string.Equals(name, "authmechanismproperties", StringComparison.OrdinalIgnoreCase))
{
value = Uri.UnescapeDataString(value);
}

switch (name.ToLowerInvariant())
{
case "appname":
Expand Down
24 changes: 24 additions & 0 deletions tests/MongoDB.Driver.Tests/MongoCredentialTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,5 +155,29 @@ public void TestMechanismProperty()
Assert.Equal("awesome", withProperties.GetMechanismProperty<string>("SPN", null));
Assert.Equal(10, withProperties.GetMechanismProperty<int>("OTHER", 0));
}

[Theory]
[InlineData("mongodb+srv://<cluster>/?retryWrites=true&authMechanism=MONGODB-OIDC&authSource=%24external&authMechanismProperties=ENVIRONMENT:azure,prop:ab%2Ccd%2Cef%2Cjh,TOKEN_RESOURCE:mongodb%3A%2F%2Ftest-cluster,ANOTHER:test")]
[InlineData("mongodb+srv://<cluster>/?retryWrites=true&authMechanism=MONGODB-OIDC&authSource=%24external&authMechanismProperties=ENVIRONMENT:azure,prop:ab%2Ccd%2Cef%2Cjh,TOKEN_RESOURCE:mongodb://test-cluster,ANOTHER:test")]
public void TestMechanismPropertyFromUnresolvedConnectionString(string url)
{
var mongoConnection = MongoClientSettings.FromConnectionString(url);
mongoConnection.Credential.GetMechanismProperty("ENVIRONMENT", "").Should().Be("azure");
mongoConnection.Credential.GetMechanismProperty("prop", "").Should().Be("ab,cd,ef,jh");
mongoConnection.Credential.GetMechanismProperty("TOKEN_RESOURCE", "").Should().Be("mongodb://test-cluster");
mongoConnection.Credential.GetMechanismProperty("ANOTHER", "").Should().Be("test");
}

[Theory]
[InlineData("mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authSource=$external&authMechanismProperties=ENVIRONMENT:azure,prop:ab%2Ccd%2Cef%2Cjh,TOKEN_RESOURCE:mongodb%3A%2F%2Ftest-cluster,ANOTHER:test")]
[InlineData("mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authSource=$external&authMechanismProperties=ENVIRONMENT:azure,prop:ab%2Ccd%2Cef%2Cjh,TOKEN_RESOURCE:mongodb://test-cluster,ANOTHER:test")]
public void TestMechanismPropertyFromResolvedConnectionString(string url)
{
var mongoConnection = MongoClientSettings.FromConnectionString(url);
mongoConnection.Credential.GetMechanismProperty("ENVIRONMENT", "").Should().Be("azure");
mongoConnection.Credential.GetMechanismProperty("prop", "").Should().Be("ab,cd,ef,jh");
mongoConnection.Credential.GetMechanismProperty("TOKEN_RESOURCE", "").Should().Be("mongodb://test-cluster");
mongoConnection.Credential.GetMechanismProperty("ANOTHER", "").Should().Be("test");
}
}
}