OK, Custom Policy Based Authorization in ASP.NET Core. I kinda of understood the idea of this new identity framework, but still not 100% clear what you can achieve with this. Assuming we have an Action in HomeController called List. This action will query and display a list of products from the database. The users that must access this list must be part of the Marketing division. Therefore in our policy we check if user has a claim called Division and the value of that is Marketing. If yes then he will be allowed to see the list otherwise not. We can decorate our action like this:
[Authorize(Policy = "ProductsAccess")]
public IActionResult List()
{
//do query and return the products view model
return View();
}
All good with this. It will work perfectly.
Scenario 1: What if I want to add the policy at the product level, and based on the policy the user will see only the products from his division. So marketing guy will see his products, R&D will see his and so forth. How can I achieve that? Can it be done with a policy? If yes how?
Scenario 2: What about access at the field level? Let's say maybe I want to hide certain fields? Example: all products will have certain columns that must be visible to Managers and hidden to the rest of users? Can this be done using custom policies? If yes how?
Right-click on the solution and add a new class. Enter the class name and click on Add. Next Inherite Attribute, IAuthorizationFilter to CustomAuthorization class which has overridden the OnAuthorization method. The OnAuthorization Method has the AuthorizationFilterContext parameter.
By using Policy-based & Role-based Authorization process, we can provide access to particular area of application to the user based on the Role/Policy of the user.
It stores the request and response information, such as the properties of request, request-related services, and any data to/from the request or errors, if there are any. ASP.NET Core applications access the HTTPContext through the IHttpContextAccessor interface. The HttpContextAccessor class implements it.
AddAuthorizationCore(IServiceCollection)Adds authorization services to the specified IServiceCollection. C# Copy. public static Microsoft.Extensions.DependencyInjection.
For Scenario 1 you can use resource based authorization.
In essence, you'd inject IAuthorizationService
into your service or controller, then and have one or more authorization handlers which derive form AuthorizationHandler<TRequirement, TDocument>
and then call
if(await _authorizationService.AuthorizeAsync(User, document, "MyPolicy"))
{
// Success, user has access to it
}
Downside: You have to fetch all products from database, then filter in memory, so it will work well for single documents or smaller data, where you don't need pagination. Pagination will break it, even on smaller data (i.e. if you request 50 products, but user don't have access to 40 of them, only 10 will be returned, despite the page size being 50).
an alternative will be possible with EF Core 2.0 (that's if you use EF Core as your ORM). You can add global filters, which will applied to all queries to a certain entity.
For more information, see Entity Framework Core 2.0 Announcement blog post:
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public int TenantId {get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasQueryFilter(p => !p.IsDeleted &&
p.TenantId == this.TenantId );
}
}
It may or may not be suitable for your case, it depends if you have a row-level data you can use (i.e. some kind of "resource owner" field).
Scenario 2 isn't possible out of the box with Identity as far as I know, you'd have to implement something on your own, but it's a very complex topic (if you ever worked with Dynamics CRM, you know what I mean).
Update
Just for an quick implementation, you can try to wrap your response around an ExpandoObject
(that's what's used underlying when you use dynamic
keyword) and the iterate over it, removing properties the user doesn't have access to before returning it from the controller action or write an Authorization filter, which will automatically do that for specific or all controllers.
For an rough idea (on how to construct/use the expando object), see my answer here.
I don't think policies were designed to solve the cases you have. I'm uncertain if it's possible, and even if it would, I feel like the code itself would suffer from the complexity and confusion it would bring. I wouldn't give my authorization filters that much responsibility.
As for your second scenario, you could just skip outputting certain data in your view based on the role, claim or whatever of the current user. Seems unnecessarily complex to solve that with policies.
Use policies for what it was made for, authorizing if the user is allowed to even run a method. Any differences to what is being returned? Handle it in the normal code flow.
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