6

I want to create a custom authorization attribute for checking the role and url path.

I've find the way for doing it in the Asp.Net Core using the Policy-Based-Authorization but I've tried implement it but I can't get the HttpContext with incomming url.

AuthorizationHandlerContext hasn't access to HttpContext probable.

How can I get current HttpContext with the url path? Is it possible to do that or with another way?

I've tried this code for creating the custom Policy:

public class RoleUrlValidationHandler : AuthorizationHandler<RoleUrlValidationRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RoleUrlValidationRequirement requirement)
    {           
        var path = //Here I need get current url path for example - /api/posts/4545411
        var pathPart = path.Split('/');
        var clientId = pathPart[3];

        if (context.User.IsInRole(clientId))
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

I want to create following:

[Authorize(Policy="RoleUrlValidation")] //Get ClientId from Url and check User's roles
public class PostsController : Controller
{
    public ActionResult Get()
    {
    }
}
Jenan
  • 2,956
  • 10
  • 52
  • 99
  • Frist you are not supposed to implement own Autzorize attributes. The approach with Policies is correct. Second you can use DI inside the handlers – Tseng Jan 08 '17 at 14:57
  • @Tseng Sorry, What do you mean "not supposed to implement own Autzorize attributes." and Why? – b.ben May 27 '17 at 15:38
  • 1
    @b.ben: ASP.NET Core team made certain methods on `AuthorizeAttribute` method non-virtual (or removed them), means you can't override them anymore (like it used to work in legacy ASP.NET). They introduced the policy based authorization as replacement, since its more flexible and easier for 3rd parties to add new policies, see [this answer](https://stackoverflow.com/a/31465227/455493) from blowdart, the developer responsible for ASP.NET Core security – Tseng May 27 '17 at 15:41

1 Answers1

9

The policy approach is the right one. Only bit you missed is, that you can use Dependency Injection in the Handlers.

public class RoleUrlValidationHandler : AuthorizationHandler<RoleUrlValidationRequirement>
{
    private readonly IHttpContextAccessor contextAccessor;
    public class RoleUrlValidationHandler(IHttpContextAccessor contextAccessor)
    {
        this.contextAccessor = contextAccessor;
    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RoleUrlValidationRequirement requirement)
    {
        var httpContext = contextAccessor.HttpContext;
        var path = httpContext.Request.Path;
        var pathPart = path.Split('/');
        var clientId = pathPart[3];

        if (context.User.IsInRole(clientId))
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

You also may have to register the IHttpContextAccessor as its not registered by default.

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

Extra bits:

Consider using var routeData = httpContext.GetRouteData() instead of using path.Split('/') for reading values from it so you can easily read the parameter values from the route.

Tseng
  • 52,202
  • 10
  • 166
  • 183
  • It looks amazing. I'll try it. Yes, you're correct, I missed the DI possibilty for doing that. :-) Thanks. – Jenan Jan 08 '17 at 15:13
  • Do you mean httpContext.GetRouteData(); probable? – Jenan Jan 08 '17 at 15:34
  • Yea, right. Wasn't really related to the code above, just a generalized form. But will fix it to avoid confusion – Tseng Jan 08 '17 at 15:40