mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
- Added whitelist site setting for allowed file extensions to upload via media module.
- Hardcoded blacklist: web.config - Superuser immune to whitelist restriction - Zip files still allowed even if not in the list since these are expanded by the media module to allow for multi upload. - Files within a zip must still pass white/black-list test per normal rules (file is skipped if not). --HG-- branch : dev
This commit is contained in:
@@ -3,10 +3,13 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Media.Models;
|
||||
using Orchard.Media.Services;
|
||||
using Orchard.Media.ViewModels;
|
||||
using Orchard.Settings;
|
||||
using Orchard.UI.Notify;
|
||||
using Orchard.Utility.Extensions;
|
||||
|
||||
@@ -153,6 +156,15 @@ namespace Orchard.Media.Controllers {
|
||||
if (!ModelState.IsValid)
|
||||
return View(viewModel);
|
||||
|
||||
// first validate them all
|
||||
foreach (string fileName in Request.Files) {
|
||||
HttpPostedFileBase file = Request.Files[fileName];
|
||||
if (!_mediaService.FileAllowed(file)) {
|
||||
ModelState.AddModelError("File", T("That file type is not allowed.").ToString());
|
||||
return View(viewModel);
|
||||
}
|
||||
}
|
||||
// then save them
|
||||
foreach (string fileName in Request.Files) {
|
||||
HttpPostedFileBase file = Request.Files[fileName];
|
||||
_mediaService.UploadMediaFile(viewModel.MediaPath, file);
|
||||
|
@@ -0,0 +1,15 @@
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.Data;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.Media.Models;
|
||||
|
||||
namespace Orchard.Media.Handlers {
|
||||
[UsedImplicitly]
|
||||
public class MediaSettingsPartHandler : ContentHandler {
|
||||
public MediaSettingsPartHandler(IRepository<MediaSettingsPartRecord> repository) {
|
||||
Filters.Add(new ActivatingFilter<MediaSettingsPart>("Site"));
|
||||
Filters.Add(StorageFilter.For(repository));
|
||||
Filters.Add(new TemplateFilterForRecord<MediaSettingsPartRecord>("MediaSettings", "Parts/Media.MediaSettings"));
|
||||
}
|
||||
}
|
||||
}
|
16
src/Orchard.Web/Modules/Orchard.Media/Migrations.cs
Normal file
16
src/Orchard.Web/Modules/Orchard.Media/Migrations.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Orchard.Data.Migration;
|
||||
using Orchard.Media.Models;
|
||||
|
||||
namespace Orchard.Media {
|
||||
public class MediaDataMigration : DataMigrationImpl {
|
||||
public int Create() {
|
||||
SchemaBuilder.CreateTable("MediaSettingsPartRecord",
|
||||
table => table
|
||||
.ContentPartRecord()
|
||||
.Column<string>("UploadAllowedFileTypeWhitelist", c => c.WithDefault(MediaSettingsPartRecord.DefaultWhitelist).WithLength(255))
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
using Orchard.ContentManagement;
|
||||
using System;
|
||||
|
||||
namespace Orchard.Media.Models {
|
||||
public class MediaSettingsPart : ContentPart<MediaSettingsPartRecord> {
|
||||
public string UploadAllowedFileTypeWhitelist {
|
||||
get { return Record.UploadAllowedFileTypeWhitelist; }
|
||||
set { Record.UploadAllowedFileTypeWhitelist = value; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
using System.Net.Mail;
|
||||
using Orchard.ContentManagement.Records;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Orchard.Media.Models {
|
||||
public class MediaSettingsPartRecord : ContentPartRecord {
|
||||
internal const string DefaultWhitelist = "jpg jpeg gif png txt doc docx xls xlsx pdf ppt pptx pps ppsx odt ods odp";
|
||||
private string _whitelist = DefaultWhitelist;
|
||||
|
||||
public virtual string UploadAllowedFileTypeWhitelist {
|
||||
get { return _whitelist; }
|
||||
set { _whitelist = value; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -71,6 +71,10 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="AdminMenu.cs" />
|
||||
<Compile Include="Controllers\AdminController.cs" />
|
||||
<Compile Include="Handlers\MediaSettingsPartHandler.cs" />
|
||||
<Compile Include="Migrations.cs" />
|
||||
<Compile Include="Models\MediaSettingsPart.cs" />
|
||||
<Compile Include="Models\MediaSettingsPartRecord.cs" />
|
||||
<Compile Include="ResourceManifest.cs" />
|
||||
<Compile Include="Helpers\MediaHelpers.cs" />
|
||||
<Compile Include="Permissions.cs" />
|
||||
@@ -113,6 +117,9 @@
|
||||
<Content Include="Views\Admin\EditProperties.cshtml" />
|
||||
<Content Include="Views\Admin\Index.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\EditorTemplates\Parts\Media.MediaSettings.cshtml" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
@@ -13,5 +13,6 @@ namespace Orchard.Media.Services {
|
||||
void DeleteFile(string name, string folderName);
|
||||
void RenameFile(string name, string newName, string folderName);
|
||||
string UploadMediaFile(string folderName, HttpPostedFileBase postedFile);
|
||||
bool FileAllowed(HttpPostedFileBase postedFile);
|
||||
}
|
||||
}
|
@@ -5,9 +5,12 @@ using System.Text;
|
||||
using System.Web;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.FileSystems.Media;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Media.Models;
|
||||
using Orchard.Security;
|
||||
using Orchard.Settings;
|
||||
|
||||
namespace Orchard.Media.Services {
|
||||
[UsedImplicitly]
|
||||
@@ -21,6 +24,9 @@ namespace Orchard.Media.Services {
|
||||
}
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
protected virtual ISite CurrentSite { get; [UsedImplicitly] private set; }
|
||||
protected virtual IUser CurrentUser { get; [UsedImplicitly] private set; }
|
||||
|
||||
|
||||
public string GetPublicUrl(string path) {
|
||||
return _storageProvider.GetPublicUrl(path);
|
||||
@@ -82,18 +88,18 @@ namespace Orchard.Media.Services {
|
||||
}
|
||||
|
||||
public void RenameFile(string name, string newName, string folderName) {
|
||||
_storageProvider.RenameFile(folderName + "\\" + name, folderName + "\\" + newName);
|
||||
if (FileAllowed(newName, false)) {
|
||||
_storageProvider.RenameFile(folderName + "\\" + name, folderName + "\\" + newName);
|
||||
}
|
||||
}
|
||||
|
||||
public string UploadMediaFile(string folderName, HttpPostedFileBase postedFile) {
|
||||
|
||||
if (postedFile.FileName.EndsWith(".zip")) {
|
||||
UnzipMediaFileArchive(folderName, postedFile);
|
||||
// Don't save the zip file.
|
||||
return _storageProvider.GetPublicUrl(folderName);
|
||||
}
|
||||
|
||||
if (postedFile.ContentLength > 0) {
|
||||
if (FileAllowed(postedFile) && postedFile.ContentLength > 0) {
|
||||
var filePath = Path.Combine(folderName, Path.GetFileName(postedFile.FileName));
|
||||
var inputStream = postedFile.InputStream;
|
||||
|
||||
@@ -104,6 +110,41 @@ namespace Orchard.Media.Services {
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool FileAllowed(string name, bool allowZip) {
|
||||
if (string.IsNullOrWhiteSpace(name)) {
|
||||
return false;
|
||||
}
|
||||
var mediaSettings = CurrentSite.As<MediaSettingsPart>();
|
||||
var allowedExtensions = mediaSettings.UploadAllowedFileTypeWhitelist.ToLowerInvariant().Split(' ');
|
||||
var ext = (Path.GetExtension(name) ?? "").TrimStart('.').ToLowerInvariant();
|
||||
if (string.IsNullOrWhiteSpace(ext)) {
|
||||
return false;
|
||||
}
|
||||
// whitelist does not apply to the superuser
|
||||
if (CurrentUser == null || !CurrentSite.SuperUser.Equals(CurrentUser.UserName, StringComparison.Ordinal)) {
|
||||
// zip files at the top level are allowed since this is how you upload multiple files at once.
|
||||
if (allowZip && ext.Equals("zip", StringComparison.OrdinalIgnoreCase)) {
|
||||
return true;
|
||||
}
|
||||
// must be in the whitelist
|
||||
if (Array.IndexOf(allowedExtensions, ext) == -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// blacklist always applies
|
||||
if (string.Equals(name.Trim(), "web.config", StringComparison.OrdinalIgnoreCase)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool FileAllowed(HttpPostedFileBase postedFile) {
|
||||
if (postedFile == null) {
|
||||
return false;
|
||||
}
|
||||
return FileAllowed(postedFile.FileName, true);
|
||||
}
|
||||
|
||||
private void SaveStream(string filePath, Stream inputStream) {
|
||||
var file = _storageProvider.CreateFile(filePath);
|
||||
var outputStream = file.OpenWrite();
|
||||
@@ -139,12 +180,17 @@ namespace Orchard.Media.Services {
|
||||
var entryName = Path.Combine(targetFolder, entry.Name);
|
||||
var directoryName = Path.GetDirectoryName(entryName);
|
||||
|
||||
try { _storageProvider.CreateFolder(directoryName); }
|
||||
catch {
|
||||
// no handling needed - this is to force the folder to exist if it doesn't
|
||||
}
|
||||
// skip disallowed files
|
||||
if (FileAllowed(entry.Name, false)) {
|
||||
try {
|
||||
_storageProvider.CreateFolder(directoryName);
|
||||
}
|
||||
catch {
|
||||
// no handling needed - this is to force the folder to exist if it doesn't
|
||||
}
|
||||
|
||||
SaveStream(entryName, fileInflater);
|
||||
SaveStream(entryName, fileInflater);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,6 @@
|
||||
namespace Orchard.Media.ViewModels {
|
||||
using Orchard.Media.Models;
|
||||
|
||||
namespace Orchard.Media.ViewModels {
|
||||
public class MediaItemAddViewModel {
|
||||
public string FolderName { get; set; }
|
||||
public string MediaPath { get; set; }
|
||||
|
@@ -0,0 +1,9 @@
|
||||
@model Orchard.Media.Models.MediaSettingsPartRecord
|
||||
|
||||
<fieldset>
|
||||
<legend>@T("Media Settings")</legend>
|
||||
<div>
|
||||
@Html.LabelFor(m => m.UploadAllowedFileTypeWhitelist, T("Upload allowed file types (list of extensions separated by spaces)"))
|
||||
@Html.TextBoxFor(m => m.UploadAllowedFileTypeWhitelist, new { @class = "textMedium" })
|
||||
</div>
|
||||
</fieldset>
|
Reference in New Issue
Block a user