-
Notifications
You must be signed in to change notification settings - Fork 0
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
CSHARP-2838: MONGODB-AWS Support #22
Conversation
public static string GetRegion(string host) | ||
{ | ||
if (host == "sts.amazonaws.com") | ||
{ | ||
return "us-east-1"; | ||
} | ||
|
||
var split = host.Split('.'); | ||
if (split.Count() > 1) | ||
{ | ||
return split[1]; | ||
} | ||
|
||
return "us-east-1"; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From specification (link):
Region is not a header, but simply part of the authorization header. Region by default is ‘us-east-1’ since this is the implicit region for ‘sts.amazonaws.com’. Drivers will need to derive the region to use from the endpoint. The region is the second piece of a FQDN name. While all official AWS STS endpoints start with “sts.”, there are non-AWS hosted endpoints and test endpoints that will not follow this rule.
Host | Region
sts.amazonaws.com | us-east-1
first.second | second
first | us-east-1
} | ||
|
||
/// <summary> | ||
/// Signs the request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/MongoDB.Driver.Core/Core/Authentication/AwsSignatureVersion4.cs
Outdated
Show resolved
Hide resolved
/// </summary> | ||
public class MongoAWSAuthenticator : SaslAuthenticator | ||
{ | ||
private static readonly Lazy<HttpClient> _httpClientInstance = new Lazy<HttpClient>(() => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is the usage of single instance HttpClient
we discussed in slack.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's move the whole httpclient details into a helper class. Consider how it can be:
DmitryLukyanov@1b03be9#diff-ffb35b98eecd05d3c7b6133327bf13b3R375-R479
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job! Thanks for suggested code!
Done.
private const string awsContainerCredentialsRelativeUriKey = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"; | ||
private const string awsSessionTokenMechanismPropertyKey = "AWS_SESSION_TOKEN"; | ||
private const string ecsBaseUri = "http://169.254.170.2"; | ||
private const string ec2BaseUri = "http://169.254.169.254"; | ||
private const int randomLength = 32; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Constant values taken from spec: link
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly we use the upper camelcase for constants names
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, the constant name does tell for what reason this value will be used. I think this value may be moved into the method private byte[] GenerateRandomBytes()
similar how it's done in https://github.com/mongodb/mongo-csharp-driver/blob/master/src/MongoDB.Driver.Core/Core/Authentication/ScramShaAuthenticator.cs#L160
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's also used in server nonce validation here: https://github.com/MikalaiMazurenka/mongo-csharp-driver/pull/22/files#diff-ffb35b98eecd05d3c7b6133327bf13b3R436
So to avoid using magic numbers in code, I leave it as is. Regarding case: I see different formats throughout the solution. E.g. pascal case: link, winapi style: link (not sure how it's called), camel case: link. Could you please link some guidelines on this?
For now I've made the name more meaningful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only guide is to use the most latest way, and as far as I know, in the latest code, we use the upper camelcase format. It's a good point to mention it somewhere, I think we have some document which can be used for that target. @vincentkam do you remember the document which we wanted to use for that target (I remember we discussed similar case around a year ago)?
if (username == null) | ||
{ | ||
if (evidence != null && evidence is PasswordEvidence) | ||
{ | ||
throw new ArgumentException("A MONGODB-AWS must have access key id."); | ||
} | ||
|
||
return new MongoCredential( | ||
mechanism, | ||
new MongoExternalAwsIdentity(), | ||
evidence); | ||
} | ||
if (evidence == null || evidence is ExternalEvidence) | ||
{ | ||
throw new ArgumentException("A MONGODB-AWS must have secret access key."); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding validation please refer to spec (link):
regardless of how Drivers obtain credentials the only valid combination of credentials is an access key ID and a secret access key or an access key ID, a secret access key and a session token.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This message about needing secret access key appears often enough that I wonder if we should have a constant that defines it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those are different:
"A MONGODB-AWS must have access key id."
"A MONGODB-AWS must have secret access key."
public string SessionToken; | ||
} | ||
|
||
private class MongoAWSMechanism : ISaslMechanism |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The conversation process is described in specification: link
private static readonly Lazy<HttpClient> _httpClientInstance = new Lazy<HttpClient>(() => | ||
{ | ||
var httpClient = new HttpClient(); | ||
httpClient.Timeout = TimeSpan.FromSeconds(10); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From specification (link):
Drivers SHOULD enforce a 10 second read timeout while waiting for incoming content from both the ECS and EC2 endpoints.
|
||
namespace MongoDB.Driver.Core.Misc | ||
{ | ||
internal class DefaultRandomByteGenerator : IRandomByteGenerator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per conversation with Mark Benvenuto, for AWS auth I switched to random byte generation (with 256 possible values) instead of existing random character generation (with around 100).
<ItemGroup> | ||
<Reference Include="System.Net.Http" /> | ||
</ItemGroup> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an inevitable requirement for using HttpClient
(link).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we be using a PackageReference
, targeting https://www.nuget.org/packages/System.Net.Http/ instead of a Reference
? I feel like Reference
only works if the user already has that dll/package somewhere in their path but I'm not 100% on that...
34aa986
to
9f08c4f
Compare
9f08c4f
to
2614e75
Compare
PASS=$(urlencode "${iam_auth_ecs_secret_access_key}") | ||
MONGODB_URI="mongodb://$USER:$PASS@localhost" | ||
EOF | ||
PROJECT_DIRECTORY=${PROJECT_DIRECTORY} evergreen/run-mongodb-aws-test.sh |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to be silent? This is silent in Java because the URI is echoed as part of the command that runs the test here. And the URI will be echoed again when the test run. Thus any test that passed secrets as the value to -Dorg.mongodb.test.uri=
was silenced. This doesn't appear to be the case from looking at run-mongodb-aws-test.sh
but I haven't looked at the output in evergreen. If evergreen doesn't echo the URI I'd consider putting this in a separate command. Looking at the POC for Node or Python might be helpful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see Python splitting this test into two parts: one which must be silenced and one not (link)
I will run a patch build to check this, but I'm mostly sure cat
command should be silenced.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed silencing can be removed there.
Done.
SESSION_TOKEN=$(urlencode $SESSION_TOKEN) | ||
MONGODB_URI="mongodb://$USER:$PASS@localhost" | ||
EOF | ||
PROJECT_DIRECTORY=${PROJECT_DIRECTORY} DRIVERS_TOOLS=${DRIVERS_TOOLS} evergreen/run-mongodb-aws-test.sh |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per my comment above, I don't think this needs to be silent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ready for review!
src/MongoDB.Driver.Core/Core/Authentication/MongoAWSAuthenticator.cs
Outdated
Show resolved
Hide resolved
tests/MongoDB.Driver.Core.Tests/Core/Authentication/AvsSignatureVersion4Tests.cs
Outdated
Show resolved
Hide resolved
tests/MongoDB.Driver.Core.Tests/Core/Authentication/AvsSignatureVersion4Tests.cs
Outdated
Show resolved
Hide resolved
actual.Should().Be(expected); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the unit test in Java, I used the values in AWS's documentation as opposed to the values returned from the mock servers. This was to ensure that my code conformed to AWS's signature v4 process rather than the mocks rendition of it, should they differ. It might be worthwhile to the same as a cautionary measure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ready for review!
PASS=$(urlencode "${iam_auth_ecs_secret_access_key}") | ||
MONGODB_URI="mongodb://$USER:$PASS@localhost" | ||
EOF | ||
PROJECT_DIRECTORY=${PROJECT_DIRECTORY} evergreen/run-mongodb-aws-test.sh |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see Python splitting this test into two parts: one which must be silenced and one not (link)
I will run a patch build to check this, but I'm mostly sure cat
command should be silenced.
src/MongoDB.Driver.Core/Core/Authentication/MongoAWSAuthenticator.cs
Outdated
Show resolved
Hide resolved
tests/MongoDB.Driver.Core.Tests/Core/Authentication/AvsSignatureVersion4Tests.cs
Outdated
Show resolved
Hide resolved
tests/MongoDB.Driver.Core.Tests/Core/Authentication/AvsSignatureVersion4Tests.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Partial review, mostly for the MongoAWSAuthenticator. I will continue reviewing.
Commit with suggestions: DmitryLukyanov@1b03be9
} | ||
} | ||
|
||
private static BsonDocument ToDocument(byte[] bytes) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that this is the best way to do this work. However maybe I missed something, I need to understand what data are presented in bytes to be sure. I think we need to have a test in Core with mocked data similar to: https://github.com/mongodb/mongo-csharp-driver/blob/master/tests/MongoDB.Driver.Core.Tests/Core/Authentication/ScramSha256AuthenticatorTests.cs#L33. This will allow us to be able to run these code paths without any additional configuration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added the high level mocked test.
{ | ||
BsonBinaryWriterSettings settings = new BsonBinaryWriterSettings() | ||
{ | ||
#pragma warning disable 618 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess you need to change this code on the following:
if (BsonDefaults.GuidRepresentationMode == GuidRepresentationMode.V2)
{
clientSettings.GuidRepresentation = GuidRepresentation.Unspecified;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Please recheck that it's what you expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better to leave the suggested code since the reason why it's left without covering via methods or some other possible refactoring, that it will be removed in the version 3.0
throw new MongoAuthenticationException(conversation.ConnectionId, message: "Server sent an invalid nonce."); | ||
} | ||
|
||
var tuple = AwsSignatureVersion4.SignRequest( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that tuple is a good enough variable name here. Also, please note that previously we decided to avoid using tuples in the driver code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored with out
parameter.
Done.
document.Add("t", _sessionToken); | ||
} | ||
|
||
var clientSecondMessageBytes = ToBytes(document); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NOTE: previously we used encoding.GetBytes for similar case https://github.com/mongodb/mongo-csharp-driver/blob/master/src/MongoDB.Driver.Core/Core/Authentication/ScramShaAuthenticator.cs#L253 Not sure that document is better to use here. I will come back to it in my next round review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My point that previously we didn't use BsonDocument for that target. Instead we used Utf8Encodings.Strict
. See here: https://github.com/mongodb/mongo-csharp-driver/blob/master/src/MongoDB.Driver.Core/Core/Authentication/ScramShaAuthenticator.cs#L253
But I don't think that there is anything wrong in the current implementation. I will rely on other reviewers' opinion here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My apologies if I'm misunderstanding your comment Dima, but are you saying that we shouldn't use a BsonDocument here?
If so, I think the key difference between SCRAM-SHA and MONGODB-AWS is that the payload
field in SCRAM-SHA uses the "SASL map" format (https://github.com/mongodb/mongo-csharp-driver/blob/master/src/MongoDB.Driver.Core/Core/Authentication/ScramShaAuthenticator.cs#L210), which is essentially a fancy comma separated value string, and the payload field in MONGODB-AWS uses BSON (https://github.com/mongodb/specifications/blob/master/source/auth/auth.rst#mongodb-aws). Therefore, I believe the approach of using a BsonDocument here is correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can't we just use:
var clientSecondMessageBytes = document.ToBson();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vincentkam , I agree with you. Originally I thought that I saw comma separated data here, but probably i was wrong
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
} | ||
} | ||
|
||
private class ClientLast : ISaslStep |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it looks like this class does nothing (unless I didn't miss something)? Is it expected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It completes the conversation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as far as I can see there is no real logic here, instead of this, we can just return CompletedStep
. But maybe it makes sense to leave it to be consistent with the other implementations
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't mind either way: I defer to Mikalai here :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ok with the current version
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I leave it as is unless there are other concerns.
var serverNonce = serverFirstMessageDoc["s"].AsByteArray; | ||
var host = serverFirstMessageDoc["h"].AsString; | ||
|
||
if (serverNonce.Length != randomLength * 2 || !serverNonce.Take(randomLength).SequenceEqual(_nonce)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From spec (link):
Drivers MUST validate that the server nonce is exactly 64 bytes and the first 32 bytes are the same as the client nonce.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, currently, my main suggestion is adding a new mocked test in Core to be able to debug this functionality without additional configuration
evergreen/run-mongodb-aws-test.sh
Outdated
shopt -s expand_aliases # needed for `urlencode` alias | ||
[ -s "${PROJECT_DIRECTORY}/prepare_mongodb_aws.sh" ] && source "${PROJECT_DIRECTORY}/prepare_mongodb_aws.sh" | ||
|
||
MONGODB_URI=${MONGODB_URI:-"mongodb://localhost"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have empty MONGODB_URI at all? And even if so, should we update the up level script to fill it? As I understand, we don't fill connection string on this level usually
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we don't want to miss a bug in the our configuration when we missed filling of MONGODB_URI, maybe it's better to trigger exception in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! With addition of code here: line MONGODB_URI should always be set.
Done.
src/MongoDB.Driver.Core/Core/Authentication/AwsSignatureVersion4.cs
Outdated
Show resolved
Hide resolved
/// <returns>The region.</returns> | ||
public static string GetRegion(string host) | ||
{ | ||
if (host == "sts.amazonaws.com") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider:
public static string GetRegion(string host)
{
string[] splitted;
if (host == "sts.amazonaws.com" || (splitted = host.Split('.')).Length <= 1)
{
return "us-east-1";
}
return splitted[1];
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say that current code is a bit more readable (for me at least), but if you think suggested way is better, I'll update it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe,
I read this code as: if a host is sts.amazonaws.com
or host has one or fewer parts => use us-east-1
, otherwise use the first. This is exactly what I wrote. But I will rely on your and other reviewers opinion here
byte[] salt, | ||
string host) | ||
{ | ||
var body = "Action=GetCallerIdentity&Version=2011-06-15"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you clarify this line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From specification (see the name/value table, closest anchor: link):
Body | Action=GetCallerIdentity&Version=2011-06-15\n
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I saw this line in the doc. This is weird, but at the same time this is what the doc requires
tests/MongoDB.Driver.Tests/Communication/Security/AwsAuthenticationTests.cs
Outdated
Show resolved
Hide resolved
var connectionString = Environment.GetEnvironmentVariable(__environmentVariableName); | ||
connectionString.Should().NotBeNull(); | ||
|
||
using (var client = CreateDisposableClient(connectionString)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider simplifying:
[Fact]
public void Aws_authentication_should_behave_as_expected()
{
using (var client = DriverTestConfiguration.CreateDisposableClient())
{
// test that a command that doesn't require auth completes normally
var adminDatabase = client.GetDatabase("admin");
var isMasterCommand = new BsonDocument("ismaster", 1);
var isMasterResult = adminDatabase.RunCommand<BsonDocument>(isMasterCommand);
// test that a command that does require auth completes normally
var database = client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName);
var collection = database.GetCollection<BsonDocument>(DriverTestConfiguration.CollectionNamespace.CollectionName);
var count = collection.CountDocuments(FilterDefinition<BsonDocument>.Empty);
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's not important, but this test will be called not only for aws tests. I think it's not problem to run this small tests in any case, but it may confuse if we check the EG log and will see aws test for the environment when it wasn't configured.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NOTE: lately we use the following test naming notation: Aws_authentication_should_behave_as_expected
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rstam regarding Dmitry's comment https://github.com/MikalaiMazurenka/mongo-csharp-driver/pull/22/files#r393350233
I think it's not crucial for this test to be run only on AWS task, but if yes, I'm thinking about checking an environment variable, e.g. "AWS_TESTS_ENABLED".
Could you please suggest which way it should be done here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very quick comments + weighing in on the SecureString discussion: more to come!
evergreen/run-mongodb-aws-test.sh
Outdated
# show test output | ||
set -x | ||
|
||
powershell.exe .\\build.ps1 -target TestAwsAuthentication |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: add missing newline at the end of the file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I didn't notice that!
Done.
IRandomByteGenerator randomByteGenerator) | ||
{ | ||
var relativeUri = Environment.GetEnvironmentVariable(awsContainerCredentialsRelativeUriKey); | ||
var methodsList = new Func<AwsCredentials>[] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, perhaps another name for methodsList
? credentialsGetters
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This use of a list funcs is quite neat IMHO! :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I've taken name from Dmitry's suggestions (link)
() => AcquireMechanismDataFromEc2Response(ec2BaseUri) | ||
}; | ||
|
||
foreach (var method in methodsList) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Side comment:
I was curious as to whether this could be expressed via LINQ and came up with this, which I think should work due to lazy linq evaluation:
var credentials = methodsList
.Select(GetCredentials => GetCredentials())
.Where(creds => creds != null)
.FirstOrDefault(creds => return new MongoAWSMechanism(creds.Credentials, creds.SessionToken, randomByteGenerator));
if (credentials == null)
{
throw new ArgumentException("A MONGODB-AWS must have access key ID.");
}
return credentials;
But the foreach loop is totally fine, one line shorter, and probably easier to read :) Just thought I'd share a tiny bit of fun I had.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I feel the foreach would be a bit more readable.
src/MongoDB.Driver.Core/Core/Authentication/MongoAWSAuthenticator.cs
Outdated
Show resolved
Hide resolved
sessionToken = (string)pair.Value; | ||
break; | ||
default: | ||
throw new ArgumentException($"Unknown AWS property '{pair.Key}'.", "properties"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nameof(properties)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/MongoDB.Driver.Core/Core/Authentication/MongoAWSAuthenticator.cs
Outdated
Show resolved
Hide resolved
/// <summary> | ||
/// The AWS signature version 4. | ||
/// </summary> | ||
public class AwsSignatureVersion4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this class really be public and even a separate non nested class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it looks like the only logic which happens here is creating signature request
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we could make this class internal + static?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point!
Done.
/// </summary> | ||
/// <param name="requestHeaders">The request headers.</param> | ||
/// <returns>The canonical headers.</returns> | ||
public static string GetCanonicalHeaders(SortedDictionary<string, string> requestHeaders) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Idk, but this name makes me feel that some real logic happens inside, but it can be called just ToString
or so
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't mind this name as a getter can have minimal logic. Also I feel like it might be a bit odd for a static class to have a ToString() method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From my point of view this method improves readability, so I would like to leave it as is, unless there are other concerns.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a static class to have a ToString() method?
My point was that I don't see how changing a form from dictionary to a string can be named getting canonical headers? It sounds like just technical operation. In other words what was done in the scope of this method to call it getting canonical headers? Maybe I'm missing something. It's a minor point and I can rely on other reviewers here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked this link https://raw.githubusercontent.com/mongodb/specifications/master/source/auth/includes/calculating_a_signature.png and I see where this came from.
However, it's still confusing me a bit, since the dictionary before this method also was in canonical form, the only change here that we change a format of this data
/// </summary> | ||
/// <param name="requestHeaders">The request headers.</param> | ||
/// <returns>The signed headers.</returns> | ||
public static string GetSignedHeaders(SortedDictionary<string, string> requestHeaders) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
similar to the above, does this name tell what happens inside this method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, please refer to specification illustration: link
It's SignedHeaders
on the picture and the method which acquires them should be called GetSignedHeaders
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I wrote above, I will rely on the other reviewers' opinion here
src/MongoDB.Driver.Core/Core/Authentication/AwsSignatureVersion4.cs
Outdated
Show resolved
Hide resolved
/// <param name="salt">The salt.</param> | ||
/// <param name="host">The host.</param> | ||
/// <returns>The signed request.</returns> | ||
public static Tuple<string, string> SignRequest( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CreateAuthenticateRequest?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to leave it as is. Because this method does a lot of signing and returns signed request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on this name, I would say that this method signs some request which was passed to him which is not true.
CreateSignedRequest?
I will rely on your and other reviewers' opinion here
src/MongoDB.Driver.Core/Core/Authentication/AwsSignatureVersion4.cs
Outdated
Show resolved
Hide resolved
private const string awsContainerCredentialsRelativeUriKey = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"; | ||
private const string awsSessionTokenMechanismPropertyKey = "AWS_SESSION_TOKEN"; | ||
private const string ecsBaseUri = "http://169.254.170.2"; | ||
private const string ec2BaseUri = "http://169.254.169.254"; | ||
private const int randomLength = 32; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly we use the upper camelcase for constants names
private const string awsContainerCredentialsRelativeUriKey = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"; | ||
private const string awsSessionTokenMechanismPropertyKey = "AWS_SESSION_TOKEN"; | ||
private const string ecsBaseUri = "http://169.254.170.2"; | ||
private const string ec2BaseUri = "http://169.254.169.254"; | ||
private const int randomLength = 32; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, the constant name does tell for what reason this value will be used. I think this value may be moved into the method private byte[] GenerateRandomBytes()
similar how it's done in https://github.com/mongodb/mongo-csharp-driver/blob/master/src/MongoDB.Driver.Core/Core/Authentication/ScramShaAuthenticator.cs#L160
src/MongoDB.Driver.Core/Core/Authentication/AwsSignatureVersion4.cs
Outdated
Show resolved
Hide resolved
/// </summary> | ||
/// <param name="requestHeaders">The request headers.</param> | ||
/// <returns>The canonical headers.</returns> | ||
public static string GetCanonicalHeaders(SortedDictionary<string, string> requestHeaders) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't mind this name as a getter can have minimal logic. Also I feel like it might be a bit odd for a static class to have a ToString() method?
src/MongoDB.Driver.Core/Core/Authentication/AwsSignatureVersion4.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ready for review!
evergreen/run-mongodb-aws-test.sh
Outdated
# show test output | ||
set -x | ||
|
||
powershell.exe .\\build.ps1 -target TestAwsAuthentication |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I didn't notice that!
Done.
src/MongoDB.Driver.Core/Core/Authentication/AwsSignatureVersion4.cs
Outdated
Show resolved
Hide resolved
private const string awsContainerCredentialsRelativeUriKey = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"; | ||
private const string awsSessionTokenMechanismPropertyKey = "AWS_SESSION_TOKEN"; | ||
private const string ecsBaseUri = "http://169.254.170.2"; | ||
private const string ec2BaseUri = "http://169.254.169.254"; | ||
private const int randomLength = 32; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's also used in server nonce validation here: https://github.com/MikalaiMazurenka/mongo-csharp-driver/pull/22/files#diff-ffb35b98eecd05d3c7b6133327bf13b3R436
So to avoid using magic numbers in code, I leave it as is. Regarding case: I see different formats throughout the solution. E.g. pascal case: link, winapi style: link (not sure how it's called), camel case: link. Could you please link some guidelines on this?
For now I've made the name more meaningful.
sessionToken = (string)pair.Value; | ||
break; | ||
default: | ||
throw new ArgumentException($"Unknown AWS property '{pair.Key}'.", "properties"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
{ | ||
BsonBinaryWriterSettings settings = new BsonBinaryWriterSettings() | ||
{ | ||
#pragma warning disable 618 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Please recheck that it's what you expected.
src/MongoDB.Driver.Core/Core/Authentication/MongoAWSAuthenticator.cs
Outdated
Show resolved
Hide resolved
tests/MongoDB.Driver.Tests/Communication/Security/AwsAuthenticationTests.cs
Outdated
Show resolved
Hide resolved
return roleRequest; | ||
} | ||
|
||
private static HttpRequestMessage CraeteTokenRequest(Uri baseUri) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CreateTokenRequest?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good notice.
Done.
private const string awsContainerCredentialsRelativeUriKey = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"; | ||
private const string awsSessionTokenMechanismPropertyKey = "AWS_SESSION_TOKEN"; | ||
private const string ecsBaseUri = "http://169.254.170.2"; | ||
private const string ec2BaseUri = "http://169.254.169.254"; | ||
private const int randomLength = 32; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only guide is to use the most latest way, and as far as I know, in the latest code, we use the upper camelcase format. It's a good point to mention it somewhere, I think we have some document which can be used for that target. @vincentkam do you remember the document which we wanted to use for that target (I remember we discussed similar case around a year ago)?
var username = Environment.GetEnvironmentVariable("AWS_ACCESS_KEY_ID"); | ||
var password = Environment.GetEnvironmentVariable("AWS_SECRET_ACCESS_KEY"); | ||
var sessionToken = Environment.GetEnvironmentVariable("AWS_SESSION_TOKEN"); | ||
ValidateCredentials(username, password, sessionToken); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this method should be responsible only for the creation of Aws credentials. The whole validation can be moved in one place where we iterate the created credentials.
It will also allow us to avoid code duplication in the ValidateCredentials methods. The only reason why we have two methods now with fully the same idea is because we have passwords in two forms (string and SecurePassword), after this method, we will have only one variant of the password.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
return sessionToken; | ||
} | ||
|
||
private static SecureString ToSecureString(string str) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that we need to have this logic here. We already have the same method in UsernamePasswordCredential. I think we can create a helper for that target or move this method to BsonUtils. Thoughts?
I will be ok with any solution here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you can see UsernamePasswordCredential.ConvertPasswordToSecureString
is private: link
And I wouldn't like to touch existing code, unless there's a crucial necessity. Moreover, my updated ToSecureString
wouldn't throw on null
input. So I would like to leave this method, unless you insist.
|
||
var response = AwsHttpClientHelper.GetECSResponse(relativeUri).GetAwaiter().GetResult(); | ||
var parsedReponse = BsonDocument.Parse(response); | ||
var username = parsedReponse.GetValue("AccessKeyId")?.AsString; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this code will trigger an exception in case if AccessKeyId
is not presented in the document, you need to specify the default value:
var username = parsedReponse.GetValue("AccessKeyId", null)?.AsString;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
||
private static AwsCredentials CreateAwsCredentialsFromEcsResponse(string relativeUri) | ||
{ | ||
if (relativeUri == null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure why you want to specify the relative path for that case in the top level, but the value latest/meta-data/iam/security-credentials/
for ec2 at the lowest level. I would say we need to specify both of them at the same level
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/MongoDB.Driver.Core/Core/Authentication/MongoAWSAuthenticator.cs
Outdated
Show resolved
Hide resolved
HttpResponseMessage response; | ||
try | ||
{ | ||
response = await __httpClientInstance.Value.SendAsync(request).ConfigureAwait(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
copied from outdated: #22 (comment)
} | ||
} | ||
|
||
private class ClientLast : ISaslStep |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as far as I can see there is no real logic here, instead of this, we can just return CompletedStep
. But maybe it makes sense to leave it to be consistent with the other implementations
throw new MongoAuthenticationException(conversation.ConnectionId, message: "Server sent an invalid nonce."); | ||
} | ||
|
||
AwsSignatureVersion4.SignRequest( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that SignRequest
method name really tells about what happens inside. As I understand, we just create an authenticateHeader
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This name is perfectly fine.
_sessionToken, | ||
serverNonce, | ||
host, | ||
out var authHeader, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as far as I know, mostly we don't use the short names in the driver code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
document.Add("t", _sessionToken); | ||
} | ||
|
||
var clientSecondMessageBytes = ToBytes(document); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My point that previously we didn't use BsonDocument for that target. Instead we used Utf8Encodings.Strict
. See here: https://github.com/mongodb/mongo-csharp-driver/blob/master/src/MongoDB.Driver.Core/Core/Authentication/ScramShaAuthenticator.cs#L253
But I don't think that there is anything wrong in the current implementation. I will rely on other reviewers' opinion here
EnsureNullOrExternalSource(mechanism, source); | ||
if (username == null) | ||
{ | ||
if (evidence != null && evidence is PasswordEvidence) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: it's not really necessary to check evidence != null
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{ | ||
if (evidence != null && evidence is PasswordEvidence) | ||
{ | ||
throw new ArgumentException("A MONGODB-AWS must have access key id."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about this exception. I will rely on @rstam opinion here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto above: should this "A MONGODB-AWS credential must have an access key ID."? Perhaps we should use a constant?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ready for review!
return roleRequest; | ||
} | ||
|
||
private static HttpRequestMessage CraeteTokenRequest(Uri baseUri) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good notice.
Done.
var username = Environment.GetEnvironmentVariable("AWS_ACCESS_KEY_ID"); | ||
var password = Environment.GetEnvironmentVariable("AWS_SECRET_ACCESS_KEY"); | ||
var sessionToken = Environment.GetEnvironmentVariable("AWS_SESSION_TOKEN"); | ||
ValidateCredentials(username, password, sessionToken); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
return sessionToken; | ||
} | ||
|
||
private static SecureString ToSecureString(string str) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you can see UsernamePasswordCredential.ConvertPasswordToSecureString
is private: link
And I wouldn't like to touch existing code, unless there's a crucial necessity. Moreover, my updated ToSecureString
wouldn't throw on null
input. So I would like to leave this method, unless you insist.
@@ -99,6 +99,7 @@ | |||
|
|||
<ItemGroup Condition="'$(TargetFramework)' == 'net452'"> | |||
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" /> | |||
<Reference Include="System.Net.Http" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, now I'm slightly confused: According to this, HttpClient
is available for the following platforms
.NET Framework
4.8 4.7.2 4.7.1 4.7 4.6.2 4.6.1 4.6 4.5.2 4.5.1 4.5
.NET Standard
2.1 2.0 1.6 1.4 1.3 1.2 1.1
It seems weird for it to be available .Net Standard 1.4 and 1.6 but not 1.5, so this feels like a documentation error to me to be honest. I've confirmed via local testing that the current Driver.Core project builds when targeting .Net Standard 1.5 and 2.0, and fails to build when targeting .NET Framework 4.5.2 when I remove line 102 <Reference Include="System.Net.Http" />
My (other) question is: should we err on the side of safety and use a NuGet package reference instead of a regular reference? i.e.
<PackageReference Include="System.Net.Http" Version="4.3.4" />
(i.e. including https://www.nuget.org/packages/System.Net.Http/).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll defer to you all on this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it may be safer to use the PackageReference. I believe by using Reference
, the application will need to have the dll in its path somewhere: for our developer machines and the evergren machines, that wouldn't be a problem because we have the DLL from later versions of .NET. By using a PackageReference, I think that it'd allow a machine to not have the DLL on its path at all, as it'll simply download the package from Nuget as needed. What do you think Mikalai?
evergreen/run-mongodb-aws-test.sh
Outdated
@@ -0,0 +1,35 @@ | |||
#!/bin/bash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A random side nit for this and other bash scripts:
My loose understanding is that using
#!/usr/bin/env bash
increases the portability of the script (as bash may not always be in /bin/bash). Probably not an issue in this case since our scripts were tailored for Cygwin, where bash does indeed exist at /bin/bash
all the time (AFAIK). So again, as this is merely a nit, feel free to ignore :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for suggestion!
Done.
|
||
var subject = new MongoAWSAuthenticator(credential, null, randomByteGeneratorMock.Object, dateTimeProviderMock.Object); | ||
|
||
var connection = new MockConnection(serverId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah interesting hm... perhaps we should mock ServerVersion as well.
|
||
var actualRequestId0 = sentMessages[0]["requestId"].AsInt32; | ||
var actualRequestId1 = sentMessages[1]["requestId"].AsInt32; | ||
actualRequestId0.Should().BeInRange(expectedRequestId, expectedRequestId + 10); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm I wonder if this pattern was meant for "live" connections instead of "mocked" connections. We can keep it, or we can change it to use +1: I defer to Mikalai.
private static string GetExpectedSaslContinueMessage(int requestId, BsonDocument clientMessage) | ||
{ | ||
return "{" + | ||
"opcode: \"query\", " + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just an FYI: An alternative style to multiline bsondocuments, but feel free to simply resolve this after reading this, I just wanted to share another option :) :
Line 251 in 8925eae
@"{opcode: ""query""," + |
|
||
timestamp = dateTime.ToString("yyyyMMddTHHmmssZ"); | ||
|
||
string datestamp = dateTime.ToString("yyyyMMdd"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: use var
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
using MongoDB.Bson.Serialization; | ||
using MongoDB.Bson.Serialization.Serializers; | ||
using MongoDB.Driver.Core.Connections; | ||
using MongoDB.Driver.Core.Misc; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: sort and remove unused namespaces
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
UsernamePasswordCredential credential, | ||
IEnumerable<KeyValuePair<string, string>> properties, | ||
IRandomByteGenerator randomByteGenerator, | ||
IClock dateTimeProvider) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: name parameter "clock"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
string username, | ||
IEnumerable<KeyValuePair<string, string>> properties, | ||
IRandomByteGenerator randomByteGenerator, | ||
IClock dateTimeProvider) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: name parameter "clock"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
UsernamePasswordCredential credential, | ||
IEnumerable<KeyValuePair<string, string>> properties, | ||
IRandomByteGenerator randomByteGenerator, | ||
IClock dateTimeProvider) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: name parameter "clock"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
"\"mechanism\" : \"MONGODB-AWS\", " + | ||
$"\"payload\" : new BinData(0, \"{ToBase64(ToBytes(clientMessage))}\") " + | ||
"}" + | ||
"}"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above regarding formatting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
return Convert.ToBase64String(bytes); | ||
} | ||
|
||
private static byte[] ToBytes(BsonDocument doc) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this method is not needed. Callers can just use:
var bytes = document.ToBson();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
} | ||
} | ||
|
||
throw new ArgumentException("A MONGODB-AWS must have access key ID."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that ArgumentException is questionable. ArgumentException is usually reserved for problems with the arguments of the method itself.
Not sure InvalidDataException is right either (there is no data...).
Normally I think we would throw an InvalidOperationException here.
} | ||
} | ||
|
||
throw new ArgumentException("A MONGODB-AWS must have access key ID."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding the error message, make sure every message is a grammatically correct sentence that clearly identifies the object that is involved. So "MONGODB-AWS" doesn't seem correct (i.e. what is a "MONGODB-AWS").
Seems like the correct object is either a AwsCredential
or a MongoAwsAuthenticator
depending on where the error check is.
@@ -99,6 +99,7 @@ | |||
|
|||
<ItemGroup Condition="'$(TargetFramework)' == 'net452'"> | |||
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" /> | |||
<Reference Include="System.Net.Http" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll defer to you all on this one.
|
||
// constructors | ||
/// <summary> | ||
/// Initializes a new instance of the <see cref="GssapiAuthenticator"/> class. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GssapiAuthenticator
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.
Done.
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="GssapiAuthenticator"/> class. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same as above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
() => CreateAwsCredentialsFromEnvironmentVariables(), | ||
() => CreateAwsCredentialsFromEcsResponse(relativeUri), | ||
() => CreateAwsCredentialsFromEc2Response() | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it was my suggestion to have debugging a bit simpler. But I like Robert's idea too.
{ | ||
var username = Environment.GetEnvironmentVariable("AWS_ACCESS_KEY_ID"); | ||
var password = Environment.GetEnvironmentVariable("AWS_SECRET_ACCESS_KEY"); | ||
var sessionToken = Environment.GetEnvironmentVariable("AWS_SESSION_TOKEN"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add an exception in this case? If only one or two required fields were added?
var parsedReponse = BsonDocument.Parse(response); | ||
var username = parsedReponse.GetValue("AccessKeyId")?.AsString; | ||
var password = parsedReponse.GetValue("SecretAccessKey")?.AsString; | ||
var sessionToken = parsedReponse.GetValue("Token")?.AsString; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add an exception in case if only one or two required fields were received?
var context = BsonDeserializationContext.CreateRoot(jsonReader); | ||
return BsonDocumentSerializer.Instance.Deserialize(context); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like Robert's suggestion
document.Add("t", _sessionToken); | ||
} | ||
|
||
var clientSecondMessageBytes = ToBytes(document); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vincentkam , I agree with you. Originally I thought that I saw comma separated data here, but probably i was wrong
} | ||
} | ||
|
||
private class ClientLast : ISaslStep |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ok with the current version
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ready for review!
evergreen/run-mongodb-aws-test.sh
Outdated
@@ -0,0 +1,35 @@ | |||
#!/bin/bash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for suggestion!
Done.
set -o errexit # Exit the script with error if any of the commands fail | ||
|
||
############################################ | ||
# Main Program # |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
||
timestamp = dateTime.ToString("yyyyMMddTHHmmssZ"); | ||
|
||
string datestamp = dateTime.ToString("yyyyMMdd"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
} | ||
|
||
//private static methods | ||
private static string GetCanonicalHeaders(SortedDictionary<string, string> requestHeaders) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my opinion this name correlates to specification. Moreover Java uses the same name for method with same functionality and semantics: link
I prefer to leave it as is, unless there are other concerns besides naming.
using MongoDB.Bson.Serialization; | ||
using MongoDB.Bson.Serialization.Serializers; | ||
using MongoDB.Driver.Core.Connections; | ||
using MongoDB.Driver.Core.Misc; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
||
// constructors | ||
/// <summary> | ||
/// Initializes a new instance of the <see cref="GssapiAuthenticator"/> class. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.
Done.
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="GssapiAuthenticator"/> class. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
{ | ||
() => CreateAwsCredentialsFromMongoCredentials(username, securePassword, properties), | ||
() => CreateAwsCredentialsFromEnvironmentVariables(), | ||
() => CreateAwsCredentialsFromEcsResponse(relativeUri), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
if (_sessionToken != null) | ||
{ | ||
document.Add("t", _sessionToken); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
||
var serverFirstMessage = new BsonDocument() | ||
.Add("s", new BsonBinaryData(serverNonce)) | ||
.Add("h", new BsonString(host)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Evergreen: https://evergreen.mongodb.com/version/5e728a2056234309d47a9510
Note: Mark's AWS .NET POC branch: https://github.com/markbenvenuto/mongo-csharp-driver/commits/iam_auth