mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-29 08:00:19 +01:00
Test and fix configuration
This commit is contained in:
@@ -137,9 +137,14 @@ namespace Tgstation.Server.Host.Components.StaticFiles
|
||||
|
||||
string ValidateConfigRelativePath(string configurationRelativePath)
|
||||
{
|
||||
if (String.IsNullOrEmpty(configurationRelativePath))
|
||||
var nullOrEmptyCheck = String.IsNullOrEmpty(configurationRelativePath);
|
||||
if (nullOrEmptyCheck)
|
||||
configurationRelativePath = ".";
|
||||
return ioManager.ResolvePath(configurationRelativePath);
|
||||
var resolved = ioManager.ResolvePath(configurationRelativePath);
|
||||
var local = !nullOrEmptyCheck ? ioManager.ResolvePath(".") : null;
|
||||
if (!nullOrEmptyCheck && resolved.Length < local.Length) //.. fuccbois
|
||||
throw new InvalidOperationException("Attempted to access file outside of configuration manager!");
|
||||
return resolved;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -152,17 +157,30 @@ namespace Tgstation.Server.Host.Components.StaticFiles
|
||||
|
||||
void ListImpl()
|
||||
{
|
||||
var enumerator = synchronousIOManager.GetDirectories(configurationRelativePath, cancellationToken);
|
||||
result.AddRange(enumerator.Select(x => new ConfigurationFile
|
||||
var enumerator = synchronousIOManager.GetDirectories(path, cancellationToken);
|
||||
try
|
||||
{
|
||||
IsDirectory = true,
|
||||
Path = ioManager.ConcatPath(configurationRelativePath, x),
|
||||
}));
|
||||
enumerator = synchronousIOManager.GetFiles(configurationRelativePath, cancellationToken);
|
||||
result.AddRange(enumerator.Select(x => new ConfigurationFile
|
||||
{
|
||||
IsDirectory = true,
|
||||
Path = ioManager.ConcatPath(path, x),
|
||||
}));
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
result = null;
|
||||
return;
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
result = null;
|
||||
return;
|
||||
}
|
||||
enumerator = synchronousIOManager.GetFiles(path, cancellationToken);
|
||||
result.AddRange(enumerator.Select(x => new ConfigurationFile
|
||||
{
|
||||
IsDirectory = false,
|
||||
Path = ioManager.ConcatPath(configurationRelativePath, x),
|
||||
Path = ioManager.ConcatPath(path, x),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -201,15 +219,31 @@ namespace Tgstation.Server.Host.Components.StaticFiles
|
||||
Path = configurationRelativePath
|
||||
};
|
||||
}
|
||||
catch (FileNotFoundException) { }
|
||||
catch (DirectoryNotFoundException) { }
|
||||
catch (IOException e)
|
||||
{
|
||||
logger.LogWarning("IOException while reading {0}: {1}", path, e);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
//this happens on windows, dunno about linux
|
||||
bool isDirectory;
|
||||
try
|
||||
{
|
||||
isDirectory = synchronousIOManager.IsDirectory(path);
|
||||
}
|
||||
catch
|
||||
{
|
||||
isDirectory = false;
|
||||
}
|
||||
|
||||
result = new ConfigurationFile
|
||||
{
|
||||
AccessDenied = true,
|
||||
Path = configurationRelativePath
|
||||
};
|
||||
if (!isDirectory)
|
||||
result.AccessDenied = true;
|
||||
else
|
||||
result.IsDirectory = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,13 +300,15 @@ namespace Tgstation.Server.Host.Components.StaticFiles
|
||||
var success = synchronousIOManager.WriteFileChecked(path, data, previousHash, cancellationToken);
|
||||
if (!success)
|
||||
return;
|
||||
string sha1String = null;
|
||||
if (data != null)
|
||||
{
|
||||
postWriteHandler.HandleWrite(path);
|
||||
string sha1String;
|
||||
#pragma warning disable CA5350 // Do not use insecure cryptographic algorithm SHA1.
|
||||
using (var sha1 = new SHA1Managed())
|
||||
using (var sha1 = new SHA1Managed())
|
||||
#pragma warning restore CA5350 // Do not use insecure cryptographic algorithm SHA1.
|
||||
sha1String = String.Join("", sha1.ComputeHash(data).Select(b => b.ToString("x2", CultureInfo.InvariantCulture)));
|
||||
sha1String = String.Join("", sha1.ComputeHash(data).Select(b => b.ToString("x2", CultureInfo.InvariantCulture)));
|
||||
}
|
||||
result = new ConfigurationFile
|
||||
{
|
||||
Content = data,
|
||||
@@ -282,15 +318,31 @@ namespace Tgstation.Server.Host.Components.StaticFiles
|
||||
Path = configurationRelativePath
|
||||
};
|
||||
}
|
||||
catch (FileNotFoundException) { }
|
||||
catch (DirectoryNotFoundException) { }
|
||||
catch (IOException e)
|
||||
{
|
||||
logger.LogWarning("IOException while writing {0}: {1}", path, e);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
//this happens on windows, dunno about linux
|
||||
bool isDirectory;
|
||||
try
|
||||
{
|
||||
isDirectory = synchronousIOManager.IsDirectory(path);
|
||||
}
|
||||
catch
|
||||
{
|
||||
isDirectory = false;
|
||||
}
|
||||
|
||||
result = new ConfigurationFile
|
||||
{
|
||||
AccessDenied = true,
|
||||
Path = configurationRelativePath
|
||||
};
|
||||
if (!isDirectory)
|
||||
result.AccessDenied = true;
|
||||
else
|
||||
result.IsDirectory = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,20 +69,20 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// <summary>
|
||||
/// Get the contents of a file at a <paramref name="path"/>
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file to get</param>
|
||||
/// <param name="filePath">The path of the file to get</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> for the operation</returns>
|
||||
[HttpGet("/File/{path}")]
|
||||
[HttpGet("File/{*filePath}")]
|
||||
[TgsAuthorize(ConfigurationRights.Read)]
|
||||
public async Task<IActionResult> File(string path, CancellationToken cancellationToken)
|
||||
public async Task<IActionResult> File(string filePath, CancellationToken cancellationToken)
|
||||
{
|
||||
if (ForbidDueToModeConflicts())
|
||||
return Forbid();
|
||||
|
||||
try
|
||||
{
|
||||
var result = await instanceManager.GetInstance(Instance).Configuration.Read(path, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false);
|
||||
if (result == null || result.IsDirectory.Value)
|
||||
var result = await instanceManager.GetInstance(Instance).Configuration.Read(filePath, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false);
|
||||
if (result == null)
|
||||
return StatusCode((int)HttpStatusCode.Gone);
|
||||
|
||||
return Json(result);
|
||||
@@ -96,19 +96,19 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// <summary>
|
||||
/// Get the contents of a directory at a <paramref name="path"/>
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the directory to get</param>
|
||||
/// <param name="directoryPath">The path of the directory to get</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the <see cref="IActionResult"/> for the operation</returns>
|
||||
[HttpGet("/List/{path}")]
|
||||
[HttpGet("List/{*directoryPath}")]
|
||||
[TgsAuthorize(ConfigurationRights.List)]
|
||||
public async Task<IActionResult> Directory(string path, CancellationToken cancellationToken)
|
||||
public async Task<IActionResult> Directory(string directoryPath, CancellationToken cancellationToken)
|
||||
{
|
||||
if (ForbidDueToModeConflicts())
|
||||
return Forbid();
|
||||
|
||||
try
|
||||
{
|
||||
var result = await instanceManager.GetInstance(Instance).Configuration.ListDirectory(path, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false);
|
||||
var result = await instanceManager.GetInstance(Instance).Configuration.ListDirectory(directoryPath, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false);
|
||||
if (result == null)
|
||||
return StatusCode((int)HttpStatusCode.Gone);
|
||||
|
||||
@@ -123,5 +123,9 @@ namespace Tgstation.Server.Host.Controllers
|
||||
return Forbid();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[TgsAuthorize(ConfigurationRights.List)]
|
||||
public override Task<IActionResult> List(CancellationToken cancellationToken) => Directory(null, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,5 +40,12 @@ namespace Tgstation.Server.Host.IO
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns><see langword="true"/> on success, <see langword="false"/> if the operation failed due to <paramref name="previousSha1"/> not matching the file's contents</returns>
|
||||
bool WriteFileChecked(string path, byte[] data, string previousSha1, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a given <paramref name="path"/> is a directory
|
||||
/// </summary>
|
||||
/// <param name="path">The path to check</param>
|
||||
/// <returns><see langword="true"/> if <paramref name="path"/> is a directory, <see langword="false"/> otherwise</returns>
|
||||
bool IsDirectory(string path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace Tgstation.Server.Host.IO
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<string> GetDirectories(string path, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
foreach (var I in Directory.EnumerateDirectories(path))
|
||||
{
|
||||
yield return I;
|
||||
@@ -25,7 +24,6 @@ namespace Tgstation.Server.Host.IO
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<string> GetFiles(string path, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
foreach (var I in Directory.EnumerateFiles(path))
|
||||
{
|
||||
yield return I;
|
||||
@@ -34,29 +32,54 @@ namespace Tgstation.Server.Host.IO
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte[] ReadFile(string path) => File.ReadAllBytes(path);
|
||||
public bool IsDirectory(string path)
|
||||
{
|
||||
if (path == null)
|
||||
throw new ArgumentNullException(nameof(path));
|
||||
return Directory.Exists(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte[] ReadFile(string path)
|
||||
{
|
||||
if (path == null)
|
||||
throw new ArgumentNullException(nameof(path));
|
||||
return File.ReadAllBytes(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool WriteFileChecked(string path, byte[] data, string previousSha1, CancellationToken cancellationToken)
|
||||
{
|
||||
if (path == null)
|
||||
throw new ArgumentNullException(nameof(path));
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
using (var file = File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
//as nice as it would be to not have to arrayify the memory stream, we have to
|
||||
//because, oddly enough sha1(memorystream) != sha1(memorystream.ToArray())
|
||||
// vOv
|
||||
byte[] originalBytes;
|
||||
using (var readMs = new MemoryStream())
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
file.CopyTo(readMs);
|
||||
if (readMs.Length != 0 && previousSha1 == null)
|
||||
return false; //no sha1? no write
|
||||
//suppressed due to only using for consistency checks
|
||||
originalBytes = readMs.ToArray();
|
||||
}
|
||||
if (originalBytes.Length != 0 && previousSha1 == null)
|
||||
//no sha1? no write
|
||||
return false;
|
||||
|
||||
//suppressed due to only using for consistency checks
|
||||
#pragma warning disable CA5350 // Do not use insecure cryptographic algorithm SHA1.
|
||||
using (var sha1 = new SHA1Managed())
|
||||
using (var sha1 = new SHA1Managed())
|
||||
#pragma warning restore CA5350 // Do not use insecure cryptographic algorithm SHA1.
|
||||
{
|
||||
var sha1String = String.Join("", sha1.ComputeHash(readMs).Select(b => b.ToString("x2", CultureInfo.InvariantCulture)));
|
||||
if (sha1String != previousSha1)
|
||||
return false;
|
||||
}
|
||||
{
|
||||
var sha1String = originalBytes.Length != 0 ? String.Join("", sha1.ComputeHash(originalBytes).Select(b => b.ToString("x2", CultureInfo.InvariantCulture))) : null;
|
||||
if (sha1String != previousSha1)
|
||||
return false;
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -66,8 +89,6 @@ namespace Tgstation.Server.Host.IO
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
file.SetLength(data.Length);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
file.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user