In ASP.Net Core we defined authorization policy in ConfigureServices method as below.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthorization(options =>
{
options.AddPolicy("Founders", policy =>
policy.RequireClaim ("EmployeeNumber", "1", "2", "3", "4", "5"));
}
}
and we use it on controller action as below.
[Authorize("Founders")]
public IActionResult GenerateReport()
{
return View();
}
This is all fine and works perfectly. Now my question is, instead of defining "Founders" policy in code (using AddPolicy method in above snippet) how can I add policy details (name,claimtype, value) from database? Is there any hook provided in framework which I can use to populate policies from database?
Idea here is I should be able to add new Employee numbers to list (in database) and policy should be evaluated against that list. Obliviously I can pull this list from database in ConfigureServices itself however any new addition to employee list will not be picked up until application is restarted. Any pointers around this highly appreciated.
Role-Based Authorization in ASP.NET Core You can specify what roles are authorized to access a specific resource by using the [Authorize] attribute. You can even declare them in such a way that the authorization evaluates at the controller level, action level, or even at a global level. Let's take Slack as an example.
Authorization Policy The user must satisfy all the requirements. We Add the policy using the AddAuthorization method in the ConfigureServices of the startup class. options. AddPolicy("AdminOnly", policy => policy.
Authorization in ASP.NET Core is controlled with AuthorizeAttribute and its various parameters. In its most basic form, applying the [Authorize] attribute to a controller, action, or Razor Page, limits access to that component to authenticated users. Now only authenticated users can access the Logout function.
You need to combine Requirement and Authorization handlers, but not to populate policies from database, but for doing whatever authentication mechanism that you want to do.
First create a requirement class to feed to to the policy. Let's name it ThePolicyRequirement
for now, more on this class later. second, create ThePolicyAuthorizationHandler
and add it as a scoped service
services.AddAuthorization(options =>
{
options.AddPolicy("ThePolicy", policy => policy.Requirements.Add( new ThePolicyRequirement() ));
});
services.AddScoped<IAuthorizationHandler, ThePolicyAuthorizationHandler>();
The key is that we can inject pretty much anything in ThePolicyAuthorizationHandler
, and then we pass those injected objects and any other objects that are available at hand to ThePolicyRequirement
, for it to determine wether the user is authenticated or not.
For example, here's my ThePolicyAuthorizationHandler
for asp.net core 2 and 3:
public class ThePolicyAuthorizationHandler : AuthorizationHandler<ThePolicyRequirement>
{
readonly AppDbContext _context;
readonly IHttpContextAccessor _contextAccessor;
public ThePolicyAuthorizationHandler(DbContext c, IHttpContextAccessor ca)
{
_context = c;
_contextAccessor = ca;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ThePolicyRequirement requirement)
{
if (context.Resource is AuthorizationFilterContext filterContext)
{
var area = (filterContext.RouteData.Values["area"] as string)?.ToLower();
var controller = (filterContext.RouteData.Values["controller"] as string)?.ToLower();
var action = (filterContext.RouteData.Values["action"] as string)?.ToLower();
var id = (filterContext.RouteData.Values["id"] as string)?.ToLower();
if (await requirement.Pass(_context, _contextAccessor, area, controller, action, id))
{
context.Succeed(requirement);
}
}
else if (context.Resource is PolicyResource policyResource)
{
var pr = policyResource;
if (await requirement.Pass(_context, _contextAccessor, pr.Area, pr.Controller, pr.Action, pr.Id))
{
context.Succeed(requirement);
}
}
}
}
UPDATE
apparently, things have changes a bit in .net 5, below ThePolicyAuthorizationHandler
for .net 5:
public class ThePolicyAuthorizationHandler : AuthorizationHandler<ThePolicyRequirement>
{
readonly AppDbContext _context;
readonly IHttpContextAccessor _contextAccessor;
public ThePolicyAuthorizationHandler(DbContext c, IHttpContextAccessor ca)
{
_context = c;
_contextAccessor = ca;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ThePolicyRequirement requirement)
{
if (context.Resource is AuthorizationFilterContext filterContext)
{
var area = (filterContext.RouteData.Values["area"] as string)?.ToLower();
var controller = (filterContext.RouteData.Values["controller"] as string)?.ToLower();
var action = (filterContext.RouteData.Values["action"] as string)?.ToLower();
var id = (filterContext.RouteData.Values["id"] as string)?.ToLower();
if (await req.Pass(_context, _contextAccessor, area, controller, action, id))
{
context.Succeed(req);
}
}
if (context.Resource is DefaultHttpContext httpContext)
{
var area = httpContext.Request.RouteValues["area"].ToString();
var controller = httpContext.Request.RouteValues["controller"].ToString();
var action = httpContext.Request.RouteValues["action"].ToString();
var id = httpContext.Request.RouteValues["id"].ToString();
if (await req.Pass(_context, _contextAccessor, area, controller, action, id))
{
context.Succeed(req);
}
}
}
}
and my Requirement
class:
public class ThePolicyRequirement : IAuthorizationRequirement
{
AppDbContext _context;
IHttpContextAccessor _contextAccessor;
public async Task<bool> Pass(AppDbContext context, IHttpContextAccessor contextAccessor, string area, string controller, string action, string id)
{
_context = context;
_contextAccessor = contextAccessor;
//authorization logic goes here
return await Task.FromResult(false);
}
}
in my example, I pass my AppDbContext
and HttpContextAssessor
to ThePolicyRequirement
, but refrain from passing the AuthorizationHandlerContext
because in my case, i just need the area/controller/action name only. The important thing is we can pass almost any information available in our whole application to it.
Hope this will help.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With