From afa8746cfb7159648cb7d0567f2f03fead957b94 Mon Sep 17 00:00:00 2001 From: Prajon Date: Mon, 22 Jul 2019 19:08:42 -0400 Subject: [PATCH 01/40] Refactor 'MAuthCoreExtensions.cs' and implement using interface --- CHANGELOG.md | 3 + .../IMAuthCoreImplementation.cs | 14 ++ src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 35 ++++- .../MAuthCoreExtensions.cs | 143 +----------------- .../MAuthCoreImplementation.cs | 122 +++++++++++++++ .../MAuthSigningHandler.cs | 8 +- .../Infrastructure/MAuthServerHandler.cs | 9 +- .../MAuthAuthenticatorTests.cs | 44 +++++- ...sts.cs => MAuthCoreImplementationTests.cs} | 42 ++--- .../UtilityExtensionsTest.cs | 6 +- version.props | 2 +- 11 files changed, 240 insertions(+), 188 deletions(-) create mode 100644 src/Medidata.MAuth.Core/IMAuthCoreImplementation.cs create mode 100644 src/Medidata.MAuth.Core/MAuthCoreImplementation.cs rename tests/Medidata.MAuth.Tests/{MAuthCoreExtensionsTests.cs => MAuthCoreImplementationTests.cs} (75%) diff --git a/CHANGELOG.md b/CHANGELOG.md index edb9f86..e28ce78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changes in Medidata.MAuth +## v3.1.3 +- **[Core]** Refactored `MAuthCoreExtensions.cs` and moved Signing and Verification method into `IMAuthCoreImplementation.cs`. + ## v3.1.2 - **[Core]** Fixed and enabled caching of the `ApplicationInfo` from the MAuth server. diff --git a/src/Medidata.MAuth.Core/IMAuthCoreImplementation.cs b/src/Medidata.MAuth.Core/IMAuthCoreImplementation.cs new file mode 100644 index 0000000..791b5b0 --- /dev/null +++ b/src/Medidata.MAuth.Core/IMAuthCoreImplementation.cs @@ -0,0 +1,14 @@ +using System.Net.Http; +using System.Threading.Tasks; + +namespace Medidata.MAuth.Core +{ + internal interface IMAuthCoreImplementation + { + Task Sign(HttpRequestMessage request, MAuthSigningOptions options); + + bool Verify(byte[] signedData, byte[] signature, string publicKey); + + Task GetSignature(HttpRequestMessage request, AuthenticationInfo authInfo); + } +} diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index 73c01f3..75c9910 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -2,6 +2,7 @@ using System.Net.Http; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; +using Newtonsoft.Json.Linq; using Org.BouncyCastle.Crypto; namespace Medidata.MAuth.Core @@ -11,6 +12,7 @@ internal class MAuthAuthenticator private readonly MAuthOptionsBase options; private readonly MAuthRequestRetrier retrier; private readonly IMemoryCache cache = new MemoryCache(new MemoryCacheOptions()); + private readonly MAuthCoreImplementation mAuthCore; public Guid ApplicationUuid => options.ApplicationUuid; @@ -26,7 +28,7 @@ public MAuthAuthenticator(MAuthOptionsBase options) throw new ArgumentNullException(nameof(options.PrivateKey)); this.options = options; - + mAuthCore = MAuthCoreImplementation.MAuthCore; retrier = new MAuthRequestRetrier(options); } @@ -34,10 +36,10 @@ public async Task AuthenticateRequest(HttpRequestMessage request) { try { - var authInfo = request.GetAuthenticationInfo(); + var authInfo = GetAuthenticationInfo(request); var appInfo = await GetApplicationInfo(authInfo.ApplicationUuid); - return authInfo.Payload.Verify(await request.GetSignature(authInfo), appInfo.PublicKey); + return mAuthCore.Verify(authInfo.Payload, await mAuthCore.GetSignature(request, authInfo), appInfo.PublicKey); } catch (ArgumentException ex) { @@ -84,5 +86,32 @@ private Task GetApplicationInfo(Guid applicationUuid) => private HttpRequestMessage CreateRequest(Guid applicationUuid) => new HttpRequestMessage(HttpMethod.Get, new Uri(options.MAuthServiceUrl, $"{Constants.MAuthTokenRequestPath}{applicationUuid.ToHyphenString()}.json")); + + /// + /// Extracts the authentication information from a . + /// + /// The request that has the authentication information. + /// The authentication information with the payload from the request. + public PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage request) + { + var authHeader = request.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKey); + + if (authHeader == null) + throw new ArgumentNullException(nameof(authHeader), "The MAuth header is missing from the request."); + + var signedTime = request.Headers.GetFirstValueOrDefault(Constants.MAuthTimeHeaderKey); + + if (signedTime == default(long)) + throw new ArgumentException("Invalid MAuth signed time header value.", nameof(signedTime)); + + var (uuid, payload) = authHeader.ParseAuthenticationHeader(); + + return new PayloadAuthenticationInfo() + { + ApplicationUuid = uuid, + Payload = Convert.FromBase64String(payload), + SignedTime = signedTime.FromUnixTimeSeconds() + }; + } } } diff --git a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs index 270fbad..9c3d7dd 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs @@ -10,154 +10,20 @@ using System.Threading.Tasks; using Newtonsoft.Json.Linq; using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Encodings; -using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.OpenSsl; -using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities.Encoders; namespace Medidata.MAuth.Core { internal static class MAuthCoreExtensions { - /// - /// Calculates the payload information based on the request and the authentication information. - /// - /// - /// The request which has the information (method, url and content) for the calculation. - /// - /// - /// The which holds the application uuid, the time of the - /// signature and the private key. - /// - /// A task object which will result the payload as a Base64 encoded string when completed. - public static async Task CalculatePayload( - this HttpRequestMessage request, PrivateKeyAuthenticationInfo authInfo) - { - var unsignedData = await request.GetSignature(authInfo).ConfigureAwait(false); - var signer = new Pkcs1Encoding(new RsaEngine()); - signer.Init(true, authInfo.PrivateKey.AsCipherParameters()); - - return Convert.ToBase64String(signer.ProcessBlock(unsignedData, 0, unsignedData.Length)); - } - - /// - /// Verifies that the signed data is equal to the signature by using the public key of the keypair which - /// was used to sign the data. - /// - /// The data in its signed form. - /// The signature which the signed data generated from. - /// The public key used to decrypt the signed data. - /// - /// If the signed data matches the signature, it returns ; otherwise it - /// returns . - /// - public static bool Verify(this byte[] signedData, byte[] signature, string publicKey) - { - Pkcs1Encoding.StrictLengthEnabled = false; - var cipher = CipherUtilities.GetCipher("RSA/ECB/PKCS1Padding"); - cipher.Init(false, publicKey.AsCipherParameters()); - - return cipher.DoFinal(signedData).SequenceEqual(signature); - } - - /// - /// Composes a signature as a SHA512 hash to be signed based on the request and authentication information. - /// - /// - /// The request which has the information (method, url and content) for the signature. - /// - /// - /// The which holds the application uuid and the time of the signature. - /// - /// A Task object which will result the SHA512 hash of the signature when it completes. - public static async Task GetSignature(this HttpRequestMessage request, AuthenticationInfo authInfo) => - new byte[][] - { - request.Method.Method.ToBytes(), Constants.NewLine, - request.RequestUri.AbsolutePath.ToBytes(), Constants.NewLine, - (request.Content != null ? await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false) : new byte[] { }), - Constants.NewLine, - authInfo.ApplicationUuid.ToHyphenString().ToBytes(), Constants.NewLine, - authInfo.SignedTime.ToUnixTimeSeconds().ToString().ToBytes() - } - .Concat() - .AsSHA512Hash(); - - /// - /// Extracts the authentication information from a . - /// - /// The request that has the authentication information. - /// The authentication information with the payload from the request. - public static PayloadAuthenticationInfo GetAuthenticationInfo(this HttpRequestMessage request) - { - var authHeader = request.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKey); - - if (authHeader == null) - throw new ArgumentNullException(nameof(authHeader), "The MAuth header is missing from the request."); - - var signedTime = request.Headers.GetFirstValueOrDefault(Constants.MAuthTimeHeaderKey); - - if (signedTime == default(long)) - throw new ArgumentException("Invalid MAuth signed time header value.", nameof(signedTime)); - - var (uuid, payload) = authHeader.ParseAuthenticationHeader(); - - return new PayloadAuthenticationInfo() - { - ApplicationUuid = uuid, - Payload = Convert.FromBase64String(payload), - SignedTime = signedTime.FromUnixTimeSeconds() - }; - } - - /// - /// Adds authentication information to a . - /// - /// The request to add the information to. - /// - /// The authentication information with a private key to calculate the payload with. - /// - /// - /// A Task object which will result the request with the authentication information added when it completes. - /// - public async static Task AddAuthenticationInfo( - this HttpRequestMessage request, PrivateKeyAuthenticationInfo authInfo) - { - var authHeader = - $"MWS {authInfo.ApplicationUuid.ToHyphenString()}:" + - $"{await request.CalculatePayload(authInfo).ConfigureAwait(false)}"; - - request.Headers.Add(Constants.MAuthHeaderKey, authHeader); - request.Headers.Add(Constants.MAuthTimeHeaderKey, authInfo.SignedTime.ToUnixTimeSeconds().ToString()); - - return request; - } - - /// - /// Signs an HTTP request with the MAuth-specific authentication information. - /// - /// The HTTP request message to sign. - /// The options that contains the required information for the signing. - /// - /// A Task object which will result the request signed with the authentication information when it completes. - /// - public static Task Sign( - this HttpRequestMessage request, MAuthSigningOptions options) => - request.AddAuthenticationInfo(new PrivateKeyAuthenticationInfo() - { - ApplicationUuid = options.ApplicationUuid, - SignedTime = options.SignedTime ?? DateTimeOffset.UtcNow, - PrivateKey = options.PrivateKey.Dereference().NormalizeLines() - }); - /// /// Deserializes an object from a content of a Http message. /// /// The content to deserialize the information from. /// A Task object which will result the application information when it completes. - public async static Task FromResponse(this HttpContent content) + public static async Task FromResponse(this HttpContent content) { var jsonObject = JObject.Parse(await content.ReadAsStringAsync().ConfigureAwait(false)); @@ -180,7 +46,6 @@ public static long ToUnixTimeSeconds(this DateTimeOffset value) => public static DateTimeOffset FromUnixTimeSeconds(this long value) => Constants.UnixEpoch.AddSeconds(value); - /// /// Returns the hyphenated representation of a as a . /// @@ -253,7 +118,7 @@ public static string Dereference(this string keyPathOrKey) => public static string NormalizeLines(this string key) => key.RemoveLineBreaks().InsertLineBreakAfterBegin().InsertLineBreakBeforeEnd(); - private static bool IsValidPath(this string value) => + public static bool IsValidPath(this string value) => Uri.TryCreate(value, UriKind.Absolute, out var pathUri) && pathUri.IsLoopback; private static string RemoveLineBreaks(this string key) => @@ -266,9 +131,9 @@ private static string InsertLineBreakBeforeEnd(this string key) => Regex.Replace(key, Constants.KeyNormalizeLinesEndRegexPattern, "\n${end}"); - private static byte[] ToBytes(this string value) => Encoding.UTF8.GetBytes(value); + public static byte[] ToBytes(this string value) => Encoding.UTF8.GetBytes(value); - private static byte[] Concat(this byte[][] values) + public static byte[] Concat(this byte[][] values) { var result = new byte[values.Sum(x => x.Length)]; var offset = 0; diff --git a/src/Medidata.MAuth.Core/MAuthCoreImplementation.cs b/src/Medidata.MAuth.Core/MAuthCoreImplementation.cs new file mode 100644 index 0000000..1dcdded --- /dev/null +++ b/src/Medidata.MAuth.Core/MAuthCoreImplementation.cs @@ -0,0 +1,122 @@ +using Org.BouncyCastle.Crypto.Encodings; +using System; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Security; + +namespace Medidata.MAuth.Core +{ + internal class MAuthCoreImplementation: IMAuthCoreImplementation + { + private MAuthCoreImplementation() { } + + public static MAuthCoreImplementation MAuthCore { get; } = new MAuthCoreImplementation(); + + /// + /// Signs an HTTP request with the MAuth-specific authentication information. + /// + /// The HTTP request message to sign. + /// The options that contains the required information for the signing. + /// + /// A Task object which will result the request signed with the authentication information when it completes. + /// + public Task Sign(HttpRequestMessage request, MAuthSigningOptions options) + { + return AddAuthenticationInfo(request, new PrivateKeyAuthenticationInfo() + { + ApplicationUuid = options.ApplicationUuid, + SignedTime = options.SignedTime ?? DateTimeOffset.UtcNow, + PrivateKey = options.PrivateKey.Dereference().NormalizeLines() + }); + } + + /// + /// Verifies that the signed data is equal to the signature by using the public key of the keypair which + /// was used to sign the data. + /// + /// The data in its signed form. + /// The signature which the signed data generated from. + /// The public key used to decrypt the signed data. + /// + /// If the signed data matches the signature, it returns ; otherwise it + /// returns . + /// + public bool Verify(byte[] signedData, byte[] signature, string publicKey) + { + Pkcs1Encoding.StrictLengthEnabled = false; + var cipher = CipherUtilities.GetCipher("RSA/ECB/PKCS1Padding"); + cipher.Init(false, publicKey.AsCipherParameters()); + + return cipher.DoFinal(signedData).SequenceEqual(signature); + } + + /// + /// Composes a signature as a SHA512 hash to be signed based on the request and authentication information. + /// + /// + /// The request which has the information (method, url and content) for the signature. + /// + /// + /// The which holds the application uuid and the time of the signature. + /// + /// A Task object which will result the SHA512 hash of the signature when it completes. + public async Task GetSignature(HttpRequestMessage request, AuthenticationInfo authInfo) + { + return new byte[][] + { + request.Method.Method.ToBytes(), Constants.NewLine, + request.RequestUri.AbsolutePath.ToBytes(), Constants.NewLine, + (request.Content != null ? await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false) : new byte[] { }), + Constants.NewLine, + authInfo.ApplicationUuid.ToHyphenString().ToBytes(), Constants.NewLine, + authInfo.SignedTime.ToUnixTimeSeconds().ToString().ToBytes() + } + .Concat() + .AsSHA512Hash(); + } + + /// + /// Adds authentication information to a . + /// + /// The request to add the information to. + /// + /// The authentication information with a private key to calculate the payload with. + /// + /// + /// A Task object which will result the request with the authentication information added when it completes. + /// + public async Task AddAuthenticationInfo(HttpRequestMessage request, PrivateKeyAuthenticationInfo authInfo) + { + var authHeader = + $"MWS {authInfo.ApplicationUuid.ToHyphenString()}:" + + $"{await CalculatePayload(request, authInfo).ConfigureAwait(false)}"; + + request.Headers.Add(Constants.MAuthHeaderKey, authHeader); + request.Headers.Add(Constants.MAuthTimeHeaderKey, authInfo.SignedTime.ToUnixTimeSeconds().ToString()); + + return request; + } + + /// + /// Calculates the payload information based on the request and the authentication information. + /// + /// + /// The request which has the information (method, url and content) for the calculation. + /// + /// + /// The which holds the application uuid, the time of the + /// signature and the private key. + /// + /// A task object which will result the payload as a Base64 encoded string when completed. + public async Task CalculatePayload(HttpRequestMessage request, PrivateKeyAuthenticationInfo authInfo) + { + var unsignedData = await GetSignature(request, authInfo).ConfigureAwait(false); + var signer = new Pkcs1Encoding(new RsaEngine()); + signer.Init(true, authInfo.PrivateKey.AsCipherParameters()); + + return Convert.ToBase64String(signer.ProcessBlock(unsignedData, 0, unsignedData.Length)); + } + } +} \ No newline at end of file diff --git a/src/Medidata.MAuth.Core/MAuthSigningHandler.cs b/src/Medidata.MAuth.Core/MAuthSigningHandler.cs index cc944b2..3cd44d7 100644 --- a/src/Medidata.MAuth.Core/MAuthSigningHandler.cs +++ b/src/Medidata.MAuth.Core/MAuthSigningHandler.cs @@ -12,12 +12,13 @@ namespace Medidata.MAuth.Core public class MAuthSigningHandler: DelegatingHandler { private readonly MAuthSigningOptions options; + private readonly MAuthCoreImplementation mAuthCore; /// Gets the Uuid of the client application. public Guid ClientAppUuid => options.ApplicationUuid; /// - /// Initializes a new insance of the class with the provided + /// Initializes a new instance of the class with the provided /// . /// /// The options for this message handler. @@ -37,6 +38,7 @@ public MAuthSigningHandler(MAuthSigningOptions options) public MAuthSigningHandler(MAuthSigningOptions options, HttpMessageHandler innerHandler): base(innerHandler) { this.options = options; + mAuthCore = MAuthCoreImplementation.MAuthCore; } /// @@ -47,14 +49,14 @@ public MAuthSigningHandler(MAuthSigningOptions options, HttpMessageHandler inner /// A cancellation token to cancel operation. /// Returns . The task object representing the asynchronous /// operation. - protected async override Task SendAsync( + protected override async Task SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { if (InnerHandler == null) InnerHandler = new HttpClientHandler(); return await base - .SendAsync(await request.Sign(options).ConfigureAwait(false), cancellationToken) + .SendAsync(await mAuthCore.Sign(request, options).ConfigureAwait(false), cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); } } diff --git a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs index 34e8876..408a7bd 100644 --- a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs +++ b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs @@ -19,14 +19,17 @@ protected override async Task SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { currentNumberOfAttempts += 1; + var mAuthCore = MAuthCoreImplementation.MAuthCore; if (currentNumberOfAttempts < SucceedAfterThisManyAttempts) return new HttpResponseMessage(HttpStatusCode.ServiceUnavailable); - var authInfo = request.GetAuthenticationInfo(); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); - if (!authInfo.Payload.Verify( - await request.GetSignature(authInfo), + var authInfo = authenticator.GetAuthenticationInfo(request); + + if (!mAuthCore.Verify(authInfo.Payload, + await mAuthCore.GetSignature(request, authInfo), TestExtensions.ServerPublicKey )) return new HttpResponseMessage(HttpStatusCode.Unauthorized) { RequestMessage = request }; diff --git a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs index b64edcf..5132e0b 100644 --- a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Net; +using System.Net.Http; using System.Threading.Tasks; using Medidata.MAuth.Core; using Medidata.MAuth.Tests.Infrastructure; @@ -40,9 +41,10 @@ public static async Task AuthenticateRequest_WithValidRequest_WillAuthenticate(s var testData = await method.FromResource(); var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); + var mAuthCore = MAuthCoreImplementation.MAuthCore; - var signedRequest = await testData.ToHttpRequestMessage() - .AddAuthenticationInfo(new PrivateKeyAuthenticationInfo() + var signedRequest = await mAuthCore + .AddAuthenticationInfo(testData.ToHttpRequestMessage(), new PrivateKeyAuthenticationInfo() { ApplicationUuid = testData.ApplicationUuid, PrivateKey = TestExtensions.ClientPrivateKey, @@ -70,9 +72,10 @@ public static async Task AuthenticateRequest_WithNumberOfAttempts_WillAuthentica var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( policy, shouldSucceedWithin: true)); + var mAuthCore = MAuthCoreImplementation.MAuthCore; - var signedRequest = await testData.ToHttpRequestMessage() - .AddAuthenticationInfo(new PrivateKeyAuthenticationInfo() + var signedRequest = await mAuthCore + .AddAuthenticationInfo(testData.ToHttpRequestMessage(),new PrivateKeyAuthenticationInfo() { ApplicationUuid = testData.ApplicationUuid, PrivateKey = TestExtensions.ClientPrivateKey, @@ -99,9 +102,10 @@ public static async Task AuthenticateRequest_AfterNumberOfAttempts_WillThrowExce var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( policy, shouldSucceedWithin: false)); + var mAuthCore = MAuthCoreImplementation.MAuthCore; - var signedRequest = await testData.ToHttpRequestMessage() - .AddAuthenticationInfo(new PrivateKeyAuthenticationInfo() + var signedRequest = await mAuthCore + .AddAuthenticationInfo(testData.ToHttpRequestMessage(),new PrivateKeyAuthenticationInfo() { ApplicationUuid = testData.ApplicationUuid, PrivateKey = TestExtensions.ClientPrivateKey, @@ -130,9 +134,10 @@ public static async Task SignRequest_WithValidRequest_WillSignProperly(string me // Arrange var testData = await method.FromResource(); var expectedMAuthHeader = testData.MAuthHeader; + var mAuthCore = MAuthCoreImplementation.MAuthCore; // Act - var actual = await testData.ToHttpRequestMessage().Sign(TestExtensions.ClientOptions(testData.SignedTime)); + var actual = await mAuthCore.Sign(testData.ToHttpRequestMessage(),TestExtensions.ClientOptions(testData.SignedTime)); // Assert Assert.Equal(expectedMAuthHeader, actual.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKey)); @@ -141,5 +146,30 @@ public static async Task SignRequest_WithValidRequest_WillSignProperly(string me actual.Headers.GetFirstValueOrDefault(Constants.MAuthTimeHeaderKey) ); } + + [Theory] + [InlineData("GET")] + [InlineData("DELETE")] + [InlineData("POST")] + [InlineData("PUT")] + public static async Task GetAuthenticationInfo_WithSignedRequest_WillReturnCorrectAuthInfo(string method) + { + // Arrange + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); + var testData = await method.FromResource(); + var request = new HttpRequestMessage(new HttpMethod(testData.Method), TestExtensions.TestUri); + + request.Headers.Add( + Constants.MAuthHeaderKey, testData.MAuthHeader); + request.Headers.Add(Constants.MAuthTimeHeaderKey, testData.SignedTimeUnixSeconds.ToString()); + + // Act + var actual = authenticator.GetAuthenticationInfo(request); + + // Assert + Assert.Equal(testData.ApplicationUuid, actual.ApplicationUuid); + Assert.Equal(Convert.FromBase64String(testData.Payload), actual.Payload); + Assert.Equal(testData.SignedTime, actual.SignedTime); + } } } diff --git a/tests/Medidata.MAuth.Tests/MAuthCoreExtensionsTests.cs b/tests/Medidata.MAuth.Tests/MAuthCoreImplementationTests.cs similarity index 75% rename from tests/Medidata.MAuth.Tests/MAuthCoreExtensionsTests.cs rename to tests/Medidata.MAuth.Tests/MAuthCoreImplementationTests.cs index 15f8fa5..b7bbca5 100644 --- a/tests/Medidata.MAuth.Tests/MAuthCoreExtensionsTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthCoreImplementationTests.cs @@ -10,7 +10,7 @@ namespace Medidata.MAuth.Tests { - public static class MAuthCoreExtensionsTests + public static class MAuthCoreImplementationTests { [Theory] [InlineData("GET")] @@ -21,6 +21,7 @@ public static async Task CalculatePayload_WithRequestAndAuthInfo_WillReturnCorre { // Arrange var testData = await method.FromResource(); + var mAuthCore = MAuthCoreImplementation.MAuthCore; var authInfo = new PrivateKeyAuthenticationInfo() { @@ -30,7 +31,7 @@ public static async Task CalculatePayload_WithRequestAndAuthInfo_WillReturnCorre }; // Act - var result = await testData.ToHttpRequestMessage().CalculatePayload(authInfo); + var result = await mAuthCore.CalculatePayload(testData.ToHttpRequestMessage(), authInfo); // Assert Assert.Equal(testData.Payload, result); @@ -48,8 +49,10 @@ public static void Verify_WithCorrectlySignedData_WillVerifyTheDataAsValid() var signedData = signer.ProcessBlock(unsignedData, 0, unsignedData.Length); + var mAuthCore = MAuthCoreImplementation.MAuthCore; + // Act - var result = signedData.Verify(Encoding.UTF8.GetBytes(signature), TestExtensions.ClientPublicKey); + var result = mAuthCore.Verify(signedData, Encoding.UTF8.GetBytes(signature), TestExtensions.ClientPublicKey); // Assert Assert.True(result); @@ -64,6 +67,7 @@ public static async Task GetSignature_WithRequest_WillReturnTheCorrectSignature( { // Arrange var testData = await method.FromResource(); + var mAuthCore = MAuthCoreImplementation.MAuthCore; var expectedSignature = ($"{testData.Method}\n" + @@ -80,7 +84,7 @@ public static async Task GetSignature_WithRequest_WillReturnTheCorrectSignature( }; // Act - var result = await testData.ToHttpRequestMessage().GetSignature(authInfo); + var result = await mAuthCore.GetSignature(testData.ToHttpRequestMessage(), authInfo); // Assert Assert.Equal(expectedSignature, result); @@ -91,9 +95,10 @@ public static async Task CalculatePayload_WithBinaryContent_WillCalculateTheProp { // Arrange var testData = await "POSTWithBinaryData".FromResource(); + var mAuthCore = MAuthCoreImplementation.MAuthCore; // Act - var result = await testData.ToHttpRequestMessage().CalculatePayload(new PrivateKeyAuthenticationInfo() + var result = await mAuthCore.CalculatePayload(testData.ToHttpRequestMessage(), new PrivateKeyAuthenticationInfo() { ApplicationUuid = testData.ApplicationUuid, SignedTime = testData.SignedTime, @@ -104,30 +109,6 @@ public static async Task CalculatePayload_WithBinaryContent_WillCalculateTheProp Assert.Equal(testData.Payload, result); } - [Theory] - [InlineData("GET")] - [InlineData("DELETE")] - [InlineData("POST")] - [InlineData("PUT")] - public static async Task GetAuthenticationInfo_WithSignedRequest_WillReturnCorrectAuthInfo(string method) - { - // Arrange - var testData = await method.FromResource(); - var request = new HttpRequestMessage(new HttpMethod(testData.Method), TestExtensions.TestUri); - - request.Headers.Add( - Constants.MAuthHeaderKey, testData.MAuthHeader); - request.Headers.Add(Constants.MAuthTimeHeaderKey, testData.SignedTimeUnixSeconds.ToString()); - - // Act - var actual = request.GetAuthenticationInfo(); - - // Assert - Assert.Equal(testData.ApplicationUuid, actual.ApplicationUuid); - Assert.Equal(Convert.FromBase64String(testData.Payload), actual.Payload); - Assert.Equal(testData.SignedTime, actual.SignedTime); - } - [Theory] [InlineData("GET")] [InlineData("DELETE")] @@ -138,6 +119,7 @@ public static async Task AddAuthenticationInfo_WithRequestAndAuthInfo_WillAddCor // Arrange var testData = await method.FromResource(); var expectedMAuthHeader = testData.MAuthHeader; + var mAuthCore = MAuthCoreImplementation.MAuthCore; var authInfo = new PrivateKeyAuthenticationInfo() { @@ -147,7 +129,7 @@ public static async Task AddAuthenticationInfo_WithRequestAndAuthInfo_WillAddCor }; // Act - var actual = await testData.ToHttpRequestMessage().AddAuthenticationInfo(authInfo); + var actual = await mAuthCore.AddAuthenticationInfo(testData.ToHttpRequestMessage(), authInfo); // Assert Assert.Equal(expectedMAuthHeader, actual.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKey)); diff --git a/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs b/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs index bede4b7..58f97e8 100644 --- a/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs +++ b/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs @@ -56,8 +56,10 @@ public static async Task Authenticate_WithValidRequest_WillAuthenticate(string m // Arrange var testData = await method.FromResource(); - var signedRequest = await testData.ToHttpRequestMessage() - .AddAuthenticationInfo(new PrivateKeyAuthenticationInfo() + var mAuthCore = MAuthCoreImplementation.MAuthCore; + + var signedRequest = await mAuthCore + .AddAuthenticationInfo(testData.ToHttpRequestMessage(), new PrivateKeyAuthenticationInfo() { ApplicationUuid = testData.ApplicationUuid, PrivateKey = TestExtensions.ClientPrivateKey, diff --git a/version.props b/version.props index c264a31..f4563c5 100644 --- a/version.props +++ b/version.props @@ -1,6 +1,6 @@  - 3.1.2 + 3.1.3 From a0ca1c1d132dd967214c553ec0b7d4f8f66e85c3 Mon Sep 17 00:00:00 2001 From: Prajon Date: Wed, 24 Jul 2019 09:02:12 -0400 Subject: [PATCH 02/40] Fix the feedback on access modifiers. --- src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 3 +-- src/Medidata.MAuth.Core/MAuthCoreExtensions.cs | 2 +- src/Medidata.MAuth.Core/MAuthCoreImplementation.cs | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index 75c9910..fe986cb 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -2,7 +2,6 @@ using System.Net.Http; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; -using Newtonsoft.Json.Linq; using Org.BouncyCastle.Crypto; namespace Medidata.MAuth.Core @@ -92,7 +91,7 @@ private HttpRequestMessage CreateRequest(Guid applicationUuid) => /// /// The request that has the authentication information. /// The authentication information with the payload from the request. - public PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage request) + internal PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage request) { var authHeader = request.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKey); diff --git a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs index 9c3d7dd..e853545 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs @@ -118,7 +118,7 @@ public static string Dereference(this string keyPathOrKey) => public static string NormalizeLines(this string key) => key.RemoveLineBreaks().InsertLineBreakAfterBegin().InsertLineBreakBeforeEnd(); - public static bool IsValidPath(this string value) => + private static bool IsValidPath(this string value) => Uri.TryCreate(value, UriKind.Absolute, out var pathUri) && pathUri.IsLoopback; private static string RemoveLineBreaks(this string key) => diff --git a/src/Medidata.MAuth.Core/MAuthCoreImplementation.cs b/src/Medidata.MAuth.Core/MAuthCoreImplementation.cs index 1dcdded..6ee1595 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreImplementation.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreImplementation.cs @@ -87,7 +87,7 @@ public async Task GetSignature(HttpRequestMessage request, Authenticatio /// /// A Task object which will result the request with the authentication information added when it completes. /// - public async Task AddAuthenticationInfo(HttpRequestMessage request, PrivateKeyAuthenticationInfo authInfo) + internal async Task AddAuthenticationInfo(HttpRequestMessage request, PrivateKeyAuthenticationInfo authInfo) { var authHeader = $"MWS {authInfo.ApplicationUuid.ToHyphenString()}:" + @@ -110,7 +110,7 @@ public async Task AddAuthenticationInfo(HttpRequestMessage r /// signature and the private key. /// /// A task object which will result the payload as a Base64 encoded string when completed. - public async Task CalculatePayload(HttpRequestMessage request, PrivateKeyAuthenticationInfo authInfo) + internal async Task CalculatePayload(HttpRequestMessage request, PrivateKeyAuthenticationInfo authInfo) { var unsignedData = await GetSignature(request, authInfo).ConfigureAwait(false); var signer = new Pkcs1Encoding(new RsaEngine()); From edcb96556f1335a6ef8d1745f8038de81ebf2d51 Mon Sep 17 00:00:00 2001 From: Prajon Date: Thu, 25 Jul 2019 12:58:24 -0400 Subject: [PATCH 03/40] Updated class name and created MAuthCoreFactory for instantiation --- CHANGELOG.md | 2 +- .../{IMAuthCoreImplementation.cs => IMAuthCore.cs} | 2 +- src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 4 ++-- .../{MAuthCoreImplementation.cs => MAuthCore.cs} | 6 ++---- src/Medidata.MAuth.Core/MAuthCoreFactory.cs | 10 ++++++++++ src/Medidata.MAuth.Core/MAuthSigningHandler.cs | 5 +++-- .../Infrastructure/MAuthServerHandler.cs | 2 +- .../Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs | 8 ++++---- ...hCoreImplementationTests.cs => MAuthCoreTests.cs} | 12 ++++++------ tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs | 2 +- 10 files changed, 31 insertions(+), 22 deletions(-) rename src/Medidata.MAuth.Core/{IMAuthCoreImplementation.cs => IMAuthCore.cs} (88%) rename src/Medidata.MAuth.Core/{MAuthCoreImplementation.cs => MAuthCore.cs} (96%) create mode 100644 src/Medidata.MAuth.Core/MAuthCoreFactory.cs rename tests/Medidata.MAuth.Tests/{MAuthCoreImplementationTests.cs => MAuthCoreTests.cs} (93%) diff --git a/CHANGELOG.md b/CHANGELOG.md index e28ce78..d04e51b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Changes in Medidata.MAuth ## v3.1.3 -- **[Core]** Refactored `MAuthCoreExtensions.cs` and moved Signing and Verification method into `IMAuthCoreImplementation.cs`. +- **[Core]** Refactored `MAuthCoreExtensions.cs` and moved Signing and Verification method into `IMAuthCore.cs`. ## v3.1.2 - **[Core]** Fixed and enabled caching of the `ApplicationInfo` from the MAuth server. diff --git a/src/Medidata.MAuth.Core/IMAuthCoreImplementation.cs b/src/Medidata.MAuth.Core/IMAuthCore.cs similarity index 88% rename from src/Medidata.MAuth.Core/IMAuthCoreImplementation.cs rename to src/Medidata.MAuth.Core/IMAuthCore.cs index 791b5b0..0d319c5 100644 --- a/src/Medidata.MAuth.Core/IMAuthCoreImplementation.cs +++ b/src/Medidata.MAuth.Core/IMAuthCore.cs @@ -3,7 +3,7 @@ namespace Medidata.MAuth.Core { - internal interface IMAuthCoreImplementation + internal interface IMAuthCore { Task Sign(HttpRequestMessage request, MAuthSigningOptions options); diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index fe986cb..11655cd 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -11,7 +11,7 @@ internal class MAuthAuthenticator private readonly MAuthOptionsBase options; private readonly MAuthRequestRetrier retrier; private readonly IMemoryCache cache = new MemoryCache(new MemoryCacheOptions()); - private readonly MAuthCoreImplementation mAuthCore; + private IMAuthCore mAuthCore; public Guid ApplicationUuid => options.ApplicationUuid; @@ -27,7 +27,6 @@ public MAuthAuthenticator(MAuthOptionsBase options) throw new ArgumentNullException(nameof(options.PrivateKey)); this.options = options; - mAuthCore = MAuthCoreImplementation.MAuthCore; retrier = new MAuthRequestRetrier(options); } @@ -35,6 +34,7 @@ public async Task AuthenticateRequest(HttpRequestMessage request) { try { + mAuthCore = MAuthCoreFactory.Instantiate(); var authInfo = GetAuthenticationInfo(request); var appInfo = await GetApplicationInfo(authInfo.ApplicationUuid); diff --git a/src/Medidata.MAuth.Core/MAuthCoreImplementation.cs b/src/Medidata.MAuth.Core/MAuthCore.cs similarity index 96% rename from src/Medidata.MAuth.Core/MAuthCoreImplementation.cs rename to src/Medidata.MAuth.Core/MAuthCore.cs index 6ee1595..2cbc2b9 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreImplementation.cs +++ b/src/Medidata.MAuth.Core/MAuthCore.cs @@ -8,11 +8,9 @@ namespace Medidata.MAuth.Core { - internal class MAuthCoreImplementation: IMAuthCoreImplementation + internal class MAuthCore: IMAuthCore { - private MAuthCoreImplementation() { } - - public static MAuthCoreImplementation MAuthCore { get; } = new MAuthCoreImplementation(); + public MAuthCore () { } /// /// Signs an HTTP request with the MAuth-specific authentication information. diff --git a/src/Medidata.MAuth.Core/MAuthCoreFactory.cs b/src/Medidata.MAuth.Core/MAuthCoreFactory.cs new file mode 100644 index 0000000..3e113d4 --- /dev/null +++ b/src/Medidata.MAuth.Core/MAuthCoreFactory.cs @@ -0,0 +1,10 @@ +namespace Medidata.MAuth.Core +{ + internal class MAuthCoreFactory + { + public static IMAuthCore Instantiate() + { + return new MAuthCore(); + } + } +} diff --git a/src/Medidata.MAuth.Core/MAuthSigningHandler.cs b/src/Medidata.MAuth.Core/MAuthSigningHandler.cs index 3cd44d7..03fbe4f 100644 --- a/src/Medidata.MAuth.Core/MAuthSigningHandler.cs +++ b/src/Medidata.MAuth.Core/MAuthSigningHandler.cs @@ -12,7 +12,7 @@ namespace Medidata.MAuth.Core public class MAuthSigningHandler: DelegatingHandler { private readonly MAuthSigningOptions options; - private readonly MAuthCoreImplementation mAuthCore; + private IMAuthCore mAuthCore; /// Gets the Uuid of the client application. public Guid ClientAppUuid => options.ApplicationUuid; @@ -38,7 +38,6 @@ public MAuthSigningHandler(MAuthSigningOptions options) public MAuthSigningHandler(MAuthSigningOptions options, HttpMessageHandler innerHandler): base(innerHandler) { this.options = options; - mAuthCore = MAuthCoreImplementation.MAuthCore; } /// @@ -55,6 +54,8 @@ protected override async Task SendAsync( if (InnerHandler == null) InnerHandler = new HttpClientHandler(); + mAuthCore = MAuthCoreFactory.Instantiate(); + return await base .SendAsync(await mAuthCore.Sign(request, options).ConfigureAwait(false), cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); diff --git a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs index 408a7bd..534bf50 100644 --- a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs +++ b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs @@ -19,7 +19,7 @@ protected override async Task SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { currentNumberOfAttempts += 1; - var mAuthCore = MAuthCoreImplementation.MAuthCore; + var mAuthCore = MAuthCoreFactory.Instantiate(); if (currentNumberOfAttempts < SucceedAfterThisManyAttempts) return new HttpResponseMessage(HttpStatusCode.ServiceUnavailable); diff --git a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs index 5132e0b..d368fd4 100644 --- a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs @@ -41,7 +41,7 @@ public static async Task AuthenticateRequest_WithValidRequest_WillAuthenticate(s var testData = await method.FromResource(); var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); - var mAuthCore = MAuthCoreImplementation.MAuthCore; + var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore .AddAuthenticationInfo(testData.ToHttpRequestMessage(), new PrivateKeyAuthenticationInfo() @@ -72,7 +72,7 @@ public static async Task AuthenticateRequest_WithNumberOfAttempts_WillAuthentica var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( policy, shouldSucceedWithin: true)); - var mAuthCore = MAuthCoreImplementation.MAuthCore; + var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore .AddAuthenticationInfo(testData.ToHttpRequestMessage(),new PrivateKeyAuthenticationInfo() @@ -102,7 +102,7 @@ public static async Task AuthenticateRequest_AfterNumberOfAttempts_WillThrowExce var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( policy, shouldSucceedWithin: false)); - var mAuthCore = MAuthCoreImplementation.MAuthCore; + var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore .AddAuthenticationInfo(testData.ToHttpRequestMessage(),new PrivateKeyAuthenticationInfo() @@ -134,7 +134,7 @@ public static async Task SignRequest_WithValidRequest_WillSignProperly(string me // Arrange var testData = await method.FromResource(); var expectedMAuthHeader = testData.MAuthHeader; - var mAuthCore = MAuthCoreImplementation.MAuthCore; + var mAuthCore = new MAuthCore(); // Act var actual = await mAuthCore.Sign(testData.ToHttpRequestMessage(),TestExtensions.ClientOptions(testData.SignedTime)); diff --git a/tests/Medidata.MAuth.Tests/MAuthCoreImplementationTests.cs b/tests/Medidata.MAuth.Tests/MAuthCoreTests.cs similarity index 93% rename from tests/Medidata.MAuth.Tests/MAuthCoreImplementationTests.cs rename to tests/Medidata.MAuth.Tests/MAuthCoreTests.cs index b7bbca5..96147fb 100644 --- a/tests/Medidata.MAuth.Tests/MAuthCoreImplementationTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthCoreTests.cs @@ -10,7 +10,7 @@ namespace Medidata.MAuth.Tests { - public static class MAuthCoreImplementationTests + public static class MAuthCoreTests { [Theory] [InlineData("GET")] @@ -21,7 +21,7 @@ public static async Task CalculatePayload_WithRequestAndAuthInfo_WillReturnCorre { // Arrange var testData = await method.FromResource(); - var mAuthCore = MAuthCoreImplementation.MAuthCore; + var mAuthCore = new MAuthCore(); var authInfo = new PrivateKeyAuthenticationInfo() { @@ -49,7 +49,7 @@ public static void Verify_WithCorrectlySignedData_WillVerifyTheDataAsValid() var signedData = signer.ProcessBlock(unsignedData, 0, unsignedData.Length); - var mAuthCore = MAuthCoreImplementation.MAuthCore; + var mAuthCore = new MAuthCore(); // Act var result = mAuthCore.Verify(signedData, Encoding.UTF8.GetBytes(signature), TestExtensions.ClientPublicKey); @@ -67,7 +67,7 @@ public static async Task GetSignature_WithRequest_WillReturnTheCorrectSignature( { // Arrange var testData = await method.FromResource(); - var mAuthCore = MAuthCoreImplementation.MAuthCore; + var mAuthCore = new MAuthCore(); var expectedSignature = ($"{testData.Method}\n" + @@ -95,7 +95,7 @@ public static async Task CalculatePayload_WithBinaryContent_WillCalculateTheProp { // Arrange var testData = await "POSTWithBinaryData".FromResource(); - var mAuthCore = MAuthCoreImplementation.MAuthCore; + var mAuthCore = new MAuthCore(); // Act var result = await mAuthCore.CalculatePayload(testData.ToHttpRequestMessage(), new PrivateKeyAuthenticationInfo() @@ -119,7 +119,7 @@ public static async Task AddAuthenticationInfo_WithRequestAndAuthInfo_WillAddCor // Arrange var testData = await method.FromResource(); var expectedMAuthHeader = testData.MAuthHeader; - var mAuthCore = MAuthCoreImplementation.MAuthCore; + var mAuthCore = new MAuthCore(); var authInfo = new PrivateKeyAuthenticationInfo() { diff --git a/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs b/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs index 58f97e8..432f3dc 100644 --- a/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs +++ b/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs @@ -56,7 +56,7 @@ public static async Task Authenticate_WithValidRequest_WillAuthenticate(string m // Arrange var testData = await method.FromResource(); - var mAuthCore = MAuthCoreImplementation.MAuthCore; + var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore .AddAuthenticationInfo(testData.ToHttpRequestMessage(), new PrivateKeyAuthenticationInfo() From 9b5f3f75ad44024a2f475097c484e6d523c58923 Mon Sep 17 00:00:00 2001 From: Prajon Date: Tue, 16 Jul 2019 18:59:09 -0400 Subject: [PATCH 04/40] Added logic for MAuth Client V2, updated netstandard framework and aspnetcore framework --- .../MAuthAspNetCoreExtensions.cs | 8 +- .../MAuthMiddleware.cs | 3 +- .../Medidata.MAuth.AspNetCore.csproj | 2 +- src/Medidata.MAuth.Core/Constants.cs | 17 ++ src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 43 ++- .../MAuthCoreExtensionsV2.cs | 246 ++++++++++++++++++ .../MAuthSigningHandler.cs | 1 + .../Medidata.MAuth.Core.csproj | 6 +- .../Models/MAuthVersion.cs | 21 ++ .../Options/MAuthOptionsBase.cs | 6 + .../Options/MAuthSigningOptions.cs | 10 +- src/Medidata.MAuth.Core/UtilityExtensions.cs | 7 +- src/Medidata.MAuth.Owin/MAuthMiddleware.cs | 3 +- .../MAuthOwinExtensions.cs | 8 +- .../MAuthAuthenticatingHandler.cs | 6 +- .../MAuthWebApiExtensions.cs | 11 +- .../Medidata.MAuth.Tests.csproj | 4 +- 17 files changed, 379 insertions(+), 23 deletions(-) create mode 100644 src/Medidata.MAuth.Core/MAuthCoreExtensionsV2.cs create mode 100644 src/Medidata.MAuth.Core/Models/MAuthVersion.cs diff --git a/src/Medidata.MAuth.AspNetCore/MAuthAspNetCoreExtensions.cs b/src/Medidata.MAuth.AspNetCore/MAuthAspNetCoreExtensions.cs index 7be333b..7d7215e 100644 --- a/src/Medidata.MAuth.AspNetCore/MAuthAspNetCoreExtensions.cs +++ b/src/Medidata.MAuth.AspNetCore/MAuthAspNetCoreExtensions.cs @@ -3,8 +3,10 @@ using System.Net.Http; using System.Threading.Tasks; using Medidata.MAuth.Core; +using Medidata.MAuth.Core.Models; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; +using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.AspNetCore { @@ -39,16 +41,20 @@ public static HttpRequestMessage ToHttpRequestMessage(this HttpRequest request) /// The authenticator which will attempt the request authentication. /// Determines if any exceptions during the authentication /// should be thrown. + /// Determines if the authentication uses V2 or V1 method /// /// This method returns if it successfully authenticated the request; /// otherwise it will return either if the method should ignore exceptions or /// will throw an exception if any errors occurred during the authentication. /// public static async Task TryAuthenticate( - this HttpContext context, MAuthAuthenticator authenticator, bool shouldIgnoreExceptions) + this HttpContext context, MAuthAuthenticator authenticator, bool shouldIgnoreExceptions, Enum mAuthVersion) { try { + if (mAuthVersion.Equals(Version.V2)) + return await authenticator.AuthenticateRequestV2(context.Request.ToHttpRequestMessage()); + return await authenticator.AuthenticateRequest(context.Request.ToHttpRequestMessage()); } catch (Exception) diff --git a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs index 0efdee7..63c4706 100644 --- a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs @@ -1,6 +1,7 @@ using System.Net; using System.Threading.Tasks; using Medidata.MAuth.Core; +using Medidata.MAuth.Core.Models; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Internal; @@ -24,7 +25,7 @@ public async Task Invoke(HttpContext context) context.Request.EnableRewind(); if (!options.Bypass(context.Request) && - !await context.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized)) + !await context.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized, options.MAuthVersion)) { context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; return; diff --git a/src/Medidata.MAuth.AspNetCore/Medidata.MAuth.AspNetCore.csproj b/src/Medidata.MAuth.AspNetCore/Medidata.MAuth.AspNetCore.csproj index 5f52e0b..022379a 100644 --- a/src/Medidata.MAuth.AspNetCore/Medidata.MAuth.AspNetCore.csproj +++ b/src/Medidata.MAuth.AspNetCore/Medidata.MAuth.AspNetCore.csproj @@ -2,7 +2,7 @@ - netcoreapp1.1 + netstandard2.0 This package contains an ASP.NET Core middleware to validate signed http requests with the Medidata MAuth protocol. The middleware communicates with an MAuth server in order to confirm the validity of the request authentication header. Include this package in your ASP.NET Core web api if you want to authenticate the api requests signed with the MAuth protocol. Medidata.MAuth.AspNetCore Medidata.MAuth.AspNetCore diff --git a/src/Medidata.MAuth.Core/Constants.cs b/src/Medidata.MAuth.Core/Constants.cs index 4aad4ca..e9c6ed4 100644 --- a/src/Medidata.MAuth.Core/Constants.cs +++ b/src/Medidata.MAuth.Core/Constants.cs @@ -30,5 +30,22 @@ internal static class Constants public static readonly string KeyNormalizeLinesEndRegexPattern = "(?-----END [A-Z ]+[-]+)$"; public static readonly byte[] NewLine = Encoding.UTF8.GetBytes("\n"); + + public static readonly Regex AuthenticationHeaderRegexV2 = new Regex( + "^MWSV2 " + + "(?[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})" + + ":" + + "(?" + + "(?:[0-9a-zA-Z+/]{4})*" + + "(?:[0-9a-zA-Z+/]{2}==|[0-9a-zA-Z+/]{3}=)" + + "?" + + ");$" + ); + + public static readonly string MAuthHeaderKeyV2 = "MCC-Authentication"; + + public static readonly string MAuthTimeHeaderKeyV2 = "MCC-Time"; + + public static readonly string MAuthTokenRequestPathV2 = "/mauth/v2/security_tokens/"; } } diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index 11655cd..3fe6a2f 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -1,8 +1,10 @@ using System; using System.Net.Http; +using System.Security.Cryptography; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; using Org.BouncyCastle.Crypto; +using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.Core { @@ -63,6 +65,38 @@ public async Task AuthenticateRequest(HttpRequestMessage request) } } + public async Task AuthenticateRequestV2(HttpRequestMessage request) + { + try + { + var authInfo = request.GetAuthenticationInfoV2(); + var appInfo = await GetApplicationInfo(authInfo.ApplicationUuid); + + return authInfo.Payload.VerifyV2(await request.GetSignatureV2(authInfo), appInfo.PublicKey); + } + catch (ArgumentException ex) + { + throw new AuthenticationException("The request has invalid MAuth authentication headers.", ex); + } + catch (RetriedRequestException ex) + { + throw new AuthenticationException( + "Could not query the application information for the application from the MAuth server.", ex); + } + catch (CryptographicException ex) + { + throw new AuthenticationException( + "The request verification failed due to an invalid payload information.", ex); + } + catch (Exception ex) + { + throw new AuthenticationException( + "An unexpected error occured during authentication. Please see the inner exception for details.", + ex + ); + } + } + private Task GetApplicationInfo(Guid applicationUuid) => cache.GetOrCreateAsync(applicationUuid, async entry => { @@ -82,8 +116,13 @@ private Task GetApplicationInfo(Guid applicationUuid) => return result; }); - private HttpRequestMessage CreateRequest(Guid applicationUuid) => - new HttpRequestMessage(HttpMethod.Get, new Uri(options.MAuthServiceUrl, + private HttpRequestMessage CreateRequest(Guid applicationUuid) + { + if (options.MAuthVersion.Equals(Version.V2)) + return new HttpRequestMessage(HttpMethod.Get, new Uri(options.MAuthServiceUrl, + $"{Constants.MAuthTokenRequestPathV2}{applicationUuid.ToHyphenString()}.json")); + + return new HttpRequestMessage(HttpMethod.Get, new Uri(options.MAuthServiceUrl, $"{Constants.MAuthTokenRequestPath}{applicationUuid.ToHyphenString()}.json")); /// diff --git a/src/Medidata.MAuth.Core/MAuthCoreExtensionsV2.cs b/src/Medidata.MAuth.Core/MAuthCoreExtensionsV2.cs new file mode 100644 index 0000000..8c05f7d --- /dev/null +++ b/src/Medidata.MAuth.Core/MAuthCoreExtensionsV2.cs @@ -0,0 +1,246 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; + +namespace Medidata.MAuth.Core +{ + /// + /// + /// + internal static class MAuthCoreExtensionsV2 + { + private const string MwsV2Token = "MWSV2"; + + //Response Signing + + // 1) Sign the response body separately from the rest of the response so that it does not have to be read into the memory all at once. + // 2) Force UTF-8 encoding of all text (e.g. status code, app_uuid etc.) + + /// + /// Calculates the payload information based on the request and the authentication information. + /// + /// + /// The request which has the information (method, url and content) for the calculation. + /// + /// + /// The which holds the application uuid, the time of the + /// signature and the private key. + /// + /// A task object which will result the payload as a Base64 encoded string when completed. + public static async Task CalculatePayloadV2( + this HttpRequestMessage request, PrivateKeyAuthenticationInfo authInfo) + { + var unsignedData = await request.GetSignatureV2(authInfo).ConfigureAwait(false); + var signer = new RSACryptoServiceProvider(); + + return Convert.ToBase64String(signer.SignHash(unsignedData, authInfo.PrivateKey)); + } + + /// + /// Verifies that the signed data is equal to the signature by using the public key of the keypair which + /// was used to sign the data. + /// + /// The data in its signed form. + /// The signature which the signed data generated from. + /// The public key used to decrypt the signed data. + /// + /// If the signed data matches the signature, it returns ; otherwise it + /// returns . + /// + public static bool VerifyV2(this byte[] signedData, byte[] signature, string publicKey) + { + var rsa = new RSACryptoServiceProvider(); + return rsa.VerifyHash(signedData, publicKey, signature); + } + + /// + /// Composes a signature as a SHA512 hash to be signed based on the request and authentication information. + /// + /// + /// The request which has the information (method, url and content) for the signature. + /// + /// + /// The which holds the application uuid and the time of the signature. + /// + /// A Task object which will result the SHA512 hash of the signature when it completes. + public static async Task GetSignatureV2(this HttpRequestMessage request, AuthenticationInfo authInfo) + { + var encodedHttpVerb = request.Method.Method.ToBytes(); + //var encodedResourceUriPath = Uri.EscapeUriString(request.RequestUri.AbsolutePath); + var encodedResourceUriPath = request.RequestUri.AbsolutePath.ToBytes(); + var encodedAppUUid = authInfo.ApplicationUuid.ToHyphenString().ToBytes(); + + var requestBody = request.Content != null ? + await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false) : new byte[] { }; + // var requestBodyDigest = SHA512 hash of request body + // UTF-8 encode the resulting hash + var requestBodyDigest = requestBody.AsSha512HashV2(); + //var requestBodyDigest = Encoding.UTF8.GetString(SHA512.Create().ComputeHash(requestBody)); + + var encodedCurrentSecondsSinceEpoch = authInfo.SignedTime.ToUnixTimeSeconds().ToString().ToBytes(); + + var encodedQueryParams = request.GetQueryStringParams().SortByKeyAscending().BuildEncodedQueryParams().ToBytes(); + + return new byte[][] + { + encodedHttpVerb, Constants.NewLine, + encodedResourceUriPath, Constants.NewLine, + requestBodyDigest, Constants.NewLine, + encodedAppUUid, Constants.NewLine, + encodedCurrentSecondsSinceEpoch, Constants.NewLine, + encodedQueryParams + }.Concat().AsSha512HashV2(); + } + + /// + /// Extracts the authentication information from a . + /// + /// The request that has the authentication information. + /// The authentication information with the payload from the request. + public static PayloadAuthenticationInfo GetAuthenticationInfoV2(this HttpRequestMessage request) + { + var authHeader = request.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKeyV2); + + if (authHeader == null) + throw new ArgumentNullException(nameof(authHeader), "The MAuth header is missing from the request."); + + var signedTime = request.Headers.GetFirstValueOrDefault(Constants.MAuthTimeHeaderKeyV2); + + if (signedTime == default(long)) + throw new ArgumentException("Invalid MAuth signed time header value.", nameof(signedTime)); + + var (uuid, payload) = authHeader.ParseAuthenticationHeader(); + + return new PayloadAuthenticationInfo() + { + ApplicationUuid = uuid, + Payload = Convert.FromBase64String(payload), + SignedTime = signedTime.FromUnixTimeSeconds() + }; + } + + /// + /// Adds authentication information to a . + /// + /// The request to add the information to. + /// + /// The authentication information with a private key to calculate the payload with. + /// + /// + /// A Task object which will result the request with the authentication information added when it completes. + /// + public static async Task AddAuthenticationInfoV2( + this HttpRequestMessage request, PrivateKeyAuthenticationInfo authInfo) + { + var authHeader = + $"{MwsV2Token} {authInfo.ApplicationUuid.ToHyphenString()}:" + + $"{await request.CalculatePayloadV2(authInfo).ConfigureAwait(false)};"; + + request.Headers.Add(Constants.MAuthHeaderKeyV2, authHeader); + request.Headers.Add(Constants.MAuthTimeHeaderKeyV2, authInfo.SignedTime.ToUnixTimeSeconds().ToString()); + + return request; + } + + /// + /// Signs an HTTP request with the MAuth-specific authentication information. + /// + /// The HTTP request message to sign. + /// The options that contains the required information for the signing. + /// + /// A Task object which will result the request signed with the authentication information when it completes. + /// + public static async Task SignV2( + this HttpRequestMessage request, MAuthSigningOptions options) + { + return await request.AddAuthenticationInfoV2(new PrivateKeyAuthenticationInfo() + { + ApplicationUuid = options.ApplicationUuid, + SignedTime = options.SignedTime ?? DateTimeOffset.UtcNow, + PrivateKey = options.PrivateKey.Dereference().NormalizeLines() + }); + } + + + /// + /// Deserializes an object from a content of a Http message. + /// + /// The content to deserialize the information from. + /// A Task object which will result the application information when it completes. + public static async Task FromResponseV2(this HttpContent content) + { + var jsonObject = JObject.Parse(await content.ReadAsStringAsync().ConfigureAwait(false)); + + return jsonObject.GetValue("security_token").ToObject(); + } + + /// + /// Provides an SHA512 hash value of bytes. + /// + /// The byte value for calculating the hash. + /// The UTF8 encoded bytes of SHA512 hash of the input value as a hex-encoded byte array. + private static byte[] AsSha512HashV2(this byte[] value) => + Encoding.UTF8.GetBytes(SHA512.Create().ComputeHash(value).ToString()); + + private static byte[] ToBytes(this string value) => Encoding.UTF8.GetBytes(value); + + private static byte[] Concat(this byte[][] values) + { + var result = new byte[values.Sum(x => x.Length)]; + var offset = 0; + foreach (var value in values) + { + Buffer.BlockCopy(value, 0, result, offset, value.Length); + offset += value.Length; + } + + return result; + } + + private static IDictionary GetQueryStringParams(this HttpRequestMessage request) + { + var query = request.RequestUri.Query; + var dictionary = query.Replace("?", "").Split('&').ToDictionary(x => x.Split('=')[0], x => x.Split('=')[1]); + return dictionary; + } + + private static IDictionary SortByKeyAscending(this IDictionary queryStringParams) + { + var sortDictionary = new Dictionary(); + var list = queryStringParams.Keys.ToList(); + list.Sort(); + foreach (var key in list) + { + sortDictionary.Add(key, queryStringParams[key]); + } + return sortDictionary; + } + + private static string BuildEncodedQueryParams(this IDictionary queryParams) + { + var encodedQueryParam = string.Empty; + foreach (var key in queryParams.Keys) + { + encodedQueryParam = (encodedQueryParam != string.Empty) ? + $"{encodedQueryParam}&" : encodedQueryParam; + + var encodedKey = Encoding.UTF8.GetBytes(key); // Is this UTF8 encoding or URI encoding + + var encodedValue = (queryParams[key] != string.Empty) + ? Encoding.UTF8.GetBytes(queryParams[key]) // same here UTF8 encoding or uri encoding + : new byte[] { }; + encodedQueryParam = $"{encodedQueryParam}{encodedKey}={encodedValue}"; + } + return encodedQueryParam; + } + } +} diff --git a/src/Medidata.MAuth.Core/MAuthSigningHandler.cs b/src/Medidata.MAuth.Core/MAuthSigningHandler.cs index 03fbe4f..3aafbcd 100644 --- a/src/Medidata.MAuth.Core/MAuthSigningHandler.cs +++ b/src/Medidata.MAuth.Core/MAuthSigningHandler.cs @@ -2,6 +2,7 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.Core { diff --git a/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj b/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj index c71d0a1..11dc3a1 100644 --- a/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj +++ b/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj @@ -4,10 +4,10 @@ A core package for Medidata HMAC protocol implementation. This package contains the core functionality which used by the MAuth authentication protocol-specific components. This package also can be used standalone if you want to sign HTTP/HTTPS requests with Medidata MAuth keys using the .NET HttpClient message handler mechanism. Medidata.MAuth.Core - netstandard1.4;net452 + netstandard2.0;net452 Medidata.MAuth.Core medidata;mauth;hmac;authentication;core;httpclient;messagehandler - $(PackageTargetFallback);dnxcore50 + $(PackageTargetFallback);dnxcore50 @@ -16,7 +16,7 @@ - + diff --git a/src/Medidata.MAuth.Core/Models/MAuthVersion.cs b/src/Medidata.MAuth.Core/Models/MAuthVersion.cs new file mode 100644 index 0000000..1889f14 --- /dev/null +++ b/src/Medidata.MAuth.Core/Models/MAuthVersion.cs @@ -0,0 +1,21 @@ +using System.Runtime.Serialization; + +namespace Medidata.MAuth.Core.Models +{ + /// + /// + /// + public enum Version + { + /// + /// + /// + [EnumMember(Value = "V1")] + V1 = 0, + /// + /// + /// + [EnumMember(Value = "V2")] + V2 = 1, + } +} diff --git a/src/Medidata.MAuth.Core/Options/MAuthOptionsBase.cs b/src/Medidata.MAuth.Core/Options/MAuthOptionsBase.cs index a4ed6e2..a432a8b 100644 --- a/src/Medidata.MAuth.Core/Options/MAuthOptionsBase.cs +++ b/src/Medidata.MAuth.Core/Options/MAuthOptionsBase.cs @@ -1,5 +1,6 @@ using System; using System.Net.Http; +using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.Core { @@ -38,5 +39,10 @@ public abstract class MAuthOptionsBase /// Determines the message handler for the requests to the MAuth server. /// public HttpMessageHandler MAuthServerHandler { get; set; } + + /// + /// + /// + public Version MAuthVersion { get; set; } } } diff --git a/src/Medidata.MAuth.Core/Options/MAuthSigningOptions.cs b/src/Medidata.MAuth.Core/Options/MAuthSigningOptions.cs index a913482..37a6b03 100644 --- a/src/Medidata.MAuth.Core/Options/MAuthSigningOptions.cs +++ b/src/Medidata.MAuth.Core/Options/MAuthSigningOptions.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.Core { @@ -24,5 +21,10 @@ public class MAuthSigningOptions /// This property is for testing purposes only. /// internal DateTimeOffset? SignedTime { get; set; } + + /// + /// + /// + public Version MAuthVersion { get; set; } } } diff --git a/src/Medidata.MAuth.Core/UtilityExtensions.cs b/src/Medidata.MAuth.Core/UtilityExtensions.cs index 5fc065a..a217362 100644 --- a/src/Medidata.MAuth.Core/UtilityExtensions.cs +++ b/src/Medidata.MAuth.Core/UtilityExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Net.Http; using System.Threading.Tasks; +using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.Core { @@ -45,7 +46,8 @@ public static (Guid Uuid, string Base64Payload) ParseAuthenticationHeader(this s public static bool TryParseAuthenticationHeader(this string headerValue, out (Guid Uuid, string Base64Payload) result) { - var match = Constants.AuthenticationHeaderRegex.Match(headerValue); + var match = headerValue.Contains("MWSV2") ? Constants.AuthenticationHeaderRegexV2.Match(headerValue) : + Constants.AuthenticationHeaderRegex.Match(headerValue); result = default((Guid, string)); @@ -66,6 +68,7 @@ public static bool TryParseAuthenticationHeader(this string headerValue, /// The task for the operation that is when completes will result in if /// the authentication is successful; otherwise . public static Task Authenticate(this HttpRequestMessage request, MAuthOptionsBase options) => - new MAuthAuthenticator(options).AuthenticateRequest(request); + options.MAuthVersion.Equals(Version.V2) ? new MAuthAuthenticator(options).AuthenticateRequestV2(request): + new MAuthAuthenticator(options).AuthenticateRequest(request); } } diff --git a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs index 3883f9e..5ac07c9 100644 --- a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs @@ -1,6 +1,7 @@ using System.Net; using System.Threading.Tasks; using Medidata.MAuth.Core; +using Medidata.MAuth.Core.Models; using Microsoft.Owin; namespace Medidata.MAuth.Owin @@ -21,7 +22,7 @@ public override async Task Invoke(IOwinContext context) await context.EnsureRequestBodyStreamSeekable(); if (!options.Bypass(context.Request) && - !await context.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized)) + !await context.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized, options.MAuthVersion)) { context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; return; diff --git a/src/Medidata.MAuth.Owin/MAuthOwinExtensions.cs b/src/Medidata.MAuth.Owin/MAuthOwinExtensions.cs index b4d272e..3795bd9 100644 --- a/src/Medidata.MAuth.Owin/MAuthOwinExtensions.cs +++ b/src/Medidata.MAuth.Owin/MAuthOwinExtensions.cs @@ -3,7 +3,9 @@ using System.Net.Http; using System.Threading.Tasks; using Medidata.MAuth.Core; +using Medidata.MAuth.Core.Models; using Microsoft.Owin; +using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.Owin { @@ -38,15 +40,19 @@ public static HttpRequestMessage ToHttpRequestMessage(this IOwinRequest request) /// The authenticator which will attempt the request authentication. /// Determines if any exceptions during the authentication /// should be thrown. + /// Determines if the authentication uses V2 or V1 method /// /// This method returns if it successfully authenticated the request; /// otherwise it will return either if the method should ignore exceptions or /// will throw an exception if any errors occurred during the authentication. public static async Task TryAuthenticate( - this IOwinContext context, MAuthAuthenticator authenticator, bool shouldIgnoreExceptions) + this IOwinContext context, MAuthAuthenticator authenticator, bool shouldIgnoreExceptions, Enum mAuthVersion) { try { + if (mAuthVersion.Equals(Version.V2)) + return await authenticator.AuthenticateRequestV2(context.Request.ToHttpRequestMessage()); + return await authenticator.AuthenticateRequest(context.Request.ToHttpRequestMessage()); } catch (Exception) diff --git a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs index 235e6f2..3ed934f 100644 --- a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs +++ b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs @@ -4,6 +4,8 @@ using System.Threading; using System.Threading.Tasks; using Medidata.MAuth.Core; +using Medidata.MAuth.Core.Models; +using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.WebApi { @@ -53,13 +55,13 @@ public MAuthAuthenticatingHandler(MAuthWebApiOptions options, HttpMessageHandler /// A cancellation token to cancel operation. /// Returns . The task object representing the asynchronous /// operation. - protected async override Task SendAsync( + protected override async Task SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { if (InnerHandler == null) InnerHandler = new HttpClientHandler(); - if (!await request.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized)) + if (!await request.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized, options.MAuthVersion)) return new HttpResponseMessage(HttpStatusCode.Unauthorized) { RequestMessage = request }; return await base diff --git a/src/Medidata.MAuth.WebApi/MAuthWebApiExtensions.cs b/src/Medidata.MAuth.WebApi/MAuthWebApiExtensions.cs index f4289e8..faa518c 100644 --- a/src/Medidata.MAuth.WebApi/MAuthWebApiExtensions.cs +++ b/src/Medidata.MAuth.WebApi/MAuthWebApiExtensions.cs @@ -1,7 +1,8 @@ -using System; +using Medidata.MAuth.Core; +using System; using System.Net.Http; using System.Threading.Tasks; -using Medidata.MAuth.Core; +using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.WebApi { @@ -15,15 +16,19 @@ internal static class MAuthWebApiExtensions /// The authenticator which will attempt the request authentication. /// Determines if any exceptions during the authentication /// should be thrown. + /// Determines if the authentication uses V2 or V1 method /// /// This method returns if it successfully authenticated the request; /// otherwise it will return either if the method should ignore exceptions or /// will throw an exception if any errors occurred during the authentication. public static async Task TryAuthenticate( - this HttpRequestMessage request, MAuthAuthenticator authenticator, bool shouldIgnoreExceptions) + this HttpRequestMessage request, MAuthAuthenticator authenticator, bool shouldIgnoreExceptions, Enum mAuthVersion) { try { + if (mAuthVersion.Equals(Version.V2)) + return await authenticator.AuthenticateRequestV2(request); + return await authenticator.AuthenticateRequest(request); } catch (Exception) diff --git a/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj b/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj index 9c39df1..501dfc1 100644 --- a/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj +++ b/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj @@ -5,7 +5,7 @@ Copyright © Medidata Solutions, Inc. 2017 Medidata.MAuth.Tests Medidata Solutions, Inc. - net452;netcoreapp1.1 + net452;netcoreapp2.0 Medidata.MAuth.Tests Medidata.MAuth.Tests true @@ -50,7 +50,7 @@ - + From abd25d3d8854b8f60694dc765295b080cf1cbee6 Mon Sep 17 00:00:00 2001 From: Prajon Date: Wed, 17 Jul 2019 19:45:59 -0400 Subject: [PATCH 05/40] Updated net452 to net461 so that Medidata.Platform.Logging can be intalled and upgraded Newstonsoft.Json to latest 12.0.2 --- src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj | 9 +++++---- src/Medidata.MAuth.Owin/Medidata.MAuth.Owin.csproj | 4 ++-- src/Medidata.MAuth.WebApi/Medidata.MAuth.WebApi.csproj | 4 ++-- tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj | 6 +++--- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj b/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj index 11dc3a1..642c982 100644 --- a/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj +++ b/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj @@ -4,14 +4,15 @@ A core package for Medidata HMAC protocol implementation. This package contains the core functionality which used by the MAuth authentication protocol-specific components. This package also can be used standalone if you want to sign HTTP/HTTPS requests with Medidata MAuth keys using the .NET HttpClient message handler mechanism. Medidata.MAuth.Core - netstandard2.0;net452 + netstandard2.0;net461 Medidata.MAuth.Core medidata;mauth;hmac;authentication;core;httpclient;messagehandler - $(PackageTargetFallback);dnxcore50 + - + + @@ -21,7 +22,7 @@ - + diff --git a/src/Medidata.MAuth.Owin/Medidata.MAuth.Owin.csproj b/src/Medidata.MAuth.Owin/Medidata.MAuth.Owin.csproj index a1a9fa4..a99ed86 100644 --- a/src/Medidata.MAuth.Owin/Medidata.MAuth.Owin.csproj +++ b/src/Medidata.MAuth.Owin/Medidata.MAuth.Owin.csproj @@ -4,7 +4,7 @@ This package contains an OWIN middleware to validate signed http requests with the Medidata MAuth protocol. The middleware communicates with an MAuth server in order to confirm the validity of the request authentication header. Include this package in your OWIN-enabled web api if you want to authenticate the api requests signed with the MAuth protocol. Medidata.MAuth.Owin - net452 + net461 Medidata.MAuth.Owin medidata;mauth;hmac;authentication;core;owin;middleware;webapi @@ -13,7 +13,7 @@ - + diff --git a/src/Medidata.MAuth.WebApi/Medidata.MAuth.WebApi.csproj b/src/Medidata.MAuth.WebApi/Medidata.MAuth.WebApi.csproj index ebb9868..bb36ccc 100644 --- a/src/Medidata.MAuth.WebApi/Medidata.MAuth.WebApi.csproj +++ b/src/Medidata.MAuth.WebApi/Medidata.MAuth.WebApi.csproj @@ -4,7 +4,7 @@ This package contains an HTTP message handler to validate signed http requests with the Medidata MAuth protocol. The handler communicates with an MAuth server in order to confirm the validity of the request authentication header. Include this package in your WebAPI application if you want to authenticate the api requests signed with the MAuth protocol. Medidata.MAuth.WebApi - net452 + net461 Medidata.MAuth.WebApi medidata;mauth;hmac;authentication;core;webapi;message;handler @@ -13,7 +13,7 @@ - + diff --git a/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj b/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj index 501dfc1..e432750 100644 --- a/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj +++ b/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj @@ -5,7 +5,7 @@ Copyright © Medidata Solutions, Inc. 2017 Medidata.MAuth.Tests Medidata Solutions, Inc. - net452;netcoreapp2.0 + net461;netcoreapp2.0 Medidata.MAuth.Tests Medidata.MAuth.Tests true @@ -39,7 +39,7 @@ - + @@ -61,7 +61,7 @@ - + From 47dbae286628dbb05e9556fa8e3152a2da789540 Mon Sep 17 00:00:00 2001 From: Prajon Date: Thu, 18 Jul 2019 08:12:03 -0400 Subject: [PATCH 06/40] Removed 'Medidata.Platform.Logging' --- src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj b/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj index 642c982..bb9a4e7 100644 --- a/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj +++ b/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj @@ -11,7 +11,6 @@ - From 6ecd20c42f6f45cc77fec50f8141a92466495bfd Mon Sep 17 00:00:00 2001 From: Prajon Date: Sun, 4 Aug 2019 20:19:14 -0400 Subject: [PATCH 07/40] Rebased to refactored code and added unit tests for V2 functionality and fixed V1 unit tests --- .../MAuthAspNetCoreExtensions.cs | 8 +- .../MAuthMiddleware.cs | 3 +- src/Medidata.MAuth.Core/Constants.cs | 8 +- .../Exceptions/InvalidVersionException.cs | 24 +++ src/Medidata.MAuth.Core/IMAuthCore.cs | 2 + src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 111 ++++------ src/Medidata.MAuth.Core/MAuthCore.cs | 27 +++ .../MAuthCoreExtensions.cs | 78 ++++++- src/Medidata.MAuth.Core/MAuthCoreFactory.cs | 14 +- ...AuthCoreExtensionsV2.cs => MAuthCoreV2.cs} | 195 ++++++------------ .../MAuthRequestRetrier.cs | 10 +- .../MAuthSigningHandler.cs | 8 +- .../Models/{MAuthVersion.cs => Version.cs} | 8 +- .../Options/MAuthOptionsBase.cs | 7 +- .../Options/MAuthSigningOptions.cs | 7 +- src/Medidata.MAuth.Core/UtilityExtensions.cs | 52 ++++- src/Medidata.MAuth.Owin/MAuthMiddleware.cs | 3 +- .../MAuthOwinExtensions.cs | 8 +- .../MAuthAuthenticatingHandler.cs | 4 +- .../MAuthWebApiExtensions.cs | 7 +- .../Infrastructure/AssertSigningHandler.cs | 9 +- .../Infrastructure/MAuthServerHandler.cs | 11 +- .../Infrastructure/RequestData.cs | 3 + .../Infrastructure/TestExtensions.cs | 16 +- .../MAuthAuthenticatorTests.cs | 148 +++++++++++-- tests/Medidata.MAuth.Tests/MAuthCoreTests.cs | 28 ++- .../Medidata.MAuth.Tests/MAuthCoreV2Tests.cs | 176 ++++++++++++++++ .../MAuthSigningHandlerTests.cs | 53 ++++- .../Medidata.MAuth.Tests/MAuthWebApiTests.cs | 33 ++- .../Medidata.MAuth.Tests.csproj | 10 + .../Mocks/RequestData/DELETE.json | 3 +- .../Mocks/RequestData/GET.json | 3 +- .../Mocks/RequestData/POST.json | 3 +- .../Mocks/RequestData/POSTWithBinaryData.json | 3 +- .../Mocks/RequestData/PUT.json | 3 +- .../Mocks/RequestDataV2/DELETE.json | 7 + .../Mocks/RequestDataV2/GET.json | 7 + .../Mocks/RequestDataV2/POST.json | 8 + .../RequestDataV2/POSTWithBinaryData.json | 8 + .../Mocks/RequestDataV2/PUT.json | 8 + 40 files changed, 842 insertions(+), 282 deletions(-) create mode 100644 src/Medidata.MAuth.Core/Exceptions/InvalidVersionException.cs rename src/Medidata.MAuth.Core/{MAuthCoreExtensionsV2.cs => MAuthCoreV2.cs} (56%) rename src/Medidata.MAuth.Core/Models/{MAuthVersion.cs => Version.cs} (71%) create mode 100644 tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs create mode 100644 tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/DELETE.json create mode 100644 tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/GET.json create mode 100644 tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/POST.json create mode 100644 tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/POSTWithBinaryData.json create mode 100644 tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/PUT.json diff --git a/src/Medidata.MAuth.AspNetCore/MAuthAspNetCoreExtensions.cs b/src/Medidata.MAuth.AspNetCore/MAuthAspNetCoreExtensions.cs index 7d7215e..7be333b 100644 --- a/src/Medidata.MAuth.AspNetCore/MAuthAspNetCoreExtensions.cs +++ b/src/Medidata.MAuth.AspNetCore/MAuthAspNetCoreExtensions.cs @@ -3,10 +3,8 @@ using System.Net.Http; using System.Threading.Tasks; using Medidata.MAuth.Core; -using Medidata.MAuth.Core.Models; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; -using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.AspNetCore { @@ -41,20 +39,16 @@ public static HttpRequestMessage ToHttpRequestMessage(this HttpRequest request) /// The authenticator which will attempt the request authentication. /// Determines if any exceptions during the authentication /// should be thrown. - /// Determines if the authentication uses V2 or V1 method /// /// This method returns if it successfully authenticated the request; /// otherwise it will return either if the method should ignore exceptions or /// will throw an exception if any errors occurred during the authentication. /// public static async Task TryAuthenticate( - this HttpContext context, MAuthAuthenticator authenticator, bool shouldIgnoreExceptions, Enum mAuthVersion) + this HttpContext context, MAuthAuthenticator authenticator, bool shouldIgnoreExceptions) { try { - if (mAuthVersion.Equals(Version.V2)) - return await authenticator.AuthenticateRequestV2(context.Request.ToHttpRequestMessage()); - return await authenticator.AuthenticateRequest(context.Request.ToHttpRequestMessage()); } catch (Exception) diff --git a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs index 63c4706..0efdee7 100644 --- a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs @@ -1,7 +1,6 @@ using System.Net; using System.Threading.Tasks; using Medidata.MAuth.Core; -using Medidata.MAuth.Core.Models; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Internal; @@ -25,7 +24,7 @@ public async Task Invoke(HttpContext context) context.Request.EnableRewind(); if (!options.Bypass(context.Request) && - !await context.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized, options.MAuthVersion)) + !await context.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized)) { context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; return; diff --git a/src/Medidata.MAuth.Core/Constants.cs b/src/Medidata.MAuth.Core/Constants.cs index e9c6ed4..fe56038 100644 --- a/src/Medidata.MAuth.Core/Constants.cs +++ b/src/Medidata.MAuth.Core/Constants.cs @@ -16,7 +16,7 @@ internal static class Constants "(?:[0-9a-zA-Z+/]{4})*" + "(?:[0-9a-zA-Z+/]{2}==|[0-9a-zA-Z+/]{3}=)" + "?" + - ")$" + ")$", RegexOptions.Compiled ); public static readonly string MAuthHeaderKey = "X-MWS-Authentication"; @@ -39,13 +39,13 @@ internal static class Constants "(?:[0-9a-zA-Z+/]{4})*" + "(?:[0-9a-zA-Z+/]{2}==|[0-9a-zA-Z+/]{3}=)" + "?" + - ");$" + ");$", RegexOptions.Compiled ); + public static readonly string MAuthTokenRequestPathV2 = "/mauth/v2/security_tokens/"; + public static readonly string MAuthHeaderKeyV2 = "MCC-Authentication"; public static readonly string MAuthTimeHeaderKeyV2 = "MCC-Time"; - - public static readonly string MAuthTokenRequestPathV2 = "/mauth/v2/security_tokens/"; } } diff --git a/src/Medidata.MAuth.Core/Exceptions/InvalidVersionException.cs b/src/Medidata.MAuth.Core/Exceptions/InvalidVersionException.cs new file mode 100644 index 0000000..6150a53 --- /dev/null +++ b/src/Medidata.MAuth.Core/Exceptions/InvalidVersionException.cs @@ -0,0 +1,24 @@ +using System; + +namespace Medidata.MAuth.Core.Exceptions +{ + /// + /// The exception that is thrown when version no longer allowed is passed. + /// + public class InvalidVersionException : Exception + { + /// + /// Initializes a new instance of the class with the specified message. + /// + /// A message that describes the invalid version failure. + public InvalidVersionException(string message) : base(message) { } + + /// + /// Initializes a new instance of the class with the specified message + /// and inner exception. + /// + /// A message that describes the invalid version failure. + /// An exception that is the cause of the current exception. + public InvalidVersionException(string message, Exception innerException) : base(message, innerException) { } + } +} diff --git a/src/Medidata.MAuth.Core/IMAuthCore.cs b/src/Medidata.MAuth.Core/IMAuthCore.cs index 0d319c5..d1f27fc 100644 --- a/src/Medidata.MAuth.Core/IMAuthCore.cs +++ b/src/Medidata.MAuth.Core/IMAuthCore.cs @@ -10,5 +10,7 @@ internal interface IMAuthCore bool Verify(byte[] signedData, byte[] signature, string publicKey); Task GetSignature(HttpRequestMessage request, AuthenticationInfo authInfo); + + PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage request); } } diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index 3fe6a2f..02d0d8d 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -2,6 +2,7 @@ using System.Net.Http; using System.Security.Cryptography; using System.Threading.Tasks; +using Medidata.MAuth.Core.Exceptions; using Microsoft.Extensions.Caching.Memory; using Org.BouncyCastle.Crypto; using Version = Medidata.MAuth.Core.Models.Version; @@ -11,7 +12,7 @@ namespace Medidata.MAuth.Core internal class MAuthAuthenticator { private readonly MAuthOptionsBase options; - private readonly MAuthRequestRetrier retrier; + private MAuthRequestRetrier retrier; private readonly IMemoryCache cache = new MemoryCache(new MemoryCacheOptions()); private IMAuthCore mAuthCore; @@ -29,18 +30,23 @@ public MAuthAuthenticator(MAuthOptionsBase options) throw new ArgumentNullException(nameof(options.PrivateKey)); this.options = options; - retrier = new MAuthRequestRetrier(options); } public async Task AuthenticateRequest(HttpRequestMessage request) { try { - mAuthCore = MAuthCoreFactory.Instantiate(); - var authInfo = GetAuthenticationInfo(request); - var appInfo = await GetApplicationInfo(authInfo.ApplicationUuid); + var version = request.GetAuthHeaderValue().GetVersionFromAuthenticationHeader(); - return mAuthCore.Verify(authInfo.Payload, await mAuthCore.GetSignature(request, authInfo), appInfo.PublicKey); + if (options.DisableV1 && version == Version.MWS.ToString()) + throw new InvalidVersionException($"Authentication with {version} version is no longer supported."); + + mAuthCore = MAuthCoreFactory.Instantiate(version); + var authInfo = mAuthCore.GetAuthenticationInfo(request); + var appInfo = await GetApplicationInfo(authInfo.ApplicationUuid, version); + + return mAuthCore.Verify(authInfo.Payload, await mAuthCore.GetSignature(request, authInfo), + appInfo.PublicKey); } catch (ArgumentException ex) { @@ -56,37 +62,9 @@ public async Task AuthenticateRequest(HttpRequestMessage request) throw new AuthenticationException( "The request verification failed due to an invalid payload information.", ex); } - catch (Exception ex) - { - throw new AuthenticationException( - "An unexpected error occured during authentication. Please see the inner exception for details.", - ex - ); - } - } - - public async Task AuthenticateRequestV2(HttpRequestMessage request) - { - try - { - var authInfo = request.GetAuthenticationInfoV2(); - var appInfo = await GetApplicationInfo(authInfo.ApplicationUuid); - - return authInfo.Payload.VerifyV2(await request.GetSignatureV2(authInfo), appInfo.PublicKey); - } - catch (ArgumentException ex) - { - throw new AuthenticationException("The request has invalid MAuth authentication headers.", ex); - } - catch (RetriedRequestException ex) - { - throw new AuthenticationException( - "Could not query the application information for the application from the MAuth server.", ex); - } - catch (CryptographicException ex) + catch (InvalidVersionException ex) { - throw new AuthenticationException( - "The request verification failed due to an invalid payload information.", ex); + throw new InvalidVersionException(ex.Message, ex); } catch (Exception ex) { @@ -97,12 +75,14 @@ public async Task AuthenticateRequestV2(HttpRequestMessage request) } } - private Task GetApplicationInfo(Guid applicationUuid) => + private Task GetApplicationInfo(Guid applicationUuid, string version) => cache.GetOrCreateAsync(applicationUuid, async entry => { + var tokenRequestPath = version.GetMAuthTokenRequestPath(); + retrier = new MAuthRequestRetrier(options, (Version)Enum.Parse(typeof(Version), version)); var response = await retrier.GetSuccessfulResponse( applicationUuid, - CreateRequest, + CreateRequest, tokenRequestPath, requestAttempts: (int)options.MAuthServiceRetryPolicy + 1 ); @@ -116,40 +96,35 @@ private Task GetApplicationInfo(Guid applicationUuid) => return result; }); - private HttpRequestMessage CreateRequest(Guid applicationUuid) - { - if (options.MAuthVersion.Equals(Version.V2)) - return new HttpRequestMessage(HttpMethod.Get, new Uri(options.MAuthServiceUrl, - $"{Constants.MAuthTokenRequestPathV2}{applicationUuid.ToHyphenString()}.json")); - - return new HttpRequestMessage(HttpMethod.Get, new Uri(options.MAuthServiceUrl, - $"{Constants.MAuthTokenRequestPath}{applicationUuid.ToHyphenString()}.json")); - - /// - /// Extracts the authentication information from a . - /// - /// The request that has the authentication information. - /// The authentication information with the payload from the request. - internal PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage request) - { - var authHeader = request.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKey); + private HttpRequestMessage CreateRequest(Guid applicationUuid, string tokenRequestPath) => + new HttpRequestMessage(HttpMethod.Get, new Uri(options.MAuthServiceUrl, + $"{tokenRequestPath}{applicationUuid.ToHyphenString()}.json")); - if (authHeader == null) - throw new ArgumentNullException(nameof(authHeader), "The MAuth header is missing from the request."); + ///// + ///// Extracts the authentication information from a . + ///// + ///// The request that has the authentication information. + ///// The authentication information with the payload from the request. + //internal PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage request) + //{ + // var authHeader = request.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKey); - var signedTime = request.Headers.GetFirstValueOrDefault(Constants.MAuthTimeHeaderKey); + // if (authHeader == null) + // throw new ArgumentNullException(nameof(authHeader), "The MAuth header is missing from the request."); - if (signedTime == default(long)) - throw new ArgumentException("Invalid MAuth signed time header value.", nameof(signedTime)); + // var signedTime = request.Headers.GetFirstValueOrDefault(Constants.MAuthTimeHeaderKey); - var (uuid, payload) = authHeader.ParseAuthenticationHeader(); + // if (signedTime == default(long)) + // throw new ArgumentException("Invalid MAuth signed time header value.", nameof(signedTime)); - return new PayloadAuthenticationInfo() - { - ApplicationUuid = uuid, - Payload = Convert.FromBase64String(payload), - SignedTime = signedTime.FromUnixTimeSeconds() - }; - } + // var (uuid, payload) = authHeader.ParseAuthenticationHeader(); + + // return new PayloadAuthenticationInfo() + // { + // ApplicationUuid = uuid, + // Payload = Convert.FromBase64String(payload), + // SignedTime = signedTime.FromUnixTimeSeconds() + // }; + //} } } diff --git a/src/Medidata.MAuth.Core/MAuthCore.cs b/src/Medidata.MAuth.Core/MAuthCore.cs index 2cbc2b9..c893686 100644 --- a/src/Medidata.MAuth.Core/MAuthCore.cs +++ b/src/Medidata.MAuth.Core/MAuthCore.cs @@ -116,5 +116,32 @@ internal async Task CalculatePayload(HttpRequestMessage request, Private return Convert.ToBase64String(signer.ProcessBlock(unsignedData, 0, unsignedData.Length)); } + + /// + /// Extracts the authentication information from a . + /// + /// The request that has the authentication information. + /// The authentication information with the payload from the request. + public PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage request) + { + var authHeader = request.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKey); + + if (authHeader == null) + throw new ArgumentNullException(nameof(authHeader), "The MAuth header is missing from the request."); + + var signedTime = request.Headers.GetFirstValueOrDefault(Constants.MAuthTimeHeaderKey); + + if (signedTime == default(long)) + throw new ArgumentException("Invalid MAuth signed time header value.", nameof(signedTime)); + + var (uuid, payload) = authHeader.ParseAuthenticationHeader(); + + return new PayloadAuthenticationInfo() + { + ApplicationUuid = uuid, + Payload = Convert.FromBase64String(payload), + SignedTime = signedTime.FromUnixTimeSeconds() + }; + } } } \ No newline at end of file diff --git a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs index e853545..b66551a 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs @@ -8,6 +8,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; +using System.Web; using Newtonsoft.Json.Linq; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; @@ -96,6 +97,37 @@ public static ICipherParameters AsCipherParameters(this string key) } } + public static RSAParameters AsRsaParameters(this string key) + { + var rsaParameter = new RSAParameters(); + using (var reader = new StringReader(key)) + { + var keyObject = new PemReader(reader).ReadObject(); + + if (keyObject is AsymmetricCipherKeyPair pair) + { + var privateKeyParams = (RsaPrivateCrtKeyParameters) pair.Private; + + rsaParameter.Modulus = privateKeyParams.Modulus.ToByteArrayUnsigned(); + rsaParameter.P = privateKeyParams.P.ToByteArrayUnsigned(); + rsaParameter.Q = privateKeyParams.Q.ToByteArrayUnsigned(); + rsaParameter.DP = privateKeyParams.DP.ToByteArrayUnsigned(); + rsaParameter.DQ = privateKeyParams.DQ.ToByteArrayUnsigned(); + rsaParameter.InverseQ = privateKeyParams.QInv.ToByteArrayUnsigned(); + rsaParameter.D = privateKeyParams.Exponent.ToByteArrayUnsigned(); + rsaParameter.Exponent = privateKeyParams.PublicExponent.ToByteArrayUnsigned(); + return rsaParameter; + } + else + { + var publicKeyParam = (RsaKeyParameters)keyObject; + rsaParameter.Modulus = publicKeyParam.Modulus.ToByteArrayUnsigned(); + rsaParameter.Exponent = publicKeyParam.Exponent.ToByteArrayUnsigned(); + return rsaParameter; + } + } + } + /// /// Sets the stream position to 0 if the stream is seekable; otherwise it will return the stream with its /// current position. @@ -130,7 +162,6 @@ private static string InsertLineBreakAfterBegin(this string key) => private static string InsertLineBreakBeforeEnd(this string key) => Regex.Replace(key, Constants.KeyNormalizeLinesEndRegexPattern, "\n${end}"); - public static byte[] ToBytes(this string value) => Encoding.UTF8.GetBytes(value); public static byte[] Concat(this byte[][] values) @@ -145,5 +176,50 @@ public static byte[] Concat(this byte[][] values) return result; } + + public static IDictionary GetQueryStringParams(this string queryString) + { + var dictionary = queryString.Replace("?", "").Split('&').ToDictionary(x => x.Split('=')[0], x => x.Split('=')[1]); + return dictionary; + } + + public static IDictionary SortByKeyAscending(this IDictionary queryStringParams) + { + var sortDictionary = new Dictionary(); + var list = queryStringParams.Keys.ToList(); + list.Sort(); + foreach (var key in list) + { + sortDictionary.Add(key, queryStringParams[key]); + } + return sortDictionary; + } + + public static string BuildEncodedQueryParams(this IDictionary queryParams) + { + var encodedQueryParam = string.Empty; + foreach (var key in queryParams) + { + encodedQueryParam = (encodedQueryParam != string.Empty) ? + $"{encodedQueryParam}&" : encodedQueryParam; + + var encodedKey = Uri.EscapeUriString(key.Key); // Is this UTF8 encoding or URI encoding + + var encodedValue = (queryParams[key.Key] != string.Empty) + ? Uri.EscapeUriString(queryParams[key.Key]) // same here UTF8 encoding or uri encoding + : ""; + encodedQueryParam = $"{encodedQueryParam}{encodedKey}={encodedValue}"; + } + return encodedQueryParam; + } + + /// + /// Provides an SHA512 hash value of bytes. + /// + /// The byte value for calculating the hash. + /// The UTF8 encoded bytes of SHA512 hash of the input value as a hex-encoded byte array. + public static byte[] AsSha512HashV2(this byte[] value) => + SHA512.Create().ComputeHash(value); + } } diff --git a/src/Medidata.MAuth.Core/MAuthCoreFactory.cs b/src/Medidata.MAuth.Core/MAuthCoreFactory.cs index 3e113d4..762728e 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreFactory.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreFactory.cs @@ -1,10 +1,18 @@ -namespace Medidata.MAuth.Core +using System; +using Version = Medidata.MAuth.Core.Models.Version; + +namespace Medidata.MAuth.Core { internal class MAuthCoreFactory { - public static IMAuthCore Instantiate() + public static IMAuthCore Instantiate(string version = "MWS") { - return new MAuthCore(); + if (version == Version.MWSV2.ToString()) + return new MAuthCoreV2(); + else if (version == Version.MWS.ToString()) + return new MAuthCore(); + + throw new Exception("Version is not recognized"); } } } diff --git a/src/Medidata.MAuth.Core/MAuthCoreExtensionsV2.cs b/src/Medidata.MAuth.Core/MAuthCoreV2.cs similarity index 56% rename from src/Medidata.MAuth.Core/MAuthCoreExtensionsV2.cs rename to src/Medidata.MAuth.Core/MAuthCoreV2.cs index 8c05f7d..2c1f79e 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreExtensionsV2.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreV2.cs @@ -10,39 +10,35 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using Newtonsoft.Json.Linq; +using Org.BouncyCastle.Crypto.Encodings; +using Org.BouncyCastle.Crypto.Engines; namespace Medidata.MAuth.Core { /// /// /// - internal static class MAuthCoreExtensionsV2 + internal class MAuthCoreV2 : IMAuthCore { private const string MwsV2Token = "MWSV2"; - //Response Signing - - // 1) Sign the response body separately from the rest of the response so that it does not have to be read into the memory all at once. - // 2) Force UTF-8 encoding of all text (e.g. status code, app_uuid etc.) - /// - /// Calculates the payload information based on the request and the authentication information. + /// Signs an HTTP request with the MAuth-specific authentication information. /// - /// - /// The request which has the information (method, url and content) for the calculation. - /// - /// - /// The which holds the application uuid, the time of the - /// signature and the private key. - /// - /// A task object which will result the payload as a Base64 encoded string when completed. - public static async Task CalculatePayloadV2( - this HttpRequestMessage request, PrivateKeyAuthenticationInfo authInfo) + /// The HTTP request message to sign. + /// The options that contains the required information for the signing. + /// + /// A Task object which will result the request signed with the authentication information when it completes. + /// + public async Task Sign( + HttpRequestMessage request, MAuthSigningOptions options) { - var unsignedData = await request.GetSignatureV2(authInfo).ConfigureAwait(false); - var signer = new RSACryptoServiceProvider(); - - return Convert.ToBase64String(signer.SignHash(unsignedData, authInfo.PrivateKey)); + return await AddAuthenticationInfo(request, new PrivateKeyAuthenticationInfo() + { + ApplicationUuid = options.ApplicationUuid, + SignedTime = options.SignedTime ?? DateTimeOffset.UtcNow, + PrivateKey = options.PrivateKey.Dereference().NormalizeLines() + }); } /// @@ -56,10 +52,12 @@ public static async Task CalculatePayloadV2( /// If the signed data matches the signature, it returns ; otherwise it /// returns . /// - public static bool VerifyV2(this byte[] signedData, byte[] signature, string publicKey) + public bool Verify(byte[] signedData, byte[] signature, string publicKey) { var rsa = new RSACryptoServiceProvider(); - return rsa.VerifyHash(signedData, publicKey, signature); + rsa.PersistKeyInCsp = false; + rsa.ImportParameters(publicKey.AsRsaParameters()); + return rsa.VerifyHash(signature, CryptoConfig.MapNameToOID("SHA512"), signedData); } /// @@ -72,23 +70,22 @@ public static bool VerifyV2(this byte[] signedData, byte[] signature, string pub /// The which holds the application uuid and the time of the signature. /// /// A Task object which will result the SHA512 hash of the signature when it completes. - public static async Task GetSignatureV2(this HttpRequestMessage request, AuthenticationInfo authInfo) + public async Task GetSignature(HttpRequestMessage request, AuthenticationInfo authInfo) { var encodedHttpVerb = request.Method.Method.ToBytes(); - //var encodedResourceUriPath = Uri.EscapeUriString(request.RequestUri.AbsolutePath); var encodedResourceUriPath = request.RequestUri.AbsolutePath.ToBytes(); var encodedAppUUid = authInfo.ApplicationUuid.ToHyphenString().ToBytes(); var requestBody = request.Content != null ? await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false) : new byte[] { }; // var requestBodyDigest = SHA512 hash of request body - // UTF-8 encode the resulting hash var requestBodyDigest = requestBody.AsSha512HashV2(); - //var requestBodyDigest = Encoding.UTF8.GetString(SHA512.Create().ComputeHash(requestBody)); var encodedCurrentSecondsSinceEpoch = authInfo.SignedTime.ToUnixTimeSeconds().ToString().ToBytes(); - var encodedQueryParams = request.GetQueryStringParams().SortByKeyAscending().BuildEncodedQueryParams().ToBytes(); + var encodedQueryParams = (!string.IsNullOrEmpty(request.RequestUri.Query)) + ? request.RequestUri.Query.GetQueryStringParams().SortByKeyAscending().BuildEncodedQueryParams().ToBytes() + : new byte[] { }; return new byte[][] { @@ -101,33 +98,6 @@ public static async Task GetSignatureV2(this HttpRequestMessage request, }.Concat().AsSha512HashV2(); } - /// - /// Extracts the authentication information from a . - /// - /// The request that has the authentication information. - /// The authentication information with the payload from the request. - public static PayloadAuthenticationInfo GetAuthenticationInfoV2(this HttpRequestMessage request) - { - var authHeader = request.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKeyV2); - - if (authHeader == null) - throw new ArgumentNullException(nameof(authHeader), "The MAuth header is missing from the request."); - - var signedTime = request.Headers.GetFirstValueOrDefault(Constants.MAuthTimeHeaderKeyV2); - - if (signedTime == default(long)) - throw new ArgumentException("Invalid MAuth signed time header value.", nameof(signedTime)); - - var (uuid, payload) = authHeader.ParseAuthenticationHeader(); - - return new PayloadAuthenticationInfo() - { - ApplicationUuid = uuid, - Payload = Convert.FromBase64String(payload), - SignedTime = signedTime.FromUnixTimeSeconds() - }; - } - /// /// Adds authentication information to a . /// @@ -138,12 +108,12 @@ public static PayloadAuthenticationInfo GetAuthenticationInfoV2(this HttpRequest /// /// A Task object which will result the request with the authentication information added when it completes. /// - public static async Task AddAuthenticationInfoV2( - this HttpRequestMessage request, PrivateKeyAuthenticationInfo authInfo) + internal async Task AddAuthenticationInfo( + HttpRequestMessage request, PrivateKeyAuthenticationInfo authInfo) { var authHeader = $"{MwsV2Token} {authInfo.ApplicationUuid.ToHyphenString()}:" + - $"{await request.CalculatePayloadV2(authInfo).ConfigureAwait(false)};"; + $"{await CalculatePayload(request, authInfo).ConfigureAwait(false)};"; request.Headers.Add(Constants.MAuthHeaderKeyV2, authHeader); request.Headers.Add(Constants.MAuthTimeHeaderKeyV2, authInfo.SignedTime.ToUnixTimeSeconds().ToString()); @@ -152,95 +122,52 @@ public static async Task AddAuthenticationInfoV2( } /// - /// Signs an HTTP request with the MAuth-specific authentication information. - /// - /// The HTTP request message to sign. - /// The options that contains the required information for the signing. - /// - /// A Task object which will result the request signed with the authentication information when it completes. - /// - public static async Task SignV2( - this HttpRequestMessage request, MAuthSigningOptions options) - { - return await request.AddAuthenticationInfoV2(new PrivateKeyAuthenticationInfo() - { - ApplicationUuid = options.ApplicationUuid, - SignedTime = options.SignedTime ?? DateTimeOffset.UtcNow, - PrivateKey = options.PrivateKey.Dereference().NormalizeLines() - }); - } - - - /// - /// Deserializes an object from a content of a Http message. + /// Calculates the payload information based on the request and the authentication information. /// - /// The content to deserialize the information from. - /// A Task object which will result the application information when it completes. - public static async Task FromResponseV2(this HttpContent content) + /// + /// The request which has the information (method, url and content) for the calculation. + /// + /// + /// The which holds the application uuid, the time of the + /// signature and the private key. + /// + /// A task object which will result the payload as a Base64 encoded string when completed. + internal async Task CalculatePayload( + HttpRequestMessage request, PrivateKeyAuthenticationInfo authInfo) { - var jsonObject = JObject.Parse(await content.ReadAsStringAsync().ConfigureAwait(false)); + var unsignedData = await GetSignature(request, authInfo).ConfigureAwait(false); + var signer = new RSACryptoServiceProvider(); + signer.PersistKeyInCsp = false; + signer.ImportParameters(authInfo.PrivateKey.AsRsaParameters()); - return jsonObject.GetValue("security_token").ToObject(); + return Convert.ToBase64String(signer.SignHash(unsignedData, CryptoConfig.MapNameToOID("SHA512"))); } /// - /// Provides an SHA512 hash value of bytes. + /// Extracts the authentication information from a . /// - /// The byte value for calculating the hash. - /// The UTF8 encoded bytes of SHA512 hash of the input value as a hex-encoded byte array. - private static byte[] AsSha512HashV2(this byte[] value) => - Encoding.UTF8.GetBytes(SHA512.Create().ComputeHash(value).ToString()); - - private static byte[] ToBytes(this string value) => Encoding.UTF8.GetBytes(value); - - private static byte[] Concat(this byte[][] values) + /// The request that has the authentication information. + /// The authentication information with the payload from the request. + public PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage request) { - var result = new byte[values.Sum(x => x.Length)]; - var offset = 0; - foreach (var value in values) - { - Buffer.BlockCopy(value, 0, result, offset, value.Length); - offset += value.Length; - } - - return result; - } + var authHeader = request.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKeyV2); - private static IDictionary GetQueryStringParams(this HttpRequestMessage request) - { - var query = request.RequestUri.Query; - var dictionary = query.Replace("?", "").Split('&').ToDictionary(x => x.Split('=')[0], x => x.Split('=')[1]); - return dictionary; - } + if (authHeader == null) + throw new ArgumentNullException(nameof(authHeader), "The MAuth header is missing from the request."); - private static IDictionary SortByKeyAscending(this IDictionary queryStringParams) - { - var sortDictionary = new Dictionary(); - var list = queryStringParams.Keys.ToList(); - list.Sort(); - foreach (var key in list) - { - sortDictionary.Add(key, queryStringParams[key]); - } - return sortDictionary; - } + var signedTime = request.Headers.GetFirstValueOrDefault(Constants.MAuthTimeHeaderKeyV2); - private static string BuildEncodedQueryParams(this IDictionary queryParams) - { - var encodedQueryParam = string.Empty; - foreach (var key in queryParams.Keys) - { - encodedQueryParam = (encodedQueryParam != string.Empty) ? - $"{encodedQueryParam}&" : encodedQueryParam; + if (signedTime == default(long)) + throw new ArgumentException("Invalid MAuth signed time header value.", nameof(signedTime)); - var encodedKey = Encoding.UTF8.GetBytes(key); // Is this UTF8 encoding or URI encoding + var (uuid, payload) = authHeader.ParseAuthenticationHeader(); - var encodedValue = (queryParams[key] != string.Empty) - ? Encoding.UTF8.GetBytes(queryParams[key]) // same here UTF8 encoding or uri encoding - : new byte[] { }; - encodedQueryParam = $"{encodedQueryParam}{encodedKey}={encodedValue}"; - } - return encodedQueryParam; + return new PayloadAuthenticationInfo() + { + ApplicationUuid = uuid, + Payload = Convert.FromBase64String(payload), + SignedTime = signedTime.FromUnixTimeSeconds() + }; } } } diff --git a/src/Medidata.MAuth.Core/MAuthRequestRetrier.cs b/src/Medidata.MAuth.Core/MAuthRequestRetrier.cs index 8db78e9..7da7041 100644 --- a/src/Medidata.MAuth.Core/MAuthRequestRetrier.cs +++ b/src/Medidata.MAuth.Core/MAuthRequestRetrier.cs @@ -1,6 +1,7 @@ using System; using System.Net.Http; using System.Threading.Tasks; +using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.Core { @@ -8,12 +9,13 @@ internal class MAuthRequestRetrier { private readonly HttpClient client; - public MAuthRequestRetrier(MAuthOptionsBase options) + public MAuthRequestRetrier(MAuthOptionsBase options, Version version) { var signingHandler = new MAuthSigningHandler(options: new MAuthSigningOptions() { ApplicationUuid = options.ApplicationUuid, - PrivateKey = options.PrivateKey + PrivateKey = options.PrivateKey, + MAuthVersion = version }, innerHandler: options.MAuthServerHandler ?? new HttpClientHandler() ); @@ -25,7 +27,7 @@ public MAuthRequestRetrier(MAuthOptionsBase options) } public async Task GetSuccessfulResponse(Guid applicationUuid, - Func requestFactory, int requestAttempts) + Func requestFactory, string tokenRequestPath, int requestAttempts) { if (requestFactory == null) throw new ArgumentNullException(nameof(requestFactory)); @@ -42,7 +44,7 @@ public async Task GetSuccessfulResponse(Guid applicationUui int remainingAttempts = requestAttempts; while (remainingAttempts > 0) { - var request = requestFactory?.Invoke(applicationUuid); + var request = requestFactory?.Invoke(applicationUuid, tokenRequestPath); if (request == null) throw new ArgumentException( diff --git a/src/Medidata.MAuth.Core/MAuthSigningHandler.cs b/src/Medidata.MAuth.Core/MAuthSigningHandler.cs index 3aafbcd..deb5b57 100644 --- a/src/Medidata.MAuth.Core/MAuthSigningHandler.cs +++ b/src/Medidata.MAuth.Core/MAuthSigningHandler.cs @@ -1,7 +1,9 @@ using System; +using System.Linq.Expressions; using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Medidata.MAuth.Core.Exceptions; using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.Core @@ -55,7 +57,11 @@ protected override async Task SendAsync( if (InnerHandler == null) InnerHandler = new HttpClientHandler(); - mAuthCore = MAuthCoreFactory.Instantiate(); + if(options.DisableV1 && options.MAuthVersion.ToString() == Version.MWS.ToString()) + throw new InvalidVersionException + ($"Signing with {options.MAuthVersion.ToString()} is no longer supported."); + + mAuthCore = MAuthCoreFactory.Instantiate(options.MAuthVersion.ToString()); return await base .SendAsync(await mAuthCore.Sign(request, options).ConfigureAwait(false), cancellationToken) diff --git a/src/Medidata.MAuth.Core/Models/MAuthVersion.cs b/src/Medidata.MAuth.Core/Models/Version.cs similarity index 71% rename from src/Medidata.MAuth.Core/Models/MAuthVersion.cs rename to src/Medidata.MAuth.Core/Models/Version.cs index 1889f14..0cb1cce 100644 --- a/src/Medidata.MAuth.Core/Models/MAuthVersion.cs +++ b/src/Medidata.MAuth.Core/Models/Version.cs @@ -10,12 +10,12 @@ public enum Version /// /// /// - [EnumMember(Value = "V1")] - V1 = 0, + [EnumMember(Value = "MWS")] + MWS = 0, /// /// /// - [EnumMember(Value = "V2")] - V2 = 1, + [EnumMember(Value = "MWSV2")] + MWSV2 = 1, } } diff --git a/src/Medidata.MAuth.Core/Options/MAuthOptionsBase.cs b/src/Medidata.MAuth.Core/Options/MAuthOptionsBase.cs index a432a8b..54ba52c 100644 --- a/src/Medidata.MAuth.Core/Options/MAuthOptionsBase.cs +++ b/src/Medidata.MAuth.Core/Options/MAuthOptionsBase.cs @@ -1,5 +1,6 @@ using System; using System.Net.Http; +using Medidata.MAuth.Core.Models; using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.Core @@ -38,11 +39,11 @@ public abstract class MAuthOptionsBase /// /// Determines the message handler for the requests to the MAuth server. /// - public HttpMessageHandler MAuthServerHandler { get; set; } + public HttpMessageHandler MAuthServerHandler { get; set; } /// - /// + /// Determines the boolean value if V1 option of signing should be disabled or not with default value of false. /// - public Version MAuthVersion { get; set; } + public bool DisableV1 { get; set; } = false; } } diff --git a/src/Medidata.MAuth.Core/Options/MAuthSigningOptions.cs b/src/Medidata.MAuth.Core/Options/MAuthSigningOptions.cs index 37a6b03..936cecf 100644 --- a/src/Medidata.MAuth.Core/Options/MAuthSigningOptions.cs +++ b/src/Medidata.MAuth.Core/Options/MAuthSigningOptions.cs @@ -23,8 +23,13 @@ public class MAuthSigningOptions internal DateTimeOffset? SignedTime { get; set; } /// - /// + /// Determines the MAuth version for signing requests. /// public Version MAuthVersion { get; set; } + + /// + /// Determines the boolean value if V1 option of signing should be disabled or not with default value of false. + /// + public bool DisableV1 { get; set; } = false; } } diff --git a/src/Medidata.MAuth.Core/UtilityExtensions.cs b/src/Medidata.MAuth.Core/UtilityExtensions.cs index a217362..1debbe2 100644 --- a/src/Medidata.MAuth.Core/UtilityExtensions.cs +++ b/src/Medidata.MAuth.Core/UtilityExtensions.cs @@ -63,12 +63,60 @@ public static bool TryParseAuthenticationHeader(this string headerValue, /// /// Authenticates a with the provided options. /// - /// The requesst message to authenticate. + /// The request message to authenticate. /// The MAuth options to use for the authentication. /// The task for the operation that is when completes will result in if /// the authentication is successful; otherwise . public static Task Authenticate(this HttpRequestMessage request, MAuthOptionsBase options) => - options.MAuthVersion.Equals(Version.V2) ? new MAuthAuthenticator(options).AuthenticateRequestV2(request): new MAuthAuthenticator(options).AuthenticateRequest(request); + + /// + /// + /// + /// + /// + public static string GetVersionFromAuthenticationHeader(this string authHeader) + { + return authHeader.StartsWith(Version.MWSV2.ToString()) + ? Version.MWSV2.ToString() : Version.MWS.ToString(); + } + + /// + /// Determines the correct MAuthHeader value + /// + /// + /// The MAuthHeader value. + public static string GetAuthHeaderValue(this HttpRequestMessage request) + { + //By default first check for V2 + var authHeader = request.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKeyV2) + ?? request.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKey); + if (authHeader == null) + throw new ArgumentNullException(nameof(authHeader), "The MAuth header is missing from the request."); + return authHeader; + } + + /// + /// Determines the correct token request path based on version + /// + /// + /// The correct token request path + public static string GetMAuthTokenRequestPath(this string version) + { + return (version == Version.MWSV2.ToString()) ? + Constants.MAuthTokenRequestPathV2 : Constants.MAuthTokenRequestPath; + } + + /// + /// + /// + /// + /// + public static (string mAuthHeaderKey, string mAuthTimeHeaderKey) GetHeaderKeys(this string version) + { + return (version == Version.MWSV2.ToString()) + ? (Constants.MAuthHeaderKeyV2, Constants.MAuthTimeHeaderKeyV2) + : (Constants.MAuthHeaderKey, Constants.MAuthTimeHeaderKey); + } } } diff --git a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs index 5ac07c9..3883f9e 100644 --- a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs @@ -1,7 +1,6 @@ using System.Net; using System.Threading.Tasks; using Medidata.MAuth.Core; -using Medidata.MAuth.Core.Models; using Microsoft.Owin; namespace Medidata.MAuth.Owin @@ -22,7 +21,7 @@ public override async Task Invoke(IOwinContext context) await context.EnsureRequestBodyStreamSeekable(); if (!options.Bypass(context.Request) && - !await context.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized, options.MAuthVersion)) + !await context.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized)) { context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; return; diff --git a/src/Medidata.MAuth.Owin/MAuthOwinExtensions.cs b/src/Medidata.MAuth.Owin/MAuthOwinExtensions.cs index 3795bd9..b4d272e 100644 --- a/src/Medidata.MAuth.Owin/MAuthOwinExtensions.cs +++ b/src/Medidata.MAuth.Owin/MAuthOwinExtensions.cs @@ -3,9 +3,7 @@ using System.Net.Http; using System.Threading.Tasks; using Medidata.MAuth.Core; -using Medidata.MAuth.Core.Models; using Microsoft.Owin; -using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.Owin { @@ -40,19 +38,15 @@ public static HttpRequestMessage ToHttpRequestMessage(this IOwinRequest request) /// The authenticator which will attempt the request authentication. /// Determines if any exceptions during the authentication /// should be thrown. - /// Determines if the authentication uses V2 or V1 method /// /// This method returns if it successfully authenticated the request; /// otherwise it will return either if the method should ignore exceptions or /// will throw an exception if any errors occurred during the authentication. public static async Task TryAuthenticate( - this IOwinContext context, MAuthAuthenticator authenticator, bool shouldIgnoreExceptions, Enum mAuthVersion) + this IOwinContext context, MAuthAuthenticator authenticator, bool shouldIgnoreExceptions) { try { - if (mAuthVersion.Equals(Version.V2)) - return await authenticator.AuthenticateRequestV2(context.Request.ToHttpRequestMessage()); - return await authenticator.AuthenticateRequest(context.Request.ToHttpRequestMessage()); } catch (Exception) diff --git a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs index 3ed934f..21a22dc 100644 --- a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs +++ b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs @@ -4,8 +4,6 @@ using System.Threading; using System.Threading.Tasks; using Medidata.MAuth.Core; -using Medidata.MAuth.Core.Models; -using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.WebApi { @@ -61,7 +59,7 @@ protected override async Task SendAsync( if (InnerHandler == null) InnerHandler = new HttpClientHandler(); - if (!await request.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized, options.MAuthVersion)) + if (!await request.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized)) return new HttpResponseMessage(HttpStatusCode.Unauthorized) { RequestMessage = request }; return await base diff --git a/src/Medidata.MAuth.WebApi/MAuthWebApiExtensions.cs b/src/Medidata.MAuth.WebApi/MAuthWebApiExtensions.cs index faa518c..40b69bd 100644 --- a/src/Medidata.MAuth.WebApi/MAuthWebApiExtensions.cs +++ b/src/Medidata.MAuth.WebApi/MAuthWebApiExtensions.cs @@ -2,7 +2,6 @@ using System; using System.Net.Http; using System.Threading.Tasks; -using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.WebApi { @@ -16,19 +15,15 @@ internal static class MAuthWebApiExtensions /// The authenticator which will attempt the request authentication. /// Determines if any exceptions during the authentication /// should be thrown. - /// Determines if the authentication uses V2 or V1 method /// /// This method returns if it successfully authenticated the request; /// otherwise it will return either if the method should ignore exceptions or /// will throw an exception if any errors occurred during the authentication. public static async Task TryAuthenticate( - this HttpRequestMessage request, MAuthAuthenticator authenticator, bool shouldIgnoreExceptions, Enum mAuthVersion) + this HttpRequestMessage request, MAuthAuthenticator authenticator, bool shouldIgnoreExceptions) { try { - if (mAuthVersion.Equals(Version.V2)) - return await authenticator.AuthenticateRequestV2(request); - return await authenticator.AuthenticateRequest(request); } catch (Exception) diff --git a/tests/Medidata.MAuth.Tests/Infrastructure/AssertSigningHandler.cs b/tests/Medidata.MAuth.Tests/Infrastructure/AssertSigningHandler.cs index ac9d935..ec7a3c2 100644 --- a/tests/Medidata.MAuth.Tests/Infrastructure/AssertSigningHandler.cs +++ b/tests/Medidata.MAuth.Tests/Infrastructure/AssertSigningHandler.cs @@ -19,8 +19,13 @@ internal class AssertSigningHandler : HttpMessageHandler protected override Task SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { - MAuthHeader = request.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKey); - MAuthTimeHeader = request.Headers.GetFirstValueOrDefault(Constants.MAuthTimeHeaderKey); + MAuthHeader = request.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKeyV2) ?? + request.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKey); + + var timeHeader = request.Headers.GetFirstValueOrDefault(Constants.MAuthTimeHeaderKeyV2) ?? + request.Headers.GetFirstValueOrDefault(Constants.MAuthTimeHeaderKey); + + MAuthTimeHeader = long.Parse(timeHeader); return Task.Run(() => new HttpResponseMessage(HttpStatusCode.OK)); } diff --git a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs index 534bf50..31a5f3c 100644 --- a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs +++ b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs @@ -19,14 +19,13 @@ protected override async Task SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { currentNumberOfAttempts += 1; - var mAuthCore = MAuthCoreFactory.Instantiate(); + var version = request.GetAuthHeaderValue().GetVersionFromAuthenticationHeader(); + var mAuthCore = MAuthCoreFactory.Instantiate(version); if (currentNumberOfAttempts < SucceedAfterThisManyAttempts) return new HttpResponseMessage(HttpStatusCode.ServiceUnavailable); - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); - - var authInfo = authenticator.GetAuthenticationInfo(request); + var authInfo = mAuthCore.GetAuthenticationInfo(request); if (!mAuthCore.Verify(authInfo.Payload, await mAuthCore.GetSignature(request, authInfo), @@ -34,8 +33,10 @@ await mAuthCore.GetSignature(request, authInfo), )) return new HttpResponseMessage(HttpStatusCode.Unauthorized) { RequestMessage = request }; + var tokenRequestPath = version.GetMAuthTokenRequestPath(); + if (!request.RequestUri.AbsolutePath.Equals( - $"{Constants.MAuthTokenRequestPath}{clientUuid.ToHyphenString()}.json", + $"{tokenRequestPath}{clientUuid.ToHyphenString()}.json", StringComparison.OrdinalIgnoreCase)) return new HttpResponseMessage(HttpStatusCode.NotFound) { RequestMessage = request }; diff --git a/tests/Medidata.MAuth.Tests/Infrastructure/RequestData.cs b/tests/Medidata.MAuth.Tests/Infrastructure/RequestData.cs index 2f31944..6eef43b 100644 --- a/tests/Medidata.MAuth.Tests/Infrastructure/RequestData.cs +++ b/tests/Medidata.MAuth.Tests/Infrastructure/RequestData.cs @@ -34,5 +34,8 @@ internal class RequestData [IgnoreDataMember] public string ApplicationUuidString => ApplicationUuid.ToHyphenString(); + + [IgnoreDataMember] + public string MAuthHeaderV2 => $"MWSV2 {ApplicationUuidString}:{Payload};"; } } diff --git a/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs b/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs index fccf61a..2b82ae2 100644 --- a/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs +++ b/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Medidata.MAuth.Core; using Newtonsoft.Json; +using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.Tests.Infrastructure { @@ -65,12 +66,17 @@ public static async Task FromResource(this string requestDataName) await GetStringFromResource($"Medidata.MAuth.Tests.Mocks.RequestData.{requestDataName}.json") ); + public static async Task FromResourceV2(this string requestDataName) => + JsonConvert.DeserializeObject( + await GetStringFromResource($"Medidata.MAuth.Tests.Mocks.RequestDataV2.{requestDataName}.json") + ); + private static Task GetKeyFromResource(string keyName) { return GetStringFromResource($"Medidata.MAuth.Tests.Mocks.Keys.{keyName}.pem"); } - public static HttpRequestMessage ToHttpRequestMessage(this RequestData data) + public static HttpRequestMessage ToHttpRequestMessage(this RequestData data, string version = "MWS") { var result = new HttpRequestMessage(new HttpMethod(data.Method), data.Url) { @@ -78,9 +84,13 @@ public static HttpRequestMessage ToHttpRequestMessage(this RequestData data) new ByteArrayContent(Convert.FromBase64String(data.Base64Content)) : null, }; + var headerKeys = version.GetHeaderKeys(); + var mauthHeader = version == "MWS" + ? $"{version} {data.ApplicationUuidString}:{data.Payload}" + : $"{version} {data.ApplicationUuidString}:{data.Payload};"; - result.Headers.Add(Constants.MAuthHeaderKey, $"MWS {data.ApplicationUuidString}:{data.Payload}"); - result.Headers.Add(Constants.MAuthTimeHeaderKey, data.SignedTimeUnixSeconds.ToString()); + result.Headers.Add(headerKeys.mAuthHeaderKey, mauthHeader); + result.Headers.Add(headerKeys.mAuthTimeHeaderKey, data.SignedTimeUnixSeconds.ToString()); return result; } diff --git a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs index d368fd4..8151393 100644 --- a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs @@ -1,9 +1,9 @@ using System; using System.Linq; using System.Net; -using System.Net.Http; using System.Threading.Tasks; using Medidata.MAuth.Core; +using Medidata.MAuth.Core.Exceptions; using Medidata.MAuth.Tests.Infrastructure; using Xunit; @@ -58,6 +58,34 @@ public static async Task AuthenticateRequest_WithValidRequest_WillAuthenticate(s Assert.True(isAuthenticated); } + [Theory] + [InlineData("GET")] + [InlineData("DELETE")] + [InlineData("POST")] + [InlineData("PUT")] + public static async Task AuthenticateRequest_WithValidMWSV2Request_WillAuthenticate(string method) + { + // Arrange + var testData = await method.FromResourceV2(); + var version = "MWSV2"; + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); + var mAuthCore = new MAuthCoreV2(); + + var signedRequest = await mAuthCore + .AddAuthenticationInfo(testData.ToHttpRequestMessage(version), new PrivateKeyAuthenticationInfo() + { + ApplicationUuid = testData.ApplicationUuid, + PrivateKey = TestExtensions.ClientPrivateKey, + SignedTime = testData.SignedTime + }); + + // Act + var isAuthenticated = await authenticator.AuthenticateRequest(signedRequest); + + // Assert + Assert.True(isAuthenticated); + } + [Theory] [InlineData(MAuthServiceRetryPolicy.NoRetry)] @@ -89,6 +117,36 @@ public static async Task AuthenticateRequest_WithNumberOfAttempts_WillAuthentica Assert.True(isAuthenticated); } + [Theory] + [InlineData(MAuthServiceRetryPolicy.NoRetry)] + [InlineData(MAuthServiceRetryPolicy.RetryOnce)] + [InlineData(MAuthServiceRetryPolicy.RetryTwice)] + [InlineData(MAuthServiceRetryPolicy.Agressive)] + public static async Task AuthenticateRequest_WithMWSV2Request_WithNumberOfAttempts_WillAuthenticate( + MAuthServiceRetryPolicy policy) + { + // Arrange + var testData = await "GET".FromResourceV2(); + var version = "MWSV2"; + var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( + policy, shouldSucceedWithin: true)); + var mAuthCore = new MAuthCoreV2(); + + var signedRequest = await mAuthCore + .AddAuthenticationInfo(testData.ToHttpRequestMessage(version), new PrivateKeyAuthenticationInfo() + { + ApplicationUuid = testData.ApplicationUuid, + PrivateKey = TestExtensions.ClientPrivateKey, + SignedTime = testData.SignedTime + }); + + // Act + var isAuthenticated = await authenticator.AuthenticateRequest(signedRequest); + + // Assert + Assert.True(isAuthenticated); + } + [Theory] [InlineData(MAuthServiceRetryPolicy.NoRetry)] [InlineData(MAuthServiceRetryPolicy.RetryOnce)] @@ -124,12 +182,47 @@ public static async Task AuthenticateRequest_AfterNumberOfAttempts_WillThrowExce Assert.Equal(HttpStatusCode.ServiceUnavailable, innerException.Responses.First().StatusCode); } + [Theory] + [InlineData(MAuthServiceRetryPolicy.NoRetry)] + [InlineData(MAuthServiceRetryPolicy.RetryOnce)] + [InlineData(MAuthServiceRetryPolicy.RetryTwice)] + [InlineData(MAuthServiceRetryPolicy.Agressive)] + public static async Task AuthenticateRequest_WithMWSV2Request_AfterNumberOfAttempts_WillThrowExceptionWithRequestFailure( + MAuthServiceRetryPolicy policy) + { + // Arrange + var testData = await "GET".FromResource(); + var version = "MWSV2"; + var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( + policy, shouldSucceedWithin: false)); + var mAuthCore = new MAuthCoreV2(); + + var signedRequest = await mAuthCore + .AddAuthenticationInfo(testData.ToHttpRequestMessage(version), new PrivateKeyAuthenticationInfo() + { + ApplicationUuid = testData.ApplicationUuid, + PrivateKey = TestExtensions.ClientPrivateKey, + SignedTime = testData.SignedTime + }); + + // Act + var exception = (await Assert.ThrowsAsync( + () => authenticator.AuthenticateRequest(signedRequest))); + + var innerException = exception.InnerException as RetriedRequestException; + + // Assert + Assert.NotNull(innerException); + Assert.Equal((int)policy + 1, innerException.Responses.Count); + Assert.Equal(HttpStatusCode.ServiceUnavailable, innerException.Responses.First().StatusCode); + } + [Theory] [InlineData("GET")] [InlineData("DELETE")] [InlineData("POST")] [InlineData("PUT")] - public static async Task SignRequest_WithValidRequest_WillSignProperly(string method) + public static async Task SignRequest_WithMWSValidRequest_WillSignProperly(string method) { // Arrange var testData = await method.FromResource(); @@ -152,24 +245,55 @@ public static async Task SignRequest_WithValidRequest_WillSignProperly(string me [InlineData("DELETE")] [InlineData("POST")] [InlineData("PUT")] - public static async Task GetAuthenticationInfo_WithSignedRequest_WillReturnCorrectAuthInfo(string method) + public static async Task SignRequest_WithMWSV2ValidRequest_WillSignProperly(string method) + { + // Arrange + var testData = await method.FromResourceV2(); + var version = "MWSV2"; + var expectedMAuthHeader = testData.MAuthHeaderV2; + var mAuthCore = new MAuthCoreV2(); + + // Act + var actual = await mAuthCore.Sign(testData.ToHttpRequestMessage(version), TestExtensions.ClientOptions(testData.SignedTime)); + + // Assert + Assert.Equal(expectedMAuthHeader, actual.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKeyV2)); + Assert.Equal( + testData.SignedTime.ToUnixTimeSeconds(), + actual.Headers.GetFirstValueOrDefault(Constants.MAuthTimeHeaderKeyV2) + ); + } + + [Theory] + [InlineData("GET")] + [InlineData("DELETE")] + [InlineData("POST")] + [InlineData("PUT")] + public static async Task AuthenticateRequest_WithMWSVersion_WithDisableV1_WillThrowExceptionWithInvalidVersion( + string method) { // Arrange - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); var testData = await method.FromResource(); - var request = new HttpRequestMessage(new HttpMethod(testData.Method), TestExtensions.TestUri); + var testOptions = TestExtensions.ServerOptions; + testOptions.DisableV1 = true; + var authenticator = new MAuthAuthenticator(testOptions); + var mAuthCore = new MAuthCore(); - request.Headers.Add( - Constants.MAuthHeaderKey, testData.MAuthHeader); - request.Headers.Add(Constants.MAuthTimeHeaderKey, testData.SignedTimeUnixSeconds.ToString()); + var signedRequest = await mAuthCore + .AddAuthenticationInfo(testData.ToHttpRequestMessage(), new PrivateKeyAuthenticationInfo() + { + ApplicationUuid = testData.ApplicationUuid, + PrivateKey = TestExtensions.ClientPrivateKey, + SignedTime = testData.SignedTime + }); // Act - var actual = authenticator.GetAuthenticationInfo(request); + var exception = (await Assert.ThrowsAsync( + () => authenticator.AuthenticateRequest(signedRequest))); // Assert - Assert.Equal(testData.ApplicationUuid, actual.ApplicationUuid); - Assert.Equal(Convert.FromBase64String(testData.Payload), actual.Payload); - Assert.Equal(testData.SignedTime, actual.SignedTime); + Assert.NotNull(exception); + Assert.Equal("Authentication with MWS version is no longer supported.", exception.Message); } } } diff --git a/tests/Medidata.MAuth.Tests/MAuthCoreTests.cs b/tests/Medidata.MAuth.Tests/MAuthCoreTests.cs index 96147fb..059f62d 100644 --- a/tests/Medidata.MAuth.Tests/MAuthCoreTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthCoreTests.cs @@ -52,7 +52,7 @@ public static void Verify_WithCorrectlySignedData_WillVerifyTheDataAsValid() var mAuthCore = new MAuthCore(); // Act - var result = mAuthCore.Verify(signedData, Encoding.UTF8.GetBytes(signature), TestExtensions.ClientPublicKey); + var result = mAuthCore.Verify(signedData, unsignedData, TestExtensions.ClientPublicKey); // Assert Assert.True(result); @@ -154,5 +154,31 @@ public static void AsCipherParameters_WithDifferentLineEnding_WillReadTheKeysSuc // Assert Assert.Null(exception); } + + [Theory] + [InlineData("GET")] + [InlineData("DELETE")] + [InlineData("POST")] + [InlineData("PUT")] + public static async Task GetAuthenticationInfo_WithSignedRequest_WillReturnCorrectAuthInfo(string method) + { + // Arrange + var testData = await method.FromResource(); + var request = new HttpRequestMessage(new HttpMethod(testData.Method), TestExtensions.TestUri); + + request.Headers.Add( + Constants.MAuthHeaderKey, testData.MAuthHeader); + request.Headers.Add(Constants.MAuthTimeHeaderKey, testData.SignedTimeUnixSeconds.ToString()); + var mAuthCore = new MAuthCore(); + + // Act + var actual = mAuthCore.GetAuthenticationInfo(request); + + // Assert + Assert.Equal(testData.ApplicationUuid, actual.ApplicationUuid); + Assert.Equal(Convert.FromBase64String(testData.Payload), actual.Payload); + Assert.Equal(testData.SignedTime, actual.SignedTime); + } + } } diff --git a/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs b/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs new file mode 100644 index 0000000..a1b8b22 --- /dev/null +++ b/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs @@ -0,0 +1,176 @@ +using System; +using System.Net.Http; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using Medidata.MAuth.Core; +using Medidata.MAuth.Tests.Infrastructure; +using Xunit; + +namespace Medidata.MAuth.Tests +{ + public class MAuthCoreV2Tests + { + + [Theory] + [InlineData("GET")] + [InlineData("DELETE")] + [InlineData("POST")] + [InlineData("PUT")] + public async Task CalculatePayload_WithRequestAndAuthInfo_WillReturnCorrectPayload(string method) + { + // Arrange + var testData = await method.FromResourceV2(); + var version = "MWSV2"; + var mAuthCore = new MAuthCoreV2(); + + var authInfo = new PrivateKeyAuthenticationInfo() + { + ApplicationUuid = testData.ApplicationUuid, + SignedTime = testData.SignedTime, + PrivateKey = TestExtensions.ClientPrivateKey + }; + + // Act + var result = await mAuthCore.CalculatePayload(testData.ToHttpRequestMessage(version), authInfo); + + // Assert + Assert.Equal(testData.Payload, result); + } + + [Fact] + public static async Task CalculatePayload_WithBinaryContent_WillCalculateTheProperPayload() + { + // Arrange + var testData = await "POSTWithBinaryData".FromResourceV2(); + var mAuthCore = new MAuthCoreV2(); + var version = "MWSV2"; + // Act + var result = await mAuthCore.CalculatePayload(testData.ToHttpRequestMessage(version), new PrivateKeyAuthenticationInfo() + { + ApplicationUuid = testData.ApplicationUuid, + SignedTime = testData.SignedTime, + PrivateKey = TestExtensions.ClientPrivateKey + }); + + // Assert + Assert.Equal(testData.Payload, result); + } + + [Fact] + public static void Verify_WithCorrectlySignedData_WillVerifyTheDataAsValid() + { + // Arrange + var signature = "This is a signature."; + var unsignedData = signature.ToBytes().AsSha512HashV2(); + var mAuthCore = new MAuthCoreV2(); + + var signer = new RSACryptoServiceProvider(); + signer.PersistKeyInCsp = false; + signer.ImportParameters(TestExtensions.ClientPrivateKey.AsRsaParameters()); + var signedData = signer.SignHash(unsignedData, CryptoConfig.MapNameToOID("SHA512")); + + // Act + var result = mAuthCore.Verify(signedData, unsignedData, TestExtensions.ClientPublicKey); + + // Assert + Assert.True(result); + } + + [Theory] + [InlineData("GET")] + [InlineData("DELETE")] + [InlineData("POST")] + [InlineData("PUT")] + public static async Task GetSignature_WithRequest_WillReturnTheCorrectSignature(string method) + { + // Arrange + var testData = await method.FromResourceV2(); + var version = "MWSV2"; + var mAuthCore = new MAuthCoreV2(); + var queryParams = !string.IsNullOrEmpty(testData.Url.Query) ? + testData.Url.Query.GetQueryStringParams().SortByKeyAscending().BuildEncodedQueryParams().ToBytes(): new byte[] { }; + var content = !string.IsNullOrEmpty(testData.Base64Content) ? + Convert.FromBase64String(testData.Base64Content) + : new byte[] { }; + + var expectedSignature = new byte[][] + { + testData.Method.ToBytes(), Constants.NewLine, + testData.Url.AbsolutePath.ToBytes(), Constants.NewLine, + content.AsSha512HashV2(), Constants.NewLine, + testData.ApplicationUuidString.ToBytes(), Constants.NewLine, + testData.SignedTimeUnixSeconds.ToString().ToBytes(), Constants.NewLine, + queryParams + }.Concat().AsSha512HashV2(); + + var authInfo = new PrivateKeyAuthenticationInfo() + { + ApplicationUuid = testData.ApplicationUuid, + SignedTime = testData.SignedTime + }; + + // Act + var result = await mAuthCore.GetSignature(testData.ToHttpRequestMessage(version), authInfo); + + // Assert + Assert.Equal(expectedSignature, result); + } + + [Theory] + [InlineData("GET")] + [InlineData("DELETE")] + [InlineData("POST")] + [InlineData("PUT")] + public static async Task AddAuthenticationInfo_WithRequestAndAuthInfo_WillAddCorrectInformation(string method) + { + // Arrange + var testData = await method.FromResourceV2(); + var expectedMAuthHeader = testData.MAuthHeaderV2; + var version = "MWSV2"; + var mAuthCore = new MAuthCoreV2(); + + var authInfo = new PrivateKeyAuthenticationInfo() + { + ApplicationUuid = testData.ApplicationUuid, + SignedTime = testData.SignedTime, + PrivateKey = TestExtensions.ClientPrivateKey + }; + + // Act + var actual = await mAuthCore.AddAuthenticationInfo(testData.ToHttpRequestMessage(version), authInfo); + + // Assert + Assert.Equal(expectedMAuthHeader, actual.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKeyV2)); + Assert.Equal( + authInfo.SignedTime.ToUnixTimeSeconds(), + actual.Headers.GetFirstValueOrDefault(Constants.MAuthTimeHeaderKeyV2) + ); + } + + [Theory] + [InlineData("GET")] + [InlineData("DELETE")] + [InlineData("POST")] + [InlineData("PUT")] + public static async Task GetAuthenticationInfo_WithSignedRequest_WillReturnCorrectAuthInfo(string method) + { + // Arrange + var testData = await method.FromResourceV2(); + var request = new HttpRequestMessage(new HttpMethod(testData.Method), TestExtensions.TestUri); + + request.Headers.Add( + Constants.MAuthHeaderKeyV2, testData.MAuthHeaderV2); + request.Headers.Add(Constants.MAuthTimeHeaderKeyV2, testData.SignedTimeUnixSeconds.ToString()); + var mAuthCore = new MAuthCoreV2(); + + // Act + var actual = mAuthCore.GetAuthenticationInfo(request); + + // Assert + Assert.Equal(testData.ApplicationUuid, actual.ApplicationUuid); + Assert.Equal(Convert.FromBase64String(testData.Payload), actual.Payload); + Assert.Equal(testData.SignedTime, actual.SignedTime); + } + } +} diff --git a/tests/Medidata.MAuth.Tests/MAuthSigningHandlerTests.cs b/tests/Medidata.MAuth.Tests/MAuthSigningHandlerTests.cs index cd3a306..0e4e365 100644 --- a/tests/Medidata.MAuth.Tests/MAuthSigningHandlerTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthSigningHandlerTests.cs @@ -1,8 +1,10 @@ using System.Net.Http; using System.Threading.Tasks; using Medidata.MAuth.Core; +using Medidata.MAuth.Core.Exceptions; using Medidata.MAuth.Tests.Infrastructure; using Xunit; +using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.Tests { @@ -13,7 +15,7 @@ public static class MAuthSigningHandlerTests [InlineData("DELETE")] [InlineData("POST")] [InlineData("PUT")] - public static async Task SendAsync_WithValidRequest_WillSignProperly(string method) + public static async Task SendAsync_WithValidMWSRequest_WillSignProperly(string method) { // Arrange var testData = await method.FromResource(); @@ -30,5 +32,54 @@ public static async Task SendAsync_WithValidRequest_WillSignProperly(string meth Assert.Equal(testData.MAuthHeader, actual.MAuthHeader); Assert.Equal(testData.SignedTime, actual.MAuthTimeHeader.FromUnixTimeSeconds()); } + + [Theory] + [InlineData("GET")] + [InlineData("DELETE")] + [InlineData("POST")] + [InlineData("PUT")] + public static async Task SendAsync_WithValidMWSV2Request_WillSignProperly(string method) + { + // Arrange + var testData = await method.FromResource(); + var version = "MWSV2"; + var actual = new AssertSigningHandler(); + var clientOptions = TestExtensions.ClientOptions(testData.SignedTime); + clientOptions.MAuthVersion = Version.MWSV2; + var signingHandler = new MAuthSigningHandler(clientOptions, actual); + + // Act + using (var client = new HttpClient(signingHandler)) + { + await client.SendAsync(testData.ToHttpRequestMessage(version)); + } + + // Assert + Assert.Equal(testData.MAuthHeaderV2, actual.MAuthHeader); + Assert.Equal(testData.SignedTime, actual.MAuthTimeHeader.FromUnixTimeSeconds()); + } + + [Theory] + [InlineData("GET")] + [InlineData("DELETE")] + [InlineData("POST")] + [InlineData("PUT")] + public static async Task SendAsync_WithMWSRequest_WithDisableV1_WillThrowInvalidVersionException(string method) + { + // Arrange + var testData = await method.FromResource(); + var actual = new AssertSigningHandler(); + var clientOptions = TestExtensions.ClientOptions(testData.SignedTime); + clientOptions.DisableV1 = true; + clientOptions.MAuthVersion = Version.MWS; + var signingHandler = new MAuthSigningHandler(clientOptions, actual); + + // Act, Assert + using (var client = new HttpClient(signingHandler)) + { + var exception = (await Assert.ThrowsAsync( + () => client.SendAsync(testData.ToHttpRequestMessage()))); + } + } } } diff --git a/tests/Medidata.MAuth.Tests/MAuthWebApiTests.cs b/tests/Medidata.MAuth.Tests/MAuthWebApiTests.cs index 4004b0b..27b9e86 100644 --- a/tests/Medidata.MAuth.Tests/MAuthWebApiTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthWebApiTests.cs @@ -15,7 +15,7 @@ public static class MAuthWebApiTests [InlineData("DELETE")] [InlineData("POST")] [InlineData("PUT")] - public static async Task MAuthAuthenticatingHandler_WithValidRequest_WillAuthenticate(string method) + public static async Task MAuthAuthenticatingHandler_WithValidMWSRequest_WillAuthenticate(string method) { // Arrange var testData = await method.FromResource(); @@ -41,6 +41,37 @@ public static async Task MAuthAuthenticatingHandler_WithValidRequest_WillAuthent } } + [Theory] + [InlineData("GET")] + [InlineData("DELETE")] + [InlineData("POST")] + [InlineData("PUT")] + public static async Task MAuthAuthenticatingHandler_WithValidMWSV2Request_WillAuthenticate(string method) + { + // Arrange + var testData = await method.FromResourceV2(); + var actual = new AssertSigningHandler(); + var version = "MWSV2"; + var handler = new MAuthAuthenticatingHandler(new MAuthWebApiOptions() + { + ApplicationUuid = TestExtensions.ServerUuid, + MAuthServiceUrl = TestExtensions.TestUri, + PrivateKey = TestExtensions.ServerPrivateKey, + MAuthServerHandler = new MAuthServerHandler() + }, actual); + + using (var server = new HttpClient(handler)) + { + // Act + var response = await server.SendAsync(testData.ToHttpRequestMessage(version)); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(testData.MAuthHeaderV2, actual.MAuthHeader); + Assert.Equal(testData.SignedTime, actual.MAuthTimeHeader.FromUnixTimeSeconds()); + } + } + [Theory] [InlineData("GET")] [InlineData("DELETE")] diff --git a/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj b/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj index e432750..d7c6251 100644 --- a/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj +++ b/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj @@ -12,6 +12,11 @@ + + + + + @@ -25,6 +30,11 @@ + + + + + diff --git a/tests/Medidata.MAuth.Tests/Mocks/RequestData/DELETE.json b/tests/Medidata.MAuth.Tests/Mocks/RequestData/DELETE.json index e674ac3..8138753 100644 --- a/tests/Medidata.MAuth.Tests/Mocks/RequestData/DELETE.json +++ b/tests/Medidata.MAuth.Tests/Mocks/RequestData/DELETE.json @@ -3,5 +3,6 @@ "Method": "DELETE", "ApplicationUuid": "192cce84-8466-490e-b03e-074f82da3ee2", "SignedTime": "2016-08-01 05:40:38 +0000", - "Payload": "C6vv/XIurgTnbp3YMPcejteTrVfXtb3NY/2A9NFmiMh85uDQ9rnZr1fXPdmX5tQX2rUh5g3KT65ky3c4gi0/fwKJbFLCv+AoTtNl1jTwam4ZBp76/8pd1WSEnXBMyKu9qpCpnEhxQZLJkBTjmg1gyin4/r+slxDfktwSYPUkkeK41ewgwudI/t76qZOE9kX2VZpqHAgGBKRACbOpclmkdtMQ3Fbg/pSWrPN5ve34+TCNZbcqPI7E0dRfAzcAOD5DNftVcv2jKy3vJ68J0HnvdDIj03xVemjMRewDveD4OG4DGjP6HF4MouYy0QnbWIE+EVvUFRy81xhneTR10yDcYA==" + "Payload": "C6vv/XIurgTnbp3YMPcejteTrVfXtb3NY/2A9NFmiMh85uDQ9rnZr1fXPdmX5tQX2rUh5g3KT65ky3c4gi0/fwKJbFLCv+AoTtNl1jTwam4ZBp76/8pd1WSEnXBMyKu9qpCpnEhxQZLJkBTjmg1gyin4/r+slxDfktwSYPUkkeK41ewgwudI/t76qZOE9kX2VZpqHAgGBKRACbOpclmkdtMQ3Fbg/pSWrPN5ve34+TCNZbcqPI7E0dRfAzcAOD5DNftVcv2jKy3vJ68J0HnvdDIj03xVemjMRewDveD4OG4DGjP6HF4MouYy0QnbWIE+EVvUFRy81xhneTR10yDcYA==", + "PayloadV2": "ZDU3p0J5Tate1lJxtSixlIxeiTUEVkWXQ3VEKuwcQUeFxzfDsy4G6A8rheVf+LJorGpDLIad88mFsMHFuNoRVMJP+jt+XZFcV2P+UrKwn2KKGiTxD617Fwfltw5GrasPXoNkcVMXSBWS2MvAX5Z7IuAO4vlxn0XqiHUhG5FhH3sSIObEuuIPHPpcyXN9bUGFCzOvKtLQf7b+Fd+axOPX1DjDIsGHV/jH6iPfTQxlFQW9qKpn9CofzTGtpwq85YIzBIskl99fJ13rr/qLgTVEjiYaz8d0vREZgLmks04ofkVc+8SZqVFDd26+qz5zIrnK/Djf69syf/d7kYDtJ1XmUg==" } \ No newline at end of file diff --git a/tests/Medidata.MAuth.Tests/Mocks/RequestData/GET.json b/tests/Medidata.MAuth.Tests/Mocks/RequestData/GET.json index 49f5f8f..aee692d 100644 --- a/tests/Medidata.MAuth.Tests/Mocks/RequestData/GET.json +++ b/tests/Medidata.MAuth.Tests/Mocks/RequestData/GET.json @@ -3,5 +3,6 @@ "Method": "GET", "ApplicationUuid": "192cce84-8466-490e-b03e-074f82da3ee2", "SignedTime": "2016-08-01 03:22:47 +0000", - "Payload": "hg1RmUN/PNfiOpGRieEd77k9QnUluHag5UP7v/3azj8WHNmG9aHPZjPvwzDgCLCPFTthtaWOk5LSgHjQUcr/qWcg5o+Z+dbvzfbZpqkrwkzxrPLdzdZkulXSXNs+xEBFs1dQxLBL58jJ3BOvWPscWGYzd8IQThJ6rjNZiD+/lMGyDBm0BJgOHcBJ0AThywdEvOMXYQqR3daDM8OY2souleMzU1t0A1naq67TqTMAi5vEVCXvJ4AJhoNvrjATpzg/hM7ozXy91NgO0nVqJErcJBu3qX8UU4sqsQOP26ROTNHed/cBMdjzQZ/Bm/jMtWRtPmGozNzW12utRzh/Twzagg==" + "Payload": "hg1RmUN/PNfiOpGRieEd77k9QnUluHag5UP7v/3azj8WHNmG9aHPZjPvwzDgCLCPFTthtaWOk5LSgHjQUcr/qWcg5o+Z+dbvzfbZpqkrwkzxrPLdzdZkulXSXNs+xEBFs1dQxLBL58jJ3BOvWPscWGYzd8IQThJ6rjNZiD+/lMGyDBm0BJgOHcBJ0AThywdEvOMXYQqR3daDM8OY2souleMzU1t0A1naq67TqTMAi5vEVCXvJ4AJhoNvrjATpzg/hM7ozXy91NgO0nVqJErcJBu3qX8UU4sqsQOP26ROTNHed/cBMdjzQZ/Bm/jMtWRtPmGozNzW12utRzh/Twzagg==", + "PayloadV2": "kH00qgDJVwtGp+aUTlC935c+MOUGgyMY7zrCL/TeaaUu3MNq5CA/v8/NWwvtoqExHDnOv1sNP9Fa3Sb46WnudHmDW0/IAlbg9VtH8E+A/GIcUbsOix+R32UH7TzpnULG317A7N4Q+CS9WhvBrMydJ32Lfi+SpKbdb1O2QtPjuauXG4AbGtk4Qs7kQ+LwJk0QfIDBqT6qZturzOawDk4KymGOErtTblCkQb7oo78zehsPZSW3Ca7sJYty23rKByqemhJkz71b4DUoZaeI8xCcU1pcSU6gp4oexYqC1RAFNB6hnVZW8GcHlAyFu42jCEqR4HTJFPjLN4eI266YqwBE3w==" } \ No newline at end of file diff --git a/tests/Medidata.MAuth.Tests/Mocks/RequestData/POST.json b/tests/Medidata.MAuth.Tests/Mocks/RequestData/POST.json index b639f9a..49f63d1 100644 --- a/tests/Medidata.MAuth.Tests/Mocks/RequestData/POST.json +++ b/tests/Medidata.MAuth.Tests/Mocks/RequestData/POST.json @@ -4,5 +4,6 @@ "ApplicationUuid": "192cce84-8466-490e-b03e-074f82da3ee2", "SignedTime": "2016-08-01 03:31:54 +0000", "Base64Content": "VGhlIHJlcXVlc3QgYm9keSBmb3IgdGhlIFBPU1QgbWVzc2FnZS4=", - "Payload": "cdsjH35jR0IR77riAxMkYOWT8ZNrZzlF336HM274sFvUwV57vZ0200/sO4f2NXkwru9iiLIDAoo7ZKI8atWt96cEGVXE+5EkN8BJa0iLkhaqZ9fEqj6F6YiB6QH6JJa2RiAW4xZmvFFVjjc6crxkk9MxB8qv/l2Ta08JMZg6npfjQG6OQ0DDPckkYWKHXq2f8dfwoWe/jhBBrzT9TdMmxO74GT/e5FffcBIQFVw6Cm1DBoQFETU61SMtI/4AS5hewGMbHjvCdIRzV7Wg3GDnLsNCXqk/pNV0vWLL+jXd4Zqx8VUhFDUGq7yn9ZjslBhkca80DoQmX7Zgd/4BGUzTxg==" + "Payload": "cdsjH35jR0IR77riAxMkYOWT8ZNrZzlF336HM274sFvUwV57vZ0200/sO4f2NXkwru9iiLIDAoo7ZKI8atWt96cEGVXE+5EkN8BJa0iLkhaqZ9fEqj6F6YiB6QH6JJa2RiAW4xZmvFFVjjc6crxkk9MxB8qv/l2Ta08JMZg6npfjQG6OQ0DDPckkYWKHXq2f8dfwoWe/jhBBrzT9TdMmxO74GT/e5FffcBIQFVw6Cm1DBoQFETU61SMtI/4AS5hewGMbHjvCdIRzV7Wg3GDnLsNCXqk/pNV0vWLL+jXd4Zqx8VUhFDUGq7yn9ZjslBhkca80DoQmX7Zgd/4BGUzTxg==", + "PayloadV2": "AsPLP5sgJaHV25wLh+hD4cYxtJcj5JIfvRcCtiSxOYZcpS0EAEGWIkkE24AA10erFnTJYGVaSyFShuBHnUpZ4NiU0MOEwcAzT6q99vspDh594+GfNoJV93O2Frc+lX1bxEVzdzqGIvaS+1kLRsgUPYSTyNGWROloYVyfOoFMiZirllA+sVOaKUrVjZ1D/TV3PX7TzcFc9ZifxfSg+y7FZ0PunvQfxH9hwaf3jJ8eQLSDOn7hJzbffgtyvlm2Wtw20H/bUcPUj2H0dz+mJVnOTilmzofrsBx2WPHrMQQnahMg5HOn95XkKBpWhto3RVN/ldtspqDS8nC8kIifyCwMRA==" } \ No newline at end of file diff --git a/tests/Medidata.MAuth.Tests/Mocks/RequestData/POSTWithBinaryData.json b/tests/Medidata.MAuth.Tests/Mocks/RequestData/POSTWithBinaryData.json index f551516..ca1a2e9 100644 --- a/tests/Medidata.MAuth.Tests/Mocks/RequestData/POSTWithBinaryData.json +++ b/tests/Medidata.MAuth.Tests/Mocks/RequestData/POSTWithBinaryData.json @@ -4,5 +4,6 @@ "ApplicationUuid": "192cce84-8466-490e-b03e-074f82da3ee2", "SignedTime": "2018-10-02 08:21:03 +0000", "Base64Content": "N3q8ryccAAPP2NhAnAoAAAAAAABUAAAAAAAAAJp51s8AJhvKRmda8ne4fYbYQdsFNc2DpXwSpQXbkL0vFNNxcpaoin2EVnGNaiKYq549w1XvzKXD3XbQa2rmXszZWApBl1bI/FsLDKGC0VHA+Y6actTa9q0WzOvPT7/aMG+3/N3IbuV6O9dsvLNbWeoADUnubbql0yh6JA15cgda8pWI/9o6LnMsEPHBctImUxDUvnAdxMll37Vx9jnjKVTZ3FUqN6kJ6LpZgYlX3pFsgT8ABjgdhg4dIE7xwUdom4LWsylukG/JYGNuCi58JLtx5H8I1mUgnjpXDSL1Z6xRSpypddIQjv+3MpHD5iQlYTkhfwmczvSQZG+aSkOk6A5BL67QV4ozo03RH2nP3QqTQnwEWhmyvWV5AQGWukryjLbhilhIaN7oqS/B9Jhve666xIhfg8Ev7F+lrHgvtue1cnbaCVFXuyZJs0jYfRds4wy6Ytkh8dE=", - "Payload": "pXO9EecWIU1Qf0diqYhTypEfSnT822YFviS4K0Jq3OfZjpDb1fL5b8ePDgD9BXLLM1Sp8+wh90RyE3QkMnfuQL1pHD+ODnPfglIQFvOAtuqVEVHN3ztQ4l8LZcPD1pX3PC+8Aj3WzgbCaMDg6trArkZKFNE2IWuwuLvaWGHfANBCfF/VVINAtZ8ZUSDJrYT9vkfdFvQDsEYDwtdlt9fY6EvhNRtILXXafchtk0c9eC3VC1jBlWwcK+v1OM3YKKEa9oqR9wv7bjxS2sBfkVA6Av4cm1O6uUCYJoxbrKtmfU6sBHhBW55B+NLLoNajfxU+quaaoxmX/x3KaTW3QFVUYQ==" + "Payload": "pXO9EecWIU1Qf0diqYhTypEfSnT822YFviS4K0Jq3OfZjpDb1fL5b8ePDgD9BXLLM1Sp8+wh90RyE3QkMnfuQL1pHD+ODnPfglIQFvOAtuqVEVHN3ztQ4l8LZcPD1pX3PC+8Aj3WzgbCaMDg6trArkZKFNE2IWuwuLvaWGHfANBCfF/VVINAtZ8ZUSDJrYT9vkfdFvQDsEYDwtdlt9fY6EvhNRtILXXafchtk0c9eC3VC1jBlWwcK+v1OM3YKKEa9oqR9wv7bjxS2sBfkVA6Av4cm1O6uUCYJoxbrKtmfU6sBHhBW55B+NLLoNajfxU+quaaoxmX/x3KaTW3QFVUYQ==", + "PayloadV2": "kbD5Fvx6Q8SjdGqRSusA5qAbYm9JD5dfbFE65X5uMxJ7yxSwZrJgL2Hhek5LFhrMupTiIziaVDkAvWJEl1pjpuFwSunxUshABwh/cpfPTkMTWF7cNAOUCGrT4JiqYDgtQdEEtPwUv1gUpvUkD/xdrG8HOiBXz3WTiYrlg2xcz4GI4m32tF7AMZScc02vdUVRPjAbvsK3IAfvE0iA1EDhlegWNncMc63M2wKdUU3xNjOcvNVYQqbGBjoLfbJWyrsaYbLxkuHmD8wi0p/pfFYe7ipszBUMuTT7Q/kNt/Z26s5oqlekL8JEoZk2XDDaGahK5vvMEMKs2xgdbzwk5ntZnQ==" } \ No newline at end of file diff --git a/tests/Medidata.MAuth.Tests/Mocks/RequestData/PUT.json b/tests/Medidata.MAuth.Tests/Mocks/RequestData/PUT.json index 86dfcc2..06b66bf 100644 --- a/tests/Medidata.MAuth.Tests/Mocks/RequestData/PUT.json +++ b/tests/Medidata.MAuth.Tests/Mocks/RequestData/PUT.json @@ -4,5 +4,6 @@ "ApplicationUuid": "192cce84-8466-490e-b03e-074f82da3ee2", "SignedTime": "2016-08-01 05:38:19 +0000", "Base64Content": "VGhlIHJlcXVlc3QgYm9keSBmb3IgdGhlIFBVVCBtZXNzYWdlLg==", - "Payload": "h++GyB+btZfl0yRbhnIMqVJqytxFakYKpwvcVmI6ofTMSp5HjVSn54wwEVoCFEz1Esvc9rEwHlruavR1qxFSj7FBzwTM6eKiJywTSeNqDqf1SYmjppl7giz7KAGRY2bmxoyV0g63a5TncbbSmBKJSngYs1x1kskFILt8gDeEKHZHNxLqgPwXuzPpNUiwCvtxypIsC6YEHml59TRAXjsHJHC8HxxOWQW+io9Pb6w1adgI9nzekyxgQX8+LVZRn9mBLiux+6O5N5mY7V1yONeMifHwVhTMouyJDwGBda1/0E7IUlFv2wsxJIO4O/ulkwbk1XBECUdIe7ITtnrC6zY1Fg==" + "Payload": "h++GyB+btZfl0yRbhnIMqVJqytxFakYKpwvcVmI6ofTMSp5HjVSn54wwEVoCFEz1Esvc9rEwHlruavR1qxFSj7FBzwTM6eKiJywTSeNqDqf1SYmjppl7giz7KAGRY2bmxoyV0g63a5TncbbSmBKJSngYs1x1kskFILt8gDeEKHZHNxLqgPwXuzPpNUiwCvtxypIsC6YEHml59TRAXjsHJHC8HxxOWQW+io9Pb6w1adgI9nzekyxgQX8+LVZRn9mBLiux+6O5N5mY7V1yONeMifHwVhTMouyJDwGBda1/0E7IUlFv2wsxJIO4O/ulkwbk1XBECUdIe7ITtnrC6zY1Fg==", + "PayloadV2": "pFZP+yntKerPdAJ8OuTGcAQ+sE++XVfrjdvO6tuIXlL69J7B+VBrfkUMWkV77BQw590HEuAO+xn+4XB5YKB4T3de/UnIetUZB4itViIc9xiZEZHPaRNlUO70Tkjj3ZkUfbjG1X+jLmPfO7b2xtupnHjckb/pkwtsfO4MS1U7T9xnIWhkC+50SZvajiyg+Jf4mVzdabDYME4/prqzQc+uBVQy9n/wFSBJtkG3VMlsoHTbuIdij4ObeY6kgdakYMR0/CMnp7bXSnos0KvSxgRmY4+6lel2V+fyh02n3zPQUX1+Js5s47hywALuzJdjVMvc8q5jo1b1t6QrqUAAABYCdg==" } \ No newline at end of file diff --git a/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/DELETE.json b/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/DELETE.json new file mode 100644 index 0000000..778f296 --- /dev/null +++ b/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/DELETE.json @@ -0,0 +1,7 @@ +{ + "Url": "http://localhost:29999/one=hello&two=how", + "Method": "DELETE", + "ApplicationUuid": "192cce84-8466-490e-b03e-074f82da3ee2", + "SignedTime": "2016-08-01 05:40:38 +0000", + "Payload": "SsEKsQZr9kZ97hk8puw3Lbk+kb0rY0xgSUlaipsoRKsfmmemJ+ll/AF0oRG24JlGqnv+7lFJMXz3ulBQ6QxBTBaYCIb8SFJVXlTatyp+TynD6axgXISYTmzF9ZCO4CCfJWfWQhTXKgOVRG5y1rcAvBD5gUAr9/jwo0ZqtnMxheqvM7GpqqajTeJICxKlZ8c865NzkE8Fr8/KKu27viNawQUZF9kacZlugTkM2ASm5JNwQRM4cwTURFhJaDA6NN7v8Q7Y1YrwjbqBmwttuZ0jrgspXTBSus1rgwAHOD8D+7GoJPM07uGtWun/QDDlzjNEmdShPvMNoe9KQuWEGJO3oQ==" +} \ No newline at end of file diff --git a/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/GET.json b/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/GET.json new file mode 100644 index 0000000..1a34d17 --- /dev/null +++ b/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/GET.json @@ -0,0 +1,7 @@ +{ + "Url": "http://localhost:29999/test?one=hello&two=how", + "Method": "GET", + "ApplicationUuid": "192cce84-8466-490e-b03e-074f82da3ee2", + "SignedTime": "2016-08-01 03:22:47 +0000", + "Payload": "Vb3BqsWQPBRvh9QLAWpsJAnuzHd6+hwdjqpgB/tRgEfiWWeIFjzRsNvUPqPyx03adNLiZWwBWK/+7Axo78u18QgO/Sxn7wVh/2U6+MM2fJwADAj07zI461ulfSUihy6qHPumEI9ylX9hMMs4pxPRTmE4fTYI7kJ3KDPBThBHnsL9gOor49P5QyndxwKuH+pxG/+wqqjgtCfHFMOoJbvitNhgiUD9XVyc9r/qQH0hPnPylDVOOhAXzzgN9fZT75H3K6bQffiBEfmqcwEKhzM32d3pb956pmsOHHtoJ1SsbJfE+aWOuI/NQUbIuLHfCZbhjDYamvjj90a/tIUnO9kz5w==" +} \ No newline at end of file diff --git a/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/POST.json b/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/POST.json new file mode 100644 index 0000000..906a6cb --- /dev/null +++ b/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/POST.json @@ -0,0 +1,8 @@ +{ + "Url": "http://localhost:29999/test?one=hello&two=how", + "Method": "POST", + "ApplicationUuid": "192cce84-8466-490e-b03e-074f82da3ee2", + "SignedTime": "2016-08-01 03:31:54 +0000", + "Base64Content": "VGhlIHJlcXVlc3QgYm9keSBmb3IgdGhlIFBPU1QgbWVzc2FnZS4=", + "Payload": "MDc38+RSrknfBon3zf2RWIn0m3B0qLpf7bBGHPAtUU4/31KYpsRDSzxLefVlReffe7vMndOV8gEsXDNzglOu9bVU4KSK1Wz2kvjlW9Mij+fSps8DUkM+xiljjFFnFUQMH2xVq3tlo1XyvGIPFlbkCcLcEG6o+UTUPTkjIpmFJx1M4+LgQPILey6L0nFBXT7vviZ+zkGjyfH7FTrSjmrOANCbjzKb5fKckP/+IZjFql6J3UJXK7J90P5tb2oag6LXQgkWTzIDgQO/0Xg0NOOz3Zbj5IX9vHF9p3ekITeVt3BuLs3zqdRiSsl17173p51ERRHwhtqeue38BYJlznZmLg==" +} \ No newline at end of file diff --git a/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/POSTWithBinaryData.json b/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/POSTWithBinaryData.json new file mode 100644 index 0000000..9a3a578 --- /dev/null +++ b/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/POSTWithBinaryData.json @@ -0,0 +1,8 @@ +{ + "Url": "http://localhost:29999/one=hello&two=how", + "Method": "POST", + "ApplicationUuid": "192cce84-8466-490e-b03e-074f82da3ee2", + "SignedTime": "2018-10-02 08:21:03 +0000", + "Base64Content": "N3q8ryccAAPP2NhAnAoAAAAAAABUAAAAAAAAAJp51s8AJhvKRmda8ne4fYbYQdsFNc2DpXwSpQXbkL0vFNNxcpaoin2EVnGNaiKYq549w1XvzKXD3XbQa2rmXszZWApBl1bI/FsLDKGC0VHA+Y6actTa9q0WzOvPT7/aMG+3/N3IbuV6O9dsvLNbWeoADUnubbql0yh6JA15cgda8pWI/9o6LnMsEPHBctImUxDUvnAdxMll37Vx9jnjKVTZ3FUqN6kJ6LpZgYlX3pFsgT8ABjgdhg4dIE7xwUdom4LWsylukG/JYGNuCi58JLtx5H8I1mUgnjpXDSL1Z6xRSpypddIQjv+3MpHD5iQlYTkhfwmczvSQZG+aSkOk6A5BL67QV4ozo03RH2nP3QqTQnwEWhmyvWV5AQGWukryjLbhilhIaN7oqS/B9Jhve666xIhfg8Ev7F+lrHgvtue1cnbaCVFXuyZJs0jYfRds4wy6Ytkh8dE=", + "Payload": "lA6fs39YYTfsCDCCuE/3leO8HZy3liKYrTPUf9v4pF2AryTc4brSTk5IHeVAZ58TEJWFOTGLRXv8P93qYCg74PK9J25cJhJDRbM5U70iFdQPbp2r2ZwYVOh3yasrqNJ4lExH5lDpOnjPDjFhhsVNuHgaQnH+HWoTFkWpx4CJhZQKfNIG1lWCsDiNTNrsz9cQ0dAvH+xqiWnLqgcJXG1uxR+p2UBqDZU/Hj6bBhI710s+HGhtE0xeBWKzNbkH7h5DJDaE7pPcC5BrtAOk3g0BYJIIsIDYlJRR8P5J5njt6YDm16WiLff8l5xx2x+mTTAnZz7+QZ+4ycJSGuzSbJyFVw==" +} \ No newline at end of file diff --git a/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/PUT.json b/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/PUT.json new file mode 100644 index 0000000..e34abfe --- /dev/null +++ b/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/PUT.json @@ -0,0 +1,8 @@ +{ + "Url": "http://localhost:29999/one=hello&two=how", + "Method": "PUT", + "ApplicationUuid": "192cce84-8466-490e-b03e-074f82da3ee2", + "SignedTime": "2016-08-01 05:38:19 +0000", + "Base64Content": "VGhlIHJlcXVlc3QgYm9keSBmb3IgdGhlIFBVVCBtZXNzYWdlLg==", + "Payload": "Tij8xb3jpGn4LkrQSBSL0gZjd7s+hOTzj0EuOvum1DR/oPYehlaVygIee8FayjCfkOhrEDNEYggDuHVii6F4TQ1lmIRhYnDCZnaWQymHe/gM0K0i7EczlEWo3LoqYHXG094aji4moKjZQlph2NK+qf5qBEbTjf0gELKqZBdOP4FGHj8whnvQGhB/H58qxReltC8xaqcUL+llw8eOrx34HT2gV0C8Id+0JLYZbO6YwfBenBIGESesZhOw8Ekr9YljoSRKpApRVsDCcIwK7BiAH+D+xzptHtIjr2tHGDkj9UFMRqFJSVkXQlWeDp4I6+GCL6zzNeus0MmXPhN5uln/zA==" +} \ No newline at end of file From db4c0d11494ef55f10f5651fbdb732b04333bfec Mon Sep 17 00:00:00 2001 From: Prajon Date: Mon, 5 Aug 2019 12:47:43 -0400 Subject: [PATCH 08/40] Updated the feedbacks --- CHANGELOG.md | 3 ++ src/Medidata.MAuth.Core/Constants.cs | 4 +- src/Medidata.MAuth.Core/IMAuthCore.cs | 2 + src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 40 ++++--------------- src/Medidata.MAuth.Core/MAuthCore.cs | 12 +++++- .../MAuthCoreExtensions.cs | 30 +++++++------- src/Medidata.MAuth.Core/MAuthCoreFactory.cs | 12 +++--- src/Medidata.MAuth.Core/MAuthCoreV2.cs | 26 +++++------- .../MAuthRequestRetrier.cs | 4 +- .../MAuthSigningHandler.cs | 8 ++-- .../Models/MAuthVersion.cs | 18 +++++++++ src/Medidata.MAuth.Core/Models/Version.cs | 21 ---------- .../Options/MAuthOptionsBase.cs | 2 - .../Options/MAuthSigningOptions.cs | 4 +- src/Medidata.MAuth.Core/UtilityExtensions.cs | 38 ++++-------------- .../MAuthAuthenticatingHandler.cs | 3 +- .../Infrastructure/MAuthServerHandler.cs | 2 +- .../Infrastructure/TestExtensions.cs | 9 ++++- .../MAuthAuthenticatorTests.cs | 2 +- .../MAuthSigningHandlerTests.cs | 9 +++-- .../Mocks/RequestDataV2/DELETE.json | 4 +- .../RequestDataV2/POSTWithBinaryData.json | 4 +- .../Mocks/RequestDataV2/PUT.json | 4 +- version.props | 2 +- 24 files changed, 115 insertions(+), 148 deletions(-) create mode 100644 src/Medidata.MAuth.Core/Models/MAuthVersion.cs delete mode 100644 src/Medidata.MAuth.Core/Models/Version.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index d04e51b..74b55db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changes in Medidata.MAuth +## v4.0.0 +- **[All]** Added implementation for MWSV2 signinig and authentication. + ## v3.1.3 - **[Core]** Refactored `MAuthCoreExtensions.cs` and moved Signing and Verification method into `IMAuthCore.cs`. diff --git a/src/Medidata.MAuth.Core/Constants.cs b/src/Medidata.MAuth.Core/Constants.cs index fe56038..73a9a67 100644 --- a/src/Medidata.MAuth.Core/Constants.cs +++ b/src/Medidata.MAuth.Core/Constants.cs @@ -23,7 +23,7 @@ internal static class Constants public static readonly string MAuthTimeHeaderKey = "X-MWS-Time"; - public static readonly string MAuthTokenRequestPath = "/mauth/v1/security_tokens/"; + //public static readonly string MAuthTokenRequestPath = "/mauth/v1/security_tokens/"; public static readonly string KeyNormalizeLinesStartRegexPattern = "^(?-----BEGIN [A-Z ]+[-]+)"; @@ -42,7 +42,7 @@ internal static class Constants ");$", RegexOptions.Compiled ); - public static readonly string MAuthTokenRequestPathV2 = "/mauth/v2/security_tokens/"; + //public static readonly string MAuthTokenRequestPathV2 = "/mauth/v2/security_tokens/"; public static readonly string MAuthHeaderKeyV2 = "MCC-Authentication"; diff --git a/src/Medidata.MAuth.Core/IMAuthCore.cs b/src/Medidata.MAuth.Core/IMAuthCore.cs index d1f27fc..7fa01c6 100644 --- a/src/Medidata.MAuth.Core/IMAuthCore.cs +++ b/src/Medidata.MAuth.Core/IMAuthCore.cs @@ -12,5 +12,7 @@ internal interface IMAuthCore Task GetSignature(HttpRequestMessage request, AuthenticationInfo authInfo); PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage request); + + string GetMAuthTokenRequestPath(); } } diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index 02d0d8d..caf7ce8 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -5,7 +5,7 @@ using Medidata.MAuth.Core.Exceptions; using Microsoft.Extensions.Caching.Memory; using Org.BouncyCastle.Crypto; -using Version = Medidata.MAuth.Core.Models.Version; +using Medidata.MAuth.Core.Models; namespace Medidata.MAuth.Core { @@ -38,8 +38,8 @@ public async Task AuthenticateRequest(HttpRequestMessage request) { var version = request.GetAuthHeaderValue().GetVersionFromAuthenticationHeader(); - if (options.DisableV1 && version == Version.MWS.ToString()) - throw new InvalidVersionException($"Authentication with {version} version is no longer supported."); + if (options.DisableV1 && version == MAuthVersion.MWS) + throw new InvalidVersionException($"Authentication with {version} version is disabled."); mAuthCore = MAuthCoreFactory.Instantiate(version); var authInfo = mAuthCore.GetAuthenticationInfo(request); @@ -75,11 +75,12 @@ public async Task AuthenticateRequest(HttpRequestMessage request) } } - private Task GetApplicationInfo(Guid applicationUuid, string version) => + private Task GetApplicationInfo(Guid applicationUuid, MAuthVersion version) => cache.GetOrCreateAsync(applicationUuid, async entry => { - var tokenRequestPath = version.GetMAuthTokenRequestPath(); - retrier = new MAuthRequestRetrier(options, (Version)Enum.Parse(typeof(Version), version)); + mAuthCore = MAuthCoreFactory.Instantiate(version); + var tokenRequestPath = mAuthCore.GetMAuthTokenRequestPath(); + retrier = new MAuthRequestRetrier(options, version); var response = await retrier.GetSuccessfulResponse( applicationUuid, CreateRequest, tokenRequestPath, @@ -99,32 +100,5 @@ private Task GetApplicationInfo(Guid applicationUuid, string ve private HttpRequestMessage CreateRequest(Guid applicationUuid, string tokenRequestPath) => new HttpRequestMessage(HttpMethod.Get, new Uri(options.MAuthServiceUrl, $"{tokenRequestPath}{applicationUuid.ToHyphenString()}.json")); - - ///// - ///// Extracts the authentication information from a . - ///// - ///// The request that has the authentication information. - ///// The authentication information with the payload from the request. - //internal PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage request) - //{ - // var authHeader = request.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKey); - - // if (authHeader == null) - // throw new ArgumentNullException(nameof(authHeader), "The MAuth header is missing from the request."); - - // var signedTime = request.Headers.GetFirstValueOrDefault(Constants.MAuthTimeHeaderKey); - - // if (signedTime == default(long)) - // throw new ArgumentException("Invalid MAuth signed time header value.", nameof(signedTime)); - - // var (uuid, payload) = authHeader.ParseAuthenticationHeader(); - - // return new PayloadAuthenticationInfo() - // { - // ApplicationUuid = uuid, - // Payload = Convert.FromBase64String(payload), - // SignedTime = signedTime.FromUnixTimeSeconds() - // }; - //} } } diff --git a/src/Medidata.MAuth.Core/MAuthCore.cs b/src/Medidata.MAuth.Core/MAuthCore.cs index c893686..2f71ade 100644 --- a/src/Medidata.MAuth.Core/MAuthCore.cs +++ b/src/Medidata.MAuth.Core/MAuthCore.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Security; +using Medidata.MAuth.Core.Models; namespace Medidata.MAuth.Core { @@ -88,7 +89,7 @@ public async Task GetSignature(HttpRequestMessage request, Authenticatio internal async Task AddAuthenticationInfo(HttpRequestMessage request, PrivateKeyAuthenticationInfo authInfo) { var authHeader = - $"MWS {authInfo.ApplicationUuid.ToHyphenString()}:" + + $"{MAuthVersion.MWS} {authInfo.ApplicationUuid.ToHyphenString()}:" + $"{await CalculatePayload(request, authInfo).ConfigureAwait(false)}"; request.Headers.Add(Constants.MAuthHeaderKey, authHeader); @@ -143,5 +144,14 @@ public PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage reques SignedTime = signedTime.FromUnixTimeSeconds() }; } + + /// + /// Determines the correct token request path. + /// + /// The token request path. + public string GetMAuthTokenRequestPath() + { + return "/mauth/v1/security_tokens/"; + } } } \ No newline at end of file diff --git a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs index b66551a..45c602f 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs @@ -179,8 +179,18 @@ public static byte[] Concat(this byte[][] values) public static IDictionary GetQueryStringParams(this string queryString) { - var dictionary = queryString.Replace("?", "").Split('&').ToDictionary(x => x.Split('=')[0], x => x.Split('=')[1]); - return dictionary; + //var dictionary = queryString.Replace("?", "").Split('&').ToDictionary(x => x.Split('=')[0], x => x.Split('=')[1]); + //return dictionary; + + var queryStrings = new SortedDictionary(); + queryString.Replace("?", "") + .Split('&') + .ToList() + .ForEach((query) => { + var keyValue = query.Split('='); + queryStrings.Add(keyValue[0], keyValue[1]); + }); + return queryStrings; } public static IDictionary SortByKeyAscending(this IDictionary queryStringParams) @@ -197,20 +207,12 @@ public static IDictionary SortByKeyAscending(this IDictionary queryParams) { - var encodedQueryParam = string.Empty; - foreach (var key in queryParams) + var encodedQueryStrings = new List(); + foreach (var query in queryParams) { - encodedQueryParam = (encodedQueryParam != string.Empty) ? - $"{encodedQueryParam}&" : encodedQueryParam; - - var encodedKey = Uri.EscapeUriString(key.Key); // Is this UTF8 encoding or URI encoding - - var encodedValue = (queryParams[key.Key] != string.Empty) - ? Uri.EscapeUriString(queryParams[key.Key]) // same here UTF8 encoding or uri encoding - : ""; - encodedQueryParam = $"{encodedQueryParam}{encodedKey}={encodedValue}"; + encodedQueryStrings.Add($"{Uri.EscapeUriString(query.Key)}={Uri.EscapeUriString(query.Value)}"); } - return encodedQueryParam; + return string.Join("&", encodedQueryStrings); } /// diff --git a/src/Medidata.MAuth.Core/MAuthCoreFactory.cs b/src/Medidata.MAuth.Core/MAuthCoreFactory.cs index 762728e..6bf3c26 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreFactory.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreFactory.cs @@ -1,18 +1,18 @@ -using System; -using Version = Medidata.MAuth.Core.Models.Version; +using Medidata.MAuth.Core.Exceptions; +using Medidata.MAuth.Core.Models; namespace Medidata.MAuth.Core { internal class MAuthCoreFactory { - public static IMAuthCore Instantiate(string version = "MWS") + public static IMAuthCore Instantiate(MAuthVersion version = MAuthVersion.MWS) { - if (version == Version.MWSV2.ToString()) + if (version == MAuthVersion.MWSV2) return new MAuthCoreV2(); - else if (version == Version.MWS.ToString()) + else if (version == MAuthVersion.MWS) return new MAuthCore(); - throw new Exception("Version is not recognized"); + throw new InvalidVersionException("Version is not recognized"); } } } diff --git a/src/Medidata.MAuth.Core/MAuthCoreV2.cs b/src/Medidata.MAuth.Core/MAuthCoreV2.cs index 2c1f79e..60b1a0c 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreV2.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreV2.cs @@ -1,17 +1,8 @@ using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Net.Http; -using System.Net.Http.Headers; using System.Security.Cryptography; -using System.Text; -using System.Text.RegularExpressions; using System.Threading.Tasks; -using Newtonsoft.Json.Linq; -using Org.BouncyCastle.Crypto.Encodings; -using Org.BouncyCastle.Crypto.Engines; +using Medidata.MAuth.Core.Models; namespace Medidata.MAuth.Core { @@ -20,8 +11,6 @@ namespace Medidata.MAuth.Core /// internal class MAuthCoreV2 : IMAuthCore { - private const string MwsV2Token = "MWSV2"; - /// /// Signs an HTTP request with the MAuth-specific authentication information. /// @@ -78,13 +67,12 @@ public async Task GetSignature(HttpRequestMessage request, Authenticatio var requestBody = request.Content != null ? await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false) : new byte[] { }; - // var requestBodyDigest = SHA512 hash of request body var requestBodyDigest = requestBody.AsSha512HashV2(); var encodedCurrentSecondsSinceEpoch = authInfo.SignedTime.ToUnixTimeSeconds().ToString().ToBytes(); var encodedQueryParams = (!string.IsNullOrEmpty(request.RequestUri.Query)) - ? request.RequestUri.Query.GetQueryStringParams().SortByKeyAscending().BuildEncodedQueryParams().ToBytes() + ? request.RequestUri.Query.GetQueryStringParams().BuildEncodedQueryParams().ToBytes() : new byte[] { }; return new byte[][] @@ -112,7 +100,7 @@ internal async Task AddAuthenticationInfo( HttpRequestMessage request, PrivateKeyAuthenticationInfo authInfo) { var authHeader = - $"{MwsV2Token} {authInfo.ApplicationUuid.ToHyphenString()}:" + + $"{MAuthVersion.MWSV2} {authInfo.ApplicationUuid.ToHyphenString()}:" + $"{await CalculatePayload(request, authInfo).ConfigureAwait(false)};"; request.Headers.Add(Constants.MAuthHeaderKeyV2, authHeader); @@ -169,5 +157,13 @@ public PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage reques SignedTime = signedTime.FromUnixTimeSeconds() }; } + /// + /// Determines the correct token request path. + /// + /// The token request path. + public string GetMAuthTokenRequestPath() + { + return "/mauth/v2/security_tokens/"; + } } } diff --git a/src/Medidata.MAuth.Core/MAuthRequestRetrier.cs b/src/Medidata.MAuth.Core/MAuthRequestRetrier.cs index 7da7041..e049dc8 100644 --- a/src/Medidata.MAuth.Core/MAuthRequestRetrier.cs +++ b/src/Medidata.MAuth.Core/MAuthRequestRetrier.cs @@ -1,7 +1,7 @@ using System; using System.Net.Http; using System.Threading.Tasks; -using Version = Medidata.MAuth.Core.Models.Version; +using Medidata.MAuth.Core.Models; namespace Medidata.MAuth.Core { @@ -9,7 +9,7 @@ internal class MAuthRequestRetrier { private readonly HttpClient client; - public MAuthRequestRetrier(MAuthOptionsBase options, Version version) + public MAuthRequestRetrier(MAuthOptionsBase options, MAuthVersion version) { var signingHandler = new MAuthSigningHandler(options: new MAuthSigningOptions() { diff --git a/src/Medidata.MAuth.Core/MAuthSigningHandler.cs b/src/Medidata.MAuth.Core/MAuthSigningHandler.cs index deb5b57..9d7a316 100644 --- a/src/Medidata.MAuth.Core/MAuthSigningHandler.cs +++ b/src/Medidata.MAuth.Core/MAuthSigningHandler.cs @@ -4,7 +4,7 @@ using System.Threading; using System.Threading.Tasks; using Medidata.MAuth.Core.Exceptions; -using Version = Medidata.MAuth.Core.Models.Version; +using Medidata.MAuth.Core.Models; namespace Medidata.MAuth.Core { @@ -57,11 +57,11 @@ protected override async Task SendAsync( if (InnerHandler == null) InnerHandler = new HttpClientHandler(); - if(options.DisableV1 && options.MAuthVersion.ToString() == Version.MWS.ToString()) + if(options.DisableV1 && options.MAuthVersion.ToString() == MAuthVersion.MWS.ToString()) throw new InvalidVersionException - ($"Signing with {options.MAuthVersion.ToString()} is no longer supported."); + ($"Signing with {options.MAuthVersion.ToString()} is disabled."); - mAuthCore = MAuthCoreFactory.Instantiate(options.MAuthVersion.ToString()); + mAuthCore = MAuthCoreFactory.Instantiate(options.MAuthVersion); return await base .SendAsync(await mAuthCore.Sign(request, options).ConfigureAwait(false), cancellationToken) diff --git a/src/Medidata.MAuth.Core/Models/MAuthVersion.cs b/src/Medidata.MAuth.Core/Models/MAuthVersion.cs new file mode 100644 index 0000000..9fc9ff0 --- /dev/null +++ b/src/Medidata.MAuth.Core/Models/MAuthVersion.cs @@ -0,0 +1,18 @@ +namespace Medidata.MAuth.Core.Models +{ + /// + /// + /// + public enum MAuthVersion + { + /// + /// + /// + MWS, + + /// + /// + /// + MWSV2 + } +} diff --git a/src/Medidata.MAuth.Core/Models/Version.cs b/src/Medidata.MAuth.Core/Models/Version.cs deleted file mode 100644 index 0cb1cce..0000000 --- a/src/Medidata.MAuth.Core/Models/Version.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Runtime.Serialization; - -namespace Medidata.MAuth.Core.Models -{ - /// - /// - /// - public enum Version - { - /// - /// - /// - [EnumMember(Value = "MWS")] - MWS = 0, - /// - /// - /// - [EnumMember(Value = "MWSV2")] - MWSV2 = 1, - } -} diff --git a/src/Medidata.MAuth.Core/Options/MAuthOptionsBase.cs b/src/Medidata.MAuth.Core/Options/MAuthOptionsBase.cs index 54ba52c..48be507 100644 --- a/src/Medidata.MAuth.Core/Options/MAuthOptionsBase.cs +++ b/src/Medidata.MAuth.Core/Options/MAuthOptionsBase.cs @@ -1,7 +1,5 @@ using System; using System.Net.Http; -using Medidata.MAuth.Core.Models; -using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.Core { diff --git a/src/Medidata.MAuth.Core/Options/MAuthSigningOptions.cs b/src/Medidata.MAuth.Core/Options/MAuthSigningOptions.cs index 936cecf..b2da8a5 100644 --- a/src/Medidata.MAuth.Core/Options/MAuthSigningOptions.cs +++ b/src/Medidata.MAuth.Core/Options/MAuthSigningOptions.cs @@ -1,5 +1,5 @@ using System; -using Version = Medidata.MAuth.Core.Models.Version; +using Medidata.MAuth.Core.Models; namespace Medidata.MAuth.Core { @@ -25,7 +25,7 @@ public class MAuthSigningOptions /// /// Determines the MAuth version for signing requests. /// - public Version MAuthVersion { get; set; } + public MAuthVersion MAuthVersion { get; set; } /// /// Determines the boolean value if V1 option of signing should be disabled or not with default value of false. diff --git a/src/Medidata.MAuth.Core/UtilityExtensions.cs b/src/Medidata.MAuth.Core/UtilityExtensions.cs index 1debbe2..e3dd47a 100644 --- a/src/Medidata.MAuth.Core/UtilityExtensions.cs +++ b/src/Medidata.MAuth.Core/UtilityExtensions.cs @@ -1,7 +1,7 @@ using System; using System.Net.Http; using System.Threading.Tasks; -using Version = Medidata.MAuth.Core.Models.Version; +using Medidata.MAuth.Core.Models; namespace Medidata.MAuth.Core { @@ -75,14 +75,14 @@ public static Task Authenticate(this HttpRequestMessage request, MAuthOpti /// /// /// - public static string GetVersionFromAuthenticationHeader(this string authHeader) + public static MAuthVersion GetVersionFromAuthenticationHeader(this string authHeader) { - return authHeader.StartsWith(Version.MWSV2.ToString()) - ? Version.MWSV2.ToString() : Version.MWS.ToString(); + return authHeader.StartsWith(MAuthVersion.MWSV2.ToString()) + ? MAuthVersion.MWSV2 : MAuthVersion.MWS; } /// - /// Determines the correct MAuthHeader value + /// Determines the correct MAuthHeader value with default check for V2. /// /// /// The MAuthHeader value. @@ -91,32 +91,8 @@ public static string GetAuthHeaderValue(this HttpRequestMessage request) //By default first check for V2 var authHeader = request.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKeyV2) ?? request.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKey); - if (authHeader == null) - throw new ArgumentNullException(nameof(authHeader), "The MAuth header is missing from the request."); - return authHeader; - } - - /// - /// Determines the correct token request path based on version - /// - /// - /// The correct token request path - public static string GetMAuthTokenRequestPath(this string version) - { - return (version == Version.MWSV2.ToString()) ? - Constants.MAuthTokenRequestPathV2 : Constants.MAuthTokenRequestPath; - } - - /// - /// - /// - /// - /// - public static (string mAuthHeaderKey, string mAuthTimeHeaderKey) GetHeaderKeys(this string version) - { - return (version == Version.MWSV2.ToString()) - ? (Constants.MAuthHeaderKeyV2, Constants.MAuthTimeHeaderKeyV2) - : (Constants.MAuthHeaderKey, Constants.MAuthTimeHeaderKey); + return authHeader ?? + throw new ArgumentNullException(nameof(authHeader), "The MAuth header is missing from the request."); } } } diff --git a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs index 21a22dc..0240a97 100644 --- a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs +++ b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using Medidata.MAuth.Core; +using Medidata.MAuth.Core.Models; namespace Medidata.MAuth.WebApi { @@ -20,7 +21,7 @@ public class MAuthAuthenticatingHandler : DelegatingHandler public Guid ClientAppUuid => authenticator.ApplicationUuid; /// - /// Initializes a new insance of the class with the provided + /// Initializes a new instance of the class with the provided /// . /// /// The options for this message handler. diff --git a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs index 31a5f3c..28867ff 100644 --- a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs +++ b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs @@ -33,7 +33,7 @@ await mAuthCore.GetSignature(request, authInfo), )) return new HttpResponseMessage(HttpStatusCode.Unauthorized) { RequestMessage = request }; - var tokenRequestPath = version.GetMAuthTokenRequestPath(); + var tokenRequestPath = mAuthCore.GetMAuthTokenRequestPath(); if (!request.RequestUri.AbsolutePath.Equals( $"{tokenRequestPath}{clientUuid.ToHyphenString()}.json", diff --git a/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs b/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs index 2b82ae2..bbb0f17 100644 --- a/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs +++ b/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using Medidata.MAuth.Core; using Newtonsoft.Json; -using Version = Medidata.MAuth.Core.Models.Version; +using Medidata.MAuth.Core.Models; namespace Medidata.MAuth.Tests.Infrastructure { @@ -99,5 +99,12 @@ public static string ToStringContent(this string base64Content) => base64Content == null ? null : Encoding.UTF8.GetString(Convert.FromBase64String(base64Content)); public static HttpMethod ToHttpMethod(this string method) => new HttpMethod(method); + + private static (string mAuthHeaderKey, string mAuthTimeHeaderKey) GetHeaderKeys(this string version) + { + return (version == MAuthVersion.MWSV2.ToString()) + ? (Constants.MAuthHeaderKeyV2, Constants.MAuthTimeHeaderKeyV2) + : (Constants.MAuthHeaderKey, Constants.MAuthTimeHeaderKey); + } } } diff --git a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs index 8151393..8e571b5 100644 --- a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs @@ -293,7 +293,7 @@ public static async Task AuthenticateRequest_WithMWSVersion_WithDisableV1_WillTh // Assert Assert.NotNull(exception); - Assert.Equal("Authentication with MWS version is no longer supported.", exception.Message); + Assert.Equal("Authentication with MWS version is disabled.", exception.Message); } } } diff --git a/tests/Medidata.MAuth.Tests/MAuthSigningHandlerTests.cs b/tests/Medidata.MAuth.Tests/MAuthSigningHandlerTests.cs index 0e4e365..e37a8c7 100644 --- a/tests/Medidata.MAuth.Tests/MAuthSigningHandlerTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthSigningHandlerTests.cs @@ -1,10 +1,11 @@ -using System.Net.Http; +using System; +using System.Net.Http; using System.Threading.Tasks; using Medidata.MAuth.Core; using Medidata.MAuth.Core.Exceptions; +using Medidata.MAuth.Core.Models; using Medidata.MAuth.Tests.Infrastructure; using Xunit; -using Version = Medidata.MAuth.Core.Models.Version; namespace Medidata.MAuth.Tests { @@ -45,7 +46,7 @@ public static async Task SendAsync_WithValidMWSV2Request_WillSignProperly(string var version = "MWSV2"; var actual = new AssertSigningHandler(); var clientOptions = TestExtensions.ClientOptions(testData.SignedTime); - clientOptions.MAuthVersion = Version.MWSV2; + clientOptions.MAuthVersion = MAuthVersion.MWSV2; var signingHandler = new MAuthSigningHandler(clientOptions, actual); // Act @@ -71,7 +72,7 @@ public static async Task SendAsync_WithMWSRequest_WithDisableV1_WillThrowInvalid var actual = new AssertSigningHandler(); var clientOptions = TestExtensions.ClientOptions(testData.SignedTime); clientOptions.DisableV1 = true; - clientOptions.MAuthVersion = Version.MWS; + clientOptions.MAuthVersion = MAuthVersion.MWS; var signingHandler = new MAuthSigningHandler(clientOptions, actual); // Act, Assert diff --git a/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/DELETE.json b/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/DELETE.json index 778f296..8b5cce8 100644 --- a/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/DELETE.json +++ b/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/DELETE.json @@ -1,7 +1,7 @@ { - "Url": "http://localhost:29999/one=hello&two=how", + "Url": "http://localhost:29999/test?one=hello&two=how", "Method": "DELETE", "ApplicationUuid": "192cce84-8466-490e-b03e-074f82da3ee2", "SignedTime": "2016-08-01 05:40:38 +0000", - "Payload": "SsEKsQZr9kZ97hk8puw3Lbk+kb0rY0xgSUlaipsoRKsfmmemJ+ll/AF0oRG24JlGqnv+7lFJMXz3ulBQ6QxBTBaYCIb8SFJVXlTatyp+TynD6axgXISYTmzF9ZCO4CCfJWfWQhTXKgOVRG5y1rcAvBD5gUAr9/jwo0ZqtnMxheqvM7GpqqajTeJICxKlZ8c865NzkE8Fr8/KKu27viNawQUZF9kacZlugTkM2ASm5JNwQRM4cwTURFhJaDA6NN7v8Q7Y1YrwjbqBmwttuZ0jrgspXTBSus1rgwAHOD8D+7GoJPM07uGtWun/QDDlzjNEmdShPvMNoe9KQuWEGJO3oQ==" + "Payload": "JaFjw0SCWI+y6dLbhSYOBgjmCFZ5nR8hTEK5GZqMjlAnif3J/lAYdbMVPaGkC5qNBKOOwqh2D4CFyOejVrbZ5uGS5q3MteI7pO4jPM3ZbWz2yaa1rc2ZlhKcm42Y1YpgChKwHoN/NuZHogyunv1KlYR8CDOt8k+uIF0G0TNVOEv3Puh3NqM+HbuNhWupZBmsGH7u/mo2yXi1Z5FD9YfeyG5eWFtwQ7Ui5SSTmgF7hlgtqoM5EYCRzp9wy1xZrB+MpsfMsSFTS5aa6Rydbj3bTHHH9AS8fjRtWHAEe/W5OOfQWfc+O70u46AsINhJbA1Sw4mqrTvEuGkkEr3oYyzsrQ==" } \ No newline at end of file diff --git a/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/POSTWithBinaryData.json b/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/POSTWithBinaryData.json index 9a3a578..cf58815 100644 --- a/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/POSTWithBinaryData.json +++ b/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/POSTWithBinaryData.json @@ -1,8 +1,8 @@ { - "Url": "http://localhost:29999/one=hello&two=how", + "Url": "http://localhost:29999/test?one=hello&two=how", "Method": "POST", "ApplicationUuid": "192cce84-8466-490e-b03e-074f82da3ee2", "SignedTime": "2018-10-02 08:21:03 +0000", "Base64Content": "N3q8ryccAAPP2NhAnAoAAAAAAABUAAAAAAAAAJp51s8AJhvKRmda8ne4fYbYQdsFNc2DpXwSpQXbkL0vFNNxcpaoin2EVnGNaiKYq549w1XvzKXD3XbQa2rmXszZWApBl1bI/FsLDKGC0VHA+Y6actTa9q0WzOvPT7/aMG+3/N3IbuV6O9dsvLNbWeoADUnubbql0yh6JA15cgda8pWI/9o6LnMsEPHBctImUxDUvnAdxMll37Vx9jnjKVTZ3FUqN6kJ6LpZgYlX3pFsgT8ABjgdhg4dIE7xwUdom4LWsylukG/JYGNuCi58JLtx5H8I1mUgnjpXDSL1Z6xRSpypddIQjv+3MpHD5iQlYTkhfwmczvSQZG+aSkOk6A5BL67QV4ozo03RH2nP3QqTQnwEWhmyvWV5AQGWukryjLbhilhIaN7oqS/B9Jhve666xIhfg8Ev7F+lrHgvtue1cnbaCVFXuyZJs0jYfRds4wy6Ytkh8dE=", - "Payload": "lA6fs39YYTfsCDCCuE/3leO8HZy3liKYrTPUf9v4pF2AryTc4brSTk5IHeVAZ58TEJWFOTGLRXv8P93qYCg74PK9J25cJhJDRbM5U70iFdQPbp2r2ZwYVOh3yasrqNJ4lExH5lDpOnjPDjFhhsVNuHgaQnH+HWoTFkWpx4CJhZQKfNIG1lWCsDiNTNrsz9cQ0dAvH+xqiWnLqgcJXG1uxR+p2UBqDZU/Hj6bBhI710s+HGhtE0xeBWKzNbkH7h5DJDaE7pPcC5BrtAOk3g0BYJIIsIDYlJRR8P5J5njt6YDm16WiLff8l5xx2x+mTTAnZz7+QZ+4ycJSGuzSbJyFVw==" + "Payload": "Kcw87FCuY3D16X+mvUHlt1eVENLH/zs4OIfkikV6y/47DTH6aV1nzv1kl1CUHP0tE279bSSMupQUQvfPLH/cJ+2igf64HfdOSIUTJwEr+6O68oINTiiNAVc2VeO41SYXqn9857usLIem75j9egpHBYqkcKN4D+WsxxWBEf/ee73vHVJYvPiK63FTjw4P+Ek31ccn0MexxRUP/eYuo5ahE6UgooWZjIgseB3jRWPZ9LwI/zyb2HDRVebk+uGX0HFmnZAHQ4ut6Fft6AJRTMl+kNtUL6MEdI6ofPQj7uLjpNcz6sXAx/tmzs5z/kWyatfBCMBku/RE1yUoYm051NgF7g==" } \ No newline at end of file diff --git a/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/PUT.json b/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/PUT.json index e34abfe..74c5535 100644 --- a/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/PUT.json +++ b/tests/Medidata.MAuth.Tests/Mocks/RequestDataV2/PUT.json @@ -1,8 +1,8 @@ { - "Url": "http://localhost:29999/one=hello&two=how", + "Url": "http://localhost:29999/test?one=hello&two=how", "Method": "PUT", "ApplicationUuid": "192cce84-8466-490e-b03e-074f82da3ee2", "SignedTime": "2016-08-01 05:38:19 +0000", "Base64Content": "VGhlIHJlcXVlc3QgYm9keSBmb3IgdGhlIFBVVCBtZXNzYWdlLg==", - "Payload": "Tij8xb3jpGn4LkrQSBSL0gZjd7s+hOTzj0EuOvum1DR/oPYehlaVygIee8FayjCfkOhrEDNEYggDuHVii6F4TQ1lmIRhYnDCZnaWQymHe/gM0K0i7EczlEWo3LoqYHXG094aji4moKjZQlph2NK+qf5qBEbTjf0gELKqZBdOP4FGHj8whnvQGhB/H58qxReltC8xaqcUL+llw8eOrx34HT2gV0C8Id+0JLYZbO6YwfBenBIGESesZhOw8Ekr9YljoSRKpApRVsDCcIwK7BiAH+D+xzptHtIjr2tHGDkj9UFMRqFJSVkXQlWeDp4I6+GCL6zzNeus0MmXPhN5uln/zA==" + "Payload": "m9BjozK5VVEkTwb6ectmSC4MVvi02FPbE+d8mem8nYDz/1rpPUqwvmQvZDPBGOVduVAmwuZnXmmRHJfUwF0SwMPilivEmp1Mc4VbkH/DGvVLq/5Jy4GuQYk/rtSkPsp3Ee2TbhSwYtfSfF6ACUsLSkIqCDjJaL7K4fgV7B+Xl7hFefm9gY1r3xLIMX/aX+2KS4r0J7uCFRGjubi53JYC1+XkMoUqkNJXQ1Cpuw+5EkIZCVja+Crz31YuTSBlYDF76LofXPvlKpF+oh6B+/UHb8pbuSSJGjmTgbhluzoJbVJ0SPYxkXoscrtMfgdvkQYgE5jaMo521A6Oi97wgnRWSg==" } \ No newline at end of file diff --git a/version.props b/version.props index f4563c5..ceb060e 100644 --- a/version.props +++ b/version.props @@ -1,6 +1,6 @@  - 3.1.3 + 4.0.0 From a4dc1bcb2eb1e08387892a3c259fbba5ce0c970b Mon Sep 17 00:00:00 2001 From: Prajon Date: Mon, 5 Aug 2019 15:24:39 -0400 Subject: [PATCH 09/40] Feedback implementation - Remove commented codes and not used namespaces. - Add xml documentation for some methods. - Remove empty controller --- src/Medidata.MAuth.Core/Constants.cs | 4 ---- src/Medidata.MAuth.Core/MAuthCore.cs | 4 +--- src/Medidata.MAuth.Core/MAuthCoreExtensions.cs | 6 +----- src/Medidata.MAuth.Core/MAuthCoreV2.cs | 3 --- src/Medidata.MAuth.Core/UtilityExtensions.cs | 4 ++-- tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs | 3 +-- tests/Medidata.MAuth.Tests/Mocks/RequestData/DELETE.json | 3 +-- tests/Medidata.MAuth.Tests/Mocks/RequestData/GET.json | 3 +-- tests/Medidata.MAuth.Tests/Mocks/RequestData/POST.json | 3 +-- .../Mocks/RequestData/POSTWithBinaryData.json | 3 +-- tests/Medidata.MAuth.Tests/Mocks/RequestData/PUT.json | 3 +-- 11 files changed, 10 insertions(+), 29 deletions(-) diff --git a/src/Medidata.MAuth.Core/Constants.cs b/src/Medidata.MAuth.Core/Constants.cs index 73a9a67..240ff8a 100644 --- a/src/Medidata.MAuth.Core/Constants.cs +++ b/src/Medidata.MAuth.Core/Constants.cs @@ -23,8 +23,6 @@ internal static class Constants public static readonly string MAuthTimeHeaderKey = "X-MWS-Time"; - //public static readonly string MAuthTokenRequestPath = "/mauth/v1/security_tokens/"; - public static readonly string KeyNormalizeLinesStartRegexPattern = "^(?-----BEGIN [A-Z ]+[-]+)"; public static readonly string KeyNormalizeLinesEndRegexPattern = "(?-----END [A-Z ]+[-]+)$"; @@ -42,8 +40,6 @@ internal static class Constants ");$", RegexOptions.Compiled ); - //public static readonly string MAuthTokenRequestPathV2 = "/mauth/v2/security_tokens/"; - public static readonly string MAuthHeaderKeyV2 = "MCC-Authentication"; public static readonly string MAuthTimeHeaderKeyV2 = "MCC-Time"; diff --git a/src/Medidata.MAuth.Core/MAuthCore.cs b/src/Medidata.MAuth.Core/MAuthCore.cs index 2f71ade..571edd6 100644 --- a/src/Medidata.MAuth.Core/MAuthCore.cs +++ b/src/Medidata.MAuth.Core/MAuthCore.cs @@ -11,8 +11,6 @@ namespace Medidata.MAuth.Core { internal class MAuthCore: IMAuthCore { - public MAuthCore () { } - /// /// Signs an HTTP request with the MAuth-specific authentication information. /// @@ -67,7 +65,7 @@ public async Task GetSignature(HttpRequestMessage request, Authenticatio { request.Method.Method.ToBytes(), Constants.NewLine, request.RequestUri.AbsolutePath.ToBytes(), Constants.NewLine, - (request.Content != null ? await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false) : new byte[] { }), + request.Content is null ? new byte[] {} : await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false), Constants.NewLine, authInfo.ApplicationUuid.ToHyphenString().ToBytes(), Constants.NewLine, authInfo.SignedTime.ToUnixTimeSeconds().ToString().ToBytes() diff --git a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs index 45c602f..aeab7b5 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs @@ -8,7 +8,6 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; -using System.Web; using Newtonsoft.Json.Linq; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; @@ -179,9 +178,6 @@ public static byte[] Concat(this byte[][] values) public static IDictionary GetQueryStringParams(this string queryString) { - //var dictionary = queryString.Replace("?", "").Split('&').ToDictionary(x => x.Split('=')[0], x => x.Split('=')[1]); - //return dictionary; - var queryStrings = new SortedDictionary(); queryString.Replace("?", "") .Split('&') @@ -219,7 +215,7 @@ public static string BuildEncodedQueryParams(this IDictionary qu /// Provides an SHA512 hash value of bytes. /// /// The byte value for calculating the hash. - /// The UTF8 encoded bytes of SHA512 hash of the input value as a hex-encoded byte array. + /// SHA512 hash of the input value. public static byte[] AsSha512HashV2(this byte[] value) => SHA512.Create().ComputeHash(value); diff --git a/src/Medidata.MAuth.Core/MAuthCoreV2.cs b/src/Medidata.MAuth.Core/MAuthCoreV2.cs index 60b1a0c..2bde6e9 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreV2.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreV2.cs @@ -6,9 +6,6 @@ namespace Medidata.MAuth.Core { - /// - /// - /// internal class MAuthCoreV2 : IMAuthCore { /// diff --git a/src/Medidata.MAuth.Core/UtilityExtensions.cs b/src/Medidata.MAuth.Core/UtilityExtensions.cs index e3dd47a..7fb474b 100644 --- a/src/Medidata.MAuth.Core/UtilityExtensions.cs +++ b/src/Medidata.MAuth.Core/UtilityExtensions.cs @@ -71,10 +71,10 @@ public static Task Authenticate(this HttpRequestMessage request, MAuthOpti new MAuthAuthenticator(options).AuthenticateRequest(request); /// - /// + /// Determines the MAuth version enumerator reading authHeader. /// /// - /// + /// Enumeration value of MAuthVersion. public static MAuthVersion GetVersionFromAuthenticationHeader(this string authHeader) { return authHeader.StartsWith(MAuthVersion.MWSV2.ToString()) diff --git a/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs b/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs index a1b8b22..489d63b 100644 --- a/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs @@ -1,7 +1,6 @@ using System; using System.Net.Http; using System.Security.Cryptography; -using System.Text; using System.Threading.Tasks; using Medidata.MAuth.Core; using Medidata.MAuth.Tests.Infrastructure; @@ -89,7 +88,7 @@ public static async Task GetSignature_WithRequest_WillReturnTheCorrectSignature( var version = "MWSV2"; var mAuthCore = new MAuthCoreV2(); var queryParams = !string.IsNullOrEmpty(testData.Url.Query) ? - testData.Url.Query.GetQueryStringParams().SortByKeyAscending().BuildEncodedQueryParams().ToBytes(): new byte[] { }; + testData.Url.Query.GetQueryStringParams().BuildEncodedQueryParams().ToBytes(): new byte[] { }; var content = !string.IsNullOrEmpty(testData.Base64Content) ? Convert.FromBase64String(testData.Base64Content) : new byte[] { }; diff --git a/tests/Medidata.MAuth.Tests/Mocks/RequestData/DELETE.json b/tests/Medidata.MAuth.Tests/Mocks/RequestData/DELETE.json index 8138753..e674ac3 100644 --- a/tests/Medidata.MAuth.Tests/Mocks/RequestData/DELETE.json +++ b/tests/Medidata.MAuth.Tests/Mocks/RequestData/DELETE.json @@ -3,6 +3,5 @@ "Method": "DELETE", "ApplicationUuid": "192cce84-8466-490e-b03e-074f82da3ee2", "SignedTime": "2016-08-01 05:40:38 +0000", - "Payload": "C6vv/XIurgTnbp3YMPcejteTrVfXtb3NY/2A9NFmiMh85uDQ9rnZr1fXPdmX5tQX2rUh5g3KT65ky3c4gi0/fwKJbFLCv+AoTtNl1jTwam4ZBp76/8pd1WSEnXBMyKu9qpCpnEhxQZLJkBTjmg1gyin4/r+slxDfktwSYPUkkeK41ewgwudI/t76qZOE9kX2VZpqHAgGBKRACbOpclmkdtMQ3Fbg/pSWrPN5ve34+TCNZbcqPI7E0dRfAzcAOD5DNftVcv2jKy3vJ68J0HnvdDIj03xVemjMRewDveD4OG4DGjP6HF4MouYy0QnbWIE+EVvUFRy81xhneTR10yDcYA==", - "PayloadV2": "ZDU3p0J5Tate1lJxtSixlIxeiTUEVkWXQ3VEKuwcQUeFxzfDsy4G6A8rheVf+LJorGpDLIad88mFsMHFuNoRVMJP+jt+XZFcV2P+UrKwn2KKGiTxD617Fwfltw5GrasPXoNkcVMXSBWS2MvAX5Z7IuAO4vlxn0XqiHUhG5FhH3sSIObEuuIPHPpcyXN9bUGFCzOvKtLQf7b+Fd+axOPX1DjDIsGHV/jH6iPfTQxlFQW9qKpn9CofzTGtpwq85YIzBIskl99fJ13rr/qLgTVEjiYaz8d0vREZgLmks04ofkVc+8SZqVFDd26+qz5zIrnK/Djf69syf/d7kYDtJ1XmUg==" + "Payload": "C6vv/XIurgTnbp3YMPcejteTrVfXtb3NY/2A9NFmiMh85uDQ9rnZr1fXPdmX5tQX2rUh5g3KT65ky3c4gi0/fwKJbFLCv+AoTtNl1jTwam4ZBp76/8pd1WSEnXBMyKu9qpCpnEhxQZLJkBTjmg1gyin4/r+slxDfktwSYPUkkeK41ewgwudI/t76qZOE9kX2VZpqHAgGBKRACbOpclmkdtMQ3Fbg/pSWrPN5ve34+TCNZbcqPI7E0dRfAzcAOD5DNftVcv2jKy3vJ68J0HnvdDIj03xVemjMRewDveD4OG4DGjP6HF4MouYy0QnbWIE+EVvUFRy81xhneTR10yDcYA==" } \ No newline at end of file diff --git a/tests/Medidata.MAuth.Tests/Mocks/RequestData/GET.json b/tests/Medidata.MAuth.Tests/Mocks/RequestData/GET.json index aee692d..49f5f8f 100644 --- a/tests/Medidata.MAuth.Tests/Mocks/RequestData/GET.json +++ b/tests/Medidata.MAuth.Tests/Mocks/RequestData/GET.json @@ -3,6 +3,5 @@ "Method": "GET", "ApplicationUuid": "192cce84-8466-490e-b03e-074f82da3ee2", "SignedTime": "2016-08-01 03:22:47 +0000", - "Payload": "hg1RmUN/PNfiOpGRieEd77k9QnUluHag5UP7v/3azj8WHNmG9aHPZjPvwzDgCLCPFTthtaWOk5LSgHjQUcr/qWcg5o+Z+dbvzfbZpqkrwkzxrPLdzdZkulXSXNs+xEBFs1dQxLBL58jJ3BOvWPscWGYzd8IQThJ6rjNZiD+/lMGyDBm0BJgOHcBJ0AThywdEvOMXYQqR3daDM8OY2souleMzU1t0A1naq67TqTMAi5vEVCXvJ4AJhoNvrjATpzg/hM7ozXy91NgO0nVqJErcJBu3qX8UU4sqsQOP26ROTNHed/cBMdjzQZ/Bm/jMtWRtPmGozNzW12utRzh/Twzagg==", - "PayloadV2": "kH00qgDJVwtGp+aUTlC935c+MOUGgyMY7zrCL/TeaaUu3MNq5CA/v8/NWwvtoqExHDnOv1sNP9Fa3Sb46WnudHmDW0/IAlbg9VtH8E+A/GIcUbsOix+R32UH7TzpnULG317A7N4Q+CS9WhvBrMydJ32Lfi+SpKbdb1O2QtPjuauXG4AbGtk4Qs7kQ+LwJk0QfIDBqT6qZturzOawDk4KymGOErtTblCkQb7oo78zehsPZSW3Ca7sJYty23rKByqemhJkz71b4DUoZaeI8xCcU1pcSU6gp4oexYqC1RAFNB6hnVZW8GcHlAyFu42jCEqR4HTJFPjLN4eI266YqwBE3w==" + "Payload": "hg1RmUN/PNfiOpGRieEd77k9QnUluHag5UP7v/3azj8WHNmG9aHPZjPvwzDgCLCPFTthtaWOk5LSgHjQUcr/qWcg5o+Z+dbvzfbZpqkrwkzxrPLdzdZkulXSXNs+xEBFs1dQxLBL58jJ3BOvWPscWGYzd8IQThJ6rjNZiD+/lMGyDBm0BJgOHcBJ0AThywdEvOMXYQqR3daDM8OY2souleMzU1t0A1naq67TqTMAi5vEVCXvJ4AJhoNvrjATpzg/hM7ozXy91NgO0nVqJErcJBu3qX8UU4sqsQOP26ROTNHed/cBMdjzQZ/Bm/jMtWRtPmGozNzW12utRzh/Twzagg==" } \ No newline at end of file diff --git a/tests/Medidata.MAuth.Tests/Mocks/RequestData/POST.json b/tests/Medidata.MAuth.Tests/Mocks/RequestData/POST.json index 49f63d1..b639f9a 100644 --- a/tests/Medidata.MAuth.Tests/Mocks/RequestData/POST.json +++ b/tests/Medidata.MAuth.Tests/Mocks/RequestData/POST.json @@ -4,6 +4,5 @@ "ApplicationUuid": "192cce84-8466-490e-b03e-074f82da3ee2", "SignedTime": "2016-08-01 03:31:54 +0000", "Base64Content": "VGhlIHJlcXVlc3QgYm9keSBmb3IgdGhlIFBPU1QgbWVzc2FnZS4=", - "Payload": "cdsjH35jR0IR77riAxMkYOWT8ZNrZzlF336HM274sFvUwV57vZ0200/sO4f2NXkwru9iiLIDAoo7ZKI8atWt96cEGVXE+5EkN8BJa0iLkhaqZ9fEqj6F6YiB6QH6JJa2RiAW4xZmvFFVjjc6crxkk9MxB8qv/l2Ta08JMZg6npfjQG6OQ0DDPckkYWKHXq2f8dfwoWe/jhBBrzT9TdMmxO74GT/e5FffcBIQFVw6Cm1DBoQFETU61SMtI/4AS5hewGMbHjvCdIRzV7Wg3GDnLsNCXqk/pNV0vWLL+jXd4Zqx8VUhFDUGq7yn9ZjslBhkca80DoQmX7Zgd/4BGUzTxg==", - "PayloadV2": "AsPLP5sgJaHV25wLh+hD4cYxtJcj5JIfvRcCtiSxOYZcpS0EAEGWIkkE24AA10erFnTJYGVaSyFShuBHnUpZ4NiU0MOEwcAzT6q99vspDh594+GfNoJV93O2Frc+lX1bxEVzdzqGIvaS+1kLRsgUPYSTyNGWROloYVyfOoFMiZirllA+sVOaKUrVjZ1D/TV3PX7TzcFc9ZifxfSg+y7FZ0PunvQfxH9hwaf3jJ8eQLSDOn7hJzbffgtyvlm2Wtw20H/bUcPUj2H0dz+mJVnOTilmzofrsBx2WPHrMQQnahMg5HOn95XkKBpWhto3RVN/ldtspqDS8nC8kIifyCwMRA==" + "Payload": "cdsjH35jR0IR77riAxMkYOWT8ZNrZzlF336HM274sFvUwV57vZ0200/sO4f2NXkwru9iiLIDAoo7ZKI8atWt96cEGVXE+5EkN8BJa0iLkhaqZ9fEqj6F6YiB6QH6JJa2RiAW4xZmvFFVjjc6crxkk9MxB8qv/l2Ta08JMZg6npfjQG6OQ0DDPckkYWKHXq2f8dfwoWe/jhBBrzT9TdMmxO74GT/e5FffcBIQFVw6Cm1DBoQFETU61SMtI/4AS5hewGMbHjvCdIRzV7Wg3GDnLsNCXqk/pNV0vWLL+jXd4Zqx8VUhFDUGq7yn9ZjslBhkca80DoQmX7Zgd/4BGUzTxg==" } \ No newline at end of file diff --git a/tests/Medidata.MAuth.Tests/Mocks/RequestData/POSTWithBinaryData.json b/tests/Medidata.MAuth.Tests/Mocks/RequestData/POSTWithBinaryData.json index ca1a2e9..f551516 100644 --- a/tests/Medidata.MAuth.Tests/Mocks/RequestData/POSTWithBinaryData.json +++ b/tests/Medidata.MAuth.Tests/Mocks/RequestData/POSTWithBinaryData.json @@ -4,6 +4,5 @@ "ApplicationUuid": "192cce84-8466-490e-b03e-074f82da3ee2", "SignedTime": "2018-10-02 08:21:03 +0000", "Base64Content": "N3q8ryccAAPP2NhAnAoAAAAAAABUAAAAAAAAAJp51s8AJhvKRmda8ne4fYbYQdsFNc2DpXwSpQXbkL0vFNNxcpaoin2EVnGNaiKYq549w1XvzKXD3XbQa2rmXszZWApBl1bI/FsLDKGC0VHA+Y6actTa9q0WzOvPT7/aMG+3/N3IbuV6O9dsvLNbWeoADUnubbql0yh6JA15cgda8pWI/9o6LnMsEPHBctImUxDUvnAdxMll37Vx9jnjKVTZ3FUqN6kJ6LpZgYlX3pFsgT8ABjgdhg4dIE7xwUdom4LWsylukG/JYGNuCi58JLtx5H8I1mUgnjpXDSL1Z6xRSpypddIQjv+3MpHD5iQlYTkhfwmczvSQZG+aSkOk6A5BL67QV4ozo03RH2nP3QqTQnwEWhmyvWV5AQGWukryjLbhilhIaN7oqS/B9Jhve666xIhfg8Ev7F+lrHgvtue1cnbaCVFXuyZJs0jYfRds4wy6Ytkh8dE=", - "Payload": "pXO9EecWIU1Qf0diqYhTypEfSnT822YFviS4K0Jq3OfZjpDb1fL5b8ePDgD9BXLLM1Sp8+wh90RyE3QkMnfuQL1pHD+ODnPfglIQFvOAtuqVEVHN3ztQ4l8LZcPD1pX3PC+8Aj3WzgbCaMDg6trArkZKFNE2IWuwuLvaWGHfANBCfF/VVINAtZ8ZUSDJrYT9vkfdFvQDsEYDwtdlt9fY6EvhNRtILXXafchtk0c9eC3VC1jBlWwcK+v1OM3YKKEa9oqR9wv7bjxS2sBfkVA6Av4cm1O6uUCYJoxbrKtmfU6sBHhBW55B+NLLoNajfxU+quaaoxmX/x3KaTW3QFVUYQ==", - "PayloadV2": "kbD5Fvx6Q8SjdGqRSusA5qAbYm9JD5dfbFE65X5uMxJ7yxSwZrJgL2Hhek5LFhrMupTiIziaVDkAvWJEl1pjpuFwSunxUshABwh/cpfPTkMTWF7cNAOUCGrT4JiqYDgtQdEEtPwUv1gUpvUkD/xdrG8HOiBXz3WTiYrlg2xcz4GI4m32tF7AMZScc02vdUVRPjAbvsK3IAfvE0iA1EDhlegWNncMc63M2wKdUU3xNjOcvNVYQqbGBjoLfbJWyrsaYbLxkuHmD8wi0p/pfFYe7ipszBUMuTT7Q/kNt/Z26s5oqlekL8JEoZk2XDDaGahK5vvMEMKs2xgdbzwk5ntZnQ==" + "Payload": "pXO9EecWIU1Qf0diqYhTypEfSnT822YFviS4K0Jq3OfZjpDb1fL5b8ePDgD9BXLLM1Sp8+wh90RyE3QkMnfuQL1pHD+ODnPfglIQFvOAtuqVEVHN3ztQ4l8LZcPD1pX3PC+8Aj3WzgbCaMDg6trArkZKFNE2IWuwuLvaWGHfANBCfF/VVINAtZ8ZUSDJrYT9vkfdFvQDsEYDwtdlt9fY6EvhNRtILXXafchtk0c9eC3VC1jBlWwcK+v1OM3YKKEa9oqR9wv7bjxS2sBfkVA6Av4cm1O6uUCYJoxbrKtmfU6sBHhBW55B+NLLoNajfxU+quaaoxmX/x3KaTW3QFVUYQ==" } \ No newline at end of file diff --git a/tests/Medidata.MAuth.Tests/Mocks/RequestData/PUT.json b/tests/Medidata.MAuth.Tests/Mocks/RequestData/PUT.json index 06b66bf..86dfcc2 100644 --- a/tests/Medidata.MAuth.Tests/Mocks/RequestData/PUT.json +++ b/tests/Medidata.MAuth.Tests/Mocks/RequestData/PUT.json @@ -4,6 +4,5 @@ "ApplicationUuid": "192cce84-8466-490e-b03e-074f82da3ee2", "SignedTime": "2016-08-01 05:38:19 +0000", "Base64Content": "VGhlIHJlcXVlc3QgYm9keSBmb3IgdGhlIFBVVCBtZXNzYWdlLg==", - "Payload": "h++GyB+btZfl0yRbhnIMqVJqytxFakYKpwvcVmI6ofTMSp5HjVSn54wwEVoCFEz1Esvc9rEwHlruavR1qxFSj7FBzwTM6eKiJywTSeNqDqf1SYmjppl7giz7KAGRY2bmxoyV0g63a5TncbbSmBKJSngYs1x1kskFILt8gDeEKHZHNxLqgPwXuzPpNUiwCvtxypIsC6YEHml59TRAXjsHJHC8HxxOWQW+io9Pb6w1adgI9nzekyxgQX8+LVZRn9mBLiux+6O5N5mY7V1yONeMifHwVhTMouyJDwGBda1/0E7IUlFv2wsxJIO4O/ulkwbk1XBECUdIe7ITtnrC6zY1Fg==", - "PayloadV2": "pFZP+yntKerPdAJ8OuTGcAQ+sE++XVfrjdvO6tuIXlL69J7B+VBrfkUMWkV77BQw590HEuAO+xn+4XB5YKB4T3de/UnIetUZB4itViIc9xiZEZHPaRNlUO70Tkjj3ZkUfbjG1X+jLmPfO7b2xtupnHjckb/pkwtsfO4MS1U7T9xnIWhkC+50SZvajiyg+Jf4mVzdabDYME4/prqzQc+uBVQy9n/wFSBJtkG3VMlsoHTbuIdij4ObeY6kgdakYMR0/CMnp7bXSnos0KvSxgRmY4+6lel2V+fyh02n3zPQUX1+Js5s47hywALuzJdjVMvc8q5jo1b1t6QrqUAAABYCdg==" + "Payload": "h++GyB+btZfl0yRbhnIMqVJqytxFakYKpwvcVmI6ofTMSp5HjVSn54wwEVoCFEz1Esvc9rEwHlruavR1qxFSj7FBzwTM6eKiJywTSeNqDqf1SYmjppl7giz7KAGRY2bmxoyV0g63a5TncbbSmBKJSngYs1x1kskFILt8gDeEKHZHNxLqgPwXuzPpNUiwCvtxypIsC6YEHml59TRAXjsHJHC8HxxOWQW+io9Pb6w1adgI9nzekyxgQX8+LVZRn9mBLiux+6O5N5mY7V1yONeMifHwVhTMouyJDwGBda1/0E7IUlFv2wsxJIO4O/ulkwbk1XBECUdIe7ITtnrC6zY1Fg==" } \ No newline at end of file From ed66a4aa91d05d4f66a357b3d84ca06a4751d9bc Mon Sep 17 00:00:00 2001 From: Prajon Date: Mon, 5 Aug 2019 16:03:12 -0400 Subject: [PATCH 10/40] Updated tests to use enum instead of string value of version --- tests/Medidata.MAuth.Tests/Infrastructure/RequestData.cs | 1 - .../Infrastructure/TestExtensions.cs | 8 ++++---- tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs | 9 +++++---- tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs | 9 +++++---- tests/Medidata.MAuth.Tests/MAuthSigningHandlerTests.cs | 2 +- tests/Medidata.MAuth.Tests/MAuthWebApiTests.cs | 3 ++- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/tests/Medidata.MAuth.Tests/Infrastructure/RequestData.cs b/tests/Medidata.MAuth.Tests/Infrastructure/RequestData.cs index 6eef43b..b1112e7 100644 --- a/tests/Medidata.MAuth.Tests/Infrastructure/RequestData.cs +++ b/tests/Medidata.MAuth.Tests/Infrastructure/RequestData.cs @@ -1,5 +1,4 @@ using System; -using System.Net.Http; using System.Runtime.Serialization; using Medidata.MAuth.Core; diff --git a/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs b/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs index bbb0f17..8457ef4 100644 --- a/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs +++ b/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs @@ -76,7 +76,7 @@ private static Task GetKeyFromResource(string keyName) return GetStringFromResource($"Medidata.MAuth.Tests.Mocks.Keys.{keyName}.pem"); } - public static HttpRequestMessage ToHttpRequestMessage(this RequestData data, string version = "MWS") + public static HttpRequestMessage ToHttpRequestMessage(this RequestData data, MAuthVersion version = MAuthVersion.MWS) { var result = new HttpRequestMessage(new HttpMethod(data.Method), data.Url) { @@ -85,7 +85,7 @@ public static HttpRequestMessage ToHttpRequestMessage(this RequestData data, str null, }; var headerKeys = version.GetHeaderKeys(); - var mauthHeader = version == "MWS" + var mauthHeader = version == MAuthVersion.MWS ? $"{version} {data.ApplicationUuidString}:{data.Payload}" : $"{version} {data.ApplicationUuidString}:{data.Payload};"; @@ -100,9 +100,9 @@ public static string ToStringContent(this string base64Content) => public static HttpMethod ToHttpMethod(this string method) => new HttpMethod(method); - private static (string mAuthHeaderKey, string mAuthTimeHeaderKey) GetHeaderKeys(this string version) + private static (string mAuthHeaderKey, string mAuthTimeHeaderKey) GetHeaderKeys(this MAuthVersion version) { - return (version == MAuthVersion.MWSV2.ToString()) + return (version == MAuthVersion.MWSV2) ? (Constants.MAuthHeaderKeyV2, Constants.MAuthTimeHeaderKeyV2) : (Constants.MAuthHeaderKey, Constants.MAuthTimeHeaderKey); } diff --git a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs index 8e571b5..57a9410 100644 --- a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Medidata.MAuth.Core; using Medidata.MAuth.Core.Exceptions; +using Medidata.MAuth.Core.Models; using Medidata.MAuth.Tests.Infrastructure; using Xunit; @@ -67,7 +68,7 @@ public static async Task AuthenticateRequest_WithValidMWSV2Request_WillAuthentic { // Arrange var testData = await method.FromResourceV2(); - var version = "MWSV2"; + var version = MAuthVersion.MWSV2; var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); var mAuthCore = new MAuthCoreV2(); @@ -127,7 +128,7 @@ public static async Task AuthenticateRequest_WithMWSV2Request_WithNumberOfAttemp { // Arrange var testData = await "GET".FromResourceV2(); - var version = "MWSV2"; + var version = MAuthVersion.MWSV2; var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( policy, shouldSucceedWithin: true)); var mAuthCore = new MAuthCoreV2(); @@ -192,7 +193,7 @@ public static async Task AuthenticateRequest_WithMWSV2Request_AfterNumberOfAttem { // Arrange var testData = await "GET".FromResource(); - var version = "MWSV2"; + var version = MAuthVersion.MWSV2; var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( policy, shouldSucceedWithin: false)); var mAuthCore = new MAuthCoreV2(); @@ -249,7 +250,7 @@ public static async Task SignRequest_WithMWSV2ValidRequest_WillSignProperly(stri { // Arrange var testData = await method.FromResourceV2(); - var version = "MWSV2"; + var version = MAuthVersion.MWSV2; var expectedMAuthHeader = testData.MAuthHeaderV2; var mAuthCore = new MAuthCoreV2(); diff --git a/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs b/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs index 489d63b..ac6ce81 100644 --- a/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs @@ -3,6 +3,7 @@ using System.Security.Cryptography; using System.Threading.Tasks; using Medidata.MAuth.Core; +using Medidata.MAuth.Core.Models; using Medidata.MAuth.Tests.Infrastructure; using Xunit; @@ -20,7 +21,7 @@ public async Task CalculatePayload_WithRequestAndAuthInfo_WillReturnCorrectPaylo { // Arrange var testData = await method.FromResourceV2(); - var version = "MWSV2"; + var version = MAuthVersion.MWSV2; var mAuthCore = new MAuthCoreV2(); var authInfo = new PrivateKeyAuthenticationInfo() @@ -43,7 +44,7 @@ public static async Task CalculatePayload_WithBinaryContent_WillCalculateTheProp // Arrange var testData = await "POSTWithBinaryData".FromResourceV2(); var mAuthCore = new MAuthCoreV2(); - var version = "MWSV2"; + var version = MAuthVersion.MWSV2; // Act var result = await mAuthCore.CalculatePayload(testData.ToHttpRequestMessage(version), new PrivateKeyAuthenticationInfo() { @@ -85,7 +86,7 @@ public static async Task GetSignature_WithRequest_WillReturnTheCorrectSignature( { // Arrange var testData = await method.FromResourceV2(); - var version = "MWSV2"; + var version = MAuthVersion.MWSV2; var mAuthCore = new MAuthCoreV2(); var queryParams = !string.IsNullOrEmpty(testData.Url.Query) ? testData.Url.Query.GetQueryStringParams().BuildEncodedQueryParams().ToBytes(): new byte[] { }; @@ -126,7 +127,7 @@ public static async Task AddAuthenticationInfo_WithRequestAndAuthInfo_WillAddCor // Arrange var testData = await method.FromResourceV2(); var expectedMAuthHeader = testData.MAuthHeaderV2; - var version = "MWSV2"; + var version = MAuthVersion.MWSV2; var mAuthCore = new MAuthCoreV2(); var authInfo = new PrivateKeyAuthenticationInfo() diff --git a/tests/Medidata.MAuth.Tests/MAuthSigningHandlerTests.cs b/tests/Medidata.MAuth.Tests/MAuthSigningHandlerTests.cs index e37a8c7..84b1f46 100644 --- a/tests/Medidata.MAuth.Tests/MAuthSigningHandlerTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthSigningHandlerTests.cs @@ -43,7 +43,7 @@ public static async Task SendAsync_WithValidMWSV2Request_WillSignProperly(string { // Arrange var testData = await method.FromResource(); - var version = "MWSV2"; + var version = MAuthVersion.MWSV2; var actual = new AssertSigningHandler(); var clientOptions = TestExtensions.ClientOptions(testData.SignedTime); clientOptions.MAuthVersion = MAuthVersion.MWSV2; diff --git a/tests/Medidata.MAuth.Tests/MAuthWebApiTests.cs b/tests/Medidata.MAuth.Tests/MAuthWebApiTests.cs index 27b9e86..bb252bd 100644 --- a/tests/Medidata.MAuth.Tests/MAuthWebApiTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthWebApiTests.cs @@ -2,6 +2,7 @@ using System.Net.Http; using System.Threading.Tasks; using Medidata.MAuth.Core; +using Medidata.MAuth.Core.Models; using Medidata.MAuth.Tests.Infrastructure; using Medidata.MAuth.WebApi; using Xunit; @@ -51,7 +52,7 @@ public static async Task MAuthAuthenticatingHandler_WithValidMWSV2Request_WillAu // Arrange var testData = await method.FromResourceV2(); var actual = new AssertSigningHandler(); - var version = "MWSV2"; + var version = MAuthVersion.MWSV2; var handler = new MAuthAuthenticatingHandler(new MAuthWebApiOptions() { ApplicationUuid = TestExtensions.ServerUuid, From aceb9655be89f399db5ec01398e55aa12ce8e9f9 Mon Sep 17 00:00:00 2001 From: Prajon Date: Tue, 6 Aug 2019 12:26:48 -0400 Subject: [PATCH 11/40] Extract query using utility method --- .../MAuthCoreExtensions.cs | 22 +++++-------------- src/Medidata.MAuth.Core/MAuthCoreV2.cs | 6 ++--- .../Medidata.MAuth.Core.csproj | 2 +- .../Medidata.MAuth.Tests/MAuthCoreV2Tests.cs | 4 ++-- 4 files changed, 10 insertions(+), 24 deletions(-) diff --git a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs index aeab7b5..d88820b 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.IO; using System.Linq; using System.Net.Http; @@ -176,23 +177,10 @@ public static byte[] Concat(this byte[][] values) return result; } - public static IDictionary GetQueryStringParams(this string queryString) + public static SortedDictionary SortByKeyAscending(this NameValueCollection queryStringParams) { - var queryStrings = new SortedDictionary(); - queryString.Replace("?", "") - .Split('&') - .ToList() - .ForEach((query) => { - var keyValue = query.Split('='); - queryStrings.Add(keyValue[0], keyValue[1]); - }); - return queryStrings; - } - - public static IDictionary SortByKeyAscending(this IDictionary queryStringParams) - { - var sortDictionary = new Dictionary(); - var list = queryStringParams.Keys.ToList(); + var sortDictionary = new SortedDictionary(); + var list = queryStringParams.AllKeys.ToList(); list.Sort(); foreach (var key in list) { @@ -201,7 +189,7 @@ public static IDictionary SortByKeyAscending(this IDictionary queryParams) + public static string BuildEncodedQueryParams(this SortedDictionary queryParams) { var encodedQueryStrings = new List(); foreach (var query in queryParams) diff --git a/src/Medidata.MAuth.Core/MAuthCoreV2.cs b/src/Medidata.MAuth.Core/MAuthCoreV2.cs index 2bde6e9..8dc3fd0 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreV2.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreV2.cs @@ -67,10 +67,8 @@ public async Task GetSignature(HttpRequestMessage request, Authenticatio var requestBodyDigest = requestBody.AsSha512HashV2(); var encodedCurrentSecondsSinceEpoch = authInfo.SignedTime.ToUnixTimeSeconds().ToString().ToBytes(); - - var encodedQueryParams = (!string.IsNullOrEmpty(request.RequestUri.Query)) - ? request.RequestUri.Query.GetQueryStringParams().BuildEncodedQueryParams().ToBytes() - : new byte[] { }; + var encodedQueryParams = request.RequestUri.ParseQueryString().SortByKeyAscending() + .BuildEncodedQueryParams().ToBytes() ?? new byte[] { }; return new byte[][] { diff --git a/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj b/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj index bb9a4e7..b814394 100644 --- a/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj +++ b/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj @@ -7,10 +7,10 @@ netstandard2.0;net461 Medidata.MAuth.Core medidata;mauth;hmac;authentication;core;httpclient;messagehandler - + diff --git a/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs b/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs index ac6ce81..a2cac93 100644 --- a/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs @@ -88,8 +88,8 @@ public static async Task GetSignature_WithRequest_WillReturnTheCorrectSignature( var testData = await method.FromResourceV2(); var version = MAuthVersion.MWSV2; var mAuthCore = new MAuthCoreV2(); - var queryParams = !string.IsNullOrEmpty(testData.Url.Query) ? - testData.Url.Query.GetQueryStringParams().BuildEncodedQueryParams().ToBytes(): new byte[] { }; + var queryParams = testData.Url.ParseQueryString().SortByKeyAscending().BuildEncodedQueryParams().ToBytes() ?? + new byte[] { }; var content = !string.IsNullOrEmpty(testData.Base64Content) ? Convert.FromBase64String(testData.Base64Content) : new byte[] { }; From 01e1e3ed815051d70f0c7eab699ac3b52c0035c6 Mon Sep 17 00:00:00 2001 From: Prajon Date: Tue, 6 Aug 2019 16:46:29 -0400 Subject: [PATCH 12/40] Feedback implementation --- src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 4 +--- src/Medidata.MAuth.Core/MAuthCoreFactory.cs | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index caf7ce8..ba3d799 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -1,6 +1,5 @@ using System; using System.Net.Http; -using System.Security.Cryptography; using System.Threading.Tasks; using Medidata.MAuth.Core.Exceptions; using Microsoft.Extensions.Caching.Memory; @@ -12,7 +11,6 @@ namespace Medidata.MAuth.Core internal class MAuthAuthenticator { private readonly MAuthOptionsBase options; - private MAuthRequestRetrier retrier; private readonly IMemoryCache cache = new MemoryCache(new MemoryCacheOptions()); private IMAuthCore mAuthCore; @@ -80,7 +78,7 @@ private Task GetApplicationInfo(Guid applicationUuid, MAuthVers { mAuthCore = MAuthCoreFactory.Instantiate(version); var tokenRequestPath = mAuthCore.GetMAuthTokenRequestPath(); - retrier = new MAuthRequestRetrier(options, version); + var retrier = new MAuthRequestRetrier(options, version); var response = await retrier.GetSuccessfulResponse( applicationUuid, CreateRequest, tokenRequestPath, diff --git a/src/Medidata.MAuth.Core/MAuthCoreFactory.cs b/src/Medidata.MAuth.Core/MAuthCoreFactory.cs index 6bf3c26..49b73c7 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreFactory.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreFactory.cs @@ -12,7 +12,7 @@ public static IMAuthCore Instantiate(MAuthVersion version = MAuthVersion.MWS) else if (version == MAuthVersion.MWS) return new MAuthCore(); - throw new InvalidVersionException("Version is not recognized"); + throw new InvalidVersionException($"Version is not recognized:{version}"); } } } From b0fbf62e93203c57c1f78ffcbef194dedf0247c1 Mon Sep 17 00:00:00 2001 From: Prajon Date: Tue, 6 Aug 2019 17:15:14 -0400 Subject: [PATCH 13/40] Update feedback --- src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 5 ++--- src/Medidata.MAuth.Core/MAuthCoreExtensions.cs | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index ba3d799..a799d15 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -12,7 +12,6 @@ internal class MAuthAuthenticator { private readonly MAuthOptionsBase options; private readonly IMemoryCache cache = new MemoryCache(new MemoryCacheOptions()); - private IMAuthCore mAuthCore; public Guid ApplicationUuid => options.ApplicationUuid; @@ -39,7 +38,7 @@ public async Task AuthenticateRequest(HttpRequestMessage request) if (options.DisableV1 && version == MAuthVersion.MWS) throw new InvalidVersionException($"Authentication with {version} version is disabled."); - mAuthCore = MAuthCoreFactory.Instantiate(version); + var mAuthCore = MAuthCoreFactory.Instantiate(version); var authInfo = mAuthCore.GetAuthenticationInfo(request); var appInfo = await GetApplicationInfo(authInfo.ApplicationUuid, version); @@ -76,7 +75,7 @@ public async Task AuthenticateRequest(HttpRequestMessage request) private Task GetApplicationInfo(Guid applicationUuid, MAuthVersion version) => cache.GetOrCreateAsync(applicationUuid, async entry => { - mAuthCore = MAuthCoreFactory.Instantiate(version); + var mAuthCore = MAuthCoreFactory.Instantiate(version); var tokenRequestPath = mAuthCore.GetMAuthTokenRequestPath(); var retrier = new MAuthRequestRetrier(options, version); var response = await retrier.GetSuccessfulResponse( diff --git a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs index d88820b..8de5219 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs @@ -181,7 +181,6 @@ public static SortedDictionary SortByKeyAscending(this NameValue { var sortDictionary = new SortedDictionary(); var list = queryStringParams.AllKeys.ToList(); - list.Sort(); foreach (var key in list) { sortDictionary.Add(key, queryStringParams[key]); From e85025d67df145cc4c2c93dc8ccb1a2003a9cbd3 Mon Sep 17 00:00:00 2001 From: Prajon Date: Wed, 7 Aug 2019 09:37:32 -0400 Subject: [PATCH 14/40] Update Xunit packages and replace `dotnet xunit` with `dotnet test` to fix appveyor build --- build/build.ps1 | 2 +- tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/build/build.ps1 b/build/build.ps1 index fc36019..91ddcb0 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -67,7 +67,7 @@ Write-Host "Running unit tests..." -ForegroundColor Cyan Push-Location -Path .\tests\Medidata.MAuth.Tests -dotnet xunit +dotnet test Pop-Location diff --git a/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj b/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj index d7c6251..e0dd322 100644 --- a/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj +++ b/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj @@ -72,8 +72,11 @@ - - + + all + runtime; build; native; contentfiles; analyzers + + From a44d7cdd813eba04e0f1d2b1ad7ebbc183106664 Mon Sep 17 00:00:00 2001 From: Prajon Date: Wed, 7 Aug 2019 11:56:41 -0400 Subject: [PATCH 15/40] Updated building encodedQueryString to properly sort by character point encoding --- .../MAuthCoreExtensions.cs | 27 +++++++++---------- src/Medidata.MAuth.Core/MAuthCoreV2.cs | 4 +-- .../Medidata.MAuth.Tests/MAuthCoreV2Tests.cs | 4 +-- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs index 8de5219..60e4610 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs @@ -177,24 +177,21 @@ public static byte[] Concat(this byte[][] values) return result; } - public static SortedDictionary SortByKeyAscending(this NameValueCollection queryStringParams) - { - var sortDictionary = new SortedDictionary(); - var list = queryStringParams.AllKeys.ToList(); - foreach (var key in list) - { - sortDictionary.Add(key, queryStringParams[key]); - } - return sortDictionary; - } - - public static string BuildEncodedQueryParams(this SortedDictionary queryParams) + /// + /// Builds Encoded QueryString after sort by code point and then uri encode key and values + /// + /// + /// EncodedQueryParameter string. + public static string BuildEncodedQueryParams(this string queryString) { var encodedQueryStrings = new List(); - foreach (var query in queryParams) + var queryArray = queryString.Split('&'); + Array.Sort(queryArray, StringComparer.Ordinal); + Array.ForEach(queryArray, x => { - encodedQueryStrings.Add($"{Uri.EscapeUriString(query.Key)}={Uri.EscapeUriString(query.Value)}"); - } + var keyValue = x.Split('='); + encodedQueryStrings.Add($"{Uri.EscapeUriString(keyValue[0])}={Uri.EscapeUriString(keyValue[1])}"); + }); return string.Join("&", encodedQueryStrings); } diff --git a/src/Medidata.MAuth.Core/MAuthCoreV2.cs b/src/Medidata.MAuth.Core/MAuthCoreV2.cs index 8dc3fd0..61c4eda 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreV2.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreV2.cs @@ -67,8 +67,8 @@ public async Task GetSignature(HttpRequestMessage request, Authenticatio var requestBodyDigest = requestBody.AsSha512HashV2(); var encodedCurrentSecondsSinceEpoch = authInfo.SignedTime.ToUnixTimeSeconds().ToString().ToBytes(); - var encodedQueryParams = request.RequestUri.ParseQueryString().SortByKeyAscending() - .BuildEncodedQueryParams().ToBytes() ?? new byte[] { }; + var encodedQueryParams = !string.IsNullOrEmpty(request.RequestUri.Query) ? + request.RequestUri.Query.Replace("?", "").BuildEncodedQueryParams().ToBytes() : new byte[] { }; return new byte[][] { diff --git a/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs b/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs index a2cac93..dc7b08d 100644 --- a/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs @@ -88,8 +88,8 @@ public static async Task GetSignature_WithRequest_WillReturnTheCorrectSignature( var testData = await method.FromResourceV2(); var version = MAuthVersion.MWSV2; var mAuthCore = new MAuthCoreV2(); - var queryParams = testData.Url.ParseQueryString().SortByKeyAscending().BuildEncodedQueryParams().ToBytes() ?? - new byte[] { }; + var queryParams = !string.IsNullOrEmpty(testData.Url.Query) ? + testData.Url.Query.Replace("?","").BuildEncodedQueryParams().ToBytes() :new byte[] { }; var content = !string.IsNullOrEmpty(testData.Base64Content) ? Convert.FromBase64String(testData.Base64Content) : new byte[] { }; From f4392c2f5f696803d40dcac60fbeefcbefa29baf Mon Sep 17 00:00:00 2001 From: Prajon Date: Wed, 7 Aug 2019 13:19:54 -0400 Subject: [PATCH 16/40] Moved 'GetAuthenticationInfo(..)' to 'MAuthenticator' and made necessary updates --- src/Medidata.MAuth.Core/IMAuthCore.cs | 2 - src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 31 ++++++++++++- src/Medidata.MAuth.Core/MAuthCore.cs | 27 ----------- src/Medidata.MAuth.Core/MAuthCoreV2.cs | 26 ----------- src/Medidata.MAuth.Core/UtilityExtensions.cs | 12 +++++ .../Infrastructure/MAuthServerHandler.cs | 4 +- .../Infrastructure/TestExtensions.cs | 7 --- .../MAuthAuthenticatorTests.cs | 45 +++++++++++++++++++ tests/Medidata.MAuth.Tests/MAuthCoreTests.cs | 26 ----------- .../Medidata.MAuth.Tests/MAuthCoreV2Tests.cs | 25 ----------- 10 files changed, 90 insertions(+), 115 deletions(-) diff --git a/src/Medidata.MAuth.Core/IMAuthCore.cs b/src/Medidata.MAuth.Core/IMAuthCore.cs index 7fa01c6..aa91b4d 100644 --- a/src/Medidata.MAuth.Core/IMAuthCore.cs +++ b/src/Medidata.MAuth.Core/IMAuthCore.cs @@ -11,8 +11,6 @@ internal interface IMAuthCore Task GetSignature(HttpRequestMessage request, AuthenticationInfo authInfo); - PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage request); - string GetMAuthTokenRequestPath(); } } diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index a799d15..7aaab2c 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -39,7 +39,7 @@ public async Task AuthenticateRequest(HttpRequestMessage request) throw new InvalidVersionException($"Authentication with {version} version is disabled."); var mAuthCore = MAuthCoreFactory.Instantiate(version); - var authInfo = mAuthCore.GetAuthenticationInfo(request); + var authInfo = GetAuthenticationInfo(request, version); var appInfo = await GetApplicationInfo(authInfo.ApplicationUuid, version); return mAuthCore.Verify(authInfo.Payload, await mAuthCore.GetSignature(request, authInfo), @@ -97,5 +97,34 @@ private Task GetApplicationInfo(Guid applicationUuid, MAuthVers private HttpRequestMessage CreateRequest(Guid applicationUuid, string tokenRequestPath) => new HttpRequestMessage(HttpMethod.Get, new Uri(options.MAuthServiceUrl, $"{tokenRequestPath}{applicationUuid.ToHyphenString()}.json")); + + /// + /// Extracts the authentication information from a . + /// + /// The request that has the authentication information. + /// /// Enum value of the MAuthVersion. + /// The authentication information with the payload from the request. + public PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage request, MAuthVersion version) + { + var headerKeys = version.GetHeaderKeys(); + var authHeader = request.Headers.GetFirstValueOrDefault(headerKeys.mAuthHeaderKey); + + if (authHeader == null) + throw new ArgumentNullException(nameof(authHeader), "The MAuth header is missing from the request."); + + var signedTime = request.Headers.GetFirstValueOrDefault(headerKeys.mAuthTimeHeaderKey); + + if (signedTime == default(long)) + throw new ArgumentException("Invalid MAuth signed time header value.", nameof(signedTime)); + + var (uuid, payload) = authHeader.ParseAuthenticationHeader(); + + return new PayloadAuthenticationInfo() + { + ApplicationUuid = uuid, + Payload = Convert.FromBase64String(payload), + SignedTime = signedTime.FromUnixTimeSeconds() + }; + } } } diff --git a/src/Medidata.MAuth.Core/MAuthCore.cs b/src/Medidata.MAuth.Core/MAuthCore.cs index 571edd6..d451d56 100644 --- a/src/Medidata.MAuth.Core/MAuthCore.cs +++ b/src/Medidata.MAuth.Core/MAuthCore.cs @@ -116,33 +116,6 @@ internal async Task CalculatePayload(HttpRequestMessage request, Private return Convert.ToBase64String(signer.ProcessBlock(unsignedData, 0, unsignedData.Length)); } - /// - /// Extracts the authentication information from a . - /// - /// The request that has the authentication information. - /// The authentication information with the payload from the request. - public PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage request) - { - var authHeader = request.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKey); - - if (authHeader == null) - throw new ArgumentNullException(nameof(authHeader), "The MAuth header is missing from the request."); - - var signedTime = request.Headers.GetFirstValueOrDefault(Constants.MAuthTimeHeaderKey); - - if (signedTime == default(long)) - throw new ArgumentException("Invalid MAuth signed time header value.", nameof(signedTime)); - - var (uuid, payload) = authHeader.ParseAuthenticationHeader(); - - return new PayloadAuthenticationInfo() - { - ApplicationUuid = uuid, - Payload = Convert.FromBase64String(payload), - SignedTime = signedTime.FromUnixTimeSeconds() - }; - } - /// /// Determines the correct token request path. /// diff --git a/src/Medidata.MAuth.Core/MAuthCoreV2.cs b/src/Medidata.MAuth.Core/MAuthCoreV2.cs index 61c4eda..47fdcb3 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreV2.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreV2.cs @@ -126,32 +126,6 @@ internal async Task CalculatePayload( return Convert.ToBase64String(signer.SignHash(unsignedData, CryptoConfig.MapNameToOID("SHA512"))); } - /// - /// Extracts the authentication information from a . - /// - /// The request that has the authentication information. - /// The authentication information with the payload from the request. - public PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage request) - { - var authHeader = request.Headers.GetFirstValueOrDefault(Constants.MAuthHeaderKeyV2); - - if (authHeader == null) - throw new ArgumentNullException(nameof(authHeader), "The MAuth header is missing from the request."); - - var signedTime = request.Headers.GetFirstValueOrDefault(Constants.MAuthTimeHeaderKeyV2); - - if (signedTime == default(long)) - throw new ArgumentException("Invalid MAuth signed time header value.", nameof(signedTime)); - - var (uuid, payload) = authHeader.ParseAuthenticationHeader(); - - return new PayloadAuthenticationInfo() - { - ApplicationUuid = uuid, - Payload = Convert.FromBase64String(payload), - SignedTime = signedTime.FromUnixTimeSeconds() - }; - } /// /// Determines the correct token request path. /// diff --git a/src/Medidata.MAuth.Core/UtilityExtensions.cs b/src/Medidata.MAuth.Core/UtilityExtensions.cs index 7fb474b..4eb9e66 100644 --- a/src/Medidata.MAuth.Core/UtilityExtensions.cs +++ b/src/Medidata.MAuth.Core/UtilityExtensions.cs @@ -94,5 +94,17 @@ public static string GetAuthHeaderValue(this HttpRequestMessage request) return authHeader ?? throw new ArgumentNullException(nameof(authHeader), "The MAuth header is missing from the request."); } + + /// + /// Gets the MAuthHeader values based on the enum value of the version. + /// + /// + /// MAuthHeaderKey and MAuthTimeHeaderKey. + public static (string mAuthHeaderKey, string mAuthTimeHeaderKey) GetHeaderKeys(this MAuthVersion version) + { + return (version == MAuthVersion.MWSV2) + ? (Constants.MAuthHeaderKeyV2, Constants.MAuthTimeHeaderKeyV2) + : (Constants.MAuthHeaderKey, Constants.MAuthTimeHeaderKey); + } } } diff --git a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs index 28867ff..2d497a3 100644 --- a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs +++ b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs @@ -25,7 +25,9 @@ protected override async Task SendAsync( if (currentNumberOfAttempts < SucceedAfterThisManyAttempts) return new HttpResponseMessage(HttpStatusCode.ServiceUnavailable); - var authInfo = mAuthCore.GetAuthenticationInfo(request); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); + + var authInfo = authenticator.GetAuthenticationInfo(request, version); if (!mAuthCore.Verify(authInfo.Payload, await mAuthCore.GetSignature(request, authInfo), diff --git a/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs b/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs index 8457ef4..a9cbf3f 100644 --- a/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs +++ b/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs @@ -99,12 +99,5 @@ public static string ToStringContent(this string base64Content) => base64Content == null ? null : Encoding.UTF8.GetString(Convert.FromBase64String(base64Content)); public static HttpMethod ToHttpMethod(this string method) => new HttpMethod(method); - - private static (string mAuthHeaderKey, string mAuthTimeHeaderKey) GetHeaderKeys(this MAuthVersion version) - { - return (version == MAuthVersion.MWSV2) - ? (Constants.MAuthHeaderKeyV2, Constants.MAuthTimeHeaderKeyV2) - : (Constants.MAuthHeaderKey, Constants.MAuthTimeHeaderKey); - } } } diff --git a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs index 57a9410..578bfc8 100644 --- a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Net; +using System.Net.Http; using System.Threading.Tasks; using Medidata.MAuth.Core; using Medidata.MAuth.Core.Exceptions; @@ -296,5 +297,49 @@ public static async Task AuthenticateRequest_WithMWSVersion_WithDisableV1_WillTh Assert.NotNull(exception); Assert.Equal("Authentication with MWS version is disabled.", exception.Message); } + + [Theory] + [InlineData("GET")] + [InlineData("DELETE")] + [InlineData("POST")] + [InlineData("PUT")] + public static async Task GetAuthenticationInfo_WithSignedRequest_ForMWSV2Version_WillReturnCorrectAuthInfo(string method) + { + // Arrange + var testData = await method.FromResourceV2(); + var version = MAuthVersion.MWSV2; + var testOptions = TestExtensions.ServerOptions; + var authenticator = new MAuthAuthenticator(testOptions); + + // Act + var actual = authenticator.GetAuthenticationInfo(testData.ToHttpRequestMessage(version), version); + + // Assert + Assert.Equal(testData.ApplicationUuid, actual.ApplicationUuid); + Assert.Equal(Convert.FromBase64String(testData.Payload), actual.Payload); + Assert.Equal(testData.SignedTime, actual.SignedTime); + } + + [Theory] + [InlineData("GET")] + [InlineData("DELETE")] + [InlineData("POST")] + [InlineData("PUT")] + public static async Task GetAuthenticationInfo_WithSignedRequest_ForMWSVersion_WillReturnCorrectAuthInfo(string method) + { + // Arrange + var testData = await method.FromResource(); + var version = MAuthVersion.MWS; + var testOptions = TestExtensions.ServerOptions; + var authenticator = new MAuthAuthenticator(testOptions); + + // Act + var actual = authenticator.GetAuthenticationInfo(testData.ToHttpRequestMessage(version), version); + + // Assert + Assert.Equal(testData.ApplicationUuid, actual.ApplicationUuid); + Assert.Equal(Convert.FromBase64String(testData.Payload), actual.Payload); + Assert.Equal(testData.SignedTime, actual.SignedTime); + } } } diff --git a/tests/Medidata.MAuth.Tests/MAuthCoreTests.cs b/tests/Medidata.MAuth.Tests/MAuthCoreTests.cs index 059f62d..9ba74ea 100644 --- a/tests/Medidata.MAuth.Tests/MAuthCoreTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthCoreTests.cs @@ -154,31 +154,5 @@ public static void AsCipherParameters_WithDifferentLineEnding_WillReadTheKeysSuc // Assert Assert.Null(exception); } - - [Theory] - [InlineData("GET")] - [InlineData("DELETE")] - [InlineData("POST")] - [InlineData("PUT")] - public static async Task GetAuthenticationInfo_WithSignedRequest_WillReturnCorrectAuthInfo(string method) - { - // Arrange - var testData = await method.FromResource(); - var request = new HttpRequestMessage(new HttpMethod(testData.Method), TestExtensions.TestUri); - - request.Headers.Add( - Constants.MAuthHeaderKey, testData.MAuthHeader); - request.Headers.Add(Constants.MAuthTimeHeaderKey, testData.SignedTimeUnixSeconds.ToString()); - var mAuthCore = new MAuthCore(); - - // Act - var actual = mAuthCore.GetAuthenticationInfo(request); - - // Assert - Assert.Equal(testData.ApplicationUuid, actual.ApplicationUuid); - Assert.Equal(Convert.FromBase64String(testData.Payload), actual.Payload); - Assert.Equal(testData.SignedTime, actual.SignedTime); - } - } } diff --git a/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs b/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs index dc7b08d..e36cad4 100644 --- a/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthCoreV2Tests.cs @@ -147,30 +147,5 @@ public static async Task AddAuthenticationInfo_WithRequestAndAuthInfo_WillAddCor actual.Headers.GetFirstValueOrDefault(Constants.MAuthTimeHeaderKeyV2) ); } - - [Theory] - [InlineData("GET")] - [InlineData("DELETE")] - [InlineData("POST")] - [InlineData("PUT")] - public static async Task GetAuthenticationInfo_WithSignedRequest_WillReturnCorrectAuthInfo(string method) - { - // Arrange - var testData = await method.FromResourceV2(); - var request = new HttpRequestMessage(new HttpMethod(testData.Method), TestExtensions.TestUri); - - request.Headers.Add( - Constants.MAuthHeaderKeyV2, testData.MAuthHeaderV2); - request.Headers.Add(Constants.MAuthTimeHeaderKeyV2, testData.SignedTimeUnixSeconds.ToString()); - var mAuthCore = new MAuthCoreV2(); - - // Act - var actual = mAuthCore.GetAuthenticationInfo(request); - - // Assert - Assert.Equal(testData.ApplicationUuid, actual.ApplicationUuid); - Assert.Equal(Convert.FromBase64String(testData.Payload), actual.Payload); - Assert.Equal(testData.SignedTime, actual.SignedTime); - } } } From 476d37bb45d787157ef93e11b7ae08b57a8db12b Mon Sep 17 00:00:00 2001 From: Prajon Date: Wed, 7 Aug 2019 14:22:50 -0400 Subject: [PATCH 17/40] Added unit test for 'BuildingEncodedQueryParams' --- .../MAuthCoreExtensions.cs | 2 +- .../MAuthCoreExtensionsTests.cs | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 tests/Medidata.MAuth.Tests/MAuthCoreExtensionsTests.cs diff --git a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs index 60e4610..364868d 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs @@ -190,7 +190,7 @@ public static string BuildEncodedQueryParams(this string queryString) Array.ForEach(queryArray, x => { var keyValue = x.Split('='); - encodedQueryStrings.Add($"{Uri.EscapeUriString(keyValue[0])}={Uri.EscapeUriString(keyValue[1])}"); + encodedQueryStrings.Add($"{Uri.EscapeDataString(keyValue[0])}={Uri.EscapeDataString(keyValue[1])}"); }); return string.Join("&", encodedQueryStrings); } diff --git a/tests/Medidata.MAuth.Tests/MAuthCoreExtensionsTests.cs b/tests/Medidata.MAuth.Tests/MAuthCoreExtensionsTests.cs new file mode 100644 index 0000000..daeaae1 --- /dev/null +++ b/tests/Medidata.MAuth.Tests/MAuthCoreExtensionsTests.cs @@ -0,0 +1,39 @@ +using Medidata.MAuth.Core; +using Xunit; + +namespace Medidata.MAuth.Tests +{ + public static class MAuthCoreExtensionsTests + { + [Fact] + public static void BuildEncodedQueryParams_WillEncodeQueryStringWithSpecialCharacters() + { + var queryString = "key=-_.~!@#$%^*()+{}|:\"'`<>?"; + var expected = "key=-_.~%21%40%23%24%25%5E%2A%28%29%2B%7B%7D%7C%3A%22%27%60%3C%3E%3F"; + Assert.Equal(queryString.BuildEncodedQueryParams(), expected); + } + + [Fact] + public static void BuildEncodedQueryParams_WillEncodeQueryStringBySortingWithCodePointAscending() + { + var queryString = "∞=v&キ=v&0=v&a=v"; + var expected = "0=v&a=v&%E2%88%9E=v&%E3%82%AD=v"; + Assert.Equal(queryString.BuildEncodedQueryParams(), expected); + } + + [Fact] + public static void BuildEncodedQueryParams_WillEncodeQueryStringBySortingWithValuesIfSameKeys() + { + var queryString = "a=b&a=c&a=a"; + var expected = "a=a&a=b&a=c"; + Assert.Equal(queryString.BuildEncodedQueryParams(), expected); + } + + [Fact] + public static void BuildEncodedQueryParams_WillHandlesQueryStringWithEmptyValues() + { + var queryString = "k=&k=v"; + Assert.Equal(queryString.BuildEncodedQueryParams(), queryString); + } + } +} From f6bcb5ef2aa99290e1e4986a5185720adab2f6c1 Mon Sep 17 00:00:00 2001 From: Prajon Date: Thu, 8 Aug 2019 09:16:19 -0400 Subject: [PATCH 18/40] Updated for readability --- src/Medidata.MAuth.Core/MAuthCoreExtensions.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs index 364868d..34fc0f6 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreExtensions.cs @@ -190,7 +190,9 @@ public static string BuildEncodedQueryParams(this string queryString) Array.ForEach(queryArray, x => { var keyValue = x.Split('='); - encodedQueryStrings.Add($"{Uri.EscapeDataString(keyValue[0])}={Uri.EscapeDataString(keyValue[1])}"); + var escapedKey = Uri.EscapeDataString(keyValue[0]); + var escapedValue = Uri.EscapeDataString(keyValue[1]); + encodedQueryStrings.Add($"{escapedKey}={escapedValue}"); }); return string.Join("&", encodedQueryStrings); } From c6c15513690060cefb3403a80339f72bb09fa4e6 Mon Sep 17 00:00:00 2001 From: Prajon Date: Thu, 8 Aug 2019 12:22:44 -0400 Subject: [PATCH 19/40] Moved `GetHeaderKeys()` to IMAuthCore from UtilityExtensions --- src/Medidata.MAuth.Core/IMAuthCore.cs | 2 ++ src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 3 ++- src/Medidata.MAuth.Core/MAuthCore.cs | 9 +++++++++ src/Medidata.MAuth.Core/MAuthCoreV2.cs | 5 +++++ src/Medidata.MAuth.Core/UtilityExtensions.cs | 12 ------------ .../Infrastructure/TestExtensions.cs | 4 +++- 6 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/Medidata.MAuth.Core/IMAuthCore.cs b/src/Medidata.MAuth.Core/IMAuthCore.cs index aa91b4d..04893e3 100644 --- a/src/Medidata.MAuth.Core/IMAuthCore.cs +++ b/src/Medidata.MAuth.Core/IMAuthCore.cs @@ -12,5 +12,7 @@ internal interface IMAuthCore Task GetSignature(HttpRequestMessage request, AuthenticationInfo authInfo); string GetMAuthTokenRequestPath(); + + (string mAuthHeaderKey, string mAuthTimeHeaderKey) GetHeaderKeys(); } } diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index 7aaab2c..62b6b6b 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -106,7 +106,8 @@ private HttpRequestMessage CreateRequest(Guid applicationUuid, string tokenReque /// The authentication information with the payload from the request. public PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage request, MAuthVersion version) { - var headerKeys = version.GetHeaderKeys(); + var mAuthCore = MAuthCoreFactory.Instantiate(version); + var headerKeys = mAuthCore.GetHeaderKeys(); var authHeader = request.Headers.GetFirstValueOrDefault(headerKeys.mAuthHeaderKey); if (authHeader == null) diff --git a/src/Medidata.MAuth.Core/MAuthCore.cs b/src/Medidata.MAuth.Core/MAuthCore.cs index d451d56..b9e03e9 100644 --- a/src/Medidata.MAuth.Core/MAuthCore.cs +++ b/src/Medidata.MAuth.Core/MAuthCore.cs @@ -124,5 +124,14 @@ public string GetMAuthTokenRequestPath() { return "/mauth/v1/security_tokens/"; } + + /// + /// Gets the MAuthHeader and MAuthTimeHeader keys + /// + /// MAuthHeaderKey and MAuthTimeHeaderKey. + public (string mAuthHeaderKey, string mAuthTimeHeaderKey) GetHeaderKeys() + { + return (Constants.MAuthHeaderKey, Constants.MAuthTimeHeaderKey); + } } } \ No newline at end of file diff --git a/src/Medidata.MAuth.Core/MAuthCoreV2.cs b/src/Medidata.MAuth.Core/MAuthCoreV2.cs index 47fdcb3..d2723d6 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreV2.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreV2.cs @@ -134,5 +134,10 @@ public string GetMAuthTokenRequestPath() { return "/mauth/v2/security_tokens/"; } + + public (string mAuthHeaderKey, string mAuthTimeHeaderKey) GetHeaderKeys() + { + return (Constants.MAuthHeaderKeyV2, Constants.MAuthTimeHeaderKeyV2); + } } } diff --git a/src/Medidata.MAuth.Core/UtilityExtensions.cs b/src/Medidata.MAuth.Core/UtilityExtensions.cs index 4eb9e66..7fb474b 100644 --- a/src/Medidata.MAuth.Core/UtilityExtensions.cs +++ b/src/Medidata.MAuth.Core/UtilityExtensions.cs @@ -94,17 +94,5 @@ public static string GetAuthHeaderValue(this HttpRequestMessage request) return authHeader ?? throw new ArgumentNullException(nameof(authHeader), "The MAuth header is missing from the request."); } - - /// - /// Gets the MAuthHeader values based on the enum value of the version. - /// - /// - /// MAuthHeaderKey and MAuthTimeHeaderKey. - public static (string mAuthHeaderKey, string mAuthTimeHeaderKey) GetHeaderKeys(this MAuthVersion version) - { - return (version == MAuthVersion.MWSV2) - ? (Constants.MAuthHeaderKeyV2, Constants.MAuthTimeHeaderKeyV2) - : (Constants.MAuthHeaderKey, Constants.MAuthTimeHeaderKey); - } } } diff --git a/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs b/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs index a9cbf3f..c9623c5 100644 --- a/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs +++ b/tests/Medidata.MAuth.Tests/Infrastructure/TestExtensions.cs @@ -84,7 +84,8 @@ public static HttpRequestMessage ToHttpRequestMessage(this RequestData data, MAu new ByteArrayContent(Convert.FromBase64String(data.Base64Content)) : null, }; - var headerKeys = version.GetHeaderKeys(); + var mAuthCore = MAuthCoreFactory.Instantiate(version); + var headerKeys = mAuthCore.GetHeaderKeys(); var mauthHeader = version == MAuthVersion.MWS ? $"{version} {data.ApplicationUuidString}:{data.Payload}" : $"{version} {data.ApplicationUuidString}:{data.Payload};"; @@ -99,5 +100,6 @@ public static string ToStringContent(this string base64Content) => base64Content == null ? null : Encoding.UTF8.GetString(Convert.FromBase64String(base64Content)); public static HttpMethod ToHttpMethod(this string method) => new HttpMethod(method); + } } From 69d3469a868bb258774abb6544aea8c2e4c621a0 Mon Sep 17 00:00:00 2001 From: Prajon Date: Thu, 8 Aug 2019 12:23:18 -0400 Subject: [PATCH 20/40] Added missed comment for the method --- src/Medidata.MAuth.Core/MAuthCoreV2.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Medidata.MAuth.Core/MAuthCoreV2.cs b/src/Medidata.MAuth.Core/MAuthCoreV2.cs index d2723d6..9fcdb73 100644 --- a/src/Medidata.MAuth.Core/MAuthCoreV2.cs +++ b/src/Medidata.MAuth.Core/MAuthCoreV2.cs @@ -135,6 +135,10 @@ public string GetMAuthTokenRequestPath() return "/mauth/v2/security_tokens/"; } + /// + /// Gets the MAuthHeader and MAuthTimeHeader keys. + /// + /// MAuthHeaderKey and MAuthTimeHeaderKey. public (string mAuthHeaderKey, string mAuthTimeHeaderKey) GetHeaderKeys() { return (Constants.MAuthHeaderKeyV2, Constants.MAuthTimeHeaderKeyV2); From 09ed91ac30fb201a00b9ae06f9bd4f522829b3e1 Mon Sep 17 00:00:00 2001 From: Prajon Date: Thu, 8 Aug 2019 16:29:06 -0400 Subject: [PATCH 21/40] Updated Readme for V2 protocol support information --- README.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index bdb53d7..316c1fa 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,7 @@ An example: ```C# using Medidata.MAuth.Core; +using Medidata.MAuth.Core.Models; public async Task SignAndSendRequest(HttpRequestMessage request) { @@ -111,7 +112,13 @@ public async Task SignAndSendRequest(HttpRequestMessage req ApplicationUuid = new Guid("7c872d75-986b-4c61-bb17-f2569d42bfb0"), // The following can be either a path to the key file or the contents of the file itself - PrivateKey = "ClientPrivateKey.pem" + PrivateKey = "ClientPrivateKey.pem", + + // With 4.0.0 version, V2 protocol is supported + MAuthVersion = (MAuthVersion.MWSV2 || MAuthVersion.MWS) + + // when ready to disable authentication of V1 protococl + DisableV1 = true }); using (var client = new HttpClient(signingHandler)) @@ -120,6 +127,11 @@ public async Task SignAndSendRequest(HttpRequestMessage req } } ``` +With the MAuth V2 protocol, there are two new options `MAuthVersion` and `DisableV1` added for Signing MAuth request. +`MAuthVersion` is passed either as `MAuthVersion.MWSV2` for signing with V2 protocol or `MAuthVersion.MWS` for continue +signing with V1 protocol. By default, `DisableV1` option is set to false (if not included). When we are ready to +disable all the V1 request, then we need to include this disable option as : `DisableV1 = true`. +Signing with V2 protocol supports query string. The example above is creating a new instance of a `HttpClient` with the handler responsible for signing the requests and sends the request to its designation. Finally it returns the response from the remote server. @@ -130,6 +142,8 @@ The `MAuthSigningOptions` has the following properties to determine the required | ---- | ----------- | | **ApplicationUuid** | Determines the unique identifier of the client application used for the MAuth service authentication requests. This uuid needs to be registered with the MAuth Server in order for the authenticating server application to be able to authenticate the signed request. | | **PrivateKey** | Determines the RSA private key of the client for signing a request. This key must be in a PEM ASN.1 format. The value of this property can be set as a valid path to a readable key file as well. | +| **MAuthVersion** | Determines the MAuth version of the request used for signing. This is enumeration value which is `MAuthVersion.MWSV2` for V2 requests. | +| **DisableV1** | Determines the boolean value which controls whether to disable the signing requests with `MAuthVersion.MWS` requests or not. If not supplied, this value is `false`. | ### Authenticating Incoming Requests with the OWIN and ASP.NET Core Middlewares @@ -138,6 +152,9 @@ provided by the Owin and AspNetCore NuGet packages. The setting and usage is as follows in case of OWIN (in the application's `Startup` class): +With the update of MAuth V2 protocol, the authentication will first check for mauth header with `MWSV2` and if not found then only fallback to authenticate for `MWS` version. +Also, there is options of `DisableV1`, which is `false` by default and when we are ready to disable authentication of V1 protocl i.e. `MWS` version, then we need to pass this option too as `true`. + ```C# using Medidata.MAuth.Owin; @@ -154,6 +171,9 @@ public class Startup options.HideExceptionsAndReturnUnauthorized = true; options.PrivateKey = "ServerPrivateKey.pem"; options.Bypass = (request) => request.Uri.AbsolutePath.StartsWith("/allowed"); + + // when ready to disable authentication of V1 protococl + options.DisableV1 = true; }); } } @@ -177,6 +197,9 @@ public class Startup options.HideExceptionsAndReturnUnauthorized = true; options.PrivateKey = "ServerPrivateKey.pem"; options.Bypass = (request) => request.Uri.AbsolutePath.StartsWith("/allowed"); + + // when ready to disable authentication of V1 protococl + options.DisableV1 = true; }); } } @@ -193,6 +216,7 @@ The middlewares take an `MAuthMiddlewareOptions` instance to set up the authenti | **MAuthServiceRetryPolicy** | The policy for the retry attempts when communicating with the MAuth service. The following policies can be used: `NoRetry` (no retries), `RetryOnce` (one additional attempt), `RetryTwice` (two additional attempts) and `Agressive` (9 additional attempts) - the default value is **RetryOnce**. | | **HideExceptionsAndReturnUnauthorized** | An optional parameter that determines if the middleware should swallow all exceptions and return an empty HTTP response with a status code Unauthorized (401) in case of any errors (including authentication and validation errors). The default is **true**. | | **Bypass** | Determines a function which evaluates if a given request should bypass the MAuth authentication. | +| **DisableV1** | Determines the boolean value which controls whether to disable the signing requests with `MAuthVersion.MWS` requests or not. If not supplied, this default value is `false`. | The **HideExceptionsAndReturnUnauthorized** parameter is useful (if set to **false**) when you have an exception handler mechanism (for example a logger) in your middleware pipeline. In this case the MAuth middleware won't swallow the @@ -209,6 +233,8 @@ should produce **true** as a result, if the given request satisfies the conditio otherwise it should result **false** therefore an authentication attempt will occur. If no Bypass predicate provided in the options, every request will be authenticated by default. +When authentication of V1 requests needs to be disabled, then **DisableV1** should be passed as **true**. + ### Authenticating Incoming Requests with the WebApi Message Handler If your application does not use the OWIN or ASP.NET Core middleware infrastructure, but it uses the ASP.NET WebAPI @@ -232,7 +258,10 @@ public static class WebApiConfig AuthenticateRequestTimeoutSeconds = 3, MAuthServiceRetryPolicy = MAuthServiceRetryPolicy.RetryOnce, HideExceptionsAndReturnUnauthorized = true, - PrivateKey = "ServerPrivateKey.pem" + PrivateKey = "ServerPrivateKey.pem", + + // when ready to disable authentication of V1 protococl + options.DisableV1 = true }; config.MessageHandlers.Add(new MAuthAuthenticatingHandler(options)); @@ -273,21 +302,38 @@ The framework is licensed under the [MIT licensing terms](https://github.com/mds ##### What is the current target .NET Framework version? -The current target is **.NET Framework 4.5.2** - this means that you have to use at least this target framework version +The current target is **.NET Framework 4.6.1** - this means that you have to use at least this target framework version in your project in order to make Medidata.MAuth work for you. ##### Is there an .NET Standard/Core support? Yes, for signing outgoing requests you can use the library with any framework which implements -the **.NET Standard 1.4** and onwards; additionally we support the **ASP.NET Core App 1.1** and onwards with a middleware +the **.NET Standard 2.0** and onwards; additionally we support the **ASP.NET Core App 2.0** and onwards with a middleware for authenticating the incoming requests. ##### What Cryptographic provider is used for the encryption/decryption? +In the latest version of 4.0.0, we are using the available dotnet security [System.Security.Cryptography] which works +for both **.NET Framework 4.6.1** and **.NET Standard 2.0** in case of V2 protocol. However, for the continue support +of V1 protcol, we are still maintaining the BouncyCastle library as mentioned below. + On the .NET Framework side (WebAPI, Owin, Core) we are using the latest version (as of date 1.81) of the [BouncyCastle](https://github.com/bcgit/bc-csharp) library; on the .NET Standard side (Core, AspNetCore) we are using the portable fork of the [BouncyCastle](https://github.com/onovotny/BouncyCastle-PCL) library. +##### What are the major changes in the 4.0.0 version? + +In this version we have added support for V2 protocol which uses `MCC-Authentication` as MAuthHeader and `MCC-Time` as +MAuthTimeHeader. And, this V2 protocol supports for signing and authenticating url with query string parameters. +For Signing, we added two new options in `MAuthSigningOptions`: `MAuthVersion` which is mandatory and takes enumeration + value of `MAuthVersion.MWSV2` for V2 protocol or `MAuthVersion.MWS` for continue of using V1 protocol. +Another option `DisableV1` is `false` by default if not provided. But, it is needed to provide as `true` when the client +need to sign on by no more supporting V1 protocol. + +Also while authentication, the logic defaults to check for V2 protcol header `MWSV2` and if fails then only fallback to +check for V1 protocol header for `MWS`. Also, `MAuthOptionsBase` includes new option as `DisableV1` which is `false` by +default and need to be passed as `true` if the authenticating client no longer wants to support V1 protocol. + ##### What are the major changes in the 2.0.0 version? In this version we have only one major and a minor change: from this version the `MAuthSigningHandler` is accepting an @@ -314,5 +360,3 @@ This policy will make the number of requests to the MAuth service to an overall to receive a successful response from the MAuth service is gradually decreasing by the number of attempts (the more the clients are sending requests to a presumably overloaded server the less the chance for a successful response) - therefore we do not recommend to use this policy in any production scenario. - - From c9e73df46ff6a9938615a71c206d789684ed45d6 Mon Sep 17 00:00:00 2001 From: Prajon Date: Fri, 9 Aug 2019 07:38:55 -0400 Subject: [PATCH 22/40] Adding some xml comments and fixing nitpick --- src/Medidata.MAuth.Core/MAuthSigningHandler.cs | 3 +-- src/Medidata.MAuth.Core/Models/MAuthVersion.cs | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Medidata.MAuth.Core/MAuthSigningHandler.cs b/src/Medidata.MAuth.Core/MAuthSigningHandler.cs index 9d7a316..f9ff468 100644 --- a/src/Medidata.MAuth.Core/MAuthSigningHandler.cs +++ b/src/Medidata.MAuth.Core/MAuthSigningHandler.cs @@ -1,5 +1,4 @@ using System; -using System.Linq.Expressions; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -57,7 +56,7 @@ protected override async Task SendAsync( if (InnerHandler == null) InnerHandler = new HttpClientHandler(); - if(options.DisableV1 && options.MAuthVersion.ToString() == MAuthVersion.MWS.ToString()) + if(options.DisableV1 && options.MAuthVersion == MAuthVersion.MWS) throw new InvalidVersionException ($"Signing with {options.MAuthVersion.ToString()} is disabled."); diff --git a/src/Medidata.MAuth.Core/Models/MAuthVersion.cs b/src/Medidata.MAuth.Core/Models/MAuthVersion.cs index 9fc9ff0..05ad6be 100644 --- a/src/Medidata.MAuth.Core/Models/MAuthVersion.cs +++ b/src/Medidata.MAuth.Core/Models/MAuthVersion.cs @@ -1,17 +1,17 @@ namespace Medidata.MAuth.Core.Models { /// - /// + /// Contains the Enumeration values for different versions supported by the library. /// public enum MAuthVersion { /// - /// + /// Defines the enumeration value for V1 protocol. /// MWS, /// - /// + /// Defines the enumeration value for V2 protocol. /// MWSV2 } From 85d06412aeb0db27274dc9684b1dad39323ca81f Mon Sep 17 00:00:00 2001 From: Prajon Date: Fri, 9 Aug 2019 07:43:45 -0400 Subject: [PATCH 23/40] Adding Logging in AuthenticateRequest --- src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 13 +++++++++++++ src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj | 3 +++ 2 files changed, 16 insertions(+) diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index 62b6b6b..3435cb8 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.Caching.Memory; using Org.BouncyCastle.Crypto; using Medidata.MAuth.Core.Models; +using Microsoft.Extensions.Logging; namespace Medidata.MAuth.Core { @@ -12,6 +13,7 @@ internal class MAuthAuthenticator { private readonly MAuthOptionsBase options; private readonly IMemoryCache cache = new MemoryCache(new MemoryCacheOptions()); + private readonly ILogger logger; public Guid ApplicationUuid => options.ApplicationUuid; @@ -27,14 +29,20 @@ public MAuthAuthenticator(MAuthOptionsBase options) throw new ArgumentNullException(nameof(options.PrivateKey)); this.options = options; + + logger = LoggerFactory.Create(builder => builder.AddConsole().AddDebug()) + .CreateLogger(); } public async Task AuthenticateRequest(HttpRequestMessage request) { try { + logger.LogInformation($"Initiating Authentication of request", request); var version = request.GetAuthHeaderValue().GetVersionFromAuthenticationHeader(); + logger.LogInformation($"Authentication is for the request with {version} version."); + if (options.DisableV1 && version == MAuthVersion.MWS) throw new InvalidVersionException($"Authentication with {version} version is disabled."); @@ -47,24 +55,29 @@ public async Task AuthenticateRequest(HttpRequestMessage request) } catch (ArgumentException ex) { + logger.LogError($"Unable to authenticate due to invalid MAuth authentication headers. Exception: {ex.Message}"); throw new AuthenticationException("The request has invalid MAuth authentication headers.", ex); } catch (RetriedRequestException ex) { + logger.LogError($"Unable to query the application information from MAuth server. Exception:{ex.Message}"); throw new AuthenticationException( "Could not query the application information for the application from the MAuth server.", ex); } catch (InvalidCipherTextException ex) { + logger.LogError($"Unable to authenticate due to invalid payload information. Exception: {ex.Message}"); throw new AuthenticationException( "The request verification failed due to an invalid payload information.", ex); } catch (InvalidVersionException ex) { + logger.LogError(ex, $"Unable to authenticate due to invalid version. Exception: {ex.Message}"); throw new InvalidVersionException(ex.Message, ex); } catch (Exception ex) { + logger.LogError($"Unable to authenticate due to unexpected error. Exception: {ex.Message}"); throw new AuthenticationException( "An unexpected error occured during authentication. Please see the inner exception for details.", ex diff --git a/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj b/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj index b814394..582d3d7 100644 --- a/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj +++ b/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj @@ -11,6 +11,9 @@ + + + From afaca6af65070609ead41cd9bdb0793f271e9258 Mon Sep 17 00:00:00 2001 From: Prajon Date: Fri, 9 Aug 2019 08:42:14 -0400 Subject: [PATCH 24/40] Add Microsoft.Extensions.Logging.Abstractions --- src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 18 +++++++++--------- .../Medidata.MAuth.Core.csproj | 1 + 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index 3435cb8..e1306c9 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -13,7 +13,7 @@ internal class MAuthAuthenticator { private readonly MAuthOptionsBase options; private readonly IMemoryCache cache = new MemoryCache(new MemoryCacheOptions()); - private readonly ILogger logger; + private readonly ILogger _logger; public Guid ApplicationUuid => options.ApplicationUuid; @@ -30,7 +30,7 @@ public MAuthAuthenticator(MAuthOptionsBase options) this.options = options; - logger = LoggerFactory.Create(builder => builder.AddConsole().AddDebug()) + _logger = LoggerFactory.Create(builder => builder.AddConsole().AddDebug()) .CreateLogger(); } @@ -38,10 +38,10 @@ public async Task AuthenticateRequest(HttpRequestMessage request) { try { - logger.LogInformation($"Initiating Authentication of request", request); + _logger.LogInformation($"Initiating Authentication of request", request); var version = request.GetAuthHeaderValue().GetVersionFromAuthenticationHeader(); - logger.LogInformation($"Authentication is for the request with {version} version."); + _logger.LogInformation($"Authentication is for the request with {version} version."); if (options.DisableV1 && version == MAuthVersion.MWS) throw new InvalidVersionException($"Authentication with {version} version is disabled."); @@ -55,29 +55,29 @@ public async Task AuthenticateRequest(HttpRequestMessage request) } catch (ArgumentException ex) { - logger.LogError($"Unable to authenticate due to invalid MAuth authentication headers. Exception: {ex.Message}"); + _logger.LogError($"Unable to authenticate due to invalid MAuth authentication headers. Exception: {ex.Message}"); throw new AuthenticationException("The request has invalid MAuth authentication headers.", ex); } catch (RetriedRequestException ex) { - logger.LogError($"Unable to query the application information from MAuth server. Exception:{ex.Message}"); + _logger.LogError($"Unable to query the application information from MAuth server. Exception:{ex.Message}"); throw new AuthenticationException( "Could not query the application information for the application from the MAuth server.", ex); } catch (InvalidCipherTextException ex) { - logger.LogError($"Unable to authenticate due to invalid payload information. Exception: {ex.Message}"); + _logger.LogError($"Unable to authenticate due to invalid payload information. Exception: {ex.Message}"); throw new AuthenticationException( "The request verification failed due to an invalid payload information.", ex); } catch (InvalidVersionException ex) { - logger.LogError(ex, $"Unable to authenticate due to invalid version. Exception: {ex.Message}"); + _logger.LogError(ex, $"Unable to authenticate due to invalid version. Exception: {ex.Message}"); throw new InvalidVersionException(ex.Message, ex); } catch (Exception ex) { - logger.LogError($"Unable to authenticate due to unexpected error. Exception: {ex.Message}"); + _logger.LogError($"Unable to authenticate due to unexpected error. Exception: {ex.Message}"); throw new AuthenticationException( "An unexpected error occured during authentication. Please see the inner exception for details.", ex diff --git a/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj b/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj index 582d3d7..a5494b2 100644 --- a/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj +++ b/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj @@ -12,6 +12,7 @@ + From 6f91c9616fc56f6507515d8356fe33563e1ff0d4 Mon Sep 17 00:00:00 2001 From: Prajon Date: Fri, 9 Aug 2019 16:44:00 -0400 Subject: [PATCH 25/40] Updating "MEL" to latest stable version and removed not used namespace. --- src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 4 ++-- src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj | 7 +++---- tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs | 1 - 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index e1306c9..b494cfb 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -30,8 +30,8 @@ public MAuthAuthenticator(MAuthOptionsBase options) this.options = options; - _logger = LoggerFactory.Create(builder => builder.AddConsole().AddDebug()) - .CreateLogger(); + var factory = new LoggerFactory().AddDebug().AddConsole(); + _logger = factory.CreateLogger(); } public async Task AuthenticateRequest(HttpRequestMessage request) diff --git a/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj b/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj index a5494b2..1c15fe7 100644 --- a/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj +++ b/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj @@ -11,10 +11,9 @@ - - - - + + + diff --git a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs index 578bfc8..442924e 100644 --- a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs @@ -1,7 +1,6 @@ using System; using System.Linq; using System.Net; -using System.Net.Http; using System.Threading.Tasks; using Medidata.MAuth.Core; using Medidata.MAuth.Core.Exceptions; From ebe375a0f627efbed7812d20e3bb338cebc90148 Mon Sep 17 00:00:00 2001 From: Prajon Date: Mon, 12 Aug 2019 20:15:40 -0400 Subject: [PATCH 26/40] Updated logging to use abstraction provided by application --- .../MAuthAppBuilderExtensions.cs | 8 ++++++- .../MAuthMiddleware.cs | 5 ++-- .../Medidata.MAuth.AspNetCore.csproj | 2 +- src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 12 ++++++---- src/Medidata.MAuth.Core/UtilityExtensions.cs | 6 +++-- .../MAuthAppBuilderExtensions.cs | 8 ++++++- src/Medidata.MAuth.Owin/MAuthMiddleware.cs | 5 ++-- .../MAuthAuthenticatingHandler.cs | 18 ++++++++++----- .../Infrastructure/MAuthServerHandler.cs | 3 ++- .../MAuthAuthenticatorTests.cs | 23 ++++++++++--------- .../UtilityExtensionsTest.cs | 3 ++- 11 files changed, 61 insertions(+), 32 deletions(-) diff --git a/src/Medidata.MAuth.AspNetCore/MAuthAppBuilderExtensions.cs b/src/Medidata.MAuth.AspNetCore/MAuthAppBuilderExtensions.cs index 84a8192..f242c4b 100644 --- a/src/Medidata.MAuth.AspNetCore/MAuthAppBuilderExtensions.cs +++ b/src/Medidata.MAuth.AspNetCore/MAuthAppBuilderExtensions.cs @@ -1,5 +1,8 @@ using System; using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Medidata.MAuth.AspNetCore { @@ -26,7 +29,10 @@ public static IApplicationBuilder UseMAuthAuthentication(this IApplicationBuilde if (options == null) throw new ArgumentNullException(nameof(options)); - return app.UseMiddleware(options); + var loggerFactory = app.ApplicationServices.GetService() ?? + NullLoggerFactory.Instance; + + return app.UseMiddleware(options, loggerFactory); } /// diff --git a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs index 0efdee7..69f679d 100644 --- a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs @@ -3,6 +3,7 @@ using Medidata.MAuth.Core; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Internal; +using Microsoft.Extensions.Logging; namespace Medidata.MAuth.AspNetCore { @@ -12,11 +13,11 @@ internal class MAuthMiddleware private readonly MAuthAuthenticator authenticator; private readonly RequestDelegate next; - public MAuthMiddleware(RequestDelegate next, MAuthMiddlewareOptions options) + public MAuthMiddleware(RequestDelegate next, MAuthMiddlewareOptions options, ILoggerFactory loggerFactory) { this.next = next; this.options = options; - this.authenticator = new MAuthAuthenticator(options); + this.authenticator = new MAuthAuthenticator(options, loggerFactory); } public async Task Invoke(HttpContext context) diff --git a/src/Medidata.MAuth.AspNetCore/Medidata.MAuth.AspNetCore.csproj b/src/Medidata.MAuth.AspNetCore/Medidata.MAuth.AspNetCore.csproj index 022379a..c556230 100644 --- a/src/Medidata.MAuth.AspNetCore/Medidata.MAuth.AspNetCore.csproj +++ b/src/Medidata.MAuth.AspNetCore/Medidata.MAuth.AspNetCore.csproj @@ -2,7 +2,7 @@ - netstandard2.0 + netcoreapp2.0 This package contains an ASP.NET Core middleware to validate signed http requests with the Medidata MAuth protocol. The middleware communicates with an MAuth server in order to confirm the validity of the request authentication header. Include this package in your ASP.NET Core web api if you want to authenticate the api requests signed with the MAuth protocol. Medidata.MAuth.AspNetCore Medidata.MAuth.AspNetCore diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index b494cfb..31aa579 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -17,7 +17,7 @@ internal class MAuthAuthenticator public Guid ApplicationUuid => options.ApplicationUuid; - public MAuthAuthenticator(MAuthOptionsBase options) + public MAuthAuthenticator(MAuthOptionsBase options, ILoggerFactory loggerFactory) { if (options.ApplicationUuid == default(Guid)) throw new ArgumentException(nameof(options.ApplicationUuid)); @@ -30,10 +30,14 @@ public MAuthAuthenticator(MAuthOptionsBase options) this.options = options; - var factory = new LoggerFactory().AddDebug().AddConsole(); - _logger = factory.CreateLogger(); + this._logger = loggerFactory.CreateLogger(); } + /// + /// Verifies if the request is authenticated or not. + /// + /// The request. + /// A task object of the boolean value that verifies if the request is authenticated or not. public async Task AuthenticateRequest(HttpRequestMessage request) { try @@ -117,7 +121,7 @@ private HttpRequestMessage CreateRequest(Guid applicationUuid, string tokenReque /// The request that has the authentication information. /// /// Enum value of the MAuthVersion. /// The authentication information with the payload from the request. - public PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage request, MAuthVersion version) + internal PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage request, MAuthVersion version) { var mAuthCore = MAuthCoreFactory.Instantiate(version); var headerKeys = mAuthCore.GetHeaderKeys(); diff --git a/src/Medidata.MAuth.Core/UtilityExtensions.cs b/src/Medidata.MAuth.Core/UtilityExtensions.cs index 7fb474b..bc7ee05 100644 --- a/src/Medidata.MAuth.Core/UtilityExtensions.cs +++ b/src/Medidata.MAuth.Core/UtilityExtensions.cs @@ -2,6 +2,7 @@ using System.Net.Http; using System.Threading.Tasks; using Medidata.MAuth.Core.Models; +using Microsoft.Extensions.Logging; namespace Medidata.MAuth.Core { @@ -65,10 +66,11 @@ public static bool TryParseAuthenticationHeader(this string headerValue, /// /// The request message to authenticate. /// The MAuth options to use for the authentication. + /// The logger factory used with authentication. /// The task for the operation that is when completes will result in if /// the authentication is successful; otherwise . - public static Task Authenticate(this HttpRequestMessage request, MAuthOptionsBase options) => - new MAuthAuthenticator(options).AuthenticateRequest(request); + public static Task Authenticate(this HttpRequestMessage request, MAuthOptionsBase options, ILoggerFactory loggerFactory) => + new MAuthAuthenticator(options, loggerFactory).AuthenticateRequest(request); /// /// Determines the MAuth version enumerator reading authHeader. diff --git a/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs b/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs index 5a9cd17..8f4e596 100644 --- a/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs +++ b/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs @@ -1,5 +1,8 @@ using System; +using System.Linq; +using Microsoft.Extensions.Logging.Abstractions; using Owin; +using Microsoft.Extensions.Logging; namespace Medidata.MAuth.Owin { @@ -25,7 +28,10 @@ public static IAppBuilder UseMAuthAuthentication(this IAppBuilder app, MAuthMidd if (options == null) throw new ArgumentNullException(nameof(options)); - return app.Use(options); + var loggerFactory = (ILoggerFactory)app.Properties.Where(x => x.Key == "ILoggerFactory")? + .FirstOrDefault().Value ?? NullLoggerFactory.Instance; + + return app.Use(options, loggerFactory); } /// diff --git a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs index 3883f9e..702d837 100644 --- a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs @@ -1,6 +1,7 @@ using System.Net; using System.Threading.Tasks; using Medidata.MAuth.Core; +using Microsoft.Extensions.Logging; using Microsoft.Owin; namespace Medidata.MAuth.Owin @@ -10,10 +11,10 @@ internal class MAuthMiddleware: OwinMiddleware private readonly MAuthMiddlewareOptions options; private readonly MAuthAuthenticator authenticator; - public MAuthMiddleware(OwinMiddleware next, MAuthMiddlewareOptions options): base(next) + public MAuthMiddleware(OwinMiddleware next, MAuthMiddlewareOptions options, ILoggerFactory loggerFactory): base(next) { this.options = options; - authenticator = new MAuthAuthenticator(options); + authenticator = new MAuthAuthenticator(options, loggerFactory); } public override async Task Invoke(IOwinContext context) diff --git a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs index 0240a97..26c67c7 100644 --- a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs +++ b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs @@ -4,7 +4,8 @@ using System.Threading; using System.Threading.Tasks; using Medidata.MAuth.Core; -using Medidata.MAuth.Core.Models; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Medidata.MAuth.WebApi { @@ -25,10 +26,13 @@ public class MAuthAuthenticatingHandler : DelegatingHandler /// . /// /// The options for this message handler. - public MAuthAuthenticatingHandler(MAuthWebApiOptions options) + /// The logger factory used by this message handler. + public MAuthAuthenticatingHandler(MAuthWebApiOptions options, ILoggerFactory loggerFactory = null) { this.options = options; - authenticator = new MAuthAuthenticator(options); + var loggerFact = loggerFactory ?? NullLoggerFactory.Instance; + + authenticator = new MAuthAuthenticator(options, loggerFact); } /// @@ -39,11 +43,13 @@ public MAuthAuthenticatingHandler(MAuthWebApiOptions options) /// /// The inner handler which is responsible for processing the HTTP response messages. /// - public MAuthAuthenticatingHandler(MAuthWebApiOptions options, HttpMessageHandler innerHandler) - : base(innerHandler) + /// The logger factory used by this message handler. + public MAuthAuthenticatingHandler(MAuthWebApiOptions options, HttpMessageHandler innerHandler, + ILoggerFactory loggerFactory = null) : base(innerHandler) { this.options = options; - authenticator = new MAuthAuthenticator(options); + var loggerFact = loggerFactory ?? NullLoggerFactory.Instance; + authenticator = new MAuthAuthenticator(options, loggerFact); } /// diff --git a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs index 2d497a3..0515bb5 100644 --- a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs +++ b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using Medidata.MAuth.Core; +using Microsoft.Extensions.Logging.Abstractions; using Newtonsoft.Json; namespace Medidata.MAuth.Tests.Infrastructure @@ -25,7 +26,7 @@ protected override async Task SendAsync( if (currentNumberOfAttempts < SucceedAfterThisManyAttempts) return new HttpResponseMessage(HttpStatusCode.ServiceUnavailable); - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions, NullLoggerFactory.Instance); var authInfo = authenticator.GetAuthenticationInfo(request, version); diff --git a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs index 442924e..82d5bde 100644 --- a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs @@ -6,6 +6,7 @@ using Medidata.MAuth.Core.Exceptions; using Medidata.MAuth.Core.Models; using Medidata.MAuth.Tests.Infrastructure; +using Microsoft.Extensions.Logging.Abstractions; using Xunit; namespace Medidata.MAuth.Tests @@ -22,14 +23,14 @@ public static void MAuthAuthenticator_WithInvalidOptions_WillThrowException( ApplicationUuid = TestExtensions.ClientUuid, MAuthServiceUrl = mauthServiceUrl != null ? new Uri(mauthServiceUrl) : null, PrivateKey = privateKey - })); + }, NullLoggerFactory.Instance)); [Fact] public static void MAuthAuthenticator_WithDefaultUuid_WillThrowException() => Assert.Throws(() => new MAuthAuthenticator(new MAuthTestOptions() { ApplicationUuid = default(Guid) - })); + }, NullLoggerFactory.Instance)); [Theory] [InlineData("GET")] @@ -41,7 +42,7 @@ public static async Task AuthenticateRequest_WithValidRequest_WillAuthenticate(s // Arrange var testData = await method.FromResource(); - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions, NullLoggerFactory.Instance); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -69,7 +70,7 @@ public static async Task AuthenticateRequest_WithValidMWSV2Request_WillAuthentic // Arrange var testData = await method.FromResourceV2(); var version = MAuthVersion.MWSV2; - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions, NullLoggerFactory.Instance); var mAuthCore = new MAuthCoreV2(); var signedRequest = await mAuthCore @@ -100,7 +101,7 @@ public static async Task AuthenticateRequest_WithNumberOfAttempts_WillAuthentica var testData = await "GET".FromResource(); var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: true)); + policy, shouldSucceedWithin: true), NullLoggerFactory.Instance); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -130,7 +131,7 @@ public static async Task AuthenticateRequest_WithMWSV2Request_WithNumberOfAttemp var testData = await "GET".FromResourceV2(); var version = MAuthVersion.MWSV2; var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: true)); + policy, shouldSucceedWithin: true), NullLoggerFactory.Instance); var mAuthCore = new MAuthCoreV2(); var signedRequest = await mAuthCore @@ -160,7 +161,7 @@ public static async Task AuthenticateRequest_AfterNumberOfAttempts_WillThrowExce var testData = await "GET".FromResource(); var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: false)); + policy, shouldSucceedWithin: false), NullLoggerFactory.Instance); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -195,7 +196,7 @@ public static async Task AuthenticateRequest_WithMWSV2Request_AfterNumberOfAttem var testData = await "GET".FromResource(); var version = MAuthVersion.MWSV2; var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: false)); + policy, shouldSucceedWithin: false), NullLoggerFactory.Instance); var mAuthCore = new MAuthCoreV2(); var signedRequest = await mAuthCore @@ -277,7 +278,7 @@ public static async Task AuthenticateRequest_WithMWSVersion_WithDisableV1_WillTh var testData = await method.FromResource(); var testOptions = TestExtensions.ServerOptions; testOptions.DisableV1 = true; - var authenticator = new MAuthAuthenticator(testOptions); + var authenticator = new MAuthAuthenticator(testOptions, NullLoggerFactory.Instance); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -308,7 +309,7 @@ public static async Task GetAuthenticationInfo_WithSignedRequest_ForMWSV2Version var testData = await method.FromResourceV2(); var version = MAuthVersion.MWSV2; var testOptions = TestExtensions.ServerOptions; - var authenticator = new MAuthAuthenticator(testOptions); + var authenticator = new MAuthAuthenticator(testOptions, NullLoggerFactory.Instance); // Act var actual = authenticator.GetAuthenticationInfo(testData.ToHttpRequestMessage(version), version); @@ -330,7 +331,7 @@ public static async Task GetAuthenticationInfo_WithSignedRequest_ForMWSVersion_W var testData = await method.FromResource(); var version = MAuthVersion.MWS; var testOptions = TestExtensions.ServerOptions; - var authenticator = new MAuthAuthenticator(testOptions); + var authenticator = new MAuthAuthenticator(testOptions, NullLoggerFactory.Instance); // Act var actual = authenticator.GetAuthenticationInfo(testData.ToHttpRequestMessage(version), version); diff --git a/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs b/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs index 432f3dc..d07c67b 100644 --- a/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs +++ b/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Medidata.MAuth.Core; using Medidata.MAuth.Tests.Infrastructure; +using Microsoft.Extensions.Logging.Abstractions; using Xunit; namespace Medidata.MAuth.Tests @@ -67,7 +68,7 @@ public static async Task Authenticate_WithValidRequest_WillAuthenticate(string m }); // Act - var isAuthenticated = await signedRequest.Authenticate(TestExtensions.ServerOptions); + var isAuthenticated = await signedRequest.Authenticate(TestExtensions.ServerOptions, NullLoggerFactory.Instance); // Assert Assert.True(isAuthenticated); From 13676aa0caa915161567787f13a88fda641b0558 Mon Sep 17 00:00:00 2001 From: Prajon Date: Wed, 14 Aug 2019 15:12:31 -0400 Subject: [PATCH 27/40] Removed Logging Part from the current PR as suggested. Logging will be done in follow up PR. --- .../MAuthAppBuilderExtensions.cs | 7 +----- .../MAuthMiddleware.cs | 5 ++-- src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 15 ++---------- src/Medidata.MAuth.Core/UtilityExtensions.cs | 6 ++--- .../MAuthAppBuilderExtensions.cs | 8 +------ src/Medidata.MAuth.Owin/MAuthMiddleware.cs | 5 ++-- .../MAuthAuthenticatingHandler.cs | 15 ++++-------- .../Infrastructure/MAuthServerHandler.cs | 3 +-- .../MAuthAuthenticatorTests.cs | 23 +++++++++---------- .../UtilityExtensionsTest.cs | 3 +-- 10 files changed, 27 insertions(+), 63 deletions(-) diff --git a/src/Medidata.MAuth.AspNetCore/MAuthAppBuilderExtensions.cs b/src/Medidata.MAuth.AspNetCore/MAuthAppBuilderExtensions.cs index f242c4b..35c90a1 100644 --- a/src/Medidata.MAuth.AspNetCore/MAuthAppBuilderExtensions.cs +++ b/src/Medidata.MAuth.AspNetCore/MAuthAppBuilderExtensions.cs @@ -1,8 +1,5 @@ using System; using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; namespace Medidata.MAuth.AspNetCore { @@ -29,10 +26,8 @@ public static IApplicationBuilder UseMAuthAuthentication(this IApplicationBuilde if (options == null) throw new ArgumentNullException(nameof(options)); - var loggerFactory = app.ApplicationServices.GetService() ?? - NullLoggerFactory.Instance; - return app.UseMiddleware(options, loggerFactory); + return app.UseMiddleware(options); } /// diff --git a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs index 69f679d..0efdee7 100644 --- a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs @@ -3,7 +3,6 @@ using Medidata.MAuth.Core; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Internal; -using Microsoft.Extensions.Logging; namespace Medidata.MAuth.AspNetCore { @@ -13,11 +12,11 @@ internal class MAuthMiddleware private readonly MAuthAuthenticator authenticator; private readonly RequestDelegate next; - public MAuthMiddleware(RequestDelegate next, MAuthMiddlewareOptions options, ILoggerFactory loggerFactory) + public MAuthMiddleware(RequestDelegate next, MAuthMiddlewareOptions options) { this.next = next; this.options = options; - this.authenticator = new MAuthAuthenticator(options, loggerFactory); + this.authenticator = new MAuthAuthenticator(options); } public async Task Invoke(HttpContext context) diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index 31aa579..df5eb95 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -5,7 +5,6 @@ using Microsoft.Extensions.Caching.Memory; using Org.BouncyCastle.Crypto; using Medidata.MAuth.Core.Models; -using Microsoft.Extensions.Logging; namespace Medidata.MAuth.Core { @@ -13,11 +12,10 @@ internal class MAuthAuthenticator { private readonly MAuthOptionsBase options; private readonly IMemoryCache cache = new MemoryCache(new MemoryCacheOptions()); - private readonly ILogger _logger; public Guid ApplicationUuid => options.ApplicationUuid; - public MAuthAuthenticator(MAuthOptionsBase options, ILoggerFactory loggerFactory) + public MAuthAuthenticator(MAuthOptionsBase options) { if (options.ApplicationUuid == default(Guid)) throw new ArgumentException(nameof(options.ApplicationUuid)); @@ -29,8 +27,6 @@ public MAuthAuthenticator(MAuthOptionsBase options, ILoggerFactory loggerFactory throw new ArgumentNullException(nameof(options.PrivateKey)); this.options = options; - - this._logger = loggerFactory.CreateLogger(); } /// @@ -42,11 +38,8 @@ public async Task AuthenticateRequest(HttpRequestMessage request) { try { - _logger.LogInformation($"Initiating Authentication of request", request); var version = request.GetAuthHeaderValue().GetVersionFromAuthenticationHeader(); - _logger.LogInformation($"Authentication is for the request with {version} version."); - if (options.DisableV1 && version == MAuthVersion.MWS) throw new InvalidVersionException($"Authentication with {version} version is disabled."); @@ -59,29 +52,25 @@ public async Task AuthenticateRequest(HttpRequestMessage request) } catch (ArgumentException ex) { - _logger.LogError($"Unable to authenticate due to invalid MAuth authentication headers. Exception: {ex.Message}"); throw new AuthenticationException("The request has invalid MAuth authentication headers.", ex); } catch (RetriedRequestException ex) { - _logger.LogError($"Unable to query the application information from MAuth server. Exception:{ex.Message}"); throw new AuthenticationException( "Could not query the application information for the application from the MAuth server.", ex); } catch (InvalidCipherTextException ex) { - _logger.LogError($"Unable to authenticate due to invalid payload information. Exception: {ex.Message}"); + throw new AuthenticationException( "The request verification failed due to an invalid payload information.", ex); } catch (InvalidVersionException ex) { - _logger.LogError(ex, $"Unable to authenticate due to invalid version. Exception: {ex.Message}"); throw new InvalidVersionException(ex.Message, ex); } catch (Exception ex) { - _logger.LogError($"Unable to authenticate due to unexpected error. Exception: {ex.Message}"); throw new AuthenticationException( "An unexpected error occured during authentication. Please see the inner exception for details.", ex diff --git a/src/Medidata.MAuth.Core/UtilityExtensions.cs b/src/Medidata.MAuth.Core/UtilityExtensions.cs index bc7ee05..7fb474b 100644 --- a/src/Medidata.MAuth.Core/UtilityExtensions.cs +++ b/src/Medidata.MAuth.Core/UtilityExtensions.cs @@ -2,7 +2,6 @@ using System.Net.Http; using System.Threading.Tasks; using Medidata.MAuth.Core.Models; -using Microsoft.Extensions.Logging; namespace Medidata.MAuth.Core { @@ -66,11 +65,10 @@ public static bool TryParseAuthenticationHeader(this string headerValue, /// /// The request message to authenticate. /// The MAuth options to use for the authentication. - /// The logger factory used with authentication. /// The task for the operation that is when completes will result in if /// the authentication is successful; otherwise . - public static Task Authenticate(this HttpRequestMessage request, MAuthOptionsBase options, ILoggerFactory loggerFactory) => - new MAuthAuthenticator(options, loggerFactory).AuthenticateRequest(request); + public static Task Authenticate(this HttpRequestMessage request, MAuthOptionsBase options) => + new MAuthAuthenticator(options).AuthenticateRequest(request); /// /// Determines the MAuth version enumerator reading authHeader. diff --git a/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs b/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs index 8f4e596..5a9cd17 100644 --- a/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs +++ b/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs @@ -1,8 +1,5 @@ using System; -using System.Linq; -using Microsoft.Extensions.Logging.Abstractions; using Owin; -using Microsoft.Extensions.Logging; namespace Medidata.MAuth.Owin { @@ -28,10 +25,7 @@ public static IAppBuilder UseMAuthAuthentication(this IAppBuilder app, MAuthMidd if (options == null) throw new ArgumentNullException(nameof(options)); - var loggerFactory = (ILoggerFactory)app.Properties.Where(x => x.Key == "ILoggerFactory")? - .FirstOrDefault().Value ?? NullLoggerFactory.Instance; - - return app.Use(options, loggerFactory); + return app.Use(options); } /// diff --git a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs index 702d837..3883f9e 100644 --- a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs @@ -1,7 +1,6 @@ using System.Net; using System.Threading.Tasks; using Medidata.MAuth.Core; -using Microsoft.Extensions.Logging; using Microsoft.Owin; namespace Medidata.MAuth.Owin @@ -11,10 +10,10 @@ internal class MAuthMiddleware: OwinMiddleware private readonly MAuthMiddlewareOptions options; private readonly MAuthAuthenticator authenticator; - public MAuthMiddleware(OwinMiddleware next, MAuthMiddlewareOptions options, ILoggerFactory loggerFactory): base(next) + public MAuthMiddleware(OwinMiddleware next, MAuthMiddlewareOptions options): base(next) { this.options = options; - authenticator = new MAuthAuthenticator(options, loggerFactory); + authenticator = new MAuthAuthenticator(options); } public override async Task Invoke(IOwinContext context) diff --git a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs index 26c67c7..c652d69 100644 --- a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs +++ b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs @@ -4,8 +4,6 @@ using System.Threading; using System.Threading.Tasks; using Medidata.MAuth.Core; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; namespace Medidata.MAuth.WebApi { @@ -26,13 +24,11 @@ public class MAuthAuthenticatingHandler : DelegatingHandler /// . /// /// The options for this message handler. - /// The logger factory used by this message handler. - public MAuthAuthenticatingHandler(MAuthWebApiOptions options, ILoggerFactory loggerFactory = null) + public MAuthAuthenticatingHandler(MAuthWebApiOptions options) { this.options = options; - var loggerFact = loggerFactory ?? NullLoggerFactory.Instance; - authenticator = new MAuthAuthenticator(options, loggerFact); + authenticator = new MAuthAuthenticator(options); } /// @@ -43,13 +39,10 @@ public MAuthAuthenticatingHandler(MAuthWebApiOptions options, ILoggerFactory log /// /// The inner handler which is responsible for processing the HTTP response messages. /// - /// The logger factory used by this message handler. - public MAuthAuthenticatingHandler(MAuthWebApiOptions options, HttpMessageHandler innerHandler, - ILoggerFactory loggerFactory = null) : base(innerHandler) + public MAuthAuthenticatingHandler(MAuthWebApiOptions options, HttpMessageHandler innerHandler) : base(innerHandler) { this.options = options; - var loggerFact = loggerFactory ?? NullLoggerFactory.Instance; - authenticator = new MAuthAuthenticator(options, loggerFact); + authenticator = new MAuthAuthenticator(options); } /// diff --git a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs index 0515bb5..2d497a3 100644 --- a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs +++ b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs @@ -4,7 +4,6 @@ using System.Threading; using System.Threading.Tasks; using Medidata.MAuth.Core; -using Microsoft.Extensions.Logging.Abstractions; using Newtonsoft.Json; namespace Medidata.MAuth.Tests.Infrastructure @@ -26,7 +25,7 @@ protected override async Task SendAsync( if (currentNumberOfAttempts < SucceedAfterThisManyAttempts) return new HttpResponseMessage(HttpStatusCode.ServiceUnavailable); - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions, NullLoggerFactory.Instance); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); var authInfo = authenticator.GetAuthenticationInfo(request, version); diff --git a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs index 82d5bde..442924e 100644 --- a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs @@ -6,7 +6,6 @@ using Medidata.MAuth.Core.Exceptions; using Medidata.MAuth.Core.Models; using Medidata.MAuth.Tests.Infrastructure; -using Microsoft.Extensions.Logging.Abstractions; using Xunit; namespace Medidata.MAuth.Tests @@ -23,14 +22,14 @@ public static void MAuthAuthenticator_WithInvalidOptions_WillThrowException( ApplicationUuid = TestExtensions.ClientUuid, MAuthServiceUrl = mauthServiceUrl != null ? new Uri(mauthServiceUrl) : null, PrivateKey = privateKey - }, NullLoggerFactory.Instance)); + })); [Fact] public static void MAuthAuthenticator_WithDefaultUuid_WillThrowException() => Assert.Throws(() => new MAuthAuthenticator(new MAuthTestOptions() { ApplicationUuid = default(Guid) - }, NullLoggerFactory.Instance)); + })); [Theory] [InlineData("GET")] @@ -42,7 +41,7 @@ public static async Task AuthenticateRequest_WithValidRequest_WillAuthenticate(s // Arrange var testData = await method.FromResource(); - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions, NullLoggerFactory.Instance); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -70,7 +69,7 @@ public static async Task AuthenticateRequest_WithValidMWSV2Request_WillAuthentic // Arrange var testData = await method.FromResourceV2(); var version = MAuthVersion.MWSV2; - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions, NullLoggerFactory.Instance); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); var mAuthCore = new MAuthCoreV2(); var signedRequest = await mAuthCore @@ -101,7 +100,7 @@ public static async Task AuthenticateRequest_WithNumberOfAttempts_WillAuthentica var testData = await "GET".FromResource(); var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: true), NullLoggerFactory.Instance); + policy, shouldSucceedWithin: true)); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -131,7 +130,7 @@ public static async Task AuthenticateRequest_WithMWSV2Request_WithNumberOfAttemp var testData = await "GET".FromResourceV2(); var version = MAuthVersion.MWSV2; var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: true), NullLoggerFactory.Instance); + policy, shouldSucceedWithin: true)); var mAuthCore = new MAuthCoreV2(); var signedRequest = await mAuthCore @@ -161,7 +160,7 @@ public static async Task AuthenticateRequest_AfterNumberOfAttempts_WillThrowExce var testData = await "GET".FromResource(); var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: false), NullLoggerFactory.Instance); + policy, shouldSucceedWithin: false)); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -196,7 +195,7 @@ public static async Task AuthenticateRequest_WithMWSV2Request_AfterNumberOfAttem var testData = await "GET".FromResource(); var version = MAuthVersion.MWSV2; var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: false), NullLoggerFactory.Instance); + policy, shouldSucceedWithin: false)); var mAuthCore = new MAuthCoreV2(); var signedRequest = await mAuthCore @@ -278,7 +277,7 @@ public static async Task AuthenticateRequest_WithMWSVersion_WithDisableV1_WillTh var testData = await method.FromResource(); var testOptions = TestExtensions.ServerOptions; testOptions.DisableV1 = true; - var authenticator = new MAuthAuthenticator(testOptions, NullLoggerFactory.Instance); + var authenticator = new MAuthAuthenticator(testOptions); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -309,7 +308,7 @@ public static async Task GetAuthenticationInfo_WithSignedRequest_ForMWSV2Version var testData = await method.FromResourceV2(); var version = MAuthVersion.MWSV2; var testOptions = TestExtensions.ServerOptions; - var authenticator = new MAuthAuthenticator(testOptions, NullLoggerFactory.Instance); + var authenticator = new MAuthAuthenticator(testOptions); // Act var actual = authenticator.GetAuthenticationInfo(testData.ToHttpRequestMessage(version), version); @@ -331,7 +330,7 @@ public static async Task GetAuthenticationInfo_WithSignedRequest_ForMWSVersion_W var testData = await method.FromResource(); var version = MAuthVersion.MWS; var testOptions = TestExtensions.ServerOptions; - var authenticator = new MAuthAuthenticator(testOptions, NullLoggerFactory.Instance); + var authenticator = new MAuthAuthenticator(testOptions); // Act var actual = authenticator.GetAuthenticationInfo(testData.ToHttpRequestMessage(version), version); diff --git a/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs b/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs index d07c67b..432f3dc 100644 --- a/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs +++ b/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using Medidata.MAuth.Core; using Medidata.MAuth.Tests.Infrastructure; -using Microsoft.Extensions.Logging.Abstractions; using Xunit; namespace Medidata.MAuth.Tests @@ -68,7 +67,7 @@ public static async Task Authenticate_WithValidRequest_WillAuthenticate(string m }); // Act - var isAuthenticated = await signedRequest.Authenticate(TestExtensions.ServerOptions, NullLoggerFactory.Instance); + var isAuthenticated = await signedRequest.Authenticate(TestExtensions.ServerOptions); // Assert Assert.True(isAuthenticated); From 18c44997799c4da4ede5cb7b871195bbe2c04ad3 Mon Sep 17 00:00:00 2001 From: Prajon Date: Mon, 19 Aug 2019 13:01:56 -0400 Subject: [PATCH 28/40] Adding logging functionality during authentication. --- CHANGELOG.md | 3 +++ README.md | 7 +++++- .../MAuthAppBuilderExtensions.cs | 8 +++++-- .../MAuthMiddleware.cs | 5 ++-- src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 14 +++++++++-- src/Medidata.MAuth.Core/UtilityExtensions.cs | 6 +++-- .../MAuthAppBuilderExtensions.cs | 7 ++++-- src/Medidata.MAuth.Owin/MAuthMiddleware.cs | 7 +++--- .../MAuthAuthenticatingHandler.cs | 16 +++++++++---- .../Infrastructure/MAuthServerHandler.cs | 3 ++- .../MAuthAuthenticatorTests.cs | 23 ++++++++++--------- .../UtilityExtensionsTest.cs | 3 ++- version.props | 2 +- 13 files changed, 71 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74b55db..7853654 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changes in Medidata.MAuth +## v4.0.1 +- **[All]** Added logging suuport during authentication. + ## v4.0.0 - **[All]** Added implementation for MWSV2 signinig and authentication. diff --git a/README.md b/README.md index 316c1fa..c2b5b93 100644 --- a/README.md +++ b/README.md @@ -265,6 +265,10 @@ public static class WebApiConfig }; config.MessageHandlers.Add(new MAuthAuthenticatingHandler(options)); + + // or if there is a loggerFactory in the application + var loggerFactory = // get the loggerFactory of the logger being used. + config.MessageHandlers.Add(new MAuthAuthenticatingHandler(options, loggerFactory)); } } ``` @@ -279,13 +283,14 @@ public static class WebApiConfig public static void Register(HttpConfiguration config) { var options = // See the previous example + var loggerFactory = // See the previous example config.Routes.MapHttpRoute( name: "Route1", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional }, constraints: null, - handler: new MAuthAuthenticatingHandler(options) + handler: new MAuthAuthenticatingHandler(options, loggerFactory) ); } } diff --git a/src/Medidata.MAuth.AspNetCore/MAuthAppBuilderExtensions.cs b/src/Medidata.MAuth.AspNetCore/MAuthAppBuilderExtensions.cs index 35c90a1..c447a83 100644 --- a/src/Medidata.MAuth.AspNetCore/MAuthAppBuilderExtensions.cs +++ b/src/Medidata.MAuth.AspNetCore/MAuthAppBuilderExtensions.cs @@ -1,5 +1,8 @@ using System; using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Medidata.MAuth.AspNetCore { @@ -26,8 +29,9 @@ public static IApplicationBuilder UseMAuthAuthentication(this IApplicationBuilde if (options == null) throw new ArgumentNullException(nameof(options)); - - return app.UseMiddleware(options); + var loggerFactory = app.ApplicationServices.GetService() ?? + NullLoggerFactory.Instance; + return app.UseMiddleware(options, loggerFactory); } /// diff --git a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs index 0efdee7..69f679d 100644 --- a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs @@ -3,6 +3,7 @@ using Medidata.MAuth.Core; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Internal; +using Microsoft.Extensions.Logging; namespace Medidata.MAuth.AspNetCore { @@ -12,11 +13,11 @@ internal class MAuthMiddleware private readonly MAuthAuthenticator authenticator; private readonly RequestDelegate next; - public MAuthMiddleware(RequestDelegate next, MAuthMiddlewareOptions options) + public MAuthMiddleware(RequestDelegate next, MAuthMiddlewareOptions options, ILoggerFactory loggerFactory) { this.next = next; this.options = options; - this.authenticator = new MAuthAuthenticator(options); + this.authenticator = new MAuthAuthenticator(options, loggerFactory); } public async Task Invoke(HttpContext context) diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index df5eb95..d4768b5 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.Caching.Memory; using Org.BouncyCastle.Crypto; using Medidata.MAuth.Core.Models; +using Microsoft.Extensions.Logging; namespace Medidata.MAuth.Core { @@ -12,10 +13,11 @@ internal class MAuthAuthenticator { private readonly MAuthOptionsBase options; private readonly IMemoryCache cache = new MemoryCache(new MemoryCacheOptions()); + private readonly ILogger _logger; public Guid ApplicationUuid => options.ApplicationUuid; - public MAuthAuthenticator(MAuthOptionsBase options) + public MAuthAuthenticator(MAuthOptionsBase options, ILoggerFactory loggerFactory) { if (options.ApplicationUuid == default(Guid)) throw new ArgumentException(nameof(options.ApplicationUuid)); @@ -27,6 +29,7 @@ public MAuthAuthenticator(MAuthOptionsBase options) throw new ArgumentNullException(nameof(options.PrivateKey)); this.options = options; + this._logger = loggerFactory.CreateLogger(); } /// @@ -38,8 +41,11 @@ public async Task AuthenticateRequest(HttpRequestMessage request) { try { + _logger.LogInformation($"Initiating Authentication of request", request); var version = request.GetAuthHeaderValue().GetVersionFromAuthenticationHeader(); + _logger.LogInformation($"Authentication is for the request with {version} version."); + if (options.DisableV1 && version == MAuthVersion.MWS) throw new InvalidVersionException($"Authentication with {version} version is disabled."); @@ -52,25 +58,29 @@ public async Task AuthenticateRequest(HttpRequestMessage request) } catch (ArgumentException ex) { + _logger.LogError($"Unable to authenticate due to invalid MAuth authentication headers. Exception: {ex.Message}"); throw new AuthenticationException("The request has invalid MAuth authentication headers.", ex); } catch (RetriedRequestException ex) { + _logger.LogError($"Unable to query the application information from MAuth server. Exception:{ex.Message}"); throw new AuthenticationException( "Could not query the application information for the application from the MAuth server.", ex); } catch (InvalidCipherTextException ex) { - + _logger.LogError($"Unable to authenticate due to invalid payload information. Exception: {ex.Message}"); throw new AuthenticationException( "The request verification failed due to an invalid payload information.", ex); } catch (InvalidVersionException ex) { + _logger.LogError(ex, $"Unable to authenticate due to invalid version. Exception: {ex.Message}"); throw new InvalidVersionException(ex.Message, ex); } catch (Exception ex) { + _logger.LogError($"Unable to authenticate due to unexpected error. Exception: {ex.Message}"); throw new AuthenticationException( "An unexpected error occured during authentication. Please see the inner exception for details.", ex diff --git a/src/Medidata.MAuth.Core/UtilityExtensions.cs b/src/Medidata.MAuth.Core/UtilityExtensions.cs index 7fb474b..bc7ee05 100644 --- a/src/Medidata.MAuth.Core/UtilityExtensions.cs +++ b/src/Medidata.MAuth.Core/UtilityExtensions.cs @@ -2,6 +2,7 @@ using System.Net.Http; using System.Threading.Tasks; using Medidata.MAuth.Core.Models; +using Microsoft.Extensions.Logging; namespace Medidata.MAuth.Core { @@ -65,10 +66,11 @@ public static bool TryParseAuthenticationHeader(this string headerValue, /// /// The request message to authenticate. /// The MAuth options to use for the authentication. + /// The logger factory used with authentication. /// The task for the operation that is when completes will result in if /// the authentication is successful; otherwise . - public static Task Authenticate(this HttpRequestMessage request, MAuthOptionsBase options) => - new MAuthAuthenticator(options).AuthenticateRequest(request); + public static Task Authenticate(this HttpRequestMessage request, MAuthOptionsBase options, ILoggerFactory loggerFactory) => + new MAuthAuthenticator(options, loggerFactory).AuthenticateRequest(request); /// /// Determines the MAuth version enumerator reading authHeader. diff --git a/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs b/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs index 5a9cd17..ef2bb5f 100644 --- a/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs +++ b/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs @@ -1,4 +1,7 @@ using System; +using System.Linq; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Owin; namespace Medidata.MAuth.Owin @@ -24,8 +27,8 @@ public static IAppBuilder UseMAuthAuthentication(this IAppBuilder app, MAuthMidd if (options == null) throw new ArgumentNullException(nameof(options)); - - return app.Use(options); + var loggerFactory = NullLoggerFactory.Instance; + return app.Use(options, loggerFactory); } /// diff --git a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs index 3883f9e..de3fe03 100644 --- a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs @@ -1,6 +1,7 @@ using System.Net; using System.Threading.Tasks; using Medidata.MAuth.Core; +using Microsoft.Extensions.Logging; using Microsoft.Owin; namespace Medidata.MAuth.Owin @@ -10,16 +11,16 @@ internal class MAuthMiddleware: OwinMiddleware private readonly MAuthMiddlewareOptions options; private readonly MAuthAuthenticator authenticator; - public MAuthMiddleware(OwinMiddleware next, MAuthMiddlewareOptions options): base(next) + public MAuthMiddleware(OwinMiddleware next, MAuthMiddlewareOptions options, ILoggerFactory loggerFactory): base(next) { this.options = options; - authenticator = new MAuthAuthenticator(options); + authenticator = new MAuthAuthenticator(options, loggerFactory); } public override async Task Invoke(IOwinContext context) { await context.EnsureRequestBodyStreamSeekable(); - + var test = context.Get("test"); if (!options.Bypass(context.Request) && !await context.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized)) { diff --git a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs index c652d69..34e84ba 100644 --- a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs +++ b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs @@ -4,6 +4,8 @@ using System.Threading; using System.Threading.Tasks; using Medidata.MAuth.Core; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Medidata.MAuth.WebApi { @@ -24,11 +26,12 @@ public class MAuthAuthenticatingHandler : DelegatingHandler /// . /// /// The options for this message handler. - public MAuthAuthenticatingHandler(MAuthWebApiOptions options) + /// The logger factory used by this message handler. + public MAuthAuthenticatingHandler(MAuthWebApiOptions options, ILoggerFactory loggerFactory = null) { this.options = options; - - authenticator = new MAuthAuthenticator(options); + var loggerFact = loggerFactory ?? NullLoggerFactory.Instance; + authenticator = new MAuthAuthenticator(options, loggerFact); } /// @@ -39,10 +42,13 @@ public MAuthAuthenticatingHandler(MAuthWebApiOptions options) /// /// The inner handler which is responsible for processing the HTTP response messages. /// - public MAuthAuthenticatingHandler(MAuthWebApiOptions options, HttpMessageHandler innerHandler) : base(innerHandler) + /// The logger factory used by this message handler. + public MAuthAuthenticatingHandler(MAuthWebApiOptions options, HttpMessageHandler innerHandler, + ILoggerFactory loggerFactory = null) : base(innerHandler) { this.options = options; - authenticator = new MAuthAuthenticator(options); + var loggerFact = loggerFactory ?? NullLoggerFactory.Instance; + authenticator = new MAuthAuthenticator(options, loggerFact); } /// diff --git a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs index 2d497a3..0515bb5 100644 --- a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs +++ b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using Medidata.MAuth.Core; +using Microsoft.Extensions.Logging.Abstractions; using Newtonsoft.Json; namespace Medidata.MAuth.Tests.Infrastructure @@ -25,7 +26,7 @@ protected override async Task SendAsync( if (currentNumberOfAttempts < SucceedAfterThisManyAttempts) return new HttpResponseMessage(HttpStatusCode.ServiceUnavailable); - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions, NullLoggerFactory.Instance); var authInfo = authenticator.GetAuthenticationInfo(request, version); diff --git a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs index 442924e..82d5bde 100644 --- a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs @@ -6,6 +6,7 @@ using Medidata.MAuth.Core.Exceptions; using Medidata.MAuth.Core.Models; using Medidata.MAuth.Tests.Infrastructure; +using Microsoft.Extensions.Logging.Abstractions; using Xunit; namespace Medidata.MAuth.Tests @@ -22,14 +23,14 @@ public static void MAuthAuthenticator_WithInvalidOptions_WillThrowException( ApplicationUuid = TestExtensions.ClientUuid, MAuthServiceUrl = mauthServiceUrl != null ? new Uri(mauthServiceUrl) : null, PrivateKey = privateKey - })); + }, NullLoggerFactory.Instance)); [Fact] public static void MAuthAuthenticator_WithDefaultUuid_WillThrowException() => Assert.Throws(() => new MAuthAuthenticator(new MAuthTestOptions() { ApplicationUuid = default(Guid) - })); + }, NullLoggerFactory.Instance)); [Theory] [InlineData("GET")] @@ -41,7 +42,7 @@ public static async Task AuthenticateRequest_WithValidRequest_WillAuthenticate(s // Arrange var testData = await method.FromResource(); - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions, NullLoggerFactory.Instance); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -69,7 +70,7 @@ public static async Task AuthenticateRequest_WithValidMWSV2Request_WillAuthentic // Arrange var testData = await method.FromResourceV2(); var version = MAuthVersion.MWSV2; - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions, NullLoggerFactory.Instance); var mAuthCore = new MAuthCoreV2(); var signedRequest = await mAuthCore @@ -100,7 +101,7 @@ public static async Task AuthenticateRequest_WithNumberOfAttempts_WillAuthentica var testData = await "GET".FromResource(); var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: true)); + policy, shouldSucceedWithin: true), NullLoggerFactory.Instance); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -130,7 +131,7 @@ public static async Task AuthenticateRequest_WithMWSV2Request_WithNumberOfAttemp var testData = await "GET".FromResourceV2(); var version = MAuthVersion.MWSV2; var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: true)); + policy, shouldSucceedWithin: true), NullLoggerFactory.Instance); var mAuthCore = new MAuthCoreV2(); var signedRequest = await mAuthCore @@ -160,7 +161,7 @@ public static async Task AuthenticateRequest_AfterNumberOfAttempts_WillThrowExce var testData = await "GET".FromResource(); var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: false)); + policy, shouldSucceedWithin: false), NullLoggerFactory.Instance); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -195,7 +196,7 @@ public static async Task AuthenticateRequest_WithMWSV2Request_AfterNumberOfAttem var testData = await "GET".FromResource(); var version = MAuthVersion.MWSV2; var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: false)); + policy, shouldSucceedWithin: false), NullLoggerFactory.Instance); var mAuthCore = new MAuthCoreV2(); var signedRequest = await mAuthCore @@ -277,7 +278,7 @@ public static async Task AuthenticateRequest_WithMWSVersion_WithDisableV1_WillTh var testData = await method.FromResource(); var testOptions = TestExtensions.ServerOptions; testOptions.DisableV1 = true; - var authenticator = new MAuthAuthenticator(testOptions); + var authenticator = new MAuthAuthenticator(testOptions, NullLoggerFactory.Instance); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -308,7 +309,7 @@ public static async Task GetAuthenticationInfo_WithSignedRequest_ForMWSV2Version var testData = await method.FromResourceV2(); var version = MAuthVersion.MWSV2; var testOptions = TestExtensions.ServerOptions; - var authenticator = new MAuthAuthenticator(testOptions); + var authenticator = new MAuthAuthenticator(testOptions, NullLoggerFactory.Instance); // Act var actual = authenticator.GetAuthenticationInfo(testData.ToHttpRequestMessage(version), version); @@ -330,7 +331,7 @@ public static async Task GetAuthenticationInfo_WithSignedRequest_ForMWSVersion_W var testData = await method.FromResource(); var version = MAuthVersion.MWS; var testOptions = TestExtensions.ServerOptions; - var authenticator = new MAuthAuthenticator(testOptions); + var authenticator = new MAuthAuthenticator(testOptions, NullLoggerFactory.Instance); // Act var actual = authenticator.GetAuthenticationInfo(testData.ToHttpRequestMessage(version), version); diff --git a/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs b/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs index 432f3dc..d07c67b 100644 --- a/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs +++ b/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Medidata.MAuth.Core; using Medidata.MAuth.Tests.Infrastructure; +using Microsoft.Extensions.Logging.Abstractions; using Xunit; namespace Medidata.MAuth.Tests @@ -67,7 +68,7 @@ public static async Task Authenticate_WithValidRequest_WillAuthenticate(string m }); // Act - var isAuthenticated = await signedRequest.Authenticate(TestExtensions.ServerOptions); + var isAuthenticated = await signedRequest.Authenticate(TestExtensions.ServerOptions, NullLoggerFactory.Instance); // Assert Assert.True(isAuthenticated); diff --git a/version.props b/version.props index ceb060e..4716aab 100644 --- a/version.props +++ b/version.props @@ -1,6 +1,6 @@  - 4.0.0 + 4.0.1 From 4fb1ae1b46f16d734ad91f864c61a8ab60f9a0c2 Mon Sep 17 00:00:00 2001 From: Prajon Date: Tue, 20 Aug 2019 10:51:35 -0400 Subject: [PATCH 29/40] Updated feedback --- .../MAuthAppBuilderExtensions.cs | 7 +------ .../MAuthMiddleware.cs | 17 ++++++++++++++++- src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 14 +++++++------- .../MAuthAppBuilderExtensions.cs | 7 ++----- src/Medidata.MAuth.Owin/MAuthMiddleware.cs | 7 +++---- .../MAuthAuthenticatingHandler.cs | 6 ++---- 6 files changed, 31 insertions(+), 27 deletions(-) diff --git a/src/Medidata.MAuth.AspNetCore/MAuthAppBuilderExtensions.cs b/src/Medidata.MAuth.AspNetCore/MAuthAppBuilderExtensions.cs index c447a83..84a8192 100644 --- a/src/Medidata.MAuth.AspNetCore/MAuthAppBuilderExtensions.cs +++ b/src/Medidata.MAuth.AspNetCore/MAuthAppBuilderExtensions.cs @@ -1,8 +1,5 @@ using System; using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; namespace Medidata.MAuth.AspNetCore { @@ -29,9 +26,7 @@ public static IApplicationBuilder UseMAuthAuthentication(this IApplicationBuilde if (options == null) throw new ArgumentNullException(nameof(options)); - var loggerFactory = app.ApplicationServices.GetService() ?? - NullLoggerFactory.Instance; - return app.UseMiddleware(options, loggerFactory); + return app.UseMiddleware(options); } /// diff --git a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs index 69f679d..f948a48 100644 --- a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs @@ -4,22 +4,37 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Internal; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Medidata.MAuth.AspNetCore { + /// + /// Enables the middleware for the aspnet core applications. + /// internal class MAuthMiddleware { private readonly MAuthMiddlewareOptions options; private readonly MAuthAuthenticator authenticator; private readonly RequestDelegate next; + /// + /// Creates a new + /// + /// The representing the next middleware in the pipeline. + /// The representing the options for the middleware. + /// The representing the factory that used to create logger instances. public MAuthMiddleware(RequestDelegate next, MAuthMiddlewareOptions options, ILoggerFactory loggerFactory) { this.next = next; this.options = options; - this.authenticator = new MAuthAuthenticator(options, loggerFactory); + this.authenticator = new MAuthAuthenticator(options, loggerFactory ?? NullLoggerFactory.Instance); } + /// + /// Invokes the logic of the middleware. + /// + /// The . + /// A that completes when the middleware has completed processing. public async Task Invoke(HttpContext context) { context.Request.EnableRewind(); diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index d4768b5..e78c14d 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -41,10 +41,10 @@ public async Task AuthenticateRequest(HttpRequestMessage request) { try { - _logger.LogInformation($"Initiating Authentication of request", request); + _logger.LogInformation("Initiating Authentication of {request}.", request); var version = request.GetAuthHeaderValue().GetVersionFromAuthenticationHeader(); - _logger.LogInformation($"Authentication is for the request with {version} version."); + _logger.LogInformation("Authentication is for {version}.", version); if (options.DisableV1 && version == MAuthVersion.MWS) throw new InvalidVersionException($"Authentication with {version} version is disabled."); @@ -58,29 +58,29 @@ public async Task AuthenticateRequest(HttpRequestMessage request) } catch (ArgumentException ex) { - _logger.LogError($"Unable to authenticate due to invalid MAuth authentication headers. Exception: {ex.Message}"); + _logger.LogError(ex, "Unable to authenticate due to invalid MAuth authentication headers."); throw new AuthenticationException("The request has invalid MAuth authentication headers.", ex); } catch (RetriedRequestException ex) { - _logger.LogError($"Unable to query the application information from MAuth server. Exception:{ex.Message}"); + _logger.LogError(ex, "Unable to query the application information from MAuth server."); throw new AuthenticationException( "Could not query the application information for the application from the MAuth server.", ex); } catch (InvalidCipherTextException ex) { - _logger.LogError($"Unable to authenticate due to invalid payload information. Exception: {ex.Message}"); + _logger.LogError(ex, "Unable to authenticate due to invalid payload information."); throw new AuthenticationException( "The request verification failed due to an invalid payload information.", ex); } catch (InvalidVersionException ex) { - _logger.LogError(ex, $"Unable to authenticate due to invalid version. Exception: {ex.Message}"); + _logger.LogError(ex, "Unable to authenticate due to invalid version."); throw new InvalidVersionException(ex.Message, ex); } catch (Exception ex) { - _logger.LogError($"Unable to authenticate due to unexpected error. Exception: {ex.Message}"); + _logger.LogError(ex, "Unable to authenticate due to unexpected error."); throw new AuthenticationException( "An unexpected error occured during authentication. Please see the inner exception for details.", ex diff --git a/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs b/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs index ef2bb5f..5a9cd17 100644 --- a/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs +++ b/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs @@ -1,7 +1,4 @@ using System; -using System.Linq; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; using Owin; namespace Medidata.MAuth.Owin @@ -27,8 +24,8 @@ public static IAppBuilder UseMAuthAuthentication(this IAppBuilder app, MAuthMidd if (options == null) throw new ArgumentNullException(nameof(options)); - var loggerFactory = NullLoggerFactory.Instance; - return app.Use(options, loggerFactory); + + return app.Use(options); } /// diff --git a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs index de3fe03..7bec6f3 100644 --- a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs @@ -1,7 +1,7 @@ using System.Net; using System.Threading.Tasks; using Medidata.MAuth.Core; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Owin; namespace Medidata.MAuth.Owin @@ -11,16 +11,15 @@ internal class MAuthMiddleware: OwinMiddleware private readonly MAuthMiddlewareOptions options; private readonly MAuthAuthenticator authenticator; - public MAuthMiddleware(OwinMiddleware next, MAuthMiddlewareOptions options, ILoggerFactory loggerFactory): base(next) + public MAuthMiddleware(OwinMiddleware next, MAuthMiddlewareOptions options) : base(next) { this.options = options; - authenticator = new MAuthAuthenticator(options, loggerFactory); + authenticator = new MAuthAuthenticator(options, NullLoggerFactory.Instance); } public override async Task Invoke(IOwinContext context) { await context.EnsureRequestBodyStreamSeekable(); - var test = context.Get("test"); if (!options.Bypass(context.Request) && !await context.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized)) { diff --git a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs index 34e84ba..5ff8500 100644 --- a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs +++ b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs @@ -30,8 +30,7 @@ public class MAuthAuthenticatingHandler : DelegatingHandler public MAuthAuthenticatingHandler(MAuthWebApiOptions options, ILoggerFactory loggerFactory = null) { this.options = options; - var loggerFact = loggerFactory ?? NullLoggerFactory.Instance; - authenticator = new MAuthAuthenticator(options, loggerFact); + authenticator = new MAuthAuthenticator(options, loggerFactory ?? NullLoggerFactory.Instance); } /// @@ -47,8 +46,7 @@ public MAuthAuthenticatingHandler(MAuthWebApiOptions options, HttpMessageHandler ILoggerFactory loggerFactory = null) : base(innerHandler) { this.options = options; - var loggerFact = loggerFactory ?? NullLoggerFactory.Instance; - authenticator = new MAuthAuthenticator(options, loggerFact); + authenticator = new MAuthAuthenticator(options, loggerFactory ?? NullLoggerFactory.Instance); } /// From a00f128be5223c67e814d7d239ba37e09a2b690a Mon Sep 17 00:00:00 2001 From: Prajon Date: Thu, 22 Aug 2019 16:14:26 -0400 Subject: [PATCH 30/40] Feedback implementation - indents, version update revert and logging --- CHANGELOG.md | 5 +---- README.md | 6 +++--- src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 4 ++-- version.props | 2 +- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7853654..60d2941 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,7 @@ # Changes in Medidata.MAuth -## v4.0.1 -- **[All]** Added logging suuport during authentication. - ## v4.0.0 -- **[All]** Added implementation for MWSV2 signinig and authentication. +- **[All]** Added implementation for MWSV2 signinig and authentication. Added logging support during MAuthentication. ## v3.1.3 - **[Core]** Refactored `MAuthCoreExtensions.cs` and moved Signing and Verification method into `IMAuthCore.cs`. diff --git a/README.md b/README.md index c2b5b93..1a15198 100644 --- a/README.md +++ b/README.md @@ -266,9 +266,9 @@ public static class WebApiConfig config.MessageHandlers.Add(new MAuthAuthenticatingHandler(options)); - // or if there is a loggerFactory in the application - var loggerFactory = // get the loggerFactory of the logger being used. - config.MessageHandlers.Add(new MAuthAuthenticatingHandler(options, loggerFactory)); + // or if there is a loggerFactory in the application + var loggerFactory = // get the loggerFactory of the logger being used. + config.MessageHandlers.Add(new MAuthAuthenticatingHandler(options, loggerFactory)); } } ``` diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index e78c14d..e22435b 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -41,7 +41,7 @@ public async Task AuthenticateRequest(HttpRequestMessage request) { try { - _logger.LogInformation("Initiating Authentication of {request}.", request); + _logger.LogInformation("Initiating Authentication of the request"); var version = request.GetAuthHeaderValue().GetVersionFromAuthenticationHeader(); _logger.LogInformation("Authentication is for {version}.", version); @@ -69,7 +69,7 @@ public async Task AuthenticateRequest(HttpRequestMessage request) } catch (InvalidCipherTextException ex) { - _logger.LogError(ex, "Unable to authenticate due to invalid payload information."); + _logger.LogWarning(ex, "Unable to authenticate due to invalid payload information."); throw new AuthenticationException( "The request verification failed due to an invalid payload information.", ex); } diff --git a/version.props b/version.props index 4716aab..ceb060e 100644 --- a/version.props +++ b/version.props @@ -1,6 +1,6 @@  - 4.0.1 + 4.0.0 From 856455139e757cbdafbc4f57ee9d33df606d1a1f Mon Sep 17 00:00:00 2001 From: Prajon Date: Tue, 27 Aug 2019 10:23:09 -0400 Subject: [PATCH 31/40] Updated for MAuth.Owin as per feedback --- README.md | 5 ++++- src/Medidata.MAuth.Owin/MAuthMiddleware.cs | 2 +- src/Medidata.MAuth.Owin/MAuthMiddlewareOptions.cs | 8 ++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1a15198..4270b52 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,9 @@ public class Startup // when ready to disable authentication of V1 protococl options.DisableV1 = true; + + //or if ILoggerFactory instance available + options.LoggerFactory = // get the loggerFactory of the logger being used. }); } } @@ -283,7 +286,7 @@ public static class WebApiConfig public static void Register(HttpConfiguration config) { var options = // See the previous example - var loggerFactory = // See the previous example + var loggerFactory = // See the previous example config.Routes.MapHttpRoute( name: "Route1", diff --git a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs index 7bec6f3..fe6681d 100644 --- a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs @@ -14,7 +14,7 @@ internal class MAuthMiddleware: OwinMiddleware public MAuthMiddleware(OwinMiddleware next, MAuthMiddlewareOptions options) : base(next) { this.options = options; - authenticator = new MAuthAuthenticator(options, NullLoggerFactory.Instance); + authenticator = new MAuthAuthenticator(options, options.LoggerFactory ?? NullLoggerFactory.Instance); } public override async Task Invoke(IOwinContext context) diff --git a/src/Medidata.MAuth.Owin/MAuthMiddlewareOptions.cs b/src/Medidata.MAuth.Owin/MAuthMiddlewareOptions.cs index 96e3811..27a10be 100644 --- a/src/Medidata.MAuth.Owin/MAuthMiddlewareOptions.cs +++ b/src/Medidata.MAuth.Owin/MAuthMiddlewareOptions.cs @@ -1,5 +1,7 @@ using System; using Medidata.MAuth.Core; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Owin; namespace Medidata.MAuth.Owin @@ -26,5 +28,11 @@ public class MAuthMiddlewareOptions: MAuthOptionsBase /// the options, every request will be authenticated by default. /// public Func Bypass { get; set; } = (request) => false; + + /// + /// Determines the instance passed by the application to create the logger. + /// The default is . + /// + public ILoggerFactory LoggerFactory { get; set; } = NullLoggerFactory.Instance; } } From d3f7d33d8a31bc7fbc7551ce68b52ecb688d5b69 Mon Sep 17 00:00:00 2001 From: Prajon Date: Wed, 4 Sep 2019 20:51:31 -0400 Subject: [PATCH 32/40] Modified to use log4net for logging via OWIN and older dotnet framework --- README.md | 10 +-- .../MAuthAspNetCoreExtensions.cs | 5 +- .../MAuthMiddleware.cs | 6 +- src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 77 ++++++++++++++++--- .../Medidata.MAuth.Core.csproj | 1 + src/Medidata.MAuth.Core/UtilityExtensions.cs | 2 +- src/Medidata.MAuth.Owin/MAuthMiddleware.cs | 3 +- .../MAuthMiddlewareOptions.cs | 8 -- .../Medidata.MAuth.Owin.csproj | 4 + .../MAuthAuthenticatingHandler.cs | 13 +--- .../Infrastructure/MAuthServerHandler.cs | 2 +- .../MAuthAuthenticatorTests.cs | 23 +++--- 12 files changed, 98 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 4270b52..316c1fa 100644 --- a/README.md +++ b/README.md @@ -174,9 +174,6 @@ public class Startup // when ready to disable authentication of V1 protococl options.DisableV1 = true; - - //or if ILoggerFactory instance available - options.LoggerFactory = // get the loggerFactory of the logger being used. }); } } @@ -268,10 +265,6 @@ public static class WebApiConfig }; config.MessageHandlers.Add(new MAuthAuthenticatingHandler(options)); - - // or if there is a loggerFactory in the application - var loggerFactory = // get the loggerFactory of the logger being used. - config.MessageHandlers.Add(new MAuthAuthenticatingHandler(options, loggerFactory)); } } ``` @@ -286,14 +279,13 @@ public static class WebApiConfig public static void Register(HttpConfiguration config) { var options = // See the previous example - var loggerFactory = // See the previous example config.Routes.MapHttpRoute( name: "Route1", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional }, constraints: null, - handler: new MAuthAuthenticatingHandler(options, loggerFactory) + handler: new MAuthAuthenticatingHandler(options) ); } } diff --git a/src/Medidata.MAuth.AspNetCore/MAuthAspNetCoreExtensions.cs b/src/Medidata.MAuth.AspNetCore/MAuthAspNetCoreExtensions.cs index 7be333b..78d9c38 100644 --- a/src/Medidata.MAuth.AspNetCore/MAuthAspNetCoreExtensions.cs +++ b/src/Medidata.MAuth.AspNetCore/MAuthAspNetCoreExtensions.cs @@ -5,6 +5,7 @@ using Medidata.MAuth.Core; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.Extensions.Logging; namespace Medidata.MAuth.AspNetCore { @@ -45,11 +46,11 @@ public static HttpRequestMessage ToHttpRequestMessage(this HttpRequest request) /// will throw an exception if any errors occurred during the authentication. /// public static async Task TryAuthenticate( - this HttpContext context, MAuthAuthenticator authenticator, bool shouldIgnoreExceptions) + this HttpContext context, MAuthAuthenticator authenticator, bool shouldIgnoreExceptions, ILoggerFactory loggerFactory) { try { - return await authenticator.AuthenticateRequest(context.Request.ToHttpRequestMessage()); + return await authenticator.AuthenticateRequest(context.Request.ToHttpRequestMessage(), loggerFactory); } catch (Exception) { diff --git a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs index f948a48..b370b9e 100644 --- a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs @@ -16,6 +16,7 @@ internal class MAuthMiddleware private readonly MAuthMiddlewareOptions options; private readonly MAuthAuthenticator authenticator; private readonly RequestDelegate next; + private readonly ILoggerFactory loggerFactory; /// /// Creates a new @@ -27,7 +28,8 @@ public MAuthMiddleware(RequestDelegate next, MAuthMiddlewareOptions options, ILo { this.next = next; this.options = options; - this.authenticator = new MAuthAuthenticator(options, loggerFactory ?? NullLoggerFactory.Instance); + this.loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; + this.authenticator = new MAuthAuthenticator(options); //, loggerFactory ?? NullLoggerFactory.Instance); } /// @@ -40,7 +42,7 @@ public async Task Invoke(HttpContext context) context.Request.EnableRewind(); if (!options.Bypass(context.Request) && - !await context.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized)) + !await context.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized, loggerFactory)) { context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; return; diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index e22435b..e02bfdd 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -6,6 +6,7 @@ using Org.BouncyCastle.Crypto; using Medidata.MAuth.Core.Models; using Microsoft.Extensions.Logging; +using log4net; namespace Medidata.MAuth.Core { @@ -13,11 +14,10 @@ internal class MAuthAuthenticator { private readonly MAuthOptionsBase options; private readonly IMemoryCache cache = new MemoryCache(new MemoryCacheOptions()); - private readonly ILogger _logger; public Guid ApplicationUuid => options.ApplicationUuid; - public MAuthAuthenticator(MAuthOptionsBase options, ILoggerFactory loggerFactory) + public MAuthAuthenticator(MAuthOptionsBase options) { if (options.ApplicationUuid == default(Guid)) throw new ArgumentException(nameof(options.ApplicationUuid)); @@ -29,22 +29,79 @@ public MAuthAuthenticator(MAuthOptionsBase options, ILoggerFactory loggerFactory throw new ArgumentNullException(nameof(options.PrivateKey)); this.options = options; - this._logger = loggerFactory.CreateLogger(); } /// /// Verifies if the request is authenticated or not. /// /// The request. + /// The used with authentication. + /// A task object of the boolean value that verifies if the request is authenticated or not. + public async Task AuthenticateRequest(HttpRequestMessage request, ILoggerFactory loggerFactory) + { + var logger = loggerFactory.CreateLogger(); + try + { + logger.LogInformation("Initiating Authentication of the request"); + var version = request.GetAuthHeaderValue().GetVersionFromAuthenticationHeader(); + + logger.LogInformation("Authentication is for {version}.", version); + + if (options.DisableV1 && version == MAuthVersion.MWS) + throw new InvalidVersionException($"Authentication with {version} version is disabled."); + + var mAuthCore = MAuthCoreFactory.Instantiate(version); + var authInfo = GetAuthenticationInfo(request, version); + var appInfo = await GetApplicationInfo(authInfo.ApplicationUuid, version); + + return mAuthCore.Verify(authInfo.Payload, await mAuthCore.GetSignature(request, authInfo), + appInfo.PublicKey); + } + catch (ArgumentException ex) + { + logger.LogError(ex, "Unable to authenticate due to invalid MAuth authentication headers."); + throw new AuthenticationException("The request has invalid MAuth authentication headers.", ex); + } + catch (RetriedRequestException ex) + { + logger.LogError(ex, "Unable to query the application information from MAuth server."); + throw new AuthenticationException( + "Could not query the application information for the application from the MAuth server.", ex); + } + catch (InvalidCipherTextException ex) + { + logger.LogWarning(ex, "Unable to authenticate due to invalid payload information."); + throw new AuthenticationException( + "The request verification failed due to an invalid payload information.", ex); + } + catch (InvalidVersionException ex) + { + logger.LogError(ex, "Unable to authenticate due to invalid version."); + throw new InvalidVersionException(ex.Message, ex); + } + catch (Exception ex) + { + logger.LogError(ex, "Unable to authenticate due to unexpected error."); + throw new AuthenticationException( + "An unexpected error occured during authentication. Please see the inner exception for details.", + ex + ); + } + } + /// + /// Verifies if the request is authenticated or not. + /// + /// The request. /// A task object of the boolean value that verifies if the request is authenticated or not. public async Task AuthenticateRequest(HttpRequestMessage request) { + var logger = LogManager.GetLogger(typeof(MAuthAuthenticator)); try { - _logger.LogInformation("Initiating Authentication of the request"); + logger.Info("Initiating Authentication of the request"); var version = request.GetAuthHeaderValue().GetVersionFromAuthenticationHeader(); - _logger.LogInformation("Authentication is for {version}.", version); + logger.InfoFormat("Authentication is for the {0}.", version); if (options.DisableV1 && version == MAuthVersion.MWS) throw new InvalidVersionException($"Authentication with {version} version is disabled."); @@ -58,29 +115,29 @@ public async Task AuthenticateRequest(HttpRequestMessage request) } catch (ArgumentException ex) { - _logger.LogError(ex, "Unable to authenticate due to invalid MAuth authentication headers."); + logger.Error("Unable to authenticate due to invalid MAuth authentication headers.", ex); throw new AuthenticationException("The request has invalid MAuth authentication headers.", ex); } catch (RetriedRequestException ex) { - _logger.LogError(ex, "Unable to query the application information from MAuth server."); + logger.Error("Unable to query the application information from MAuth server.", ex); throw new AuthenticationException( "Could not query the application information for the application from the MAuth server.", ex); } catch (InvalidCipherTextException ex) { - _logger.LogWarning(ex, "Unable to authenticate due to invalid payload information."); + logger.Warn("Unable to authenticate due to invalid payload information.", ex); throw new AuthenticationException( "The request verification failed due to an invalid payload information.", ex); } catch (InvalidVersionException ex) { - _logger.LogError(ex, "Unable to authenticate due to invalid version."); + logger.Error("Unable to authenticate due to invalid version.", ex); throw new InvalidVersionException(ex.Message, ex); } catch (Exception ex) { - _logger.LogError(ex, "Unable to authenticate due to unexpected error."); + logger.Error("Unable to authenticate due to unexpected error.", ex); throw new AuthenticationException( "An unexpected error occured during authentication. Please see the inner exception for details.", ex diff --git a/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj b/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj index 1c15fe7..23aaadf 100644 --- a/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj +++ b/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj @@ -10,6 +10,7 @@ + diff --git a/src/Medidata.MAuth.Core/UtilityExtensions.cs b/src/Medidata.MAuth.Core/UtilityExtensions.cs index bc7ee05..6c826ec 100644 --- a/src/Medidata.MAuth.Core/UtilityExtensions.cs +++ b/src/Medidata.MAuth.Core/UtilityExtensions.cs @@ -70,7 +70,7 @@ public static bool TryParseAuthenticationHeader(this string headerValue, /// The task for the operation that is when completes will result in if /// the authentication is successful; otherwise . public static Task Authenticate(this HttpRequestMessage request, MAuthOptionsBase options, ILoggerFactory loggerFactory) => - new MAuthAuthenticator(options, loggerFactory).AuthenticateRequest(request); + new MAuthAuthenticator(options).AuthenticateRequest(request, loggerFactory); /// /// Determines the MAuth version enumerator reading authHeader. diff --git a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs index fe6681d..9dc975b 100644 --- a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs @@ -1,7 +1,6 @@ using System.Net; using System.Threading.Tasks; using Medidata.MAuth.Core; -using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Owin; namespace Medidata.MAuth.Owin @@ -14,7 +13,7 @@ internal class MAuthMiddleware: OwinMiddleware public MAuthMiddleware(OwinMiddleware next, MAuthMiddlewareOptions options) : base(next) { this.options = options; - authenticator = new MAuthAuthenticator(options, options.LoggerFactory ?? NullLoggerFactory.Instance); + authenticator = new MAuthAuthenticator(options); } public override async Task Invoke(IOwinContext context) diff --git a/src/Medidata.MAuth.Owin/MAuthMiddlewareOptions.cs b/src/Medidata.MAuth.Owin/MAuthMiddlewareOptions.cs index 27a10be..96e3811 100644 --- a/src/Medidata.MAuth.Owin/MAuthMiddlewareOptions.cs +++ b/src/Medidata.MAuth.Owin/MAuthMiddlewareOptions.cs @@ -1,7 +1,5 @@ using System; using Medidata.MAuth.Core; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Owin; namespace Medidata.MAuth.Owin @@ -28,11 +26,5 @@ public class MAuthMiddlewareOptions: MAuthOptionsBase /// the options, every request will be authenticated by default. /// public Func Bypass { get; set; } = (request) => false; - - /// - /// Determines the instance passed by the application to create the logger. - /// The default is . - /// - public ILoggerFactory LoggerFactory { get; set; } = NullLoggerFactory.Instance; } } diff --git a/src/Medidata.MAuth.Owin/Medidata.MAuth.Owin.csproj b/src/Medidata.MAuth.Owin/Medidata.MAuth.Owin.csproj index a99ed86..cacb895 100644 --- a/src/Medidata.MAuth.Owin/Medidata.MAuth.Owin.csproj +++ b/src/Medidata.MAuth.Owin/Medidata.MAuth.Owin.csproj @@ -9,6 +9,10 @@ medidata;mauth;hmac;authentication;core;owin;middleware;webapi + + + + diff --git a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs index 5ff8500..daf5cb6 100644 --- a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs +++ b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs @@ -4,8 +4,6 @@ using System.Threading; using System.Threading.Tasks; using Medidata.MAuth.Core; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; namespace Medidata.MAuth.WebApi { @@ -26,11 +24,10 @@ public class MAuthAuthenticatingHandler : DelegatingHandler /// . /// /// The options for this message handler. - /// The logger factory used by this message handler. - public MAuthAuthenticatingHandler(MAuthWebApiOptions options, ILoggerFactory loggerFactory = null) + public MAuthAuthenticatingHandler(MAuthWebApiOptions options) { this.options = options; - authenticator = new MAuthAuthenticator(options, loggerFactory ?? NullLoggerFactory.Instance); + authenticator = new MAuthAuthenticator(options); } /// @@ -41,12 +38,10 @@ public MAuthAuthenticatingHandler(MAuthWebApiOptions options, ILoggerFactory log /// /// The inner handler which is responsible for processing the HTTP response messages. /// - /// The logger factory used by this message handler. - public MAuthAuthenticatingHandler(MAuthWebApiOptions options, HttpMessageHandler innerHandler, - ILoggerFactory loggerFactory = null) : base(innerHandler) + public MAuthAuthenticatingHandler(MAuthWebApiOptions options, HttpMessageHandler innerHandler) : base(innerHandler) { this.options = options; - authenticator = new MAuthAuthenticator(options, loggerFactory ?? NullLoggerFactory.Instance); + authenticator = new MAuthAuthenticator(options); } /// diff --git a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs index 0515bb5..e7eb451 100644 --- a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs +++ b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs @@ -26,7 +26,7 @@ protected override async Task SendAsync( if (currentNumberOfAttempts < SucceedAfterThisManyAttempts) return new HttpResponseMessage(HttpStatusCode.ServiceUnavailable); - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions, NullLoggerFactory.Instance); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions);//, NullLoggerFactory.Instance); var authInfo = authenticator.GetAuthenticationInfo(request, version); diff --git a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs index 82d5bde..49ea6eb 100644 --- a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs @@ -6,7 +6,6 @@ using Medidata.MAuth.Core.Exceptions; using Medidata.MAuth.Core.Models; using Medidata.MAuth.Tests.Infrastructure; -using Microsoft.Extensions.Logging.Abstractions; using Xunit; namespace Medidata.MAuth.Tests @@ -23,14 +22,14 @@ public static void MAuthAuthenticator_WithInvalidOptions_WillThrowException( ApplicationUuid = TestExtensions.ClientUuid, MAuthServiceUrl = mauthServiceUrl != null ? new Uri(mauthServiceUrl) : null, PrivateKey = privateKey - }, NullLoggerFactory.Instance)); + }));//, NullLoggerFactory.Instance)); [Fact] public static void MAuthAuthenticator_WithDefaultUuid_WillThrowException() => Assert.Throws(() => new MAuthAuthenticator(new MAuthTestOptions() { ApplicationUuid = default(Guid) - }, NullLoggerFactory.Instance)); + }));//, NullLoggerFactory.Instance)); [Theory] [InlineData("GET")] @@ -42,7 +41,7 @@ public static async Task AuthenticateRequest_WithValidRequest_WillAuthenticate(s // Arrange var testData = await method.FromResource(); - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions, NullLoggerFactory.Instance); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions);//, NullLoggerFactory.Instance); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -70,7 +69,7 @@ public static async Task AuthenticateRequest_WithValidMWSV2Request_WillAuthentic // Arrange var testData = await method.FromResourceV2(); var version = MAuthVersion.MWSV2; - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions, NullLoggerFactory.Instance); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions);//, NullLoggerFactory.Instance); var mAuthCore = new MAuthCoreV2(); var signedRequest = await mAuthCore @@ -101,7 +100,7 @@ public static async Task AuthenticateRequest_WithNumberOfAttempts_WillAuthentica var testData = await "GET".FromResource(); var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: true), NullLoggerFactory.Instance); + policy, shouldSucceedWithin: true));//, NullLoggerFactory.Instance); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -131,7 +130,7 @@ public static async Task AuthenticateRequest_WithMWSV2Request_WithNumberOfAttemp var testData = await "GET".FromResourceV2(); var version = MAuthVersion.MWSV2; var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: true), NullLoggerFactory.Instance); + policy, shouldSucceedWithin: true));//, NullLoggerFactory.Instance); var mAuthCore = new MAuthCoreV2(); var signedRequest = await mAuthCore @@ -161,7 +160,7 @@ public static async Task AuthenticateRequest_AfterNumberOfAttempts_WillThrowExce var testData = await "GET".FromResource(); var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: false), NullLoggerFactory.Instance); + policy, shouldSucceedWithin: false));//, NullLoggerFactory.Instance); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -196,7 +195,7 @@ public static async Task AuthenticateRequest_WithMWSV2Request_AfterNumberOfAttem var testData = await "GET".FromResource(); var version = MAuthVersion.MWSV2; var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: false), NullLoggerFactory.Instance); + policy, shouldSucceedWithin: false));//, NullLoggerFactory.Instance); var mAuthCore = new MAuthCoreV2(); var signedRequest = await mAuthCore @@ -278,7 +277,7 @@ public static async Task AuthenticateRequest_WithMWSVersion_WithDisableV1_WillTh var testData = await method.FromResource(); var testOptions = TestExtensions.ServerOptions; testOptions.DisableV1 = true; - var authenticator = new MAuthAuthenticator(testOptions, NullLoggerFactory.Instance); + var authenticator = new MAuthAuthenticator(testOptions);//, NullLoggerFactory.Instance); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -309,7 +308,7 @@ public static async Task GetAuthenticationInfo_WithSignedRequest_ForMWSV2Version var testData = await method.FromResourceV2(); var version = MAuthVersion.MWSV2; var testOptions = TestExtensions.ServerOptions; - var authenticator = new MAuthAuthenticator(testOptions, NullLoggerFactory.Instance); + var authenticator = new MAuthAuthenticator(testOptions);//, NullLoggerFactory.Instance); // Act var actual = authenticator.GetAuthenticationInfo(testData.ToHttpRequestMessage(version), version); @@ -331,7 +330,7 @@ public static async Task GetAuthenticationInfo_WithSignedRequest_ForMWSVersion_W var testData = await method.FromResource(); var version = MAuthVersion.MWS; var testOptions = TestExtensions.ServerOptions; - var authenticator = new MAuthAuthenticator(testOptions, NullLoggerFactory.Instance); + var authenticator = new MAuthAuthenticator(testOptions);//, NullLoggerFactory.Instance); // Act var actual = authenticator.GetAuthenticationInfo(testData.ToHttpRequestMessage(version), version); From 9e979f18411c4fb69171ed52b846519b2f77fd8e Mon Sep 17 00:00:00 2001 From: Prajon Date: Thu, 5 Sep 2019 15:10:55 -0400 Subject: [PATCH 33/40] Refactored by adding customLogger class and removed commented codes --- .../MAuthMiddleware.cs | 2 +- src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 74 +++++-------------- .../MAuthAuthenticatorLogger.cs | 64 ++++++++++++++++ .../Infrastructure/MAuthServerHandler.cs | 2 +- .../MAuthAuthenticatorTests.cs | 22 +++--- 5 files changed, 97 insertions(+), 67 deletions(-) create mode 100644 src/Medidata.MAuth.Core/MAuthAuthenticatorLogger.cs diff --git a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs index b370b9e..34b9d01 100644 --- a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs @@ -29,7 +29,7 @@ public MAuthMiddleware(RequestDelegate next, MAuthMiddlewareOptions options, ILo this.next = next; this.options = options; this.loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; - this.authenticator = new MAuthAuthenticator(options); //, loggerFactory ?? NullLoggerFactory.Instance); + this.authenticator = new MAuthAuthenticator(options); } /// diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index e02bfdd..67f4b1c 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -7,6 +7,7 @@ using Medidata.MAuth.Core.Models; using Microsoft.Extensions.Logging; using log4net; +using ILogger = Microsoft.Extensions.Logging.ILogger; namespace Medidata.MAuth.Core { @@ -39,54 +40,11 @@ public MAuthAuthenticator(MAuthOptionsBase options) /// A task object of the boolean value that verifies if the request is authenticated or not. public async Task AuthenticateRequest(HttpRequestMessage request, ILoggerFactory loggerFactory) { - var logger = loggerFactory.CreateLogger(); - try - { - logger.LogInformation("Initiating Authentication of the request"); - var version = request.GetAuthHeaderValue().GetVersionFromAuthenticationHeader(); - - logger.LogInformation("Authentication is for {version}.", version); - - if (options.DisableV1 && version == MAuthVersion.MWS) - throw new InvalidVersionException($"Authentication with {version} version is disabled."); + ILogger logger = loggerFactory.CreateLogger(); - var mAuthCore = MAuthCoreFactory.Instantiate(version); - var authInfo = GetAuthenticationInfo(request, version); - var appInfo = await GetApplicationInfo(authInfo.ApplicationUuid, version); + MAuthAuthenticatorLogger customLogger = new MAuthAuthenticatorLogger(logger); - return mAuthCore.Verify(authInfo.Payload, await mAuthCore.GetSignature(request, authInfo), - appInfo.PublicKey); - } - catch (ArgumentException ex) - { - logger.LogError(ex, "Unable to authenticate due to invalid MAuth authentication headers."); - throw new AuthenticationException("The request has invalid MAuth authentication headers.", ex); - } - catch (RetriedRequestException ex) - { - logger.LogError(ex, "Unable to query the application information from MAuth server."); - throw new AuthenticationException( - "Could not query the application information for the application from the MAuth server.", ex); - } - catch (InvalidCipherTextException ex) - { - logger.LogWarning(ex, "Unable to authenticate due to invalid payload information."); - throw new AuthenticationException( - "The request verification failed due to an invalid payload information.", ex); - } - catch (InvalidVersionException ex) - { - logger.LogError(ex, "Unable to authenticate due to invalid version."); - throw new InvalidVersionException(ex.Message, ex); - } - catch (Exception ex) - { - logger.LogError(ex, "Unable to authenticate due to unexpected error."); - throw new AuthenticationException( - "An unexpected error occured during authentication. Please see the inner exception for details.", - ex - ); - } + return await AuthenticateRequestCore(request, customLogger); } /// /// Verifies if the request is authenticated or not. @@ -95,13 +53,21 @@ public async Task AuthenticateRequest(HttpRequestMessage request, ILoggerF /// A task object of the boolean value that verifies if the request is authenticated or not. public async Task AuthenticateRequest(HttpRequestMessage request) { - var logger = LogManager.GetLogger(typeof(MAuthAuthenticator)); + ILog logger = LogManager.GetLogger(typeof(MAuthAuthenticator)); + + MAuthAuthenticatorLogger customLogger = new MAuthAuthenticatorLogger(logger); + + return await AuthenticateRequestCore(request, customLogger); + } + + private async Task AuthenticateRequestCore(HttpRequestMessage request, MAuthAuthenticatorLogger logger) + { try { - logger.Info("Initiating Authentication of the request"); + logger.LogInformation("Initiating Authentication of the request"); var version = request.GetAuthHeaderValue().GetVersionFromAuthenticationHeader(); - logger.InfoFormat("Authentication is for the {0}.", version); + logger.LogInformation($"Authentication is for the {version}."); if (options.DisableV1 && version == MAuthVersion.MWS) throw new InvalidVersionException($"Authentication with {version} version is disabled."); @@ -115,29 +81,29 @@ public async Task AuthenticateRequest(HttpRequestMessage request) } catch (ArgumentException ex) { - logger.Error("Unable to authenticate due to invalid MAuth authentication headers.", ex); + logger.LogError(ex, "Unable to authenticate due to invalid MAuth authentication headers."); throw new AuthenticationException("The request has invalid MAuth authentication headers.", ex); } catch (RetriedRequestException ex) { - logger.Error("Unable to query the application information from MAuth server.", ex); + logger.LogError(ex, "Unable to query the application information from MAuth server."); throw new AuthenticationException( "Could not query the application information for the application from the MAuth server.", ex); } catch (InvalidCipherTextException ex) { - logger.Warn("Unable to authenticate due to invalid payload information.", ex); + logger.LogWarning(ex, "Unable to authenticate due to invalid payload information."); throw new AuthenticationException( "The request verification failed due to an invalid payload information.", ex); } catch (InvalidVersionException ex) { - logger.Error("Unable to authenticate due to invalid version.", ex); + logger.LogError(ex, "Unable to authenticate due to invalid version."); throw new InvalidVersionException(ex.Message, ex); } catch (Exception ex) { - logger.Error("Unable to authenticate due to unexpected error.", ex); + logger.LogError(ex, "Unable to authenticate due to unexpected error."); throw new AuthenticationException( "An unexpected error occured during authentication. Please see the inner exception for details.", ex diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticatorLogger.cs b/src/Medidata.MAuth.Core/MAuthAuthenticatorLogger.cs new file mode 100644 index 0000000..6122ffb --- /dev/null +++ b/src/Medidata.MAuth.Core/MAuthAuthenticatorLogger.cs @@ -0,0 +1,64 @@ +using Microsoft.Extensions.Logging; +using System; +using log4net; +using ILogger = Microsoft.Extensions.Logging.ILogger; + +namespace Medidata.MAuth.Core +{ + /// + /// MAuthAuthenticatorLogger Class which exposes logging methods used for MAuthenticator + /// + internal class MAuthAuthenticatorLogger + { + private readonly log4net.ILog _log; + + private readonly ILogger _logger; + public MAuthAuthenticatorLogger(ILog logger) + { + _log = logger; + } + + public MAuthAuthenticatorLogger(ILogger logger) + { + _logger = logger; + } + + /* + * Extremely boring code ahead + */ + + #region This code is extremely dull + + public void LogWarning(Exception exception, string message) + { + if (_log is null) + { + _logger.LogWarning(exception, message); + } + else + { _log.Warn(message, exception);} + } + + public void LogInformation(string message) + { + if (_log is null) + { + _logger.LogInformation(message); + } + else + { _log.Info(message);} + } + + public void LogError(Exception exception, string message) + { + if (_log is null) + { + _logger.LogError(exception, message); + } + else + { _log.Error(message, exception);} + } + #endregion + + } +} diff --git a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs index e7eb451..12c5055 100644 --- a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs +++ b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs @@ -26,7 +26,7 @@ protected override async Task SendAsync( if (currentNumberOfAttempts < SucceedAfterThisManyAttempts) return new HttpResponseMessage(HttpStatusCode.ServiceUnavailable); - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions);//, NullLoggerFactory.Instance); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); var authInfo = authenticator.GetAuthenticationInfo(request, version); diff --git a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs index 49ea6eb..442924e 100644 --- a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs @@ -22,14 +22,14 @@ public static void MAuthAuthenticator_WithInvalidOptions_WillThrowException( ApplicationUuid = TestExtensions.ClientUuid, MAuthServiceUrl = mauthServiceUrl != null ? new Uri(mauthServiceUrl) : null, PrivateKey = privateKey - }));//, NullLoggerFactory.Instance)); + })); [Fact] public static void MAuthAuthenticator_WithDefaultUuid_WillThrowException() => Assert.Throws(() => new MAuthAuthenticator(new MAuthTestOptions() { ApplicationUuid = default(Guid) - }));//, NullLoggerFactory.Instance)); + })); [Theory] [InlineData("GET")] @@ -41,7 +41,7 @@ public static async Task AuthenticateRequest_WithValidRequest_WillAuthenticate(s // Arrange var testData = await method.FromResource(); - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions);//, NullLoggerFactory.Instance); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -69,7 +69,7 @@ public static async Task AuthenticateRequest_WithValidMWSV2Request_WillAuthentic // Arrange var testData = await method.FromResourceV2(); var version = MAuthVersion.MWSV2; - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions);//, NullLoggerFactory.Instance); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); var mAuthCore = new MAuthCoreV2(); var signedRequest = await mAuthCore @@ -100,7 +100,7 @@ public static async Task AuthenticateRequest_WithNumberOfAttempts_WillAuthentica var testData = await "GET".FromResource(); var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: true));//, NullLoggerFactory.Instance); + policy, shouldSucceedWithin: true)); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -130,7 +130,7 @@ public static async Task AuthenticateRequest_WithMWSV2Request_WithNumberOfAttemp var testData = await "GET".FromResourceV2(); var version = MAuthVersion.MWSV2; var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: true));//, NullLoggerFactory.Instance); + policy, shouldSucceedWithin: true)); var mAuthCore = new MAuthCoreV2(); var signedRequest = await mAuthCore @@ -160,7 +160,7 @@ public static async Task AuthenticateRequest_AfterNumberOfAttempts_WillThrowExce var testData = await "GET".FromResource(); var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: false));//, NullLoggerFactory.Instance); + policy, shouldSucceedWithin: false)); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -195,7 +195,7 @@ public static async Task AuthenticateRequest_WithMWSV2Request_AfterNumberOfAttem var testData = await "GET".FromResource(); var version = MAuthVersion.MWSV2; var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: false));//, NullLoggerFactory.Instance); + policy, shouldSucceedWithin: false)); var mAuthCore = new MAuthCoreV2(); var signedRequest = await mAuthCore @@ -277,7 +277,7 @@ public static async Task AuthenticateRequest_WithMWSVersion_WithDisableV1_WillTh var testData = await method.FromResource(); var testOptions = TestExtensions.ServerOptions; testOptions.DisableV1 = true; - var authenticator = new MAuthAuthenticator(testOptions);//, NullLoggerFactory.Instance); + var authenticator = new MAuthAuthenticator(testOptions); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -308,7 +308,7 @@ public static async Task GetAuthenticationInfo_WithSignedRequest_ForMWSV2Version var testData = await method.FromResourceV2(); var version = MAuthVersion.MWSV2; var testOptions = TestExtensions.ServerOptions; - var authenticator = new MAuthAuthenticator(testOptions);//, NullLoggerFactory.Instance); + var authenticator = new MAuthAuthenticator(testOptions); // Act var actual = authenticator.GetAuthenticationInfo(testData.ToHttpRequestMessage(version), version); @@ -330,7 +330,7 @@ public static async Task GetAuthenticationInfo_WithSignedRequest_ForMWSVersion_W var testData = await method.FromResource(); var version = MAuthVersion.MWS; var testOptions = TestExtensions.ServerOptions; - var authenticator = new MAuthAuthenticator(testOptions);//, NullLoggerFactory.Instance); + var authenticator = new MAuthAuthenticator(testOptions); // Act var actual = authenticator.GetAuthenticationInfo(testData.ToHttpRequestMessage(version), version); From 4f6a74879c72e8c55b8ab86c9289f83485e53258 Mon Sep 17 00:00:00 2001 From: Prajon Date: Fri, 6 Sep 2019 09:51:13 -0400 Subject: [PATCH 34/40] Refactored to use custom log4netlogger class implementing ILogger interface and removed other custom logger class --- src/Medidata.MAuth.Core/Log4NetLogger.cs | 88 +++++++++++++++++++ src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 22 +++-- .../MAuthAuthenticatorLogger.cs | 64 -------------- 3 files changed, 101 insertions(+), 73 deletions(-) create mode 100644 src/Medidata.MAuth.Core/Log4NetLogger.cs delete mode 100644 src/Medidata.MAuth.Core/MAuthAuthenticatorLogger.cs diff --git a/src/Medidata.MAuth.Core/Log4NetLogger.cs b/src/Medidata.MAuth.Core/Log4NetLogger.cs new file mode 100644 index 0000000..86bb980 --- /dev/null +++ b/src/Medidata.MAuth.Core/Log4NetLogger.cs @@ -0,0 +1,88 @@ +using System; +using log4net; +using Microsoft.Extensions.Logging; + +namespace Medidata.MAuth.Core +{ + /// + /// Log4NetLogger Class which implements ILogger interface for logging. + /// + internal class Log4NetLogger : ILogger + { + private readonly ILog _log; + + public Log4NetLogger(ILog log) + { + _log = log; + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + if (!IsEnabled(logLevel)) + { + return; + } + + if (formatter == null) + { + throw new ArgumentNullException(nameof(formatter)); + } + string message = null; + if (null != formatter) + { + message = formatter(state, exception); + } + if (!string.IsNullOrEmpty(message) || exception != null) + { + switch (logLevel) + { + case LogLevel.Critical: + _log.Fatal(message); + break; + case LogLevel.Debug: + case LogLevel.Trace: + _log.Debug(message); + break; + case LogLevel.Error: + _log.Error(message, exception); + break; + case LogLevel.Information: + _log.Info(message); + break; + case LogLevel.Warning: + _log.Warn(message, exception); + break; + default: + _log.Warn($"Encountered unknown log level {logLevel}, writing out as Info."); + _log.Info(message, exception); + break; + } + } + } + + public bool IsEnabled(LogLevel logLevel) + { + switch (logLevel) + { + case LogLevel.Critical: + return _log.IsFatalEnabled; + case LogLevel.Debug: + case LogLevel.Trace: + return _log.IsDebugEnabled; + case LogLevel.Error: + return _log.IsErrorEnabled; + case LogLevel.Information: + return _log.IsInfoEnabled; + case LogLevel.Warning: + return _log.IsWarnEnabled; + default: + throw new ArgumentOutOfRangeException(nameof(logLevel)); + } + } + + public IDisposable BeginScope(TState state) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index 67f4b1c..c45391d 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -7,6 +7,7 @@ using Medidata.MAuth.Core.Models; using Microsoft.Extensions.Logging; using log4net; +using Microsoft.Extensions.Logging.Abstractions; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace Medidata.MAuth.Core @@ -42,10 +43,9 @@ public async Task AuthenticateRequest(HttpRequestMessage request, ILoggerF { ILogger logger = loggerFactory.CreateLogger(); - MAuthAuthenticatorLogger customLogger = new MAuthAuthenticatorLogger(logger); - - return await AuthenticateRequestCore(request, customLogger); + return await AuthenticateRequestCore(request, logger); } + /// /// Verifies if the request is authenticated or not. /// @@ -53,21 +53,25 @@ public async Task AuthenticateRequest(HttpRequestMessage request, ILoggerF /// A task object of the boolean value that verifies if the request is authenticated or not. public async Task AuthenticateRequest(HttpRequestMessage request) { - ILog logger = LogManager.GetLogger(typeof(MAuthAuthenticator)); + ILog log = LogManager.GetLogger(typeof(MAuthAuthenticator)); - MAuthAuthenticatorLogger customLogger = new MAuthAuthenticatorLogger(logger); + ILogger logger; + if(log != null) + logger = new Log4NetLogger(log); + else + logger = NullLoggerFactory.Instance.CreateLogger(); - return await AuthenticateRequestCore(request, customLogger); + return await AuthenticateRequestCore(request, logger); } - private async Task AuthenticateRequestCore(HttpRequestMessage request, MAuthAuthenticatorLogger logger) + private async Task AuthenticateRequestCore(HttpRequestMessage request, ILogger logger) { try { - logger.LogInformation("Initiating Authentication of the request"); + logger.LogInformation("Initiating Authentication of the request."); var version = request.GetAuthHeaderValue().GetVersionFromAuthenticationHeader(); - logger.LogInformation($"Authentication is for the {version}."); + logger.LogInformation("Authentication is for the {version}.",version); if (options.DisableV1 && version == MAuthVersion.MWS) throw new InvalidVersionException($"Authentication with {version} version is disabled."); diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticatorLogger.cs b/src/Medidata.MAuth.Core/MAuthAuthenticatorLogger.cs deleted file mode 100644 index 6122ffb..0000000 --- a/src/Medidata.MAuth.Core/MAuthAuthenticatorLogger.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Microsoft.Extensions.Logging; -using System; -using log4net; -using ILogger = Microsoft.Extensions.Logging.ILogger; - -namespace Medidata.MAuth.Core -{ - /// - /// MAuthAuthenticatorLogger Class which exposes logging methods used for MAuthenticator - /// - internal class MAuthAuthenticatorLogger - { - private readonly log4net.ILog _log; - - private readonly ILogger _logger; - public MAuthAuthenticatorLogger(ILog logger) - { - _log = logger; - } - - public MAuthAuthenticatorLogger(ILogger logger) - { - _logger = logger; - } - - /* - * Extremely boring code ahead - */ - - #region This code is extremely dull - - public void LogWarning(Exception exception, string message) - { - if (_log is null) - { - _logger.LogWarning(exception, message); - } - else - { _log.Warn(message, exception);} - } - - public void LogInformation(string message) - { - if (_log is null) - { - _logger.LogInformation(message); - } - else - { _log.Info(message);} - } - - public void LogError(Exception exception, string message) - { - if (_log is null) - { - _logger.LogError(exception, message); - } - else - { _log.Error(message, exception);} - } - #endregion - - } -} From 73476545da4e05104c164059cf9f21777a7e022f Mon Sep 17 00:00:00 2001 From: Prajon Date: Tue, 10 Sep 2019 15:20:53 -0400 Subject: [PATCH 35/40] Removed log4Net dependency and do logging using microsoft.extensions.logging and microsoft.owin.logging --- README.md | 13 +++ .../MAuthAspNetCoreExtensions.cs | 4 +- .../MAuthMiddleware.cs | 8 +- src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 56 ++++--------- .../Medidata.MAuth.Core.csproj | 1 - src/Medidata.MAuth.Core/UtilityExtensions.cs | 7 +- .../MAuthAppBuilderExtensions.cs | 2 +- src/Medidata.MAuth.Owin/MAuthMiddleware.cs | 11 ++- .../Medidata.MAuth.Owin.csproj | 4 - .../OwinLoggerWrapper.cs} | 80 +++++++++---------- .../MAuthAuthenticatingHandler.cs | 10 ++- .../MAuthWebApiOptions.cs | 6 ++ .../Infrastructure/MAuthServerHandler.cs | 2 +- .../MAuthAuthenticatorTests.cs | 23 +++--- .../UtilityExtensionsTest.cs | 2 +- 15 files changed, 114 insertions(+), 115 deletions(-) rename src/{Medidata.MAuth.Core/Log4NetLogger.cs => Medidata.MAuth.Owin/OwinLoggerWrapper.cs} (62%) diff --git a/README.md b/README.md index 316c1fa..973d0e3 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,16 @@ public class Startup } } ``` +For enabling the logging from Owin application, the follofing configuration will need to be added or updated: +```C# + + + + + + + +``` A similar way can be implemented for ASP.NET Core (also in the `Startup` class): @@ -262,6 +272,9 @@ public static class WebApiConfig // when ready to disable authentication of V1 protococl options.DisableV1 = true + + // if loggerfactory is present + options.loggerfactory = new Microsoft.Extensions.Logging.LoggerFactory(); // or provide the existing loggerfactory }; config.MessageHandlers.Add(new MAuthAuthenticatingHandler(options)); diff --git a/src/Medidata.MAuth.AspNetCore/MAuthAspNetCoreExtensions.cs b/src/Medidata.MAuth.AspNetCore/MAuthAspNetCoreExtensions.cs index 78d9c38..6d38b1d 100644 --- a/src/Medidata.MAuth.AspNetCore/MAuthAspNetCoreExtensions.cs +++ b/src/Medidata.MAuth.AspNetCore/MAuthAspNetCoreExtensions.cs @@ -46,11 +46,11 @@ public static HttpRequestMessage ToHttpRequestMessage(this HttpRequest request) /// will throw an exception if any errors occurred during the authentication. /// public static async Task TryAuthenticate( - this HttpContext context, MAuthAuthenticator authenticator, bool shouldIgnoreExceptions, ILoggerFactory loggerFactory) + this HttpContext context, MAuthAuthenticator authenticator, bool shouldIgnoreExceptions) { try { - return await authenticator.AuthenticateRequest(context.Request.ToHttpRequestMessage(), loggerFactory); + return await authenticator.AuthenticateRequest(context.Request.ToHttpRequestMessage()); } catch (Exception) { diff --git a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs index 34b9d01..d18c7b8 100644 --- a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs @@ -16,7 +16,6 @@ internal class MAuthMiddleware private readonly MAuthMiddlewareOptions options; private readonly MAuthAuthenticator authenticator; private readonly RequestDelegate next; - private readonly ILoggerFactory loggerFactory; /// /// Creates a new @@ -28,8 +27,9 @@ public MAuthMiddleware(RequestDelegate next, MAuthMiddlewareOptions options, ILo { this.next = next; this.options = options; - this.loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; - this.authenticator = new MAuthAuthenticator(options); + loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; + ILogger logger = loggerFactory.CreateLogger(); + this.authenticator = new MAuthAuthenticator(options, logger); } /// @@ -42,7 +42,7 @@ public async Task Invoke(HttpContext context) context.Request.EnableRewind(); if (!options.Bypass(context.Request) && - !await context.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized, loggerFactory)) + !await context.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized)) { context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; return; diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index c45391d..44198a0 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -6,8 +6,6 @@ using Org.BouncyCastle.Crypto; using Medidata.MAuth.Core.Models; using Microsoft.Extensions.Logging; -using log4net; -using Microsoft.Extensions.Logging.Abstractions; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace Medidata.MAuth.Core @@ -16,10 +14,11 @@ internal class MAuthAuthenticator { private readonly MAuthOptionsBase options; private readonly IMemoryCache cache = new MemoryCache(new MemoryCacheOptions()); + private readonly ILogger _logger; public Guid ApplicationUuid => options.ApplicationUuid; - public MAuthAuthenticator(MAuthOptionsBase options) + public MAuthAuthenticator(MAuthOptionsBase options, ILogger logger) { if (options.ApplicationUuid == default(Guid)) throw new ArgumentException(nameof(options.ApplicationUuid)); @@ -31,47 +30,22 @@ public MAuthAuthenticator(MAuthOptionsBase options) throw new ArgumentNullException(nameof(options.PrivateKey)); this.options = options; + this._logger = logger; } - /// - /// Verifies if the request is authenticated or not. - /// - /// The request. - /// The used with authentication. - /// A task object of the boolean value that verifies if the request is authenticated or not. - public async Task AuthenticateRequest(HttpRequestMessage request, ILoggerFactory loggerFactory) - { - ILogger logger = loggerFactory.CreateLogger(); - - return await AuthenticateRequestCore(request, logger); - } - - /// - /// Verifies if the request is authenticated or not. - /// - /// The request. - /// A task object of the boolean value that verifies if the request is authenticated or not. + ///// + ///// Verifies if the request is authenticated or not. + ///// + ///// The request. + ///// A task object of the boolean value that verifies if the request is authenticated or not. public async Task AuthenticateRequest(HttpRequestMessage request) - { - ILog log = LogManager.GetLogger(typeof(MAuthAuthenticator)); - - ILogger logger; - if(log != null) - logger = new Log4NetLogger(log); - else - logger = NullLoggerFactory.Instance.CreateLogger(); - - return await AuthenticateRequestCore(request, logger); - } - - private async Task AuthenticateRequestCore(HttpRequestMessage request, ILogger logger) { try { - logger.LogInformation("Initiating Authentication of the request."); + _logger.LogInformation("Initiating Authentication of the request."); var version = request.GetAuthHeaderValue().GetVersionFromAuthenticationHeader(); - logger.LogInformation("Authentication is for the {version}.",version); + _logger.LogInformation("Authentication is for the {version}.",version); if (options.DisableV1 && version == MAuthVersion.MWS) throw new InvalidVersionException($"Authentication with {version} version is disabled."); @@ -85,29 +59,29 @@ private async Task AuthenticateRequestCore(HttpRequestMessage request, ILo } catch (ArgumentException ex) { - logger.LogError(ex, "Unable to authenticate due to invalid MAuth authentication headers."); + _logger.LogError(ex, "Unable to authenticate due to invalid MAuth authentication headers."); throw new AuthenticationException("The request has invalid MAuth authentication headers.", ex); } catch (RetriedRequestException ex) { - logger.LogError(ex, "Unable to query the application information from MAuth server."); + _logger.LogError(ex, "Unable to query the application information from MAuth server."); throw new AuthenticationException( "Could not query the application information for the application from the MAuth server.", ex); } catch (InvalidCipherTextException ex) { - logger.LogWarning(ex, "Unable to authenticate due to invalid payload information."); + _logger.LogWarning(ex, "Unable to authenticate due to invalid payload information."); throw new AuthenticationException( "The request verification failed due to an invalid payload information.", ex); } catch (InvalidVersionException ex) { - logger.LogError(ex, "Unable to authenticate due to invalid version."); + _logger.LogError(ex, "Unable to authenticate due to invalid version."); throw new InvalidVersionException(ex.Message, ex); } catch (Exception ex) { - logger.LogError(ex, "Unable to authenticate due to unexpected error."); + _logger.LogError(ex, "Unable to authenticate due to unexpected error."); throw new AuthenticationException( "An unexpected error occured during authentication. Please see the inner exception for details.", ex diff --git a/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj b/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj index 23aaadf..1c15fe7 100644 --- a/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj +++ b/src/Medidata.MAuth.Core/Medidata.MAuth.Core.csproj @@ -10,7 +10,6 @@ - diff --git a/src/Medidata.MAuth.Core/UtilityExtensions.cs b/src/Medidata.MAuth.Core/UtilityExtensions.cs index 6c826ec..3ae504e 100644 --- a/src/Medidata.MAuth.Core/UtilityExtensions.cs +++ b/src/Medidata.MAuth.Core/UtilityExtensions.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Medidata.MAuth.Core.Models; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Medidata.MAuth.Core { @@ -66,11 +67,11 @@ public static bool TryParseAuthenticationHeader(this string headerValue, /// /// The request message to authenticate. /// The MAuth options to use for the authentication. - /// The logger factory used with authentication. + /// The logger interface used for logging. /// The task for the operation that is when completes will result in if /// the authentication is successful; otherwise . - public static Task Authenticate(this HttpRequestMessage request, MAuthOptionsBase options, ILoggerFactory loggerFactory) => - new MAuthAuthenticator(options).AuthenticateRequest(request, loggerFactory); + public static Task Authenticate(this HttpRequestMessage request, MAuthOptionsBase options, ILogger logger) => + new MAuthAuthenticator(options, logger).AuthenticateRequest(request); /// /// Determines the MAuth version enumerator reading authHeader. diff --git a/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs b/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs index 5a9cd17..ab23090 100644 --- a/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs +++ b/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs @@ -25,7 +25,7 @@ public static IAppBuilder UseMAuthAuthentication(this IAppBuilder app, MAuthMidd if (options == null) throw new ArgumentNullException(nameof(options)); - return app.Use(options); + return app.Use(options, app); } /// diff --git a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs index 9dc975b..bbd43ef 100644 --- a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs @@ -1,7 +1,10 @@ using System.Net; using System.Threading.Tasks; using Medidata.MAuth.Core; +using Microsoft.Extensions.Logging; using Microsoft.Owin; +using Microsoft.Owin.Logging; +using Owin; namespace Medidata.MAuth.Owin { @@ -9,15 +12,19 @@ internal class MAuthMiddleware: OwinMiddleware { private readonly MAuthMiddlewareOptions options; private readonly MAuthAuthenticator authenticator; + private Microsoft.Extensions.Logging.ILogger _logger; - public MAuthMiddleware(OwinMiddleware next, MAuthMiddlewareOptions options) : base(next) + public MAuthMiddleware(OwinMiddleware next, MAuthMiddlewareOptions options, IAppBuilder app) : base(next) { this.options = options; - authenticator = new MAuthAuthenticator(options); + var owinLogger = app.CreateLogger(); + this._logger = new OwinLoggerWrapper(owinLogger); + authenticator = new MAuthAuthenticator(options, _logger); } public override async Task Invoke(IOwinContext context) { + _logger.LogInformation("hello"); await context.EnsureRequestBodyStreamSeekable(); if (!options.Bypass(context.Request) && !await context.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized)) diff --git a/src/Medidata.MAuth.Owin/Medidata.MAuth.Owin.csproj b/src/Medidata.MAuth.Owin/Medidata.MAuth.Owin.csproj index cacb895..a99ed86 100644 --- a/src/Medidata.MAuth.Owin/Medidata.MAuth.Owin.csproj +++ b/src/Medidata.MAuth.Owin/Medidata.MAuth.Owin.csproj @@ -9,10 +9,6 @@ medidata;mauth;hmac;authentication;core;owin;middleware;webapi - - - - diff --git a/src/Medidata.MAuth.Core/Log4NetLogger.cs b/src/Medidata.MAuth.Owin/OwinLoggerWrapper.cs similarity index 62% rename from src/Medidata.MAuth.Core/Log4NetLogger.cs rename to src/Medidata.MAuth.Owin/OwinLoggerWrapper.cs index 86bb980..fa84d08 100644 --- a/src/Medidata.MAuth.Core/Log4NetLogger.cs +++ b/src/Medidata.MAuth.Owin/OwinLoggerWrapper.cs @@ -1,19 +1,42 @@ using System; -using log4net; +using System.Diagnostics; using Microsoft.Extensions.Logging; +using Microsoft.Owin.Logging; +using ILogger = Microsoft.Owin.Logging.ILogger; -namespace Medidata.MAuth.Core +namespace Medidata.MAuth.Owin { - /// - /// Log4NetLogger Class which implements ILogger interface for logging. - /// - internal class Log4NetLogger : ILogger + internal class OwinLoggerWrapper : Microsoft.Extensions.Logging.ILogger { - private readonly ILog _log; + private readonly ILogger _owinLogger; - public Log4NetLogger(ILog log) + public OwinLoggerWrapper(ILogger owinLogger) { - _log = log; + _owinLogger = owinLogger; + } + + public IDisposable BeginScope(TState state) + { + throw new NotImplementedException(); + } + + public bool IsEnabled(LogLevel logLevel) + { + switch (logLevel) + { + case LogLevel.Critical: + return _owinLogger.IsEnabled(TraceEventType.Critical); + case LogLevel.Debug: + case LogLevel.Trace: + case LogLevel.Information: + return _owinLogger.IsEnabled(TraceEventType.Information); + case LogLevel.Error: + return _owinLogger.IsEnabled(TraceEventType.Error); + case LogLevel.Warning: + return _owinLogger.IsEnabled(TraceEventType.Warning); + default: + throw new ArgumentOutOfRangeException(nameof(logLevel)); + } } public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) @@ -37,52 +60,25 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except switch (logLevel) { case LogLevel.Critical: - _log.Fatal(message); + _owinLogger.WriteCritical(message, exception); break; case LogLevel.Debug: case LogLevel.Trace: - _log.Debug(message); - break; case LogLevel.Error: - _log.Error(message, exception); + _owinLogger.WriteError(message, exception); break; case LogLevel.Information: - _log.Info(message); + _owinLogger.WriteInformation(message); break; case LogLevel.Warning: - _log.Warn(message, exception); + _owinLogger.WriteWarning(message, exception); break; default: - _log.Warn($"Encountered unknown log level {logLevel}, writing out as Info."); - _log.Info(message, exception); + _owinLogger.WriteWarning($"Encountered unknown log level {logLevel}, writing out as Info."); + _owinLogger.WriteInformation(message); break; } } } - - public bool IsEnabled(LogLevel logLevel) - { - switch (logLevel) - { - case LogLevel.Critical: - return _log.IsFatalEnabled; - case LogLevel.Debug: - case LogLevel.Trace: - return _log.IsDebugEnabled; - case LogLevel.Error: - return _log.IsErrorEnabled; - case LogLevel.Information: - return _log.IsInfoEnabled; - case LogLevel.Warning: - return _log.IsWarnEnabled; - default: - throw new ArgumentOutOfRangeException(nameof(logLevel)); - } - } - - public IDisposable BeginScope(TState state) - { - throw new NotImplementedException(); - } } } diff --git a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs index daf5cb6..ab10e9a 100644 --- a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs +++ b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs @@ -4,6 +4,8 @@ using System.Threading; using System.Threading.Tasks; using Medidata.MAuth.Core; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Medidata.MAuth.WebApi { @@ -27,7 +29,9 @@ public class MAuthAuthenticatingHandler : DelegatingHandler public MAuthAuthenticatingHandler(MAuthWebApiOptions options) { this.options = options; - authenticator = new MAuthAuthenticator(options); + var loggerFactory = options.LoggerFactory ?? NullLoggerFactory.Instance; + var logger = loggerFactory.CreateLogger(typeof(MAuthAuthenticator)); + authenticator = new MAuthAuthenticator(options, logger); } /// @@ -41,7 +45,9 @@ public MAuthAuthenticatingHandler(MAuthWebApiOptions options) public MAuthAuthenticatingHandler(MAuthWebApiOptions options, HttpMessageHandler innerHandler) : base(innerHandler) { this.options = options; - authenticator = new MAuthAuthenticator(options); + var loggerFactory = options.LoggerFactory ?? NullLoggerFactory.Instance; + var logger = loggerFactory.CreateLogger(typeof(MAuthAuthenticator)); + authenticator = new MAuthAuthenticator(options, logger); } /// diff --git a/src/Medidata.MAuth.WebApi/MAuthWebApiOptions.cs b/src/Medidata.MAuth.WebApi/MAuthWebApiOptions.cs index 715b3d8..9a3239d 100644 --- a/src/Medidata.MAuth.WebApi/MAuthWebApiOptions.cs +++ b/src/Medidata.MAuth.WebApi/MAuthWebApiOptions.cs @@ -1,4 +1,5 @@ using Medidata.MAuth.Core; +using Microsoft.Extensions.Logging; namespace Medidata.MAuth.WebApi { @@ -13,5 +14,10 @@ public class MAuthWebApiOptions: MAuthOptionsBase /// The default is . /// public bool HideExceptionsAndReturnUnauthorized { get; set; } = true; + + /// + /// + /// + public ILoggerFactory LoggerFactory { get; set; } } } diff --git a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs index 12c5055..c5331c5 100644 --- a/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs +++ b/tests/Medidata.MAuth.Tests/Infrastructure/MAuthServerHandler.cs @@ -26,7 +26,7 @@ protected override async Task SendAsync( if (currentNumberOfAttempts < SucceedAfterThisManyAttempts) return new HttpResponseMessage(HttpStatusCode.ServiceUnavailable); - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions, NullLogger.Instance); var authInfo = authenticator.GetAuthenticationInfo(request, version); diff --git a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs index 442924e..753c9b3 100644 --- a/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs +++ b/tests/Medidata.MAuth.Tests/MAuthAuthenticatorTests.cs @@ -6,6 +6,7 @@ using Medidata.MAuth.Core.Exceptions; using Medidata.MAuth.Core.Models; using Medidata.MAuth.Tests.Infrastructure; +using Microsoft.Extensions.Logging.Abstractions; using Xunit; namespace Medidata.MAuth.Tests @@ -22,14 +23,14 @@ public static void MAuthAuthenticator_WithInvalidOptions_WillThrowException( ApplicationUuid = TestExtensions.ClientUuid, MAuthServiceUrl = mauthServiceUrl != null ? new Uri(mauthServiceUrl) : null, PrivateKey = privateKey - })); + }, NullLogger.Instance)); [Fact] public static void MAuthAuthenticator_WithDefaultUuid_WillThrowException() => Assert.Throws(() => new MAuthAuthenticator(new MAuthTestOptions() { ApplicationUuid = default(Guid) - })); + }, NullLogger.Instance)); [Theory] [InlineData("GET")] @@ -41,7 +42,7 @@ public static async Task AuthenticateRequest_WithValidRequest_WillAuthenticate(s // Arrange var testData = await method.FromResource(); - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions, NullLogger.Instance); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -69,7 +70,7 @@ public static async Task AuthenticateRequest_WithValidMWSV2Request_WillAuthentic // Arrange var testData = await method.FromResourceV2(); var version = MAuthVersion.MWSV2; - var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions); + var authenticator = new MAuthAuthenticator(TestExtensions.ServerOptions, NullLogger.Instance); var mAuthCore = new MAuthCoreV2(); var signedRequest = await mAuthCore @@ -100,7 +101,7 @@ public static async Task AuthenticateRequest_WithNumberOfAttempts_WillAuthentica var testData = await "GET".FromResource(); var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: true)); + policy, shouldSucceedWithin: true), NullLogger.Instance); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -130,7 +131,7 @@ public static async Task AuthenticateRequest_WithMWSV2Request_WithNumberOfAttemp var testData = await "GET".FromResourceV2(); var version = MAuthVersion.MWSV2; var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: true)); + policy, shouldSucceedWithin: true), NullLogger.Instance); var mAuthCore = new MAuthCoreV2(); var signedRequest = await mAuthCore @@ -160,7 +161,7 @@ public static async Task AuthenticateRequest_AfterNumberOfAttempts_WillThrowExce var testData = await "GET".FromResource(); var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: false)); + policy, shouldSucceedWithin: false), NullLogger.Instance); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -195,7 +196,7 @@ public static async Task AuthenticateRequest_WithMWSV2Request_AfterNumberOfAttem var testData = await "GET".FromResource(); var version = MAuthVersion.MWSV2; var authenticator = new MAuthAuthenticator(TestExtensions.GetServerOptionsWithAttempts( - policy, shouldSucceedWithin: false)); + policy, shouldSucceedWithin: false), NullLogger.Instance); var mAuthCore = new MAuthCoreV2(); var signedRequest = await mAuthCore @@ -277,7 +278,7 @@ public static async Task AuthenticateRequest_WithMWSVersion_WithDisableV1_WillTh var testData = await method.FromResource(); var testOptions = TestExtensions.ServerOptions; testOptions.DisableV1 = true; - var authenticator = new MAuthAuthenticator(testOptions); + var authenticator = new MAuthAuthenticator(testOptions, NullLogger.Instance); var mAuthCore = new MAuthCore(); var signedRequest = await mAuthCore @@ -308,7 +309,7 @@ public static async Task GetAuthenticationInfo_WithSignedRequest_ForMWSV2Version var testData = await method.FromResourceV2(); var version = MAuthVersion.MWSV2; var testOptions = TestExtensions.ServerOptions; - var authenticator = new MAuthAuthenticator(testOptions); + var authenticator = new MAuthAuthenticator(testOptions, NullLogger.Instance); // Act var actual = authenticator.GetAuthenticationInfo(testData.ToHttpRequestMessage(version), version); @@ -330,7 +331,7 @@ public static async Task GetAuthenticationInfo_WithSignedRequest_ForMWSVersion_W var testData = await method.FromResource(); var version = MAuthVersion.MWS; var testOptions = TestExtensions.ServerOptions; - var authenticator = new MAuthAuthenticator(testOptions); + var authenticator = new MAuthAuthenticator(testOptions, NullLogger.Instance); // Act var actual = authenticator.GetAuthenticationInfo(testData.ToHttpRequestMessage(version), version); diff --git a/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs b/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs index d07c67b..e69a2fa 100644 --- a/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs +++ b/tests/Medidata.MAuth.Tests/UtilityExtensionsTest.cs @@ -68,7 +68,7 @@ public static async Task Authenticate_WithValidRequest_WillAuthenticate(string m }); // Act - var isAuthenticated = await signedRequest.Authenticate(TestExtensions.ServerOptions, NullLoggerFactory.Instance); + var isAuthenticated = await signedRequest.Authenticate(TestExtensions.ServerOptions, NullLogger.Instance); // Assert Assert.True(isAuthenticated); From 76e09706b8c1732196ec1c5d22fe6ba965241cad Mon Sep 17 00:00:00 2001 From: Prajon Date: Wed, 11 Sep 2019 09:02:49 -0400 Subject: [PATCH 36/40] Refactored as per feedback --- src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 18 +++++++++--------- src/Medidata.MAuth.Owin/MAuthMiddleware.cs | 6 ++---- .../MAuthAuthenticatingHandler.cs | 17 ++++++++++------- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index 44198a0..5c27b85 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -14,7 +14,7 @@ internal class MAuthAuthenticator { private readonly MAuthOptionsBase options; private readonly IMemoryCache cache = new MemoryCache(new MemoryCacheOptions()); - private readonly ILogger _logger; + private readonly ILogger logger; public Guid ApplicationUuid => options.ApplicationUuid; @@ -30,7 +30,7 @@ public MAuthAuthenticator(MAuthOptionsBase options, ILogger logger) throw new ArgumentNullException(nameof(options.PrivateKey)); this.options = options; - this._logger = logger; + this.logger = logger; } ///// @@ -42,10 +42,10 @@ public async Task AuthenticateRequest(HttpRequestMessage request) { try { - _logger.LogInformation("Initiating Authentication of the request."); + logger.LogInformation("Initiating Authentication of the request."); var version = request.GetAuthHeaderValue().GetVersionFromAuthenticationHeader(); - _logger.LogInformation("Authentication is for the {version}.",version); + logger.LogInformation("Authentication is for the {version}.",version); if (options.DisableV1 && version == MAuthVersion.MWS) throw new InvalidVersionException($"Authentication with {version} version is disabled."); @@ -59,29 +59,29 @@ public async Task AuthenticateRequest(HttpRequestMessage request) } catch (ArgumentException ex) { - _logger.LogError(ex, "Unable to authenticate due to invalid MAuth authentication headers."); + logger.LogError(ex, "Unable to authenticate due to invalid MAuth authentication headers."); throw new AuthenticationException("The request has invalid MAuth authentication headers.", ex); } catch (RetriedRequestException ex) { - _logger.LogError(ex, "Unable to query the application information from MAuth server."); + logger.LogError(ex, "Unable to query the application information from MAuth server."); throw new AuthenticationException( "Could not query the application information for the application from the MAuth server.", ex); } catch (InvalidCipherTextException ex) { - _logger.LogWarning(ex, "Unable to authenticate due to invalid payload information."); + logger.LogWarning(ex, "Unable to authenticate due to invalid payload information."); throw new AuthenticationException( "The request verification failed due to an invalid payload information.", ex); } catch (InvalidVersionException ex) { - _logger.LogError(ex, "Unable to authenticate due to invalid version."); + logger.LogError(ex, "Unable to authenticate due to invalid version."); throw new InvalidVersionException(ex.Message, ex); } catch (Exception ex) { - _logger.LogError(ex, "Unable to authenticate due to unexpected error."); + logger.LogError(ex, "Unable to authenticate due to unexpected error."); throw new AuthenticationException( "An unexpected error occured during authentication. Please see the inner exception for details.", ex diff --git a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs index bbd43ef..60ef9f9 100644 --- a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs @@ -12,19 +12,17 @@ internal class MAuthMiddleware: OwinMiddleware { private readonly MAuthMiddlewareOptions options; private readonly MAuthAuthenticator authenticator; - private Microsoft.Extensions.Logging.ILogger _logger; public MAuthMiddleware(OwinMiddleware next, MAuthMiddlewareOptions options, IAppBuilder app) : base(next) { this.options = options; var owinLogger = app.CreateLogger(); - this._logger = new OwinLoggerWrapper(owinLogger); - authenticator = new MAuthAuthenticator(options, _logger); + Microsoft.Extensions.Logging.ILogger logger = new OwinLoggerWrapper(owinLogger); + authenticator = new MAuthAuthenticator(options, logger); } public override async Task Invoke(IOwinContext context) { - _logger.LogInformation("hello"); await context.EnsureRequestBodyStreamSeekable(); if (!options.Bypass(context.Request) && !await context.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized)) diff --git a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs index ab10e9a..48c2134 100644 --- a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs +++ b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs @@ -16,7 +16,7 @@ namespace Medidata.MAuth.WebApi public class MAuthAuthenticatingHandler : DelegatingHandler { private readonly MAuthWebApiOptions options; - private readonly MAuthAuthenticator authenticator; + private MAuthAuthenticator authenticator; /// Gets the Uuid of the client application. public Guid ClientAppUuid => authenticator.ApplicationUuid; @@ -29,9 +29,7 @@ public class MAuthAuthenticatingHandler : DelegatingHandler public MAuthAuthenticatingHandler(MAuthWebApiOptions options) { this.options = options; - var loggerFactory = options.LoggerFactory ?? NullLoggerFactory.Instance; - var logger = loggerFactory.CreateLogger(typeof(MAuthAuthenticator)); - authenticator = new MAuthAuthenticator(options, logger); + SetupHandler(); } /// @@ -45,9 +43,7 @@ public MAuthAuthenticatingHandler(MAuthWebApiOptions options) public MAuthAuthenticatingHandler(MAuthWebApiOptions options, HttpMessageHandler innerHandler) : base(innerHandler) { this.options = options; - var loggerFactory = options.LoggerFactory ?? NullLoggerFactory.Instance; - var logger = loggerFactory.CreateLogger(typeof(MAuthAuthenticator)); - authenticator = new MAuthAuthenticator(options, logger); + SetupHandler(); } /// @@ -71,5 +67,12 @@ protected override async Task SendAsync( .SendAsync(request, cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); } + + private void SetupHandler() + { + var loggerFactory = options.LoggerFactory ?? NullLoggerFactory.Instance; + var logger = loggerFactory.CreateLogger(typeof(MAuthAuthenticatingHandler)); + this.authenticator = new MAuthAuthenticator(options, logger); + } } } From 87e28a4de450c9c59c5d2448a7b4d907f5add4df Mon Sep 17 00:00:00 2001 From: Prajon Date: Wed, 11 Sep 2019 21:15:24 -0400 Subject: [PATCH 37/40] Passed options param also to SetupHandler --- .../MAuthAuthenticatingHandler.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs index 48c2134..79549d5 100644 --- a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs +++ b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs @@ -15,7 +15,7 @@ namespace Medidata.MAuth.WebApi /// public class MAuthAuthenticatingHandler : DelegatingHandler { - private readonly MAuthWebApiOptions options; + private MAuthWebApiOptions options; private MAuthAuthenticator authenticator; /// Gets the Uuid of the client application. @@ -28,8 +28,7 @@ public class MAuthAuthenticatingHandler : DelegatingHandler /// The options for this message handler. public MAuthAuthenticatingHandler(MAuthWebApiOptions options) { - this.options = options; - SetupHandler(); + SetupHandler(options); } /// @@ -42,8 +41,7 @@ public MAuthAuthenticatingHandler(MAuthWebApiOptions options) /// public MAuthAuthenticatingHandler(MAuthWebApiOptions options, HttpMessageHandler innerHandler) : base(innerHandler) { - this.options = options; - SetupHandler(); + SetupHandler(options); } /// @@ -68,8 +66,9 @@ protected override async Task SendAsync( .ConfigureAwait(continueOnCapturedContext: false); } - private void SetupHandler() + private void SetupHandler(MAuthWebApiOptions opt) { + this.options = opt; var loggerFactory = options.LoggerFactory ?? NullLoggerFactory.Instance; var logger = loggerFactory.CreateLogger(typeof(MAuthAuthenticatingHandler)); this.authenticator = new MAuthAuthenticator(options, logger); From 9ff405b565d51432c9f934bb1e0047e809244575 Mon Sep 17 00:00:00 2001 From: Prajon Date: Wed, 11 Sep 2019 22:21:42 -0400 Subject: [PATCH 38/40] Updates --- src/Medidata.MAuth.Core/MAuthAuthenticator.cs | 12 ++++++------ .../MAuthAppBuilderExtensions.cs | 4 +++- src/Medidata.MAuth.Owin/MAuthMiddleware.cs | 7 ++----- .../MAuthAuthenticatingHandler.cs | 15 ++++++++------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs index 5c27b85..23bcaf2 100644 --- a/src/Medidata.MAuth.Core/MAuthAuthenticator.cs +++ b/src/Medidata.MAuth.Core/MAuthAuthenticator.cs @@ -33,11 +33,11 @@ public MAuthAuthenticator(MAuthOptionsBase options, ILogger logger) this.logger = logger; } - ///// - ///// Verifies if the request is authenticated or not. - ///// - ///// The request. - ///// A task object of the boolean value that verifies if the request is authenticated or not. + /// + /// Verifies if the request is authenticated or not. + /// + /// The request. + /// A task object of the boolean value that verifies if the request is authenticated or not. public async Task AuthenticateRequest(HttpRequestMessage request) { try @@ -119,7 +119,7 @@ private HttpRequestMessage CreateRequest(Guid applicationUuid, string tokenReque /// Extracts the authentication information from a . /// /// The request that has the authentication information. - /// /// Enum value of the MAuthVersion. + /// Enum value of the MAuthVersion. /// The authentication information with the payload from the request. internal PayloadAuthenticationInfo GetAuthenticationInfo(HttpRequestMessage request, MAuthVersion version) { diff --git a/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs b/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs index ab23090..819f07b 100644 --- a/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs +++ b/src/Medidata.MAuth.Owin/MAuthAppBuilderExtensions.cs @@ -1,4 +1,5 @@ using System; +using Microsoft.Owin.Logging; using Owin; namespace Medidata.MAuth.Owin @@ -25,7 +26,8 @@ public static IAppBuilder UseMAuthAuthentication(this IAppBuilder app, MAuthMidd if (options == null) throw new ArgumentNullException(nameof(options)); - return app.Use(options, app); + var owinLogger = app.CreateLogger(); + return app.Use(options, owinLogger); } /// diff --git a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs index 60ef9f9..b2c8db0 100644 --- a/src/Medidata.MAuth.Owin/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.Owin/MAuthMiddleware.cs @@ -1,10 +1,8 @@ using System.Net; using System.Threading.Tasks; using Medidata.MAuth.Core; -using Microsoft.Extensions.Logging; using Microsoft.Owin; -using Microsoft.Owin.Logging; -using Owin; +using ILogger = Microsoft.Owin.Logging.ILogger; namespace Medidata.MAuth.Owin { @@ -13,10 +11,9 @@ internal class MAuthMiddleware: OwinMiddleware private readonly MAuthMiddlewareOptions options; private readonly MAuthAuthenticator authenticator; - public MAuthMiddleware(OwinMiddleware next, MAuthMiddlewareOptions options, IAppBuilder app) : base(next) + public MAuthMiddleware(OwinMiddleware next, MAuthMiddlewareOptions options, ILogger owinLogger) : base(next) { this.options = options; - var owinLogger = app.CreateLogger(); Microsoft.Extensions.Logging.ILogger logger = new OwinLoggerWrapper(owinLogger); authenticator = new MAuthAuthenticator(options, logger); } diff --git a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs index 79549d5..b1a5a40 100644 --- a/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs +++ b/src/Medidata.MAuth.WebApi/MAuthAuthenticatingHandler.cs @@ -15,8 +15,8 @@ namespace Medidata.MAuth.WebApi /// public class MAuthAuthenticatingHandler : DelegatingHandler { - private MAuthWebApiOptions options; - private MAuthAuthenticator authenticator; + private readonly MAuthWebApiOptions options; + private readonly MAuthAuthenticator authenticator; /// Gets the Uuid of the client application. public Guid ClientAppUuid => authenticator.ApplicationUuid; @@ -28,7 +28,8 @@ public class MAuthAuthenticatingHandler : DelegatingHandler /// The options for this message handler. public MAuthAuthenticatingHandler(MAuthWebApiOptions options) { - SetupHandler(options); + this.options = options; + this.authenticator = this.SetupMAuthAuthenticator(options); } /// @@ -41,7 +42,8 @@ public MAuthAuthenticatingHandler(MAuthWebApiOptions options) /// public MAuthAuthenticatingHandler(MAuthWebApiOptions options, HttpMessageHandler innerHandler) : base(innerHandler) { - SetupHandler(options); + this.options = options; + this.authenticator = this.SetupMAuthAuthenticator(options); } /// @@ -66,12 +68,11 @@ protected override async Task SendAsync( .ConfigureAwait(continueOnCapturedContext: false); } - private void SetupHandler(MAuthWebApiOptions opt) + private MAuthAuthenticator SetupMAuthAuthenticator(MAuthWebApiOptions opt) { - this.options = opt; var loggerFactory = options.LoggerFactory ?? NullLoggerFactory.Instance; var logger = loggerFactory.CreateLogger(typeof(MAuthAuthenticatingHandler)); - this.authenticator = new MAuthAuthenticator(options, logger); + return new MAuthAuthenticator(options, logger); } } } From 452d41b8b2ae6a9bd66f1404da54f86a3bcae491 Mon Sep 17 00:00:00 2001 From: Prajon Date: Tue, 17 Sep 2019 09:11:19 -0400 Subject: [PATCH 39/40] Updated typo error --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 973d0e3..d5a8127 100644 --- a/README.md +++ b/README.md @@ -178,7 +178,7 @@ public class Startup } } ``` -For enabling the logging from Owin application, the follofing configuration will need to be added or updated: +For enabling the logging from Owin application, the following configuration will need to be added or updated: ```C# From 51adaa5598d6fcc302650c41b426ab86f17e719b Mon Sep 17 00:00:00 2001 From: Prajon Date: Thu, 26 Sep 2019 14:34:28 -0400 Subject: [PATCH 40/40] Replaced `EnableRewind()` with `EnableBuffering()` to fix the issue for aspnetcore 3.0 application and updated some aspnetcore packages --- src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs | 3 +-- .../Medidata.MAuth.AspNetCore.csproj | 6 +++--- tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs index d18c7b8..fabca21 100644 --- a/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs +++ b/src/Medidata.MAuth.AspNetCore/MAuthMiddleware.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using Medidata.MAuth.Core; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Internal; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -39,7 +38,7 @@ public MAuthMiddleware(RequestDelegate next, MAuthMiddlewareOptions options, ILo /// A that completes when the middleware has completed processing. public async Task Invoke(HttpContext context) { - context.Request.EnableRewind(); + context.Request.EnableBuffering(); if (!options.Bypass(context.Request) && !await context.TryAuthenticate(authenticator, options.HideExceptionsAndReturnUnauthorized)) diff --git a/src/Medidata.MAuth.AspNetCore/Medidata.MAuth.AspNetCore.csproj b/src/Medidata.MAuth.AspNetCore/Medidata.MAuth.AspNetCore.csproj index c556230..be761ec 100644 --- a/src/Medidata.MAuth.AspNetCore/Medidata.MAuth.AspNetCore.csproj +++ b/src/Medidata.MAuth.AspNetCore/Medidata.MAuth.AspNetCore.csproj @@ -10,9 +10,9 @@ - - - + + + diff --git a/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj b/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj index e0dd322..53d5bf3 100644 --- a/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj +++ b/tests/Medidata.MAuth.Tests/Medidata.MAuth.Tests.csproj @@ -69,7 +69,7 @@ - +