Skip to content

Commit

Permalink
CSHARP-4911: Refactor AWS auth library to a separate package (mongodb…
Browse files Browse the repository at this point in the history
  • Loading branch information
sanych-sun authored Aug 28, 2024
1 parent 8447ef1 commit 1e97f44
Show file tree
Hide file tree
Showing 166 changed files with 3,658 additions and 3,111 deletions.
7 changes: 7 additions & 0 deletions CSharpDriver.sln
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FaasTests", "FaasTests", "{
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MongoDB.Driver.LambdaTest", "tests\FaasTests\LambdaTests\MongoDB.Driver.LambdaTest\MongoDB.Driver.LambdaTest.csproj", "{33B11279-DA4A-46EA-99BF-9DEDCAC50D95}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MongoDB.Driver.Authentication.AWS", "src\MongoDB.Driver.Authentication.AWS\MongoDB.Driver.Authentication.AWS.csproj", "{16FC2E26-8E6B-4D4A-8330-7639D4039E51}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -112,6 +114,10 @@ Global
{33B11279-DA4A-46EA-99BF-9DEDCAC50D95}.Debug|Any CPU.Build.0 = Debug|Any CPU
{33B11279-DA4A-46EA-99BF-9DEDCAC50D95}.Release|Any CPU.ActiveCfg = Release|Any CPU
{33B11279-DA4A-46EA-99BF-9DEDCAC50D95}.Release|Any CPU.Build.0 = Release|Any CPU
{16FC2E26-8E6B-4D4A-8330-7639D4039E51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{16FC2E26-8E6B-4D4A-8330-7639D4039E51}.Debug|Any CPU.Build.0 = Debug|Any CPU
{16FC2E26-8E6B-4D4A-8330-7639D4039E51}.Release|Any CPU.ActiveCfg = Release|Any CPU
{16FC2E26-8E6B-4D4A-8330-7639D4039E51}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -132,6 +138,7 @@ Global
{CF670F4A-49DD-4030-A4A0-1F4D600EB70A} = {503AB075-7C23-475B-BF7D-E7F751A31536}
{66E1914C-9E10-4B28-915E-AB3773EFDAB2} = {E472BDF5-61F1-461D-872B-9F53BB3ACA80}
{33B11279-DA4A-46EA-99BF-9DEDCAC50D95} = {66E1914C-9E10-4B28-915E-AB3773EFDAB2}
{16FC2E26-8E6B-4D4A-8330-7639D4039E51} = {D2012971-32BB-4C5F-BFC4-30A9994AB152}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {24BEC44B-92B0-43AA-9B15-163459D0C098}
Expand Down
1 change: 1 addition & 0 deletions CSharpDriver.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AWS/@EntryIndexedValue">AWS</s:String>


<s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=020B98D17DF67944A12E1EE5B68E5A81/@KeyIndexDefined">True</s:Boolean>
Expand Down
7 changes: 3 additions & 4 deletions apidocs/api/toc.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
items:
- name: MongoDB.Bson
href: MongoDB.Bson/toc.yml
- name: MongoDB.Driver
href: MongoDB.Driver/toc.yml
- href: MongoDB.Bson/toc.yml
- href: MongoDB.Driver/toc.yml
- href: MongoDB.Driver.Authentication.Aws/toc.yml
26 changes: 25 additions & 1 deletion apidocs/docfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,38 @@
"memberLayout": "separatePages",
"EnumSortOrder": "declaringOrder",
"allowCompilationErrors": false
}
},
{
"src": [
{
"files": [
"MongoDB.Driver.Authentication.Aws/*.csproj"
],
"exclude": [
"**/bin/**",
"**/obj/**"
],
"src": "../src"
}
],
"dest": "api/MongoDB.Driver.Authentication.Aws",
"includePrivateMembers": false,
"disableGitFeatures": false,
"disableDefaultFilter": false,
"noRestore": false,
"namespaceLayout": "flattened",
"memberLayout": "separatePages",
"EnumSortOrder": "declaringOrder",
"allowCompilationErrors": false
}
],
"build": {
"content": [
{
"files": [
"api/MongoDB.Bson/**.yml",
"api/MongoDB.Driver/**.yml",
"api/MongoDB.Driver.Authentication.Aws/**.yml",
"api/toc.yml",
"api/index.md"
]
Expand Down
18 changes: 16 additions & 2 deletions evergreen/evergreen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -939,8 +939,8 @@ functions:
- "mongo-csharp-driver/artifacts/nuget/MongoDB.Bson.${PACKAGE_VERSION}.snupkg"
- "mongo-csharp-driver/artifacts/nuget/MongoDB.Driver.${PACKAGE_VERSION}.nupkg"
- "mongo-csharp-driver/artifacts/nuget/MongoDB.Driver.${PACKAGE_VERSION}.snupkg"
- "mongo-csharp-driver/artifacts/nuget/mongocsharpdriver.${PACKAGE_VERSION}.nupkg"
- "mongo-csharp-driver/artifacts/nuget/mongocsharpdriver.${PACKAGE_VERSION}.snupkg"
- "mongo-csharp-driver/artifacts/nuget/MongoDB.Driver.Authentication.AWS.${PACKAGE_VERSION}.nupkg"
- "mongo-csharp-driver/artifacts/nuget/MongoDB.Driver.Authentication.AWS.${PACKAGE_VERSION}.snupkg"

cleanup:
- command: shell.exec
Expand Down Expand Up @@ -1087,6 +1087,20 @@ functions:
local_file: mongo-csharp-driver/artifacts/nuget/MongoDB.Driver.${PACKAGE_VERSION}.snupkg
remote_file: ${UPLOAD_BUCKET}/${revision}/MongoDB.Driver.${PACKAGE_VERSION}.snupkg
bucket: mciuploads
- command: s3.get
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
local_file: mongo-csharp-driver/artifacts/nuget/MongoDB.Driver.Authentication.AWS.${PACKAGE_VERSION}.nupkg
remote_file: ${UPLOAD_BUCKET}/${revision}/MongoDB.Driver.Authentication.AWS.${PACKAGE_VERSION}.nupkg
bucket: mciuploads
- command: s3.get
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
local_file: mongo-csharp-driver/artifacts/nuget/MongoDB.Driver.Authentication.AWS.${PACKAGE_VERSION}.snupkg
remote_file: ${UPLOAD_BUCKET}/${revision}/MongoDB.Driver.Authentication.AWS.${PACKAGE_VERSION}.snupkg
bucket: mciuploads

build-apidocs:
- command: shell.exec
Expand Down
4 changes: 2 additions & 2 deletions evergreen/push-packages.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ wait_until_package_is_available ()
count=0
echo "Checking package availability: ${package}:${version} at ${packages_search_url}"
while [ -z "$resp" ] && [ $count -le 40 ]; do
resp=$(curl -X GET -s "$packages_search_url?prerelease=true&take=1&q=PackageId:$package" | jq --arg jq_version "$version" '.data[0].versions[] | select(.version==$jq_version) | .version')
resp=$(curl -X GET -s "$packages_search_url?prerelease=true&take=1&q=PackageId:$package" | jq --arg jq_version "$version" '.data[0].versions[]? | select(.version==$jq_version) | .version')
if [ -z "$resp" ]; then
echo "sleeping for 15 seconds..."
sleep 15
Expand Down Expand Up @@ -55,7 +55,7 @@ if [ "$PACKAGES_SOURCE" = "https://api.nuget.org/v3/index.json" ] && [[ ! "$PACK
exit 1
fi

PACKAGES=("MongoDB.Bson" "MongoDB.Driver")
PACKAGES=("MongoDB.Bson" "MongoDB.Driver" "MongoDB.Driver.Authentication.AWS")

for package in ${PACKAGES[*]}; do
dotnet nuget verify ./artifacts/nuget/"$package"."$PACKAGE_VERSION".nupkg --certificate-fingerprint "$NUGET_SIGN_CERTIFICATE_FINGERPRINT"
Expand Down
6 changes: 6 additions & 0 deletions evergreen/run-gssapi-auth-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ else
touch ${PROJECT_DIRECTORY}/evergreen/krb5.conf.empty
export KRB5_CONFIG=${PROJECT_DIRECTORY}/evergreen/krb5.conf.empty

IFS=':' read -ra PARTS <<< "$AUTH_GSSAPI"
USER=$(printf ${PARTS[0]//%/\\x}) # unescape percent-encoded string
PWD=$(printf ${PARTS[1]//%/\\x})

kinit $USER<<<$PWD

for var in TMP TEMP NUGET_PACKAGES NUGET_HTTP_CACHE_PATH APPDATA; do
export $var=/data/tmp;
done
Expand Down
33 changes: 33 additions & 0 deletions src/MongoDB.Driver.Authentication.AWS/AWSCredentials.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* Copyright 2010-present MongoDB Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using MongoDB.Driver.Core.Misc;

namespace MongoDB.Driver.Authentication.AWS
{
internal sealed class AWSCredentials
{
public AWSCredentials(string accessKeyId, string secretAccessKey, string sessionToken)
{
AccessKeyId = Ensure.IsNotNull(accessKeyId, nameof(accessKeyId));
SecretAccessKey = Ensure.IsNotNull(secretAccessKey, nameof(secretAccessKey));
SessionToken = sessionToken; // can be null
}

public string AccessKeyId { get; }
public string SecretAccessKey { get; }
public string SessionToken { get; }
}
}
42 changes: 42 additions & 0 deletions src/MongoDB.Driver.Authentication.AWS/AWSKmsProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* Copyright 2010-present MongoDB Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System.Threading;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver.Authentication.AWS.CredentialsSources;
using MongoDB.Driver.Encryption;

namespace MongoDB.Driver.Authentication.AWS
{
internal sealed class AWSKmsProvider : IKmsProvider
{
public const string ProviderName = "aws";

public static readonly AWSKmsProvider Instance = new AWSKmsProvider();

public async Task<BsonDocument> GetKmsCredentialsAsync(CancellationToken cancellationToken)
{
var credentials = await AWSFallbackCredentialsSource.Instance.GetCredentialsAsync(cancellationToken).ConfigureAwait(false);

return new()
{
{ "accessKeyId", credentials.AccessKeyId },
{ "secretAccessKey", credentials.SecretAccessKey },
{ "sessionToken", credentials.SessionToken, credentials.SessionToken != null }
};
}
}
}
149 changes: 149 additions & 0 deletions src/MongoDB.Driver.Authentication.AWS/AWSSaslMechanism.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/* Copyright 2010-present MongoDB Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System;
using System.Collections.Generic;
using MongoDB.Bson;
using MongoDB.Driver.Authentication.AWS.CredentialsSources;
using MongoDB.Driver.Authentication.AWS.SaslSteps;
using MongoDB.Driver.Core.Connections;
using MongoDB.Driver.Core.Misc;

namespace MongoDB.Driver.Authentication.AWS
{
internal sealed class AWSSaslMechanism : ISaslMechanism
{
public static AWSSaslMechanism Create(SaslContext context)
=> Create(context, RandomByteGenerator.Instance, SystemClock.Instance);

public static AWSSaslMechanism Create(SaslContext context, IRandomByteGenerator randomByteGenerator, IClock clock)
{
Ensure.IsNotNull(context, nameof(context));
Ensure.IsNotNull(randomByteGenerator, nameof(randomByteGenerator));
Ensure.IsNotNull(clock, nameof(clock));
if (context.Mechanism != MechanismName)
{
throw new InvalidOperationException($"Unexpected authentication mechanism: {context.Mechanism}");
}

if (context.Identity.Source != "$external")
{
throw new ArgumentException("MONGODB-AWS authentication may only use the $external source.", nameof(context.Identity.Source));
}

string password = null;
if (context.IdentityEvidence is PasswordEvidence passwordEvidence)
{
password = passwordEvidence.ToInsecureString();
}

var awsCredentials = CreateAwsCredentialsFromMongoCredentials(context.Identity.Username, password, context.MechanismProperties);
IAWSCredentialsSource credentialsSource = awsCredentials != null ? new AWSInstanceCredentialsSource(awsCredentials) : AWSFallbackCredentialsSource.Instance;

return new AWSSaslMechanism(credentialsSource, randomByteGenerator, clock);
}

private static AWSCredentials CreateAwsCredentialsFromMongoCredentials(string username, string password, IEnumerable<KeyValuePair<string, object>> properties)
{
ValidateMechanismProperties(properties);
var sessionToken = ExtractSessionTokenFromMechanismProperties(properties);

if (username == null && password == null && sessionToken == null)
{
return null;
}
if (password != null && username == null)
{
throw new InvalidOperationException("When using MONGODB-AWS authentication if a password is provided via settings then a username must be provided also.");
}
if (username != null && password == null)
{
throw new InvalidOperationException("When using MONGODB-AWS authentication if a username is provided via settings then a password must be provided also.");
}
if (sessionToken != null && (username == null || password == null))
{
throw new InvalidOperationException("When using MONGODB-AWS authentication if a session token is provided via settings then a username and password must be provided also.");
}

return new AWSCredentials(accessKeyId: username, secretAccessKey: password, sessionToken);
}

private static string ExtractSessionTokenFromMechanismProperties(IEnumerable<KeyValuePair<string, object>> properties)
{
if (properties != null)
{
foreach (var pair in properties)
{
if (pair.Key.ToUpperInvariant() == "AWS_SESSION_TOKEN")
{
return (string)pair.Value;
}
}
}

return null;
}

private static void ValidateMechanismProperties(IEnumerable<KeyValuePair<string, object>> properties)
{
if (properties != null)
{
foreach (var pair in properties)
{
if (pair.Key.ToUpperInvariant() != "AWS_SESSION_TOKEN")
{
throw new ArgumentException($"Unknown AWS property '{pair.Key}'.", nameof(properties));
}
}
}
}

public const int ClientNonceLength = 32;
public const string MechanismName = "MONGODB-AWS";

private readonly IClock _clock;
private readonly IAWSCredentialsSource _credentialsSource;
private readonly IRandomByteGenerator _randomByteGenerator;

private AWSSaslMechanism(IAWSCredentialsSource credentialsSource, IRandomByteGenerator randomByteGenerator, IClock clock)
{
_credentialsSource = credentialsSource;
_randomByteGenerator = randomByteGenerator;
_clock = clock;
}

public string DatabaseName => "$external";

public string Name => MechanismName;

public ISaslStep CreateSpeculativeAuthenticationStep() => null;

public BsonDocument CustomizeSaslStartCommand(BsonDocument startCommand) => startCommand;

public ISaslStep Initialize(SaslConversation conversation, ConnectionDescription description)
=> new AWSFirstSaslStep(_credentialsSource, _randomByteGenerator, _clock);

public void OnReAuthenticationRequired()
{
}

public bool TryHandleAuthenticationException(MongoException exception, ISaslStep step, SaslConversation conversation, ConnectionDescription description, out ISaslStep nextStep)
{
_credentialsSource.ResetCache();
nextStep = null;
return false;
}
}
}
Loading

0 comments on commit 1e97f44

Please sign in to comment.