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.