From 348e9005d1ecf0d347ecc279c1185a0054c56145 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Fri, 5 Aug 2022 15:42:18 -0700 Subject: [PATCH 01/25] Cache stuff --- .../Core/src/IAuthorizationPolicyCache.cs | 15 +++++++++++++ .../Core/src/PublicAPI.Unshipped.txt | 4 ++++ ...tionEndpointConventionBuilderExtensions.cs | 1 + .../Policy/src/AuthorizationMiddleware.cs | 21 ++++++++++++++----- 4 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 src/Security/Authorization/Core/src/IAuthorizationPolicyCache.cs diff --git a/src/Security/Authorization/Core/src/IAuthorizationPolicyCache.cs b/src/Security/Authorization/Core/src/IAuthorizationPolicyCache.cs new file mode 100644 index 000000000000..ffd7bfa0e753 --- /dev/null +++ b/src/Security/Authorization/Core/src/IAuthorizationPolicyCache.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Authorization; + +/// +/// Represents a cache for an AuthorizationPolicy instance +/// +public class AuthorizationPolicyCache +{ + /// + /// The cached policy. + /// + public AuthorizationPolicy? Policy { get; set; } +} diff --git a/src/Security/Authorization/Core/src/PublicAPI.Unshipped.txt b/src/Security/Authorization/Core/src/PublicAPI.Unshipped.txt index 38f8cdf2c053..d315ff4af478 100644 --- a/src/Security/Authorization/Core/src/PublicAPI.Unshipped.txt +++ b/src/Security/Authorization/Core/src/PublicAPI.Unshipped.txt @@ -1,6 +1,10 @@ #nullable enable Microsoft.AspNetCore.Authorization.AuthorizationBuilder Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AuthorizationBuilder(Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> void +Microsoft.AspNetCore.Authorization.AuthorizationPolicyCache +Microsoft.AspNetCore.Authorization.AuthorizationPolicyCache.AuthorizationPolicyCache() -> void +Microsoft.AspNetCore.Authorization.AuthorizationPolicyCache.Policy.get -> Microsoft.AspNetCore.Authorization.AuthorizationPolicy? +Microsoft.AspNetCore.Authorization.AuthorizationPolicyCache.Policy.set -> void Microsoft.AspNetCore.Authorization.Infrastructure.PassThroughAuthorizationHandler.PassThroughAuthorizationHandler(Microsoft.Extensions.Options.IOptions! options) -> void static Microsoft.AspNetCore.Authorization.AuthorizationPolicy.CombineAsync(Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider! policyProvider, System.Collections.Generic.IEnumerable! authorizeData, System.Collections.Generic.IEnumerable! policies) -> System.Threading.Tasks.Task! virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddDefaultPolicy(string! name, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! diff --git a/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs b/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs index 6551f048fbba..26eec76dd517 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs @@ -166,6 +166,7 @@ private static void RequireAuthorizationCore(TBuilder builder, IEnumer { endpointBuilder.Metadata.Add(data); } + endpointBuilder.Metadata.Add(new AuthorizationPolicyCache()); }); } diff --git a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs index 0a58a00a37e0..624a367a29cc 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs @@ -47,7 +47,6 @@ public async Task Invoke(HttpContext context) } var endpoint = context.GetEndpoint(); - if (endpoint != null) { // EndpointRoutingMiddleware uses this flag to check if the Authorization middleware processed auth metadata on the endpoint. @@ -55,12 +54,24 @@ public async Task Invoke(HttpContext context) context.Items[AuthorizationMiddlewareInvokedWithEndpointKey] = AuthorizationMiddlewareWithEndpointInvokedValue; } - // IMPORTANT: Changes to authorization logic should be mirrored in MVC's AuthorizeFilter - var authorizeData = endpoint?.Metadata.GetOrderedMetadata() ?? Array.Empty(); + // Use the computed policy for this endpoint if we can + var computedPolicy = endpoint?.Metadata.GetMetadata(); + var policy = computedPolicy?.Policy; + if (policy == null) + { + // IMPORTANT: Changes to authorization logic should be mirrored in MVC's AuthorizeFilter + var authorizeData = endpoint?.Metadata.GetOrderedMetadata() ?? Array.Empty(); + + var policies = endpoint?.Metadata.GetOrderedMetadata() ?? Array.Empty(); - var policies = endpoint?.Metadata.GetOrderedMetadata() ?? Array.Empty(); + policy = await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData, policies); - var policy = await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData, policies); + // Cache the computed policy in the endpoint metadata if available + if (computedPolicy != null) + { + computedPolicy.Policy = policy; + } + } if (policy == null) { From e83673358cdfc2c834198024a4d35a5f5c012a9a Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Fri, 5 Aug 2022 16:09:05 -0700 Subject: [PATCH 02/25] Add tests --- ...ndpointConventionBuilderExtensionsTests.cs | 10 +++--- .../test/AuthorizationMiddlewareTests.cs | 33 +++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs b/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs index 8cc43a7d2f7f..9de012059572 100644 --- a/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs +++ b/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs @@ -25,7 +25,7 @@ public void RequireAuthorization_IAuthorizeData() var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); - Assert.Equal(metadata, Assert.Single(endpointModel.Metadata)); + Assert.Equal(metadata, Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); } [Fact] @@ -43,7 +43,7 @@ public void RequireAuthorization_IAuthorizeData_Empty() var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); - var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata)); + var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); Assert.Null(authMetadata.Policy); } @@ -62,7 +62,7 @@ public void RequireAuthorization_PolicyName() var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); - var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata)); + var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); Assert.Equal("policy", authMetadata.Policy); } @@ -81,7 +81,7 @@ public void RequireAuthorization_PolicyName_Empty() var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); - var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata)); + var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); Assert.Null(authMetadata.Policy); } @@ -100,7 +100,7 @@ public void RequireAuthorization_Default() var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); - var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata)); + var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); Assert.Null(authMetadata.Policy); } diff --git a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs index 131dfa71caa7..5465b910efd3 100644 --- a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs +++ b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs @@ -210,6 +210,39 @@ public async Task OnAuthorizationAsync_WillCallPolicyProvider() Assert.Equal(3, next.CalledCount); } + [Fact] + public async Task OnAuthorizationAsync_WillNotCallPolicyProviderWithCache() + { + // Arrange + var policy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build(); + var policyProvider = new Mock(); + var getPolicyCount = 0; + var getFallbackPolicyCount = 0; + policyProvider.Setup(p => p.GetPolicyAsync(It.IsAny())).ReturnsAsync(policy) + .Callback(() => getPolicyCount++); + policyProvider.Setup(p => p.GetFallbackPolicyAsync()).ReturnsAsync(policy) + .Callback(() => getFallbackPolicyCount++); + var next = new TestRequestDelegate(); + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizationPolicyCache(), new AuthorizeAttribute("whatever"))); + + // Act & Assert + await middleware.Invoke(context); + Assert.Equal(1, getPolicyCount); + Assert.Equal(0, getFallbackPolicyCount); + Assert.Equal(1, next.CalledCount); + + await middleware.Invoke(context); + Assert.Equal(1, getPolicyCount); + Assert.Equal(0, getFallbackPolicyCount); + Assert.Equal(2, next.CalledCount); + + await middleware.Invoke(context); + Assert.Equal(1, getPolicyCount); + Assert.Equal(0, getFallbackPolicyCount); + Assert.Equal(3, next.CalledCount); + } + [Fact] public async Task CanApplyPolicyDirectlyToEndpoint() { From db0b80c847034ca45f0a06bd3522085214844f9a Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Fri, 5 Aug 2022 16:23:22 -0700 Subject: [PATCH 03/25] Add tests --- ...cyCache.cs => AuthorizationPolicyCache.cs} | 0 ...tionEndpointConventionBuilderExtensions.cs | 5 ++++ ...ndpointConventionBuilderExtensionsTests.cs | 29 ++++++++++++++----- 3 files changed, 26 insertions(+), 8 deletions(-) rename src/Security/Authorization/Core/src/{IAuthorizationPolicyCache.cs => AuthorizationPolicyCache.cs} (100%) diff --git a/src/Security/Authorization/Core/src/IAuthorizationPolicyCache.cs b/src/Security/Authorization/Core/src/AuthorizationPolicyCache.cs similarity index 100% rename from src/Security/Authorization/Core/src/IAuthorizationPolicyCache.cs rename to src/Security/Authorization/Core/src/AuthorizationPolicyCache.cs diff --git a/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs b/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs index 26eec76dd517..f128fc015538 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs @@ -153,6 +153,11 @@ private static void RequirePolicyCore(TBuilder builder, AuthorizationP { endpointBuilder.Metadata.Add(new AuthorizeAttribute()); } + // Only add an policy cache if there isn't one + if (!endpointBuilder.Metadata.Any(meta => meta is AuthorizationPolicyCache)) + { + endpointBuilder.Metadata.Add(new AuthorizationPolicyCache()); + } endpointBuilder.Metadata.Add(policy); }); } diff --git a/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs b/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs index 9de012059572..26820daaeecf 100644 --- a/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs +++ b/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs @@ -25,6 +25,7 @@ public void RequireAuthorization_IAuthorizeData() var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); + Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); Assert.Equal(metadata, Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); } @@ -43,6 +44,7 @@ public void RequireAuthorization_IAuthorizeData_Empty() var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); + Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); Assert.Null(authMetadata.Policy); } @@ -62,6 +64,7 @@ public void RequireAuthorization_PolicyName() var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); + Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); Assert.Equal("policy", authMetadata.Policy); } @@ -81,6 +84,7 @@ public void RequireAuthorization_PolicyName_Empty() var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); + Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); Assert.Null(authMetadata.Policy); } @@ -100,6 +104,7 @@ public void RequireAuthorization_Default() var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); + Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); Assert.Null(authMetadata.Policy); } @@ -133,11 +138,12 @@ public void RequireAuthorization_Policy() var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); - Assert.Equal(2, endpointModel.Metadata.Count); + Assert.Equal(3, endpointModel.Metadata.Count); var authMetadata = Assert.IsAssignableFrom(endpointModel.Metadata[0]); Assert.Null(authMetadata.Policy); - Assert.Equal(policy, endpointModel.Metadata[1]); + Assert.IsType(endpointModel.Metadata[1]); + Assert.Equal(policy, endpointModel.Metadata[2]); } [Fact] @@ -156,11 +162,13 @@ public void RequireAuthorization_PolicyCallback() var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); - Assert.Equal(2, endpointModel.Metadata.Count); + Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); + Assert.Equal(3, endpointModel.Metadata.Count); var authMetadata = Assert.IsAssignableFrom(endpointModel.Metadata[0]); Assert.Null(authMetadata.Policy); - var policy = Assert.IsAssignableFrom(endpointModel.Metadata[1]); + Assert.IsType(endpointModel.Metadata[1]); + var policy = Assert.IsAssignableFrom(endpointModel.Metadata[2]); Assert.Equal(1, policy.Requirements.Count); Assert.Equal(requirement, policy.Requirements[0]); } @@ -183,10 +191,13 @@ public void RequireAuthorization_PolicyCallbackWithAuthorize() endpointModel.Metadata.Add(authorize); convention(endpointModel); + Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); + // Confirm that we don't add another authorize if one already exists - Assert.Equal(2, endpointModel.Metadata.Count); + Assert.Equal(3, endpointModel.Metadata.Count); Assert.Equal(authorize, endpointModel.Metadata[0]); - var policy = Assert.IsAssignableFrom(endpointModel.Metadata[1]); + Assert.IsType(endpointModel.Metadata[1]); + var policy = Assert.IsAssignableFrom(endpointModel.Metadata[2]); Assert.Equal(1, policy.Requirements.Count); Assert.Equal(requirement, policy.Requirements[0]); } @@ -209,10 +220,12 @@ public void RequireAuthorization_PolicyWithAuthorize() endpointModel.Metadata.Add(authorize); convention(endpointModel); + // Confirm that we don't add another authorize if one already exists - Assert.Equal(2, endpointModel.Metadata.Count); + Assert.Equal(3, endpointModel.Metadata.Count); Assert.Equal(authorize, endpointModel.Metadata[0]); - Assert.Equal(policy, endpointModel.Metadata[1]); + Assert.IsType(endpointModel.Metadata[1]); + Assert.Equal(policy, endpointModel.Metadata[2]); } class TestRequirement : IAuthorizationRequirement { } From e953612fe3d7a2741b8370c4681605607b490fd2 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Fri, 5 Aug 2022 16:26:48 -0700 Subject: [PATCH 04/25] Cleanup --- .../AuthorizationEndpointConventionBuilderExtensionsTests.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs b/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs index 26820daaeecf..d3013f40cd82 100644 --- a/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs +++ b/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs @@ -191,8 +191,6 @@ public void RequireAuthorization_PolicyCallbackWithAuthorize() endpointModel.Metadata.Add(authorize); convention(endpointModel); - Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); - // Confirm that we don't add another authorize if one already exists Assert.Equal(3, endpointModel.Metadata.Count); Assert.Equal(authorize, endpointModel.Metadata[0]); @@ -220,7 +218,6 @@ public void RequireAuthorization_PolicyWithAuthorize() endpointModel.Metadata.Add(authorize); convention(endpointModel); - // Confirm that we don't add another authorize if one already exists Assert.Equal(3, endpointModel.Metadata.Count); Assert.Equal(authorize, endpointModel.Metadata[0]); From 90b05b3aa3094935751409bc5021befbe935995d Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 8 Aug 2022 09:16:46 -0700 Subject: [PATCH 05/25] Update src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs Co-authored-by: Kahbazi --- .../src/AuthorizationEndpointConventionBuilderExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs b/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs index f128fc015538..51c5846b826f 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs @@ -153,7 +153,7 @@ private static void RequirePolicyCore(TBuilder builder, AuthorizationP { endpointBuilder.Metadata.Add(new AuthorizeAttribute()); } - // Only add an policy cache if there isn't one + // Only add a policy cache if there isn't one if (!endpointBuilder.Metadata.Any(meta => meta is AuthorizationPolicyCache)) { endpointBuilder.Metadata.Add(new AuthorizationPolicyCache()); From 28a462cf95018694a6e5e53b189f92ef4a0be587 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 10 Aug 2022 14:42:44 -0700 Subject: [PATCH 06/25] Only cache for default policy provider --- .../Policy/src/AuthorizationMiddleware.cs | 9 +++- .../test/AuthorizationMiddlewareTests.cs | 54 +++++++++++++++++-- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs index 624a367a29cc..75b069d082f6 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs @@ -23,6 +23,7 @@ public class AuthorizationMiddleware private readonly RequestDelegate _next; private readonly IAuthorizationPolicyProvider _policyProvider; + private readonly bool _cacheCombinedPolicy; /// /// Initializes a new instance of . @@ -33,6 +34,9 @@ public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvide { _next = next ?? throw new ArgumentNullException(nameof(next)); _policyProvider = policyProvider ?? throw new ArgumentNullException(nameof(policyProvider)); + + // Only try to cache combined policies for the default policy provider + _cacheCombinedPolicy = _policyProvider is DefaultAuthorizationPolicyProvider; } /// @@ -55,7 +59,10 @@ public async Task Invoke(HttpContext context) } // Use the computed policy for this endpoint if we can - var computedPolicy = endpoint?.Metadata.GetMetadata(); + var computedPolicy = _cacheCombinedPolicy + ? endpoint?.Metadata.GetMetadata() + : null; + var policy = computedPolicy?.Policy; if (policy == null) { diff --git a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs index 5465b910efd3..58371c87f0a9 100644 --- a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs +++ b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs @@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; using Moq; namespace Microsoft.AspNetCore.Authorization.Test; @@ -210,8 +211,55 @@ public async Task OnAuthorizationAsync_WillCallPolicyProvider() Assert.Equal(3, next.CalledCount); } + private class TestDefaultPolicyProvider : DefaultAuthorizationPolicyProvider + { + public int GetFallbackPolicyCount; + public int GetPolicyCount; + + public TestDefaultPolicyProvider(IOptions options) : base(options) { } + + public new Task GetFallbackPolicyAsync() + { + GetFallbackPolicyCount++; + return base.GetFallbackPolicyAsync(); + } + + public override Task GetPolicyAsync(string policyName) + { + GetPolicyCount++; + return Task.FromResult(new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build()); + } + } + + [Fact] + public async Task OnAuthorizationAsync_WillNotCallDefaultPolicyProviderWithCache() + { + // Arrange + var policy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build(); + var policyProvider = new TestDefaultPolicyProvider(Options.Create(new AuthorizationOptions())); + var next = new TestRequestDelegate(); + var middleware = CreateMiddleware(next.Invoke, policyProvider); + var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizationPolicyCache(), new AuthorizeAttribute("whatever"))); + + // Act & Assert + await middleware.Invoke(context); + Assert.Equal(1, policyProvider.GetPolicyCount); + Assert.Equal(0, policyProvider.GetFallbackPolicyCount); + Assert.Equal(1, next.CalledCount); + + await middleware.Invoke(context); + Assert.Equal(1, policyProvider.GetPolicyCount); + Assert.Equal(0, policyProvider.GetFallbackPolicyCount); + Assert.Equal(2, next.CalledCount); + + await middleware.Invoke(context); + Assert.Equal(1, policyProvider.GetPolicyCount); + Assert.Equal(0, policyProvider.GetFallbackPolicyCount); + Assert.Equal(3, next.CalledCount); + } + [Fact] - public async Task OnAuthorizationAsync_WillNotCallPolicyProviderWithCache() + public async Task OnAuthorizationAsync_WillCallCustomPolicyProviderWithCache() { // Arrange var policy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build(); @@ -233,12 +281,12 @@ public async Task OnAuthorizationAsync_WillNotCallPolicyProviderWithCache() Assert.Equal(1, next.CalledCount); await middleware.Invoke(context); - Assert.Equal(1, getPolicyCount); + Assert.Equal(2, getPolicyCount); Assert.Equal(0, getFallbackPolicyCount); Assert.Equal(2, next.CalledCount); await middleware.Invoke(context); - Assert.Equal(1, getPolicyCount); + Assert.Equal(3, getPolicyCount); Assert.Equal(0, getFallbackPolicyCount); Assert.Equal(3, next.CalledCount); } From d3c0b475b33298fab36d0a7263f091d4b99eaa69 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 10 Aug 2022 16:16:32 -0700 Subject: [PATCH 07/25] Update src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs Co-authored-by: David Fowler --- .../Authorization/Policy/src/AuthorizationMiddleware.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs index 75b069d082f6..feb0b310a295 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs @@ -36,7 +36,7 @@ public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvide _policyProvider = policyProvider ?? throw new ArgumentNullException(nameof(policyProvider)); // Only try to cache combined policies for the default policy provider - _cacheCombinedPolicy = _policyProvider is DefaultAuthorizationPolicyProvider; + _cacheCombinedPolicy = _policyProvider.GetType() == typeof(DefaultAuthorizationPolicyProvider); } /// From 0ef7da1b4a0f3dcba327043f59dffb553341bcdf Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 10 Aug 2022 16:49:54 -0700 Subject: [PATCH 08/25] Update test --- .../Authorization/test/AuthorizationMiddlewareTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs index 58371c87f0a9..cdc78599c022 100644 --- a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs +++ b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs @@ -232,7 +232,7 @@ public override Task GetPolicyAsync(string policyName) } [Fact] - public async Task OnAuthorizationAsync_WillNotCallDefaultPolicyProviderWithCache() + public async Task OnAuthorizationAsync_WillCallDerviedDefaultPolicyProviderWithCache() { // Arrange var policy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build(); @@ -248,12 +248,12 @@ public async Task OnAuthorizationAsync_WillNotCallDefaultPolicyProviderWithCache Assert.Equal(1, next.CalledCount); await middleware.Invoke(context); - Assert.Equal(1, policyProvider.GetPolicyCount); + Assert.Equal(2, policyProvider.GetPolicyCount); Assert.Equal(0, policyProvider.GetFallbackPolicyCount); Assert.Equal(2, next.CalledCount); await middleware.Invoke(context); - Assert.Equal(1, policyProvider.GetPolicyCount); + Assert.Equal(3, policyProvider.GetPolicyCount); Assert.Equal(0, policyProvider.GetFallbackPolicyCount); Assert.Equal(3, next.CalledCount); } From 20eee123ce13e6fd6b7682310be7a994682b60da Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 10 Aug 2022 23:52:54 -0700 Subject: [PATCH 09/25] Add test for caching path too --- .../Policy/src/AuthorizationMiddleware.cs | 5 +-- .../test/AuthorizationMiddlewareTests.cs | 33 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs index feb0b310a295..43b42caafa3e 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs @@ -23,7 +23,6 @@ public class AuthorizationMiddleware private readonly RequestDelegate _next; private readonly IAuthorizationPolicyProvider _policyProvider; - private readonly bool _cacheCombinedPolicy; /// /// Initializes a new instance of . @@ -36,9 +35,11 @@ public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvide _policyProvider = policyProvider ?? throw new ArgumentNullException(nameof(policyProvider)); // Only try to cache combined policies for the default policy provider - _cacheCombinedPolicy = _policyProvider.GetType() == typeof(DefaultAuthorizationPolicyProvider); + CacheCombinedPolicy = _policyProvider.GetType() == typeof(DefaultAuthorizationPolicyProvider); } + internal bool CacheCombinedPolicy { get; set; } + /// /// Invokes the middleware performing authorization. /// diff --git a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs index cdc78599c022..bffd42f10606 100644 --- a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs +++ b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs @@ -211,6 +211,39 @@ public async Task OnAuthorizationAsync_WillCallPolicyProvider() Assert.Equal(3, next.CalledCount); } + [Fact] + public async Task OnAuthorizationAsync_WillNotCallPolicyProviderWithCache() + { + // Arrange + var policy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build(); + var policyProvider = new Mock(); + var getPolicyCount = 0; + var getFallbackPolicyCount = 0; + policyProvider.Setup(p => p.GetPolicyAsync(It.IsAny())).ReturnsAsync(policy) + .Callback(() => getPolicyCount++); + policyProvider.Setup(p => p.GetFallbackPolicyAsync()).ReturnsAsync(policy) + .Callback(() => getFallbackPolicyCount++); + var next = new TestRequestDelegate(); + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizationPolicyCache(), new AuthorizeAttribute("whatever"))); + + // Act & Assert + await middleware.Invoke(context); + Assert.Equal(1, getPolicyCount); + Assert.Equal(0, getFallbackPolicyCount); + Assert.Equal(1, next.CalledCount); + + await middleware.Invoke(context); + Assert.Equal(1, getPolicyCount); + Assert.Equal(0, getFallbackPolicyCount); + Assert.Equal(2, next.CalledCount); + + await middleware.Invoke(context); + Assert.Equal(1, getPolicyCount); + Assert.Equal(0, getFallbackPolicyCount); + Assert.Equal(3, next.CalledCount); + } + private class TestDefaultPolicyProvider : DefaultAuthorizationPolicyProvider { public int GetFallbackPolicyCount; From 736e3c3c0771eb85de9a3a532ff6b55acf2b045b Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 10 Aug 2022 23:56:21 -0700 Subject: [PATCH 10/25] Fix test --- src/Security/Authorization/Policy/src/AssemblyInfo.cs | 6 ++++++ .../Authorization/Policy/src/AuthorizationMiddleware.cs | 2 +- .../Authorization/test/AuthorizationMiddlewareTests.cs | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 src/Security/Authorization/Policy/src/AssemblyInfo.cs diff --git a/src/Security/Authorization/Policy/src/AssemblyInfo.cs b/src/Security/Authorization/Policy/src/AssemblyInfo.cs new file mode 100644 index 000000000000..a03a5b6bd469 --- /dev/null +++ b/src/Security/Authorization/Policy/src/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Authorization.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs index 43b42caafa3e..891d68307a67 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs @@ -60,7 +60,7 @@ public async Task Invoke(HttpContext context) } // Use the computed policy for this endpoint if we can - var computedPolicy = _cacheCombinedPolicy + var computedPolicy = CacheCombinedPolicy ? endpoint?.Metadata.GetMetadata() : null; diff --git a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs index bffd42f10606..d5d3d48629b8 100644 --- a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs +++ b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs @@ -225,6 +225,7 @@ public async Task OnAuthorizationAsync_WillNotCallPolicyProviderWithCache() .Callback(() => getFallbackPolicyCount++); var next = new TestRequestDelegate(); var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + middleware.CacheCombinedPolicy = true; var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizationPolicyCache(), new AuthorizeAttribute("whatever"))); // Act & Assert From 72e3c0ffc7c29e6a6f2d4d6c0f41b9943e815037 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 10 Aug 2022 23:59:14 -0700 Subject: [PATCH 11/25] Check before adding policy cache --- .../src/AuthorizationEndpointConventionBuilderExtensions.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs b/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs index 51c5846b826f..751610506020 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs @@ -171,7 +171,11 @@ private static void RequireAuthorizationCore(TBuilder builder, IEnumer { endpointBuilder.Metadata.Add(data); } - endpointBuilder.Metadata.Add(new AuthorizationPolicyCache()); + // Only add a policy cache if there isn't one + if (!endpointBuilder.Metadata.Any(meta => meta is AuthorizationPolicyCache)) + { + endpointBuilder.Metadata.Add(new AuthorizationPolicyCache()); + } }); } From 0c7d11775fd2334c2acae667fc05448397ee41c6 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Fri, 12 Aug 2022 12:15:30 -0700 Subject: [PATCH 12/25] Switch to WithAuthorizationPolicyCache --- .../Core/src/AuthorizationPolicyCache.cs | 2 +- .../Core/src/Properties/AssemblyInfo.cs | 2 ++ .../Core/src/PublicAPI.Unshipped.txt | 4 --- .../src/AuthorizationAppBuilderExtensions.cs | 2 +- ...tionEndpointConventionBuilderExtensions.cs | 31 ++++++++++++------- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/Security/Authorization/Core/src/AuthorizationPolicyCache.cs b/src/Security/Authorization/Core/src/AuthorizationPolicyCache.cs index ffd7bfa0e753..faa69e1b8b2f 100644 --- a/src/Security/Authorization/Core/src/AuthorizationPolicyCache.cs +++ b/src/Security/Authorization/Core/src/AuthorizationPolicyCache.cs @@ -6,7 +6,7 @@ namespace Microsoft.AspNetCore.Authorization; /// /// Represents a cache for an AuthorizationPolicy instance /// -public class AuthorizationPolicyCache +internal class AuthorizationPolicyCache { /// /// The cached policy. diff --git a/src/Security/Authorization/Core/src/Properties/AssemblyInfo.cs b/src/Security/Authorization/Core/src/Properties/AssemblyInfo.cs index c31eb0767a6e..596b6dd9c259 100644 --- a/src/Security/Authorization/Core/src/Properties/AssemblyInfo.cs +++ b/src/Security/Authorization/Core/src/Properties/AssemblyInfo.cs @@ -5,6 +5,8 @@ using System.Runtime.CompilerServices; using Microsoft.AspNetCore.Authorization; +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Authorization.Policy, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] + // Microsoft.AspNetCore.Metadata [assembly: TypeForwardedTo(typeof(IAuthorizeData))] [assembly: TypeForwardedTo(typeof(IAllowAnonymous))] diff --git a/src/Security/Authorization/Core/src/PublicAPI.Unshipped.txt b/src/Security/Authorization/Core/src/PublicAPI.Unshipped.txt index d315ff4af478..38f8cdf2c053 100644 --- a/src/Security/Authorization/Core/src/PublicAPI.Unshipped.txt +++ b/src/Security/Authorization/Core/src/PublicAPI.Unshipped.txt @@ -1,10 +1,6 @@ #nullable enable Microsoft.AspNetCore.Authorization.AuthorizationBuilder Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AuthorizationBuilder(Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> void -Microsoft.AspNetCore.Authorization.AuthorizationPolicyCache -Microsoft.AspNetCore.Authorization.AuthorizationPolicyCache.AuthorizationPolicyCache() -> void -Microsoft.AspNetCore.Authorization.AuthorizationPolicyCache.Policy.get -> Microsoft.AspNetCore.Authorization.AuthorizationPolicy? -Microsoft.AspNetCore.Authorization.AuthorizationPolicyCache.Policy.set -> void Microsoft.AspNetCore.Authorization.Infrastructure.PassThroughAuthorizationHandler.PassThroughAuthorizationHandler(Microsoft.Extensions.Options.IOptions! options) -> void static Microsoft.AspNetCore.Authorization.AuthorizationPolicy.CombineAsync(Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider! policyProvider, System.Collections.Generic.IEnumerable! authorizeData, System.Collections.Generic.IEnumerable! policies) -> System.Threading.Tasks.Task! virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddDefaultPolicy(string! name, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! diff --git a/src/Security/Authorization/Policy/src/AuthorizationAppBuilderExtensions.cs b/src/Security/Authorization/Policy/src/AuthorizationAppBuilderExtensions.cs index 74b23c01f871..3c675d9ed6b8 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationAppBuilderExtensions.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationAppBuilderExtensions.cs @@ -42,7 +42,7 @@ private static void VerifyServicesRegistered(IApplicationBuilder app) // We use the AuthorizationPolicyMarkerService to ensure all the services were added. if (app.ApplicationServices.GetService(typeof(AuthorizationPolicyMarkerService)) == null) { - throw new InvalidOperationException(Resources.FormatException_UnableToFindServices( + throw new InvalidOperationException(Authorization.Policy.Resources.FormatException_UnableToFindServices( nameof(IServiceCollection), nameof(PolicyServiceCollectionExtensions.AddAuthorization))); } diff --git a/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs b/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs index 751610506020..de167c7f19fd 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs @@ -143,6 +143,24 @@ public static TBuilder AllowAnonymous(this TBuilder builder) where TBu return builder; } + /// + /// Enables AuthorizationMiddleware to cache the policy for a particular endpoint so its only computed once when using the default DefaultAuthorizationPolicyProvider. + /// + /// The endpoint convention builder. + /// The original convention builder parameter. + public static TBuilder WithAuthorizationCache(this TBuilder builder) where TBuilder : IEndpointConventionBuilder + { + builder.Add(endpointBuilder => + { + // Only add a policy cache if there isn't one + if (!endpointBuilder.Metadata.Any(meta => meta is AuthorizationPolicyCache)) + { + endpointBuilder.Metadata.Add(new AuthorizationPolicyCache()); + } + }); + return builder; + } + private static void RequirePolicyCore(TBuilder builder, AuthorizationPolicy policy) where TBuilder : IEndpointConventionBuilder { @@ -153,13 +171,9 @@ private static void RequirePolicyCore(TBuilder builder, AuthorizationP { endpointBuilder.Metadata.Add(new AuthorizeAttribute()); } - // Only add a policy cache if there isn't one - if (!endpointBuilder.Metadata.Any(meta => meta is AuthorizationPolicyCache)) - { - endpointBuilder.Metadata.Add(new AuthorizationPolicyCache()); - } endpointBuilder.Metadata.Add(policy); }); + builder.WithAuthorizationCache(); } private static void RequireAuthorizationCore(TBuilder builder, IEnumerable authorizeData) @@ -171,12 +185,7 @@ private static void RequireAuthorizationCore(TBuilder builder, IEnumer { endpointBuilder.Metadata.Add(data); } - // Only add a policy cache if there isn't one - if (!endpointBuilder.Metadata.Any(meta => meta is AuthorizationPolicyCache)) - { - endpointBuilder.Metadata.Add(new AuthorizationPolicyCache()); - } }); + builder.WithAuthorizationCache(); } - } From fac48795a529428444152db504a761acc77bfe46 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Fri, 12 Aug 2022 13:07:05 -0700 Subject: [PATCH 13/25] Update PublicAPI.Unshipped.txt --- src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt b/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt index 4150e1ff1c75..78970a31cc1a 100644 --- a/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt +++ b/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt @@ -1,4 +1,5 @@ #nullable enable static Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.RequireAuthorization(this TBuilder builder, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> TBuilder static Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.RequireAuthorization(this TBuilder builder, System.Action! configurePolicy) -> TBuilder +static Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.WithAuthorizationCache(this TBuilder builder) -> TBuilder static Microsoft.Extensions.DependencyInjection.PolicyServiceCollectionExtensions.AddAuthorizationBuilder(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! From 61d9564f237058cf8008770ddbcb0b5f6cd3d3df Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Fri, 12 Aug 2022 13:30:42 -0700 Subject: [PATCH 14/25] Update test --- src/Security/Authorization/Core/src/Properties/AssemblyInfo.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Security/Authorization/Core/src/Properties/AssemblyInfo.cs b/src/Security/Authorization/Core/src/Properties/AssemblyInfo.cs index 596b6dd9c259..8996ac7726f9 100644 --- a/src/Security/Authorization/Core/src/Properties/AssemblyInfo.cs +++ b/src/Security/Authorization/Core/src/Properties/AssemblyInfo.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Authorization; [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Authorization.Policy, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Authorization.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] // Microsoft.AspNetCore.Metadata [assembly: TypeForwardedTo(typeof(IAuthorizeData))] From d95b752460434358061923fbe808bad98b559a0a Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Fri, 12 Aug 2022 14:03:22 -0700 Subject: [PATCH 15/25] Fix tests --- ...ndpointConventionBuilderExtensionsTests.cs | 91 +++++++++++-------- 1 file changed, 55 insertions(+), 36 deletions(-) diff --git a/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs b/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs index d3013f40cd82..a3893b71a41a 100644 --- a/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs +++ b/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs @@ -20,10 +20,12 @@ public void RequireAuthorization_IAuthorizeData() builder.RequireAuthorization(metadata); // Assert - var convention = Assert.Single(builder.Conventions); - var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); - convention(endpointModel); + Assert.Equal(2, builder.Conventions.Count); + foreach (var convention in builder.Conventions) + { + convention(endpointModel); + } Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); Assert.Equal(metadata, Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); @@ -39,10 +41,12 @@ public void RequireAuthorization_IAuthorizeData_Empty() builder.RequireAuthorization(Array.Empty()); // Assert - var convention = Assert.Single(builder.Conventions); - var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); - convention(endpointModel); + Assert.Equal(2, builder.Conventions.Count); + foreach (var convention in builder.Conventions) + { + convention(endpointModel); + } Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); @@ -59,10 +63,12 @@ public void RequireAuthorization_PolicyName() builder.RequireAuthorization("policy"); // Assert - var convention = Assert.Single(builder.Conventions); - var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); - convention(endpointModel); + Assert.Equal(2, builder.Conventions.Count); + foreach (var convention in builder.Conventions) + { + convention(endpointModel); + } Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); @@ -79,10 +85,12 @@ public void RequireAuthorization_PolicyName_Empty() builder.RequireAuthorization(Array.Empty()); // Assert - var convention = Assert.Single(builder.Conventions); - var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); - convention(endpointModel); + Assert.Equal(2, builder.Conventions.Count); + foreach (var convention in builder.Conventions) + { + convention(endpointModel); + } Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); @@ -99,10 +107,12 @@ public void RequireAuthorization_Default() builder.RequireAuthorization(); // Assert - var convention = Assert.Single(builder.Conventions); - var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); - convention(endpointModel); + Assert.Equal(2, builder.Conventions.Count); + foreach (var convention in builder.Conventions) + { + convention(endpointModel); + } Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); @@ -133,17 +143,19 @@ public void RequireAuthorization_Policy() builder.RequireAuthorization(policy); // Assert - var convention = Assert.Single(builder.Conventions); - var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); - convention(endpointModel); + Assert.Equal(2, builder.Conventions.Count); + foreach (var convention in builder.Conventions) + { + convention(endpointModel); + } Assert.Equal(3, endpointModel.Metadata.Count); var authMetadata = Assert.IsAssignableFrom(endpointModel.Metadata[0]); Assert.Null(authMetadata.Policy); - Assert.IsType(endpointModel.Metadata[1]); - Assert.Equal(policy, endpointModel.Metadata[2]); + Assert.Equal(policy, endpointModel.Metadata[1]); + Assert.IsType(endpointModel.Metadata[2]); } [Fact] @@ -157,20 +169,22 @@ public void RequireAuthorization_PolicyCallback() builder.RequireAuthorization(policyBuilder => policyBuilder.Requirements.Add(requirement)); // Assert - var convention = Assert.Single(builder.Conventions); - var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); - convention(endpointModel); + Assert.Equal(2, builder.Conventions.Count); + foreach (var convention in builder.Conventions) + { + convention(endpointModel); + } Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); Assert.Equal(3, endpointModel.Metadata.Count); var authMetadata = Assert.IsAssignableFrom(endpointModel.Metadata[0]); Assert.Null(authMetadata.Policy); - Assert.IsType(endpointModel.Metadata[1]); - var policy = Assert.IsAssignableFrom(endpointModel.Metadata[2]); + var policy = Assert.IsAssignableFrom(endpointModel.Metadata[1]); Assert.Equal(1, policy.Requirements.Count); Assert.Equal(requirement, policy.Requirements[0]); + Assert.IsType(endpointModel.Metadata[2]); } [Fact] @@ -185,19 +199,21 @@ public void RequireAuthorization_PolicyCallbackWithAuthorize() builder.RequireAuthorization(policyBuilder => policyBuilder.Requirements.Add(requirement)); // Assert - var convention = Assert.Single(builder.Conventions); - var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); endpointModel.Metadata.Add(authorize); - convention(endpointModel); + Assert.Equal(2, builder.Conventions.Count); + foreach (var convention in builder.Conventions) + { + convention(endpointModel); + } // Confirm that we don't add another authorize if one already exists Assert.Equal(3, endpointModel.Metadata.Count); Assert.Equal(authorize, endpointModel.Metadata[0]); - Assert.IsType(endpointModel.Metadata[1]); - var policy = Assert.IsAssignableFrom(endpointModel.Metadata[2]); + var policy = Assert.IsAssignableFrom(endpointModel.Metadata[1]); Assert.Equal(1, policy.Requirements.Count); Assert.Equal(requirement, policy.Requirements[0]); + Assert.IsType(endpointModel.Metadata[2]); } [Fact] @@ -212,17 +228,21 @@ public void RequireAuthorization_PolicyWithAuthorize() builder.RequireAuthorization(policy); // Assert - var convention = Assert.Single(builder.Conventions); var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); endpointModel.Metadata.Add(authorize); - convention(endpointModel); + + Assert.Equal(2, builder.Conventions.Count); + foreach (var convention in builder.Conventions) + { + convention(endpointModel); + } // Confirm that we don't add another authorize if one already exists Assert.Equal(3, endpointModel.Metadata.Count); Assert.Equal(authorize, endpointModel.Metadata[0]); - Assert.IsType(endpointModel.Metadata[1]); - Assert.Equal(policy, endpointModel.Metadata[2]); + Assert.Equal(policy, endpointModel.Metadata[1]); + Assert.IsType(endpointModel.Metadata[2]); } class TestRequirement : IAuthorizationRequirement { } @@ -237,9 +257,8 @@ public void AllowAnonymous_Default() builder.AllowAnonymous(); // Assert - var convention = Assert.Single(builder.Conventions); - var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); + var convention = Assert.Single(builder.Conventions); convention(endpointModel); Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata)); From 28a5f647e0c12f1670cd63ee34cbfb55a2556537 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Sat, 13 Aug 2022 22:39:19 -0700 Subject: [PATCH 16/25] Add default interface implementation for CanCache --- .../src/DefaultAuthorizationPolicyProvider.cs | 7 + .../Core/src/IAuthorizationPolicyProvider.cs | 7 + .../Microsoft.AspNetCore.Authorization.csproj | 5 + .../net462}/PublicAPI.Shipped.txt | 0 .../net462}/PublicAPI.Unshipped.txt | 0 .../PublicAPI/net7.0/PublicAPI.Shipped.txt | 171 ++++++++++++++++++ .../PublicAPI/net7.0/PublicAPI.Unshipped.txt | 17 ++ .../netstandard2.0/PublicAPI.Shipped.txt | 171 ++++++++++++++++++ .../netstandard2.0/PublicAPI.Unshipped.txt | 15 ++ .../Policy/src/AuthorizationMiddleware.cs | 4 + .../test/AuthorizationMiddlewareTests.cs | 25 ++- 11 files changed, 416 insertions(+), 6 deletions(-) rename src/Security/Authorization/Core/src/{ => PublicAPI/net462}/PublicAPI.Shipped.txt (100%) rename src/Security/Authorization/Core/src/{ => PublicAPI/net462}/PublicAPI.Unshipped.txt (100%) create mode 100644 src/Security/Authorization/Core/src/PublicAPI/net7.0/PublicAPI.Shipped.txt create mode 100644 src/Security/Authorization/Core/src/PublicAPI/net7.0/PublicAPI.Unshipped.txt create mode 100644 src/Security/Authorization/Core/src/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt create mode 100644 src/Security/Authorization/Core/src/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt diff --git a/src/Security/Authorization/Core/src/DefaultAuthorizationPolicyProvider.cs b/src/Security/Authorization/Core/src/DefaultAuthorizationPolicyProvider.cs index 02b226adcd67..47c1a856ab2b 100644 --- a/src/Security/Authorization/Core/src/DefaultAuthorizationPolicyProvider.cs +++ b/src/Security/Authorization/Core/src/DefaultAuthorizationPolicyProvider.cs @@ -71,4 +71,11 @@ public Task GetDefaultPolicyAsync() // A change to either of these behaviors would require shipping a patch of MVC as well. return Task.FromResult(_options.GetPolicy(policyName)); } + +#if NETCOREAPP + /// + /// Determines if policies from this provider can be cached, which is true for this provider. + /// + public virtual bool CanCachePolicy => true; +#endif } diff --git a/src/Security/Authorization/Core/src/IAuthorizationPolicyProvider.cs b/src/Security/Authorization/Core/src/IAuthorizationPolicyProvider.cs index 7ab90400ef00..361256937265 100644 --- a/src/Security/Authorization/Core/src/IAuthorizationPolicyProvider.cs +++ b/src/Security/Authorization/Core/src/IAuthorizationPolicyProvider.cs @@ -28,4 +28,11 @@ public interface IAuthorizationPolicyProvider /// /// The fallback authorization policy. Task GetFallbackPolicyAsync(); + +#if NETCOREAPP + /// + /// Determines if policies from this provider can be cached, defaults to false. + /// + bool CanCachePolicy => false; +#endif } diff --git a/src/Security/Authorization/Core/src/Microsoft.AspNetCore.Authorization.csproj b/src/Security/Authorization/Core/src/Microsoft.AspNetCore.Authorization.csproj index db4b9d2870cc..77ce4849404b 100644 --- a/src/Security/Authorization/Core/src/Microsoft.AspNetCore.Authorization.csproj +++ b/src/Security/Authorization/Core/src/Microsoft.AspNetCore.Authorization.csproj @@ -20,4 +20,9 @@ Microsoft.AspNetCore.Authorization.AuthorizeAttribute + + + + + diff --git a/src/Security/Authorization/Core/src/PublicAPI.Shipped.txt b/src/Security/Authorization/Core/src/PublicAPI/net462/PublicAPI.Shipped.txt similarity index 100% rename from src/Security/Authorization/Core/src/PublicAPI.Shipped.txt rename to src/Security/Authorization/Core/src/PublicAPI/net462/PublicAPI.Shipped.txt diff --git a/src/Security/Authorization/Core/src/PublicAPI.Unshipped.txt b/src/Security/Authorization/Core/src/PublicAPI/net462/PublicAPI.Unshipped.txt similarity index 100% rename from src/Security/Authorization/Core/src/PublicAPI.Unshipped.txt rename to src/Security/Authorization/Core/src/PublicAPI/net462/PublicAPI.Unshipped.txt diff --git a/src/Security/Authorization/Core/src/PublicAPI/net7.0/PublicAPI.Shipped.txt b/src/Security/Authorization/Core/src/PublicAPI/net7.0/PublicAPI.Shipped.txt new file mode 100644 index 000000000000..cf34e70c4b92 --- /dev/null +++ b/src/Security/Authorization/Core/src/PublicAPI/net7.0/PublicAPI.Shipped.txt @@ -0,0 +1,171 @@ +#nullable enable +Microsoft.AspNetCore.Authorization.DefaultAuthorizationPolicyProvider.DefaultAuthorizationPolicyProvider(Microsoft.Extensions.Options.IOptions! options) -> void +Microsoft.AspNetCore.Authorization.DefaultAuthorizationService.DefaultAuthorizationService(Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider! policyProvider, Microsoft.AspNetCore.Authorization.IAuthorizationHandlerProvider! handlers, Microsoft.Extensions.Logging.ILogger! logger, Microsoft.AspNetCore.Authorization.IAuthorizationHandlerContextFactory! contextFactory, Microsoft.AspNetCore.Authorization.IAuthorizationEvaluator! evaluator, Microsoft.Extensions.Options.IOptions! options) -> void +abstract Microsoft.AspNetCore.Authorization.AuthorizationHandler.HandleRequirementAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context, TRequirement requirement, TResource resource) -> System.Threading.Tasks.Task! +abstract Microsoft.AspNetCore.Authorization.AuthorizationHandler.HandleRequirementAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context, TRequirement requirement) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute +Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute.AllowAnonymousAttribute() -> void +Microsoft.AspNetCore.Authorization.AuthorizationFailure +Microsoft.AspNetCore.Authorization.AuthorizationFailure.FailCalled.get -> bool +Microsoft.AspNetCore.Authorization.AuthorizationFailure.FailedRequirements.get -> System.Collections.Generic.IEnumerable! +Microsoft.AspNetCore.Authorization.AuthorizationFailure.FailureReasons.get -> System.Collections.Generic.IEnumerable! +Microsoft.AspNetCore.Authorization.AuthorizationFailureReason +Microsoft.AspNetCore.Authorization.AuthorizationFailureReason.AuthorizationFailureReason(Microsoft.AspNetCore.Authorization.IAuthorizationHandler! handler, string! message) -> void +Microsoft.AspNetCore.Authorization.AuthorizationFailureReason.Handler.get -> Microsoft.AspNetCore.Authorization.IAuthorizationHandler! +Microsoft.AspNetCore.Authorization.AuthorizationFailureReason.Message.get -> string! +Microsoft.AspNetCore.Authorization.AuthorizationHandler +Microsoft.AspNetCore.Authorization.AuthorizationHandler.AuthorizationHandler() -> void +Microsoft.AspNetCore.Authorization.AuthorizationHandler +Microsoft.AspNetCore.Authorization.AuthorizationHandler.AuthorizationHandler() -> void +Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext +Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.AuthorizationHandlerContext(System.Collections.Generic.IEnumerable! requirements, System.Security.Claims.ClaimsPrincipal! user, object? resource) -> void +Microsoft.AspNetCore.Authorization.AuthorizationOptions +Microsoft.AspNetCore.Authorization.AuthorizationOptions.AddPolicy(string! name, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> void +Microsoft.AspNetCore.Authorization.AuthorizationOptions.AddPolicy(string! name, System.Action! configurePolicy) -> void +Microsoft.AspNetCore.Authorization.AuthorizationOptions.AuthorizationOptions() -> void +Microsoft.AspNetCore.Authorization.AuthorizationOptions.DefaultPolicy.get -> Microsoft.AspNetCore.Authorization.AuthorizationPolicy! +Microsoft.AspNetCore.Authorization.AuthorizationOptions.DefaultPolicy.set -> void +Microsoft.AspNetCore.Authorization.AuthorizationOptions.FallbackPolicy.get -> Microsoft.AspNetCore.Authorization.AuthorizationPolicy? +Microsoft.AspNetCore.Authorization.AuthorizationOptions.FallbackPolicy.set -> void +Microsoft.AspNetCore.Authorization.AuthorizationOptions.GetPolicy(string! name) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicy? +Microsoft.AspNetCore.Authorization.AuthorizationOptions.InvokeHandlersAfterFailure.get -> bool +Microsoft.AspNetCore.Authorization.AuthorizationOptions.InvokeHandlersAfterFailure.set -> void +Microsoft.AspNetCore.Authorization.AuthorizationPolicy +Microsoft.AspNetCore.Authorization.AuthorizationPolicy.AuthenticationSchemes.get -> System.Collections.Generic.IReadOnlyList! +Microsoft.AspNetCore.Authorization.AuthorizationPolicy.AuthorizationPolicy(System.Collections.Generic.IEnumerable! requirements, System.Collections.Generic.IEnumerable! authenticationSchemes) -> void +Microsoft.AspNetCore.Authorization.AuthorizationPolicy.Requirements.get -> System.Collections.Generic.IReadOnlyList! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.AddAuthenticationSchemes(params string![]! schemes) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.AddRequirements(params Microsoft.AspNetCore.Authorization.IAuthorizationRequirement![]! requirements) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.AuthenticationSchemes.get -> System.Collections.Generic.IList! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.AuthenticationSchemes.set -> void +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.AuthorizationPolicyBuilder(Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> void +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.AuthorizationPolicyBuilder(params string![]! authenticationSchemes) -> void +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.Build() -> Microsoft.AspNetCore.Authorization.AuthorizationPolicy! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.Combine(Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireAssertion(System.Func! handler) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireAssertion(System.Func!>! handler) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireAuthenticatedUser() -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireClaim(string! claimType) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireClaim(string! claimType, params string![]! allowedValues) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireClaim(string! claimType, System.Collections.Generic.IEnumerable! allowedValues) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.Requirements.get -> System.Collections.Generic.IList! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.Requirements.set -> void +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireRole(params string![]! roles) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireRole(System.Collections.Generic.IEnumerable! roles) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireUserName(string! userName) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationResult +Microsoft.AspNetCore.Authorization.AuthorizationResult.Failure.get -> Microsoft.AspNetCore.Authorization.AuthorizationFailure? +Microsoft.AspNetCore.Authorization.AuthorizationResult.Succeeded.get -> bool +Microsoft.AspNetCore.Authorization.AuthorizationServiceExtensions +Microsoft.AspNetCore.Authorization.AuthorizeAttribute +Microsoft.AspNetCore.Authorization.AuthorizeAttribute.AuthenticationSchemes.get -> string? +Microsoft.AspNetCore.Authorization.AuthorizeAttribute.AuthenticationSchemes.set -> void +Microsoft.AspNetCore.Authorization.AuthorizeAttribute.AuthorizeAttribute() -> void +Microsoft.AspNetCore.Authorization.AuthorizeAttribute.AuthorizeAttribute(string! policy) -> void +Microsoft.AspNetCore.Authorization.AuthorizeAttribute.Policy.get -> string? +Microsoft.AspNetCore.Authorization.AuthorizeAttribute.Policy.set -> void +Microsoft.AspNetCore.Authorization.AuthorizeAttribute.Roles.get -> string? +Microsoft.AspNetCore.Authorization.AuthorizeAttribute.Roles.set -> void +Microsoft.AspNetCore.Authorization.DefaultAuthorizationEvaluator +Microsoft.AspNetCore.Authorization.DefaultAuthorizationEvaluator.DefaultAuthorizationEvaluator() -> void +Microsoft.AspNetCore.Authorization.DefaultAuthorizationEvaluator.Evaluate(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context) -> Microsoft.AspNetCore.Authorization.AuthorizationResult! +Microsoft.AspNetCore.Authorization.DefaultAuthorizationHandlerContextFactory +Microsoft.AspNetCore.Authorization.DefaultAuthorizationHandlerContextFactory.DefaultAuthorizationHandlerContextFactory() -> void +Microsoft.AspNetCore.Authorization.DefaultAuthorizationHandlerProvider +Microsoft.AspNetCore.Authorization.DefaultAuthorizationHandlerProvider.DefaultAuthorizationHandlerProvider(System.Collections.Generic.IEnumerable! handlers) -> void +Microsoft.AspNetCore.Authorization.DefaultAuthorizationHandlerProvider.GetHandlersAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context) -> System.Threading.Tasks.Task!>! +Microsoft.AspNetCore.Authorization.DefaultAuthorizationPolicyProvider +Microsoft.AspNetCore.Authorization.DefaultAuthorizationPolicyProvider.GetDefaultPolicyAsync() -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.DefaultAuthorizationPolicyProvider.GetFallbackPolicyAsync() -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.DefaultAuthorizationService +Microsoft.AspNetCore.Authorization.IAllowAnonymous (forwarded, contained in Microsoft.AspNetCore.Metadata) +Microsoft.AspNetCore.Authorization.IAuthorizationEvaluator +Microsoft.AspNetCore.Authorization.IAuthorizationEvaluator.Evaluate(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context) -> Microsoft.AspNetCore.Authorization.AuthorizationResult! +Microsoft.AspNetCore.Authorization.IAuthorizationHandler +Microsoft.AspNetCore.Authorization.IAuthorizationHandler.HandleAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.IAuthorizationHandlerContextFactory +Microsoft.AspNetCore.Authorization.IAuthorizationHandlerContextFactory.CreateContext(System.Collections.Generic.IEnumerable! requirements, System.Security.Claims.ClaimsPrincipal! user, object? resource) -> Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! +Microsoft.AspNetCore.Authorization.IAuthorizationHandlerProvider +Microsoft.AspNetCore.Authorization.IAuthorizationHandlerProvider.GetHandlersAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context) -> System.Threading.Tasks.Task!>! +Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider +Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider.GetDefaultPolicyAsync() -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider.GetFallbackPolicyAsync() -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider.GetPolicyAsync(string! policyName) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.IAuthorizationRequirement +Microsoft.AspNetCore.Authorization.IAuthorizationService +Microsoft.AspNetCore.Authorization.IAuthorizationService.AuthorizeAsync(System.Security.Claims.ClaimsPrincipal! user, object? resource, string! policyName) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.IAuthorizationService.AuthorizeAsync(System.Security.Claims.ClaimsPrincipal! user, object? resource, System.Collections.Generic.IEnumerable! requirements) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.IAuthorizeData (forwarded, contained in Microsoft.AspNetCore.Metadata) +Microsoft.AspNetCore.Authorization.IAuthorizeData.AuthenticationSchemes.get -> string? (forwarded, contained in Microsoft.AspNetCore.Metadata) +Microsoft.AspNetCore.Authorization.IAuthorizeData.AuthenticationSchemes.set -> void (forwarded, contained in Microsoft.AspNetCore.Metadata) +Microsoft.AspNetCore.Authorization.IAuthorizeData.Policy.get -> string? (forwarded, contained in Microsoft.AspNetCore.Metadata) +Microsoft.AspNetCore.Authorization.IAuthorizeData.Policy.set -> void (forwarded, contained in Microsoft.AspNetCore.Metadata) +Microsoft.AspNetCore.Authorization.IAuthorizeData.Roles.get -> string? (forwarded, contained in Microsoft.AspNetCore.Metadata) +Microsoft.AspNetCore.Authorization.IAuthorizeData.Roles.set -> void (forwarded, contained in Microsoft.AspNetCore.Metadata) +Microsoft.AspNetCore.Authorization.Infrastructure.AssertionRequirement +Microsoft.AspNetCore.Authorization.Infrastructure.AssertionRequirement.AssertionRequirement(System.Func! handler) -> void +Microsoft.AspNetCore.Authorization.Infrastructure.AssertionRequirement.AssertionRequirement(System.Func!>! handler) -> void +Microsoft.AspNetCore.Authorization.Infrastructure.AssertionRequirement.HandleAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.Infrastructure.AssertionRequirement.Handler.get -> System.Func!>! +Microsoft.AspNetCore.Authorization.Infrastructure.ClaimsAuthorizationRequirement +Microsoft.AspNetCore.Authorization.Infrastructure.ClaimsAuthorizationRequirement.AllowedValues.get -> System.Collections.Generic.IEnumerable? +Microsoft.AspNetCore.Authorization.Infrastructure.ClaimsAuthorizationRequirement.ClaimsAuthorizationRequirement(string! claimType, System.Collections.Generic.IEnumerable? allowedValues) -> void +Microsoft.AspNetCore.Authorization.Infrastructure.ClaimsAuthorizationRequirement.ClaimType.get -> string! +Microsoft.AspNetCore.Authorization.Infrastructure.DenyAnonymousAuthorizationRequirement +Microsoft.AspNetCore.Authorization.Infrastructure.DenyAnonymousAuthorizationRequirement.DenyAnonymousAuthorizationRequirement() -> void +Microsoft.AspNetCore.Authorization.Infrastructure.NameAuthorizationRequirement +Microsoft.AspNetCore.Authorization.Infrastructure.NameAuthorizationRequirement.NameAuthorizationRequirement(string! requiredName) -> void +Microsoft.AspNetCore.Authorization.Infrastructure.NameAuthorizationRequirement.RequiredName.get -> string! +Microsoft.AspNetCore.Authorization.Infrastructure.OperationAuthorizationRequirement +Microsoft.AspNetCore.Authorization.Infrastructure.OperationAuthorizationRequirement.Name.get -> string! +Microsoft.AspNetCore.Authorization.Infrastructure.OperationAuthorizationRequirement.Name.set -> void +Microsoft.AspNetCore.Authorization.Infrastructure.OperationAuthorizationRequirement.OperationAuthorizationRequirement() -> void +Microsoft.AspNetCore.Authorization.Infrastructure.PassThroughAuthorizationHandler +Microsoft.AspNetCore.Authorization.Infrastructure.PassThroughAuthorizationHandler.HandleAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.Infrastructure.PassThroughAuthorizationHandler.PassThroughAuthorizationHandler() -> void +Microsoft.AspNetCore.Authorization.Infrastructure.RolesAuthorizationRequirement +Microsoft.AspNetCore.Authorization.Infrastructure.RolesAuthorizationRequirement.AllowedRoles.get -> System.Collections.Generic.IEnumerable! +Microsoft.AspNetCore.Authorization.Infrastructure.RolesAuthorizationRequirement.RolesAuthorizationRequirement(System.Collections.Generic.IEnumerable! allowedRoles) -> void +Microsoft.Extensions.DependencyInjection.AuthorizationServiceCollectionExtensions +override Microsoft.AspNetCore.Authorization.Infrastructure.AssertionRequirement.ToString() -> string! +override Microsoft.AspNetCore.Authorization.Infrastructure.ClaimsAuthorizationRequirement.HandleRequirementAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context, Microsoft.AspNetCore.Authorization.Infrastructure.ClaimsAuthorizationRequirement! requirement) -> System.Threading.Tasks.Task! +override Microsoft.AspNetCore.Authorization.Infrastructure.ClaimsAuthorizationRequirement.ToString() -> string! +override Microsoft.AspNetCore.Authorization.Infrastructure.DenyAnonymousAuthorizationRequirement.HandleRequirementAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context, Microsoft.AspNetCore.Authorization.Infrastructure.DenyAnonymousAuthorizationRequirement! requirement) -> System.Threading.Tasks.Task! +override Microsoft.AspNetCore.Authorization.Infrastructure.DenyAnonymousAuthorizationRequirement.ToString() -> string! +override Microsoft.AspNetCore.Authorization.Infrastructure.NameAuthorizationRequirement.HandleRequirementAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context, Microsoft.AspNetCore.Authorization.Infrastructure.NameAuthorizationRequirement! requirement) -> System.Threading.Tasks.Task! +override Microsoft.AspNetCore.Authorization.Infrastructure.NameAuthorizationRequirement.ToString() -> string! +override Microsoft.AspNetCore.Authorization.Infrastructure.OperationAuthorizationRequirement.ToString() -> string! +override Microsoft.AspNetCore.Authorization.Infrastructure.RolesAuthorizationRequirement.HandleRequirementAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context, Microsoft.AspNetCore.Authorization.Infrastructure.RolesAuthorizationRequirement! requirement) -> System.Threading.Tasks.Task! +override Microsoft.AspNetCore.Authorization.Infrastructure.RolesAuthorizationRequirement.ToString() -> string! +static Microsoft.AspNetCore.Authorization.AuthorizationFailure.ExplicitFail() -> Microsoft.AspNetCore.Authorization.AuthorizationFailure! +static Microsoft.AspNetCore.Authorization.AuthorizationFailure.Failed(System.Collections.Generic.IEnumerable! reasons) -> Microsoft.AspNetCore.Authorization.AuthorizationFailure! +static Microsoft.AspNetCore.Authorization.AuthorizationFailure.Failed(System.Collections.Generic.IEnumerable! failed) -> Microsoft.AspNetCore.Authorization.AuthorizationFailure! +static Microsoft.AspNetCore.Authorization.AuthorizationPolicy.Combine(params Microsoft.AspNetCore.Authorization.AuthorizationPolicy![]! policies) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicy! +static Microsoft.AspNetCore.Authorization.AuthorizationPolicy.Combine(System.Collections.Generic.IEnumerable! policies) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicy! +static Microsoft.AspNetCore.Authorization.AuthorizationPolicy.CombineAsync(Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider! policyProvider, System.Collections.Generic.IEnumerable! authorizeData) -> System.Threading.Tasks.Task! +static Microsoft.AspNetCore.Authorization.AuthorizationResult.Failed() -> Microsoft.AspNetCore.Authorization.AuthorizationResult! +static Microsoft.AspNetCore.Authorization.AuthorizationResult.Failed(Microsoft.AspNetCore.Authorization.AuthorizationFailure! failure) -> Microsoft.AspNetCore.Authorization.AuthorizationResult! +static Microsoft.AspNetCore.Authorization.AuthorizationResult.Success() -> Microsoft.AspNetCore.Authorization.AuthorizationResult! +static Microsoft.AspNetCore.Authorization.AuthorizationServiceExtensions.AuthorizeAsync(this Microsoft.AspNetCore.Authorization.IAuthorizationService! service, System.Security.Claims.ClaimsPrincipal! user, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> System.Threading.Tasks.Task! +static Microsoft.AspNetCore.Authorization.AuthorizationServiceExtensions.AuthorizeAsync(this Microsoft.AspNetCore.Authorization.IAuthorizationService! service, System.Security.Claims.ClaimsPrincipal! user, object? resource, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> System.Threading.Tasks.Task! +static Microsoft.AspNetCore.Authorization.AuthorizationServiceExtensions.AuthorizeAsync(this Microsoft.AspNetCore.Authorization.IAuthorizationService! service, System.Security.Claims.ClaimsPrincipal! user, object? resource, Microsoft.AspNetCore.Authorization.IAuthorizationRequirement! requirement) -> System.Threading.Tasks.Task! +static Microsoft.AspNetCore.Authorization.AuthorizationServiceExtensions.AuthorizeAsync(this Microsoft.AspNetCore.Authorization.IAuthorizationService! service, System.Security.Claims.ClaimsPrincipal! user, string! policyName) -> System.Threading.Tasks.Task! +static Microsoft.Extensions.DependencyInjection.AuthorizationServiceCollectionExtensions.AddAuthorizationCore(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! +static Microsoft.Extensions.DependencyInjection.AuthorizationServiceCollectionExtensions.AddAuthorizationCore(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandler.HandleAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context) -> System.Threading.Tasks.Task! +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandler.HandleAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context) -> System.Threading.Tasks.Task! +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.Fail() -> void +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.Fail(Microsoft.AspNetCore.Authorization.AuthorizationFailureReason! reason) -> void +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.FailureReasons.get -> System.Collections.Generic.IEnumerable! +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.HasFailed.get -> bool +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.HasSucceeded.get -> bool +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.PendingRequirements.get -> System.Collections.Generic.IEnumerable! +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.Requirements.get -> System.Collections.Generic.IEnumerable! +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.Resource.get -> object? +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.Succeed(Microsoft.AspNetCore.Authorization.IAuthorizationRequirement! requirement) -> void +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.User.get -> System.Security.Claims.ClaimsPrincipal! +virtual Microsoft.AspNetCore.Authorization.DefaultAuthorizationHandlerContextFactory.CreateContext(System.Collections.Generic.IEnumerable! requirements, System.Security.Claims.ClaimsPrincipal! user, object? resource) -> Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! +virtual Microsoft.AspNetCore.Authorization.DefaultAuthorizationPolicyProvider.GetPolicyAsync(string! policyName) -> System.Threading.Tasks.Task! +virtual Microsoft.AspNetCore.Authorization.DefaultAuthorizationService.AuthorizeAsync(System.Security.Claims.ClaimsPrincipal! user, object? resource, string! policyName) -> System.Threading.Tasks.Task! +virtual Microsoft.AspNetCore.Authorization.DefaultAuthorizationService.AuthorizeAsync(System.Security.Claims.ClaimsPrincipal! user, object? resource, System.Collections.Generic.IEnumerable! requirements) -> System.Threading.Tasks.Task! diff --git a/src/Security/Authorization/Core/src/PublicAPI/net7.0/PublicAPI.Unshipped.txt b/src/Security/Authorization/Core/src/PublicAPI/net7.0/PublicAPI.Unshipped.txt new file mode 100644 index 000000000000..2e9a164e836c --- /dev/null +++ b/src/Security/Authorization/Core/src/PublicAPI/net7.0/PublicAPI.Unshipped.txt @@ -0,0 +1,17 @@ +#nullable enable +Microsoft.AspNetCore.Authorization.AuthorizationBuilder +Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AuthorizationBuilder(Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> void +Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider.CanCachePolicy.get -> bool +Microsoft.AspNetCore.Authorization.Infrastructure.PassThroughAuthorizationHandler.PassThroughAuthorizationHandler(Microsoft.Extensions.Options.IOptions! options) -> void +static Microsoft.AspNetCore.Authorization.AuthorizationPolicy.CombineAsync(Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider! policyProvider, System.Collections.Generic.IEnumerable! authorizeData, System.Collections.Generic.IEnumerable! policies) -> System.Threading.Tasks.Task! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddDefaultPolicy(string! name, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddDefaultPolicy(string! name, System.Action! configurePolicy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddFallbackPolicy(string! name, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddFallbackPolicy(string! name, System.Action! configurePolicy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddPolicy(string! name, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddPolicy(string! name, System.Action! configurePolicy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.Services.get -> Microsoft.Extensions.DependencyInjection.IServiceCollection! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.SetDefaultPolicy(Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.SetFallbackPolicy(Microsoft.AspNetCore.Authorization.AuthorizationPolicy? policy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.SetInvokeHandlersAfterFailure(bool invoke) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! +virtual Microsoft.AspNetCore.Authorization.DefaultAuthorizationPolicyProvider.CanCachePolicy.get -> bool diff --git a/src/Security/Authorization/Core/src/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt b/src/Security/Authorization/Core/src/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt new file mode 100644 index 000000000000..cf34e70c4b92 --- /dev/null +++ b/src/Security/Authorization/Core/src/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt @@ -0,0 +1,171 @@ +#nullable enable +Microsoft.AspNetCore.Authorization.DefaultAuthorizationPolicyProvider.DefaultAuthorizationPolicyProvider(Microsoft.Extensions.Options.IOptions! options) -> void +Microsoft.AspNetCore.Authorization.DefaultAuthorizationService.DefaultAuthorizationService(Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider! policyProvider, Microsoft.AspNetCore.Authorization.IAuthorizationHandlerProvider! handlers, Microsoft.Extensions.Logging.ILogger! logger, Microsoft.AspNetCore.Authorization.IAuthorizationHandlerContextFactory! contextFactory, Microsoft.AspNetCore.Authorization.IAuthorizationEvaluator! evaluator, Microsoft.Extensions.Options.IOptions! options) -> void +abstract Microsoft.AspNetCore.Authorization.AuthorizationHandler.HandleRequirementAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context, TRequirement requirement, TResource resource) -> System.Threading.Tasks.Task! +abstract Microsoft.AspNetCore.Authorization.AuthorizationHandler.HandleRequirementAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context, TRequirement requirement) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute +Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute.AllowAnonymousAttribute() -> void +Microsoft.AspNetCore.Authorization.AuthorizationFailure +Microsoft.AspNetCore.Authorization.AuthorizationFailure.FailCalled.get -> bool +Microsoft.AspNetCore.Authorization.AuthorizationFailure.FailedRequirements.get -> System.Collections.Generic.IEnumerable! +Microsoft.AspNetCore.Authorization.AuthorizationFailure.FailureReasons.get -> System.Collections.Generic.IEnumerable! +Microsoft.AspNetCore.Authorization.AuthorizationFailureReason +Microsoft.AspNetCore.Authorization.AuthorizationFailureReason.AuthorizationFailureReason(Microsoft.AspNetCore.Authorization.IAuthorizationHandler! handler, string! message) -> void +Microsoft.AspNetCore.Authorization.AuthorizationFailureReason.Handler.get -> Microsoft.AspNetCore.Authorization.IAuthorizationHandler! +Microsoft.AspNetCore.Authorization.AuthorizationFailureReason.Message.get -> string! +Microsoft.AspNetCore.Authorization.AuthorizationHandler +Microsoft.AspNetCore.Authorization.AuthorizationHandler.AuthorizationHandler() -> void +Microsoft.AspNetCore.Authorization.AuthorizationHandler +Microsoft.AspNetCore.Authorization.AuthorizationHandler.AuthorizationHandler() -> void +Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext +Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.AuthorizationHandlerContext(System.Collections.Generic.IEnumerable! requirements, System.Security.Claims.ClaimsPrincipal! user, object? resource) -> void +Microsoft.AspNetCore.Authorization.AuthorizationOptions +Microsoft.AspNetCore.Authorization.AuthorizationOptions.AddPolicy(string! name, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> void +Microsoft.AspNetCore.Authorization.AuthorizationOptions.AddPolicy(string! name, System.Action! configurePolicy) -> void +Microsoft.AspNetCore.Authorization.AuthorizationOptions.AuthorizationOptions() -> void +Microsoft.AspNetCore.Authorization.AuthorizationOptions.DefaultPolicy.get -> Microsoft.AspNetCore.Authorization.AuthorizationPolicy! +Microsoft.AspNetCore.Authorization.AuthorizationOptions.DefaultPolicy.set -> void +Microsoft.AspNetCore.Authorization.AuthorizationOptions.FallbackPolicy.get -> Microsoft.AspNetCore.Authorization.AuthorizationPolicy? +Microsoft.AspNetCore.Authorization.AuthorizationOptions.FallbackPolicy.set -> void +Microsoft.AspNetCore.Authorization.AuthorizationOptions.GetPolicy(string! name) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicy? +Microsoft.AspNetCore.Authorization.AuthorizationOptions.InvokeHandlersAfterFailure.get -> bool +Microsoft.AspNetCore.Authorization.AuthorizationOptions.InvokeHandlersAfterFailure.set -> void +Microsoft.AspNetCore.Authorization.AuthorizationPolicy +Microsoft.AspNetCore.Authorization.AuthorizationPolicy.AuthenticationSchemes.get -> System.Collections.Generic.IReadOnlyList! +Microsoft.AspNetCore.Authorization.AuthorizationPolicy.AuthorizationPolicy(System.Collections.Generic.IEnumerable! requirements, System.Collections.Generic.IEnumerable! authenticationSchemes) -> void +Microsoft.AspNetCore.Authorization.AuthorizationPolicy.Requirements.get -> System.Collections.Generic.IReadOnlyList! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.AddAuthenticationSchemes(params string![]! schemes) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.AddRequirements(params Microsoft.AspNetCore.Authorization.IAuthorizationRequirement![]! requirements) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.AuthenticationSchemes.get -> System.Collections.Generic.IList! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.AuthenticationSchemes.set -> void +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.AuthorizationPolicyBuilder(Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> void +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.AuthorizationPolicyBuilder(params string![]! authenticationSchemes) -> void +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.Build() -> Microsoft.AspNetCore.Authorization.AuthorizationPolicy! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.Combine(Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireAssertion(System.Func! handler) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireAssertion(System.Func!>! handler) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireAuthenticatedUser() -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireClaim(string! claimType) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireClaim(string! claimType, params string![]! allowedValues) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireClaim(string! claimType, System.Collections.Generic.IEnumerable! allowedValues) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.Requirements.get -> System.Collections.Generic.IList! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.Requirements.set -> void +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireRole(params string![]! roles) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireRole(System.Collections.Generic.IEnumerable! roles) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireUserName(string! userName) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder! +Microsoft.AspNetCore.Authorization.AuthorizationResult +Microsoft.AspNetCore.Authorization.AuthorizationResult.Failure.get -> Microsoft.AspNetCore.Authorization.AuthorizationFailure? +Microsoft.AspNetCore.Authorization.AuthorizationResult.Succeeded.get -> bool +Microsoft.AspNetCore.Authorization.AuthorizationServiceExtensions +Microsoft.AspNetCore.Authorization.AuthorizeAttribute +Microsoft.AspNetCore.Authorization.AuthorizeAttribute.AuthenticationSchemes.get -> string? +Microsoft.AspNetCore.Authorization.AuthorizeAttribute.AuthenticationSchemes.set -> void +Microsoft.AspNetCore.Authorization.AuthorizeAttribute.AuthorizeAttribute() -> void +Microsoft.AspNetCore.Authorization.AuthorizeAttribute.AuthorizeAttribute(string! policy) -> void +Microsoft.AspNetCore.Authorization.AuthorizeAttribute.Policy.get -> string? +Microsoft.AspNetCore.Authorization.AuthorizeAttribute.Policy.set -> void +Microsoft.AspNetCore.Authorization.AuthorizeAttribute.Roles.get -> string? +Microsoft.AspNetCore.Authorization.AuthorizeAttribute.Roles.set -> void +Microsoft.AspNetCore.Authorization.DefaultAuthorizationEvaluator +Microsoft.AspNetCore.Authorization.DefaultAuthorizationEvaluator.DefaultAuthorizationEvaluator() -> void +Microsoft.AspNetCore.Authorization.DefaultAuthorizationEvaluator.Evaluate(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context) -> Microsoft.AspNetCore.Authorization.AuthorizationResult! +Microsoft.AspNetCore.Authorization.DefaultAuthorizationHandlerContextFactory +Microsoft.AspNetCore.Authorization.DefaultAuthorizationHandlerContextFactory.DefaultAuthorizationHandlerContextFactory() -> void +Microsoft.AspNetCore.Authorization.DefaultAuthorizationHandlerProvider +Microsoft.AspNetCore.Authorization.DefaultAuthorizationHandlerProvider.DefaultAuthorizationHandlerProvider(System.Collections.Generic.IEnumerable! handlers) -> void +Microsoft.AspNetCore.Authorization.DefaultAuthorizationHandlerProvider.GetHandlersAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context) -> System.Threading.Tasks.Task!>! +Microsoft.AspNetCore.Authorization.DefaultAuthorizationPolicyProvider +Microsoft.AspNetCore.Authorization.DefaultAuthorizationPolicyProvider.GetDefaultPolicyAsync() -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.DefaultAuthorizationPolicyProvider.GetFallbackPolicyAsync() -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.DefaultAuthorizationService +Microsoft.AspNetCore.Authorization.IAllowAnonymous (forwarded, contained in Microsoft.AspNetCore.Metadata) +Microsoft.AspNetCore.Authorization.IAuthorizationEvaluator +Microsoft.AspNetCore.Authorization.IAuthorizationEvaluator.Evaluate(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context) -> Microsoft.AspNetCore.Authorization.AuthorizationResult! +Microsoft.AspNetCore.Authorization.IAuthorizationHandler +Microsoft.AspNetCore.Authorization.IAuthorizationHandler.HandleAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.IAuthorizationHandlerContextFactory +Microsoft.AspNetCore.Authorization.IAuthorizationHandlerContextFactory.CreateContext(System.Collections.Generic.IEnumerable! requirements, System.Security.Claims.ClaimsPrincipal! user, object? resource) -> Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! +Microsoft.AspNetCore.Authorization.IAuthorizationHandlerProvider +Microsoft.AspNetCore.Authorization.IAuthorizationHandlerProvider.GetHandlersAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context) -> System.Threading.Tasks.Task!>! +Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider +Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider.GetDefaultPolicyAsync() -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider.GetFallbackPolicyAsync() -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider.GetPolicyAsync(string! policyName) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.IAuthorizationRequirement +Microsoft.AspNetCore.Authorization.IAuthorizationService +Microsoft.AspNetCore.Authorization.IAuthorizationService.AuthorizeAsync(System.Security.Claims.ClaimsPrincipal! user, object? resource, string! policyName) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.IAuthorizationService.AuthorizeAsync(System.Security.Claims.ClaimsPrincipal! user, object? resource, System.Collections.Generic.IEnumerable! requirements) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.IAuthorizeData (forwarded, contained in Microsoft.AspNetCore.Metadata) +Microsoft.AspNetCore.Authorization.IAuthorizeData.AuthenticationSchemes.get -> string? (forwarded, contained in Microsoft.AspNetCore.Metadata) +Microsoft.AspNetCore.Authorization.IAuthorizeData.AuthenticationSchemes.set -> void (forwarded, contained in Microsoft.AspNetCore.Metadata) +Microsoft.AspNetCore.Authorization.IAuthorizeData.Policy.get -> string? (forwarded, contained in Microsoft.AspNetCore.Metadata) +Microsoft.AspNetCore.Authorization.IAuthorizeData.Policy.set -> void (forwarded, contained in Microsoft.AspNetCore.Metadata) +Microsoft.AspNetCore.Authorization.IAuthorizeData.Roles.get -> string? (forwarded, contained in Microsoft.AspNetCore.Metadata) +Microsoft.AspNetCore.Authorization.IAuthorizeData.Roles.set -> void (forwarded, contained in Microsoft.AspNetCore.Metadata) +Microsoft.AspNetCore.Authorization.Infrastructure.AssertionRequirement +Microsoft.AspNetCore.Authorization.Infrastructure.AssertionRequirement.AssertionRequirement(System.Func! handler) -> void +Microsoft.AspNetCore.Authorization.Infrastructure.AssertionRequirement.AssertionRequirement(System.Func!>! handler) -> void +Microsoft.AspNetCore.Authorization.Infrastructure.AssertionRequirement.HandleAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.Infrastructure.AssertionRequirement.Handler.get -> System.Func!>! +Microsoft.AspNetCore.Authorization.Infrastructure.ClaimsAuthorizationRequirement +Microsoft.AspNetCore.Authorization.Infrastructure.ClaimsAuthorizationRequirement.AllowedValues.get -> System.Collections.Generic.IEnumerable? +Microsoft.AspNetCore.Authorization.Infrastructure.ClaimsAuthorizationRequirement.ClaimsAuthorizationRequirement(string! claimType, System.Collections.Generic.IEnumerable? allowedValues) -> void +Microsoft.AspNetCore.Authorization.Infrastructure.ClaimsAuthorizationRequirement.ClaimType.get -> string! +Microsoft.AspNetCore.Authorization.Infrastructure.DenyAnonymousAuthorizationRequirement +Microsoft.AspNetCore.Authorization.Infrastructure.DenyAnonymousAuthorizationRequirement.DenyAnonymousAuthorizationRequirement() -> void +Microsoft.AspNetCore.Authorization.Infrastructure.NameAuthorizationRequirement +Microsoft.AspNetCore.Authorization.Infrastructure.NameAuthorizationRequirement.NameAuthorizationRequirement(string! requiredName) -> void +Microsoft.AspNetCore.Authorization.Infrastructure.NameAuthorizationRequirement.RequiredName.get -> string! +Microsoft.AspNetCore.Authorization.Infrastructure.OperationAuthorizationRequirement +Microsoft.AspNetCore.Authorization.Infrastructure.OperationAuthorizationRequirement.Name.get -> string! +Microsoft.AspNetCore.Authorization.Infrastructure.OperationAuthorizationRequirement.Name.set -> void +Microsoft.AspNetCore.Authorization.Infrastructure.OperationAuthorizationRequirement.OperationAuthorizationRequirement() -> void +Microsoft.AspNetCore.Authorization.Infrastructure.PassThroughAuthorizationHandler +Microsoft.AspNetCore.Authorization.Infrastructure.PassThroughAuthorizationHandler.HandleAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context) -> System.Threading.Tasks.Task! +Microsoft.AspNetCore.Authorization.Infrastructure.PassThroughAuthorizationHandler.PassThroughAuthorizationHandler() -> void +Microsoft.AspNetCore.Authorization.Infrastructure.RolesAuthorizationRequirement +Microsoft.AspNetCore.Authorization.Infrastructure.RolesAuthorizationRequirement.AllowedRoles.get -> System.Collections.Generic.IEnumerable! +Microsoft.AspNetCore.Authorization.Infrastructure.RolesAuthorizationRequirement.RolesAuthorizationRequirement(System.Collections.Generic.IEnumerable! allowedRoles) -> void +Microsoft.Extensions.DependencyInjection.AuthorizationServiceCollectionExtensions +override Microsoft.AspNetCore.Authorization.Infrastructure.AssertionRequirement.ToString() -> string! +override Microsoft.AspNetCore.Authorization.Infrastructure.ClaimsAuthorizationRequirement.HandleRequirementAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context, Microsoft.AspNetCore.Authorization.Infrastructure.ClaimsAuthorizationRequirement! requirement) -> System.Threading.Tasks.Task! +override Microsoft.AspNetCore.Authorization.Infrastructure.ClaimsAuthorizationRequirement.ToString() -> string! +override Microsoft.AspNetCore.Authorization.Infrastructure.DenyAnonymousAuthorizationRequirement.HandleRequirementAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context, Microsoft.AspNetCore.Authorization.Infrastructure.DenyAnonymousAuthorizationRequirement! requirement) -> System.Threading.Tasks.Task! +override Microsoft.AspNetCore.Authorization.Infrastructure.DenyAnonymousAuthorizationRequirement.ToString() -> string! +override Microsoft.AspNetCore.Authorization.Infrastructure.NameAuthorizationRequirement.HandleRequirementAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context, Microsoft.AspNetCore.Authorization.Infrastructure.NameAuthorizationRequirement! requirement) -> System.Threading.Tasks.Task! +override Microsoft.AspNetCore.Authorization.Infrastructure.NameAuthorizationRequirement.ToString() -> string! +override Microsoft.AspNetCore.Authorization.Infrastructure.OperationAuthorizationRequirement.ToString() -> string! +override Microsoft.AspNetCore.Authorization.Infrastructure.RolesAuthorizationRequirement.HandleRequirementAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context, Microsoft.AspNetCore.Authorization.Infrastructure.RolesAuthorizationRequirement! requirement) -> System.Threading.Tasks.Task! +override Microsoft.AspNetCore.Authorization.Infrastructure.RolesAuthorizationRequirement.ToString() -> string! +static Microsoft.AspNetCore.Authorization.AuthorizationFailure.ExplicitFail() -> Microsoft.AspNetCore.Authorization.AuthorizationFailure! +static Microsoft.AspNetCore.Authorization.AuthorizationFailure.Failed(System.Collections.Generic.IEnumerable! reasons) -> Microsoft.AspNetCore.Authorization.AuthorizationFailure! +static Microsoft.AspNetCore.Authorization.AuthorizationFailure.Failed(System.Collections.Generic.IEnumerable! failed) -> Microsoft.AspNetCore.Authorization.AuthorizationFailure! +static Microsoft.AspNetCore.Authorization.AuthorizationPolicy.Combine(params Microsoft.AspNetCore.Authorization.AuthorizationPolicy![]! policies) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicy! +static Microsoft.AspNetCore.Authorization.AuthorizationPolicy.Combine(System.Collections.Generic.IEnumerable! policies) -> Microsoft.AspNetCore.Authorization.AuthorizationPolicy! +static Microsoft.AspNetCore.Authorization.AuthorizationPolicy.CombineAsync(Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider! policyProvider, System.Collections.Generic.IEnumerable! authorizeData) -> System.Threading.Tasks.Task! +static Microsoft.AspNetCore.Authorization.AuthorizationResult.Failed() -> Microsoft.AspNetCore.Authorization.AuthorizationResult! +static Microsoft.AspNetCore.Authorization.AuthorizationResult.Failed(Microsoft.AspNetCore.Authorization.AuthorizationFailure! failure) -> Microsoft.AspNetCore.Authorization.AuthorizationResult! +static Microsoft.AspNetCore.Authorization.AuthorizationResult.Success() -> Microsoft.AspNetCore.Authorization.AuthorizationResult! +static Microsoft.AspNetCore.Authorization.AuthorizationServiceExtensions.AuthorizeAsync(this Microsoft.AspNetCore.Authorization.IAuthorizationService! service, System.Security.Claims.ClaimsPrincipal! user, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> System.Threading.Tasks.Task! +static Microsoft.AspNetCore.Authorization.AuthorizationServiceExtensions.AuthorizeAsync(this Microsoft.AspNetCore.Authorization.IAuthorizationService! service, System.Security.Claims.ClaimsPrincipal! user, object? resource, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> System.Threading.Tasks.Task! +static Microsoft.AspNetCore.Authorization.AuthorizationServiceExtensions.AuthorizeAsync(this Microsoft.AspNetCore.Authorization.IAuthorizationService! service, System.Security.Claims.ClaimsPrincipal! user, object? resource, Microsoft.AspNetCore.Authorization.IAuthorizationRequirement! requirement) -> System.Threading.Tasks.Task! +static Microsoft.AspNetCore.Authorization.AuthorizationServiceExtensions.AuthorizeAsync(this Microsoft.AspNetCore.Authorization.IAuthorizationService! service, System.Security.Claims.ClaimsPrincipal! user, string! policyName) -> System.Threading.Tasks.Task! +static Microsoft.Extensions.DependencyInjection.AuthorizationServiceCollectionExtensions.AddAuthorizationCore(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! +static Microsoft.Extensions.DependencyInjection.AuthorizationServiceCollectionExtensions.AddAuthorizationCore(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandler.HandleAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context) -> System.Threading.Tasks.Task! +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandler.HandleAsync(Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! context) -> System.Threading.Tasks.Task! +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.Fail() -> void +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.Fail(Microsoft.AspNetCore.Authorization.AuthorizationFailureReason! reason) -> void +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.FailureReasons.get -> System.Collections.Generic.IEnumerable! +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.HasFailed.get -> bool +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.HasSucceeded.get -> bool +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.PendingRequirements.get -> System.Collections.Generic.IEnumerable! +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.Requirements.get -> System.Collections.Generic.IEnumerable! +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.Resource.get -> object? +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.Succeed(Microsoft.AspNetCore.Authorization.IAuthorizationRequirement! requirement) -> void +virtual Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext.User.get -> System.Security.Claims.ClaimsPrincipal! +virtual Microsoft.AspNetCore.Authorization.DefaultAuthorizationHandlerContextFactory.CreateContext(System.Collections.Generic.IEnumerable! requirements, System.Security.Claims.ClaimsPrincipal! user, object? resource) -> Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext! +virtual Microsoft.AspNetCore.Authorization.DefaultAuthorizationPolicyProvider.GetPolicyAsync(string! policyName) -> System.Threading.Tasks.Task! +virtual Microsoft.AspNetCore.Authorization.DefaultAuthorizationService.AuthorizeAsync(System.Security.Claims.ClaimsPrincipal! user, object? resource, string! policyName) -> System.Threading.Tasks.Task! +virtual Microsoft.AspNetCore.Authorization.DefaultAuthorizationService.AuthorizeAsync(System.Security.Claims.ClaimsPrincipal! user, object? resource, System.Collections.Generic.IEnumerable! requirements) -> System.Threading.Tasks.Task! diff --git a/src/Security/Authorization/Core/src/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt b/src/Security/Authorization/Core/src/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt new file mode 100644 index 000000000000..38f8cdf2c053 --- /dev/null +++ b/src/Security/Authorization/Core/src/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt @@ -0,0 +1,15 @@ +#nullable enable +Microsoft.AspNetCore.Authorization.AuthorizationBuilder +Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AuthorizationBuilder(Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> void +Microsoft.AspNetCore.Authorization.Infrastructure.PassThroughAuthorizationHandler.PassThroughAuthorizationHandler(Microsoft.Extensions.Options.IOptions! options) -> void +static Microsoft.AspNetCore.Authorization.AuthorizationPolicy.CombineAsync(Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider! policyProvider, System.Collections.Generic.IEnumerable! authorizeData, System.Collections.Generic.IEnumerable! policies) -> System.Threading.Tasks.Task! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddDefaultPolicy(string! name, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddDefaultPolicy(string! name, System.Action! configurePolicy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddFallbackPolicy(string! name, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddFallbackPolicy(string! name, System.Action! configurePolicy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddPolicy(string! name, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddPolicy(string! name, System.Action! configurePolicy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.Services.get -> Microsoft.Extensions.DependencyInjection.IServiceCollection! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.SetDefaultPolicy(Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.SetFallbackPolicy(Microsoft.AspNetCore.Authorization.AuthorizationPolicy? policy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! +virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.SetInvokeHandlersAfterFailure(bool invoke) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! diff --git a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs index 891d68307a67..8dba205f94b3 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs @@ -35,7 +35,11 @@ public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvide _policyProvider = policyProvider ?? throw new ArgumentNullException(nameof(policyProvider)); // Only try to cache combined policies for the default policy provider +#if NETCOREAPP + CacheCombinedPolicy = _policyProvider.CanCachePolicy; +#else CacheCombinedPolicy = _policyProvider.GetType() == typeof(DefaultAuthorizationPolicyProvider); +#endif } internal bool CacheCombinedPolicy { get; set; } diff --git a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs index d5d3d48629b8..ff54372f5df2 100644 --- a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs +++ b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs @@ -223,9 +223,14 @@ public async Task OnAuthorizationAsync_WillNotCallPolicyProviderWithCache() .Callback(() => getPolicyCount++); policyProvider.Setup(p => p.GetFallbackPolicyAsync()).ReturnsAsync(policy) .Callback(() => getFallbackPolicyCount++); +#if NETCOREAPP + policyProvider.Setup(p => p.CanCachePolicy).Returns(true); +#endif var next = new TestRequestDelegate(); var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); +#if !NETCOREAPP middleware.CacheCombinedPolicy = true; +#endif var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizationPolicyCache(), new AuthorizeAttribute("whatever"))); // Act & Assert @@ -249,8 +254,12 @@ private class TestDefaultPolicyProvider : DefaultAuthorizationPolicyProvider { public int GetFallbackPolicyCount; public int GetPolicyCount; + private readonly bool _canCache; - public TestDefaultPolicyProvider(IOptions options) : base(options) { } + public TestDefaultPolicyProvider(IOptions options, bool canCache) : base(options) + { + _canCache = canCache; + } public new Task GetFallbackPolicyAsync() { @@ -263,14 +272,18 @@ public override Task GetPolicyAsync(string policyName) GetPolicyCount++; return Task.FromResult(new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build()); } + + public override bool CanCachePolicy => _canCache; } - [Fact] - public async Task OnAuthorizationAsync_WillCallDerviedDefaultPolicyProviderWithCache() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task OnAuthorizationAsync_WillCallDerviedDefaultPolicyProviderCanCache(bool canCache) { // Arrange var policy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build(); - var policyProvider = new TestDefaultPolicyProvider(Options.Create(new AuthorizationOptions())); + var policyProvider = new TestDefaultPolicyProvider(Options.Create(new AuthorizationOptions()), canCache); var next = new TestRequestDelegate(); var middleware = CreateMiddleware(next.Invoke, policyProvider); var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizationPolicyCache(), new AuthorizeAttribute("whatever"))); @@ -282,12 +295,12 @@ public async Task OnAuthorizationAsync_WillCallDerviedDefaultPolicyProviderWithC Assert.Equal(1, next.CalledCount); await middleware.Invoke(context); - Assert.Equal(2, policyProvider.GetPolicyCount); + Assert.Equal(canCache ? 1: 2, policyProvider.GetPolicyCount); Assert.Equal(0, policyProvider.GetFallbackPolicyCount); Assert.Equal(2, next.CalledCount); await middleware.Invoke(context); - Assert.Equal(3, policyProvider.GetPolicyCount); + Assert.Equal(canCache ? 1 : 3, policyProvider.GetPolicyCount); Assert.Equal(0, policyProvider.GetFallbackPolicyCount); Assert.Equal(3, next.CalledCount); } From ba559f46d93e633bec7b379dc219a1133725f646 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Sat, 13 Aug 2022 23:08:34 -0700 Subject: [PATCH 17/25] PR feedback --- .../Policy/src/AuthorizationMiddleware.cs | 11 +---------- .../test/AuthorizationMiddlewareTests.cs | 5 ----- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs index 8dba205f94b3..657b5579b9d7 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs @@ -33,17 +33,8 @@ public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvide { _next = next ?? throw new ArgumentNullException(nameof(next)); _policyProvider = policyProvider ?? throw new ArgumentNullException(nameof(policyProvider)); - - // Only try to cache combined policies for the default policy provider -#if NETCOREAPP - CacheCombinedPolicy = _policyProvider.CanCachePolicy; -#else - CacheCombinedPolicy = _policyProvider.GetType() == typeof(DefaultAuthorizationPolicyProvider); -#endif } - internal bool CacheCombinedPolicy { get; set; } - /// /// Invokes the middleware performing authorization. /// @@ -64,7 +55,7 @@ public async Task Invoke(HttpContext context) } // Use the computed policy for this endpoint if we can - var computedPolicy = CacheCombinedPolicy + var computedPolicy = _policyProvider.CanCachePolicy ? endpoint?.Metadata.GetMetadata() : null; diff --git a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs index ff54372f5df2..240aa55e6b7f 100644 --- a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs +++ b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs @@ -223,14 +223,9 @@ public async Task OnAuthorizationAsync_WillNotCallPolicyProviderWithCache() .Callback(() => getPolicyCount++); policyProvider.Setup(p => p.GetFallbackPolicyAsync()).ReturnsAsync(policy) .Callback(() => getFallbackPolicyCount++); -#if NETCOREAPP policyProvider.Setup(p => p.CanCachePolicy).Returns(true); -#endif var next = new TestRequestDelegate(); var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); -#if !NETCOREAPP - middleware.CacheCombinedPolicy = true; -#endif var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizationPolicyCache(), new AuthorizeAttribute("whatever"))); // Act & Assert From 907d79df528568bbb9a68c9b98c91377123e8188 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 15 Aug 2022 00:21:27 -0700 Subject: [PATCH 18/25] Switch to middleware cache instead --- .../Core/src/AuthorizationPolicyCache.cs | 15 ---- .../Core/src/Properties/AssemblyInfo.cs | 3 - ...tionEndpointConventionBuilderExtensions.cs | 20 ----- .../Policy/src/AuthorizationMiddleware.cs | 44 ++++++++-- ...oft.AspNetCore.Authorization.Policy.csproj | 1 + .../Policy/src/PublicAPI.Unshipped.txt | 1 + ...ndpointConventionBuilderExtensionsTests.cs | 83 +++++-------------- .../test/AuthorizationMiddlewareTests.cs | 27 ++++-- 8 files changed, 82 insertions(+), 112 deletions(-) delete mode 100644 src/Security/Authorization/Core/src/AuthorizationPolicyCache.cs diff --git a/src/Security/Authorization/Core/src/AuthorizationPolicyCache.cs b/src/Security/Authorization/Core/src/AuthorizationPolicyCache.cs deleted file mode 100644 index faa69e1b8b2f..000000000000 --- a/src/Security/Authorization/Core/src/AuthorizationPolicyCache.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Authorization; - -/// -/// Represents a cache for an AuthorizationPolicy instance -/// -internal class AuthorizationPolicyCache -{ - /// - /// The cached policy. - /// - public AuthorizationPolicy? Policy { get; set; } -} diff --git a/src/Security/Authorization/Core/src/Properties/AssemblyInfo.cs b/src/Security/Authorization/Core/src/Properties/AssemblyInfo.cs index 8996ac7726f9..c31eb0767a6e 100644 --- a/src/Security/Authorization/Core/src/Properties/AssemblyInfo.cs +++ b/src/Security/Authorization/Core/src/Properties/AssemblyInfo.cs @@ -5,9 +5,6 @@ using System.Runtime.CompilerServices; using Microsoft.AspNetCore.Authorization; -[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Authorization.Policy, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] -[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Authorization.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] - // Microsoft.AspNetCore.Metadata [assembly: TypeForwardedTo(typeof(IAuthorizeData))] [assembly: TypeForwardedTo(typeof(IAllowAnonymous))] diff --git a/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs b/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs index de167c7f19fd..83a19972ffc7 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationEndpointConventionBuilderExtensions.cs @@ -143,24 +143,6 @@ public static TBuilder AllowAnonymous(this TBuilder builder) where TBu return builder; } - /// - /// Enables AuthorizationMiddleware to cache the policy for a particular endpoint so its only computed once when using the default DefaultAuthorizationPolicyProvider. - /// - /// The endpoint convention builder. - /// The original convention builder parameter. - public static TBuilder WithAuthorizationCache(this TBuilder builder) where TBuilder : IEndpointConventionBuilder - { - builder.Add(endpointBuilder => - { - // Only add a policy cache if there isn't one - if (!endpointBuilder.Metadata.Any(meta => meta is AuthorizationPolicyCache)) - { - endpointBuilder.Metadata.Add(new AuthorizationPolicyCache()); - } - }); - return builder; - } - private static void RequirePolicyCore(TBuilder builder, AuthorizationPolicy policy) where TBuilder : IEndpointConventionBuilder { @@ -173,7 +155,6 @@ private static void RequirePolicyCore(TBuilder builder, AuthorizationP } endpointBuilder.Metadata.Add(policy); }); - builder.WithAuthorizationCache(); } private static void RequireAuthorizationCore(TBuilder builder, IEnumerable authorizeData) @@ -186,6 +167,5 @@ private static void RequireAuthorizationCore(TBuilder builder, IEnumer endpointBuilder.Metadata.Add(data); } }); - builder.WithAuthorizationCache(); } } diff --git a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs index 657b5579b9d7..9d698a501da3 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs @@ -1,10 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Concurrent; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization.Policy; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features.Authentication; +using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Authorization; @@ -24,6 +26,9 @@ public class AuthorizationMiddleware private readonly RequestDelegate _next; private readonly IAuthorizationPolicyProvider _policyProvider; + // Caches AuthorizationPolicy instances + private readonly DataSourceDependentCache>? _policyCache; + /// /// Initializes a new instance of . /// @@ -35,6 +40,27 @@ public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvide _policyProvider = policyProvider ?? throw new ArgumentNullException(nameof(policyProvider)); } + /// + /// Initializes a new instance of . + /// + /// The next middleware in the application middleware pipeline. + /// The . + /// The . + public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvider policyProvider, EndpointDataSource dataSource) : this(next, policyProvider) + { + if (dataSource != null) + { + // We cache AuthorizationPolicy instances per-Endpoint for performance, but we want to wipe out + // that cache if the endpoints change so that we don't allow unbounded memory growth. + _policyCache = new DataSourceDependentCache>(dataSource, (_) => + { + // We don't eagerly fill this cache because there's no real reason to. Unlike URL matching, we don't + // need to build a big data structure up front to be correct. + return new ConcurrentDictionary(); + }); + } + } + /// /// Invokes the middleware performing authorization. /// @@ -55,11 +81,15 @@ public async Task Invoke(HttpContext context) } // Use the computed policy for this endpoint if we can - var computedPolicy = _policyProvider.CanCachePolicy - ? endpoint?.Metadata.GetMetadata() - : null; + AuthorizationPolicy? policy = null; + + var canCachePolicy = _policyProvider.CanCachePolicy && _policyCache != null && endpoint != null; + if (canCachePolicy) + { + _policyCache!.EnsureInitialized(); + _policyCache?.Value?.TryGetValue(endpoint!, out policy); + } - var policy = computedPolicy?.Policy; if (policy == null) { // IMPORTANT: Changes to authorization logic should be mirrored in MVC's AuthorizeFilter @@ -69,10 +99,10 @@ public async Task Invoke(HttpContext context) policy = await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData, policies); - // Cache the computed policy in the endpoint metadata if available - if (computedPolicy != null) + // Cache the computed policy + if (policy != null && canCachePolicy) { - computedPolicy.Policy = policy; + _policyCache!.Value![endpoint!] = policy; } } diff --git a/src/Security/Authorization/Policy/src/Microsoft.AspNetCore.Authorization.Policy.csproj b/src/Security/Authorization/Policy/src/Microsoft.AspNetCore.Authorization.Policy.csproj index 443d62ff4c5f..14912c54a988 100644 --- a/src/Security/Authorization/Policy/src/Microsoft.AspNetCore.Authorization.Policy.csproj +++ b/src/Security/Authorization/Policy/src/Microsoft.AspNetCore.Authorization.Policy.csproj @@ -12,6 +12,7 @@ + diff --git a/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt b/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt index 78970a31cc1a..95f1dd56069a 100644 --- a/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt +++ b/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt @@ -1,4 +1,5 @@ #nullable enable +Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.AuthorizationMiddleware(Microsoft.AspNetCore.Http.RequestDelegate! next, Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider! policyProvider, Microsoft.AspNetCore.Routing.EndpointDataSource! dataSource) -> void static Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.RequireAuthorization(this TBuilder builder, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> TBuilder static Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.RequireAuthorization(this TBuilder builder, System.Action! configurePolicy) -> TBuilder static Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.WithAuthorizationCache(this TBuilder builder) -> TBuilder diff --git a/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs b/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs index a3893b71a41a..6e1b08a808a9 100644 --- a/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs +++ b/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs @@ -21,13 +21,9 @@ public void RequireAuthorization_IAuthorizeData() // Assert var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); - Assert.Equal(2, builder.Conventions.Count); - foreach (var convention in builder.Conventions) - { - convention(endpointModel); - } + var convention = Assert.Single(builder.Conventions); + convention(endpointModel); - Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); Assert.Equal(metadata, Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); } @@ -42,13 +38,9 @@ public void RequireAuthorization_IAuthorizeData_Empty() // Assert var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); - Assert.Equal(2, builder.Conventions.Count); - foreach (var convention in builder.Conventions) - { - convention(endpointModel); - } + var convention = Assert.Single(builder.Conventions); + convention(endpointModel); - Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); Assert.Null(authMetadata.Policy); } @@ -64,13 +56,9 @@ public void RequireAuthorization_PolicyName() // Assert var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); - Assert.Equal(2, builder.Conventions.Count); - foreach (var convention in builder.Conventions) - { - convention(endpointModel); - } + var convention = Assert.Single(builder.Conventions); + convention(endpointModel); - Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); Assert.Equal("policy", authMetadata.Policy); } @@ -86,13 +74,9 @@ public void RequireAuthorization_PolicyName_Empty() // Assert var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); - Assert.Equal(2, builder.Conventions.Count); - foreach (var convention in builder.Conventions) - { - convention(endpointModel); - } + var convention = Assert.Single(builder.Conventions); + convention(endpointModel); - Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); Assert.Null(authMetadata.Policy); } @@ -108,13 +92,9 @@ public void RequireAuthorization_Default() // Assert var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); - Assert.Equal(2, builder.Conventions.Count); - foreach (var convention in builder.Conventions) - { - convention(endpointModel); - } + var convention = Assert.Single(builder.Conventions); + convention(endpointModel); - Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); Assert.Null(authMetadata.Policy); } @@ -144,18 +124,14 @@ public void RequireAuthorization_Policy() // Assert var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); - Assert.Equal(2, builder.Conventions.Count); - foreach (var convention in builder.Conventions) - { - convention(endpointModel); - } - - Assert.Equal(3, endpointModel.Metadata.Count); + var convention = Assert.Single(builder.Conventions); + convention(endpointModel); + + Assert.Equal(2, endpointModel.Metadata.Count); var authMetadata = Assert.IsAssignableFrom(endpointModel.Metadata[0]); Assert.Null(authMetadata.Policy); Assert.Equal(policy, endpointModel.Metadata[1]); - Assert.IsType(endpointModel.Metadata[2]); } [Fact] @@ -170,21 +146,16 @@ public void RequireAuthorization_PolicyCallback() // Assert var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); - Assert.Equal(2, builder.Conventions.Count); - foreach (var convention in builder.Conventions) - { - convention(endpointModel); - } + var convention = Assert.Single(builder.Conventions); + convention(endpointModel); - Assert.Single(endpointModel.Metadata.Where(t => t is AuthorizationPolicyCache)); - Assert.Equal(3, endpointModel.Metadata.Count); + Assert.Equal(2, endpointModel.Metadata.Count); var authMetadata = Assert.IsAssignableFrom(endpointModel.Metadata[0]); Assert.Null(authMetadata.Policy); var policy = Assert.IsAssignableFrom(endpointModel.Metadata[1]); Assert.Equal(1, policy.Requirements.Count); Assert.Equal(requirement, policy.Requirements[0]); - Assert.IsType(endpointModel.Metadata[2]); } [Fact] @@ -201,19 +172,15 @@ public void RequireAuthorization_PolicyCallbackWithAuthorize() // Assert var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); endpointModel.Metadata.Add(authorize); - Assert.Equal(2, builder.Conventions.Count); - foreach (var convention in builder.Conventions) - { - convention(endpointModel); - } + var convention = Assert.Single(builder.Conventions); + convention(endpointModel); // Confirm that we don't add another authorize if one already exists - Assert.Equal(3, endpointModel.Metadata.Count); + Assert.Equal(2, endpointModel.Metadata.Count); Assert.Equal(authorize, endpointModel.Metadata[0]); var policy = Assert.IsAssignableFrom(endpointModel.Metadata[1]); Assert.Equal(1, policy.Requirements.Count); Assert.Equal(requirement, policy.Requirements[0]); - Assert.IsType(endpointModel.Metadata[2]); } [Fact] @@ -232,17 +199,13 @@ public void RequireAuthorization_PolicyWithAuthorize() var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); endpointModel.Metadata.Add(authorize); - Assert.Equal(2, builder.Conventions.Count); - foreach (var convention in builder.Conventions) - { - convention(endpointModel); - } + var convention = Assert.Single(builder.Conventions); + convention(endpointModel); // Confirm that we don't add another authorize if one already exists - Assert.Equal(3, endpointModel.Metadata.Count); + Assert.Equal(2, endpointModel.Metadata.Count); Assert.Equal(authorize, endpointModel.Metadata[0]); Assert.Equal(policy, endpointModel.Metadata[1]); - Assert.IsType(endpointModel.Metadata[2]); } class TestRequirement : IAuthorizationRequirement { } diff --git a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs index 240aa55e6b7f..ca0de8a95a96 100644 --- a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs +++ b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs @@ -8,9 +8,11 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Primitives; using Moq; namespace Microsoft.AspNetCore.Authorization.Test; @@ -211,6 +213,14 @@ public async Task OnAuthorizationAsync_WillCallPolicyProvider() Assert.Equal(3, next.CalledCount); } + private static EndpointDataSource CreateDataSource(Endpoint endpoint) + { + var dataSource = new Mock(); + dataSource.Setup(d => d.Endpoints).Returns(new Endpoint[] { endpoint }); + dataSource.Setup(d => d.GetChangeToken()).Returns(new CancellationChangeToken(new CancellationToken())); + return dataSource.Object; + } + [Fact] public async Task OnAuthorizationAsync_WillNotCallPolicyProviderWithCache() { @@ -225,8 +235,10 @@ public async Task OnAuthorizationAsync_WillNotCallPolicyProviderWithCache() .Callback(() => getFallbackPolicyCount++); policyProvider.Setup(p => p.CanCachePolicy).Returns(true); var next = new TestRequestDelegate(); - var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); - var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizationPolicyCache(), new AuthorizeAttribute("whatever"))); + + var endpoint = CreateEndpoint(new AuthorizeAttribute("whatever")); + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object, CreateDataSource(endpoint)); + var context = GetHttpContext(anonymous: true, endpoint: endpoint); // Act & Assert await middleware.Invoke(context); @@ -280,8 +292,9 @@ public async Task OnAuthorizationAsync_WillCallDerviedDefaultPolicyProviderCanCa var policy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build(); var policyProvider = new TestDefaultPolicyProvider(Options.Create(new AuthorizationOptions()), canCache); var next = new TestRequestDelegate(); - var middleware = CreateMiddleware(next.Invoke, policyProvider); - var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizationPolicyCache(), new AuthorizeAttribute("whatever"))); + var endpoint = CreateEndpoint(new AuthorizeAttribute("whatever")); + var middleware = CreateMiddleware(next.Invoke, policyProvider, CreateDataSource(endpoint)); + var context = GetHttpContext(anonymous: true, endpoint: endpoint); // Act & Assert await middleware.Invoke(context); @@ -314,7 +327,7 @@ public async Task OnAuthorizationAsync_WillCallCustomPolicyProviderWithCache() .Callback(() => getFallbackPolicyCount++); var next = new TestRequestDelegate(); var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); - var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizationPolicyCache(), new AuthorizeAttribute("whatever"))); + var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizeAttribute("whatever"))); // Act & Assert await middleware.Invoke(context); @@ -805,10 +818,10 @@ public async Task WebApplicationBuilder_CanRegisterAuthzMiddlewareWithScopedServ Assert.True(app.Properties.ContainsKey("__AuthorizationMiddlewareSet")); } - private AuthorizationMiddleware CreateMiddleware(RequestDelegate requestDelegate = null, IAuthorizationPolicyProvider policyProvider = null) + private AuthorizationMiddleware CreateMiddleware(RequestDelegate requestDelegate = null, IAuthorizationPolicyProvider policyProvider = null, EndpointDataSource dataSource = null) { requestDelegate = requestDelegate ?? ((context) => Task.CompletedTask); - return new AuthorizationMiddleware(requestDelegate, policyProvider); + return new AuthorizationMiddleware(requestDelegate, policyProvider, dataSource); } private Endpoint CreateEndpoint(params object[] metadata) From bef7627939e5ceddbecd568d3310348ec200d81d Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 15 Aug 2022 00:23:43 -0700 Subject: [PATCH 19/25] Cleanup --- src/Security/Authorization/Policy/src/AssemblyInfo.cs | 6 ------ .../Policy/src/AuthorizationAppBuilderExtensions.cs | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 src/Security/Authorization/Policy/src/AssemblyInfo.cs diff --git a/src/Security/Authorization/Policy/src/AssemblyInfo.cs b/src/Security/Authorization/Policy/src/AssemblyInfo.cs deleted file mode 100644 index a03a5b6bd469..000000000000 --- a/src/Security/Authorization/Policy/src/AssemblyInfo.cs +++ /dev/null @@ -1,6 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Authorization.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Security/Authorization/Policy/src/AuthorizationAppBuilderExtensions.cs b/src/Security/Authorization/Policy/src/AuthorizationAppBuilderExtensions.cs index 3c675d9ed6b8..74b23c01f871 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationAppBuilderExtensions.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationAppBuilderExtensions.cs @@ -42,7 +42,7 @@ private static void VerifyServicesRegistered(IApplicationBuilder app) // We use the AuthorizationPolicyMarkerService to ensure all the services were added. if (app.ApplicationServices.GetService(typeof(AuthorizationPolicyMarkerService)) == null) { - throw new InvalidOperationException(Authorization.Policy.Resources.FormatException_UnableToFindServices( + throw new InvalidOperationException(Resources.FormatException_UnableToFindServices( nameof(IServiceCollection), nameof(PolicyServiceCollectionExtensions.AddAuthorization))); } From 5740f937e28eee90608189865acfd0f603fc3e63 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 15 Aug 2022 00:41:15 -0700 Subject: [PATCH 20/25] PR feedback --- .../Core/src/DefaultAuthorizationPolicyProvider.cs | 4 ++-- .../Authorization/Policy/src/AuthorizationMiddleware.cs | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Security/Authorization/Core/src/DefaultAuthorizationPolicyProvider.cs b/src/Security/Authorization/Core/src/DefaultAuthorizationPolicyProvider.cs index 47c1a856ab2b..d58d883348c0 100644 --- a/src/Security/Authorization/Core/src/DefaultAuthorizationPolicyProvider.cs +++ b/src/Security/Authorization/Core/src/DefaultAuthorizationPolicyProvider.cs @@ -74,8 +74,8 @@ public Task GetDefaultPolicyAsync() #if NETCOREAPP /// - /// Determines if policies from this provider can be cached, which is true for this provider. + /// Determines if policies from this provider can be cached, which is true only for this type. /// - public virtual bool CanCachePolicy => true; + public virtual bool CanCachePolicy => GetType() == typeof(DefaultAuthorizationPolicyProvider); #endif } diff --git a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs index 9d698a501da3..4569576ad539 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs @@ -25,6 +25,7 @@ public class AuthorizationMiddleware private readonly RequestDelegate _next; private readonly IAuthorizationPolicyProvider _policyProvider; + private readonly bool _canCache; // Caches AuthorizationPolicy instances private readonly DataSourceDependentCache>? _policyCache; @@ -38,6 +39,7 @@ public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvide { _next = next ?? throw new ArgumentNullException(nameof(next)); _policyProvider = policyProvider ?? throw new ArgumentNullException(nameof(policyProvider)); + _canCache = false; } /// @@ -48,7 +50,7 @@ public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvide /// The . public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvider policyProvider, EndpointDataSource dataSource) : this(next, policyProvider) { - if (dataSource != null) + if (dataSource != null && _policyProvider.CanCachePolicy) { // We cache AuthorizationPolicy instances per-Endpoint for performance, but we want to wipe out // that cache if the endpoints change so that we don't allow unbounded memory growth. @@ -58,6 +60,7 @@ public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvide // need to build a big data structure up front to be correct. return new ConcurrentDictionary(); }); + _canCache = true; } } @@ -83,7 +86,7 @@ public async Task Invoke(HttpContext context) // Use the computed policy for this endpoint if we can AuthorizationPolicy? policy = null; - var canCachePolicy = _policyProvider.CanCachePolicy && _policyCache != null && endpoint != null; + var canCachePolicy = _canCache && endpoint != null; if (canCachePolicy) { _policyCache!.EnsureInitialized(); From ca4ca4b366cf3f76f69d5fd22d9d4a69d85726af Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 15 Aug 2022 00:43:11 -0700 Subject: [PATCH 21/25] Update PublicAPI.Unshipped.txt --- src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt b/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt index 95f1dd56069a..0eaff6d993e4 100644 --- a/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt +++ b/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt @@ -2,5 +2,4 @@ Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.AuthorizationMiddleware(Microsoft.AspNetCore.Http.RequestDelegate! next, Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider! policyProvider, Microsoft.AspNetCore.Routing.EndpointDataSource! dataSource) -> void static Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.RequireAuthorization(this TBuilder builder, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> TBuilder static Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.RequireAuthorization(this TBuilder builder, System.Action! configurePolicy) -> TBuilder -static Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.WithAuthorizationCache(this TBuilder builder) -> TBuilder static Microsoft.Extensions.DependencyInjection.PolicyServiceCollectionExtensions.AddAuthorizationBuilder(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! From 7e04d7d2ec8961b1c6b4face30590e3b28e4c4cb Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 15 Aug 2022 00:44:41 -0700 Subject: [PATCH 22/25] Update src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs Co-authored-by: David Fowler --- .../Authorization/Policy/src/AuthorizationMiddleware.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs index 4569576ad539..7e80339b1fc5 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs @@ -90,7 +90,7 @@ public async Task Invoke(HttpContext context) if (canCachePolicy) { _policyCache!.EnsureInitialized(); - _policyCache?.Value?.TryGetValue(endpoint!, out policy); + _policyCache!.Value!.TryGetValue(endpoint!, out policy); } if (policy == null) From 67d3984d3f561f7bc8ab5b66d56c1f4c58fc56c6 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 15 Aug 2022 01:23:42 -0700 Subject: [PATCH 23/25] Store cache in disposable --- .../Policy/src/AuthorizationMiddleware.cs | 28 ++++-------- .../Policy/src/AuthorizationPolicyCache.cs | 43 +++++++++++++++++++ .../src/PolicyServiceCollectionExtensions.cs | 2 + .../Policy/src/PublicAPI.Unshipped.txt | 2 +- .../test/AuthorizationMiddlewareTests.cs | 12 ++++-- 5 files changed, 64 insertions(+), 23 deletions(-) create mode 100644 src/Security/Authorization/Policy/src/AuthorizationPolicyCache.cs diff --git a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs index 7e80339b1fc5..850a5f4037c5 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs @@ -26,9 +26,7 @@ public class AuthorizationMiddleware private readonly RequestDelegate _next; private readonly IAuthorizationPolicyProvider _policyProvider; private readonly bool _canCache; - - // Caches AuthorizationPolicy instances - private readonly DataSourceDependentCache>? _policyCache; + private readonly AuthorizationPolicyCache? _policyCache; /// /// Initializes a new instance of . @@ -47,20 +45,13 @@ public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvide /// /// The next middleware in the application middleware pipeline. /// The . - /// The . - public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvider policyProvider, EndpointDataSource dataSource) : this(next, policyProvider) + /// The . + public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvider policyProvider, IServiceProvider services) : this(next, policyProvider) { - if (dataSource != null && _policyProvider.CanCachePolicy) + if (services != null && _policyProvider.CanCachePolicy) { - // We cache AuthorizationPolicy instances per-Endpoint for performance, but we want to wipe out - // that cache if the endpoints change so that we don't allow unbounded memory growth. - _policyCache = new DataSourceDependentCache>(dataSource, (_) => - { - // We don't eagerly fill this cache because there's no real reason to. Unlike URL matching, we don't - // need to build a big data structure up front to be correct. - return new ConcurrentDictionary(); - }); - _canCache = true; + _policyCache = services.GetService(); + _canCache = _policyCache != null; } } @@ -85,12 +76,10 @@ public async Task Invoke(HttpContext context) // Use the computed policy for this endpoint if we can AuthorizationPolicy? policy = null; - var canCachePolicy = _canCache && endpoint != null; if (canCachePolicy) { - _policyCache!.EnsureInitialized(); - _policyCache!.Value!.TryGetValue(endpoint!, out policy); + policy = _policyCache!.Lookup(endpoint!); } if (policy == null) @@ -105,7 +94,7 @@ public async Task Invoke(HttpContext context) // Cache the computed policy if (policy != null && canCachePolicy) { - _policyCache!.Value![endpoint!] = policy; + _policyCache!.Store(endpoint!, policy); } } @@ -155,4 +144,5 @@ public async Task Invoke(HttpContext context) var authorizationMiddlewareResultHandler = context.RequestServices.GetRequiredService(); await authorizationMiddlewareResultHandler.HandleAsync(_next, context, policy, authorizeResult); } + } diff --git a/src/Security/Authorization/Policy/src/AuthorizationPolicyCache.cs b/src/Security/Authorization/Policy/src/AuthorizationPolicyCache.cs new file mode 100644 index 000000000000..b0702774c3f7 --- /dev/null +++ b/src/Security/Authorization/Policy/src/AuthorizationPolicyCache.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Concurrent; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; + +namespace Microsoft.AspNetCore.Authorization.Policy; + +internal class AuthorizationPolicyCache : IDisposable +{ + // Caches AuthorizationPolicy instances + private readonly DataSourceDependentCache>? _policyCache; + + public AuthorizationPolicyCache(EndpointDataSource dataSource) + { + // We cache AuthorizationPolicy instances per-Endpoint for performance, but we want to wipe out + // that cache if the endpoints change so that we don't allow unbounded memory growth. + _policyCache = new DataSourceDependentCache>(dataSource, (_) => + { + // We don't eagerly fill this cache because there's no real reason to. Unlike URL matching, we don't + // need to build a big data structure up front to be correct. + return new ConcurrentDictionary(); + }); + } + + public AuthorizationPolicy? Lookup(Endpoint endpoint) + { + _policyCache!.EnsureInitialized(); + _policyCache!.Value!.TryGetValue(endpoint!, out var policy); + return policy; + } + + public void Store(Endpoint endpoint, AuthorizationPolicy policy) + { + _policyCache!.Value![endpoint!] = policy; + } + + public void Dispose() + { + _policyCache?.Dispose(); + } +} diff --git a/src/Security/Authorization/Policy/src/PolicyServiceCollectionExtensions.cs b/src/Security/Authorization/Policy/src/PolicyServiceCollectionExtensions.cs index dd7cfd7ccc39..6f68fb2194d8 100644 --- a/src/Security/Authorization/Policy/src/PolicyServiceCollectionExtensions.cs +++ b/src/Security/Authorization/Policy/src/PolicyServiceCollectionExtensions.cs @@ -52,6 +52,7 @@ public static IServiceCollection AddAuthorization(this IServiceCollection servic services.AddAuthorizationCore(); services.AddAuthorizationPolicyEvaluator(); + services.AddSingleton(); return services; } @@ -70,6 +71,7 @@ public static IServiceCollection AddAuthorization(this IServiceCollection servic services.AddAuthorizationCore(configure); services.AddAuthorizationPolicyEvaluator(); + services.AddSingleton(); return services; } } diff --git a/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt b/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt index 0eaff6d993e4..302dcdf9f9ae 100644 --- a/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt +++ b/src/Security/Authorization/Policy/src/PublicAPI.Unshipped.txt @@ -1,5 +1,5 @@ #nullable enable -Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.AuthorizationMiddleware(Microsoft.AspNetCore.Http.RequestDelegate! next, Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider! policyProvider, Microsoft.AspNetCore.Routing.EndpointDataSource! dataSource) -> void +Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.AuthorizationMiddleware(Microsoft.AspNetCore.Http.RequestDelegate! next, Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider! policyProvider, System.IServiceProvider! services) -> void static Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.RequireAuthorization(this TBuilder builder, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> TBuilder static Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.RequireAuthorization(this TBuilder builder, System.Action! configurePolicy) -> TBuilder static Microsoft.Extensions.DependencyInjection.PolicyServiceCollectionExtensions.AddAuthorizationBuilder(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder! diff --git a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs index ca0de8a95a96..637fc3926a65 100644 --- a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs +++ b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs @@ -237,7 +237,10 @@ public async Task OnAuthorizationAsync_WillNotCallPolicyProviderWithCache() var next = new TestRequestDelegate(); var endpoint = CreateEndpoint(new AuthorizeAttribute("whatever")); - var middleware = CreateMiddleware(next.Invoke, policyProvider.Object, CreateDataSource(endpoint)); + var services = new ServiceCollection() + .AddAuthorization() + .AddSingleton(CreateDataSource(endpoint)).BuildServiceProvider(); + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object, services); var context = GetHttpContext(anonymous: true, endpoint: endpoint); // Act & Assert @@ -293,7 +296,10 @@ public async Task OnAuthorizationAsync_WillCallDerviedDefaultPolicyProviderCanCa var policyProvider = new TestDefaultPolicyProvider(Options.Create(new AuthorizationOptions()), canCache); var next = new TestRequestDelegate(); var endpoint = CreateEndpoint(new AuthorizeAttribute("whatever")); - var middleware = CreateMiddleware(next.Invoke, policyProvider, CreateDataSource(endpoint)); + var services = new ServiceCollection() + .AddAuthorization() + .AddSingleton(CreateDataSource(endpoint)).BuildServiceProvider(); + var middleware = CreateMiddleware(next.Invoke, policyProvider, services); var context = GetHttpContext(anonymous: true, endpoint: endpoint); // Act & Assert @@ -818,7 +824,7 @@ public async Task WebApplicationBuilder_CanRegisterAuthzMiddlewareWithScopedServ Assert.True(app.Properties.ContainsKey("__AuthorizationMiddlewareSet")); } - private AuthorizationMiddleware CreateMiddleware(RequestDelegate requestDelegate = null, IAuthorizationPolicyProvider policyProvider = null, EndpointDataSource dataSource = null) + private AuthorizationMiddleware CreateMiddleware(RequestDelegate requestDelegate = null, IAuthorizationPolicyProvider policyProvider = null, IServiceProvider dataSource = null) { requestDelegate = requestDelegate ?? ((context) => Task.CompletedTask); return new AuthorizationMiddleware(requestDelegate, policyProvider, dataSource); From abb7c59c1e0418f5b08d9fd643fe56473be9d379 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 15 Aug 2022 01:37:49 -0700 Subject: [PATCH 24/25] PR feedback --- .../Policy/src/AuthorizationMiddleware.cs | 4 +- .../Policy/src/AuthorizationPolicyCache.cs | 13 +++--- ...ndpointConventionBuilderExtensionsTests.cs | 42 +++++++++++-------- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs index 850a5f4037c5..f15e55e9fa58 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs @@ -48,7 +48,9 @@ public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvide /// The . public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvider policyProvider, IServiceProvider services) : this(next, policyProvider) { - if (services != null && _policyProvider.CanCachePolicy) + ArgumentNullException.ThrowIfNull(services); + + if (_policyProvider.CanCachePolicy) { _policyCache = services.GetService(); _canCache = _policyCache != null; diff --git a/src/Security/Authorization/Policy/src/AuthorizationPolicyCache.cs b/src/Security/Authorization/Policy/src/AuthorizationPolicyCache.cs index b0702774c3f7..ce235b693cd5 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationPolicyCache.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationPolicyCache.cs @@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Authorization.Policy; internal class AuthorizationPolicyCache : IDisposable { // Caches AuthorizationPolicy instances - private readonly DataSourceDependentCache>? _policyCache; + private readonly DataSourceDependentCache> _policyCache; public AuthorizationPolicyCache(EndpointDataSource dataSource) { @@ -18,26 +18,25 @@ public AuthorizationPolicyCache(EndpointDataSource dataSource) // that cache if the endpoints change so that we don't allow unbounded memory growth. _policyCache = new DataSourceDependentCache>(dataSource, (_) => { - // We don't eagerly fill this cache because there's no real reason to. Unlike URL matching, we don't - // need to build a big data structure up front to be correct. + // We don't eagerly fill this cache because there's no real reason to. return new ConcurrentDictionary(); }); + _policyCache.EnsureInitialized(); } public AuthorizationPolicy? Lookup(Endpoint endpoint) { - _policyCache!.EnsureInitialized(); - _policyCache!.Value!.TryGetValue(endpoint!, out var policy); + _policyCache.Value!.TryGetValue(endpoint, out var policy); return policy; } public void Store(Endpoint endpoint, AuthorizationPolicy policy) { - _policyCache!.Value![endpoint!] = policy; + _policyCache.Value![endpoint] = policy; } public void Dispose() { - _policyCache?.Dispose(); + _policyCache.Dispose(); } } diff --git a/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs b/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs index 6e1b08a808a9..8cc43a7d2f7f 100644 --- a/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs +++ b/src/Security/Authorization/test/AuthorizationEndpointConventionBuilderExtensionsTests.cs @@ -20,11 +20,12 @@ public void RequireAuthorization_IAuthorizeData() builder.RequireAuthorization(metadata); // Assert - var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); var convention = Assert.Single(builder.Conventions); + + var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); - Assert.Equal(metadata, Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); + Assert.Equal(metadata, Assert.Single(endpointModel.Metadata)); } [Fact] @@ -37,11 +38,12 @@ public void RequireAuthorization_IAuthorizeData_Empty() builder.RequireAuthorization(Array.Empty()); // Assert - var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); var convention = Assert.Single(builder.Conventions); + + var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); - var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); + var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata)); Assert.Null(authMetadata.Policy); } @@ -55,11 +57,12 @@ public void RequireAuthorization_PolicyName() builder.RequireAuthorization("policy"); // Assert - var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); var convention = Assert.Single(builder.Conventions); + + var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); - var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); + var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata)); Assert.Equal("policy", authMetadata.Policy); } @@ -73,11 +76,12 @@ public void RequireAuthorization_PolicyName_Empty() builder.RequireAuthorization(Array.Empty()); // Assert - var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); var convention = Assert.Single(builder.Conventions); + + var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); - var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); + var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata)); Assert.Null(authMetadata.Policy); } @@ -91,11 +95,12 @@ public void RequireAuthorization_Default() builder.RequireAuthorization(); // Assert - var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); var convention = Assert.Single(builder.Conventions); + + var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); - var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata.Where(t => t is IAuthorizeData))); + var authMetadata = Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata)); Assert.Null(authMetadata.Policy); } @@ -123,10 +128,11 @@ public void RequireAuthorization_Policy() builder.RequireAuthorization(policy); // Assert - var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); var convention = Assert.Single(builder.Conventions); + + var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); - + Assert.Equal(2, endpointModel.Metadata.Count); var authMetadata = Assert.IsAssignableFrom(endpointModel.Metadata[0]); Assert.Null(authMetadata.Policy); @@ -145,8 +151,9 @@ public void RequireAuthorization_PolicyCallback() builder.RequireAuthorization(policyBuilder => policyBuilder.Requirements.Add(requirement)); // Assert - var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); var convention = Assert.Single(builder.Conventions); + + var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); Assert.Equal(2, endpointModel.Metadata.Count); @@ -170,9 +177,10 @@ public void RequireAuthorization_PolicyCallbackWithAuthorize() builder.RequireAuthorization(policyBuilder => policyBuilder.Requirements.Add(requirement)); // Assert + var convention = Assert.Single(builder.Conventions); + var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); endpointModel.Metadata.Add(authorize); - var convention = Assert.Single(builder.Conventions); convention(endpointModel); // Confirm that we don't add another authorize if one already exists @@ -195,11 +203,10 @@ public void RequireAuthorization_PolicyWithAuthorize() builder.RequireAuthorization(policy); // Assert + var convention = Assert.Single(builder.Conventions); var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); endpointModel.Metadata.Add(authorize); - - var convention = Assert.Single(builder.Conventions); convention(endpointModel); // Confirm that we don't add another authorize if one already exists @@ -220,8 +227,9 @@ public void AllowAnonymous_Default() builder.AllowAnonymous(); // Assert - var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); var convention = Assert.Single(builder.Conventions); + + var endpointModel = new RouteEndpointBuilder((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); convention(endpointModel); Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata)); From d6204a66e595ccb5728e526f52e75ede2639c096 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 15 Aug 2022 01:41:18 -0700 Subject: [PATCH 25/25] Fix tests --- .../Authorization/Policy/src/AuthorizationMiddleware.cs | 2 -- .../Authorization/Policy/src/AuthorizationPolicyCache.cs | 2 +- .../Authorization/test/AuthorizationMiddlewareTests.cs | 5 +++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs index f15e55e9fa58..2df6ca2e4622 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs @@ -1,12 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Concurrent; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization.Policy; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features.Authentication; -using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Authorization; diff --git a/src/Security/Authorization/Policy/src/AuthorizationPolicyCache.cs b/src/Security/Authorization/Policy/src/AuthorizationPolicyCache.cs index ce235b693cd5..276250f42cd4 100644 --- a/src/Security/Authorization/Policy/src/AuthorizationPolicyCache.cs +++ b/src/Security/Authorization/Policy/src/AuthorizationPolicyCache.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Authorization.Policy; -internal class AuthorizationPolicyCache : IDisposable +internal sealed class AuthorizationPolicyCache : IDisposable { // Caches AuthorizationPolicy instances private readonly DataSourceDependentCache> _policyCache; diff --git a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs index 637fc3926a65..3907fbd60f73 100644 --- a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs +++ b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs @@ -824,10 +824,11 @@ public async Task WebApplicationBuilder_CanRegisterAuthzMiddlewareWithScopedServ Assert.True(app.Properties.ContainsKey("__AuthorizationMiddlewareSet")); } - private AuthorizationMiddleware CreateMiddleware(RequestDelegate requestDelegate = null, IAuthorizationPolicyProvider policyProvider = null, IServiceProvider dataSource = null) + private AuthorizationMiddleware CreateMiddleware(RequestDelegate requestDelegate = null, IAuthorizationPolicyProvider policyProvider = null, IServiceProvider services = null) { requestDelegate = requestDelegate ?? ((context) => Task.CompletedTask); - return new AuthorizationMiddleware(requestDelegate, policyProvider, dataSource); + services ??= new ServiceCollection().BuildServiceProvider(); + return new AuthorizationMiddleware(requestDelegate, policyProvider, services); } private Endpoint CreateEndpoint(params object[] metadata)