Test and fix configuration

This commit is contained in:
Cyberboss
2018-08-14 15:33:37 -04:00
parent 7a5eb50227
commit 1c33691ee6
4 changed files with 126 additions and 42 deletions
@@ -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);
}
}