Skip to content

Commit 3831864

Browse files
authored
Merge pull request #2 from Jakab-Laszlo/feature/any-policies
AnyPolicies attribute +semver:minor
2 parents 09cb9a8 + b2516c1 commit 3831864

10 files changed

+238
-1
lines changed

AutSoftCore.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{4E394277
3232
README.md = README.md
3333
EndProjectSection
3434
EndProject
35+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutSoft.AspNetCore.Auth", "src\AutSoft.AspNetCore.Auth\AutSoft.AspNetCore.Auth.csproj", "{6B158B44-E28E-4276-BB65-72CDA4163F39}"
36+
EndProject
3537
Global
3638
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3739
Debug|Any CPU = Debug|Any CPU
@@ -62,6 +64,10 @@ Global
6264
{D4D62897-E6D6-484E-AE4E-F3A0347ADD88}.Debug|Any CPU.Build.0 = Debug|Any CPU
6365
{D4D62897-E6D6-484E-AE4E-F3A0347ADD88}.Release|Any CPU.ActiveCfg = Release|Any CPU
6466
{D4D62897-E6D6-484E-AE4E-F3A0347ADD88}.Release|Any CPU.Build.0 = Release|Any CPU
67+
{6B158B44-E28E-4276-BB65-72CDA4163F39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
68+
{6B158B44-E28E-4276-BB65-72CDA4163F39}.Debug|Any CPU.Build.0 = Debug|Any CPU
69+
{6B158B44-E28E-4276-BB65-72CDA4163F39}.Release|Any CPU.ActiveCfg = Release|Any CPU
70+
{6B158B44-E28E-4276-BB65-72CDA4163F39}.Release|Any CPU.Build.0 = Release|Any CPU
6571
EndGlobalSection
6672
GlobalSection(SolutionProperties) = preSolution
6773
HideSolutionNode = FALSE
@@ -73,6 +79,7 @@ Global
7379
{FF908529-9DDE-4DFB-ABA9-A2045E715B21} = {C46AA3F7-CBFE-428A-AA49-D1A1D40AB8A6}
7480
{28371EF8-D3D3-40CA-AAE9-29D80AE0BF3F} = {C46AA3F7-CBFE-428A-AA49-D1A1D40AB8A6}
7581
{D4D62897-E6D6-484E-AE4E-F3A0347ADD88} = {7029D162-AEB0-4B68-A21E-90ABCD21DAD3}
82+
{6B158B44-E28E-4276-BB65-72CDA4163F39} = {C46AA3F7-CBFE-428A-AA49-D1A1D40AB8A6}
7683
EndGlobalSection
7784
GlobalSection(ExtensibilityGlobals) = postSolution
7885
SolutionGuid = {E4767DF1-41A6-4BF0-A957-2718986D8BC7}

src/AutSoft.All/AutSoft.All.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10+
<ProjectReference Include="..\AutSoft.AspNetCore.Auth\AutSoft.AspNetCore.Auth.csproj" />
1011
<ProjectReference Include="..\AutSoft.DbScaffolding.Identity\AutSoft.DbScaffolding.Identity.csproj" />
1112
<ProjectReference Include="..\AutSoft.Linq\AutSoft.Linq.csproj" />
1213
</ItemGroup>

src/AutSoft.All/ServiceCollectionExtensions.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using AutSoft.AspNetCore.Auth;
12
using AutSoft.Common;
23

34
using Microsoft.Extensions.DependencyInjection;
@@ -15,6 +16,9 @@ public static class ServiceCollectionExtensions
1516
/// <returns>Expanded service collection</returns>
1617
public static IServiceCollection AddAutSoftAll(this IServiceCollection services)
1718
{
18-
return services.AddAutSoftCommon();
19+
services.AddAutSoftAspNetCoreAuth();
20+
services.AddAutSoftCommon();
21+
22+
return services;
1923
}
2024
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using Microsoft.AspNetCore.Authorization;
2+
using Microsoft.Extensions.DependencyInjection;
3+
4+
namespace AutSoft.AspNetCore.Auth.AnyPolicies;
5+
6+
/// <summary>
7+
/// Evulate the policies in the <see cref="AnyPoliciesRequirement"/> with OR relationship,
8+
/// so it is enough for one policy to pass the authorization requirement
9+
/// </summary>
10+
public class AnyPoliciesAuthorizationHandler : AuthorizationHandler<AnyPoliciesRequirement>
11+
{
12+
private readonly IServiceProvider _serviceProvider;
13+
14+
/// <summary>
15+
/// Initialize a new instance of the <see cref="AnyPoliciesAuthorizationHandler"/> class.
16+
/// </summary>
17+
/// <param name="serviceProvider">An instance of <see cref="IServiceProvider"/></param>
18+
public AnyPoliciesAuthorizationHandler(IServiceProvider serviceProvider)
19+
{
20+
// Not possible to inject IAuthorizationService, because it would be a circular reference
21+
_serviceProvider = serviceProvider;
22+
}
23+
24+
/// <summary>
25+
/// Evulate the policies in the <see cref="AnyPoliciesRequirement"/> with OR relationship,
26+
/// with the help of <see cref="IAuthorizationService"/>
27+
/// </summary>
28+
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, AnyPoliciesRequirement requirement)
29+
{
30+
var authorizationService = _serviceProvider.GetRequiredService<IAuthorizationService>();
31+
32+
foreach (var policy in requirement.Policies)
33+
{
34+
var result = await authorizationService.AuthorizeAsync(context.User, context.Resource, policy);
35+
if (result.Succeeded)
36+
{
37+
context.Succeed(requirement);
38+
return;
39+
}
40+
}
41+
}
42+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using Microsoft.AspNetCore.Authorization;
2+
3+
namespace AutSoft.AspNetCore.Auth.AnyPolicies;
4+
5+
/// <summary>
6+
/// The policies, which are specified in the constructor or <see cref="Policies"/> property
7+
/// will be evulated with OR condition with help of <see cref="AnyPoliciesAuthorizationHandler"/>
8+
/// </summary>
9+
/// <remarks>
10+
/// The policies will be evulated in a new dinamically generated policy,
11+
/// what the <see cref="AnyPoliciesPolicyProvider"/> class generate.
12+
/// </remarks>
13+
public class AnyPoliciesAuthorizeAttribute : AuthorizeAttribute
14+
{
15+
private string[] _policies = Array.Empty<string>();
16+
17+
/// <summary>
18+
/// The policies in OR relationship
19+
/// </summary>
20+
public string[] Policies
21+
{
22+
get => _policies;
23+
set
24+
{
25+
_policies = value;
26+
Policy = AnyPoliciesPolicyProvider.GenerateDynamicPolicy(_policies);
27+
}
28+
}
29+
30+
/// <summary>
31+
/// Initialize a new instance of the <see cref="AnyPoliciesAuthorizeAttribute"/> class.
32+
/// </summary>
33+
/// <param name="policies">The policies in OR relationship</param>
34+
public AnyPoliciesAuthorizeAttribute(params string[] policies)
35+
{
36+
Policies = policies;
37+
}
38+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
using Microsoft.AspNetCore.Authorization;
2+
using Microsoft.Extensions.Options;
3+
4+
namespace AutSoft.AspNetCore.Auth.AnyPolicies;
5+
6+
/// <summary>
7+
/// Dynamically generate policies based on <see cref="AnyPoliciesAuthorizeAttribute"/>,
8+
/// that will contain a <see cref="AnyPoliciesRequirement"/> requirement.
9+
/// For anything not <see cref="AnyPoliciesAuthorizeAttribute"/>,
10+
/// the default policy provider (<see cref="DefaultAuthorizationPolicyProvider"/>) will return the policy.
11+
/// </summary>
12+
public class AnyPoliciesPolicyProvider : IAuthorizationPolicyProvider
13+
{
14+
/// <summary>
15+
/// Prefix of dinamically generated policy's name
16+
/// </summary>
17+
public const string PolicyPrefix = "AnyPolicies_";
18+
19+
private readonly DefaultAuthorizationPolicyProvider _fallbackPolicyProvider;
20+
21+
/// <summary>
22+
/// Initialize a new instance of <see cref="AnyPoliciesPolicyProvider"/> class.
23+
/// </summary>
24+
/// <param name="options">Options of provider</param>
25+
public AnyPoliciesPolicyProvider(IOptions<AuthorizationOptions> options)
26+
{
27+
_fallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
28+
}
29+
30+
/// <summary>
31+
/// Return with the name of policies from the name of dynamic policy
32+
/// </summary>
33+
/// <param name="dynamicPolicyName">The name of dinamic policy</param>
34+
/// <returns>The name of policies</returns>
35+
public static IEnumerable<string> GetPolicyNamesFromDynamicPolicy(string dynamicPolicyName)
36+
{
37+
return dynamicPolicyName[PolicyPrefix.Length..].Split(',');
38+
}
39+
40+
/// <summary>
41+
/// Generate a dynamic policy with the OR combine of policies
42+
/// </summary>
43+
/// <param name="policies">The policies to be combine</param>
44+
/// <returns>The dynamic policy</returns>
45+
public static string GenerateDynamicPolicy(IEnumerable<string> policies)
46+
{
47+
return PolicyPrefix + string.Join(",", policies);
48+
}
49+
50+
/// <summary>
51+
/// Return with the default authorization policy
52+
/// </summary>
53+
/// <returns>The default authorization policy</returns>
54+
public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
55+
{
56+
return _fallbackPolicyProvider.GetDefaultPolicyAsync();
57+
}
58+
59+
/// <summary>
60+
/// Return with the authorization policy with the specified policy name
61+
/// </summary>
62+
/// <param name="policyName">The specified policy name</param>
63+
/// <returns>The authorization policy with the specified name</returns>
64+
public Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
65+
{
66+
if (policyName.StartsWith(PolicyPrefix, StringComparison.OrdinalIgnoreCase))
67+
{
68+
var policies = GetPolicyNamesFromDynamicPolicy(policyName).ToArray();
69+
var policy = new AuthorizationPolicyBuilder().AddRequirements(new AnyPoliciesRequirement(policies));
70+
return Task.FromResult((AuthorizationPolicy?)policy.Build());
71+
}
72+
73+
return _fallbackPolicyProvider.GetPolicyAsync(policyName);
74+
}
75+
76+
/// <summary>
77+
/// Return with the fallback policy
78+
/// </summary>
79+
/// <returns>The fallback policy</returns>
80+
public Task<AuthorizationPolicy?> GetFallbackPolicyAsync()
81+
{
82+
return _fallbackPolicyProvider.GetFallbackPolicyAsync();
83+
}
84+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using Microsoft.AspNetCore.Authorization;
2+
3+
namespace AutSoft.AspNetCore.Auth.AnyPolicies;
4+
5+
/// <summary>
6+
/// Requirement of wanted to combine OR authorizations
7+
/// </summary>
8+
public class AnyPoliciesRequirement : IAuthorizationRequirement
9+
{
10+
/// <summary>
11+
/// Initialize a new instance of <see cref="AnyPoliciesRequirement"/> class.
12+
/// </summary>
13+
/// <param name="policies">Array of policies to be combined</param>
14+
public AnyPoliciesRequirement(params string[] policies)
15+
{
16+
Policies = policies;
17+
}
18+
19+
/// <summary>
20+
/// Policies to be combine OR
21+
/// </summary>
22+
public IEnumerable<string> Policies { get; }
23+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net6.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.AspNetCore.Authorization" />
11+
</ItemGroup>
12+
13+
</Project>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using AutSoft.AspNetCore.Auth.AnyPolicies;
2+
3+
using Microsoft.AspNetCore.Authorization;
4+
using Microsoft.Extensions.DependencyInjection;
5+
6+
namespace AutSoft.AspNetCore.Auth;
7+
8+
/// <summary>
9+
/// Register all relevant services into dependency injection container
10+
/// </summary>
11+
public static class ServiceCollectionExtensions
12+
{
13+
/// <summary>
14+
/// Register all relevant services into dependency injection container
15+
/// </summary>
16+
/// <returns>Expanded service collection</returns>
17+
public static IServiceCollection AddAutSoftAspNetCoreAuth(this IServiceCollection services)
18+
{
19+
services.AddSingleton<IAuthorizationPolicyProvider, AnyPoliciesPolicyProvider>();
20+
services.AddSingleton<IAuthorizationHandler, AnyPoliciesAuthorizationHandler>();
21+
22+
return services;
23+
}
24+
}

src/Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<ItemGroup>
88
<PackageVersion Include="AutoMapper" Version="11.0.1" />
99
<PackageVersion Include="EntityFrameworkCore.Scaffolding.Handlebars" Version="6.0.3" />
10+
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="6.0.8" />
1011
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.8" />
1112
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="6.0.8" />
1213
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.8" />

0 commit comments

Comments
 (0)