Skip to content

Commit e7c11f9

Browse files
committed
Revert "Remove RequiredPolicy (#9399)"
This reverts commit 47ae9d9.
1 parent c130c06 commit e7c11f9

File tree

8 files changed

+90
-7
lines changed

8 files changed

+90
-7
lines changed

src/Security/Authorization/Core/ref/Microsoft.AspNetCore.Authorization.netcoreapp3.0.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public partial class AuthorizationOptions
5252
public AuthorizationOptions() { }
5353
public Microsoft.AspNetCore.Authorization.AuthorizationPolicy DefaultPolicy { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
5454
public bool InvokeHandlersAfterFailure { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
55+
public Microsoft.AspNetCore.Authorization.AuthorizationPolicy RequiredPolicy { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
5556
public void AddPolicy(string name, Microsoft.AspNetCore.Authorization.AuthorizationPolicy policy) { }
5657
public void AddPolicy(string name, System.Action<Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder> configurePolicy) { }
5758
public Microsoft.AspNetCore.Authorization.AuthorizationPolicy GetPolicy(string name) { throw null; }
@@ -131,6 +132,7 @@ public partial class DefaultAuthorizationPolicyProvider : Microsoft.AspNetCore.A
131132
public DefaultAuthorizationPolicyProvider(Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Authorization.AuthorizationOptions> options) { }
132133
public System.Threading.Tasks.Task<Microsoft.AspNetCore.Authorization.AuthorizationPolicy> GetDefaultPolicyAsync() { throw null; }
133134
public virtual System.Threading.Tasks.Task<Microsoft.AspNetCore.Authorization.AuthorizationPolicy> GetPolicyAsync(string policyName) { throw null; }
135+
public System.Threading.Tasks.Task<Microsoft.AspNetCore.Authorization.AuthorizationPolicy> GetRequiredPolicyAsync() { throw null; }
134136
}
135137
public partial class DefaultAuthorizationService : Microsoft.AspNetCore.Authorization.IAuthorizationService
136138
{
@@ -160,6 +162,7 @@ public partial interface IAuthorizationPolicyProvider
160162
{
161163
System.Threading.Tasks.Task<Microsoft.AspNetCore.Authorization.AuthorizationPolicy> GetDefaultPolicyAsync();
162164
System.Threading.Tasks.Task<Microsoft.AspNetCore.Authorization.AuthorizationPolicy> GetPolicyAsync(string policyName);
165+
System.Threading.Tasks.Task<Microsoft.AspNetCore.Authorization.AuthorizationPolicy> GetRequiredPolicyAsync();
163166
}
164167
public partial interface IAuthorizationRequirement
165168
{

src/Security/Authorization/Core/src/AuthorizationOptions.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@ public class AuthorizationOptions
2727
/// </remarks>
2828
public AuthorizationPolicy DefaultPolicy { get; set; } = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
2929

30+
/// <summary>
31+
/// Gets or sets the required authorization policy. Defaults to null.
32+
/// </summary>
33+
/// <remarks>
34+
/// By default the required policy is null.
35+
///
36+
/// If a required policy has been specified then it is always evaluated, even if there are no
37+
/// <see cref="IAuthorizeData"/> instances for a resource. If a resource has <see cref="IAuthorizeData"/>
38+
/// then they are evaluated together with the required policy.
39+
/// </remarks>
40+
public AuthorizationPolicy RequiredPolicy { get; set; }
41+
3042
/// <summary>
3143
/// Add an authorization policy with the provided name.
3244
/// </summary>

src/Security/Authorization/Core/src/AuthorizationPolicy.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,17 @@ public static async Task<AuthorizationPolicy> CombineAsync(IAuthorizationPolicyP
176176
}
177177
}
178178

179+
var requiredPolicy = await policyProvider.GetRequiredPolicyAsync();
180+
if (requiredPolicy != null)
181+
{
182+
if (policyBuilder == null)
183+
{
184+
policyBuilder = new AuthorizationPolicyBuilder();
185+
}
186+
187+
policyBuilder.Combine(requiredPolicy);
188+
}
189+
179190
return policyBuilder?.Build();
180191
}
181192
}

src/Security/Authorization/Core/src/DefaultAuthorizationPolicyProvider.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public class DefaultAuthorizationPolicyProvider : IAuthorizationPolicyProvider
1515
{
1616
private readonly AuthorizationOptions _options;
1717
private Task<AuthorizationPolicy> _cachedDefaultPolicy;
18+
private Task<AuthorizationPolicy> _cachedRequiredPolicy;
1819

1920
/// <summary>
2021
/// Creates a new instance of <see cref="DefaultAuthorizationPolicyProvider"/>.
@@ -39,6 +40,15 @@ public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
3940
return GetCachedPolicy(ref _cachedDefaultPolicy, _options.DefaultPolicy);
4041
}
4142

43+
/// <summary>
44+
/// Gets the required authorization policy.
45+
/// </summary>
46+
/// <returns>The required authorization policy.</returns>
47+
public Task<AuthorizationPolicy> GetRequiredPolicyAsync()
48+
{
49+
return GetCachedPolicy(ref _cachedRequiredPolicy, _options.RequiredPolicy);
50+
}
51+
4252
private Task<AuthorizationPolicy> GetCachedPolicy(ref Task<AuthorizationPolicy> cachedPolicy, AuthorizationPolicy currentPolicy)
4353
{
4454
var local = cachedPolicy;

src/Security/Authorization/Core/src/IAuthorizationPolicyProvider.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,11 @@ public interface IAuthorizationPolicyProvider
2222
/// </summary>
2323
/// <returns>The default authorization policy.</returns>
2424
Task<AuthorizationPolicy> GetDefaultPolicyAsync();
25+
26+
/// <summary>
27+
/// Gets the required authorization policy.
28+
/// </summary>
29+
/// <returns>The required authorization policy.</returns>
30+
Task<AuthorizationPolicy> GetRequiredPolicyAsync();
2531
}
2632
}

src/Security/Authorization/Core/src/Policy/AuthorizationMiddleware.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,6 @@ public async Task Invoke(HttpContext context)
5151

5252
// IMPORTANT: Changes to authorization logic should be mirrored in MVC's AuthorizeFilter
5353
var authorizeData = endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() ?? Array.Empty<IAuthorizeData>();
54-
if (authorizeData.Count() == 0)
55-
{
56-
await _next(context);
57-
return;
58-
}
59-
6054
var policy = await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData);
6155
if (policy == null)
6256
{

src/Security/Authorization/test/AuthorizationMiddlewareTests.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,25 @@ public async Task NoEndpoint_AnonymousUser_Allows()
4141
Assert.True(next.Called);
4242
}
4343

44+
[Fact]
45+
public async Task NoEndpointWithRequired_AnonymousUser_Challenges()
46+
{
47+
// Arrange
48+
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
49+
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
50+
policyProvider.Setup(p => p.GetRequiredPolicyAsync()).ReturnsAsync(policy);
51+
var next = new TestRequestDelegate();
52+
53+
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
54+
var context = GetHttpContext(anonymous: true);
55+
56+
// Act
57+
await middleware.Invoke(context);
58+
59+
// Assert
60+
Assert.False(next.Called);
61+
}
62+
4463
[Fact]
4564
public async Task HasEndpointWithoutAuth_AnonymousUser_Allows()
4665
{
@@ -60,6 +79,26 @@ public async Task HasEndpointWithoutAuth_AnonymousUser_Allows()
6079
Assert.True(next.Called);
6180
}
6281

82+
[Fact]
83+
public async Task HasEndpointWithRequiredWithoutAuth_AnonymousUser_Challenges()
84+
{
85+
// Arrange
86+
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
87+
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
88+
policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy);
89+
policyProvider.Setup(p => p.GetRequiredPolicyAsync()).ReturnsAsync(policy);
90+
var next = new TestRequestDelegate();
91+
92+
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
93+
var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint());
94+
95+
// Act
96+
await middleware.Invoke(context);
97+
98+
// Assert
99+
Assert.False(next.Called);
100+
}
101+
63102
[Fact]
64103
public async Task HasEndpointWithAuth_AnonymousUser_Challenges()
65104
{
@@ -109,23 +148,29 @@ public async Task OnAuthorizationAsync_WillCallPolicyProvider()
109148
var policy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build();
110149
var policyProvider = new Mock<IAuthorizationPolicyProvider>();
111150
var getPolicyCount = 0;
151+
var getRequiredPolicyCount = 0;
112152
policyProvider.Setup(p => p.GetPolicyAsync(It.IsAny<string>())).ReturnsAsync(policy)
113153
.Callback(() => getPolicyCount++);
154+
policyProvider.Setup(p => p.GetRequiredPolicyAsync()).ReturnsAsync(policy)
155+
.Callback(() => getRequiredPolicyCount++);
114156
var next = new TestRequestDelegate();
115157
var middleware = CreateMiddleware(next.Invoke, policyProvider.Object);
116158
var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizeAttribute("whatever")));
117159

118160
// Act & Assert
119161
await middleware.Invoke(context);
120162
Assert.Equal(1, getPolicyCount);
163+
Assert.Equal(1, getRequiredPolicyCount);
121164
Assert.Equal(1, next.CalledCount);
122165

123166
await middleware.Invoke(context);
124167
Assert.Equal(2, getPolicyCount);
168+
Assert.Equal(2, getRequiredPolicyCount);
125169
Assert.Equal(2, next.CalledCount);
126170

127171
await middleware.Invoke(context);
128172
Assert.Equal(3, getPolicyCount);
173+
Assert.Equal(3, getRequiredPolicyCount);
129174
Assert.Equal(3, next.CalledCount);
130175
}
131176

src/Security/samples/CustomPolicyProvider/Authorization/MinimumAgePolicyProvider.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public MinimumAgePolicyProvider(IOptions<AuthorizationOptions> options)
2727

2828
public Task<AuthorizationPolicy> GetDefaultPolicyAsync() => FallbackPolicyProvider.GetDefaultPolicyAsync();
2929

30+
public Task<AuthorizationPolicy> GetRequiredPolicyAsync() => FallbackPolicyProvider.GetRequiredPolicyAsync();
31+
3032
// Policies are looked up by string name, so expect 'parameters' (like age)
3133
// to be embedded in the policy names. This is abstracted away from developers
3234
// by the more strongly-typed attributes derived from AuthorizeAttribute
@@ -47,4 +49,4 @@ public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
4749
return FallbackPolicyProvider.GetPolicyAsync(policyName);
4850
}
4951
}
50-
}
52+
}

0 commit comments

Comments
 (0)