Reputation: 58
I'm fairly new to setting up security for websites and am having trouble finding the correct architecture/design/pattern/best practice for the type of authentication/authorization I am needing in a .NET MVC environment. I don't even know what to call it in order to do more research. Below is an example of what I need to implement. What is this called? (I don't think it's multi-tennant.)
Joe works inventory for a few stores in a Grocery Store chain. Joe is an Inventory Manager(can edit items) for Store A, but just an Inventory Clerk(only view items) for Store B and has no access to Store C.
So Joe should be able to access the ActionResult Edit
in the InventoryController
if he is trying to edit Store A, but should not be able to access the same ActionResult Edit
if he is trying to edit Store B or C.
The straight-forward Identity or Claims based authorization isn't enough for this scenario (I don't think), but I don't know the "name" of the design I need in order to do further research. What is this design called?
Upvotes: 4
Views: 1512
Reputation: 239250
It's called object-level authorization (aka object-level security, aka fine-grained authorization, etc.). Basically, permissions are based on "ownership" of objects, or perhaps better put in this scenario, being owned by an object. You would need to set up a many-to-many relationship between stores and employees, with payload of a role/grant. For example:
public class StoreEmployee
{
[Key, Column(Order = 1)]
[ForeignKey("Store")]
public int StoreId { get; set; }
public virtual Store Store { get; set; }
[Key, Column(Order = 2)]
[ForeignKey("Employee")]
public int EmployeeId { get; set; }
public virtual Employee Employee { get; set; }
public string Role { get; set; }
}
public class Store
{
...
public virtual ICollection<StoreEmployee> Employees { get; set; }
}
public class Employee
{
...
public virtual ICollection<StoreEmployee> Stores { get; set; }
}
With that, then you can use this relationship in your actions to verify whether a user has access:
if (!joe.Stores.Any(m => m.Store == storeA && m.Role == "Manager"))
{
return new HttpUnauthorizedResult();
}
Here, I kept things simple by just making Role
a string. You could use a enum, or even an actual class that would also be persisted in your database. Or you could tie into the existing roles for users in general. It's up to you. You might also prefer to turn that into a custom action filter.
Upvotes: 3
Reputation: 23436
You could set this up as a multi tenant system. If every store is a tenant with its own user directory, then Joe would need to login to a different directory for store A then for store B and would get a another role assigned.
Joe would not be able to login to store C as he does not have an account in that directory.
If you want users to authenticate through a federated system, you'd need to set up a role per store and assign those based on which IdP the user came from.
Upvotes: 1