9

This is not a duplicate question or rather the solutions given in other solutions have not worked.

Lets say there is a controller

[Authorize(Roles=//set dynamically)]
public IActionResult DashBoard(LoginModel model)
{
}

I have tried the solutions in the following questions

  1. Add roles to authorize attribute

  2. dynamically assign controller action permissions to roles

  3. dynamically add roles to authorize attribute for controller (Error: Handle method - no suitable method found to override )

  4. can policy based authorization be more dynamic

All of these solutions do not work as either the methods overridden in the interfaces are not present (eg. authorizeAttribute does not contain a definition for AuthorizeCore) or in some cases IServiceCollection services does not contain a particular method

Sujit.Warrier
  • 2,337
  • 2
  • 25
  • 38

3 Answers3

13

You can't do that. [Authorize(Roles=//set dynamically)] must be know at compile time. Also using roles for this very reason is discouraged as pointed in blowdart's linked post from comments.

Instead, you should use claims and policies. Claims are fine grained permissions, for example "CreateCustomer" or "DeleteCustomer" or "ViewDashboard".

So you have to use it like

[Authorize(Policy = "ViewDashboard")]

These policies need to be know at compile time.

public class ViewDashboardRequirement : AuthorizationHandler<ViewDashboardRequirement>, IAuthorizationRequirement
{
    public override void Handle(AuthorizationContext context, ViewDashboardRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == "dashboard:read"))
        {
            context.context.Succeed(requirement);
            return;
        }

        // only call fail if you do not want that other AuthorizationHandler may succeed with 
        // a different requirement
        // context.Fail();
    }
}

For an example on how to generate a generic handler (instead of writing a new Handler for each policy) see my answer here.

This will allow you to create configurable roles. Now you can create roles which consists as a bag of claims. Each claim may be one policies. When the user logs in, you add the claims that belong to a role to the list of users claims.

i.e.

  • Support: ViewDashboard, ViewCustomers, ViewContacts, ManageCases (support tickets)
  • Manager: ViewDashboard, ManageCustomers (View, Edit, Delete), ManageContacts (View, Edit, Delete)
  • Administrator: ManageDashboard (View, Edit)

etc.

Update from Comments.

You should be able to utilize ASP.NET Core Identity's claim and roleclaim abilities w/o changing a line of code, therefor you have the IdentityRole and IdentityRoleClaim classes. At runtime, you add a new IdentityRole (i.e. "Manager") and then add multiple IdentityRoleClaim (one for each permission/policy)

Tseng
  • 52,202
  • 10
  • 166
  • 183
  • The problem Im facing is that in the requirement class I need to call db. for which i need to add a datacontext. but any object created of this class will need a datacontecxt object – Sujit.Warrier Jun 30 '17 at 07:12
  • 1
    No, you don't need to call db there. Requirement should operate on the **user claims**. When you log in, you put the required claims into the the [ClaimsPrinicipal](https://github.com/dotnet/corefx/blob/master/src/System.Security.Claims/src/System/Security/Claims/ClaimsPrincipal.cs#L16). Then every time the user authenticates you can access the claims from within `HttpContext.User.HasClaim(...)` or `HttpContext.User.FindFirst(...)`. A role then is only a bunch of claims which can be configured at runtime (i.e. adding new roles means creating a role and adding certain claims to it – Tseng Jun 30 '17 at 07:18
  • A "claim" is a little more then a specific permission like "ViewCustomers" or "ManageCustomers" – Tseng Jun 30 '17 at 07:19
  • How am i supposed to add claims to Claims principal. After adding, they are not present when i try to retrieve them later. var claimsIdentity =(ClaimsIdentity) HttpContext.User.Identity; var identity = new ClaimsIdentity(claimsIdentity); identity.AddClaim(new Claim("ProductUploadRequest", "Allow")); – Sujit.Warrier Jun 30 '17 at 07:41
  • It must be done during the authentication, not at some arbitrary place in your code, since the authentication happens on **each request**. You should be able to utilize ASP.NET Core Identity's claim and roleclaim abilities w/o changing a line of code, therefor you have the `IdentityRole` and `IdentityRoleClaim` classes. At runtime, you add a new `IdentityRole` (i.e. "Manager") and then add multiple`IdentityRoleClaim` to (one for each permission/policy) – Tseng Jun 30 '17 at 07:54
  • @Tseng Since the Support and Manager role both have the ViewDashboard claim, how can one be sure that the content of the dashboard is different for each? Thanks in advance! – Rafael Jan 12 '18 at 09:05
  • @Lobato: Claims are there to see if they have access to the page/webapi. To filter data, you'd need some criteria on the database, such as "ownedBy" and "groupId", organizationid or compare that with the parent organization (if you have something similar to the way Microsoft Dynamics manages access to data).You can have a claims-array with the name or id of the roles,if you adjust the view based on a role, but that assumes that you know the roles at compile time. That's why using `Roles` property of `AuthorizeAttribute` **isn't recommended** anymore. Claims are just more dynamic. – Tseng Jan 12 '18 at 09:24
  • @Tseng Hmm I see. Because I have a DashboardController, and I want the endpoint to be the same for all the users (which I have), except the content must be different. So I just leave the endpoint to only be accesed by authorized users, and for the role specific content part I work with claims? Could you point me in the right direction? Thanks alot! (fyi ASP.CORE 2.0 & Angular) – Rafael Jan 12 '18 at 09:29
  • 1
    @Lobato: Well yea. On your DashController/actions you have the `[Authorize(Policy = "ViewDashboard")]` on there which checks if the user has `dashboard:view` policy. Do not matter which group he belongs to, as long as he has the claim, he can access **that action or controller**. Inside it, you need to read his groupis, groupids, userid, the company he belongs or whatever you need (either from claims or just grab his id and get it from your database) and apply that to the data or database queries – Tseng Jan 12 '18 at 10:09
0

i think you have to use Policy instead if role for authorization. in fact one of the benefit of the policy based vs role based is this.

-2
public IActionResult Validate(LoginDetails userobj)
       {
          LoginDetails user = new LoginDetails();
         var checklogin = from s in appDbContext.loginDetails where s.UserName == userobj.UserName && s.Password == userobj.Password select s;

               if (checklogin != null && checklogin.Count() > 0)
               {
                   if (checklogin.FirstOrDefault().Role == 1)
                   {
                       return RedirectToAction("Home");
                   }
                   else if (checklogin.FirstOrDefault().Role == 2)
                   {
                       var UserId = checklogin.FirstOrDefault().LoginId;
                       HttpContext.Session.SetInt32("UserId", UserId);
                       return RedirectToAction("Index1");
                   } 
           }

           return RedirectToAction("Failed");
       }
Pavel Anikhouski
  • 18,232
  • 12
  • 32
  • 48