From f4ff50a03fc26c1c46ef066455541f6ece3de534 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 30 Jun 2016 10:30:43 +0200 Subject: [PATCH] Cleanup - file permission helper --- .../Install/FilePermissionHelper.cs | 223 ++++++++++-------- .../InstallSteps/FilePermissionsStep.cs | 14 +- .../PublishedCache/FacadeServiceBase.cs | 3 + .../PublishedCache/IFacadeService.cs | 8 + .../PublishedCache/NuCache/FacadeService.cs | 14 ++ .../XmlPublishedCache/FacadeService.cs | 27 ++- 6 files changed, 185 insertions(+), 104 deletions(-) diff --git a/src/Umbraco.Web/Install/FilePermissionHelper.cs b/src/Umbraco.Web/Install/FilePermissionHelper.cs index bdb8243d06..b036f21916 100644 --- a/src/Umbraco.Web/Install/FilePermissionHelper.cs +++ b/src/Umbraco.Web/Install/FilePermissionHelper.cs @@ -1,158 +1,195 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Web; using System.IO; using Umbraco.Core.IO; -using umbraco; using Umbraco.Web.PublishedCache; -using Umbraco.Web.PublishedCache.XmlPublishedCache; namespace Umbraco.Web.Install { internal class FilePermissionHelper { - internal static readonly string[] PermissionDirs = { SystemDirectories.Css, SystemDirectories.Config, SystemDirectories.Data, SystemDirectories.Media, SystemDirectories.Masterpages, SystemDirectories.Xslt, SystemDirectories.UserControls, SystemDirectories.Preview }; - internal static readonly string[] PermissionFiles = { }; - internal static readonly string[] PackagesPermissionsDirs = { SystemDirectories.Bin, SystemDirectories.Umbraco, SystemDirectories.UserControls, SystemDirectories.Packages }; + // ensure that these directories exist and Umbraco can write to them + private static readonly string[] PermissionDirs = { SystemDirectories.Css, SystemDirectories.Config, SystemDirectories.Data, SystemDirectories.Media, SystemDirectories.Masterpages, SystemDirectories.Xslt, SystemDirectories.UserControls, SystemDirectories.Preview }; + private static readonly string[] PackagesPermissionsDirs = { SystemDirectories.Bin, SystemDirectories.Umbraco, SystemDirectories.UserControls, SystemDirectories.Packages }; - public static bool RunFilePermissionTestSuite(out Dictionary> errorReport) + // ensure Umbraco can write to these files (the directories must exist) + private static readonly string[] PermissionFiles = { }; + + public static bool RunFilePermissionTestSuite(out Dictionary> report) { - errorReport = new Dictionary>(); + report = new Dictionary>(); - List errors; + IEnumerable errors; - if (TestDirectories(PermissionDirs, out errors) == false) - errorReport["Folder creation failed"] = errors.ToList(); + if (EnsureDirectories(PermissionDirs, out errors) == false) + report["Folder creation failed"] = errors.ToList(); - if (TestDirectories(PackagesPermissionsDirs, out errors) == false) - errorReport["File writing for packages failed"] = errors.ToList(); + if (EnsureDirectories(PackagesPermissionsDirs, out errors) == false) + report["File writing for packages failed"] = errors.ToList(); - if (TestFiles(PermissionFiles, out errors) == false) - errorReport["File writing failed"] = errors.ToList(); + if (EnsureFiles(PermissionFiles, out errors) == false) + report["File writing failed"] = errors.ToList(); - if (TestContentXml(out errors) == false) - errorReport["Cache file writing failed"] = errors.ToList(); + if (TestFacade(out errors) == false) + report["Facade environment check failed"] = errors.ToList(); - if (TestFolderCreation(SystemDirectories.Media, out errors) == false) - errorReport["Media folder creation failed"] = errors.ToList(); + if (EnsureCanCreateSubDirectory(SystemDirectories.Media, out errors) == false) + report["Media folder creation failed"] = errors.ToList(); - return errorReport.Any() == false; + return report.Count == 0; } - public static bool TestDirectories(string[] directories, out List errorReport) + public static bool EnsureDirectories(string[] dirs, out IEnumerable errors) { - errorReport = new List(); - bool succes = true; - foreach (string dir in directories) + List temp = null; + var success = true; + foreach (var dir in dirs) { - if (Directory.Exists(dir) == false) continue; + // we don't want to create/ship unnecessary directories, so + // here we just ensure we can access the directory, not create it + var tryAccess = TryAccessDirectory(dir); + if (tryAccess) continue; - bool result = SaveAndDeleteFile(IOHelper.MapPath(dir + "/configWizardPermissionTest.txt")); - - if (result == false) - { - succes = false; - errorReport.Add(dir); - } + if (temp == null) temp = new List(); + temp.Add(dir); + success = false; } - return succes; + errors = success ? Enumerable.Empty() : temp; + return success; } - public static bool TestFiles(string[] files, out List errorReport) + public static bool EnsureFiles(string[] files, out IEnumerable errors) { - errorReport = new List(); - bool succes = true; - foreach (string file in files) + List temp = null; + var success = true; + foreach (var file in files) { - bool result = OpenFileForWrite(IOHelper.MapPath(file)); - if (result == false) - { - errorReport.Add(file); - succes = false; - } + var canWrite = TryWriteFile(file); + if (canWrite) continue; + + if (temp == null) temp = new List(); + temp.Add(file); + success = false; } - return succes; + errors = success ? Enumerable.Empty() : temp; + return success; } - public static bool TestFolderCreation(string folder, out List errorReport) + public static bool EnsureCanCreateSubDirectory(string dir, out IEnumerable errors) { - errorReport = new List(); - try - { - string tempDir = IOHelper.MapPath(folder + "/testCreatedByConfigWizard"); - Directory.CreateDirectory(tempDir); - Directory.Delete(tempDir); - return true; - } - catch - { - errorReport.Add(folder); - return false; - } + return EnsureCanCreateSubDirectories(new[] { dir }, out errors); } - public static bool TestContentXml(out List errorReport) + public static bool EnsureCanCreateSubDirectories(IEnumerable dirs, out IEnumerable errors) { - errorReport = new List(); - - // makes sense for xml cache only - // fixme and to nucache that writes files too? - var svc = FacadeServiceResolver.Current.Service as FacadeService; - if (svc == null) return true; - - // Test creating/saving/deleting a file in the same location as the content xml file - // NOTE: We cannot modify the xml file directly because a background thread is responsible for - // that and we might get lock issues. - try + List temp = null; + var success = true; + foreach (var dir in dirs) { - // xml cache will persist file, other caches may do something else - svc.XmlStore.EnsureFilePermission(); - return true; - } - catch - { - errorReport.Add(SystemFiles.ContentCacheXml); - return false; + var canCreate = TryCreateSubDirectory(dir); + if (canCreate) continue; + + if (temp == null) temp = new List(); + temp.Add(dir); + success = false; } + + errors = success ? Enumerable.Empty() : temp; + return success; } - private static bool SaveAndDeleteFile(string file) + public static bool TestFacade(out IEnumerable errors) + { + var facadeService = FacadeServiceResolver.Current.Service; + return facadeService.EnsureEnvironment(out errors); + } + + // tries to create a sub-directory + // if successful, the sub-directory is deleted + // creates the directory if needed - does not delete it + private static bool TryCreateSubDirectory(string dir) { try { - //first check if the directory of the file exists, and if not try to create that first. - FileInfo fi = new FileInfo(file); - if (fi.Directory.Exists == false) - { - fi.Directory.Create(); - } - - File.WriteAllText(file, - "This file has been created by the umbraco configuration wizard. It is safe to delete it!"); - File.Delete(file); + var path = IOHelper.MapPath(dir + "/" + CreateRandomName()); + Directory.CreateDirectory(path); + Directory.Delete(path); return true; } catch { return false; } - } - private static bool OpenFileForWrite(string file) + // tries to create a file + // if successful, the file is deleted + // creates the directory if needed - does not delete it + public static bool TryCreateDirectory(string dir) { try { - File.AppendText(file).Close(); + var dirPath = IOHelper.MapPath(dir); + + if (Directory.Exists(dirPath) == false) + Directory.CreateDirectory(dirPath); + + var filePath = dirPath + "/" + CreateRandomName() + ".tmp"; + File.WriteAllText(filePath, "This is an Umbraco internal test file. It is safe to delete it."); + File.Delete(filePath); + return true; } catch { return false; } - return true; + } + + // tries to create a file + // if successful, the file is deleted + // if the directory does not exist, do nothing & success + public static bool TryAccessDirectory(string dir) + { + try + { + var dirPath = IOHelper.MapPath(dir); + + if (Directory.Exists(dirPath) == false) + return true; + + var filePath = dirPath + "/" + CreateRandomName() + ".tmp"; + File.WriteAllText(filePath, "This is an Umbraco internal test file. It is safe to delete it."); + File.Delete(filePath); + return true; + } + catch + { + return false; + } + } + + // tries to write into a file + // fails if the directory does not exist + private static bool TryWriteFile(string file) + { + try + { + var path = IOHelper.MapPath(file); + File.AppendText(path).Close(); + return true; + } + catch + { + return false; + } + } + + private static string CreateRandomName() + { + return "umbraco-test." + Guid.NewGuid().ToString("N").Substring(0, 8); } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Install/InstallSteps/FilePermissionsStep.cs b/src/Umbraco.Web/Install/InstallSteps/FilePermissionsStep.cs index 37b12798cb..8c9dec9f6e 100644 --- a/src/Umbraco.Web/Install/InstallSteps/FilePermissionsStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/FilePermissionsStep.cs @@ -15,17 +15,13 @@ namespace Umbraco.Web.Install.InstallSteps { public override InstallSetupResult Execute(object model) { - //first validate file permissions - var permissionsOk = true; - Dictionary> reportParts; - - permissionsOk = FilePermissionHelper.RunFilePermissionTestSuite(out reportParts); + // validate file permissions + Dictionary> report; + var permissionsOk = FilePermissionHelper.RunFilePermissionTestSuite(out report); if (permissionsOk == false) - { - throw new InstallException("Permission check failed", "permissionsreport", new { errors = reportParts }); - } - + throw new InstallException("Permission check failed", "permissionsreport", new { errors = report }); + return null; } diff --git a/src/Umbraco.Web/PublishedCache/FacadeServiceBase.cs b/src/Umbraco.Web/PublishedCache/FacadeServiceBase.cs index 9384ad2e42..a52883049f 100644 --- a/src/Umbraco.Web/PublishedCache/FacadeServiceBase.cs +++ b/src/Umbraco.Web/PublishedCache/FacadeServiceBase.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; @@ -21,6 +22,8 @@ namespace Umbraco.Web.PublishedCache protected IFacade CurrentFacade => FacadeAccessor.Facade; + public abstract bool EnsureEnvironment(out IEnumerable errors); + public abstract IPublishedProperty CreateFragmentProperty(PublishedPropertyType propertyType, Guid itemKey, bool previewing, PropertyCacheLevel referenceCacheLevel, object sourceValue = null); public abstract string EnterPreview(IUser user, int contentId); diff --git a/src/Umbraco.Web/PublishedCache/IFacadeService.cs b/src/Umbraco.Web/PublishedCache/IFacadeService.cs index 7f1b92e102..d5b6c43a58 100644 --- a/src/Umbraco.Web/PublishedCache/IFacadeService.cs +++ b/src/Umbraco.Web/PublishedCache/IFacadeService.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; @@ -44,6 +45,13 @@ namespace Umbraco.Web.PublishedCache /// IFacadeAccessor FacadeAccessor { get; } + /// + /// Ensures that the facade has the proper environment to run. + /// + /// The errors, if any. + /// A value indicating whether the facade has the proper environment to run. + bool EnsureEnvironment(out IEnumerable errors); + #endregion #region Preview diff --git a/src/Umbraco.Web/PublishedCache/NuCache/FacadeService.cs b/src/Umbraco.Web/PublishedCache/NuCache/FacadeService.cs index ff5157c5dc..4c988529ab 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/FacadeService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/FacadeService.cs @@ -8,6 +8,7 @@ using CSharpTest.Net.Collections; using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; @@ -21,6 +22,7 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Web.Cache; +using Umbraco.Web.Install; using Umbraco.Web.PublishedCache.NuCache.DataSource; using Umbraco.Web.PublishedCache.XmlPublishedCache; using Umbraco.Web.Routing; @@ -210,6 +212,18 @@ namespace Umbraco.Web.PublishedCache.NuCache #endregion + #region Environment + + public override bool EnsureEnvironment(out IEnumerable errors) + { + // must have app_data and be able to write files into it + var ok = FilePermissionHelper.TryCreateDirectory(SystemDirectories.Data); + errors = ok ? Enumerable.Empty() : new[] { "NuCache local DB files." }; + return ok; + } + + #endregion + #region Populate Stores // sudden panic... but in RepeatableRead can a content that I haven't already read, be removed diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/FacadeService.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/FacadeService.cs index ffccc1b7c7..3ee9ca40c0 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/FacadeService.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/FacadeService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.PublishedContent; @@ -80,7 +81,29 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache #endregion - #region PublishedCachesService Caches + #region Environment + + public override bool EnsureEnvironment(out IEnumerable errors) + { + // Test creating/saving/deleting a file in the same location as the content xml file + // NOTE: We cannot modify the xml file directly because a background thread is responsible for + // that and we might get lock issues. + try + { + XmlStore.EnsureFilePermission(); + errors = Enumerable.Empty(); + return true; + } + catch + { + errors = new[] { SystemFiles.ContentCacheXml }; + return false; + } + } + + #endregion + + #region Caches public override IFacade CreateFacade(string previewToken) { @@ -100,7 +123,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache #endregion - #region PublishedCachesService Preview + #region Preview public override string EnterPreview(IUser user, int contentId) {