Admin ItemDeleter, Logging

This commit is contained in:
2023-07-26 11:30:26 +02:00
parent 66113ee9a0
commit 3de71cbdbe
15 changed files with 157 additions and 27 deletions

View File

@@ -22,6 +22,8 @@ public record FullName(string Path)
public string GetName() public string GetName()
=> Path.Split(Constants.SeparatorChar).Last(); => Path.Split(Constants.SeparatorChar).Last();
public FullName GetChild(string childName) public FullName GetChild(string childName)
=> new FullName(Path + Constants.SeparatorChar + childName); => new(Path + Constants.SeparatorChar + childName);
public override string ToString() => Path;
} }

View File

@@ -1,3 +1,6 @@
namespace FileTime.Core.Models; namespace FileTime.Core.Models;
public record NativePath(string Path); public record NativePath(string Path)
{
public override string ToString() => Path;
}

View File

@@ -13,6 +13,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="DynamicData" Version="7.14.2" /> <PackageReference Include="DynamicData" Version="7.14.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,6 +1,7 @@
using FileTime.Core.ContentAccess; using FileTime.Core.ContentAccess;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Providers.LocalAdmin; using FileTime.Providers.LocalAdmin;
using Microsoft.Extensions.Logging;
namespace FileTime.Providers.Local; namespace FileTime.Providers.Local;
@@ -8,50 +9,75 @@ public class LocalItemCreator : ItemCreatorBase<ILocalContentProvider>
{ {
private readonly IAdminContentAccessorFactory _adminContentAccessorFactory; private readonly IAdminContentAccessorFactory _adminContentAccessorFactory;
private readonly IAdminContentProvider _adminContentProvider; private readonly IAdminContentProvider _adminContentProvider;
private readonly ILogger<LocalItemCreator> _logger;
public LocalItemCreator( public LocalItemCreator(
IAdminContentAccessorFactory adminContentAccessorFactory, IAdminContentAccessorFactory adminContentAccessorFactory,
IAdminContentProvider adminContentProvider) IAdminContentProvider adminContentProvider,
ILogger<LocalItemCreator> logger)
{ {
_adminContentAccessorFactory = adminContentAccessorFactory; _adminContentAccessorFactory = adminContentAccessorFactory;
_adminContentProvider = adminContentProvider; _adminContentProvider = adminContentProvider;
_logger = logger;
} }
public override async Task CreateContainerAsync(ILocalContentProvider contentProvider, FullName fullName) public override async Task CreateContainerAsync(ILocalContentProvider contentProvider, FullName fullName)
{ {
_logger.LogTrace("Start creating container {FullName}", fullName);
var path = contentProvider.GetNativePath(fullName).Path; var path = contentProvider.GetNativePath(fullName).Path;
if (Directory.Exists(path)) return; if (Directory.Exists(path))
{
_logger.LogTrace("Container with path {Path} already exists", path);
return;
}
try try
{ {
_logger.LogTrace("Trying to create container with path {Path}", path);
Directory.CreateDirectory(path); Directory.CreateDirectory(path);
} }
catch (UnauthorizedAccessException) catch (UnauthorizedAccessException e)
{ {
if (!_adminContentAccessorFactory.IsAdminModeSupported) throw; _logger.LogDebug(e, "Failed to create container with path {Path}", path);
if (!_adminContentAccessorFactory.IsAdminModeSupported)
{
_logger.LogTrace("Admin mode is disabled, not trying to create {Path} as admin", path);
throw;
}
var adminContentAccessor = await _adminContentAccessorFactory.CreateAdminItemCreatorAsync(); var adminItemCreator = await _adminContentAccessorFactory.CreateAdminItemCreatorAsync();
await adminContentAccessor.CreateContainerAsync(_adminContentProvider, fullName); await adminItemCreator.CreateContainerAsync(_adminContentProvider, fullName);
} }
} }
public override async Task CreateElementAsync(ILocalContentProvider contentProvider, FullName fullName) public override async Task CreateElementAsync(ILocalContentProvider contentProvider, FullName fullName)
{ {
_logger.LogTrace("Start creating element {FullName}", fullName);
var path = contentProvider.GetNativePath(fullName).Path; var path = contentProvider.GetNativePath(fullName).Path;
if (File.Exists(path)) return; if (File.Exists(path))
{
_logger.LogTrace("Element with path {Path} already exists", path);
return;
}
try try
{ {
_logger.LogTrace("Trying to create element with path {Path}", path);
await using (File.Create(path)) await using (File.Create(path))
{ {
} }
} }
catch (UnauthorizedAccessException) catch (UnauthorizedAccessException e)
{ {
if (!_adminContentAccessorFactory.IsAdminModeSupported) throw; _logger.LogDebug(e, "Failed to create element with path {Path}", path);
if (!_adminContentAccessorFactory.IsAdminModeSupported)
{
_logger.LogTrace("Admin mode is disabled, not trying to create {Path} as admin", path);
throw;
}
var adminContentAccessor = await _adminContentAccessorFactory.CreateAdminItemCreatorAsync(); var adminItemCreator = await _adminContentAccessorFactory.CreateAdminItemCreatorAsync();
await adminContentAccessor.CreateElementAsync(_adminContentProvider, fullName); await adminItemCreator.CreateElementAsync(_adminContentProvider, fullName);
} }
} }
} }

View File

@@ -1,27 +1,65 @@
using FileTime.Core.ContentAccess; using FileTime.Core.ContentAccess;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Providers.LocalAdmin;
using Microsoft.Extensions.Logging;
namespace FileTime.Providers.Local; namespace FileTime.Providers.Local;
public class LocalItemDeleter : IItemDeleter<ILocalContentProvider> public class LocalItemDeleter : IItemDeleter<ILocalContentProvider>
{ {
public Task DeleteAsync(ILocalContentProvider contentProvider, FullName fullName) private readonly IAdminContentAccessorFactory _adminContentAccessorFactory;
private readonly IAdminContentProvider _adminContentProvider;
private readonly ILogger<LocalItemDeleter> _logger;
public LocalItemDeleter(
IAdminContentAccessorFactory adminContentAccessorFactory,
IAdminContentProvider adminContentProvider,
ILogger<LocalItemDeleter> logger)
{ {
_adminContentAccessorFactory = adminContentAccessorFactory;
_adminContentProvider = adminContentProvider;
_logger = logger;
}
public async Task DeleteAsync(ILocalContentProvider contentProvider, FullName fullName)
{
_logger.LogTrace("Start deleting item {FullName}", fullName);
var nativePath = contentProvider.GetNativePath(fullName).Path; var nativePath = contentProvider.GetNativePath(fullName).Path;
if (File.Exists(nativePath)) try
{ {
File.Delete(nativePath); if (File.Exists(nativePath))
{
_logger.LogTrace("File exists with path {Path}", nativePath);
File.Delete(nativePath);
}
else if (Directory.Exists(nativePath))
{
_logger.LogTrace("Directory exists with path {Path}", nativePath);
Directory.Delete(nativePath, true);
}
else
{
_logger.LogTrace("No file or directory exists with path {Path}", nativePath);
throw new FileNotFoundException(nativePath);
}
} }
else if (Directory.Exists(nativePath)) catch (Exception e)
{ {
Directory.Delete(nativePath, true); _logger.LogDebug(e, "Failed to delete item with path {Path}", nativePath);
}
else
{
throw new FileNotFoundException(nativePath);
}
return Task.CompletedTask; if (!_adminContentAccessorFactory.IsAdminModeSupported
|| e is not UnauthorizedAccessException and not IOException)
{
_logger.LogTrace(
"Admin mode is disabled or exception is not an access denied one, not trying to create {Path} as admin",
nativePath
);
throw;
}
var adminItemDeleter = await _adminContentAccessorFactory.CreateAdminItemDeleterAsync();
await adminItemDeleter.DeleteAsync(_adminContentProvider, fullName);
}
} }
} }

View File

@@ -6,4 +6,5 @@ public interface IAdminContentAccessorFactory
{ {
bool IsAdminModeSupported { get; } bool IsAdminModeSupported { get; }
Task<IRemoteItemCreator> CreateAdminItemCreatorAsync(); Task<IRemoteItemCreator> CreateAdminItemCreatorAsync();
Task<IRemoteItemDeleter> CreateAdminItemDeleterAsync();
} }

View File

@@ -33,4 +33,18 @@ public class AdminContentAccessorFactory : IAdminContentAccessorFactory
.GetRequiredService<IRemoteItemCreator>(); .GetRequiredService<IRemoteItemCreator>();
return adminItemCreator; return adminItemCreator;
} }
public async Task<IRemoteItemDeleter> CreateAdminItemDeleterAsync()
{
await _adminElevationManager.CreateAdminInstanceIfNecessaryAsync();
var connection = await _adminElevationManager.CreateConnectionAsync();
Debug.Assert(connection != null);
var adminItemDeleter = _serviceProvider.GetInitableResolver(
connection,
_adminElevationManager.ProviderName)
.GetRequiredService<IRemoteItemDeleter>();
return adminItemDeleter;
}
} }

View File

@@ -0,0 +1,10 @@
using FileTime.Core.ContentAccess;
using FileTime.Server.Common;
using InitableService;
namespace FileTime.Providers.Remote;
public interface IRemoteItemDeleter : IItemDeleter<IRemoteContentProvider>, IInitable<IRemoteConnection, string>
{
}

View File

@@ -0,0 +1,17 @@
using FileTime.Core.Models;
using FileTime.Server.Common;
namespace FileTime.Providers.Remote;
public class RemoteItemDeleter : IRemoteItemDeleter
{
private IRemoteConnection _remoteConnection = null!;
private string _remoteContentProviderId = null!;
public void Init(IRemoteConnection remoteConnection, string remoteContentProviderId)
{
_remoteConnection = remoteConnection;
_remoteContentProviderId = remoteContentProviderId;
}
public async Task DeleteAsync(IRemoteContentProvider contentProvider, FullName fullName)
=> await _remoteConnection.DeleteItemAsync(_remoteContentProviderId, fullName);
}

View File

@@ -10,6 +10,7 @@ public static class Startup
{ {
serviceCollection.TryAddSingleton<IRemoteContentProvider, RemoteContentProvider>(); serviceCollection.TryAddSingleton<IRemoteContentProvider, RemoteContentProvider>();
serviceCollection.TryAddTransient<IRemoteItemCreator, RemoteItemCreator>(); serviceCollection.TryAddTransient<IRemoteItemCreator, RemoteItemCreator>();
serviceCollection.TryAddTransient<IRemoteItemDeleter, RemoteItemDeleter>();
return serviceCollection; return serviceCollection;
} }

View File

@@ -7,4 +7,5 @@ public interface IRemoteConnection
Task Exit(); Task Exit();
Task CreateContainerAsync(string contentProviderId, FullName fullName); Task CreateContainerAsync(string contentProviderId, FullName fullName);
Task CreateElementAsync(string contentProviderId, FullName fullName); Task CreateElementAsync(string contentProviderId, FullName fullName);
Task DeleteItemAsync(string contentProviderId, FullName fullName);
} }

View File

@@ -5,4 +5,5 @@ public interface ISignalRHub
Task Exit(); Task Exit();
Task CreateContainerAsync(string contentProviderId, string fullName); Task CreateContainerAsync(string contentProviderId, string fullName);
Task CreateElementAsync(string contentProviderId, string fullName); Task CreateElementAsync(string contentProviderId, string fullName);
Task DeleteItemAsync(string contentProviderId, string fullName);
} }

View File

@@ -39,4 +39,10 @@ public class SignalRConnection : IRemoteConnection, IAsyncInitable<string>
var client = CreateClient(); var client = CreateClient();
await client.CreateElementAsync(contentProviderId, fullName.Path); await client.CreateElementAsync(contentProviderId, fullName.Path);
} }
public async Task DeleteItemAsync(string contentProviderId, FullName fullName)
{
var client = CreateClient();
await client.DeleteItemAsync(contentProviderId, fullName.Path);
}
} }

View File

@@ -42,4 +42,11 @@ public class ConnectionHub : Hub<ISignalRClient>, ISignalRHub
var itemCreator = _contentAccessorFactory.GetItemCreator(contentProvider); var itemCreator = _contentAccessorFactory.GetItemCreator(contentProvider);
await itemCreator.CreateElementAsync(contentProvider, new FullName(fullName)); await itemCreator.CreateElementAsync(contentProvider, new FullName(fullName));
} }
public async Task DeleteItemAsync(string contentProviderId, string fullName)
{
var contentProvider = _contentProviderRegistry.ContentProviders.First(p => p.Name == contentProviderId);
var itemDeleter = _contentAccessorFactory.GetItemDeleter(contentProvider);
await itemDeleter.DeleteAsync(contentProvider, new FullName(fullName));
}
} }

View File

@@ -5,6 +5,7 @@ using FileTime.Providers.Local;
using FileTime.Server.App; using FileTime.Server.App;
using FileTime.Server.Common; using FileTime.Server.Common;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Serilog; using Serilog;
@@ -52,7 +53,8 @@ IContainer CreateRootDiContainer(IConfigurationRoot configuration)
var serviceCollection = DependencyInjection var serviceCollection = DependencyInjection
.RegisterDefaultServices(configuration) .RegisterDefaultServices(configuration)
.AddLocalProviderServices() .AddLocalProviderServices()
.AddServerServices(); .AddServerServices()
.AddLogging(loggingBuilder => loggingBuilder.AddSerilog());
serviceCollection.TryAddSingleton<IApplicationStopper>( serviceCollection.TryAddSingleton<IApplicationStopper>(
new ApplicationStopper(() => applicationCancellation.Cancel()) new ApplicationStopper(() => applicationCancellation.Cancel())