Enable reuse of BackOfficeSecurityRequirementsOperationFilter for custom APIs (#15699)

This commit is contained in:
Kenn Jacobsen
2024-02-13 15:08:42 +01:00
committed by GitHub
parent 690bfe3e68
commit 4f04669dce
2 changed files with 60 additions and 50 deletions

View File

@@ -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<OpenApiSecurityRequirement>
{
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;
}

View File

@@ -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<OpenApiSecurityRequirement>
{
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"
});
}
}
}