Reputation: 314
I have an web-application built on dotnet core + entityframework core, identity server and MS SQL Server as database (Angular 2 on the frontend). I already have implemented user auth with roles. Right now I'm using the Authorize attribute to control access to different part of my application. Here we have an example of a api function available for 3 different roles:
[Authorize(Roles = "ProductAdministrator, WebEditor, Manager")]
public IActionResult Get()
{
IList<Product> products = _service.GetProducts();
return Ok(products);
}
If you don't have one of these roles on your user, then you will not be able to access this list of products.
The problem I'm facing right now is that the customer I work for want to be able to control role access to each Product object. When created there should probably be some sort of standard set of roles assigned to the object. But a "admin" user should be able to control the access to each object/entity.
For example, we could have this object with some roles:
var carProduct1 = new Product() {
Name = "Volvo XE 90",
Category = "Car",
Price = 51.600,
Roles = ["CarSeller", "ProductAdministrator", "Insurance", "VolvoExpert"]
};
And then this could be another object
var carProduct2 = new Product() {
Name = "Ford Fiesta",
Category = "Car",
Price = 15000,
Roles = ["ProductAdministrator", "CarDealer", "FordExpert", "CarWorkshop"]
};
You probably get the idea now. How can I best go from controlling access on route-level to object-level? Is there any writing on this (my google-searches have not been fruitful)?
Upvotes: 0
Views: 451
Reputation: 4120
Abstract the permission concept away from the Product
. You may want to apply permissions to other objects later too.
public interface IPermissionProtected
{
string[] AllowedGroups { get; }
}
The Product
is a permissible entity.
public class Product : IPermissionProtected
{
...
public Product(..., string[] allowedGroups)
{
this.AllowedGroups = allowedGroups;
}
public string[] AllowedGroups { get; private set;}
}
Maybe when you create the Product
you specify the permissions. Nothing stops you from later changing those permissions too.
var carProduct1 = new Product(new [] {"CarSeller", "ProductAdministrator", "Insurance", "VolvoExpert"}) {
Name = "Volvo XE 90",
Category = "Car",
Price = 51.600
};
You can allow anyone to access the Product
Controller. From this point I am unclear how you want to go exactly, but for example, you could return only the products to which the current user has permission.
public IActionResult Get()
{
IList<Product> products = _service.GetProducts(currentUserRole);
return Ok(products);
}
You can also have this
public IActionResult Get(int productId)
{
try
{
Product product = _service.GetProduct(productId, currentUserRole);
return Ok(product);
}
catch (AuthorizationException ex)
{
return NotAuthorized(ex);
}
}
You can modify the Service
to give you the product, to which the user has permission.
public class Service
{
public IList<Product> GetProducts(string currentUserRole)
{
// compare against Product's AllowedGroups
return this.dataRepo.Products.Where(product => product.AllowedGroups.Contains(currentUserRole)).ToList();
}
public Product GetProduct(int productId, string currentUserRole)
{
// compare against Product's AllowedGroups
var product = this.dataRepo.Products.FirstOrDefault(product => product.Id == productId);
if (!product.AllowedGroups.Contains(currentUserRole))
{
throw new AuthorizationException("{0} not allowed on {1}", currentUserRole, product.Name);
}
}
}
Upvotes: 3