From b8d3f50f404dc0ce87a8bd000bd9df33cbc58cf4 Mon Sep 17 00:00:00 2001 From: Albie Date: Mon, 21 Dec 2020 10:57:38 +0000 Subject: [PATCH 1/2] fix issues with client generation system and add new ssl bypass handler --- .../HeaderPreservingHandlerClient.cs | 13 ------ .../HeaderPreservingHeaderTests.cs | 8 +++- .../InsecureSslVerificationHandlerTests.cs | 40 +++++++++++++++++++ DragonFruit.Common.Data/ApiClient.cs | 14 ++++++- .../HeaderPreservingRedirectHandler.cs | 1 - .../InsecureSslVerificationHandler.cs | 20 ++++++++++ .../Headers/HeaderCollection.cs | 2 +- 7 files changed, 79 insertions(+), 19 deletions(-) delete mode 100644 DragonFruit.Common.Data.Tests/Handlers/AuthPreservingHandler/HeaderPreservingHandlerClient.cs create mode 100644 DragonFruit.Common.Data.Tests/Handlers/InsecureSslVerificationHandlerTests.cs create mode 100644 DragonFruit.Common.Data/Handlers/InsecureSslVerificationHandler.cs diff --git a/DragonFruit.Common.Data.Tests/Handlers/AuthPreservingHandler/HeaderPreservingHandlerClient.cs b/DragonFruit.Common.Data.Tests/Handlers/AuthPreservingHandler/HeaderPreservingHandlerClient.cs deleted file mode 100644 index 776e423..0000000 --- a/DragonFruit.Common.Data.Tests/Handlers/AuthPreservingHandler/HeaderPreservingHandlerClient.cs +++ /dev/null @@ -1,13 +0,0 @@ -// DragonFruit.Common Copyright 2020 DragonFruit Network -// Licensed under the MIT License. Please refer to the LICENSE file at the root of this project for details - -using System.Net.Http; -using DragonFruit.Common.Data.Handlers; - -namespace DragonFruit.Common.Data.Tests.Handlers.AuthPreservingHandler -{ - public class HeaderPreservingHandlerClient : ApiClient - { - protected override HttpMessageHandler CreateHandler() => new HeaderPreservingRedirectHandler(); - } -} diff --git a/DragonFruit.Common.Data.Tests/Handlers/AuthPreservingHandler/HeaderPreservingHeaderTests.cs b/DragonFruit.Common.Data.Tests/Handlers/AuthPreservingHandler/HeaderPreservingHeaderTests.cs index fa6c3ec..757797e 100644 --- a/DragonFruit.Common.Data.Tests/Handlers/AuthPreservingHandler/HeaderPreservingHeaderTests.cs +++ b/DragonFruit.Common.Data.Tests/Handlers/AuthPreservingHandler/HeaderPreservingHeaderTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. Please refer to the LICENSE file at the root of this project for details using System; +using DragonFruit.Common.Data.Handlers; using DragonFruit.Common.Data.Tests.Handlers.AuthPreservingHandler.Objects; using NUnit.Framework; @@ -13,7 +14,10 @@ public class HeaderPreservingHeaderTests [TestCase] public void TestHeaderPreservation() { - var redirectClient = new HeaderPreservingHandlerClient(); + var redirectClient = new ApiClient + { + Handler = () => new HeaderPreservingRedirectHandler() + }; //get auth token var request = new AuthRequest(); @@ -28,7 +32,7 @@ public void TestHeaderPreservation() var auth = redirectClient.Perform(request); redirectClient.Authorization = $"{auth.Type} {auth.AccessToken}"; - //user lookups by username = 301. without our HeaderPreservingHandler we'd get a 401 + // user lookups by username = 301. without our HeaderPreservingHandler we'd get a 401 redirectClient.Perform(new OrbitTestUserRequest()); } } diff --git a/DragonFruit.Common.Data.Tests/Handlers/InsecureSslVerificationHandlerTests.cs b/DragonFruit.Common.Data.Tests/Handlers/InsecureSslVerificationHandlerTests.cs new file mode 100644 index 0000000..19a25ad --- /dev/null +++ b/DragonFruit.Common.Data.Tests/Handlers/InsecureSslVerificationHandlerTests.cs @@ -0,0 +1,40 @@ +// DragonFruit.Common Copyright 2020 DragonFruit Network +// Licensed under the MIT License. Please refer to the LICENSE file at the root of this project for details + +using System; +using System.Linq; +using System.Security.Authentication; +using DragonFruit.Common.Data.Basic; +using DragonFruit.Common.Data.Handlers; +using NUnit.Framework; + +#pragma warning disable 618 + +namespace DragonFruit.Common.Data.Tests.Handlers +{ + [TestFixture] + public class InsecureSslVerificationHandlerTests + { + [Test] + public void TestSslVerificationHandler() + { + var client = new ApiClient(); + var request = new BasicApiRequest("http://wrong.host.badssl.com/"); + + try + { + client.Perform(request); + Assert.Fail("Request must fail when SSL validation is enabled"); + } + catch (AggregateException e) + { + var innerExceptions = e.InnerExceptions.Select(x => x.InnerException); + Assert.IsTrue(innerExceptions.Any(x => x is AuthenticationException)); + } + + // set handler and go again + client.Handler = () => new InsecureSslVerificationHandler(); + Assert.IsTrue(client.Perform(request).IsSuccessStatusCode); + } + } +} diff --git a/DragonFruit.Common.Data/ApiClient.cs b/DragonFruit.Common.Data/ApiClient.cs index e458a47..db38b05 100644 --- a/DragonFruit.Common.Data/ApiClient.cs +++ b/DragonFruit.Common.Data/ApiClient.cs @@ -92,7 +92,7 @@ public Func Handler set { _handler = value; - RequestClientReset(false); + RequestClientReset(true); } } @@ -366,7 +366,17 @@ protected virtual void ValidateRequest(ApiRequest request) } } - public void RequestClientReset(bool fullReset) => Interlocked.Exchange(ref _clientAdjustmentRequestSignal, fullReset ? 2 : 1); + public void RequestClientReset(bool fullReset) + { + if (fullReset) + { + Interlocked.Exchange(ref _clientAdjustmentRequestSignal, 2); + } + else + { + Interlocked.CompareExchange(ref _clientAdjustmentRequestSignal, 1, 0); + } + } private void Timeout() => Thread.Sleep(AdjustmentTimeout / 2); } diff --git a/DragonFruit.Common.Data/Handlers/HeaderPreservingRedirectHandler.cs b/DragonFruit.Common.Data/Handlers/HeaderPreservingRedirectHandler.cs index 4829b2a..318a22d 100644 --- a/DragonFruit.Common.Data/Handlers/HeaderPreservingRedirectHandler.cs +++ b/DragonFruit.Common.Data/Handlers/HeaderPreservingRedirectHandler.cs @@ -12,7 +12,6 @@ namespace DragonFruit.Common.Data.Handlers /// /// will auto-strip the auth header, even on redirects to the same site. /// This handler "bypasses" this protection if the host is the same. It also supports an inner handler, should you wish to configure one. - /// /// /// You should only use this if you know what you're doing /// diff --git a/DragonFruit.Common.Data/Handlers/InsecureSslVerificationHandler.cs b/DragonFruit.Common.Data/Handlers/InsecureSslVerificationHandler.cs new file mode 100644 index 0000000..a1bb650 --- /dev/null +++ b/DragonFruit.Common.Data/Handlers/InsecureSslVerificationHandler.cs @@ -0,0 +1,20 @@ +// DragonFruit.Common Copyright 2020 DragonFruit Network +// Licensed under the MIT License. Please refer to the LICENSE file at the root of this project for details + +using System; +using System.Net.Http; + +namespace DragonFruit.Common.Data.Handlers +{ + [Obsolete(nameof(InsecureSslVerificationHandler) + "is insecure and should only be used for testing/development purposes")] + public class InsecureSslVerificationHandler : HttpClientHandler + { + public InsecureSslVerificationHandler() + { + ServerCertificateCustomValidationCallback = delegate + { + return true; + }; + } + } +} diff --git a/DragonFruit.Common.Data/Headers/HeaderCollection.cs b/DragonFruit.Common.Data/Headers/HeaderCollection.cs index d278004..ea8c74f 100644 --- a/DragonFruit.Common.Data/Headers/HeaderCollection.cs +++ b/DragonFruit.Common.Data/Headers/HeaderCollection.cs @@ -10,7 +10,7 @@ namespace DragonFruit.Common.Data.Headers public class HeaderCollection { private readonly ConcurrentDictionary _values = new ConcurrentDictionary(); - private ApiClient _client; + private readonly ApiClient _client; public HeaderCollection(ApiClient client) { From 2ee7f985cdac2f19ae826c8c62a3a50340772f69 Mon Sep 17 00:00:00 2001 From: Albie Date: Mon, 21 Dec 2020 11:02:50 +0000 Subject: [PATCH 2/2] fix tests --- .../Handlers/InsecureSslVerificationHandlerTests.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/DragonFruit.Common.Data.Tests/Handlers/InsecureSslVerificationHandlerTests.cs b/DragonFruit.Common.Data.Tests/Handlers/InsecureSslVerificationHandlerTests.cs index 19a25ad..a51b9b0 100644 --- a/DragonFruit.Common.Data.Tests/Handlers/InsecureSslVerificationHandlerTests.cs +++ b/DragonFruit.Common.Data.Tests/Handlers/InsecureSslVerificationHandlerTests.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using System.Net.Http; using System.Security.Authentication; using DragonFruit.Common.Data.Basic; using DragonFruit.Common.Data.Handlers; @@ -26,11 +27,17 @@ public void TestSslVerificationHandler() client.Perform(request); Assert.Fail("Request must fail when SSL validation is enabled"); } + // .NET Standard 2 returns aggregate exception catch (AggregateException e) { var innerExceptions = e.InnerExceptions.Select(x => x.InnerException); Assert.IsTrue(innerExceptions.Any(x => x is AuthenticationException)); } + // .NET 5 returns a non-aggregated copy of above + catch (HttpRequestException e) + { + Assert.IsTrue(e.InnerException is AuthenticationException); + } // set handler and go again client.Handler = () => new InsecureSslVerificationHandler();