Reputation: 1279
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
Managers can
but they can't
In the same way, users can
but they can't:
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.
Upvotes: 0
Views: 2344
Reputation: 7079
I think you can start with below Custom Authorization Filter. It also handle cases for both - ajax request and full page request.
public class MyCustomAuthAttribute : FilterAttribute, IAuthorizationFilter
{
public string Permission { get; set; }
public string Groups { get; set; }
public void OnAuthorization(AuthorizationContext filterContext)
{
bool isauthorized = CheckIfUserIsAuthorized();
if (!isauthorized)
context.Result = new HttpUnauthorizedResult(); // mark unauthorized
// Only do something if we are about to give a HttpUnauthorizedResult and we are in AJAX mode.
if (filterContext.Result is HttpUnauthorizedResult && filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.Result = new JsonResult
{
Data = new { Success = false, Message = "Unauthorized Access" },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
else
{
base.OnAuthorization(filterContext);
if (filterContext.Result is HttpUnauthorizedResult)
{
HttpContext.Current.Session.Abandon();
System.Web.Security.FormsAuthentication.SignOut();
filterContext.Result = new RedirectResult("Your Login Page.");
}
}
}
private bool IsAuthorizedUser()
{
// use Permission, Groups and your logic
}
}
and use it the same way you mentioned:
[MyCustomAuth(Permission="PetitionsEdit",Groups="Admins")]
Upvotes: 1