Cleanup - file permission helper

This commit is contained in:
Stephan
2016-06-30 10:30:43 +02:00
parent a8bb18ecae
commit f4ff50a03f
6 changed files with 185 additions and 104 deletions

View File

@@ -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<string, List<string>> errorReport)
// ensure Umbraco can write to these files (the directories must exist)
private static readonly string[] PermissionFiles = { };
public static bool RunFilePermissionTestSuite(out Dictionary<string, IEnumerable<string>> report)
{
errorReport = new Dictionary<string, List<string>>();
report = new Dictionary<string, IEnumerable<string>>();
List<string> errors;
IEnumerable<string> 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<string> errorReport)
public static bool EnsureDirectories(string[] dirs, out IEnumerable<string> errors)
{
errorReport = new List<string>();
bool succes = true;
foreach (string dir in directories)
List<string> 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<string>();
temp.Add(dir);
success = false;
}
return succes;
errors = success ? Enumerable.Empty<string>() : temp;
return success;
}
public static bool TestFiles(string[] files, out List<string> errorReport)
public static bool EnsureFiles(string[] files, out IEnumerable<string> errors)
{
errorReport = new List<string>();
bool succes = true;
foreach (string file in files)
List<string> 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<string>();
temp.Add(file);
success = false;
}
return succes;
errors = success ? Enumerable.Empty<string>() : temp;
return success;
}
public static bool TestFolderCreation(string folder, out List<string> errorReport)
public static bool EnsureCanCreateSubDirectory(string dir, out IEnumerable<string> errors)
{
errorReport = new List<string>();
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<string> errorReport)
public static bool EnsureCanCreateSubDirectories(IEnumerable<string> dirs, out IEnumerable<string> errors)
{
errorReport = new List<string>();
// 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<string> 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<string>();
temp.Add(dir);
success = false;
}
errors = success ? Enumerable.Empty<string>() : temp;
return success;
}
private static bool SaveAndDeleteFile(string file)
public static bool TestFacade(out IEnumerable<string> 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);
}
}
}

View File

@@ -15,17 +15,13 @@ namespace Umbraco.Web.Install.InstallSteps
{
public override InstallSetupResult Execute(object model)
{
//first validate file permissions
var permissionsOk = true;
Dictionary<string, List<string>> reportParts;
permissionsOk = FilePermissionHelper.RunFilePermissionTestSuite(out reportParts);
// validate file permissions
Dictionary<string, IEnumerable<string>> 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;
}

View File

@@ -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<string> errors);
public abstract IPublishedProperty CreateFragmentProperty(PublishedPropertyType propertyType, Guid itemKey, bool previewing, PropertyCacheLevel referenceCacheLevel, object sourceValue = null);
public abstract string EnterPreview(IUser user, int contentId);

View File

@@ -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
/// </summary>
IFacadeAccessor FacadeAccessor { get; }
/// <summary>
/// Ensures that the facade has the proper environment to run.
/// </summary>
/// <param name="errors">The errors, if any.</param>
/// <returns>A value indicating whether the facade has the proper environment to run.</returns>
bool EnsureEnvironment(out IEnumerable<string> errors);
#endregion
#region Preview

View File

@@ -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<string> errors)
{
// must have app_data and be able to write files into it
var ok = FilePermissionHelper.TryCreateDirectory(SystemDirectories.Data);
errors = ok ? Enumerable.Empty<string>() : 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

View File

@@ -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<string> 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<string>();
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)
{