6

I'm developing some website which is a kind of online workplace, there will be some users and some ongoing computer programming projects, and each user can have multiple roles, for example one particular user can be a project manager for an project and a developer for another project. naturally the project manager has more authority than the developer in the project. my question is how to manage this in my code neatly? I was going to use my custom Role Provider and use the Authorize attribute with this, but it's not sufficient , since I'd need the project Id plus the user Id to find the role of user in an specific project.

ePezhman
  • 4,010
  • 7
  • 44
  • 80
  • How about adding two columns (or normalize with tables) to the aspnetdb database. Ad project Id's to the project_manager table and developer_table connected to the user and check against these. I use a similar solution in a project and it works like a charm :) – Magnus Karlsson May 09 '12 at 18:45
  • Does each project have only 1 project manager? If that's the case I would store project manager's user id in projects table. Every time you query the project (to display it) you can check if the user accessing it is a PM or not. – LukeP May 09 '12 at 22:56
  • @Luke, no, there shall not be any restrictions – ePezhman May 13 '12 at 11:37
  • @Magnus, I'd rather not to use any DB solutions, I want to develop a flexible code – ePezhman May 13 '12 at 11:38

7 Answers7

6

First all you will have to create additional tables for your extended role management like projects and there relationship with the users in context of operations, which might be your controller's actions.

One way of doing is to create your own table for roles. In that case you will only use only Asp net membership users, but it all depends your requirements.

Secondly you have to handle it in MVC, In my opinion the best way is to implement it through your own custom Authorization attribute, and decorate your controller's actions with your custom authorization attribute instead of [Authorization] attribute.

Its very simple.

[CustomAuthorize]
//[Authorize]
public ActionResult GetProjectTasks(string projectname)
{

}

For that you have to inherent your class from FilterAttribute and also have to implement IAuthorizationFilter interface.

 public void OnAuthorization(AuthorizationContext filterContext)
    {
        HttpCookie authCookie = filterContext.HttpContext.Request.Cookies[FormsAuthentication.FormsCookieName];

        if (authCookie != null)
        {
            FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
            var identity = new GenericIdentity(authTicket.Name, "Forms");
            var principal = new GenericPrincipal(identity, new string[] { authTicket.UserData });
            filterContext.HttpContext.User = principal;
        }

        var controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
        var action = filterContext.ActionDescriptor.ActionName;
        var user = filterContext.HttpContext.User;
        var ip = filterContext.HttpContext.Request.UserHostAddress;

        var isAccessAllowed = CustomAuthenticationLogic.IsAccessAllowed(controller, action, user, ip);
        if (!isAccessAllowed)
        {
            // Code if user is authenticated
            FormsAuthentication.RedirectToLoginPage();
        }            
    }

In the method OnAuthorization, you can get all the information which you might be require in your custom authorization logic like HttpContext, Controller name, Action name. You have to just call your custom authentication logic from this method. Your custom authentication logic might look like the following.

 public class CustomAuthenticationLogic
{
    public static bool IsAccessAllowed(string controller, string action, IPrincipal user, string ip)
    {
        //
        // Your custom logic here              
        //              
    }
} 
Asif Mushtaq
  • 13,010
  • 3
  • 33
  • 42
  • Where should we put this "OnAuthorization" code? and how it is linked with all the actions. Is it linked by attribute - [CustomAuthorize].? This "CustomAuthorize" is my class name that inherits from -"System.Web.Http.Filters.FilterAttribute" and i have "OnAuthorization" code or method in it. But still authorization not working. Breakpoint is not hitting in "OnAuthorization" code. – pvaju896 Jun 14 '16 at 12:34
  • @pvaju896 yes; you will need to add the customAuthorizeAttribute on the action in order the invoke OnAuthorization method // For that you have to inherent your class from FilterAttribute and also have to implement IAuthorizationFilter interface. // – Asif Mushtaq Jun 14 '16 at 12:41
  • my action in controller; public class PersonController : Controller { [SmartClinic.Util.CustomAuthorize] [HttpGet] public ActionResult List() { – pvaju896 Jun 14 '16 at 12:50
  • And another class with onAuthorization code: public class CustomAuthorize : System.Web.Http.Filters.FilterAttribute, System.Web.Http.Filters.IAuthorizationFilter { public void OnAuthorization(AuthorizationContext filterContext) { – pvaju896 Jun 14 '16 at 12:58
  • and one more class as you last mentioned; CustomAuthenticationLogic – pvaju896 Jun 14 '16 at 12:59
4

I did some research a while ago and can assure you:

  1. ASP.NET builtin features most probably wont help (there s no way to take into account things like project Id)
  2. Role based access model is most suitable, there are different ways to implement it. AzMan suggested by Rusted is actually good, but managing context related rules may be difficult. For example: User A performing operation B in Project C, while its lets say Sunday. Take a look at azman.
  3. Mixing you access rules with code is extreamly bad. Your security model does'nt need to be related to they way application works (ASP.NET MVC) so this one is wrong:
var isAllowed = AccessControl.IsAccessAllowed(controller, action, user, ip);

it must look like:

var isAllowed = AccessControl.IsAccessAllowed(user, operation, context);

then you can use it whenever you like, In each action or wrap it as attribute.

where operation is "Login", "Post reply", "Read topics", etc. context is everithing else, like you "project id", "day of week", "user ip", etc

there are a lot of things could be written, like, role overlapping, context etc. In short: Google for ".NET role based access model" probably it may be easier to write small custom security framework. Make it work with Users, Roles, Operations and Project Id

Operations are assigned to Roles, Roles are assigned to users with defined project Id, you can hardcode operations and roles, so in your DB will be only one small change: User to Roles mapping

Alexander Selishchev
  • 1,180
  • 13
  • 29
2

If you have rules that are more complex and attributes are not enough, then you can calculate in your Controller if the user can access some features and add properties in your ViewModel that reflect access or no access to those features.

This way your view would be very slim, it would display stuff depending on those ViewModel boolean properties.

So, imagining your user can only read, you could have a property bool IsReadOnly that would be filles in the controller, depending on the authorization rules, and that would be used in the view to, for instance, generate labels instead of textboxes.

Dante
  • 3,833
  • 4
  • 38
  • 55
2

I like the main idea with AzMan is the concept of programming against operations.

Operations are very granular things that should have no overlap in usage and defined only by developer. By grouping operations into tasks and tasks into roles and then mapping principals (users and groups) to roles, you have a very power model for defining authorization in your app. Since you program directly to the operations, your code doesn't need to know what roles a user has and that can be changed by the administrator at runtime. In fact, someone could define a completely different set of roles to consume the operations you use in your code and you wouldn't need to change any code at all. That's where the real power lies.

I don't mean "use AzMan in your app" (but maybe you should try). It is a powerful model, but it is also complex and is probably overkill for simple things. If you just have a role or two and the operations they protect don't overlap or aren't likely to change, then it probably isn't warranted.

Rusted
  • 579
  • 3
  • 9
2

I would suggest you to create a custom Authorize filter by extending the built-in AuthorizeAttribute filter instead of implementing the IAuthorizationFilter interface. The built-in AuthorizeAttribute does many plumbing work that takes care of the cache problem and other stuff and if you are going to implement the interface you have to do all those work.

You have to override the AuthorizeCore method and there you have do all your role checking logic. The user id you have to store in session and project id you have to figure it out.

public override bool AuthorizeCore(HttpContextBase httpContext)
{

}

AuthorizeAttribute source code - http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/913d37658a44#src%2fSystem.Web.Mvc%2fAuthorizeAttribute.cs

Custom Authorize Attribute Example: http://msdn.microsoft.com/en-us/library/ee707357(v=vs.91).aspx

VJAI
  • 32,167
  • 23
  • 102
  • 164
2

You can have groups based on roles.

Then Add different users to specific groups. Groups can be >

1) Admin Group  
2) Developer Group  
3) Project1-QA Group  
4) Project2-Manager Group

Save the mapping of [user - group] and [group - projects], depending on your database design.

You can have as many roles(groups) for one user as you want.

Estefany Velez
  • 328
  • 4
  • 18
1

A very simple approach - for site-wide access control, you can add an INT column to the user table and map each bit of that INT to an [flags] Enum - e.g. [Flags] enum Access { UpdateProjects, AddProjects }.

For per-project access control, create a table named e.g. ProjectAccessControl with three columns: ProjectID (foreign key to Project table), UserID (foreign key to User table) and Role (INT). The Role column is an INT, and each its bit should mean different boolean flag (as in previous example, you can map this to a enum in C#) and say that if the first bit is on, then user has rights to update description, if the second bit is on, user can change schedules and so on.

[Flags]
enum ProjectAccessRole
{
    UpdateDescription,
    ChangeSchedule,
    etc...
}

In the code, you can test if the user's role has right to update schedule this way:

if( (intUserRole & ProjectAccessRole.ChangeSchedule) 
     == ProjectAccessRole.ChangeSchedule)
{
    /*user has right*/
}

Then you can wrap this check into a simple function that takes two parameters, 1) role that is to be checked if it has 2) role. Then you just call HasRights(intUserRole, ProjectAccessRole.ChangeSchedule);.

Luka Ramishvili
  • 889
  • 1
  • 11
  • 20