Michael
Michael

Reputation: 12011

OverrideAuthorizationAttribute in ASP.NET 5

I would like to implement the following in MVC6:

[Authorize(Roles = "Shopper")]
public class HomeController
{
    [Authorize(Roles = "Editor"), OverrideAuthorization]
    public IActionResult EditPage() {}
}

But OverrideAuthorizationAttribute no longer exists. So how do you set it so that a user only needs to be in the Editor role and not Editor and Shopper role to access EditPage in MVC6?

Upvotes: 4

Views: 1684

Answers (2)

Daniel J.G.
Daniel J.G.

Reputation: 34992

I found this blog post from Filip W that explains how write your own solution using the filter providers.

However the framework has changed a lot and his solution has to be updated to take into account the changes in the framework up to beta8.

First you will create a new attribute where you can specify the type of the filter that you want to override. (In your case this would be the AuthorizeFilter)

public class OverrideFilter : ActionFilterAttribute
{
    public Type Type { get; set; }
}

If you want. you could create more specific filters like:

public class OverrideAuthorization : OverrideFilter
{
    public OverrideAuthorization()
    {
        this.Type = typeof(AuthorizeFilter);
    }
}

Then you need to create a new IFilterProvider.

  1. This filter provider will be executed after the default providers in the framework have run.
  2. You can inspect the FilterProviderContext.Results and search for your OverrideFilter
  3. If found, you can then inspect the rest of the filters, and delete any filter that is of the filtered type and a lower scope

For example create a new OverrideFriendlyFilterProvider following this idea:

public class OverrideFriendlyFilterProvider : IFilterProvider
{
    //all framework providers have negative orders, so ours will come later
    public int Order => 1;

    public void OnProvidersExecuting(FilterProviderContext context)
    {
        if (context.ActionContext.ActionDescriptor.FilterDescriptors != null)
        {
            //Does the action have any OverrideFilter?
            var overrideFilters = context.Results.Where(filterItem => filterItem.Filter is OverrideFilter).ToArray();
            foreach (var overrideFilter in overrideFilters)
            {                    
                context.Results.RemoveAll(filterItem =>
                    //Remove any filter for the type indicated in the OverrideFilter attribute
                    filterItem.Descriptor.Filter.GetType() == ((OverrideFilter)overrideFilter.Filter).Type &&
                    //Remove filters with lower scope (ie controller) than the override filter (i.e. action method)
                    filterItem.Descriptor.Scope < overrideFilter.Descriptor.Scope);
            }
        }
    }

    public void OnProvidersExecuted(FilterProviderContext context)
    {
    }
}

You need to register it on the ConfigureServices of your startup class:

services.TryAddEnumerable(
    ServiceDescriptor.Singleton<IFilterProvider, OverrideFriendlyFilterProvider>());

With all this pieces you will be able to override the authorization filter (or any other filter).

For example in the default HomeController of a new mvc application, any logged in user will be able to access the Home action, but only the ones with the admin role will be able to access the About action:

[Authorize]
public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    [Authorize(Roles = "admin"), OverrideAuthorization]
    public IActionResult About()
    {
        return View();
    }

Upvotes: 2

Joe Audette
Joe Audette

Reputation: 36706

I think it would be better to use the new policy based authorization approach instead of using roles directly.

There is not a lot of documentation yet about policy based authorization but this article is a good start

Upvotes: 0

Related Questions