From 48091b993311bfc704b14fe4be7ec0409cbc7f68 Mon Sep 17 00:00:00 2001 From: Arsalan Moniee Date: Mon, 26 Jul 2021 21:53:01 +0430 Subject: [PATCH 1/6] add AfterAuthenticationMiddleware and AfterAuthorizationMiddleware property to OcelotPipelineConfiguration claas for more flexibility in pipieline --- .../Middleware/OcelotPipelineConfiguration.cs | 25 +++- .../Middleware/OcelotPipelineExtensions.cs | 6 + .../CustomMiddlewareTests.cs | 136 +++++++++++++++--- 3 files changed, 142 insertions(+), 25 deletions(-) diff --git a/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs b/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs index 403b6e844..f05e1d157 100644 --- a/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs +++ b/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs @@ -32,7 +32,18 @@ public class OcelotPipelineConfiguration public Func, Task> AuthenticationMiddleware { get; set; } /// - /// This is to allow the user to run any extra authorization before the Ocelot authentication kicks in. + /// This is to allow the user to run any extra authentication after the Ocelot authentication + /// kicks in + /// + /// + /// This is to allow the user to run any extra authentication after the Ocelot authentication + /// kicks in + /// + public Func, Task> AfterAuthenticationMiddleware { get; set; } + + /// + /// This is to allow the user to run any extra authorization before the Ocelot authentication + /// kicks in /// /// /// A delegate object. @@ -48,7 +59,17 @@ public class OcelotPipelineConfiguration public Func, Task> AuthorizationMiddleware { get; set; } /// - /// This allows the user to implement there own query string manipulation logic. + /// This is to allow the user to run any extra authorization after the Ocelot authentication + /// kicks in + /// + /// + /// This is to allow the user to run any extra authorization after the Ocelot authentication + /// kicks in + /// + public Func, Task> AfterAuthorizationMiddleware { get; set; } + + /// + /// This allows the user to implement there own query string manipulation logic /// /// /// A delegate object. diff --git a/src/Ocelot/Middleware/OcelotPipelineExtensions.cs b/src/Ocelot/Middleware/OcelotPipelineExtensions.cs index 16f4a5cff..4e601e33d 100644 --- a/src/Ocelot/Middleware/OcelotPipelineExtensions.cs +++ b/src/Ocelot/Middleware/OcelotPipelineExtensions.cs @@ -100,6 +100,9 @@ public static RequestDelegate BuildOcelotPipeline(this IApplicationBuilder app, app.Use(pipelineConfiguration.AuthenticationMiddleware); } + // Allow After authentication logic. The idea being people might want to run something custom after what is built in. + app.UseIfNotNull(pipelineConfiguration.AfterAuthenticationMiddleware); + // The next thing we do is look at any claims transforms in case this is important for authorization app.UseClaimsToClaimsMiddleware(); @@ -119,6 +122,9 @@ public static RequestDelegate BuildOcelotPipeline(this IApplicationBuilder app, app.Use(pipelineConfiguration.AuthorizationMiddleware); } + // Allow after authorization logic. The idea being people might want to run something custom after what is built in. + app.UseIfNotNull(pipelineConfiguration.AfterAuthorizationMiddleware); + // Now we can run the claims to headers transformation middleware app.UseClaimsToHeadersMiddleware(); diff --git a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs index 5fc91dd8d..adbb6dd21 100644 --- a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs +++ b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs @@ -2,7 +2,7 @@ using Ocelot.Configuration.File; using Ocelot.Middleware; using System.Diagnostics; - + namespace Ocelot.AcceptanceTests { public class CustomMiddlewareTests : IDisposable @@ -28,8 +28,8 @@ public void should_call_pre_query_string_builder_middleware() _counter++; await next.Invoke(); }, - }; - + }; + var port = PortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration @@ -73,8 +73,8 @@ public void should_call_authorization_middleware() _counter++; await next.Invoke(); }, - }; - + }; + var port = PortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration @@ -118,8 +118,8 @@ public void should_call_authentication_middleware() _counter++; await next.Invoke(); }, - }; - + }; + var port = PortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration @@ -163,8 +163,8 @@ public void should_call_pre_error_middleware() _counter++; await next.Invoke(); }, - }; - + }; + var port = PortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration @@ -208,8 +208,8 @@ public void should_call_pre_authorization_middleware() _counter++; await next.Invoke(); }, - }; - + }; + var port = PortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration @@ -243,6 +243,51 @@ public void should_call_pre_authorization_middleware() .BDDfy(); } + [Fact] + public void should_call_after_authorization_middleware() + { + var configuration = new OcelotPipelineConfiguration + { + AfterAuthorizationMiddleware = async (ctx, next) => + { + _counter++; + await next.Invoke(); + } + }; + + var port = RandomPortFinder.GetRandomPort(); + + var fileConfiguration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "")) + .And(x => _steps.GivenThereIsAConfiguration(fileConfiguration, _configurationPath)) + .And(x => _steps.GivenOcelotIsRunning(configuration)) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => x.ThenTheCounterIs(1)) + .BDDfy(); + } + [Fact] public void should_call_pre_http_authentication_middleware() { @@ -253,8 +298,8 @@ public void should_call_pre_http_authentication_middleware() _counter++; await next.Invoke(); }, - }; - + }; + var port = PortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration @@ -332,8 +377,53 @@ public void should_not_throw_when_pipeline_terminates_early() .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => x.ThenTheCounterIs(1)) .BDDfy(); - } - + } + + [Fact] + public void should_call_after_http_authentication_middleware() + { + var configuration = new OcelotPipelineConfiguration + { + AfterAuthenticationMiddleware = async (ctx, next) => + { + _counter++; + await next.Invoke(); + } + }; + + var port = RandomPortFinder.GetRandomPort(); + + var fileConfiguration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "")) + .And(x => _steps.GivenThereIsAConfiguration(fileConfiguration, _configurationPath)) + .And(x => _steps.GivenOcelotIsRunning(configuration)) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => x.ThenTheCounterIs(1)) + .BDDfy(); + } + [Fact(Skip = "This is just an example to show how you could hook into Ocelot pipeline with your own middleware. At the moment you must use Response.OnCompleted callback and cannot change the response :( I will see if this can be changed one day!")] public void should_fix_issue_237() { @@ -342,14 +432,14 @@ public void should_fix_issue_237() var httpContext = (HttpContext)state; if (httpContext.Response.StatusCode > 400) - { + { Debug.WriteLine("COUNT CALLED"); Console.WriteLine("COUNT CALLED"); } return Task.CompletedTask; - }; - + }; + var port = PortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration @@ -413,8 +503,8 @@ public void Dispose() public class FakeMiddleware { private readonly RequestDelegate _next; - private readonly Func _callback; - + private readonly Func _callback; + public FakeMiddleware(RequestDelegate next, Func callback) { _next = next; @@ -423,10 +513,10 @@ public FakeMiddleware(RequestDelegate next, Func callback) public async Task Invoke(HttpContext context) { - await _next(context); - + await _next(context); + context.Response.OnCompleted(_callback, context); } } } -} +} From eefcf4a55fce256f0553c75144508f80fcd38116 Mon Sep 17 00:00:00 2001 From: raman-m Date: Fri, 25 Aug 2023 16:59:30 +0300 Subject: [PATCH 2/6] Fix warnings --- .../Middleware/OcelotPipelineConfiguration.cs | 17 ++++++---------- .../CustomMiddlewareTests.cs | 20 +++++++++---------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs b/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs index f05e1d157..273a97786 100644 --- a/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs +++ b/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs @@ -32,18 +32,15 @@ public class OcelotPipelineConfiguration public Func, Task> AuthenticationMiddleware { get; set; } /// - /// This is to allow the user to run any extra authentication after the Ocelot authentication - /// kicks in + /// This is to allow the user to run any extra authentication after the Ocelot authentication kicks in. /// /// - /// This is to allow the user to run any extra authentication after the Ocelot authentication - /// kicks in + /// A delegate object. /// public Func, Task> AfterAuthenticationMiddleware { get; set; } /// - /// This is to allow the user to run any extra authorization before the Ocelot authentication - /// kicks in + /// This is to allow the user to run any extra authorization before the Ocelot authentication kicks in. /// /// /// A delegate object. @@ -59,17 +56,15 @@ public class OcelotPipelineConfiguration public Func, Task> AuthorizationMiddleware { get; set; } /// - /// This is to allow the user to run any extra authorization after the Ocelot authentication - /// kicks in + /// This is to allow the user to run any extra authorization after the Ocelot authorization kicks in. /// /// - /// This is to allow the user to run any extra authorization after the Ocelot authentication - /// kicks in + /// A delegate object. /// public Func, Task> AfterAuthorizationMiddleware { get; set; } /// - /// This allows the user to implement there own query string manipulation logic + /// This allows the user to implement there own query string manipulation logic. /// /// /// A delegate object. diff --git a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs index adbb6dd21..921213565 100644 --- a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs +++ b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs @@ -248,11 +248,11 @@ public void should_call_after_authorization_middleware() { var configuration = new OcelotPipelineConfiguration { - AfterAuthorizationMiddleware = async (ctx, next) => + AfterAuthorizationMiddleware = async (ctx, next) => { _counter++; await next.Invoke(); - } + }, }; var port = RandomPortFinder.GetRandomPort(); @@ -270,13 +270,13 @@ public void should_call_after_authorization_middleware() { Host = "localhost", Port = port, - } + }, }, DownstreamScheme = "http", UpstreamPathTemplate = "/", UpstreamHttpMethod = new List { "Get" }, - } - } + }, + }, }; this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "")) @@ -384,11 +384,11 @@ public void should_call_after_http_authentication_middleware() { var configuration = new OcelotPipelineConfiguration { - AfterAuthenticationMiddleware = async (ctx, next) => + AfterAuthenticationMiddleware = async (ctx, next) => { _counter++; await next.Invoke(); - } + }, }; var port = RandomPortFinder.GetRandomPort(); @@ -406,13 +406,13 @@ public void should_call_after_http_authentication_middleware() { Host = "localhost", Port = port, - } + }, }, DownstreamScheme = "http", UpstreamPathTemplate = "/", UpstreamHttpMethod = new List { "Get" }, - } - } + }, + }, }; this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "")) From 18c11105ff67f954622d64164564081e980aac77 Mon Sep 17 00:00:00 2001 From: raman-m Date: Fri, 25 Aug 2023 17:35:43 +0300 Subject: [PATCH 3/6] Use target-typed new() --- test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs index 921213565..233c296dc 100644 --- a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs +++ b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs @@ -261,12 +261,12 @@ public void should_call_after_authorization_middleware() { Routes = new List { - new FileRoute + new() { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List { - new FileHostAndPort + new() { Host = "localhost", Port = port, @@ -397,12 +397,12 @@ public void should_call_after_http_authentication_middleware() { Routes = new List { - new FileRoute + new() { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List { - new FileHostAndPort + new() { Host = "localhost", Port = port, From 1a7e1ba90c930394b41b0a7897ee698b0626134b Mon Sep 17 00:00:00 2001 From: raman-m Date: Fri, 25 Aug 2023 17:40:08 +0300 Subject: [PATCH 4/6] IDE1006 Naming rule violation: These words must begin with upper case characters: should_* --- .../CustomMiddlewareTests.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs index 233c296dc..25cfb27e8 100644 --- a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs +++ b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs @@ -19,7 +19,7 @@ public CustomMiddlewareTests() } [Fact] - public void should_call_pre_query_string_builder_middleware() + public void Should_call_pre_query_string_builder_middleware() { var configuration = new OcelotPipelineConfiguration { @@ -64,7 +64,7 @@ public void should_call_pre_query_string_builder_middleware() } [Fact] - public void should_call_authorization_middleware() + public void Should_call_authorization_middleware() { var configuration = new OcelotPipelineConfiguration { @@ -109,7 +109,7 @@ public void should_call_authorization_middleware() } [Fact] - public void should_call_authentication_middleware() + public void Should_call_authentication_middleware() { var configuration = new OcelotPipelineConfiguration { @@ -154,7 +154,7 @@ public void should_call_authentication_middleware() } [Fact] - public void should_call_pre_error_middleware() + public void Should_call_pre_error_middleware() { var configuration = new OcelotPipelineConfiguration { @@ -199,7 +199,7 @@ public void should_call_pre_error_middleware() } [Fact] - public void should_call_pre_authorization_middleware() + public void Should_call_pre_authorization_middleware() { var configuration = new OcelotPipelineConfiguration { @@ -244,7 +244,7 @@ public void should_call_pre_authorization_middleware() } [Fact] - public void should_call_after_authorization_middleware() + public void Should_call_after_authorization_middleware() { var configuration = new OcelotPipelineConfiguration { @@ -289,7 +289,7 @@ public void should_call_after_authorization_middleware() } [Fact] - public void should_call_pre_http_authentication_middleware() + public void Should_call_pre_http_authentication_middleware() { var configuration = new OcelotPipelineConfiguration { @@ -380,7 +380,7 @@ public void should_not_throw_when_pipeline_terminates_early() } [Fact] - public void should_call_after_http_authentication_middleware() + public void Should_call_after_http_authentication_middleware() { var configuration = new OcelotPipelineConfiguration { @@ -425,7 +425,7 @@ public void should_call_after_http_authentication_middleware() } [Fact(Skip = "This is just an example to show how you could hook into Ocelot pipeline with your own middleware. At the moment you must use Response.OnCompleted callback and cannot change the response :( I will see if this can be changed one day!")] - public void should_fix_issue_237() + public void Should_fix_issue_237() { Func callback = state => { From 0fc02f29b5d03951e66d14c5c6b7f742251e1302 Mon Sep 17 00:00:00 2001 From: raman-m Date: Fri, 25 Aug 2023 17:44:12 +0300 Subject: [PATCH 5/6] CA1816 Change Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. --- test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs index 25cfb27e8..352dcd974 100644 --- a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs +++ b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs @@ -496,8 +496,9 @@ private void GivenThereIsAServiceRunningOn(string url, int statusCode, string ba public void Dispose() { - _serviceHandler?.Dispose(); - _steps.Dispose(); + _serviceHandler.Dispose(); + _steps.Dispose(); + GC.SuppressFinalize(this); } public class FakeMiddleware From f8662f44de8c99ede09680b857b4d9d003551adc Mon Sep 17 00:00:00 2001 From: Raman Maksimchuk Date: Sun, 7 Apr 2024 16:02:31 +0300 Subject: [PATCH 6/6] Fix build errors --- test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs index 352dcd974..65ea66589 100644 --- a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs +++ b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs @@ -255,7 +255,7 @@ public void Should_call_after_authorization_middleware() }, }; - var port = RandomPortFinder.GetRandomPort(); + var port = PortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration { @@ -280,7 +280,7 @@ public void Should_call_after_authorization_middleware() }; this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "")) - .And(x => _steps.GivenThereIsAConfiguration(fileConfiguration, _configurationPath)) + .And(x => _steps.GivenThereIsAConfiguration(fileConfiguration)) .And(x => _steps.GivenOcelotIsRunning(configuration)) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) @@ -391,7 +391,7 @@ public void Should_call_after_http_authentication_middleware() }, }; - var port = RandomPortFinder.GetRandomPort(); + var port = PortFinder.GetRandomPort(); var fileConfiguration = new FileConfiguration { @@ -416,7 +416,7 @@ public void Should_call_after_http_authentication_middleware() }; this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "")) - .And(x => _steps.GivenThereIsAConfiguration(fileConfiguration, _configurationPath)) + .And(x => _steps.GivenThereIsAConfiguration(fileConfiguration)) .And(x => _steps.GivenOcelotIsRunning(configuration)) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))