Merge
This commit is contained in:
@@ -1,48 +0,0 @@
|
||||
using System.Runtime.Remoting.Contexts;
|
||||
using System.Web.Http.Controllers;
|
||||
|
||||
namespace Umbraco.Web.WebApi
|
||||
{
|
||||
internal static class ControllerContextExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the JSON GUID format to not have hyphens
|
||||
/// </summary>
|
||||
/// <param name="controllerContext"></param>
|
||||
internal static void SetOutgoingNoHyphenGuidFormat(this HttpControllerContext controllerContext)
|
||||
{
|
||||
var jsonFormatter = controllerContext.Configuration.Formatters.JsonFormatter;
|
||||
jsonFormatter.SerializerSettings.Converters.Add(new GuidNoHyphenConverter());
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the JSON datetime format to be a custom one
|
||||
/// </summary>
|
||||
/// <param name="controllerContext"></param>
|
||||
/// <param name="format"></param>
|
||||
internal static void SetOutgoingDateTimeFormat(this HttpControllerContext controllerContext, string format)
|
||||
{
|
||||
var jsonFormatter = controllerContext.Configuration.Formatters.JsonFormatter;
|
||||
jsonFormatter.SerializerSettings.Converters.Add(new CustomDateTimeConvertor(format));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the JSON datetime format to be our regular iso standard
|
||||
/// </summary>
|
||||
internal static void SetOutgoingDateTimeFormat(this HttpControllerContext controllerContext)
|
||||
{
|
||||
var jsonFormatter = controllerContext.Configuration.Formatters.JsonFormatter;
|
||||
jsonFormatter.SerializerSettings.Converters.Add(new CustomDateTimeConvertor("yyyy-MM-dd HH:mm:ss"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the xml formatter so it only outputs json
|
||||
/// </summary>
|
||||
/// <param name="controllerContext"></param>
|
||||
internal static void EnsureJsonOutputOnly(this HttpControllerContext controllerContext)
|
||||
{
|
||||
controllerContext.Configuration.Formatters.Remove(controllerContext.Configuration.Formatters.XmlFormatter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,138 +1,134 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Web.Http.Filters;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.WebApi.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// This inspects the result of the action that returns a collection of content and removes
|
||||
/// any item that the current user doesn't have access to
|
||||
/// </summary>
|
||||
internal class FilterAllowedOutgoingMediaAttribute : ActionFilterAttribute
|
||||
{
|
||||
private readonly Type _outgoingType;
|
||||
private readonly string _propertyName;
|
||||
|
||||
public FilterAllowedOutgoingMediaAttribute(Type outgoingType)
|
||||
{
|
||||
_outgoingType = outgoingType;
|
||||
}
|
||||
|
||||
public FilterAllowedOutgoingMediaAttribute(Type outgoingType, string propertyName)
|
||||
: this(outgoingType)
|
||||
{
|
||||
_propertyName = propertyName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true so that other filters can execute along with this one
|
||||
/// </summary>
|
||||
public override bool AllowMultiple
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
|
||||
{
|
||||
var user = UmbracoContext.Current.Security.CurrentUser;
|
||||
if (user == null)
|
||||
{
|
||||
base.OnActionExecuted(actionExecutedContext);
|
||||
return;
|
||||
}
|
||||
|
||||
var objectContent = actionExecutedContext.Response.Content as ObjectContent;
|
||||
if (objectContent != null)
|
||||
{
|
||||
var collection = GetValueFromResponse(objectContent);
|
||||
|
||||
if (collection != null)
|
||||
{
|
||||
var items = Enumerable.ToList(collection);
|
||||
|
||||
FilterItems(user, items);
|
||||
|
||||
//set the return value
|
||||
SetValueForResponse(objectContent, items);
|
||||
}
|
||||
}
|
||||
|
||||
base.OnActionExecuted(actionExecutedContext);
|
||||
}
|
||||
|
||||
protected virtual void FilterItems(IUser user, IList items)
|
||||
{
|
||||
FilterBasedOnStartNode(items, user);
|
||||
}
|
||||
|
||||
internal void FilterBasedOnStartNode(IList items, IUser user)
|
||||
{
|
||||
if (items.Count > 0)
|
||||
{
|
||||
var toRemove = new List<dynamic>();
|
||||
foreach (dynamic item in items)
|
||||
{
|
||||
var hasPathAccess = (item != null && UserExtensions.HasPathAccess(item.Path, user.StartContentId, Constants.System.RecycleBinContent));
|
||||
if (!hasPathAccess)
|
||||
{
|
||||
toRemove.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in toRemove)
|
||||
{
|
||||
items.Remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetValueForResponse(ObjectContent objectContent, dynamic newVal)
|
||||
{
|
||||
if (TypeHelper.IsTypeAssignableFrom(_outgoingType, objectContent.Value.GetType()))
|
||||
{
|
||||
objectContent.Value = newVal;
|
||||
}
|
||||
else if (_propertyName.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
//try to get the enumerable collection from a property on the result object using reflection
|
||||
var property = objectContent.Value.GetType().GetProperty(_propertyName);
|
||||
if (property != null)
|
||||
{
|
||||
property.SetValue(objectContent.Value, newVal);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal dynamic GetValueFromResponse(ObjectContent objectContent)
|
||||
{
|
||||
if (TypeHelper.IsTypeAssignableFrom(_outgoingType, objectContent.Value.GetType()))
|
||||
{
|
||||
return objectContent.Value;
|
||||
}
|
||||
|
||||
if (_propertyName.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
//try to get the enumerable collection from a property on the result object using reflection
|
||||
var property = objectContent.Value.GetType().GetProperty(_propertyName);
|
||||
if (property != null)
|
||||
{
|
||||
var result = property.GetValue(objectContent.Value);
|
||||
if (result != null && TypeHelper.IsTypeAssignableFrom(_outgoingType, result.GetType()))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Web.Http.Filters;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.WebApi.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// This inspects the result of the action that returns a collection of content and removes
|
||||
/// any item that the current user doesn't have access to
|
||||
/// </summary>
|
||||
internal class FilterAllowedOutgoingMediaAttribute : ActionFilterAttribute
|
||||
{
|
||||
private readonly Type _outgoingType;
|
||||
private readonly string _propertyName;
|
||||
|
||||
public FilterAllowedOutgoingMediaAttribute(Type outgoingType)
|
||||
{
|
||||
_outgoingType = outgoingType;
|
||||
}
|
||||
|
||||
public FilterAllowedOutgoingMediaAttribute(Type outgoingType, string propertyName)
|
||||
: this(outgoingType)
|
||||
{
|
||||
_propertyName = propertyName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true so that other filters can execute along with this one
|
||||
/// </summary>
|
||||
public override bool AllowMultiple
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
|
||||
{
|
||||
var user = UmbracoContext.Current.Security.CurrentUser;
|
||||
if (user == null)
|
||||
{
|
||||
base.OnActionExecuted(actionExecutedContext);
|
||||
return;
|
||||
}
|
||||
|
||||
var objectContent = actionExecutedContext.Response.Content as ObjectContent;
|
||||
if (objectContent != null)
|
||||
{
|
||||
var collection = GetValueFromResponse(objectContent);
|
||||
|
||||
if (collection != null)
|
||||
{
|
||||
var items = Enumerable.ToList(collection);
|
||||
|
||||
FilterItems(user, items);
|
||||
|
||||
//set the return value
|
||||
SetValueForResponse(objectContent, items);
|
||||
}
|
||||
}
|
||||
|
||||
base.OnActionExecuted(actionExecutedContext);
|
||||
}
|
||||
|
||||
protected virtual void FilterItems(IUser user, IList items)
|
||||
{
|
||||
FilterBasedOnStartNode(items, user);
|
||||
}
|
||||
|
||||
internal void FilterBasedOnStartNode(IList items, IUser user)
|
||||
{
|
||||
if (items.Count > 0)
|
||||
{
|
||||
var hasPathAccess = (item != null && UserExtensions.HasPathAccess(item.Path, user.StartContentId, Constants.System.RecycleBinContent));
|
||||
if (!hasPathAccess)
|
||||
{
|
||||
toRemove.Add(item);
|
||||
}
|
||||
|
||||
foreach (var item in toRemove)
|
||||
{
|
||||
items.Remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetValueForResponse(ObjectContent objectContent, dynamic newVal)
|
||||
{
|
||||
if (TypeHelper.IsTypeAssignableFrom(_outgoingType, objectContent.Value.GetType()))
|
||||
{
|
||||
objectContent.Value = newVal;
|
||||
}
|
||||
else if (_propertyName.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
//try to get the enumerable collection from a property on the result object using reflection
|
||||
var property = objectContent.Value.GetType().GetProperty(_propertyName);
|
||||
if (property != null)
|
||||
{
|
||||
property.SetValue(objectContent.Value, newVal);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal dynamic GetValueFromResponse(ObjectContent objectContent)
|
||||
{
|
||||
if (TypeHelper.IsTypeAssignableFrom(_outgoingType, objectContent.Value.GetType()))
|
||||
{
|
||||
return objectContent.Value;
|
||||
}
|
||||
|
||||
if (_propertyName.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
//try to get the enumerable collection from a property on the result object using reflection
|
||||
var property = objectContent.Value.GetType().GetProperty(_propertyName);
|
||||
if (property != null)
|
||||
{
|
||||
var result = property.GetValue(objectContent.Value);
|
||||
if (result != null && TypeHelper.IsTypeAssignableFrom(_outgoingType, result.GetType()))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
56
src/Umbraco.Web/WebApi/Filters/FilterGrouping.cs
Normal file
56
src/Umbraco.Web/WebApi/Filters/FilterGrouping.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Http.Filters;
|
||||
|
||||
namespace Umbraco.Web.WebApi.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// Quickly split filters into different types
|
||||
/// </summary>
|
||||
internal class FilterGrouping
|
||||
{
|
||||
private readonly List<IActionFilter> _actionFilters = new List<IActionFilter>();
|
||||
private readonly List<IAuthorizationFilter> _authorizationFilters = new List<IAuthorizationFilter>();
|
||||
private readonly List<IExceptionFilter> _exceptionFilters = new List<IExceptionFilter>();
|
||||
|
||||
public FilterGrouping(IEnumerable<FilterInfo> filters)
|
||||
{
|
||||
if (filters == null) throw new ArgumentNullException("filters");
|
||||
|
||||
foreach (FilterInfo f in filters)
|
||||
{
|
||||
var filter = f.Instance;
|
||||
Categorize(filter, _actionFilters);
|
||||
Categorize(filter, _authorizationFilters);
|
||||
Categorize(filter, _exceptionFilters);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<IActionFilter> ActionFilters
|
||||
{
|
||||
get { return _actionFilters; }
|
||||
}
|
||||
|
||||
public IEnumerable<IAuthorizationFilter> AuthorizationFilters
|
||||
{
|
||||
get { return _authorizationFilters; }
|
||||
}
|
||||
|
||||
public IEnumerable<IExceptionFilter> ExceptionFilters
|
||||
{
|
||||
get { return _exceptionFilters; }
|
||||
}
|
||||
|
||||
private static void Categorize<T>(IFilter filter, List<T> list) where T : class
|
||||
{
|
||||
T match = filter as T;
|
||||
if (match != null)
|
||||
{
|
||||
list.Add(match);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
107
src/Umbraco.Web/WebApi/HttpControllerContextExtensions.cs
Normal file
107
src/Umbraco.Web/WebApi/HttpControllerContextExtensions.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.Remoting.Contexts;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Http;
|
||||
using System.Web.Http.Controllers;
|
||||
using System.Web.Http.Filters;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
|
||||
namespace Umbraco.Web.WebApi
|
||||
{
|
||||
internal static class HttpControllerContextExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// This method will go an execute the authorization filters for the controller action, if any fail
|
||||
/// it will return their response, otherwise we'll return null.
|
||||
/// </summary>
|
||||
/// <param name="controllerContext"></param>
|
||||
internal static async Task<HttpResponseMessage> InvokeAuthorizationFiltersForRequest(this HttpControllerContext controllerContext)
|
||||
{
|
||||
var controllerDescriptor = controllerContext.ControllerDescriptor;
|
||||
var controllerServices = controllerDescriptor.Configuration.Services;
|
||||
var actionDescriptor = controllerServices.GetActionSelector().SelectAction(controllerContext);
|
||||
var actionContext = new HttpActionContext(controllerContext, actionDescriptor);
|
||||
var filters = actionDescriptor.GetFilterPipeline();
|
||||
var filterGrouping = new FilterGrouping(filters);
|
||||
var actionFilters = filterGrouping.ActionFilters;
|
||||
// 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().ToArray();
|
||||
|
||||
if (authorizationFilters.Any())
|
||||
{
|
||||
var cancelToken = new CancellationToken();
|
||||
var filterResult = await FilterContinuation(actionContext, cancelToken, authorizationFilters, 0);
|
||||
if (filterResult != null)
|
||||
{
|
||||
//this means that the authorization filter has returned a result - unauthorized so we cannot continue
|
||||
return filterResult;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is how you execute a chain of filters, it needs to recursively call in to itself as the continuation for the next filter in the chain
|
||||
/// </summary>
|
||||
/// <param name="actionContext"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <param name="filters"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
private static async Task<HttpResponseMessage> FilterContinuation(HttpActionContext actionContext, CancellationToken token, IList<IAuthorizationFilter> filters, int index)
|
||||
{
|
||||
return await filters[index].ExecuteAuthorizationFilterAsync(actionContext, token, () =>
|
||||
{
|
||||
Func<HttpResponseMessage> nullResponse = () => null;
|
||||
return (index + 1) == filters.Count
|
||||
? Task.Run(nullResponse)
|
||||
: FilterContinuation(actionContext, token, filters, ++index);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the JSON GUID format to not have hyphens
|
||||
/// </summary>
|
||||
/// <param name="controllerContext"></param>
|
||||
internal static void SetOutgoingNoHyphenGuidFormat(this HttpControllerContext controllerContext)
|
||||
{
|
||||
var jsonFormatter = controllerContext.Configuration.Formatters.JsonFormatter;
|
||||
jsonFormatter.SerializerSettings.Converters.Add(new GuidNoHyphenConverter());
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the JSON datetime format to be a custom one
|
||||
/// </summary>
|
||||
/// <param name="controllerContext"></param>
|
||||
/// <param name="format"></param>
|
||||
internal static void SetOutgoingDateTimeFormat(this HttpControllerContext controllerContext, string format)
|
||||
{
|
||||
var jsonFormatter = controllerContext.Configuration.Formatters.JsonFormatter;
|
||||
jsonFormatter.SerializerSettings.Converters.Add(new CustomDateTimeConvertor(format));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the JSON datetime format to be our regular iso standard
|
||||
/// </summary>
|
||||
internal static void SetOutgoingDateTimeFormat(this HttpControllerContext controllerContext)
|
||||
{
|
||||
var jsonFormatter = controllerContext.Configuration.Formatters.JsonFormatter;
|
||||
jsonFormatter.SerializerSettings.Converters.Add(new CustomDateTimeConvertor("yyyy-MM-dd HH:mm:ss"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the xml formatter so it only outputs json
|
||||
/// </summary>
|
||||
/// <param name="controllerContext"></param>
|
||||
internal static void EnsureJsonOutputOnly(this HttpControllerContext controllerContext)
|
||||
{
|
||||
controllerContext.Configuration.Formatters.Remove(controllerContext.Configuration.Formatters.XmlFormatter);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user