diff --git a/Oqtane.Client/Modules/Admin/Files/Edit.razor b/Oqtane.Client/Modules/Admin/Files/Edit.razor index 0083cce0..1d7ddc81 100644 --- a/Oqtane.Client/Modules/Admin/Files/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Files/Edit.razor @@ -149,10 +149,16 @@ { folder = await FolderService.AddFolderAsync(folder); } - - await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId); - await logger.LogInformation("Folder Saved {Folder}", folder); - NavigationManager.NavigateTo(NavigateUrl()); + if (folder != null) + { + await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId); + await logger.LogInformation("Folder Saved {Folder}", folder); + NavigationManager.NavigateTo(NavigateUrl()); + } + else + { + AddModuleMessage("An Error Was Encountered Saving The Folder", MessageType.Error); + } } else { diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index 5a09d5a5..0c30bbdf 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -16,6 +16,7 @@ using System.Net; using Oqtane.Enums; using Oqtane.Infrastructure; using Oqtane.Repository; +using Microsoft.AspNetCore.Routing.Constraints; // ReSharper disable StringIndexOfIsCultureSpecific.1 @@ -396,12 +397,13 @@ namespace Oqtane.Controllers [HttpGet("download/{id}")] public IActionResult Download(int id) { + string errorpath = Path.Combine(GetFolderPath("images"), "error.png"); Models.File file = _files.GetFile(id); if (file != null) { if (_userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions)) { - string filepath = Path.Combine(GetFolderPath(file.Folder) , file.Name); + string filepath = Path.Combine(GetFolderPath(file.Folder), file.Name); if (System.IO.File.Exists(filepath)) { byte[] filebytes = System.IO.File.ReadAllBytes(filepath); @@ -411,21 +413,24 @@ namespace Oqtane.Controllers { _logger.Log(LogLevel.Error, this, LogFunction.Read, "File Does Not Exist {FileId} {FilePath}", id, filepath); HttpContext.Response.StatusCode = 404; - return null; + byte[] filebytes = System.IO.File.ReadAllBytes(errorpath); + return File(filebytes, "application/octet-stream", file.Name); } } else { _logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access File {FileId}", id); HttpContext.Response.StatusCode = 401; - return null; + byte[] filebytes = System.IO.File.ReadAllBytes(errorpath); + return File(filebytes, "application/octet-stream", file.Name); } } else { _logger.Log(LogLevel.Error, this, LogFunction.Read, "File Not Found {FileId}", id); HttpContext.Response.StatusCode = 404; - return null; + byte[] filebytes = System.IO.File.ReadAllBytes(errorpath); + return File(filebytes, "application/octet-stream", "error.png"); } } diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index 68a01e23..34c13a1b 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -10,7 +10,6 @@ using Oqtane.Extensions; using Oqtane.Infrastructure; using Oqtane.Repository; using Oqtane.Security; -using System.IO; namespace Oqtane.Controllers { @@ -106,13 +105,23 @@ namespace Oqtane.Controllers } if (_userPermissions.IsAuthorized(User,PermissionNames.Edit, permissions)) { - if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) + if (FolderPathValid(folder)) { - Folder parent = _folders.GetFolder(folder.ParentId.Value); - folder.Path = Utilities.PathCombine(parent.Path, folder.Name,"\\"); + if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) + { + Folder parent = _folders.GetFolder(folder.ParentId.Value); + folder.Path = Utilities.PathCombine(parent.Path, folder.Name); + } + folder.Path = Utilities.PathCombine(folder.Path, "\\"); + folder = _folders.AddFolder(folder); + _logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder); + } + else + { + _logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Name Not Valid {Folder}", folder); + HttpContext.Response.StatusCode = 401; + folder = null; } - folder = _folders.AddFolder(folder); - _logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder); } else { @@ -131,13 +140,23 @@ namespace Oqtane.Controllers { if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Folder, folder.FolderId, PermissionNames.Edit)) { - if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) + if (FolderPathValid(folder)) { - Folder parent = _folders.GetFolder(folder.ParentId.Value); - folder.Path = Utilities.PathCombine(parent.Path, folder.Name,"\\"); + if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) + { + Folder parent = _folders.GetFolder(folder.ParentId.Value); + folder.Path = Utilities.PathCombine(parent.Path, folder.Name); + } + folder.Path = Utilities.PathCombine(folder.Path, "\\"); + folder = _folders.UpdateFolder(folder); + _logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder); + } + else + { + _logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Name Not Valid {Folder}", folder); + HttpContext.Response.StatusCode = 401; + folder = null; } - folder = _folders.UpdateFolder(folder); - _logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder); } else { @@ -191,5 +210,11 @@ namespace Oqtane.Controllers HttpContext.Response.StatusCode = 401; } } + + private bool FolderPathValid(Folder folder) + { + // prevent folder path traversal and reserved devices + return (!folder.Name.Contains("\\") && !folder.Name.Contains("/") && !Constants.ReservedDevices.Split(',').Contains(folder.Name.ToUpper())); + } } } diff --git a/Oqtane.Server/Controllers/SiteTemplateController.cs b/Oqtane.Server/Controllers/SiteTemplateController.cs index 2f709fb1..c63170c1 100644 --- a/Oqtane.Server/Controllers/SiteTemplateController.cs +++ b/Oqtane.Server/Controllers/SiteTemplateController.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Oqtane.Models; using Oqtane.Repository; +using Oqtane.Shared; namespace Oqtane.Controllers { @@ -17,6 +19,7 @@ namespace Oqtane.Controllers // GET: api/ [HttpGet] + [Authorize(Roles = Constants.HostRole)] public IEnumerable Get() { return _siteTemplates.GetSiteTemplates(); diff --git a/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css b/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css index a9e5c15e..fba5b909 100644 --- a/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css +++ b/Oqtane.Server/wwwroot/Themes/Oqtane.Themes.BlazorTheme/Theme.css @@ -101,6 +101,12 @@ flex-direction: row; } + .app-logo { + display: block; + margin-left: auto; + margin-right: auto; + } + .breadcrumbs { position: fixed; left: 275px; @@ -163,7 +169,13 @@ } } -@media (max-width: 767px) { +@media (max-width: 767px) { + .app-logo { + height: 80px; + display: flex; + align-items: center; + } + .breadcrumbs { position: fixed; top: 150px; diff --git a/Oqtane.Server/wwwroot/images/error.png b/Oqtane.Server/wwwroot/images/error.png new file mode 100644 index 00000000..0095d2f1 Binary files /dev/null and b/Oqtane.Server/wwwroot/images/error.png differ diff --git a/Oqtane.Server/wwwroot/js/interop.js b/Oqtane.Server/wwwroot/js/interop.js index 50c8aa9b..5f41709c 100644 --- a/Oqtane.Server/wwwroot/js/interop.js +++ b/Oqtane.Server/wwwroot/js/interop.js @@ -81,14 +81,26 @@ window.interop = { if (link.href !== url) { link.setAttribute('href', url); } - if (type !== "" && link.type !== type) { - link.setAttribute('type', type); + if (type !== "") { + if (link.type !== type) { + link.setAttribute('type', type); + } + } else { + link.removeAttribute('type'); } - if (integrity !== "" && link.integrity !== integrity) { - link.setAttribute('integrity', integrity); + if (integrity !== "") { + if (link.integrity !== integrity) { + link.setAttribute('integrity', integrity); + } + } else { + link.removeAttribute('integrity'); } - if (crossorigin !== "" && link.crossOrigin !== crossorigin) { - link.setAttribute('crossorigin', crossorigin); + if (crossorigin !== "") { + if (link.crossOrigin !== crossorigin) { + link.setAttribute('crossorigin', crossorigin); + } + } else { + link.removeAttribute('crossorigin'); } } }, @@ -126,11 +138,19 @@ window.interop = { if (script.src !== src) { script.src = src; } - if (integrity !== "" && script.integrity !== integrity) { - script.setAttribute('integrity', integrity); + if (integrity !== "") { + if (script.integrity !== integrity) { + script.setAttribute('integrity', integrity); + } + } else { + script.removeAttribute('integrity'); } - if (crossorigin !== "" && script.crossorigin !== crossorigin) { - script.setAttribute('crossorigin', crossorigin); + if (crossorigin !== "") { + if (script.crossOrigin !== crossorigin) { + script.setAttribute('crossorigin', crossorigin); + } + } else { + script.removeAttribute('crossorigin'); } } else { diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index c50347e2..30ca2950 100644 --- a/Oqtane.Shared/Shared/Constants.cs +++ b/Oqtane.Shared/Shared/Constants.cs @@ -43,5 +43,6 @@ public const string ImageFiles = "jpg,jpeg,jpe,gif,bmp,png"; public const string UploadableFiles = "jpg,jpeg,jpe,gif,bmp,png,mov,wmv,avi,mp4,mp3,doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,nupkg"; + public const string ReservedDevices = "CON,NUL,PRN,COM1,COM2,COM3,COM4,COM5,COM6,COM7,COM8,COM9,LPT1,LPT2,LPT3,LPT4,LPT5,LPT6,LPT7,LPT8,LPT9"; } }