Allow multiple roles to access controller action
Asked Answered
H

13

330

Right now I decorate a method like this to allow "members" to access my controller action

[Authorize(Roles="members")]

How do I allow more than one role? For example the following does not work but it shows what I am trying to do (allow "members" and "admin" access):

[Authorize(Roles="members", "admin")] 
Humoresque answered 31/3, 2009 at 5:49 Comment(1)
Please change the accepted answer to this question. The person with the currently accepted answer edited it indicating that he was wrong.Corybantic
K
680

Another option is to use a single authorize filter as you posted but remove the inner quotations.

[Authorize(Roles="members,admin")]
Kindred answered 1/10, 2009 at 13:14 Comment(11)
Works in MVC 5 too. +1Hydrops
Works in ASP.NET Core 1.0 (MVC 6) and Microsoft.AspNet.Identity v3.*Unmarked
This is okay if you only have one controller that you need to authorize. If you have more than one, you're duplicating those string constants (yuck). I much prefer the static class that has the role names. My pet hate is duplicate strings... so so bad.Senarmontite
For the life of me I cannot get this to work. It's fine if I specify [Authorize(Roles = "Administrator")], but not if I specify [Authorize(Roles = "Administrator, PowerUser")]. I'm using MVC v5.2.7Explorer
So now I'm testing a custom attribute inheriting from AuthorizeAttribute. It's okay with just Administrator, it hits my break point in HandleUnauthorizedRequest just fine, but if I specify Administrator and PowerUser, it doesn't hit the break point at all and access is granted to the action. What the?!?Explorer
Never mind. I think it's a cookies issue. My user still appears to be logged in.Explorer
@kraeg good news that you solved your problem. Now, consider deleting your comments, pleaseTabescent
Why? It took me ages to work this out. It may be helpful for someone else experiencing the same problem.Explorer
Why? Because it is very long, SO cuts it, and nobody reads completely when seeing the answer. At best it could condense everything into a single brief comment.Tabescent
Only to add some more info on this issue... This answer is not completely correct. If you go to the Roles Authorization assign, it does this split: original.Split(',')) So if you have any spaces on the Roles name, it won't work!! Be careful with that. Just use the same solution as @Jim Schmehil suggests, but with no spaces on your role namesThay
extra note: as per learn.microsoft.com/en-us/aspnet/core/security/authorization/… - this is OR and not ANDExtraterritorial
T
162

If you want use custom roles, you can do this:

CustomRoles class:

public static class CustomRoles
{
    public const string Administrator = "Administrador";
    public const string User = "Usuario";
}

Usage

[Authorize(Roles = CustomRoles.Administrator +","+ CustomRoles.User)]

If you have few roles, maybe you can combine them (for clarity) like this:

public static class CustomRoles
{
     public const string Administrator = "Administrador";
     public const string User = "Usuario";
     public const string AdministratorOrUser = Administrator + "," + User;  
}

Usage

[Authorize(Roles = CustomRoles.AdministratorOrUser)]
Tabescent answered 23/5, 2012 at 15:32 Comment(5)
This would be a good answer, if you explained to people who didn't know what's behind CustomRoles.Semen
@JamesSkemp ok, i've extended my answer. It is very simple. CustumRoles is a class that i created that contains some constants, that corresponds with my application roles. I did that for a few reasons: 1) It allow the use of intellisense to avoid spelling mistakes 2)To simplify maintenance. If a role change, i have to update only one place within my application.Tabescent
@Pabloker Alternatively you can create an enum with an Flags attribute, e.g. Convert.ToString(CustomRoles.Administrator | CustomRoles.User); - annoying part is that this requires an explicit conversionDelirious
If you have 39 roles ?Ill
I think your problem goes through the modeling of permits beyond what can be done with .netTabescent
R
107

One possible simplification would be to subclass AuthorizeAttribute:

public class RolesAttribute : AuthorizeAttribute
{
    public RolesAttribute(params string[] roles)
    {
        Roles = String.Join(",", roles);
    }
}

Usage:

[Roles("members", "admin")]

Semantically it is the same as Jim Schmehil's answer.

Ronda answered 25/3, 2014 at 18:21 Comment(4)
This does not worked for me, the logged in user was able to bypass the attribute even if the user did not have any of the roles.Antiserum
This answer is better for when you are using constants as your values: ie [Roles(Constants.Admin,Constants.Owner)]Halothane
this is the best answerLapointe
I know this is an old post, but let me add this. In order for this code snippet to work, you have to use System.Web.Mvc.AuthorizeAttribute for System.Web.Mvc.Controllers and System.Web.Http.AuthorizeAttribute for System.Web.Http.ApiController (RESTful calls).Bauske
A
19

For MVC4, using a Enum (UserRoles) with my roles, I use a custom AuthorizeAttribute.

On my controlled action, I do:

[CustomAuthorize(UserRoles.Admin, UserRoles.User)]
public ActionResult ChangePassword()
{
    return View();
}

And I use a custom AuthorizeAttribute like that:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class CustomAuthorize : AuthorizeAttribute
{
    private string[] UserProfilesRequired { get; set; }

    public CustomAuthorize(params object[] userProfilesRequired)
    {
        if (userProfilesRequired.Any(p => p.GetType().BaseType != typeof(Enum)))
            throw new ArgumentException("userProfilesRequired");

        this.UserProfilesRequired = userProfilesRequired.Select(p => Enum.GetName(p.GetType(), p)).ToArray();
    }

    public override void OnAuthorization(AuthorizationContext context)
    {
        bool authorized = false;

        foreach (var role in this.UserProfilesRequired)
            if (HttpContext.Current.User.IsInRole(role))
            {
                authorized = true;
                break;
            }

        if (!authorized)
        {
            var url = new UrlHelper(context.RequestContext);
            var logonUrl = url.Action("Http", "Error", new { Id = 401, Area = "" });
            context.Result = new RedirectResult(logonUrl);

            return;
        }
    }
}

This is part of modifed FNHMVC by Fabricio Martínez Tamayo https://github.com/fabriciomrtnz/FNHMVC/

Ameba answered 19/2, 2014 at 20:42 Comment(6)
Your OnAuthorization method will require the user to have all the enumerated roles; was that intentional, or are you missing a break in that loop?Baryon
@Tieson: I inspected that pretty closely, it definitely seems like a break would be required in that loop.Aniconic
@TiesonT. and @ madrush, I appreciate your fix, it really could have a break inside the loop. I'll change the code above.Ameba
The enum UserRoles is nice. Do you declare it manually or is it autogenerated based on the contents of the DB?Polaris
@KonradViltersten It's manually but I guess with Reflection and Dynamic class autogenerated can be doneAmeba
Overriding OnAuthorization is a bad idea. There is some code which protects against cached pages being served up in the original. Check the source.Opsonin
F
12

You can use Authorization Policy in Startup.cs

    services.AddAuthorization(options =>
    {
        options.AddPolicy("admin", policy => policy.RequireRole("SuperAdmin","Admin"));
        options.AddPolicy("teacher", policy => policy.RequireRole("SuperAdmin", "Admin", "Teacher"));
    });

And in Controller Files:

 [Authorize(Policy = "teacher")]
 [HttpGet("stats/{id}")]
 public async Task<IActionResult> getStudentStats(int id)
 { ... }

"teacher" policy accept 3 roles.

Frech answered 6/1, 2021 at 2:33 Comment(2)
Is it AND or OR?Avarice
This was my preference. Also, in the new versions that do not have startup.cs you do the same thing now in program.cs. If you need different combinations you'll have to create a policy for each one, but then you don't have to put the Domain groups in the controller. you can put them in the AppSettings.json file.Washable
E
4

Another clear solution, you can use constants to keep convention and add multiple [Authorize] attributes. Check this out:

public static class RolesConvention
{
    public const string Administrator = "Administrator";
    public const string Guest = "Guest";
}

Then in the controller:

[Authorize(Roles = RolesConvention.Administrator )]
[Authorize(Roles = RolesConvention.Guest)]
[Produces("application/json")]
[Route("api/[controller]")]
public class MyController : Controller
Egger answered 22/2, 2017 at 0:19 Comment(1)
Multiple Authorize attributes employ AND semantics and require ALL conditions to be met (i.e. user has to be in both the Administrator and Guest roles).Leveloff
I
4

Using AspNetCore 2.x, you have to go a little different way:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class AuthorizeRoleAttribute : AuthorizeAttribute
{
    public AuthorizeRoleAttribute(params YourEnum[] roles)
    {
        Policy = string.Join(",", roles.Select(r => r.GetDescription()));
    }
}

just use it like this:

[Authorize(YourEnum.Role1, YourEnum.Role2)]
Intratelluric answered 18/10, 2018 at 18:39 Comment(2)
Policy = or Roles = ?Hidie
Roles = string.Join(",", roles.Select(r => r.ToString()));Apian
H
4

I mixed answers and proposed this method.

Firstly, We create an enum for role accesses.

public enum ERoleAccess
{
     [Description("Admin User")]
     Admin = 1,

     [Description("General User")]
     User = 2,

     [Description("Editor User")]
     Editor = 3,
}

Secondly, we need an attribute filter for customer MVC authorize.

public class RolesAttribute:AuthorizeAttribute
{
   public RolesAttribute(params ERoleAccess[] roles)
   {
      Roles = string.Join(",", roles);
   }
}

Finally, we can use "RolesAttribute" on the controllers or actions.


[Roles(ERoleAccess.Admin, ERoleAccess.Editor, ERoleAccess.User)]

In this approach, we use numbers of alternative string values. (1= Admin, 2=User,...)

It's good for decreasing token size and comparing performance.

Handyman answered 2/10, 2022 at 5:20 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Sauers
R
3

Better code with adding a subclass AuthorizeRole.cs

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
    class AuthorizeRoleAttribute : AuthorizeAttribute
    {
        public AuthorizeRoleAttribute(params Rolenames[] roles)
        {
            this.Roles = string.Join(",", roles.Select(r => Enum.GetName(r.GetType(), r)));
        }
        protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
        {
            if (filterContext.HttpContext.Request.IsAuthenticated)
            {
                filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary {
                  { "action", "Unauthorized" },
                  { "controller", "Home" },
                  { "area", "" }
                  }
              );
                //base.HandleUnauthorizedRequest(filterContext);
            }
            else
            {
                filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary {
                  { "action", "Login" },
                  { "controller", "Account" },
                  { "area", "" },
                  { "returnUrl", HttpContext.Current.Request.Url }
                  }
              );
            }
        }
    }

How to use this

[AuthorizeRole(Rolenames.Admin,Rolenames.Member)]

public ActionResult Index()
{
return View();
}
Rooke answered 27/1, 2016 at 7:20 Comment(0)
M
3

If you find yourself applying those 2 roles often you can wrap them in their own Authorize. This is really an extension of the accepted answer.

using System.Web.Mvc;

public class AuthorizeAdminOrMember : AuthorizeAttribute
{
    public AuthorizeAdminOrMember()
    {
        Roles = "members, admin";
    }
}

And then apply your new authorize to the Action. I think this looks cleaner and reads easily.

public class MyController : Controller
{
    [AuthorizeAdminOrMember]
    public ActionResult MyAction()
    {
        return null;
    }
}
Mcilwain answered 15/2, 2018 at 19:57 Comment(0)
L
2

For "Or" you need comma between roles

[Authorize(Roles="members,admin")]

both members and admins can access

But for "and" you need

[Authorize(Roles="manager")]
[Authorize(Roles="sales")]

managers who are in sales can access.

Luigiluigino answered 25/6, 2023 at 12:34 Comment(0)
E
0
[Authorize(Roles="admin")]
[Authorize(Roles="members")]

will work when the AND is needed (as asked by question) whereas this answer shows the OR version. See more at https://learn.microsoft.com/en-us/aspnet/core/security/authorization/roles?view=aspnetcore-6.0#adding-role-checks

Extraterritorial answered 1/3, 2022 at 1:17 Comment(0)
P
-11
Intent promptInstall = new Intent(android.content.Intent.ACTION_VIEW);
promptInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
promptInstall.setDataAndType(Uri.parse("http://10.0.2.2:8081/MyAPPStore/apk/Teflouki.apk"), "application/vnd.android.package-archive" );

startActivity(promptInstall);
Panelboard answered 3/1, 2020 at 15:4 Comment(1)
Answers including code should have at least a minumum description explaining how the code works and why it answers to the question. Furthermode the code section formatting needs to be improved.Rabblement

© 2022 - 2024 — McMap. All rights reserved.