henrik
henrik

Reputation: 314

How to control users read/write access to entities?

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

Answers (1)

hyankov
hyankov

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

Related Questions