Netcore: Handle tree authentication (#8866)

* Added helper methods to invoke the authorization filters of the other controller action

Signed-off-by: Bjarke Berg <mail@bergmania.dk>

* Implemented Tree Auth

Signed-off-by: Bjarke Berg <mail@bergmania.dk>

* cleanup

Signed-off-by: Bjarke Berg <mail@bergmania.dk>

* Throw forbidden if user has no access instead of InternalServerError

Signed-off-by: Bjarke Berg <mail@bergmania.dk>

* EnsureBackofficeSecurity for background jobs

Signed-off-by: Bjarke Berg <mail@bergmania.dk>

Co-authored-by: Elitsa Marinovska <elm@umbraco.dk>
This commit is contained in:
Bjarke Berg
2020-09-22 13:19:54 +02:00
committed by GitHub
parent 96facc4d35
commit a80de91031
8 changed files with 170 additions and 115 deletions

View File

@@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
namespace Umbraco.Extensions
{
internal static class ControllerContextExtensions
{
/// <summary>
/// Invokes the authorization filters for the controller action.
/// </summary>
/// <returns>Whether the user is authenticated or not.</returns>
internal static async Task<bool> InvokeAuthorizationFiltersForRequest(this ControllerContext controllerContext, ActionContext actionContext)
{
var actionDescriptor = controllerContext.ActionDescriptor;
var filters = actionDescriptor.FilterDescriptors;
var filterGrouping = new FilterGrouping(filters, controllerContext.HttpContext.RequestServices);
// because the continuation gets built from the inside out we need to reverse the filter list
// so that least specific filters (Global) get run first and the most specific filters (Action) get run last.
var authorizationFilters = filterGrouping.AuthorizationFilters.Reverse().ToList();
var asyncAuthorizationFilters = filterGrouping.AsyncAuthorizationFilters.Reverse().ToList();
if (authorizationFilters.Count == 0 && asyncAuthorizationFilters.Count == 0)
return true;
// if the authorization filter returns a result, it means it failed to authorize
var authorizationFilterContext = new AuthorizationFilterContext(actionContext, filters.Select(x=>x.Filter).ToArray());
return await ExecuteAuthorizationFiltersAsync(authorizationFilterContext, authorizationFilters, asyncAuthorizationFilters);
}
/// <summary>
/// Executes a chain of filters.
/// </summary>
/// <remarks>
/// Recursively calls in to itself as its continuation for the next filter in the chain.
/// </remarks>
private static async Task<bool> ExecuteAuthorizationFiltersAsync(
AuthorizationFilterContext authorizationFilterContext,
IList<IAuthorizationFilter> authorizationFilters,
IList<IAsyncAuthorizationFilter> asyncAuthorizationFilters)
{
foreach (var authorizationFilter in authorizationFilters)
{
authorizationFilter.OnAuthorization(authorizationFilterContext);
if (!(authorizationFilterContext.Result is null))
{
return false;
}
}
foreach (var asyncAuthorizationFilter in asyncAuthorizationFilters)
{
await asyncAuthorizationFilter.OnAuthorizationAsync(authorizationFilterContext);
if (!(authorizationFilterContext.Result is null))
{
return false;
}
}
return true;
}
/// <summary>
/// Quickly split filters into different types
/// </summary>
private class FilterGrouping
{
private readonly List<IActionFilter> _actionFilters = new List<IActionFilter>();
private readonly List<IAsyncActionFilter> _asyncActionFilters = new List<IAsyncActionFilter>();
private readonly List<IAuthorizationFilter> _authorizationFilters = new List<IAuthorizationFilter>();
private readonly List<IAsyncAuthorizationFilter> _asyncAuthorizationFilters = new List<IAsyncAuthorizationFilter>();
private readonly List<IExceptionFilter> _exceptionFilters = new List<IExceptionFilter>();
private readonly List<IAsyncExceptionFilter> _asyncExceptionFilters = new List<IAsyncExceptionFilter>();
public FilterGrouping(IEnumerable<FilterDescriptor> filters, IServiceProvider serviceProvider)
{
if (filters == null) throw new ArgumentNullException("filters");
foreach (FilterDescriptor f in filters)
{
var filter = f.Filter;
Categorize(filter, _actionFilters, serviceProvider);
Categorize(filter, _authorizationFilters, serviceProvider);
Categorize(filter, _exceptionFilters, serviceProvider);
Categorize(filter, _asyncActionFilters, serviceProvider);
Categorize(filter, _asyncAuthorizationFilters, serviceProvider);
}
}
public IEnumerable<IActionFilter> ActionFilters => _actionFilters;
public IEnumerable<IAsyncActionFilter> AsyncActionFilters => _asyncActionFilters;
public IEnumerable<IAuthorizationFilter> AuthorizationFilters => _authorizationFilters;
public IEnumerable<IAsyncAuthorizationFilter> AsyncAuthorizationFilters => _asyncAuthorizationFilters;
public IEnumerable<IExceptionFilter> ExceptionFilters => _exceptionFilters;
public IEnumerable<IAsyncExceptionFilter> AsyncExceptionFilters => _asyncExceptionFilters;
private static void Categorize<T>(IFilterMetadata filter, List<T> list, IServiceProvider serviceProvider) where T : class
{
if(filter is TypeFilterAttribute typeFilterAttribute)
{
filter = typeFilterAttribute.CreateInstance(serviceProvider);
}
T match = filter as T;
if (match != null)
{
list.Add(match);
}
}
}
}
}