From 4f04669dce304f982897369ae4d779923d22dd0a Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 13 Feb 2024 15:08:42 +0100 Subject: [PATCH] Enable reuse of BackOfficeSecurityRequirementsOperationFilter for custom APIs (#15699) --- ...ficeSecurityRequirementsOperationFilter.cs | 52 +---------------- ...SecurityRequirementsOperationFilterBase.cs | 58 +++++++++++++++++++ 2 files changed, 60 insertions(+), 50 deletions(-) create mode 100644 src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilterBase.cs diff --git a/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilter.cs b/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilter.cs index 05950a8d5e08..5e5f3f865389 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilter.cs +++ b/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilter.cs @@ -1,56 +1,8 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; using Umbraco.Cms.Api.Management.DependencyInjection; -using Umbraco.Extensions; namespace Umbraco.Cms.Api.Management.OpenApi; -internal class BackOfficeSecurityRequirementsOperationFilter : IOperationFilter +internal class BackOfficeSecurityRequirementsOperationFilter : BackOfficeSecurityRequirementsOperationFilterBase { - public void Apply(OpenApiOperation operation, OperationFilterContext context) - { - if (context.MethodInfo.HasMapToApiAttribute(ManagementApiConfiguration.ApiName) == false) - { - return; - } - - if (!context.MethodInfo.GetCustomAttributes(true).Any(x => x is AllowAnonymousAttribute) && - !(context.MethodInfo.DeclaringType?.GetCustomAttributes(true).Any(x => x is AllowAnonymousAttribute) ?? false)) - { - operation.Responses.Add(StatusCodes.Status401Unauthorized.ToString(), new OpenApiResponse - { - Description = "The resource is protected and requires an authentication token" - }); - - operation.Security = new List - { - new OpenApiSecurityRequirement - { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = ManagementApiConfiguration.ApiSecurityName - } - }, new string[] { } - } - } - }; - } - - - // If method/controller has an explicit AuthorizeAttribute or the controller ctor injects IAuthorizationService, then we know Forbid result is possible. - if (context.MethodInfo.GetCustomAttributes(false).Any(x => x is AuthorizeAttribute - || context.MethodInfo.DeclaringType?.GetConstructors().Any(x => x.GetParameters().Any(x => x.ParameterType == typeof(IAuthorizationService))) is true)) - { - operation.Responses.Add(StatusCodes.Status403Forbidden.ToString(), new OpenApiResponse - { - Description = "The authenticated user do not have access to this resource" - }); - } - } + protected override string ApiName => ManagementApiConfiguration.ApiName; } diff --git a/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilterBase.cs b/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilterBase.cs new file mode 100644 index 000000000000..667ed153d94c --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilterBase.cs @@ -0,0 +1,58 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using Umbraco.Cms.Api.Management.DependencyInjection; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Api.Management.OpenApi; + +public abstract class BackOfficeSecurityRequirementsOperationFilterBase : IOperationFilter +{ + protected abstract string ApiName { get; } + + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + if (context.MethodInfo.HasMapToApiAttribute(ApiName) == false) + { + return; + } + + if (!context.MethodInfo.GetCustomAttributes(true).Any(x => x is AllowAnonymousAttribute) && + !(context.MethodInfo.DeclaringType?.GetCustomAttributes(true).Any(x => x is AllowAnonymousAttribute) ?? false)) + { + operation.Responses.Add(StatusCodes.Status401Unauthorized.ToString(), new OpenApiResponse + { + Description = "The resource is protected and requires an authentication token" + }); + + operation.Security = new List + { + new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = ManagementApiConfiguration.ApiSecurityName + } + }, new string[] { } + } + } + }; + } + + + // If method/controller has an explicit AuthorizeAttribute or the controller ctor injects IAuthorizationService, then we know Forbid result is possible. + if (context.MethodInfo.GetCustomAttributes(false).Any(x => x is AuthorizeAttribute + || context.MethodInfo.DeclaringType?.GetConstructors().Any(x => x.GetParameters().Any(x => x.ParameterType == typeof(IAuthorizationService))) is true)) + { + operation.Responses.Add(StatusCodes.Status403Forbidden.ToString(), new OpenApiResponse + { + Description = "The authenticated user do not have access to this resource" + }); + } + } +}