Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/aspnetcore.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHao Kung <HaoK@users.noreply.github.com>2022-11-01 21:30:39 +0300
committerGitHub <noreply@github.com>2022-11-01 21:30:39 +0300
commitabf67cbdaca1cd01ba517bf3ff9fbfeb12fcd6e8 (patch)
tree9268ed1c6c10f1ccecc02fccc9e7a6058d95d8ff
parent1d62cf9aa5e26274bb4d1993c35ae101d27c8d54 (diff)
[AuthZ] Add IRequirementData (#44342)
-rw-r--r--src/Security/Authorization/Core/src/IAuthorizationRequirementData.cs18
-rw-r--r--src/Security/Authorization/Core/src/PublicAPI/net462/PublicAPI.Unshipped.txt2
-rw-r--r--src/Security/Authorization/Core/src/PublicAPI/net7.0/PublicAPI.Unshipped.txt2
-rw-r--r--src/Security/Authorization/Core/src/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt2
-rw-r--r--src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs18
-rw-r--r--src/Security/Authorization/test/AuthorizationMiddlewareTests.cs108
6 files changed, 149 insertions, 1 deletions
diff --git a/src/Security/Authorization/Core/src/IAuthorizationRequirementData.cs b/src/Security/Authorization/Core/src/IAuthorizationRequirementData.cs
new file mode 100644
index 0000000000..7b21894c06
--- /dev/null
+++ b/src/Security/Authorization/Core/src/IAuthorizationRequirementData.cs
@@ -0,0 +1,18 @@
+// 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.Generic;
+
+namespace Microsoft.AspNetCore.Authorization;
+
+/// <summary>
+/// Interface that can produce authorization requirements.
+/// </summary>
+public interface IAuthorizationRequirementData
+{
+ /// <summary>
+ /// Returns <see cref="IAuthorizationRequirement"/> that should be satisfied for authorization.
+ /// </summary>
+ /// <returns><see cref="IAuthorizationRequirement"/> used for authorization.</returns>
+ IEnumerable<IAuthorizationRequirement> GetRequirements();
+}
diff --git a/src/Security/Authorization/Core/src/PublicAPI/net462/PublicAPI.Unshipped.txt b/src/Security/Authorization/Core/src/PublicAPI/net462/PublicAPI.Unshipped.txt
index 38f8cdf2c0..1b9c663162 100644
--- a/src/Security/Authorization/Core/src/PublicAPI/net462/PublicAPI.Unshipped.txt
+++ b/src/Security/Authorization/Core/src/PublicAPI/net462/PublicAPI.Unshipped.txt
@@ -1,6 +1,8 @@
#nullable enable
Microsoft.AspNetCore.Authorization.AuthorizationBuilder
Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AuthorizationBuilder(Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> void
+Microsoft.AspNetCore.Authorization.IAuthorizationRequirementData
+Microsoft.AspNetCore.Authorization.IAuthorizationRequirementData.GetRequirements() -> System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Authorization.IAuthorizationRequirement!>!
Microsoft.AspNetCore.Authorization.Infrastructure.PassThroughAuthorizationHandler.PassThroughAuthorizationHandler(Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Authorization.AuthorizationOptions!>! options) -> void
static Microsoft.AspNetCore.Authorization.AuthorizationPolicy.CombineAsync(Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider! policyProvider, System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Authorization.IAuthorizeData!>! authorizeData, System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Authorization.AuthorizationPolicy!>! policies) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Authorization.AuthorizationPolicy?>!
virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddDefaultPolicy(string! name, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder!
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
index ae0a6aaae0..1755112163 100644
--- a/src/Security/Authorization/Core/src/PublicAPI/net7.0/PublicAPI.Unshipped.txt
+++ b/src/Security/Authorization/Core/src/PublicAPI/net7.0/PublicAPI.Unshipped.txt
@@ -3,6 +3,8 @@ Microsoft.AspNetCore.Authorization.AuthorizationBuilder
Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AuthorizationBuilder(Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> void
Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider.AllowsCachingPolicies.get -> bool
Microsoft.AspNetCore.Authorization.Infrastructure.PassThroughAuthorizationHandler.PassThroughAuthorizationHandler(Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Authorization.AuthorizationOptions!>! options) -> void
+Microsoft.AspNetCore.Authorization.IAuthorizationRequirementData
+Microsoft.AspNetCore.Authorization.IAuthorizationRequirementData.GetRequirements() -> System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Authorization.IAuthorizationRequirement!>!
static Microsoft.AspNetCore.Authorization.AuthorizationPolicy.CombineAsync(Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider! policyProvider, System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Authorization.IAuthorizeData!>! authorizeData, System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Authorization.AuthorizationPolicy!>! policies) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Authorization.AuthorizationPolicy?>!
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<Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder!>! configurePolicy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder!
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
index 38f8cdf2c0..b2c12aec57 100644
--- a/src/Security/Authorization/Core/src/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt
+++ b/src/Security/Authorization/Core/src/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt
@@ -2,6 +2,8 @@
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<Microsoft.AspNetCore.Authorization.AuthorizationOptions!>! options) -> void
+Microsoft.AspNetCore.Authorization.IAuthorizationRequirementData
+Microsoft.AspNetCore.Authorization.IAuthorizationRequirementData.GetRequirements() -> System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Authorization.IAuthorizationRequirement!>!
static Microsoft.AspNetCore.Authorization.AuthorizationPolicy.CombineAsync(Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider! policyProvider, System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Authorization.IAuthorizeData!>! authorizeData, System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Authorization.AuthorizationPolicy!>! policies) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Authorization.AuthorizationPolicy?>!
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<Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder!>! configurePolicy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder!
diff --git a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs
index 5a3c17a4d5..2ff13e3779 100644
--- a/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs
+++ b/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs
@@ -111,6 +111,24 @@ public class AuthorizationMiddleware
policy = await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData, policies);
+ var requirementData = endpoint?.Metadata?.GetOrderedMetadata<IAuthorizationRequirementData>() ?? Array.Empty<IAuthorizationRequirementData>();
+ if (requirementData.Count > 0)
+ {
+ var reqPolicy = new AuthorizationPolicyBuilder();
+ foreach (var rd in requirementData)
+ {
+ foreach (var r in rd.GetRequirements())
+ {
+ reqPolicy.AddRequirements(r);
+ }
+ }
+
+ // Combine policy with requirements or just use requirements if no policy
+ policy = (policy is null)
+ ? reqPolicy.Build()
+ : AuthorizationPolicy.Combine(policy, reqPolicy.Build());
+ }
+
// Cache the computed policy
if (policy != null && canCachePolicy)
{
diff --git a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs
index 2d8fb8a570..bc599a6a11 100644
--- a/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs
+++ b/src/Security/Authorization/test/AuthorizationMiddlewareTests.cs
@@ -3,6 +3,7 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authorization.Infrastructure;
using Microsoft.AspNetCore.Authorization.Policy;
using Microsoft.AspNetCore.Authorization.Test.TestObjects;
using Microsoft.AspNetCore.Builder;
@@ -244,7 +245,9 @@ public class AuthorizationMiddlewareTests
var next = new TestRequestDelegate();
var logger = new Mock<ILogger<AuthorizationMiddleware>>();
- var endpoint = CreateEndpoint(new AuthorizeAttribute("whatever"));
+ var req = new AssertionRequirement(_ => true);
+
+ var endpoint = CreateEndpoint(new AuthorizeAttribute("whatever"), new ReqAttribute(req));
var services = new ServiceCollection()
.AddAuthorization()
.AddSingleton(CreateDataSource(endpoint)).BuildServiceProvider();
@@ -384,6 +387,109 @@ public class AuthorizationMiddlewareTests
Assert.True(calledPolicy);
}
+ public class ReqAttribute : Attribute, IAuthorizationRequirementData
+ {
+ IEnumerable<IAuthorizationRequirement> _reqs;
+ public ReqAttribute(params IAuthorizationRequirement[] req)
+ => _reqs = req;
+
+ public IEnumerable<IAuthorizationRequirement> GetRequirements()
+ => _reqs;
+ }
+
+ public class ReqAuthorizeAttribute : AuthorizeAttribute, IAuthorizationRequirementData
+ {
+ IEnumerable<IAuthorizationRequirement> _reqs;
+ public ReqAuthorizeAttribute(params IAuthorizationRequirement[] req)
+ => _reqs = req;
+
+ public IEnumerable<IAuthorizationRequirement> GetRequirements()
+ => _reqs;
+ }
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public async Task CanApplyRequirementAttributeDirectlyToEndpoint(bool assertSuccess)
+ {
+ // Arrange
+ var calledPolicy = false;
+ var req = new AssertionRequirement(_ => { calledPolicy = true; return assertSuccess; });
+ var policyProvider = new Mock<IAuthorizationPolicyProvider>();
+ var next = new TestRequestDelegate();
+ var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
+ var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new ReqAttribute(req)));
+
+ // Act & Assert
+ await middleware.Invoke(context);
+ Assert.True(calledPolicy);
+ Assert.Equal(assertSuccess, next.Called);
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(false, true)]
+ [InlineData(true, false)]
+ [InlineData(false, false)]
+ public async Task CanApplyMultipleRequirements(bool assertSuccess, bool assert2Success)
+ {
+ // Arrange
+ var calledPolicy = false;
+ var req = new AssertionRequirement(_ => { calledPolicy = true; return assertSuccess; });
+ var req2 = new AssertionRequirement(_ => { calledPolicy = true; return assert2Success; });
+ var policyProvider = new Mock<IAuthorizationPolicyProvider>();
+ var next = new TestRequestDelegate();
+ var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
+ var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new ReqAttribute(req, req2)));
+
+ // Act & Assert
+ await middleware.Invoke(context);
+ Assert.True(calledPolicy);
+ Assert.Equal(assertSuccess && assert2Success, next.Called);
+ }
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public async Task CanApplyRequirementAttributeWithAuthorizeDirectlyToEndpoint(bool assertSuccess)
+ {
+ // Arrange
+ var calledPolicy = false;
+ var req = new AssertionRequirement(_ => { calledPolicy = true; return assertSuccess; });
+ var policyProvider = new Mock<IAuthorizationPolicyProvider>();
+ policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
+ var next = new TestRequestDelegate();
+ var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
+ var context = GetHttpContext(anonymous: false, endpoint: CreateEndpoint(new AuthorizeAttribute(), new ReqAttribute(req)));
+
+ // Act & Assert
+ await middleware.Invoke(context);
+ Assert.True(calledPolicy);
+ Assert.Equal(assertSuccess, next.Called);
+ }
+
+ [Theory]
+ [InlineData(true, true)]
+ [InlineData(false, true)]
+ [InlineData(true, false)]
+ [InlineData(false, false)]
+ public async Task CanApplyAuthorizeRequirementAttributeDirectlyToEndpoint(bool anonymous, bool assertSuccess)
+ {
+ // Arrange
+ var calledPolicy = false;
+ var req = new AssertionRequirement(_ => { calledPolicy = true; return assertSuccess; });
+ var policyProvider = new Mock<IAuthorizationPolicyProvider>();
+ policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
+ var next = new TestRequestDelegate();
+ var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
+ var context = GetHttpContext(anonymous: anonymous, endpoint: CreateEndpoint(new ReqAuthorizeAttribute(req)));
+
+ // Act & Assert
+ await middleware.Invoke(context);
+ Assert.True(calledPolicy);
+ Assert.Equal(assertSuccess && !anonymous, next.Called);
+ }
+
[Fact]
public async Task Invoke_ValidClaimShouldNotFail()
{