Skip to content

Commit 4942713

Browse files
authored
Add AuthorizationBuilder (#42264)
1 parent 36f0f25 commit 4942713

File tree

7 files changed

+307
-92
lines changed

7 files changed

+307
-92
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using Microsoft.Extensions.DependencyInjection;
7+
8+
namespace Microsoft.AspNetCore.Authorization;
9+
10+
/// <summary>
11+
/// Used to configure authorization
12+
/// </summary>
13+
public class AuthorizationBuilder
14+
{
15+
/// <summary>
16+
/// Initializes a new instance of <see cref="AuthorizationBuilder"/>.
17+
/// </summary>
18+
/// <param name="services">The services being configured.</param>
19+
public AuthorizationBuilder(IServiceCollection services)
20+
=> Services = services;
21+
22+
/// <summary>
23+
/// The services being configured.
24+
/// </summary>
25+
public virtual IServiceCollection Services { get; }
26+
27+
/// <summary>
28+
/// Determines whether authorization handlers should be invoked after <see cref="AuthorizationHandlerContext.HasFailed"/>.
29+
/// Defaults to true.
30+
/// </summary>
31+
/// <returns>The builder.</returns>
32+
public virtual AuthorizationBuilder SetInvokeHandlersAfterFailure(bool invoke)
33+
{
34+
Services.Configure<AuthorizationOptions>(o => o.InvokeHandlersAfterFailure = invoke);
35+
return this;
36+
}
37+
38+
/// <summary>
39+
/// Sets the default authorization policy. Defaults to require authenticated users.
40+
/// </summary>
41+
/// <remarks>
42+
/// The default policy used when evaluating <see cref="IAuthorizeData"/> with no policy name specified.
43+
/// </remarks>
44+
/// <returns>The builder.</returns>
45+
public virtual AuthorizationBuilder SetDefaultPolicy(AuthorizationPolicy policy)
46+
{
47+
Services.Configure<AuthorizationOptions>(o => o.DefaultPolicy = policy);
48+
return this;
49+
}
50+
51+
/// <summary>
52+
/// Sets the fallback authorization policy used by <see cref="AuthorizationPolicy.CombineAsync(IAuthorizationPolicyProvider, IEnumerable{IAuthorizeData})"/>
53+
/// when no IAuthorizeData have been provided. As a result, the AuthorizationMiddleware uses the fallback policy
54+
/// if there are no <see cref="IAuthorizeData"/> instances for a resource. If a resource has any <see cref="IAuthorizeData"/>
55+
/// then they are evaluated instead of the fallback policy. By default the fallback policy is null, and usually will have no
56+
/// effect unless you have the AuthorizationMiddleware in your pipeline. It is not used in any way by the
57+
/// default <see cref="IAuthorizationService"/>.
58+
/// </summary>
59+
/// <returns>The builder.</returns>
60+
public virtual AuthorizationBuilder SetFallbackPolicy(AuthorizationPolicy? policy)
61+
{
62+
Services.Configure<AuthorizationOptions>(o => o.FallbackPolicy = policy);
63+
return this;
64+
}
65+
66+
/// <summary>
67+
/// Adds a <see cref="AuthorizationPolicy"/> which can be used by <see cref="IAuthorizationService"/>.
68+
/// </summary>
69+
/// <param name="name">The name of this policy.</param>
70+
/// <param name="policy">The <see cref="AuthorizationPolicy"/>.></param>
71+
/// <returns>The builder.</returns>
72+
public virtual AuthorizationBuilder AddPolicy(string name, AuthorizationPolicy policy)
73+
{
74+
Services.Configure<AuthorizationOptions>(o => o.AddPolicy(name, policy));
75+
return this;
76+
}
77+
78+
/// <summary>
79+
/// Add a policy that is built from a delegate with the provided name.
80+
/// </summary>
81+
/// <param name="name">The name of the policy.</param>
82+
/// <param name="configurePolicy">The delegate that will be used to build the policy.</param>
83+
/// <returns>The builder.</returns>
84+
public virtual AuthorizationBuilder AddPolicy(string name, Action<AuthorizationPolicyBuilder> configurePolicy)
85+
{
86+
Services.Configure<AuthorizationOptions>(o => o.AddPolicy(name, configurePolicy));
87+
return this;
88+
}
89+
90+
/// <summary>
91+
/// Add a policy that is built from a delegate with the provided name and used as the default policy.
92+
/// </summary>
93+
/// <param name="name">The name of the default policy.</param>
94+
/// <param name="policy">The default <see cref="AuthorizationPolicy"/>.></param>
95+
/// <returns>The builder.</returns>
96+
public virtual AuthorizationBuilder AddDefaultPolicy(string name, AuthorizationPolicy policy)
97+
{
98+
SetDefaultPolicy(policy);
99+
return AddPolicy(name, policy);
100+
}
101+
102+
/// <summary>
103+
/// Add a policy that is built from a delegate with the provided name and used as the DefaultPolicy.
104+
/// </summary>
105+
/// <param name="name">The name of the DefaultPolicy.</param>
106+
/// <param name="configurePolicy">The delegate that will be used to build the DefaultPolicy.</param>
107+
/// <returns>The builder.</returns>
108+
public virtual AuthorizationBuilder AddDefaultPolicy(string name, Action<AuthorizationPolicyBuilder> configurePolicy)
109+
{
110+
if (configurePolicy == null)
111+
{
112+
throw new ArgumentNullException(nameof(configurePolicy));
113+
}
114+
115+
var policyBuilder = new AuthorizationPolicyBuilder();
116+
configurePolicy(policyBuilder);
117+
return AddDefaultPolicy(name, policyBuilder.Build());
118+
}
119+
120+
/// <summary>
121+
/// Add a policy that is built from a delegate with the provided name and used as the FallbackPolicy.
122+
/// </summary>
123+
/// <param name="name">The name of the FallbackPolicy.</param>
124+
/// <param name="policy">The Fallback <see cref="AuthorizationPolicy"/>.></param>
125+
/// <returns>The builder.</returns>
126+
public virtual AuthorizationBuilder AddFallbackPolicy(string name, AuthorizationPolicy policy)
127+
{
128+
SetFallbackPolicy(policy);
129+
return AddPolicy(name, policy);
130+
}
131+
132+
/// <summary>
133+
/// Add a policy that is built from a delegate with the provided name and used as the FallbackPolicy.
134+
/// </summary>
135+
/// <param name="name">The name of the Fallback policy.</param>
136+
/// <param name="configurePolicy">The delegate that will be used to build the Fallback policy.</param>
137+
/// <returns>The builder.</returns>
138+
public virtual AuthorizationBuilder AddFallbackPolicy(string name, Action<AuthorizationPolicyBuilder> configurePolicy)
139+
{
140+
if (configurePolicy == null)
141+
{
142+
throw new ArgumentNullException(nameof(configurePolicy));
143+
}
144+
145+
var policyBuilder = new AuthorizationPolicyBuilder();
146+
configurePolicy(policyBuilder);
147+
return AddFallbackPolicy(name, policyBuilder.Build());
148+
}
149+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class AuthorizationOptions
1414
private Dictionary<string, AuthorizationPolicy> PolicyMap { get; } = new Dictionary<string, AuthorizationPolicy>(StringComparer.OrdinalIgnoreCase);
1515

1616
/// <summary>
17-
/// Determines whether authentication handlers should be invoked after <see cref="AuthorizationHandlerContext.HasFailed"/>.
17+
/// Determines whether authorization handlers should be invoked after <see cref="AuthorizationHandlerContext.HasFailed"/>.
1818
/// Defaults to true.
1919
/// </summary>
2020
public bool InvokeHandlersAfterFailure { get; set; } = true;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
11
#nullable enable
2+
Microsoft.AspNetCore.Authorization.AuthorizationBuilder
3+
Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AuthorizationBuilder(Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> void
24
Microsoft.AspNetCore.Authorization.Infrastructure.PassThroughAuthorizationHandler.PassThroughAuthorizationHandler(Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Authorization.AuthorizationOptions!>! options) -> void
35
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?>!
6+
virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddDefaultPolicy(string! name, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder!
7+
virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddDefaultPolicy(string! name, System.Action<Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder!>! configurePolicy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder!
8+
virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddFallbackPolicy(string! name, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder!
9+
virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddFallbackPolicy(string! name, System.Action<Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder!>! configurePolicy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder!
10+
virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddPolicy(string! name, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder!
11+
virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.AddPolicy(string! name, System.Action<Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder!>! configurePolicy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder!
12+
virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.Services.get -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
13+
virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.SetDefaultPolicy(Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder!
14+
virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.SetFallbackPolicy(Microsoft.AspNetCore.Authorization.AuthorizationPolicy? policy) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder!
15+
virtual Microsoft.AspNetCore.Authorization.AuthorizationBuilder.SetInvokeHandlersAfterFailure(bool invoke) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder!

src/Security/Authorization/Policy/src/PolicyServiceCollectionExtensions.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ namespace Microsoft.Extensions.DependencyInjection;
1212
/// </summary>
1313
public static class PolicyServiceCollectionExtensions
1414
{
15+
/// <summary>
16+
/// Adds authorization services to the specified <see cref="IServiceCollection" />.
17+
/// </summary>
18+
/// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
19+
/// <returns>The <see cref="AuthorizationBuilder"/> so that additional calls can be chained.</returns>
20+
public static AuthorizationBuilder AddAuthorizationBuilder(this IServiceCollection services)
21+
=> new AuthorizationBuilder(services.AddAuthorization());
22+
1523
/// <summary>
1624
/// Adds the authorization policy evaluator service to the specified <see cref="IServiceCollection" />.
1725
/// </summary>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
#nullable enable
22
static Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.RequireAuthorization<TBuilder>(this TBuilder builder, Microsoft.AspNetCore.Authorization.AuthorizationPolicy! policy) -> TBuilder
33
static Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.RequireAuthorization<TBuilder>(this TBuilder builder, System.Action<Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder!>! configurePolicy) -> TBuilder
4+
static Microsoft.Extensions.DependencyInjection.PolicyServiceCollectionExtensions.AddAuthorizationBuilder(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.AspNetCore.Authorization.AuthorizationBuilder!
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Microsoft.AspNetCore.Authorization.Infrastructure;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using Microsoft.Extensions.Options;
7+
8+
namespace Microsoft.AspNetCore.Authorization.Test;
9+
10+
public class AuthorizationBuilderTests
11+
{
12+
[Fact]
13+
public void CanSetFallbackPolicy()
14+
{
15+
// Arrange
16+
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
17+
var builder = TestHelpers.CreateAuthorizationBuilder()
18+
// Act
19+
.SetFallbackPolicy(policy);
20+
21+
var options = builder.Services.BuildServiceProvider().GetRequiredService<IOptions<AuthorizationOptions>>().Value;
22+
23+
// Assert
24+
Assert.Equal(policy, options.FallbackPolicy);
25+
}
26+
27+
[Fact]
28+
public void CanUnSetFallbackPolicy()
29+
{
30+
// Arrange
31+
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
32+
var builder = TestHelpers.CreateAuthorizationBuilder()
33+
.SetFallbackPolicy(policy)
34+
// Act
35+
.SetFallbackPolicy(null);
36+
37+
var options = builder.Services.BuildServiceProvider().GetRequiredService<IOptions<AuthorizationOptions>>().Value;
38+
39+
// Assert
40+
Assert.Null(options.FallbackPolicy);
41+
}
42+
43+
[Fact]
44+
public void CanSetDefaultPolicy()
45+
{
46+
// Arrange
47+
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
48+
var builder = TestHelpers.CreateAuthorizationBuilder()
49+
// Act
50+
.SetDefaultPolicy(policy);
51+
52+
var options = builder.Services.BuildServiceProvider().GetRequiredService<IOptions<AuthorizationOptions>>().Value;
53+
54+
// Assert
55+
Assert.Equal(policy, options.DefaultPolicy);
56+
}
57+
58+
[Theory]
59+
[InlineData(true)]
60+
[InlineData(false)]
61+
public void CanSetInvokeHandlersAfterFailure(bool invoke)
62+
{
63+
// Arrange
64+
var builder = TestHelpers.CreateAuthorizationBuilder()
65+
// Act
66+
.SetInvokeHandlersAfterFailure(invoke);
67+
68+
var options = builder.Services.BuildServiceProvider().GetRequiredService<IOptions<AuthorizationOptions>>().Value;
69+
70+
// Assert
71+
Assert.Equal(invoke, options.InvokeHandlersAfterFailure);
72+
}
73+
74+
[Fact]
75+
public void CanAddPolicyInstance()
76+
{
77+
// Arrange
78+
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
79+
var builder = TestHelpers.CreateAuthorizationBuilder()
80+
// Act
81+
.AddPolicy("name", policy);
82+
83+
var options = builder.Services.BuildServiceProvider().GetRequiredService<IOptions<AuthorizationOptions>>().Value;
84+
85+
// Assert
86+
Assert.Equal(policy, options.GetPolicy("name"));
87+
}
88+
89+
[Fact]
90+
public void CanAddPolicyDelegate()
91+
{
92+
// Arrange
93+
var builder = TestHelpers.CreateAuthorizationBuilder()
94+
// Act
95+
.AddPolicy("name", p => p.RequireAssertion(_ => true));
96+
97+
var options = builder.Services.BuildServiceProvider().GetRequiredService<IOptions<AuthorizationOptions>>().Value;
98+
99+
// Assert
100+
var policy = options.GetPolicy("name");
101+
Assert.NotNull(policy);
102+
Assert.Equal(1, policy.Requirements.Count);
103+
Assert.IsType<AssertionRequirement>(policy.Requirements.First());
104+
}
105+
}
106+
107+
internal class TestHelpers
108+
{
109+
public static AuthorizationBuilder CreateAuthorizationBuilder()
110+
{
111+
var services = new ServiceCollection();
112+
services.AddLogging();
113+
services.AddOptions();
114+
return services.AddAuthorizationBuilder();
115+
}
116+
}

0 commit comments

Comments
 (0)