From 6b5057b8708eb82f2af278225e3e0075dbb7f2b4 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 8 Sep 2015 17:48:26 +0200 Subject: [PATCH] U4-7048 - more filesystems cleanup --- src/Umbraco.Core/IO/IOHelper.cs | 27 +++- src/Umbraco.Core/IO/PhysicalFileSystem.cs | 19 +-- .../Repositories/FileRepository.cs | 4 - .../Interfaces/IPartialViewRepository.cs | 1 + .../Repositories/PartialViewRepository.cs | 45 ++++-- .../Repositories/ScriptRepository.cs | 31 +--- .../Repositories/StylesheetRepository.cs | 14 +- .../Repositories/TemplateRepository.cs | 72 +++++++-- src/Umbraco.Core/Services/FileService.cs | 16 +- src/Umbraco.Core/Services/IFileService.cs | 1 + .../Repositories/ScriptRepositoryTest.cs | 32 ++++ .../umbraco/settings/views/EditView.aspx | 1 + .../umbraco/settings/views/EditView.aspx.cs | 25 ++- .../settings/views/EditView.aspx.designer.cs | 9 ++ .../umbraco_client/Editors/EditScript.js | 30 +++- .../umbraco_client/Editors/EditView.js | 22 +-- .../Trees/PartialViewMacrosTree.cs | 2 +- src/Umbraco.Web/Trees/PartialViewsTree.cs | 2 +- .../WebServices/SaveFileController.cs | 145 +++++++----------- 19 files changed, 287 insertions(+), 211 deletions(-) diff --git a/src/Umbraco.Core/IO/IOHelper.cs b/src/Umbraco.Core/IO/IOHelper.cs index 8378081394..286acf0285 100644 --- a/src/Umbraco.Core/IO/IOHelper.cs +++ b/src/Umbraco.Core/IO/IOHelper.cs @@ -177,12 +177,23 @@ namespace Umbraco.Core.IO /// A value indicating whether the filepath is valid. internal static bool VerifyEditPath(string filePath, IEnumerable validDirs) { + // this is called from ScriptRepository, PartialViewRepository, etc. + // filePath is the fullPath (rooted, filesystem path, can be trusted) + // validDirs are virtual paths (eg ~/Views) + // + // except that for templates, filePath actually is a virtual path + + //TODO + // what's below is dirty, there are too many ways to get the root dir, etc. + // not going to fix everything today + var mappedRoot = MapPath(SystemDirectories.Root); if (filePath.StartsWith(mappedRoot) == false) filePath = MapPath(filePath); - // don't trust what we get, it may contain relative segments - filePath = Path.GetFullPath(filePath); + // yes we can (see above) + //// don't trust what we get, it may contain relative segments + //filePath = Path.GetFullPath(filePath); foreach (var dir in validDirs) { @@ -190,7 +201,7 @@ namespace Umbraco.Core.IO if (validDir.StartsWith(mappedRoot) == false) validDir = MapPath(validDir); - if (filePath.StartsWith(validDir)) + if (PathStartsWith(filePath, validDir, Path.DirectorySeparatorChar)) return true; } @@ -237,6 +248,16 @@ namespace Umbraco.Core.IO return true; } + public static bool PathStartsWith(string path, string root, char separator) + { + // either it is identical to root, + // or it is root + separator + anything + + if (path.StartsWith(root, StringComparison.OrdinalIgnoreCase) == false) return false; + if (path.Length == root.Length) return true; + if (path.Length < root.Length) return false; + return path[root.Length] == separator; + } /// /// Returns the path to the root of the application, by getting the path to where the assembly where this diff --git a/src/Umbraco.Core/IO/PhysicalFileSystem.cs b/src/Umbraco.Core/IO/PhysicalFileSystem.cs index 79b453e9cb..b801f05b8f 100644 --- a/src/Umbraco.Core/IO/PhysicalFileSystem.cs +++ b/src/Umbraco.Core/IO/PhysicalFileSystem.cs @@ -186,14 +186,14 @@ namespace Umbraco.Core.IO { // test url var path = EnsureUrlSeparatorChar(fullPathOrUrl); - if (PathStartsWith(path, _rootUrl, '/')) + if (IOHelper.PathStartsWith(path, _rootUrl, '/')) return path.Substring(_rootUrl.Length) .Replace('/', Path.DirectorySeparatorChar) .TrimStart(Path.DirectorySeparatorChar); // test path path = EnsureDirectorySeparatorChar(fullPathOrUrl); - if (PathStartsWith(path, _rootPath, Path.DirectorySeparatorChar)) + if (IOHelper.PathStartsWith(path, _rootPath, Path.DirectorySeparatorChar)) return path.Substring(_rootPath.Length) .TrimStart(Path.DirectorySeparatorChar); @@ -231,7 +231,7 @@ namespace Umbraco.Core.IO path = GetRelativePath(path); // if already a full path, return - if (PathStartsWith(path, _rootPath, Path.DirectorySeparatorChar)) + if (IOHelper.PathStartsWith(path, _rootPath, Path.DirectorySeparatorChar)) return path; // else combine and sanitize, ie GetFullPath will take care of any relative @@ -243,23 +243,12 @@ namespace Umbraco.Core.IO // at that point, path is within legal parts of the filesystem, ie we have // permissions to reach that path, but it may nevertheless be outside of // our root path, due to relative segments, so better check - if (PathStartsWith(fpath, _rootPath, Path.DirectorySeparatorChar)) + if (IOHelper.PathStartsWith(fpath, _rootPath, Path.DirectorySeparatorChar)) return fpath; throw new FileSecurityException("File '" + opath + "' is outside this filesystem's root."); } - private static bool PathStartsWith(string path, string root, char separator) - { - // either it is identical to root, - // or it is root + separator + anything - - if (path.StartsWith(root, StringComparison.OrdinalIgnoreCase) == false) return false; - if (path.Length == root.Length) return true; - if (path.Length < root.Length) return false; - return path[root.Length] == separator; - } - public string GetUrl(string path) { path = EnsureUrlSeparatorChar(path).Trim('/'); diff --git a/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs b/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs index 04ac5b6f62..e1e4c3105a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs @@ -147,10 +147,6 @@ namespace Umbraco.Core.Persistence.Repositories protected virtual void PersistUpdatedItem(TEntity entity) { - //TODO: A big problem here is if the entities 'Path' changes, if that is the case then - // we'd need to rename the underlying file, BUT how would we do this since we aren't storing an - // original path property. - using (var stream = GetContentStream(entity.Content)) { FileSystem.AddFile(entity.Path, stream, true); diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IPartialViewRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IPartialViewRepository.cs index 8ba28eeaee..3fbcdd3283 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IPartialViewRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IPartialViewRepository.cs @@ -6,5 +6,6 @@ namespace Umbraco.Core.Persistence.Repositories { void AddFolder(string folderPath); void DeleteFolder(string folderPath); + bool ValidatePartialView(IPartialView partialView); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/PartialViewRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PartialViewRepository.cs index 254d2cbc0c..e9352a945f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PartialViewRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PartialViewRepository.cs @@ -24,20 +24,24 @@ namespace Umbraco.Core.Persistence.Repositories public override IPartialView Get(string id) { - if (FileSystem.FileExists(id) == false) - { - return null; - } - + // get the relative path within the filesystem + // (though... id should be relative already) var path = FileSystem.GetRelativePath(id); + + if (FileSystem.FileExists(path) == false) + return null; + + // content will be lazy-loaded when required var created = FileSystem.GetCreated(path).UtcDateTime; var updated = FileSystem.GetLastModified(path).UtcDateTime; + //var content = GetFileContent(path); - var script = new PartialView(path, file => GetFileContent(file.Path)) + var view = new PartialView(path, file => GetFileContent(file.OriginalPath)) { //id can be the hash Id = path.GetHashCode(), Key = path.EncodeAsGuid(), + //Content = content, CreateDate = created, UpdateDate = updated, VirtualPath = FileSystem.GetUrl(id), @@ -46,9 +50,9 @@ namespace Umbraco.Core.Persistence.Repositories //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 - script.ResetDirtyProperties(false); + view.ResetDirtyProperties(false); - return script; + return view; } public override void AddOrUpdate(IPartialView entity) @@ -61,7 +65,7 @@ namespace Umbraco.Core.Persistence.Repositories // ensure that from now on, content is lazy-loaded if (partialView != null && partialView.GetFileContent == null) - partialView.GetFileContent = file => GetFileContent(file.Path); + partialView.GetFileContent = file => GetFileContent(file.OriginalPath); } public override IEnumerable GetAll(params string[] ids) @@ -86,6 +90,29 @@ namespace Umbraco.Core.Persistence.Repositories } } + private static readonly List ValidExtensions = new List { "cshtml" }; + + public virtual bool ValidatePartialView(IPartialView partialView) + { + // get full path + string fullPath; + try + { + // may throw for security reasons + fullPath = FileSystem.GetFullPath(partialView.Path); + } + catch + { + return false; + } + + // validate path & extension + var validDir = SystemDirectories.MvcViews; + var isValidPath = IOHelper.VerifyEditPath(fullPath, validDir); + var isValidExtension = IOHelper.VerifyFileExtension(fullPath, ValidExtensions); + return isValidPath && isValidExtension; + } + /// /// Gets a stream that is used to write to the file /// diff --git a/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs index e288efdc85..13b2877c50 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs @@ -38,7 +38,7 @@ namespace Umbraco.Core.Persistence.Repositories var updated = FileSystem.GetLastModified(path).UtcDateTime; //var content = GetFileContent(path); - var script = new Script(path, file => GetFileContent(file.Path)) + var script = new Script(path, file => GetFileContent(file.OriginalPath)) { //id can be the hash Id = path.GetHashCode(), @@ -62,7 +62,7 @@ namespace Umbraco.Core.Persistence.Repositories // ensure that from now on, content is lazy-loaded if (entity.GetFileContent == null) - entity.GetFileContent = file => GetFileContent(file.Path); + entity.GetFileContent = file => GetFileContent(file.OriginalPath); } public override IEnumerable