3

I am new to .Net / ASP.NET development and I am trying to use custom policy that I would like to add at the global level so that I don't have to add [Authorize] attribute in each controller for a policy I built.

I created a policy called SuperAdminUsers and uses custom requirement from UserNamesRequirement which I would like to set it at the global level, that way all SuperAdminUsers will have access to all controller and actions.

Below is my policy that I have created in startup.cs file.

services.AddAuthorization(
        option =>
        {
            option.AddPolicy("Admin", policy => policy.RequireRole("DOMAIN\\GroupName"));
            option.AddPolicy("SuperAdminUsers", policy => policy.Requirements.Add(new UserNamesRequirement("DOMAIN\\USER1", "DOMAIN\\USER2"))
        }
);
// Add Custom filters
services.AddMvc(
    config =>
    {
        var policy = new AuthorizationPolicyBuilder();
        policy.Requirements.Add(new UserNamesRequirement("DOMAIN\\USER1"));
                policy.Build();
                config.Filters.Add(new AuthorizeFilter(policy));
    }
);

In config.Filters above, I get an error shown in below screenshot:

enter image description here

Here is my Customer Requirement and Handle classes:

public class UserNamesRequirement : IAuthorizationRequirement
{
    public UserNamesRequirement(params string[] UserNames)
    {
        Users = UserNames;
    }
    public string[] Users { get; set; }
}

protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserNamesRequirement requirement)
{
        // var userName = context.User.FindFirst(ClaimTypes.NameIdentifier).Value;
        var userName = context.User.Identity.Name;

        if (requirement.Users.ToList().Contains(userName))
            context.Succeed(requirement);
        return Task.FromResult(0);
}

UPDATE 1:

Update start up file with below code, but still unable to authorize.

services.AddMvc
        (
            config =>
            {
                var policyBuilder = new AuthorizationPolicyBuilder();
                policyBuilder.Requirements.Add(new UserNamesRequirement("DOMAIN\\USER1", "DOMAIN\\USER2"));
                var policy = policyBuilder.Build();
                config.Filters.Add(new AuthorizeFilter(policy));
            }
        );

Controller I am trying to access:

[Authorize(Policy = "Admins")]
[Authorize(Policy = "SuperAdminUsers")]
public class OperatingSystemsController : Controller
{
    private readonly ServerMatrixDbContext _context;

    public OperatingSystemsController(ServerMatrixDbContext context)
    {
        _context = context;    
    }

    // GET: OperatingSystems
    public async Task<IActionResult> Index()
    {
        return View(await _context.OperatingSystems.ToListAsync());
    }
}

Here is what I see it in str output log file:

NOTE: I removed real domain and username.

Application started. Press Ctrl+C to shut down.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost/demo/OperatingSystems/Create  
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[1]
      Authorization was successful for user: DOMAIN\USER1.
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
      Authorization failed for user: DOMAIN\USER1.
warn: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
info: Microsoft.AspNetCore.Mvc.ChallengeResult[1]

Any help is really appreciated.

Ray
  • 1,095
  • 3
  • 18
  • 43
  • Create one base class for all your controllers and set [Authorize] only for that class. – Eins Oct 21 '16 at 23:01
  • So, I create a base class and add Authorize, and I just inderit that to all controller like this `Public class SearchController : Controller, BaseAuth { }` ? – Ray Oct 21 '16 at 23:08
  • I meant this BaseController : Controller, YourController: BaseController – Eins Oct 21 '16 at 23:11
  • Possible duplicate of [MVC Core How to force / set global authorization for all actions?](http://stackoverflow.com/questions/36413476/mvc-core-how-to-force-set-global-authorization-for-all-actions) – adem caglin Oct 22 '16 at 05:57

1 Answers1

2

A filter is one way to set the authorization attribute for all controllers and actions. For instance, here is policy that requires a specific user name.

services.AddMvc(config =>
{
    var policy = new AuthorizationPolicyBuilder()
                     .RequireUserName("DOMAIN\\USERID")
                     .Build();

    config.Filters.Add(new AuthorizeFilter(policy));
});

The error you're receiving happens because the AuthorizationFilter constructor expects a policy not a policyBuilder. Here is your code with the variable names changed for greater clarity.

services.AddMvc(config =>
{
    var policyBuilder = new AuthorizationPolicyBuilder();
    policyBuilder.Requirements.Add(new UserNamesRequirement("DOMAIN\\USER1"));

    // this will NOT work
    policyBuilder.Build();
    config.Filters.Add(new AuthorizeFilter(policyBuilder));

    // this will work
    var policy = policyBuilder.Build();
    config.Filters.Add(new AuthorizeFilter(policy));

});
Shaun Luttin
  • 133,272
  • 81
  • 405
  • 467
  • Thank you Shaun. That helped alot, but how can I implement my custom policy that I used to leverage custom `handler` and `requirement` class? I added more details in my question with error I get. – Ray Oct 22 '16 at 14:25
  • 1
    @ray The `Build()` method returns a `policy`. It does not change the `policyBuilder` into a `policy`. My edits might clarify this for you. – Shaun Luttin Oct 22 '16 at 15:09
  • ah.. that makes sense now. :) Thank you for taking time for explaining me. I really appreciate your help. – Ray Oct 22 '16 at 15:16
  • hmm.. looks like filtering is not working. I am getting 403 page for one of the controller I am trying to access. – Ray Oct 22 '16 at 16:06
  • @ray 403 means that the particular user is not authorized. My guess is that the one controller has additional restrictions. – Shaun Luttin Oct 22 '16 at 16:19
  • 1
    Could it be the top level `[Authorize]` attribute at controller for `operatingsystemsController`? I removed all `[Authorize]` from another controller and it is working. – Ray Oct 22 '16 at 16:25
  • @Ray What is the name of the controller that is not working? What attributes decorate that controller? – Shaun Luttin Oct 22 '16 at 19:38
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/126419/discussion-between-ray-and-shaun-luttin). – Ray Oct 22 '16 at 21:30