Introduction
In MVC the default method to perform authorization is hard coding the "Authorize" attribute in the controllers, for each action, in this article I will explain a simple way to implement "Dynamic Authorization" with the ability to assign permissions for actions to roles or users.
Using the code
First I will explain my user authentication and role assigning model, I have used Forms Authentication this scenario, here is my sample login action:
[HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public ActionResult Login(LoginModel model, string returnUrl) { //sample data Dictionary<string, string> users = new Dictionary<string, string>(); users.Add("admin", "admin-pass"); string roles; if (users[model.UserName] == model.Password) { Session["User"] = model.UserName; roles = "admin;customer"; // put the roles of the user in the Session Session["Roles"] = roles; HttpContext.Items.Add("roles", roles); //Let us now set the authentication cookie so that we can use that later. FormsAuthentication.SetAuthCookie(model.UserName, false); //Login successful lets put him to requested page string returnUrl = Request.QueryString["ReturnUrl"] as string; return RedirectToLocal(returnUrl); if (returnUrl != null) { Response.Redirect(returnUrl); } else { //no return URL specified so lets kick him to home page Response.Redirect("Default.aspx"); } } else { // If we got this far, something failed, redisplay form ModelState.AddModelError("", "The user name or password provided is incorrect"); return View(model); } }
All the actions that need authentication have to be loaded in a list, and also all of the roles and actions that each role has access to, I have put some sample code to simulate them "AllRoles" and "NeedAuthenticationActions". Then we need to create a base class for controllers in which I have overridden the OnActionExecuting
method, in which the user will be authorized based on its current role and whether he/she has logged in or not, the action may also has no need to be authorized.
public class ControllerBase : Controller { private string ActionKey; //sample data for the roles of the application Dictionary<string, List<string>> AllRoles = new Dictionary<string, List<string>>(); protected void initRoles() { AllRoles.Add("role1", new List<string>() { "Controller1-View", "Controller1-Create", "Controller1-Edit", "Controller1-Delete" }); AllRoles.Add("role2", new List<string>() { "Controller1-View", "Controller1-Create" }); AllRoles.Add("role3", new List<string>() { "Controller1-View" }); } //sample data for the pages that need authorization List<string> NeedAuthenticationActions = new List<string>() { "Controller1-Edit", "Controller1-Delete"}; protected override void OnActionExecuting(ActionExecutingContext filterContext) { ActionKey = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName + "-" + filterContext.ActionDescriptor.ActionName; string role = Session["Roles"].ToString();//getting the current role if (NeedAuthenticationActions.Any(s => s.Equals(ActionKey, StringComparison.OrdinalIgnoreCase))) { if (!filterContext.HttpContext.User.Identity.IsAuthenticated) { string redirectUrl = string.Format("?returnUrl={0}", filterContext.HttpContext.Request.Url.PathAndQuery); filterContext.HttpContext.Response.Redirect(FormsAuthentication.LoginUrl + redirectUrl, true); } else //check role { if (!AllRoles[role].Contains(ActionKey)) { filterContext.HttpContext.Response.Redirect("~/NoAccess", true); } } } }