Creating an interface for FilePermissionHelper, making the implementation (in Umbraco.Web) singleton and adding the interface to Umbraco.Abstractions. Changing static methods in FilePermissionHelper to instance ones and fixing old way of referencing those methods. Removing the usage of Current in FolderAndFilePermissionsCheck. Fixing tests - mocking the newly created IFilePermissionHelper

This commit is contained in:
elitsa
2020-01-28 13:48:44 +01:00
parent 6707ac4d5f
commit 550568dda6
10 changed files with 87 additions and 40 deletions

View File

@@ -2,10 +2,10 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core;
using Umbraco.Web.Composing;
using Umbraco.Core.Services;
using Umbraco.Web.Install;
using Umbraco.Core.Configuration;
using Umbraco.Core.Install;
using Umbraco.Core.IO;
using Umbraco.Core.Services;
namespace Umbraco.Web.HealthCheck.Checks.Permissions
{
@@ -30,11 +30,15 @@ namespace Umbraco.Web.HealthCheck.Checks.Permissions
{
private readonly ILocalizedTextService _textService;
private readonly IGlobalSettings _globalSettings;
private readonly IFilePermissionHelper _filePermissionHelper;
private readonly IIOHelper _ioHelper;
public FolderAndFilePermissionsCheck(ILocalizedTextService textService, IGlobalSettings globalSettings)
public FolderAndFilePermissionsCheck(ILocalizedTextService textService, IGlobalSettings globalSettings, IFilePermissionHelper filePermissionHelper, IIOHelper ioHelper)
{
_textService = textService;
_globalSettings = globalSettings;
_filePermissionHelper = filePermissionHelper;
_ioHelper = ioHelper;
}
/// <summary>
@@ -84,15 +88,15 @@ namespace Umbraco.Web.HealthCheck.Checks.Permissions
};
// Run checks for required and optional paths for modify permission
var requiredPathCheckResult = FilePermissionHelper.EnsureDirectories(
var requiredPathCheckResult = _filePermissionHelper.EnsureDirectories(
GetPathsToCheck(pathsToCheck, PermissionCheckRequirement.Required), out var requiredFailedPaths);
var optionalPathCheckResult = FilePermissionHelper.EnsureDirectories(
var optionalPathCheckResult = _filePermissionHelper.EnsureDirectories(
GetPathsToCheck(pathsToCheck, PermissionCheckRequirement.Optional), out var optionalFailedPaths);
//now check the special folders
var requiredPathCheckResult2 = FilePermissionHelper.EnsureDirectories(
var requiredPathCheckResult2 = _filePermissionHelper.EnsureDirectories(
GetPathsToCheck(pathsToCheckWithRestarts, PermissionCheckRequirement.Required), out var requiredFailedPaths2, writeCausesRestart:true);
var optionalPathCheckResult2 = FilePermissionHelper.EnsureDirectories(
var optionalPathCheckResult2 = _filePermissionHelper.EnsureDirectories(
GetPathsToCheck(pathsToCheckWithRestarts, PermissionCheckRequirement.Optional), out var optionalFailedPaths2, writeCausesRestart: true);
requiredPathCheckResult = requiredPathCheckResult && requiredPathCheckResult2;
@@ -117,18 +121,18 @@ namespace Umbraco.Web.HealthCheck.Checks.Permissions
// Run checks for required and optional paths for modify permission
IEnumerable<string> requiredFailedPaths;
IEnumerable<string> optionalFailedPaths;
var requiredPathCheckResult = FilePermissionHelper.EnsureFiles(GetPathsToCheck(pathsToCheck, PermissionCheckRequirement.Required), out requiredFailedPaths);
var optionalPathCheckResult = FilePermissionHelper.EnsureFiles(GetPathsToCheck(pathsToCheck, PermissionCheckRequirement.Optional), out optionalFailedPaths);
var requiredPathCheckResult = _filePermissionHelper.EnsureFiles(GetPathsToCheck(pathsToCheck, PermissionCheckRequirement.Required), out requiredFailedPaths);
var optionalPathCheckResult = _filePermissionHelper.EnsureFiles(GetPathsToCheck(pathsToCheck, PermissionCheckRequirement.Optional), out optionalFailedPaths);
return GetStatus(requiredPathCheckResult, requiredFailedPaths, optionalPathCheckResult, optionalFailedPaths, PermissionCheckFor.File);
}
private static string[] GetPathsToCheck(Dictionary<string, PermissionCheckRequirement> pathsToCheck,
private string[] GetPathsToCheck(Dictionary<string, PermissionCheckRequirement> pathsToCheck,
PermissionCheckRequirement requirement)
{
return pathsToCheck
.Where(x => x.Value == requirement)
.Select(x => Current.IOHelper.MapPath(x.Key))
.Select(x => _ioHelper.MapPath(x.Key))
.OrderBy(x => x)
.ToArray();
}
@@ -168,7 +172,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Permissions
private string GetMessageForPathCheckFailure(string messageKey, IEnumerable<string> failedPaths)
{
var rootFolder = Current.IOHelper.MapPath("/");
var rootFolder = _ioHelper.MapPath("/");
var failedFolders = failedPaths
.Select(x => ParseFolderFromFullPath(rootFolder, x));
return _textService.Localize(messageKey,

View File

@@ -0,0 +1,16 @@
using System.Collections.Generic;
namespace Umbraco.Core.Install
{
public interface IFilePermissionHelper
{
bool RunFilePermissionTestSuite(out Dictionary<string, IEnumerable<string>> report);
bool EnsureDirectories(string[] dirs, out IEnumerable<string> errors, bool writeCausesRestart = false);
bool EnsureFiles(string[] files, out IEnumerable<string> errors);
bool EnsureCanCreateSubDirectory(string dir, out IEnumerable<string> errors);
bool EnsureCanCreateSubDirectories(IEnumerable<string> dirs, out IEnumerable<string> errors);
bool TestPublishedSnapshotService(out IEnumerable<string> errors);
bool TryCreateDirectory(string dir);
bool TryAccessDirectory(string dir, bool canWrite);
}
}

View File

@@ -9,6 +9,7 @@ using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Events;
using Umbraco.Core.Hosting;
using Umbraco.Core.Install;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
@@ -143,6 +144,8 @@ namespace Umbraco.Tests.PublishedContent
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
var filePermissionHelper = Mock.Of<IFilePermissionHelper>();
// at last, create the complete NuCache snapshot service!
var options = new PublishedSnapshotServiceOptions { IgnoreLocalDb = true };
_snapshotService = new PublishedSnapshotService(options,
@@ -165,7 +168,8 @@ namespace Umbraco.Tests.PublishedContent
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(TestHelper.ShortStringHelper) }),
typeFinder,
hostingEnvironment,
new MockShortStringHelper());
new MockShortStringHelper(),
filePermissionHelper);
// invariant is the current default
_variationAccesor.VariationContext = new VariationContext();

View File

@@ -8,6 +8,7 @@ using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Events;
using Umbraco.Core.Install;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
@@ -185,6 +186,8 @@ namespace Umbraco.Tests.PublishedContent
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
var filePermissionHelper = Mock.Of<IFilePermissionHelper>();
// at last, create the complete NuCache snapshot service!
var options = new PublishedSnapshotServiceOptions { IgnoreLocalDb = true };
_snapshotService = new PublishedSnapshotService(options,
@@ -207,7 +210,8 @@ namespace Umbraco.Tests.PublishedContent
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(TestHelper.ShortStringHelper) }),
typeFinder,
TestHelper.GetHostingEnvironment(),
new MockShortStringHelper());
new MockShortStringHelper(),
filePermissionHelper);
// invariant is the current default
_variationAccesor.VariationContext = new VariationContext();

View File

@@ -10,6 +10,7 @@ using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Events;
using Umbraco.Core.Install;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
@@ -83,6 +84,7 @@ namespace Umbraco.Tests.Scoping
var mediaRepository = Mock.Of<IMediaRepository>();
var memberRepository = Mock.Of<IMemberRepository>();
var hostingEnvironment = TestHelper.GetHostingEnvironment();
var filePermissionHelper = Mock.Of<IFilePermissionHelper>();
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
@@ -105,7 +107,8 @@ namespace Umbraco.Tests.Scoping
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(ShortStringHelper) }),
typeFinder,
hostingEnvironment,
new MockShortStringHelper());
new MockShortStringHelper(),
filePermissionHelper);
}
protected UmbracoContext GetUmbracoContextNu(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable<IUrlProvider> urlProviders = null)

View File

@@ -9,6 +9,7 @@ using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Hosting;
using Umbraco.Core.Install;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
@@ -55,7 +56,7 @@ namespace Umbraco.Tests.Services
var mediaRepository = Mock.Of<IMediaRepository>();
var memberRepository = Mock.Of<IMemberRepository>();
var hostingEnvironment = Mock.Of<IHostingEnvironment>();
var filePermissionHelper = Mock.Of<IFilePermissionHelper>();
var typeFinder = new TypeFinder(Mock.Of<ILogger>());
@@ -78,7 +79,8 @@ namespace Umbraco.Tests.Services
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(ShortStringHelper) }),
typeFinder,
hostingEnvironment,
new MockShortStringHelper());
new MockShortStringHelper(),
filePermissionHelper);
}
public class LocalServerMessenger : ServerMessengerBase

View File

@@ -4,33 +4,34 @@ using System.Linq;
using System.IO;
using System.Security.AccessControl;
using Umbraco.Core;
using Umbraco.Core.Install;
using Umbraco.Core.IO;
using Umbraco.Web.Composing;
namespace Umbraco.Web.Install
{
internal class FilePermissionHelper
internal class FilePermissionHelper: IFilePermissionHelper
{
// ensure that these directories exist and Umbraco can write to them
private static readonly string[] PermissionDirs = { Current.Configs.Global().UmbracoCssPath, Constants.SystemDirectories.Config, Constants.SystemDirectories.Data, Current.Configs.Global().UmbracoMediaPath, Constants.SystemDirectories.Preview };
private static readonly string[] PackagesPermissionsDirs = { Constants.SystemDirectories.Bin, Current.Configs.Global().UmbracoPath, Constants.SystemDirectories.Packages };
private readonly string[] _permissionDirs = { Current.Configs.Global().UmbracoCssPath, Constants.SystemDirectories.Config, Constants.SystemDirectories.Data, Current.Configs.Global().UmbracoMediaPath, Constants.SystemDirectories.Preview };
private readonly string[] _packagesPermissionsDirs = { Constants.SystemDirectories.Bin, Current.Configs.Global().UmbracoPath, Constants.SystemDirectories.Packages };
// ensure Umbraco can write to these files (the directories must exist)
private static readonly string[] PermissionFiles = { };
private readonly string[] _permissionFiles = { };
public static bool RunFilePermissionTestSuite(out Dictionary<string, IEnumerable<string>> report)
public bool RunFilePermissionTestSuite(out Dictionary<string, IEnumerable<string>> report)
{
report = new Dictionary<string, IEnumerable<string>>();
using (ChangesMonitor.Suspended()) // hack: ensure this does not trigger a restart
{
if (EnsureDirectories(PermissionDirs, out var errors) == false)
if (EnsureDirectories(_permissionDirs, out var errors) == false)
report["Folder creation failed"] = errors.ToList();
if (EnsureDirectories(PackagesPermissionsDirs, out errors) == false)
if (EnsureDirectories(_packagesPermissionsDirs, out errors) == false)
report["File writing for packages failed"] = errors.ToList();
if (EnsureFiles(PermissionFiles, out errors) == false)
if (EnsureFiles(_permissionFiles, out errors) == false)
report["File writing failed"] = errors.ToList();
if (TestPublishedSnapshotService(out errors) == false)
@@ -54,7 +55,7 @@ namespace Umbraco.Web.Install
/// reliable but we cannot write a file since it will cause an app domain restart.
/// </param>
/// <returns></returns>
public static bool EnsureDirectories(string[] dirs, out IEnumerable<string> errors, bool writeCausesRestart = false)
public bool EnsureDirectories(string[] dirs, out IEnumerable<string> errors, bool writeCausesRestart = false)
{
List<string> temp = null;
var success = true;
@@ -74,7 +75,7 @@ namespace Umbraco.Web.Install
return success;
}
public static bool EnsureFiles(string[] files, out IEnumerable<string> errors)
public bool EnsureFiles(string[] files, out IEnumerable<string> errors)
{
List<string> temp = null;
var success = true;
@@ -92,12 +93,12 @@ namespace Umbraco.Web.Install
return success;
}
public static bool EnsureCanCreateSubDirectory(string dir, out IEnumerable<string> errors)
public bool EnsureCanCreateSubDirectory(string dir, out IEnumerable<string> errors)
{
return EnsureCanCreateSubDirectories(new[] { dir }, out errors);
}
public static bool EnsureCanCreateSubDirectories(IEnumerable<string> dirs, out IEnumerable<string> errors)
public bool EnsureCanCreateSubDirectories(IEnumerable<string> dirs, out IEnumerable<string> errors)
{
List<string> temp = null;
var success = true;
@@ -115,7 +116,7 @@ namespace Umbraco.Web.Install
return success;
}
public static bool TestPublishedSnapshotService(out IEnumerable<string> errors)
public bool TestPublishedSnapshotService(out IEnumerable<string> errors)
{
var publishedSnapshotService = Current.PublishedSnapshotService;
return publishedSnapshotService.EnsureEnvironment(out errors);
@@ -124,7 +125,7 @@ namespace Umbraco.Web.Install
// 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)
private bool TryCreateSubDirectory(string dir)
{
try
{
@@ -142,7 +143,7 @@ namespace Umbraco.Web.Install
// 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)
public bool TryCreateDirectory(string dir)
{
try
{
@@ -170,7 +171,7 @@ namespace Umbraco.Web.Install
// use the ACL APIs to avoid creating files
//
// if the directory does not exist, do nothing & success
public static bool TryAccessDirectory(string dir, bool canWrite)
public bool TryAccessDirectory(string dir, bool canWrite)
{
try
{
@@ -197,7 +198,7 @@ namespace Umbraco.Web.Install
}
}
private static bool HasWritePermission(string path)
private bool HasWritePermission(string path)
{
var writeAllow = false;
var writeDeny = false;
@@ -235,7 +236,7 @@ namespace Umbraco.Web.Install
// tries to write into a file
// fails if the directory does not exist
private static bool TryWriteFile(string file)
private bool TryWriteFile(string file)
{
try
{
@@ -249,7 +250,7 @@ namespace Umbraco.Web.Install
}
}
private static string CreateRandomName()
private string CreateRandomName()
{
return "umbraco-test." + Guid.NewGuid().ToString("N").Substring(0, 8);
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Umbraco.Core;
using Umbraco.Core.Install;
using Umbraco.Core.IO;
using Umbraco.Web.Install.Models;
@@ -13,11 +14,16 @@ namespace Umbraco.Web.Install.InstallSteps
PerformsAppRestart = true)]
internal class FilePermissionsStep : InstallSetupStep<object>
{
private readonly IFilePermissionHelper _filePermissionHelper;
public FilePermissionsStep(IFilePermissionHelper filePermissionHelper)
{
_filePermissionHelper = filePermissionHelper;
}
public override Task<InstallSetupResult> ExecuteAsync(object model)
{
// validate file permissions
Dictionary<string, IEnumerable<string>> report;
var permissionsOk = FilePermissionHelper.RunFilePermissionTestSuite(out report);
var permissionsOk = _filePermissionHelper.RunFilePermissionTestSuite(out report);
if (permissionsOk == false)
throw new InstallException("Permission check failed", "permissionsreport", new { errors = report });

View File

@@ -11,6 +11,7 @@ using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Hosting;
using Umbraco.Core.Install;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
@@ -51,6 +52,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
private readonly ITypeFinder _typeFinder;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IShortStringHelper _shortStringHelper;
private readonly IFilePermissionHelper _filePermissionHelper;
// volatile because we read it with no lock
private volatile bool _isReady;
@@ -87,7 +89,8 @@ namespace Umbraco.Web.PublishedCache.NuCache
UrlSegmentProviderCollection urlSegmentProviders,
ITypeFinder typeFinder,
IHostingEnvironment hostingEnvironment,
IShortStringHelper shortStringHelper)
IShortStringHelper shortStringHelper,
IFilePermissionHelper filePermissionHelper)
: base(publishedSnapshotAccessor, variationContextAccessor)
{
//if (Interlocked.Increment(ref _singletonCheck) > 1)
@@ -107,6 +110,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
_typeFinder = typeFinder;
_hostingEnvironment = hostingEnvironment;
_shortStringHelper = shortStringHelper;
_filePermissionHelper = filePermissionHelper;
// we need an Xml serializer here so that the member cache can support XPath,
// for members this is done by navigating the serialized-to-xml member
@@ -360,7 +364,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
public override bool EnsureEnvironment(out IEnumerable<string> errors)
{
// must have app_data and be able to write files into it
var ok = FilePermissionHelper.TryCreateDirectory(GetLocalFilesPath());
var ok = _filePermissionHelper.TryCreateDirectory(GetLocalFilesPath());
errors = ok ? Enumerable.Empty<string>() : new[] { "NuCache local files." };
return ok;
}

View File

@@ -10,6 +10,7 @@ using Umbraco.Core.Dashboards;
using Umbraco.Core.Dictionary;
using Umbraco.Core.Events;
using Umbraco.Core.Hosting;
using Umbraco.Core.Install;
using Umbraco.Core.Migrations.PostMigrations;
using Umbraco.Core.Models.Identity;
using Umbraco.Core.Models.PublishedContent;
@@ -27,6 +28,7 @@ using Umbraco.Web.Editors;
using Umbraco.Web.Features;
using Umbraco.Web.HealthCheck;
using Umbraco.Web.Hosting;
using Umbraco.Web.Install;
using Umbraco.Web.Macros;
using Umbraco.Web.Media.EmbedProviders;
using Umbraco.Web.Models.PublishedContent;
@@ -63,6 +65,7 @@ namespace Umbraco.Web.Runtime
composition.Register<IBackOfficeInfo, AspNetBackOfficeInfo>();
composition.Register<IPasswordHasher, AspNetPasswordHasher>();
composition.Register<ICurrentUserAccessor, CurrentUserAccessor>();
composition.Register<IFilePermissionHelper, FilePermissionHelper>(Lifetime.Singleton);
composition.RegisterUnique<IHttpContextAccessor, AspNetHttpContextAccessor>(); // required for hybrid accessors