From d06199f9eec593df3c7f42ff13681b265996e050 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Mon, 4 Oct 2021 15:52:56 +0200 Subject: [PATCH] Make Http Endpoint activity security opt-in --- .../Extensions/ServiceCollectionExtensions.cs | 1 + .../Middleware/HttpEndpointMiddleware.cs | 8 +++++--- .../Options/HttpActivityOptions.cs | 2 +- .../AllowAnonymousHttpEndpointAuthorizationHandler.cs | 10 ++++++++++ ...henticationBasedHttpEndpointAuthorizationHandler.cs | 8 ++++++-- 5 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 src/activities/Elsa.Activities.Http/Services/AllowAnonymousHttpEndpointAuthorizationHandler.cs diff --git a/src/activities/Elsa.Activities.Http/Extensions/ServiceCollectionExtensions.cs b/src/activities/Elsa.Activities.Http/Extensions/ServiceCollectionExtensions.cs index 65b653a9a..03609ada0 100644 --- a/src/activities/Elsa.Activities.Http/Extensions/ServiceCollectionExtensions.cs +++ b/src/activities/Elsa.Activities.Http/Extensions/ServiceCollectionExtensions.cs @@ -51,6 +51,7 @@ namespace Microsoft.Extensions.DependencyInjection .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton(sp => sp.GetRequiredService>().Value.HttpEndpointAuthorizationHandlerFactory(sp)) .AddBookmarkProvider() .AddHttpContextAccessor() diff --git a/src/activities/Elsa.Activities.Http/Middleware/HttpEndpointMiddleware.cs b/src/activities/Elsa.Activities.Http/Middleware/HttpEndpointMiddleware.cs index 0477e0c61..c4967d19e 100644 --- a/src/activities/Elsa.Activities.Http/Middleware/HttpEndpointMiddleware.cs +++ b/src/activities/Elsa.Activities.Http/Middleware/HttpEndpointMiddleware.cs @@ -16,6 +16,7 @@ using Elsa.Services; using Elsa.Services.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Newtonsoft.Json; using Open.Linq.AsyncExtensions; @@ -37,7 +38,6 @@ namespace Elsa.Activities.Http.Middleware IWorkflowInstanceStore workflowInstanceStore, IWorkflowRegistry workflowRegistry, IWorkflowBlueprintReflector workflowBlueprintReflector, - IHttpEndpointAuthorizationHandler authorizationHandler, IEnumerable contentParsers) { var basePath = options.Value.BasePath; @@ -92,7 +92,7 @@ namespace Elsa.Activities.Http.Middleware var contentParser = orderedContentParsers.FirstOrDefault(x => x.SupportedContentTypes.Contains(simpleContentType, StringComparer.OrdinalIgnoreCase)) ?? orderedContentParsers.LastOrDefault() ?? new DefaultHttpRequestBodyParser(); var activityWrapper = workflowBlueprintWrapper.GetUnfilteredActivity(pendingWorkflow.ActivityId!)!; - if (!await AuthorizeAsync(httpContext, activityWrapper, workflowBlueprint, pendingWorkflow, authorizationHandler, cancellationToken)) + if (!await AuthorizeAsync(httpContext, options.Value, activityWrapper, workflowBlueprint, pendingWorkflow, cancellationToken)) { httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized; return; @@ -157,10 +157,10 @@ namespace Elsa.Activities.Http.Middleware private async Task AuthorizeAsync( HttpContext httpContext, + HttpActivityOptions options, IActivityBlueprintWrapper httpEndpoint, IWorkflowBlueprint workflowBlueprint, CollectedWorkflow pendingWorkflow, - IHttpEndpointAuthorizationHandler authorizationHandler, CancellationToken cancellationToken) { var authorize = await httpEndpoint.EvaluatePropertyValueAsync(x => x.Authorize, cancellationToken); @@ -168,6 +168,8 @@ namespace Elsa.Activities.Http.Middleware if (!authorize) return true; + var authorizationHandler = options.HttpEndpointAuthorizationHandlerFactory(httpContext.RequestServices); + return await authorizationHandler.AuthorizeAsync(new AuthorizeHttpEndpointContext(httpContext, httpEndpoint, workflowBlueprint, pendingWorkflow.WorkflowInstanceId, cancellationToken)); } diff --git a/src/activities/Elsa.Activities.Http/Options/HttpActivityOptions.cs b/src/activities/Elsa.Activities.Http/Options/HttpActivityOptions.cs index 635faffe5..5de8db4ef 100644 --- a/src/activities/Elsa.Activities.Http/Options/HttpActivityOptions.cs +++ b/src/activities/Elsa.Activities.Http/Options/HttpActivityOptions.cs @@ -17,6 +17,6 @@ namespace Elsa.Activities.Http.Options /// public PathString? BasePath { get; set; } - public Func HttpEndpointAuthorizationHandlerFactory { get; set; } = ActivatorUtilities.GetServiceOrCreateInstance; + public Func HttpEndpointAuthorizationHandlerFactory { get; set; } = ActivatorUtilities.GetServiceOrCreateInstance; } } \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Http/Services/AllowAnonymousHttpEndpointAuthorizationHandler.cs b/src/activities/Elsa.Activities.Http/Services/AllowAnonymousHttpEndpointAuthorizationHandler.cs new file mode 100644 index 000000000..1ef1b56ec --- /dev/null +++ b/src/activities/Elsa.Activities.Http/Services/AllowAnonymousHttpEndpointAuthorizationHandler.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using Elsa.Activities.Http.Models; + +namespace Elsa.Activities.Http.Services +{ + public class AllowAnonymousHttpEndpointAuthorizationHandler : IHttpEndpointAuthorizationHandler + { + public ValueTask AuthorizeAsync(AuthorizeHttpEndpointContext context) => new(true); + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Http/Services/AuthenticationBasedHttpEndpointAuthorizationHandler.cs b/src/activities/Elsa.Activities.Http/Services/AuthenticationBasedHttpEndpointAuthorizationHandler.cs index a218a4677..a84132259 100644 --- a/src/activities/Elsa.Activities.Http/Services/AuthenticationBasedHttpEndpointAuthorizationHandler.cs +++ b/src/activities/Elsa.Activities.Http/Services/AuthenticationBasedHttpEndpointAuthorizationHandler.cs @@ -13,8 +13,12 @@ namespace Elsa.Activities.Http.Services { var httpContext = context.HttpContext; var user = httpContext.User; + var identity = user.Identity; - if (!user.Identity.IsAuthenticated) + if (identity == null) + return false; + + if (identity.IsAuthenticated == false) return false; var cancellationToken = context.CancellationToken; @@ -22,7 +26,7 @@ namespace Elsa.Activities.Http.Services var policyName = await httpEndpoint.EvaluatePropertyValueAsync(x => x.Policy, cancellationToken); if (string.IsNullOrWhiteSpace(policyName)) - return user.Identity.IsAuthenticated; + return identity.IsAuthenticated; var resource = new HttpWorkflowResource(context.WorkflowBlueprint, httpEndpoint.ActivityBlueprint, context.WorkflowInstanceId); var authorizationResult = await _authorizationService.AuthorizeAsync(user, resource, policyName);