Fixes up more permissions checks, refactors MediaController.PostAddFile to use the correct WebAPI usage. Fixes issue with UmbracoClientManager throwing errors because the treeProps didn't include all called props.

This commit is contained in:
Shannon
2013-09-03 16:35:36 +10:00
parent f0a2bfdea4
commit 7c9f5eda9d
10 changed files with 118 additions and 64 deletions

View File

@@ -372,7 +372,7 @@ namespace Umbraco.Core.Models
/// Sets the <see cref="System.Web.HttpPostedFile"/> value of a Property
/// </summary>
/// <param name="propertyTypeAlias">Alias of the PropertyType</param>
/// <param name="value">Value to set for the Property</param>
/// <param name="value">Value to set for the Property</param>
public virtual void SetPropertyValue(string propertyTypeAlias, HttpPostedFile value)
{
ContentExtensions.SetValue(this, propertyTypeAlias, value);
@@ -393,6 +393,7 @@ namespace Umbraco.Core.Models
/// </summary>
/// <param name="propertyTypeAlias">Alias of the PropertyType</param>
/// <param name="value">Value to set for the Property</param>
[Obsolete("There is no reason for this overload since HttpPostedFileWrapper inherits from HttpPostedFileBase")]
public virtual void SetPropertyValue(string propertyTypeAlias, HttpPostedFileWrapper value)
{
ContentExtensions.SetValue(this, propertyTypeAlias, value);

View File

@@ -365,20 +365,7 @@ namespace Umbraco.Core.Models
/// <param name="value">The <see cref="HttpPostedFile"/> containing the file that will be uploaded</param>
public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFile value)
{
// Ensure we get the filename without the path in IE in intranet mode
// http://stackoverflow.com/questions/382464/httppostedfile-filename-different-from-ie
var fileName = value.FileName;
if (fileName.LastIndexOf(@"\") > 0)
fileName = fileName.Substring(fileName.LastIndexOf(@"\") + 1);
var name =
IOHelper.SafeFileName(
fileName.Substring(fileName.LastIndexOf(IOHelper.DirSepChar) + 1,
fileName.Length - fileName.LastIndexOf(IOHelper.DirSepChar) - 1)
.ToLower());
if (string.IsNullOrEmpty(name) == false)
SetFileOnContent(content, propertyTypeAlias, name, value.InputStream);
SetValue(content, propertyTypeAlias, (HttpPostedFileBase)new HttpPostedFileWrapper(value));
}
/// <summary>
@@ -387,20 +374,12 @@ namespace Umbraco.Core.Models
/// <param name="content"><see cref="IContentBase"/> to add property value to</param>
/// <param name="propertyTypeAlias">Alias of the property to save the value on</param>
/// <param name="value">The <see cref="HttpPostedFileWrapper"/> containing the file that will be uploaded</param>
[Obsolete("There is no reason for this overload since HttpPostedFileWrapper inherits from HttpPostedFileBase")]
public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFileWrapper value)
{
// Ensure we get the filename without the path in IE in intranet mode
// http://stackoverflow.com/questions/382464/httppostedfile-filename-different-from-ie
var fileName = value.FileName;
if (fileName.LastIndexOf(@"\") > 0)
fileName = fileName.Substring(fileName.LastIndexOf(@"\") + 1);
var name = IOHelper.SafeFileName(fileName);
if (string.IsNullOrEmpty(name) == false)
SetFileOnContent(content, propertyTypeAlias, name, value.InputStream);
SetValue(content, propertyTypeAlias, (HttpPostedFileBase)value);
}
/// <summary>
/// Sets and uploads the file from a <see cref="Stream"/> as the property value
/// </summary>

View File

@@ -57,7 +57,7 @@ Umbraco.Sys.registerNamespace("Umbraco.Application");
|| this.mainWindow().jQuery(".umbTree").UmbracoTreeAPI() == null) {
//creates a false tree with all the public tree params set to a false method.
var tmpTree = {};
var treeProps = ["init", "setRecycleBinNodeId", "clearTreeCache", "toggleEditMode", "refreshTree", "rebuildTree", "saveTreeState", "syncTree", "childNodeCreated", "moveNode", "copyNode", "findNode", "selectNode", "reloadActionNode", "getActionNode", "setActiveTreeType", "getNodeDef"];
var treeProps = ["init", "setRecycleBinNodeId", "clearTreeCache", "toggleEditMode", "refreshTree", "rebuildTree", "saveTreeState", "syncTree", "childNodeCreated", "moveNode", "copyNode", "findNode", "selectNode", "reloadActionNode", "getActionNode", "setActiveTreeType", "getNodeDef", "addEventHandler", "removeEventHandler"];
for (var p in treeProps) {
tmpTree[treeProps[p]] = function() { return false; };
}

View File

@@ -1,11 +1,16 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using System.Web.Http.ModelBinding;
using AutoMapper;
using Umbraco.Core;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Editors;
@@ -89,6 +94,7 @@ namespace Umbraco.Web.Editors
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
[FilterAllowedOutgoingMedia(typeof(IEnumerable<MediaItemDisplay>))]
public IEnumerable<MediaItemDisplay> GetByIds([FromUri]int[] ids)
{
var foundMedia = ((MediaService)Services.MediaService).GetByIds(ids);
@@ -99,6 +105,7 @@ namespace Umbraco.Web.Editors
/// <summary>
/// Returns the root media objects
/// </summary>
[FilterAllowedOutgoingMedia(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic, IMedia>>))]
public IEnumerable<ContentItemBasic<ContentPropertyBasic, IMedia>> GetRootMedia()
{
//TODO: Add permissions check!
@@ -110,11 +117,11 @@ namespace Umbraco.Web.Editors
/// <summary>
/// Returns the child media objects
/// </summary>
[FilterAllowedOutgoingMedia(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic, IMedia>>))]
public IEnumerable<ContentItemBasic<ContentPropertyBasic, IMedia>> GetChildren(int parentId)
{
//TODO: Change this to be like content with paged params
//TODO: filter results based on permissions!
return Services.MediaService.GetChildren(parentId)
.Select(Mapper.Map<IMedia, ContentItemBasic<ContentPropertyBasic, IMedia>>);
}
@@ -185,6 +192,7 @@ namespace Umbraco.Web.Editors
/// </summary>
/// <param name="sorted"></param>
/// <returns></returns>
[EnsureUserPermissionForMedia("sorted.ParentId")]
public HttpResponseMessage PostSort(ContentSortOrder sorted)
{
if (sorted == null)
@@ -219,7 +227,7 @@ namespace Umbraco.Web.Editors
}
}
//shorthand to use with the media dialog
[EnsureUserPermissionForMedia("folder.ParentId")]
public MediaItemDisplay PostAddFolder(EntityBasic folder)
{
var mediaService = ApplicationContext.Services.MediaService;
@@ -229,32 +237,64 @@ namespace Umbraco.Web.Editors
return Mapper.Map<IMedia, MediaItemDisplay>(f);
}
//short hand to use with the uploader in the media dialog
public HttpResponseMessage PostAddFile()
/// <summary>
/// Used to submit a media file
/// </summary>
/// <returns></returns>
/// <remarks>
/// We cannot validate this request with attributes (nicely) due to the nature of the multi-part for data.
/// </remarks>
public async Task<HttpResponseMessage> PostAddFile()
{
var context = UmbracoContext.HttpContext;
if(context.Request.Files.Count > 0)
if (Request.Content.IsMimeMultipartContent() == false)
{
if (context.Request.Form.Count > 0)
{
int parentId;
if (int.TryParse(context.Request.Form[0], out parentId))
{
var file = context.Request.Files[0];
var name = file.FileName;
var mediaService = base.ApplicationContext.Services.MediaService;
var f = mediaService.CreateMedia(name, parentId, Constants.Conventions.MediaTypes.Image);
f.SetValue(Constants.Conventions.Media.File, file);
mediaService.Save(f);
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
return new HttpResponseMessage(HttpStatusCode.NotFound);
var root = IOHelper.MapPath("~/App_Data/TEMP/FileUploads");
//ensure it exists
Directory.CreateDirectory(root);
var provider = new MultipartFormDataStreamProvider(root);
var result = await Request.Content.ReadAsMultipartAsync(provider);
//must have a file
if (result.FileData.Count == 0)
{
return new HttpResponseMessage(HttpStatusCode.NotFound);
}
//get the string json from the request
int parentId;
if (int.TryParse(result.FormData["currentFolder"], out parentId) == false)
{
throw new HttpResponseException(
new HttpResponseMessage(HttpStatusCode.BadRequest)
{
ReasonPhrase = "The request was not formatted correctly, the currentFolder is not an integer"
});
}
//get the files
foreach (var file in result.FileData)
{
var fileName = file.Headers.ContentDisposition.FileName.Trim(new[] { '\"' });
var mediaService = ApplicationContext.Services.MediaService;
var f = mediaService.CreateMedia(fileName, parentId, Constants.Conventions.MediaTypes.Image);
using (var fs = System.IO.File.OpenRead(file.LocalFileName))
{
f.SetValue(Constants.Conventions.Media.File, fileName, fs);
}
mediaService.Save(f);
//now we can remove the temp file
System.IO.File.Delete(file.LocalFileName);
}
return new HttpResponseMessage(HttpStatusCode.OK);
}

View File

@@ -31,6 +31,6 @@ namespace Umbraco.Web.Models.ContentEditing
/// For now we'll exclude this from the json results, this is needed for permissions check filtering
/// </summary>
[IgnoreDataMember]
internal string Path { get; set; }
public string Path { get; set; }
}
}

View File

@@ -26,7 +26,7 @@ namespace Umbraco.Web.Models.Mapping
//FROM IContent TO ContentItemDisplay
config.CreateMap<IContent, ContentItemDisplay>()
.ForMember(
.ForMember(
dto => dto.Owner,
expression => expression.ResolveUsing<OwnerResolver<IContent>>())
.ForMember(

View File

@@ -16,6 +16,7 @@ using System.Web.ModelBinding;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Umbraco.Core;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Security;
@@ -54,7 +55,7 @@ namespace Umbraco.Web.WebApi.Binders
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var root = HttpContext.Current.Server.MapPath("~/App_Data/TEMP/FileUploads");
var root = IOHelper.MapPath("~/App_Data/TEMP/FileUploads");
//ensure it exists
Directory.CreateDirectory(root);
var provider = new MultipartFormDataStreamProvider(root);

View File

@@ -22,6 +22,14 @@ namespace Umbraco.Web.WebApi.Filters
{
private readonly int? _nodeId;
private readonly string _paramName;
private DictionarySource _source;
public enum DictionarySource
{
ActionArguments,
RequestForm,
RequestQueryString
}
/// <summary>
/// This constructor will only be able to test the start node access
@@ -34,7 +42,15 @@ namespace Umbraco.Web.WebApi.Filters
public EnsureUserPermissionForMediaAttribute(string paramName)
{
Mandate.ParameterNotNullOrEmpty(paramName, "paramName");
_paramName = paramName;
_paramName = paramName;
_source = DictionarySource.ActionArguments;
}
public EnsureUserPermissionForMediaAttribute(string paramName, DictionarySource source)
{
Mandate.ParameterNotNullOrEmpty(paramName, "paramName");
_paramName = paramName;
_source = source;
}
public override bool AllowMultiple
@@ -94,6 +110,21 @@ namespace Umbraco.Web.WebApi.Filters
}
//private object GetValueFromSource(HttpActionContext actionContext, string key)
//{
// switch (_source)
// {
// case DictionarySource.ActionArguments:
// return actionContext.ActionArguments[key];
// case DictionarySource.RequestForm:
// return actionContext.Request.Properties
// case DictionarySource.RequestQueryString:
// break;
// default:
// throw new ArgumentOutOfRangeException();
// }
//}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -35,25 +36,25 @@ namespace Umbraco.Web.WebApi.Filters
{
}
protected override void FilterItems(IUser user, List<dynamic> items)
protected override void FilterItems(IUser user, IList items)
{
base.FilterItems(user, items);
FilterBasedOnPermissions(items, user, ApplicationContext.Current.Services.UserService);
}
internal void FilterBasedOnPermissions(List<dynamic> items, IUser user, IUserService userService)
internal void FilterBasedOnPermissions(IList items, IUser user, IUserService userService)
{
var length = items.Count;
var ids = new List<int>();
for (var i = 0; i < length; i++)
{
ids.Add(items[i].Id);
ids.Add(((dynamic)items[i]).Id);
}
//get all the permissions for these nodes in one call
var permissions = userService.GetPermissions(user, ids.ToArray()).ToArray();
var toRemove = new List<dynamic>();
foreach(var item in items)
foreach(dynamic item in items)
{
var nodePermission = permissions.Where(x => x.EntityId == item.Id).ToArray();
//if there are no permissions for this id, then remove the item

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
@@ -66,15 +67,15 @@ namespace Umbraco.Web.WebApi.Filters
base.OnActionExecuted(actionExecutedContext);
}
protected virtual void FilterItems(IUser user, List<dynamic> items)
protected virtual void FilterItems(IUser user, IList items)
{
FilterBasedOnStartNode(items, user);
}
internal void FilterBasedOnStartNode(List<dynamic> items, IUser user)
internal void FilterBasedOnStartNode(IList items, IUser user)
{
var toRemove = new List<dynamic>();
foreach (var item in items)
foreach (dynamic item in items)
{
var hasPathAccess = UserExtensions.HasPathAccess(item.Path, user.StartContentId, Constants.System.RecycleBinContent);
if (!hasPathAccess)