From 8a1e83ff7f60f3342101137c55e917bf286918e9 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Fri, 15 May 2020 17:43:45 -0400 Subject: [PATCH 1/3] Modified the package installer to use target folders ( based on the Nuget specification ) rather than file extensions --- .../External/Package/[Owner].[Module]s.nuspec | 1 + .../Templates/External/content/resources.txt | 1 + .../Templates/External/wwwroot/resources.txt | 2 +- .../Infrastructure/InstallationManager.cs | 38 ++++++++++--------- Oqtane.Server/Startup.cs | 2 +- Oqtane.Server/wwwroot/resources.txt | 1 + 6 files changed, 25 insertions(+), 20 deletions(-) create mode 100644 Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/content/resources.txt create mode 100644 Oqtane.Server/wwwroot/resources.txt diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.nuspec b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.nuspec index 57071160..64c81a8d 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.nuspec +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/Package/[Owner].[Module]s.nuspec @@ -27,5 +27,6 @@ + \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/content/resources.txt b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/content/resources.txt new file mode 100644 index 00000000..ac84ee50 --- /dev/null +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/content/resources.txt @@ -0,0 +1 @@ +This is the location where static resources for third party libraries should be located ( the third party library assemblies will be included in the /lib folder ). They should be placed in subfolders which match the naming convention of the third party library. When the module package is deployed the static resource subfolders will be extracted under the web root. \ No newline at end of file diff --git a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/wwwroot/resources.txt b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/wwwroot/resources.txt index 2542de03..91780f80 100644 --- a/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/wwwroot/resources.txt +++ b/Oqtane.Client/Modules/Admin/ModuleCreator/Templates/External/wwwroot/resources.txt @@ -1 +1 @@ -This is the location where static resources such as images or style sheets should be located \ No newline at end of file +This is the location where static resources such as images or style sheets for this module will be located. Static assets can be organized in subfolders. When the module package is deployed the assets will be extracted under the web root in a folder that matches the module name. \ No newline at end of file diff --git a/Oqtane.Server/Infrastructure/InstallationManager.cs b/Oqtane.Server/Infrastructure/InstallationManager.cs index b1f73543..adce62e9 100644 --- a/Oqtane.Server/Infrastructure/InstallationManager.cs +++ b/Oqtane.Server/Infrastructure/InstallationManager.cs @@ -28,7 +28,7 @@ namespace Oqtane.Infrastructure { var webRootPath = _environment.WebRootPath; - var install = UnpackPackages(folders, webRootPath); + var install = InstallPackages(folders, webRootPath); if (install && restart) { @@ -36,7 +36,7 @@ namespace Oqtane.Infrastructure } } - public static bool UnpackPackages(string folders, string webRootPath) + public static bool InstallPackages(string folders, string webRootPath) { bool install = false; string binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); @@ -44,14 +44,12 @@ namespace Oqtane.Infrastructure foreach (string folder in folders.Split(',')) { string sourceFolder = Path.Combine(webRootPath, folder); - - // create folder if it does not exist if (!Directory.Exists(sourceFolder)) { Directory.CreateDirectory(sourceFolder); } - // iterate through packages + // iterate through Nuget packages in source folder foreach (string packagename in Directory.GetFiles(sourceFolder, "*.nupkg")) { string name = Path.GetFileNameWithoutExtension(packagename); @@ -89,29 +87,33 @@ namespace Oqtane.Infrastructure // deploy to appropriate locations foreach (ZipArchiveEntry entry in archive.Entries) { + string foldername = Path.GetDirectoryName(entry.FullName).Split('\\')[0]; string filename = Path.GetFileName(entry.FullName); - switch (Path.GetExtension(filename).ToLower()) + + switch (foldername) { - case ".pdb": - case ".dll": + case "lib": if (binFolder != null) entry.ExtractToFile(Path.Combine(binFolder, filename), true); break; - case ".png": - case ".jpg": - case ".jpeg": - case ".gif": - case ".svg": - case ".js": - case ".css": - string entryPath = Utilities.PathCombine(entry.FullName.Replace("wwwroot", name).Split('/')); - filename = Path.Combine(sourceFolder, entryPath); + case "wwwroot": + filename = Path.Combine(sourceFolder, Utilities.PathCombine(entry.FullName.Replace("wwwroot", name).Split('/'))); if (!Directory.Exists(Path.GetDirectoryName(filename))) { Directory.CreateDirectory(Path.GetDirectoryName(filename)); } - entry.ExtractToFile(filename, true); break; + case "content": + if (Path.GetDirectoryName(entry.FullName) != "content") // assets must be in subfolders + { + filename = Path.Combine(webRootPath, Utilities.PathCombine(entry.FullName.Replace("content", "").Split('/'))); + if (!Directory.Exists(Path.GetDirectoryName(filename))) + { + Directory.CreateDirectory(Path.GetDirectoryName(filename)); + } + entry.ExtractToFile(filename, true); + } + break; } } } diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 34f73ffe..b47cdd4f 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -163,7 +163,7 @@ namespace Oqtane services.AddSingleton(); // install any modules or themes ( this needs to occur BEFORE the assemblies are loaded into the app domain ) - InstallationManager.UnpackPackages("Modules,Themes", _webRoot); + InstallationManager.InstallPackages("Modules,Themes", _webRoot); // register transient scoped core services services.AddTransient(); diff --git a/Oqtane.Server/wwwroot/resources.txt b/Oqtane.Server/wwwroot/resources.txt new file mode 100644 index 00000000..2542de03 --- /dev/null +++ b/Oqtane.Server/wwwroot/resources.txt @@ -0,0 +1 @@ +This is the location where static resources such as images or style sheets should be located \ No newline at end of file From 13adebb36c81930e3c965181ec119c4878e00497 Mon Sep 17 00:00:00 2001 From: Jim Spillane Date: Fri, 15 May 2020 23:12:24 -0400 Subject: [PATCH 2/3] Add File Name validation Apply file name validation rules to the File Controller and client. --- Oqtane.Client/Modules/Admin/Files/Add.razor | 35 +++-- Oqtane.Server/Controllers/FileController.cs | 147 +++++++++++--------- 2 files changed, 110 insertions(+), 72 deletions(-) diff --git a/Oqtane.Client/Modules/Admin/Files/Add.razor b/Oqtane.Client/Modules/Admin/Files/Add.razor index 653f5cf3..9f19370d 100644 --- a/Oqtane.Client/Modules/Admin/Files/Add.razor +++ b/Oqtane.Client/Modules/Admin/Files/Add.razor @@ -1,4 +1,5 @@ @namespace Oqtane.Modules.Admin.Files +@using System.IO @inherits ModuleBase @inject NavigationManager NavigationManager @inject IFileService FileService @@ -70,18 +71,32 @@ private async Task Download() { + if (url == string.Empty || _folderId == -1) + { + AddModuleMessage("You Must Enter A Url And Select A Folder", MessageType.Warning); + return; + } + + var filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1); + + if (!Constants.UploadableFiles.Split(',') + .Contains(Path.GetExtension(filename).ToLower().Replace(".", ""))) + { + AddModuleMessage("File Could Not Be Downloaded From Url Due To Its File Extension", MessageType.Warning); + return ; + } + + if (!filename.IsPathOrFileValid()) + { + AddModuleMessage("You Must Enter A Url With A Valid File Name", MessageType.Warning); + return; + } + try { - if (url != string.Empty && _folderId != -1) - { - await FileService.UploadFileAsync(url, _folderId); - await logger.LogInformation("File Downloaded Successfully From Url {Url}", url); - AddModuleMessage("File Downloaded Successfully From Url", MessageType.Success); - } - else - { - AddModuleMessage("You Must Enter A Url And Select A Folder", MessageType.Warning); - } + await FileService.UploadFileAsync(url, _folderId); + await logger.LogInformation("File Downloaded Successfully From Url {Url}", url); + AddModuleMessage("File Downloaded Successfully From Url", MessageType.Success); } catch (Exception ex) { diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index 0bfe1de2..0c4bfad6 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -189,41 +189,54 @@ namespace Oqtane.Controllers { Models.File file = null; Folder folder = _folders.GetFolder(int.Parse(folderid)); - if (folder != null && _userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.Permissions)) - { - string folderPath = GetFolderPath(folder); - CreateDirectory(folderPath); - string filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1); - // check for allowable file extensions - if (Constants.UploadableFiles.Split(',').Contains(Path.GetExtension(filename).ToLower().Replace(".", ""))) - { - try - { - var client = new WebClient(); - string targetPath = Path.Combine(folderPath, filename); - // remove file if it already exists - if (System.IO.File.Exists(targetPath)) - { - System.IO.File.Delete(targetPath); - } - client.DownloadFile(url, targetPath); - _files.AddFile(CreateFile(filename, folder.FolderId, targetPath)); - } - catch - { - _logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url {Url}", url); - } - } - else - { - _logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url Due To Its File Extension {Url}", url); - } - } - else + if (folder == null || !_userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.Permissions)) { - _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Download File {Url} {FolderId}", url, folderid); + _logger.Log(LogLevel.Error, this, LogFunction.Create, + "User Not Authorized To Download File {Url} {FolderId}", url, folderid); HttpContext.Response.StatusCode = 401; + return file; + } + + string folderPath = GetFolderPath(folder); + CreateDirectory(folderPath); + + string filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1); + // check for allowable file extensions + if (!Constants.UploadableFiles.Split(',') + .Contains(Path.GetExtension(filename).ToLower().Replace(".", ""))) + { + _logger.Log(LogLevel.Error, this, LogFunction.Create, + "File Could Not Be Downloaded From Url Due To Its File Extension {Url}", url); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict; + return file; + } + + if (!filename.IsPathOrFileValid()) + { + _logger.Log(LogLevel.Error, this, LogFunction.Create, + $"File Could Not Be Downloaded From Url Due To Its File Name Not Allowed {url}"); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict; + return file; + } + + try + { + var client = new WebClient(); + string targetPath = Path.Combine(folderPath, filename); + // remove file if it already exists + if (System.IO.File.Exists(targetPath)) + { + System.IO.File.Delete(targetPath); + } + + client.DownloadFile(url, targetPath); + file = _files.AddFile(CreateFile(filename, folder.FolderId, targetPath)); + } + catch + { + _logger.Log(LogLevel.Error, this, LogFunction.Create, + "File Could Not Be Downloaded From Url {Url}", url); } return file; @@ -233,46 +246,56 @@ namespace Oqtane.Controllers [HttpPost("upload")] public async Task UploadFile(string folder, IFormFile file) { - if (file.Length > 0) + if (file.Length <= 0) { - string folderPath = ""; + return; + } - if (int.TryParse(folder, out int folderId)) + if (!file.FileName.IsPathOrFileValid()) + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict; + return; + } + + string folderPath = ""; + + if (int.TryParse(folder, out int folderId)) + { + Folder virtualFolder = _folders.GetFolder(folderId); + if (virtualFolder != null && + _userPermissions.IsAuthorized(User, PermissionNames.Edit, virtualFolder.Permissions)) { - Folder virtualFolder = _folders.GetFolder(folderId); - if (virtualFolder != null && _userPermissions.IsAuthorized(User, PermissionNames.Edit, virtualFolder.Permissions)) - { - folderPath = GetFolderPath(virtualFolder); - } + folderPath = GetFolderPath(virtualFolder); } - else + } + else + { + if (User.IsInRole(Constants.HostRole)) { - if (User.IsInRole(Constants.HostRole)) - { - folderPath = GetFolderPath(folder); - } + folderPath = GetFolderPath(folder); + } + } + + if (folderPath != "") + { + CreateDirectory(folderPath); + using (var stream = new FileStream(Path.Combine(folderPath, file.FileName), FileMode.Create)) + { + await file.CopyToAsync(stream); } - if (folderPath != "") + string upload = await MergeFile(folderPath, file.FileName); + if (upload != "" && folderId != -1) { - CreateDirectory(folderPath); - using (var stream = new FileStream(Path.Combine(folderPath, file.FileName), FileMode.Create)) - { - await file.CopyToAsync(stream); - } - - string upload = await MergeFile(folderPath, file.FileName); - if (upload != "" && folderId != -1) - { - _files.AddFile(CreateFile(upload, folderId, Path.Combine(folderPath, upload))); - } - } - else - { - _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Upload File {Folder} {File}", folder, file); - HttpContext.Response.StatusCode = 401; + _files.AddFile(CreateFile(upload, folderId, Path.Combine(folderPath, upload))); } } + else + { + _logger.Log(LogLevel.Error, this, LogFunction.Create, + "User Not Authorized To Upload File {Folder} {File}", folder, file); + HttpContext.Response.StatusCode = 401; + } } private async Task MergeFile(string folder, string filename) From 96f5668a3bf6f9278e4f0c174db010a990f06804 Mon Sep 17 00:00:00 2001 From: Pavel Vesely Date: Sat, 16 May 2020 08:40:30 +0200 Subject: [PATCH 3/3] Setting service bug. --- Oqtane.Client/Services/SettingService.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Oqtane.Client/Services/SettingService.cs b/Oqtane.Client/Services/SettingService.cs index c5cf45c4..451e97b8 100644 --- a/Oqtane.Client/Services/SettingService.cs +++ b/Oqtane.Client/Services/SettingService.cs @@ -1,4 +1,5 @@ -using Oqtane.Models; +using System; +using Oqtane.Models; using System.Threading.Tasks; using System.Net.Http; using System.Linq; @@ -103,10 +104,10 @@ namespace Oqtane.Services public async Task UpdateSettingsAsync(Dictionary settings, string entityName, int entityId) { var settingsList = await GetJsonAsync>($"{Apiurl}?entityname={entityName}&entityid={entityId}"); - + foreach (KeyValuePair kvp in settings) { - Setting setting = settingsList.FirstOrDefault(item => item.SettingName == kvp.Key); + Setting setting = settingsList.FirstOrDefault(item => item.SettingName.Equals(kvp.Key,StringComparison.OrdinalIgnoreCase)); if (setting == null) { setting = new Setting();