diff --git a/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs b/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs index f24efa2d6..af6885bdb 100644 --- a/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs +++ b/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs @@ -38,6 +38,16 @@ 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 + /// + public Func, Task> AfterAuthenticationMiddleware { get; set; } + /// /// This is to allow the user to run any extra authorization before the Ocelot authentication /// kicks in @@ -56,6 +66,16 @@ 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 authentication + /// kicks in + /// + public Func, Task> AfterAuthorizationMiddleware { get; set; } + /// /// This allows the user to implement there own query string manipulation logic /// diff --git a/src/Ocelot/Middleware/OcelotPipelineExtensions.cs b/src/Ocelot/Middleware/OcelotPipelineExtensions.cs index 58ab855df..b0e596b41 100644 --- a/src/Ocelot/Middleware/OcelotPipelineExtensions.cs +++ b/src/Ocelot/Middleware/OcelotPipelineExtensions.cs @@ -102,6 +102,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(); @@ -121,6 +124,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 e155010d0..f764d81c0 100644 --- a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs +++ b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs @@ -251,7 +251,50 @@ public void should_call_pre_authorization_middleware() .And(x => x.ThenTheCounterIs(1)) .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() { @@ -296,7 +339,50 @@ public void should_call_pre_http_authentication_middleware() .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() {