3

In the controllercode below only users who are in the "Administrator" role can accesss the GetData() action method, because of the controllerlevel Authorizeattribute. But I also want users who only are in "Manager" role to have access to the GetData() action method, how to write a custom attribute for this?

[Authorize(Roles = "Administrator")]
Pulic class AdminController : Controller
{

[Authorize(Roles = "Administrator, Manager")]
public IActionResult GetData()
{
}

}
David Lenn
  • 175
  • 1
  • 8

2 Answers2

2

The class-level attribute is always checked first, so it denies anyone who is not in the right role. You need to specify the widest access at class level, then narrow it down on method level where needed:

[Authorize(Roles = "Administrator, Manager")]
public class AdminController : Controller
{
    // no attribute needed here
    public IActionResult GetData()
    {
    }

    [Authorize(Roles = "Administrator")]
    public IActionResult RestrictedMethod()
    {
    }
}
Peter B
  • 22,460
  • 5
  • 32
  • 69
  • 1
    The absolute majority of the actions in the controller are only for "Administrator" role, so Im looking for a custom attribute for less action attribute decorations. – David Lenn Aug 02 '17 at 10:03
  • 1
    See [this answer](https://stackoverflow.com/a/32661139/1220550), it might provide what you need, but I never tried that myself, I don't even know if it is available in .NET Core. – Peter B Aug 02 '17 at 10:49
  • 1
    That attribute doesn't exist in .netcore. – David Lenn Aug 02 '17 at 13:31
1

In the startup.cs file, add the Authorization as follows:

services.AddAuthorization(options =>
        {
            var roles = new List<string>{ Role.Administrator, Role.Manager};

            var requirement =
                new List<IAuthorizationRequirement> {new AdminManagerAuthorizationOverrideOthers(roles) };
            var sharedAuthentication =
                new AuthorizationPolicy(requirement,
                    new List<string>());
            options.AddPolicy(name: "AdminManager", policy: sharedAuthentication);
            options.AddPolicy(name: "Administrator", configurePolicy: policy => policy.RequireAssertion(e =>
            {
                if (e.Resource is AuthorizationFilterContext afc)
                {
                    var noPolicy = afc.Filters.OfType<AuthorizeFilter>().Any(p =>
                        p.Policy.Requirements.Count == 1 &&
                        p.Policy.Requirements.Single() is AdminManagerAuthorizationOverrideOthers);
                    if (noPolicy)
                        return true;
                }
                return e.User.IsInRole(Role.Administrator);
            }));

        });

Create a class in any namespace that Inherits "RolesAuthorizationRequirement" from "Microsoft.AspNetCore.Authorization.Infrastructure" namespace as follows:

public class AdminManagerAuthorizationOverrideOthers : RolesAuthorizationRequirement
{
    public AdminManagerAuthorizationOverrideOthers(IEnumerable<string> allowedRoles) : base(allowedRoles)
    {
    }
}

Then, decorate the controller and action method as follows:

[Authorize(Policy = "Administrator")]
Public class AdminController : Controller
{
    public IActionResult GetData()
    {
    }

    [Authorize(Policy = "AdminManager")]
    public IActionResult AdministratorOnly()
    {
    }
}
Pravin
  • 839
  • 7
  • 12