0

I've got this controller

public class WorkController : ControllerBase
{
    [Authorize(Roles = "admin")]
    [HttpPost("something/add")]
    public async Task<IActionResult> Add()
    {
        //add
    }
    [Authorize(Roles = "admin,support")]
    [HttpGet("something/get")]
    public async Task<IActionResult> Get()
    {
        //get
    }
}

the authorization works fine, but i think it could do better, i just wanna ask if there's something like i could filter the Roles and allow it based on the http verb, like if the role of the sender is admin, he could access all methods, and if the role is support, he could only access the Get methods. Making this global would be great since i got a lot of method

Ashkan Mobayen Khiabani
  • 33,575
  • 33
  • 102
  • 171
jay r bayog
  • 53
  • 2
  • 8
  • 1
    But that is exactly what you're currently doing. If you have to explicitly tell that a method is a post, just don't give permission for the support role. – Washington A. Ramos Feb 25 '20 at 03:52
  • @WashingtonA.Ramos this setup works fine, but im just wondering if this could be global since i got a lot of methods – jay r bayog Feb 25 '20 at 05:17
  • Does this answer your question? [How to use the Authorize attribute both at the controller and action level?](https://stackoverflow.com/questions/19403013/how-to-use-the-authorize-attribute-both-at-the-controller-and-action-level) – MKR Feb 25 '20 at 06:20

1 Answers1

1

I just wanna ask if there's something like i could filter the Roles and allow it based on the http verb, like if the role of the sender is admin, he could access all methods, and if the role is support, he could only access the Get methods.

Sure. Firstly, you need to create the rules:

public class GlobalVerbRoleRequirement: IAuthorizationRequirement
{
    public bool IsAllowed(string role, string verb)
    {
        // allow all verbs if user is "admin"
        if(string.Equals("admin", role, StringComparison.OrdinalIgnoreCase)) return true;
        // allow the "GET" verb if user is "support"
        if(string.Equals("support", role, StringComparison.OrdinalIgnoreCase) && string.Equals("GET",verb, StringComparison.OrdinalIgnoreCase)){
            return true;
        };
        // ... add other rules as you like
        return false;
    }
}

(You might want to custom the IsAllowed(role, verb) further if you have more rules)

And tell ASP.NET Core how to handle this rules with an AuthorizationHandler:

public class GlobalVerbRoleHandler : AuthorizationHandler<GlobalVerbRoleRequirement>
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public GlobalVerbRoleHandler(IHttpContextAccessor httpContextAccessor)
    {
        this._httpContextAccessor = httpContextAccessor;
    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, GlobalVerbRoleRequirement requirement)
    {
        // check whether the user has required roles for current verb
        var roles = context.User.FindAll(c => string.Equals(c.Type,ClaimTypes.Role)).Select(c => c.Value);
        var verb= _httpContextAccessor.HttpContext?.Request.Method;
        if(string.IsNullOrEmpty(verb)){ throw new Exception($"request cann't be null!"); }
        foreach(var role in roles){
            if(requirement.IsAllowed(role,verb)){
                context.Succeed(requirement); 
                return Task.CompletedTask;
            }
        }
        context.Fail();
        return Task.CompletedTask;
    }
}

Finally, don't forget to register the related services in the startup:

services.AddHttpContextAccessor();
services.AddScoped<IAuthorizationHandler, GlobalVerbRoleHandler>();
services.AddAuthorization(opts =>{
    opts.DefaultPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .AddRequirements(new GlobalVerbRoleRequirement())
        .Build();
});

Now every [Authorize] will also check the Role against the current HTTP Verb automatically.

[Authorize]
public class WorkController : ControllerBase
{

    [HttpPost("something/add")]
    public async Task<IActionResult> Add()
    {
        //add
    }

    [HttpGet("something/get")]
    public async Task<IActionResult> Get()
    {
        //get
    }
}
itminus
  • 23,772
  • 2
  • 53
  • 88