mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-22 20:47:28 +01:00
Merge branch 'PrivateKeySharing' into MoreGraphQL
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
<TgsDmapiVersion>7.3.0</TgsDmapiVersion>
|
||||
<TgsInteropVersion>5.10.0</TgsInteropVersion>
|
||||
<TgsHostWatchdogVersion>1.5.0</TgsHostWatchdogVersion>
|
||||
<TgsSwarmProtocolVersion>7.0.0</TgsSwarmProtocolVersion>
|
||||
<TgsContainerScriptVersion>1.2.1</TgsContainerScriptVersion>
|
||||
<TgsMigratorVersion>2.0.0</TgsMigratorVersion>
|
||||
<TgsNugetNetFramework>netstandard2.0</TgsNugetNetFramework>
|
||||
|
||||
@@ -14,8 +14,8 @@ using Serilog.Context;
|
||||
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.Extensions;
|
||||
using Tgstation.Server.Host.Properties;
|
||||
using Tgstation.Server.Host.Swarm;
|
||||
using Tgstation.Server.Host.System;
|
||||
using Tgstation.Server.Host.Transfer;
|
||||
using Tgstation.Server.Host.Utils;
|
||||
|
||||
@@ -44,11 +44,6 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// </summary>
|
||||
readonly IFileTransferStreamHandler transferService;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IAssemblyInformationProvider"/> for the <see cref="SwarmController"/>.
|
||||
/// </summary>
|
||||
readonly IAssemblyInformationProvider assemblyInformationProvider;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ILogger"/> for the <see cref="SwarmController"/>.
|
||||
/// </summary>
|
||||
@@ -63,19 +58,16 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// Initializes a new instance of the <see cref="SwarmController"/> class.
|
||||
/// </summary>
|
||||
/// <param name="swarmOperations">The value of <see cref="swarmOperations"/>.</param>
|
||||
/// <param name="assemblyInformationProvider">The value of <see cref="assemblyInformationProvider"/>.</param>
|
||||
/// <param name="transferService">The value of <see cref="transferService"/>.</param>
|
||||
/// <param name="swarmConfigurationOptions">The <see cref="IOptions{TOptions}"/> containing the value of <see cref="swarmConfiguration"/>.</param>
|
||||
/// <param name="logger">The value of <see cref="logger"/>.</param>
|
||||
public SwarmController(
|
||||
ISwarmOperations swarmOperations,
|
||||
IAssemblyInformationProvider assemblyInformationProvider,
|
||||
IFileTransferStreamHandler transferService,
|
||||
IOptions<SwarmConfiguration> swarmConfigurationOptions,
|
||||
ILogger<SwarmController> logger)
|
||||
{
|
||||
this.swarmOperations = swarmOperations ?? throw new ArgumentNullException(nameof(swarmOperations));
|
||||
this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
|
||||
this.transferService = transferService ?? throw new ArgumentNullException(nameof(transferService));
|
||||
swarmConfiguration = swarmConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(swarmConfigurationOptions));
|
||||
this.logger = logger;
|
||||
@@ -92,13 +84,18 @@ namespace Tgstation.Server.Host.Controllers
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(registrationRequest);
|
||||
|
||||
if (registrationRequest.ServerVersion != assemblyInformationProvider.Version)
|
||||
var swarmProtocolVersion = Version.Parse(MasterVersionsAttribute.Instance.RawSwarmProtocolVersion);
|
||||
if (registrationRequest.ServerVersion?.Major != swarmProtocolVersion.Major)
|
||||
return StatusCode((int)HttpStatusCode.UpgradeRequired);
|
||||
|
||||
var registrationResult = await swarmOperations.RegisterNode(registrationRequest, RequestRegistrationId, cancellationToken);
|
||||
if (!registrationResult)
|
||||
if (registrationResult == null)
|
||||
return Conflict();
|
||||
return NoContent();
|
||||
|
||||
if (registrationRequest.ServerVersion != swarmProtocolVersion)
|
||||
logger.LogWarning("Allowed node {identifier} to register despite having a slightly different swarm protocol version!", registrationRequest.Identifier);
|
||||
|
||||
return Json(registrationResult);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -445,6 +445,7 @@ namespace Tgstation.Server.Host.Database
|
||||
// HEY YOU
|
||||
// IF YOU HAVE A TEST THAT'S CREATING ERRORS BECAUSE THESE VALUES AREN'T SET CORRECTLY THERE'S MORE TO FIXING IT THAN JUST UPDATING THEM
|
||||
// IN THE FUNCTION BELOW YOU ALSO NEED TO CORRECTLY SET THE RIGHT MIGRATION TO DOWNGRADE TO FOR THE LAST TGS VERSION
|
||||
// YOU ALSO NEED TO UPDATE THE SWARM PROTOCOL MAJOR VERSION
|
||||
// IF THIS BREAKS AGAIN I WILL PERSONALLY HAUNT YOUR ASS WHEN I DIE
|
||||
|
||||
/// <summary>
|
||||
@@ -480,6 +481,7 @@ namespace Tgstation.Server.Host.Database
|
||||
|
||||
string BadDatabaseType() => throw new ArgumentException($"Invalid DatabaseType: {currentDatabaseType}", nameof(currentDatabaseType));
|
||||
|
||||
// !!! DON'T FORGET TO UPDATE THE SWARM PROTOCOL MAJOR VERSION !!!
|
||||
if (targetVersion < new Version(6, 7, 0))
|
||||
targetMigration = currentDatabaseType switch
|
||||
{
|
||||
|
||||
@@ -41,6 +41,11 @@ namespace Tgstation.Server.Host.Properties
|
||||
/// </summary>
|
||||
public string RawMariaDBRedistVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Version"/> <see cref="string"/> of the MariaDB server bundled with TGS installs.
|
||||
/// </summary>
|
||||
public string RawSwarmProtocolVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MasterVersionsAttribute"/> class.
|
||||
/// </summary>
|
||||
@@ -49,18 +54,21 @@ namespace Tgstation.Server.Host.Properties
|
||||
/// <param name="rawWebpanelVersion">The value of <see cref="RawWebpanelVersion"/>.</param>
|
||||
/// <param name="rawHostWatchdogVersion">The value of <see cref="RawHostWatchdogVersion"/>.</param>
|
||||
/// <param name="rawMariaDBRedistVersion">The value of <see cref="RawMariaDBRedistVersion"/>.</param>
|
||||
/// <param name="rawSwarmProtocolVersion">The value of <see cref="RawSwarmProtocolVersion"/>.</param>
|
||||
public MasterVersionsAttribute(
|
||||
string rawConfigurationVersion,
|
||||
string rawInteropVersion,
|
||||
string rawWebpanelVersion,
|
||||
string rawHostWatchdogVersion,
|
||||
string rawMariaDBRedistVersion)
|
||||
string rawMariaDBRedistVersion,
|
||||
string rawSwarmProtocolVersion)
|
||||
{
|
||||
RawConfigurationVersion = rawConfigurationVersion ?? throw new ArgumentNullException(nameof(rawConfigurationVersion));
|
||||
RawInteropVersion = rawInteropVersion ?? throw new ArgumentNullException(nameof(rawInteropVersion));
|
||||
RawWebpanelVersion = rawWebpanelVersion ?? throw new ArgumentNullException(nameof(rawWebpanelVersion));
|
||||
RawHostWatchdogVersion = rawHostWatchdogVersion ?? throw new ArgumentNullException(nameof(rawHostWatchdogVersion));
|
||||
RawMariaDBRedistVersion = rawMariaDBRedistVersion ?? throw new ArgumentNullException(nameof(rawMariaDBRedistVersion));
|
||||
RawSwarmProtocolVersion = rawSwarmProtocolVersion ?? throw new ArgumentNullException(nameof(rawSwarmProtocolVersion));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System;
|
||||
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
using Tgstation.Server.Api.Models.Response;
|
||||
|
||||
@@ -9,6 +11,11 @@ namespace Tgstation.Server.Host.Security
|
||||
/// </summary>
|
||||
public interface ITokenFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ITokenFactory"/>'s signing key <see cref="byte"/>s.
|
||||
/// </summary>
|
||||
ReadOnlySpan<byte> SigningKeyBytes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="TokenValidationParameters"/> for the <see cref="ITokenFactory"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Linq;
|
||||
@@ -21,21 +22,42 @@ namespace Tgstation.Server.Host.Security
|
||||
/// <inheritdoc />
|
||||
public TokenValidationParameters ValidationParameters { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public ReadOnlySpan<byte> SigningKeyBytes
|
||||
{
|
||||
get => signingKey.Key;
|
||||
[MemberNotNull(nameof(signingKey))]
|
||||
[MemberNotNull(nameof(tokenHeader))]
|
||||
set
|
||||
{
|
||||
signingKey = new SymmetricSecurityKey(value.ToArray());
|
||||
tokenHeader = new JwtHeader(
|
||||
new SigningCredentials(
|
||||
signingKey,
|
||||
SecurityAlgorithms.HmacSha256));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="SecurityConfiguration"/> for the <see cref="TokenFactory"/>.
|
||||
/// </summary>
|
||||
readonly SecurityConfiguration securityConfiguration;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="JwtHeader"/> for generating tokens.
|
||||
/// </summary>
|
||||
readonly JwtHeader tokenHeader;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="JwtSecurityTokenHandler"/> used to generate <see cref="TokenResponse.Bearer"/> <see cref="string"/>s.
|
||||
/// </summary>
|
||||
readonly JwtSecurityTokenHandler tokenHandler;
|
||||
|
||||
/// <summary>
|
||||
/// Backing field for <see cref="SigningKeyBytes"/>.
|
||||
/// </summary>
|
||||
SymmetricSecurityKey signingKey;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="JwtHeader"/> for generating tokens.
|
||||
/// </summary>
|
||||
JwtHeader tokenHeader;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TokenFactory"/> class.
|
||||
/// </summary>
|
||||
@@ -52,14 +74,14 @@ namespace Tgstation.Server.Host.Security
|
||||
|
||||
securityConfiguration = securityConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(securityConfigurationOptions));
|
||||
|
||||
var signingKeyBytes = String.IsNullOrWhiteSpace(securityConfiguration.CustomTokenSigningKeyBase64)
|
||||
SigningKeyBytes = String.IsNullOrWhiteSpace(securityConfiguration.CustomTokenSigningKeyBase64)
|
||||
? cryptographySuite.GetSecureBytes(securityConfiguration.TokenSigningKeyByteCount)
|
||||
: Convert.FromBase64String(securityConfiguration.CustomTokenSigningKeyBase64);
|
||||
|
||||
ValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(signingKeyBytes),
|
||||
IssuerSigningKeyResolver = (_, _, _, _) => Enumerable.Repeat(signingKey, 1),
|
||||
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = assemblyInformationProvider.AssemblyName.Name,
|
||||
@@ -75,10 +97,6 @@ namespace Tgstation.Server.Host.Security
|
||||
RequireExpirationTime = true,
|
||||
};
|
||||
|
||||
tokenHeader = new JwtHeader(
|
||||
new SigningCredentials(
|
||||
ValidationParameters.IssuerSigningKey,
|
||||
SecurityAlgorithms.HmacSha256));
|
||||
tokenHandler = new JwtSecurityTokenHandler();
|
||||
}
|
||||
|
||||
|
||||
@@ -39,8 +39,8 @@ namespace Tgstation.Server.Host.Swarm
|
||||
/// <param name="node">The <see cref="SwarmServer"/> that is registering.</param>
|
||||
/// <param name="registrationId">The registration <see cref="Guid"/>.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in <see langword="true"/> if the registration was successful, <see langword="false"/> otherwise.</returns>
|
||||
ValueTask<bool> RegisterNode(SwarmServer node, Guid registrationId, CancellationToken cancellationToken);
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in a <see cref="SwarmRegistrationResponse"/> if the registration was successful, <see langword="null"/> otherwise.</returns>
|
||||
ValueTask<SwarmRegistrationResponse?> RegisterNode(SwarmServer node, Guid registrationId, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to unregister a node with a given <paramref name="registrationId"/> with the controller.
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Tgstation.Server.Host.Swarm
|
||||
public sealed class SwarmRegistrationRequest : SwarmServer
|
||||
{
|
||||
/// <summary>
|
||||
/// The TGS <see cref="Version"/> of the sending server.
|
||||
/// The swarm protocol <see cref="Version"/> of the sending server. Named this way due to legacy reasons.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Version ServerVersion { get; }
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Tgstation.Server.Host.Swarm
|
||||
{
|
||||
/// <summary>
|
||||
/// Response for a <see cref="SwarmRegistrationRequest"/>.
|
||||
/// </summary>
|
||||
public sealed class SwarmRegistrationResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// The base64 encoded token signing key.
|
||||
/// </summary>
|
||||
public required string TokenSigningKeyBase64 { get; init; }
|
||||
}
|
||||
}
|
||||
@@ -24,5 +24,10 @@
|
||||
/// A communication error occurred.
|
||||
/// </summary>
|
||||
CommunicationFailure,
|
||||
|
||||
/// <summary>
|
||||
/// Response could not be deserialized.
|
||||
/// </summary>
|
||||
PayloadFailure,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.Core;
|
||||
using Tgstation.Server.Host.Database;
|
||||
using Tgstation.Server.Host.IO;
|
||||
using Tgstation.Server.Host.Properties;
|
||||
using Tgstation.Server.Host.Security;
|
||||
using Tgstation.Server.Host.System;
|
||||
using Tgstation.Server.Host.Transfer;
|
||||
using Tgstation.Server.Host.Utils;
|
||||
@@ -33,7 +35,9 @@ namespace Tgstation.Server.Host.Swarm
|
||||
/// <summary>
|
||||
/// Helps keep servers connected to the same database in sync by coordinating updates.
|
||||
/// </summary>
|
||||
#pragma warning disable CA1506 // TODO: Decomplexify
|
||||
sealed class SwarmService : ISwarmService, ISwarmServiceController, ISwarmOperations, IDisposable
|
||||
#pragma warning restore CA1506
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public bool ExpectedNumberOfNodesConnected
|
||||
@@ -89,6 +93,11 @@ namespace Tgstation.Server.Host.Swarm
|
||||
/// </summary>
|
||||
readonly IFileTransferTicketProvider transferService;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ITokenFactory"/> for the <see cref="SwarmService"/>.
|
||||
/// </summary>
|
||||
readonly ITokenFactory tokenFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ILogger"/> for the <see cref="SwarmService"/>.
|
||||
/// </summary>
|
||||
@@ -159,6 +168,7 @@ namespace Tgstation.Server.Host.Swarm
|
||||
/// <param name="serverUpdater">The value of <see cref="serverUpdater"/>.</param>
|
||||
/// <param name="asyncDelayer">The value of <see cref="asyncDelayer"/>.</param>
|
||||
/// <param name="transferService">The value of <see cref="transferService"/>.</param>
|
||||
/// <param name="tokenFactory">The value of <see cref="tokenFactory"/>.</param>
|
||||
/// <param name="swarmConfigurationOptions">The <see cref="IOptions{TOptions}"/> containing the value of <see cref="swarmConfiguration"/>.</param>
|
||||
/// <param name="logger">The value of <see cref="logger"/>.</param>
|
||||
public SwarmService(
|
||||
@@ -169,6 +179,7 @@ namespace Tgstation.Server.Host.Swarm
|
||||
IAsyncDelayer asyncDelayer,
|
||||
IServerUpdater serverUpdater,
|
||||
IFileTransferTicketProvider transferService,
|
||||
ITokenFactory tokenFactory,
|
||||
IOptions<SwarmConfiguration> swarmConfigurationOptions,
|
||||
ILogger<SwarmService> logger)
|
||||
{
|
||||
@@ -179,6 +190,7 @@ namespace Tgstation.Server.Host.Swarm
|
||||
this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
|
||||
this.serverUpdater = serverUpdater ?? throw new ArgumentNullException(nameof(serverUpdater));
|
||||
this.transferService = transferService ?? throw new ArgumentNullException(nameof(transferService));
|
||||
this.tokenFactory = tokenFactory ?? throw new ArgumentNullException(nameof(tokenFactory));
|
||||
swarmConfiguration = swarmConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(swarmConfigurationOptions));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
@@ -540,7 +552,7 @@ namespace Tgstation.Server.Host.Swarm
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask<bool> RegisterNode(SwarmServer node, Guid registrationId, CancellationToken cancellationToken)
|
||||
public async ValueTask<SwarmRegistrationResponse?> RegisterNode(SwarmServer node, Guid registrationId, CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(node);
|
||||
|
||||
@@ -560,6 +572,11 @@ namespace Tgstation.Server.Host.Swarm
|
||||
|
||||
await AbortUpdate();
|
||||
|
||||
SwarmRegistrationResponse CreateResponse() => new()
|
||||
{
|
||||
TokenSigningKeyBase64 = Convert.ToBase64String(tokenFactory.SigningKeyBytes),
|
||||
};
|
||||
|
||||
var registrationIdsAndTimes = this.registrationIdsAndTimes!;
|
||||
lock (swarmServers)
|
||||
{
|
||||
@@ -569,7 +586,7 @@ namespace Tgstation.Server.Host.Swarm
|
||||
if (preExistingRegistrationKvp.Key == node.Identifier)
|
||||
{
|
||||
logger.LogWarning("Node {nodeId} has already registered!", node.Identifier);
|
||||
return true;
|
||||
return CreateResponse();
|
||||
}
|
||||
|
||||
logger.LogWarning(
|
||||
@@ -577,7 +594,7 @@ namespace Tgstation.Server.Host.Swarm
|
||||
node.Identifier,
|
||||
preExistingRegistrationKvp.Key,
|
||||
registrationId);
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (registrationIdsAndTimes.TryGetValue(node.Identifier, out var oldRegistration))
|
||||
@@ -599,7 +616,7 @@ namespace Tgstation.Server.Host.Swarm
|
||||
|
||||
logger.LogInformation("Registered node {nodeId} ({nodeIP}) with ID {registrationId}", node.Identifier, node.Address, registrationId);
|
||||
MarkServersDirty();
|
||||
return true;
|
||||
return CreateResponse();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -1268,7 +1285,7 @@ namespace Tgstation.Server.Host.Swarm
|
||||
null,
|
||||
HttpMethod.Post,
|
||||
SwarmConstants.RegisterRoute,
|
||||
new SwarmRegistrationRequest(assemblyInformationProvider.Version)
|
||||
new SwarmRegistrationRequest(Version.Parse(MasterVersionsAttribute.Instance.RawSwarmProtocolVersion))
|
||||
{
|
||||
Identifier = swarmConfiguration.Identifier,
|
||||
Address = swarmConfiguration.Address,
|
||||
@@ -1281,6 +1298,36 @@ namespace Tgstation.Server.Host.Swarm
|
||||
using var response = await httpClient.SendAsync(registrationRequest, HttpCompletionOption.ResponseContentRead, cancellationToken);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
if (json == null)
|
||||
{
|
||||
logger.LogDebug("Error reading registration response content stream! Text was null!");
|
||||
return SwarmRegistrationResult.PayloadFailure;
|
||||
}
|
||||
|
||||
var registrationResponse = JsonConvert.DeserializeObject<SwarmRegistrationResponse>(json);
|
||||
if (registrationResponse == null)
|
||||
{
|
||||
logger.LogDebug("Error reading registration response content stream! Payload was null!");
|
||||
return SwarmRegistrationResult.PayloadFailure;
|
||||
}
|
||||
|
||||
if (registrationResponse.TokenSigningKeyBase64 == null)
|
||||
{
|
||||
logger.LogDebug("Error reading registration response content stream! SigningKey was null!");
|
||||
return SwarmRegistrationResult.PayloadFailure;
|
||||
}
|
||||
|
||||
tokenFactory.SigningKeyBytes = Convert.FromBase64String(registrationResponse.TokenSigningKeyBase64);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogDebug(ex, "Error reading registration response content stream!");
|
||||
return SwarmRegistrationResult.PayloadFailure;
|
||||
}
|
||||
|
||||
logger.LogInformation("Sucessfully registered with ID {registrationId}", requestedRegistrationId);
|
||||
controllerRegistration = requestedRegistrationId;
|
||||
lastControllerHealthCheck = DateTimeOffset.UtcNow;
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
<_Parameter3>$(TgsWebpanelVersion)</_Parameter3>
|
||||
<_Parameter4>$(TgsHostWatchdogVersion)</_Parameter4>
|
||||
<_Parameter5>$(TgsMariaDBRedistVersion)</_Parameter5>
|
||||
<_Parameter6>$(TgsSwarmProtocolVersion)</_Parameter6>
|
||||
</MasterVersionAssemblyAttributes>
|
||||
</ItemGroup>
|
||||
<WriteCodeFragment AssemblyAttributes="@(MasterVersionAssemblyAttributes)" Language="C#" OutputDirectory="$(IntermediateOutputPath)" OutputFile="MasterVersionsAssemblyInfo.cs">
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using Moq;
|
||||
@@ -20,6 +21,7 @@ using Tgstation.Server.Host.Controllers;
|
||||
using Tgstation.Server.Host.Core;
|
||||
using Tgstation.Server.Host.Database;
|
||||
using Tgstation.Server.Host.IO;
|
||||
using Tgstation.Server.Host.Models;
|
||||
using Tgstation.Server.Host.Security;
|
||||
using Tgstation.Server.Host.System;
|
||||
using Tgstation.Server.Host.Transfer;
|
||||
@@ -75,6 +77,24 @@ namespace Tgstation.Server.Host.Swarm.Tests
|
||||
}
|
||||
}
|
||||
|
||||
private class MockTokenFactory : ITokenFactory
|
||||
{
|
||||
public ReadOnlySpan<byte> SigningKeyBytes
|
||||
{
|
||||
get => [0, 1, 2, 3, 4];
|
||||
set
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public TokenValidationParameters ValidationParameters => throw new NotSupportedException();
|
||||
|
||||
public TokenResponse CreateToken(User user, bool oAuth)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
public TestableSwarmNode(
|
||||
ILoggerFactory loggerFactory,
|
||||
SwarmConfiguration swarmConfiguration,
|
||||
@@ -138,7 +158,6 @@ namespace Tgstation.Server.Host.Swarm.Tests
|
||||
RpcMapper = new SwarmRpcMapper(
|
||||
(targetService, targetTransfer) => new SwarmController(
|
||||
targetService,
|
||||
mockAssemblyInformationProvider.Object,
|
||||
targetTransfer,
|
||||
mockOptions.Object,
|
||||
loggerFactory.CreateLogger<SwarmController>()),
|
||||
@@ -154,6 +173,8 @@ namespace Tgstation.Server.Host.Swarm.Tests
|
||||
|
||||
logger = loggerFactory.CreateLogger($"TestableSwarmNode-{swarmConfiguration.Identifier}");
|
||||
|
||||
var mockTokenFactory = new MockTokenFactory();
|
||||
|
||||
var runCount = 0;
|
||||
void RecreateControllerAndService()
|
||||
{
|
||||
@@ -180,6 +201,7 @@ namespace Tgstation.Server.Host.Swarm.Tests
|
||||
mockAsyncDelayer.Object,
|
||||
mockServerUpdater.Object,
|
||||
TransferService,
|
||||
mockTokenFactory,
|
||||
mockOptions.Object,
|
||||
serviceLogger);
|
||||
}
|
||||
|
||||
@@ -670,8 +670,8 @@ namespace Tgstation.Server.Tests.Live
|
||||
newUser.Name,
|
||||
"asdfasdfasdfasdf");
|
||||
|
||||
await using var node1BadClient = clientFactory.CreateFromToken(node1.RootUrl, controllerUserClient.Token);
|
||||
await ApiAssert.ThrowsException<UnauthorizedException, AdministrationResponse>(() => node1BadClient.Administration.Read(false, cancellationToken));
|
||||
await using var node1TokenCopiedClient = clientFactory.CreateFromToken(node1.RootUrl, controllerUserClient.Token);
|
||||
await node1TokenCopiedClient.Administration.Read(false, cancellationToken);
|
||||
|
||||
// check instance info is not shared
|
||||
var controllerInstance = await controllerClient.Instances.CreateOrAttach(
|
||||
|
||||
Reference in New Issue
Block a user