diff --git a/src/GeneratedEndpoints/MinimalApiGenerator.cs b/src/GeneratedEndpoints/MinimalApiGenerator.cs index feb2322..221768a 100644 --- a/src/GeneratedEndpoints/MinimalApiGenerator.cs +++ b/src/GeneratedEndpoints/MinimalApiGenerator.cs @@ -710,11 +710,11 @@ private static ( cancellationToken.ThrowIfCancellationRequested(); EquatableImmutableArray? tags = null; - var requireAuthorization = false; + bool? requireAuthorization = null; EquatableImmutableArray? authorizationPolicies = null; - var disableAntiforgery = false; - var allowAnonymous = false; - var excludeFromDescription = false; + bool? disableAntiforgery = null; + bool? allowAnonymous = null; + bool? excludeFromDescription = null; List? accepts = null; List? produces = null; @@ -722,6 +722,8 @@ private static ( List? producesValidationProblem = null; var classAttributes = classSymbol.GetAttributes(); + var classHasAllowAnonymousAttribute = false; + var classHasRequireAuthorizationAttribute = false; GetAdditionalRequestHandlerAttributeValues( classAttributes, ref tags, @@ -733,10 +735,14 @@ private static ( ref accepts, ref produces, ref producesProblem, - ref producesValidationProblem + ref producesValidationProblem, + ref classHasAllowAnonymousAttribute, + ref classHasRequireAuthorizationAttribute ); var methodAttributes = methodSymbol.GetAttributes(); + var methodHasAllowAnonymousAttribute = false; + var methodHasRequireAuthorizationAttribute = false; GetAdditionalRequestHandlerAttributeValues( methodAttributes, ref tags, @@ -748,16 +754,21 @@ ref producesValidationProblem ref accepts, ref produces, ref producesProblem, - ref producesValidationProblem + ref producesValidationProblem, + ref methodHasAllowAnonymousAttribute, + ref methodHasRequireAuthorizationAttribute ); + if (methodHasRequireAuthorizationAttribute && !methodHasAllowAnonymousAttribute) + allowAnonymous = false; + return ( tags, - requireAuthorization, + requireAuthorization ?? false, authorizationPolicies, - disableAntiforgery, - allowAnonymous, - excludeFromDescription, + disableAntiforgery ?? false, + allowAnonymous ?? false, + excludeFromDescription ?? false, ToEquatableOrNull(accepts), ToEquatableOrNull(produces), ToEquatableOrNull(producesProblem), @@ -768,15 +779,17 @@ ref producesValidationProblem private static void GetAdditionalRequestHandlerAttributeValues( ImmutableArray attributes, ref EquatableImmutableArray? tags, - ref bool requireAuthorization, + ref bool? requireAuthorization, ref EquatableImmutableArray? authorizationPolicies, - ref bool disableAntiforgery, - ref bool allowAnonymous, - ref bool excludeFromDescription, + ref bool? disableAntiforgery, + ref bool? allowAnonymous, + ref bool? excludeFromDescription, ref List? accepts, ref List? produces, ref List? producesProblem, - ref List? producesValidationProblem + ref List? producesValidationProblem, + ref bool hasAllowAnonymousAttribute, + ref bool hasRequireAuthorizationAttribute ) { foreach (var attribute in attributes) @@ -818,6 +831,7 @@ ref List? producesValidationProblem break; case $"global::{RequireAuthorizationAttributeFullyQualifiedName}": requireAuthorization = true; + hasRequireAuthorizationAttribute = true; if (attribute.ConstructorArguments.Length == 1) { var arg = attribute.ConstructorArguments[0]; @@ -837,6 +851,7 @@ ref List? producesValidationProblem break; case $"global::{AllowAnonymousAttributeFullyQualifiedName}": allowAnonymous = true; + hasAllowAnonymousAttribute = true; break; case "global::Microsoft.AspNetCore.Routing.ExcludeFromDescriptionAttribute": excludeFromDescription = true; diff --git a/tests/GeneratedEndpoints.Tests/GeneratedEndpointsTests.ClassAllowAnonymousMethodRequireAuthorization_MapEndpointHandlers_WithNamespace.verified.txt b/tests/GeneratedEndpoints.Tests/GeneratedEndpointsTests.ClassAllowAnonymousMethodRequireAuthorization_MapEndpointHandlers_WithNamespace.verified.txt new file mode 100644 index 0000000..e5accdf --- /dev/null +++ b/tests/GeneratedEndpoints.Tests/GeneratedEndpointsTests.ClassAllowAnonymousMethodRequireAuthorization_MapEndpointHandlers_WithNamespace.verified.txt @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// +// This code was generated by MinimalApiGenerator which can be found +// in the GeneratedEndpoints namespace. +// +// Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated. +// +//----------------------------------------------------------------------------- + +#nullable enable + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AspNetCore.Generated.Routing; + +internal static class EndpointRouteBuilderExtensions +{ + internal static IEndpointRouteBuilder MapEndpointHandlers(this IEndpointRouteBuilder builder) + { + builder.MapGet("/allow-anon", global::GeneratedEndpointsTests.AllowAnonymousClass.Handle) + .WithName("Handle") + .RequireAuthorization(); + + return builder; + } +} diff --git a/tests/GeneratedEndpoints.Tests/GeneratedEndpointsTests.ClassAllowAnonymousMethodRequireAuthorization_MapEndpointHandlers_WithoutNamespace.verified.txt b/tests/GeneratedEndpoints.Tests/GeneratedEndpointsTests.ClassAllowAnonymousMethodRequireAuthorization_MapEndpointHandlers_WithoutNamespace.verified.txt new file mode 100644 index 0000000..22be3a5 --- /dev/null +++ b/tests/GeneratedEndpoints.Tests/GeneratedEndpointsTests.ClassAllowAnonymousMethodRequireAuthorization_MapEndpointHandlers_WithoutNamespace.verified.txt @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// +// This code was generated by MinimalApiGenerator which can be found +// in the GeneratedEndpoints namespace. +// +// Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated. +// +//----------------------------------------------------------------------------- + +#nullable enable + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AspNetCore.Generated.Routing; + +internal static class EndpointRouteBuilderExtensions +{ + internal static IEndpointRouteBuilder MapEndpointHandlers(this IEndpointRouteBuilder builder) + { + builder.MapGet("/allow-anon", global::AllowAnonymousClass.Handle) + .WithName("Handle") + .RequireAuthorization(); + + return builder; + } +} diff --git a/tests/GeneratedEndpoints.Tests/GeneratedEndpointsTests.cs b/tests/GeneratedEndpoints.Tests/GeneratedEndpointsTests.cs index 331d92d..2a7750c 100644 --- a/tests/GeneratedEndpoints.Tests/GeneratedEndpointsTests.cs +++ b/tests/GeneratedEndpoints.Tests/GeneratedEndpointsTests.cs @@ -99,6 +99,29 @@ await result.VerifyAsync("MapEndpointHandlers.g.cs") .UseMethodName($"{nameof(MapGetWithConfigureServiceProvider)}_MapEndpointHandlers_With{(withNamespace ? "" : "out")}Namespace"); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task ClassAllowAnonymousMethodRequireAuthorization(bool withNamespace) + { + var sources = TestHelpers.GetSources(""" + [AllowAnonymous] + internal sealed class AllowAnonymousClass + { + [MapGet("/allow-anon")] + [RequireAuthorization] + public static Ok Handle() + => TypedResults.Ok(); + } + """, withNamespace + ); + + var result = TestHelpers.RunGenerator(sources); + + await result.VerifyAsync("MapEndpointHandlers.g.cs") + .UseMethodName($"{nameof(ClassAllowAnonymousMethodRequireAuthorization)}_MapEndpointHandlers_With{(withNamespace ? "" : "out")}Namespace"); + } + [Theory] [InlineData(true)] [InlineData(false)]