diff --git a/src/Tgstation.Server.Api/Models/ConfigurationFile.cs b/src/Tgstation.Server.Api/Models/ConfigurationFile.cs index a7e7aad737..1c27df900c 100644 --- a/src/Tgstation.Server.Api/Models/ConfigurationFile.cs +++ b/src/Tgstation.Server.Api/Models/ConfigurationFile.cs @@ -16,25 +16,25 @@ namespace Tgstation.Server.Api.Models public string Path { get; set; } /// - /// If read access to the file was denied + /// If access to the file was denied for the operation /// [Permissions(DenyWrite = true)] - public bool? ReadDenied { get; set; } + public bool? AccessDenied { get; set; } /// - /// If represents a directory. Will only be if is + /// If represents a directory /// [Permissions(DenyWrite = true)] public bool? IsDirectory { get; set; } /// - /// The MD5 hash of the file when last read by the user. Will be if is . If this doesn't match during update actions, the write will be denied with error code 409 + /// The MD5 hash of the file when last read by the user. If this doesn't match during update actions, the write will be denied with /// [Permissions(DenyWrite = true)] public string LastReadHash { get; set; } /// - /// The content of the . Will be if is or during listing operations + /// The content of the . Will be if is or during listing and write operations /// public byte[] Content { get; set; } } diff --git a/src/Tgstation.Server.Host/Components/Configuration.cs b/src/Tgstation.Server.Host/Components/Configuration.cs index 1ab7412e7d..d6de13df5c 100644 --- a/src/Tgstation.Server.Host/Components/Configuration.cs +++ b/src/Tgstation.Server.Host/Components/Configuration.cs @@ -2,7 +2,9 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; +using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Models; @@ -82,13 +84,17 @@ namespace Tgstation.Server.Host.Components return new ServerSideModifications(headFileExistsTask.Result ? IncludeLine(CodeModificationsHeadFile) : null, tailFileExistsTask.Result ? IncludeLine(CodeModificationsTailFile) : null, false); } - /// - public async Task> ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken) + string ValidateConfigRelativePath(string configurationRelativePath) { if (String.IsNullOrEmpty(configurationRelativePath)) configurationRelativePath = "."; + return ioManager.ResolvePath(configurationRelativePath); + } - var path = ioManager.ResolvePath(configurationRelativePath); + /// + public async Task> ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken) + { + var path = ValidateConfigRelativePath(configurationRelativePath); List result = new List(); @@ -98,13 +104,13 @@ namespace Tgstation.Server.Host.Components result.AddRange(enumerator.Select(x => new ConfigurationFile { IsDirectory = true, - Path = ioManager.ConcatPath(path, x), + Path = ioManager.ConcatPath(configurationRelativePath, x), })); enumerator = synchronousIOManager.GetFiles(configurationRelativePath, cancellationToken); result.AddRange(enumerator.Select(x => new ConfigurationFile { IsDirectory = false, - Path = ioManager.ConcatPath(path, x), + Path = ioManager.ConcatPath(configurationRelativePath, x), })); } @@ -117,9 +123,49 @@ namespace Tgstation.Server.Host.Components } /// - public Task Read(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken) + public async Task Read(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken) { - throw new NotImplementedException(); + var path = ValidateConfigRelativePath(configurationRelativePath); + + ConfigurationFile result = null; + + void ReadImpl() + { + try + { + var content = synchronousIOManager.ReadFile(path); + string sha1String; +#pragma warning disable CA5350 // Do not use insecure cryptographic algorithm SHA1. + using (var sha1 = new SHA1Managed()) +#pragma warning restore CA5350 // Do not use insecure cryptographic algorithm SHA1. + sha1String = String.Join("", sha1.ComputeHash(content).Select(b => b.ToString("x2", CultureInfo.InvariantCulture))); + result = new ConfigurationFile + { + Content = content, + IsDirectory = false, + LastReadHash = sha1String, + AccessDenied = false, + Path = configurationRelativePath + }; + } + catch (FileNotFoundException) { } + catch (DirectoryNotFoundException) { } + catch (UnauthorizedAccessException) + { + result = new ConfigurationFile + { + AccessDenied = true, + Path = configurationRelativePath + }; + } + } + + if (systemIdentity == null) + ReadImpl(); + else + await systemIdentity.RunImpersonated(ReadImpl, cancellationToken).ConfigureAwait(false); + + return result; } /// diff --git a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs index eb9ed1ea7b..bbdfd048b7 100644 --- a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs +++ b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs @@ -69,10 +69,6 @@ namespace Tgstation.Server.Host.Controllers { return StatusCode((int)HttpStatusCode.NotImplemented, new { message = e.Message }); } - catch (UnauthorizedAccessException) - { - return Forbid(); - } } /// @@ -126,6 +122,10 @@ namespace Tgstation.Server.Host.Controllers { return StatusCode((int)HttpStatusCode.NotImplemented, new { message = e.Message }); } + catch (UnauthorizedAccessException) + { + return Forbid(); + } } } }