From aba4f02bad57683980e54a9957137f3b6b9dbdb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Corn=C3=A9=20Hoskam?= Date: Mon, 28 Feb 2022 16:29:23 +0100 Subject: [PATCH] (umbraCollab) Check media Parent for permissions when setting correct MediaType (#11858) --- .../Controllers/ContentControllerBase.cs | 2 +- .../Controllers/MediaController.cs | 208 ++++++++++++------ src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 3 + .../umbraco/config/lang/en_us.xml | 3 + 4 files changed, 153 insertions(+), 63 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs index f389641777..b22a7d715c 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs @@ -224,7 +224,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { // if there's already a default event message, don't add our default one IEventMessagesFactory messages = EventMessages; - if (messages != null && messages.GetOrDefault().GetAll().Any(x => x.IsDefaultEventMessage)) + if (messages?.GetOrDefault()?.GetAll().Any(x => x.IsDefaultEventMessage) == true) { return; } diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs index 14a9080586..b901d5b431 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs @@ -676,12 +676,19 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { return new ActionResult(parentIdResult.Result); } + var parentId = parentIdResult.Value; if (!parentId.HasValue) { return NotFound("The passed id doesn't exist"); } + var isFolderAllowed = IsFolderCreationAllowedHere(parentId.Value); + if (isFolderAllowed == false) + { + return ValidationProblem(_localizedTextService.Localize("speechBubbles", "folderCreationNotAllowed")); + } + var f = _mediaService.CreateMedia(folder.Name, parentId.Value, Constants.Conventions.MediaTypes.Folder); _mediaService.Save(f, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); @@ -722,10 +729,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var tempFiles = new PostedFiles(); - //in case we pass a path with a folder in it, we will create it and upload media to it. if (!string.IsNullOrEmpty(path)) { + if (!IsFolderCreationAllowedHere(parentId.Value)) + { + AddCancelMessage(tempFiles, _localizedTextService.Localize("speechBubbles", "folderUploadNotAllowed")); + return Ok(tempFiles); + } var folders = path.Split(Constants.CharArrays.ForwardSlash); @@ -735,7 +746,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers IMedia folderMediaItem; //if uploading directly to media root and not a subfolder - if (parentId == -1) + if (parentId == Constants.System.Root) { //look for matching folder folderMediaItem = @@ -768,11 +779,50 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers _mediaService.Save(folderMediaItem); } } + //set the media root to the folder id so uploaded files will end there. parentId = folderMediaItem.Id; } } + var mediaTypeAlias = string.Empty; + var allMediaTypes = _mediaTypeService.GetAll().ToList(); + var allowedContentTypes = new HashSet(); + + if (parentId != Constants.System.Root) + { + var mediaFolderItem = _mediaService.GetById(parentId.Value); + var mediaFolderType = allMediaTypes.FirstOrDefault(x => x.Alias == mediaFolderItem.ContentType.Alias); + + if (mediaFolderType != null) + { + IMediaType mediaTypeItem = null; + + foreach (ContentTypeSort allowedContentType in mediaFolderType.AllowedContentTypes) + { + IMediaType checkMediaTypeItem = allMediaTypes.FirstOrDefault(x => x.Id == allowedContentType.Id.Value); + allowedContentTypes.Add(checkMediaTypeItem); + + var fileProperty = checkMediaTypeItem?.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == Constants.Conventions.Media.File); + if (fileProperty != null) + { + mediaTypeItem = checkMediaTypeItem; + } + } + + //Only set the permission-based mediaType if we only allow 1 specific file under this parent. + if (allowedContentTypes.Count == 1 && mediaTypeItem != null) + { + mediaTypeAlias = mediaTypeItem.Alias; + } + } + } + else + { + var typesAllowedAtRoot = allMediaTypes.Where(x => x.AllowedAsRoot).ToList(); + allowedContentTypes.UnionWith(typesAllowedAtRoot); + } + //get the files foreach (var formFile in file) { @@ -780,71 +830,82 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var safeFileName = fileName.ToSafeFileName(ShortStringHelper); var ext = safeFileName.Substring(safeFileName.LastIndexOf('.') + 1).ToLower(); - if (_contentSettings.IsFileAllowedForUpload(ext)) - { - var mediaType = Constants.Conventions.MediaTypes.File; - - if (contentTypeAlias == Constants.Conventions.MediaTypes.AutoSelect) - { - var mediaTypes = _mediaTypeService.GetAll(); - // Look up MediaTypes - foreach (var mediaTypeItem in mediaTypes) - { - var fileProperty = mediaTypeItem.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == "umbracoFile"); - if (fileProperty != null) - { - var dataTypeKey = fileProperty.DataTypeKey; - var dataType = _dataTypeService.GetDataType(dataTypeKey); - - if (dataType != null && dataType.Configuration is IFileExtensionsConfig fileExtensionsConfig) - { - var fileExtensions = fileExtensionsConfig.FileExtensions; - if (fileExtensions != null) - { - if (fileExtensions.Where(x => x.Value == ext).Count() != 0) - { - mediaType = mediaTypeItem.Alias; - break; - } - } - } - } - } - - // If media type is still File then let's check if it's an image. - if (mediaType == Constants.Conventions.MediaTypes.File && _imageUrlGenerator.SupportedImageFileTypes.Contains(ext)) - { - mediaType = Constants.Conventions.MediaTypes.Image; - } - } - else - { - mediaType = contentTypeAlias; - } - - var mediaItemName = fileName.ToFriendlyName(); - - var f = _mediaService.CreateMedia(mediaItemName, parentId.Value, mediaType, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); - - - await using (var stream = formFile.OpenReadStream()) - { - f.SetValue(_mediaFileManager, _mediaUrlGenerators, _shortStringHelper, _contentTypeBaseServiceProvider, Constants.Conventions.Media.File, fileName, stream); - } - - - var saveResult = _mediaService.Save(f, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); - if (saveResult == false) - { - AddCancelMessage(tempFiles, _localizedTextService.Localize("speechBubbles", "operationCancelledText") + " -- " + mediaItemName); - } - } - else + if (!_contentSettings.IsFileAllowedForUpload(ext)) { tempFiles.Notifications.Add(new BackOfficeNotification( _localizedTextService.Localize("speechBubbles", "operationFailedHeader"), _localizedTextService.Localize("media", "disallowedFileType"), NotificationStyle.Warning)); + continue; + } + + if (string.IsNullOrEmpty(mediaTypeAlias)) + { + mediaTypeAlias = Constants.Conventions.MediaTypes.File; + + if (contentTypeAlias == Constants.Conventions.MediaTypes.AutoSelect) + { + // Look up MediaTypes + foreach (var mediaTypeItem in allMediaTypes) + { + var fileProperty = mediaTypeItem.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == Constants.Conventions.Media.File); + if (fileProperty == null) + { + continue; + } + + var dataTypeKey = fileProperty.DataTypeKey; + var dataType = _dataTypeService.GetDataType(dataTypeKey); + + if (dataType == null || dataType.Configuration is not IFileExtensionsConfig fileExtensionsConfig) + { + continue; + } + + var fileExtensions = fileExtensionsConfig.FileExtensions; + if (fileExtensions == null || fileExtensions.All(x => x.Value != ext)) + { + continue; + } + + mediaTypeAlias = mediaTypeItem.Alias; + break; + } + + // If media type is still File then let's check if it's an image. + if (mediaTypeAlias == Constants.Conventions.MediaTypes.File && _imageUrlGenerator.SupportedImageFileTypes.Contains(ext)) + { + mediaTypeAlias = Constants.Conventions.MediaTypes.Image; + } + } + else + { + mediaTypeAlias = contentTypeAlias; + } + } + + if (allowedContentTypes.Any(x => x.Alias == mediaTypeAlias) == false) + { + tempFiles.Notifications.Add(new BackOfficeNotification( + _localizedTextService.Localize("speechBubbles", "operationFailedHeader"), + _localizedTextService.Localize("media", "disallowedMediaType", new[] { mediaTypeAlias }), + NotificationStyle.Warning)); + continue; + } + + var mediaItemName = fileName.ToFriendlyName(); + + var createdMediaItem = _mediaService.CreateMedia(mediaItemName, parentId.Value, mediaTypeAlias, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + + await using (var stream = formFile.OpenReadStream()) + { + createdMediaItem.SetValue(_mediaFileManager, _mediaUrlGenerators, _shortStringHelper, _contentTypeBaseServiceProvider, Constants.Conventions.Media.File, fileName, stream); + } + + var saveResult = _mediaService.Save(createdMediaItem, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + if (saveResult == false) + { + AddCancelMessage(tempFiles, _localizedTextService.Localize("speechBubbles", "operationCancelledText") + " -- " + mediaItemName); } } @@ -861,6 +922,29 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return Ok(tempFiles); } + private bool IsFolderCreationAllowedHere(int parentId) + { + var allMediaTypes = _mediaTypeService.GetAll().ToList(); + var isFolderAllowed = false; + if (parentId == Constants.System.Root) + { + var typesAllowedAtRoot = allMediaTypes.Where(ct => ct.AllowedAsRoot).ToList(); + isFolderAllowed = typesAllowedAtRoot.Any(x => x.Alias == Constants.Conventions.MediaTypes.Folder); + } + else + { + var parentMediaType = _mediaService.GetById(parentId); + var mediaFolderType = allMediaTypes.FirstOrDefault(x => x.Alias == parentMediaType.ContentType.Alias); + if (mediaFolderType != null) + { + isFolderAllowed = + mediaFolderType.AllowedContentTypes.Any(x => x.Alias == Constants.Conventions.MediaTypes.Folder); + } + } + + return isFolderAllowed; + } + private IMedia FindInChildren(int mediaId, string nameToFind, string contentTypeAlias) { const int pageSize = 500; diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 350ef69f3f..3d489a038a 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -332,6 +332,7 @@ Click to upload or click here to choose files Cannot upload this file, it does not have an approved file type + Cannot upload this file, the media type with alias '%0%' is not allowed here Cannot upload this file, it does not have a valid file name Max file size is Media root @@ -1507,6 +1508,8 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Insufficient user permissions, could not complete the operation Cancelled Operation was cancelled by a 3rd party add-in + This file is being uploaded as part of a folder, but creating a new folder is not allowed here + Creating a new folder is not allowed here Publishing was cancelled by a 3rd party add-in Property type already exists Property type created diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml index 0c49df09ea..e3f22f90e8 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -335,6 +335,7 @@ Click to upload or click here to choose files Cannot upload this file, it does not have an approved file type + Cannot upload this file, the media type with alias '%0%' is not allowed here Cannot upload this file, it does not have a valid file name Max file size is Media root @@ -1532,6 +1533,8 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Insufficient user permissions, could not complete the operation Cancelled Operation was cancelled by a 3rd party add-in + This file is being uploaded as part of a folder, but creating a new folder is not allowed here + Creating a new folder is not allowed here Property type already exists Property type created DataType: %1%]]>