I have the following database structure
and I have to build authentication/authorization for an ASP.NET MVC 5 site.
The DB schema works as follows: every user belongs to a group, and each group can be granted/denied a permission. Each permission matches an action on a controller (let's say, a Petitions controller would have the following actions: List, View, Add, Edit, Delete, VoteFor, VoteAgainst, Reject and Approve, and each action is an entry in the Permissions table).
The purpose of all this is that, each time a user invokes an action, the site verifies that the user belongs to a group that has been granted permission over that action, and reacts accordingly.
An example: let's say the admin grants the PetitionsList, PetitionsView, PetitionsApprove and PetitionsReject permissions to the Managers group, and the PetitionsList, PetitionsView, PetitionsAdd, PetitionsVoteFor and PetitionsVoteAgainst to the Users group.
In this case, both groups can
- list petitions
- view a petition
Managers can
- approve a petition
- reject a petition
but they can't
- vote for a petition
- vote against a petition.
In the same way, users can
- add a petition
- vote for a petition
- vote against a petition
but they can't:
- approve a petition
- reject a petition
and neither can edit or delete a petition.
I'd really like to leverage the attributes functionality in MVC 5. My idea is to build custom attributes that do all the authentication/authorization behind scenes. Something like this:
public class PetitionsController : Controller
{
[MyCustomAuth(Permission="PetitionsList",Groups="Users,Managers")]
public ActionResult List()
{
//show the list of petitions
}
[MyCustomAuth(Permission="PetitionsView",Groups="Users,Managers")]
public ActionResult View()
{
//show a specific petition
}
[MyCustomAuth(Permission="PetitionsAdd",Groups="Users")]
public ActionResult Add()
{
//show add petition form
}
[HttpPost]
[MyCustomAuth(Permission="PetitionsAdd",Groups="Users")]
public ActionResult Add(object[] params)
{
//save new petition
}
[MyCustomAuth(Permission="PetitionsEdit",Groups="Admins")]
public ActionResult Edit(int id)
{
//show edit petition form
}
[HttpPost]
[MyCustomAuth(Permission="PetitionsEdit",Groups="Admins")]
public ActionResult Edit(object[] params)
{
//save changes to petition
}
[HttpPost]
[MyCustomAuth(Permission="PetitionsDelete",Groups="Admins")]
public ActionResult Delete(int_id)
{
//delete petition
}
[HttpPost]
[MyCustomAuth(Permission="PetitionsVoteFor",Groups="Users")]
public ActionResult VoteFor(int id)
{
//add vote supporting petition
}
[HttpPost]
[MyCustomAuth(Permission="PetitionsVoteAgainst",Groups="Users")]
public ActionResult VoteAgainst(int id)
{
//add vote against petition
}
[HttpPost]
[MyCustomAuth(Permission="PetitionsApprove",Groups="Managers")]
public ActionResult Approve(int id)
{
//approve petition
}
[HttpPost]
[MyCustomAuth(Permission="PetitionsReject",Groups="Managers")]
public ActionResult Reject(int id)
{
//reject petition
}
}
Please note the MyCustomAuth attribute over every action. I want that attribute to do the heavy lifting of saying if the user is actually authorized to do that action, behind the scenes. Of course, in case it's not authenticated/authorized, the attribute should redirect to login page/401/somewhere else.
My question is, where do I start? Am I expected to implement some special interface/inherit from some class in MVC 5? Or do I have to write this from scratch?
Also, thanks in advance for reading this wall of text and for giving me any tips/pointers in the right direction.