Skip to content

Commit 74c1690

Browse files
committed
Revert "Remove RequiredPolicy (#9399)"
This reverts commit 47ae9d9.
1 parent c9783a6 commit 74c1690

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
@@ -50,12 +50,6 @@ public async Task Invoke(HttpContext context)
5050

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

src/Security/Authorization/test/AuthorizationMiddlewareTests.cs

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

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

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

117159
// Act & Assert
118160
await middleware.Invoke(context);
119161
Assert.Equal(1, getPolicyCount);
162+
Assert.Equal(1, getRequiredPolicyCount);
120163
Assert.Equal(1, next.CalledCount);
121164

122165
await middleware.Invoke(context);
123166
Assert.Equal(2, getPolicyCount);
167+
Assert.Equal(2, getRequiredPolicyCount);
124168
Assert.Equal(2, next.CalledCount);
125169

126170
await middleware.Invoke(context);
127171
Assert.Equal(3, getPolicyCount);
172+
Assert.Equal(3, getRequiredPolicyCount);
128173
Assert.Equal(3, next.CalledCount);
129174
}
130175

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)