Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Role Management in MVC3

I want to add a functionality to application such that only admin can create users and he can provide access to particular pages to user.

He can create roles and can provide users different roles.

I am using Visual Studio 2010 and building this application in MVC3.

Please give me suggestions to make over it.

Thanks in advance.

like image 309
summercostanza Avatar asked Jun 07 '11 10:06

summercostanza


2 Answers

1.Decorate your user creation and permission setting actions with Authorize attribute (Notify, that usage of Roles property of AuthorizeAttribute requires implementation of MembershipProvider (standart or custom) and registering it in web.config)

public class AccountController : Controller
{
[HttpGet, Authorize(Roles = "Admin")]
public ViewResult CreateUser()
{
    return View();
}

[HttpPost, Authorize(Roles = "Admin")]
public ActionResult CreateUser()
{
    //... call service method to create user
}

[HttpPost, Authorize(Roles = "Admin")]
public ActionResult AssignPageToUser(int userId, string controllerName, string ActionName)
{
    //... insert record into table (UserPermissions) with attributes (userId, actionName, controllerName)
    }
// other methods without decoration by authorize attribute
}

Next paragraphs are correct if you really want to have full control on action permissions separately for each user. If you think, that your permissions can group in finite and small number on roles - you can decorate all actions/controllers by authorize attribute and specify roles, for which action/controller available: [Authorize("Customer, Manager, RegionalAdmin")] and give admin possibility to assign roles to users. But remember, that in is enough to be in only 1 of listed roles to get access, you can't require by this attribute, for example and Admin, and Manager roles. If you want to require necessarily more than 1 role, use multiple attributes:

public class MyController:Controller
{
[Authorize(Roles = "Manager")]
[Authorize(Roles = "Admin")]
public ActionResult Action1()
{
//...
}
}

2.For your pages you can create your own filter attribute, inherited from authorize attribute, that will check, if action is available for user (i think you want to assign actions but not views to user).

public UserPermissionRequiredAttribute: AuthorizeAttribute
{
public OnAuthorization(AuthorizationContext filterContext)
{
var isAuthenticated = filterContext.HttpContext.User.Identity.IsAuthenticated;
var userName = filterContext.HttpContext.User.Identity.Name;
var actionName = filterContext.ActionDescriptior.ActionName;
var controllerName = filterContext.ActionDescriptior.ControllerDescriptor.ControllerName;
    if (isAuthenticated && myUserActionPermissionsService.UserCanAccessAction(userName, actionName, contollerName)
{
filterContext.Result = HttpUnauthorizedResult(); // aborts action executing
}
}
}

3.Decorate actions (controllers), that accessible for users granted by admin:

MySpecialController: Controller
{
[UserPermissionRequired]
Action1()
{
//...
}

[UserPermissionRequired]
Action2()
{
//...
}

Action3()
{
//...
}

}

I don't recommend to use base controller for that aim, because attribute usage is more flexible (you have control on action/controller level instead of only controller level), it is better way to implement separated responsibility. Base controller and filter attribute usage correlated as polymorphism and switch operator.

like image 137
Evgeny Levin Avatar answered Oct 20 '22 05:10

Evgeny Levin


You're asking a very broad question, and it would take some time to review all your requirements. In any case, you could start by adding a user property to a controller from which all other controllers inherit. Then, you could interrogate that user instance to determine whether they have access to the current route. This solution should give you the foundation you need to add some administrative views for your business requirements.

public class MegaController
{
    protected User CurrentUser { get; set; }

    protected override void Initialize(RequestContext context)
    {
        if (requestContext.HttpContext.User.Identity.IsAuthenticated)
        {
            var userRepository = new UserRepository();
            CurrentUser = userRepository.GetUser(
                requestContext.HttpContext.User.Identity.Name);
        }
    }
}

The User and UserRepository types can be your own design. You could use LINQ To Entities to wrap a table named "User" and then within your controllers, you could have access to any fields in that table.

Then, subclass all controllers from MegaController

public class AdminController : MegaController
{
    public ActionResult Action1()
    {
        return View();
    }
}

public class SomeOtherController : MegaController
{
    public ActionResult Action1()
    {
        return View();
    }
}

Now, this doesn't completely solve your "admin" issue. To do so, you could include logic in MegaController.Initialize() to interrogate the request information. Once you have the requested route and user in context, your code could make a decision whether to allow the request, redirect it, etc.

protected override void Initialize(RequestContext context)
{
    // ...
    if(context.HttpContext != null)
    {
        if(context.HttpContext.Request.Path == "some/restricted/route" 
            && CurrentUser.Role != "Admin")
        {
            // or similar error page
            var url = Url.Action("UnAuthorized", "Error");
            context.HttpContext.Response.Redirect(url);
        }
    }
}

One caveat with this method is that any new controllers added to your application would have to inherit from MegaController, an architecture that could be easily missed by future developers on the project.

like image 21
David Fox Avatar answered Oct 20 '22 04:10

David Fox