Hi,@mc,You could try claim based authorization as AgaveJoe suggested,and custom Authorization Providers to avoid registing plenty of policies statically ,a minimal example as below:
public static class PolicyGenerator
{
public const string Prefix = "Permit";
public const string Separator = "_";
}
public class PermissionAuthorizeAttribute : AuthorizeAttribute
{
public PermissionAuthorizeAttribute(string controller,string action)
{
Policy = $"{PolicyGenerator.Prefix}{PolicyGenerator.Separator}{controller}{PolicyGenerator.Separator}{action}";
}
}
public class PermissionRequirement : IAuthorizationRequirement
{
public string Controller { get; }
public string Action { get; }
public string Permission { get; }
public PermissionRequirement(
string controller, string action)
{
Controller = controller;
Action = action;
Permission = $"{PolicyGenerator.Prefix}{PolicyGenerator.Separator}{controller}{PolicyGenerator.Separator}{action}";
}
}
public class PermissionAuthorizationPolicyProvider : DefaultAuthorizationPolicyProvider
{
public PermissionAuthorizationPolicyProvider(
IOptions<AuthorizationOptions> options) : base(options) { }
public override async Task<AuthorizationPolicy?> GetPolicyAsync(
string policyName)
{
if (!policyName.StartsWith(PolicyGenerator.Prefix, StringComparison.OrdinalIgnoreCase))
return await base.GetPolicyAsync(policyName);
// extract the permissions from the policyname ,you could modify the method for your requirement
string[] permissions = GetPermissionsFromPolicy(policyName);
var requirement = new PermissionRequirement(permissions[1], permissions[2]);
// create a policy, add the requirement
return new AuthorizationPolicyBuilder()
.AddRequirements(requirement).Build();
}
public string[] GetPermissionsFromPolicy(string policyName)
{
return policyName.Split(PolicyGenerator.Separator);
}
}
public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context, PermissionRequirement requirement)
{
if (context.User.HasClaim("Permission", requirement.Permission))
{
context.Succeed(requirement);
return Task.CompletedTask;
}
context.Fail();
return Task.CompletedTask;
}
}
Regist the services:
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(.....);
builder.Services.AddAuthorization();
builder.Services.AddSingleton<IAuthorizationHandler, PermissionHandler>();
// Overrides the DefaultAuthorizationPolicyProvider with our own
builder.Services.AddSingleton<IAuthorizationPolicyProvider, PermissionAuthorizationPolicyProvider>();
Tried with CookieAuthentication:
[HttpPost]
public async Task<IActionResult> Login(string User,string Controller,string Action)
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, User),
//you could add your logical to allow different user visit different endpoint here
new Claim("Permission", $"{PolicyGenerator.Prefix}{PolicyGenerator.Separator}{Controller}{PolicyGenerator.Separator}{Action}"),
//You could add the claim again with different value for other endpoint
new Claim("Permission", "Permit_Product_Create"),
};
var claimsIdentity = new ClaimsIdentity(
claims, CookieAuthenticationDefaults.AuthenticationScheme);
var authProperties = new AuthenticationProperties
{
};
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
authProperties);
return Ok("Ok");
}
Attach the custom Attribute to your actions accroding to the name of controller/action
Result:
If you want to hide some content for not authorized User,You could try with View-Based Authorization
For example:
@using Microsoft.AspNetCore.Authorization
@inject IAuthorizationService AuthorizationService
@{
ViewData["Title"] = "Index";
}
<h1>Product</h1>
@if ((await AuthorizationService.AuthorizeAsync(User, "Permit_Product_Create")).Succeeded)
{
<p>Create</p>
}
@if ((await AuthorizationService.AuthorizeAsync(User, "Permit_Product_Delete")).Succeeded)
{
<p>Delete</p>
}
@if ((await AuthorizationService.AuthorizeAsync(User, "Permit_Product_Update")).Succeeded)
{
<p>Update</p>
}
Result: