mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-30 16:39:21 +01:00
Fix some multi-query issues. Remove unused dependencies
This commit is contained in:
@@ -174,6 +174,8 @@ namespace Tgstation.Server.Host.Controllers
|
||||
|
||||
// Validate it's not a child of any other instance
|
||||
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
|
||||
{
|
||||
var newCancellationToken = cts.Token;
|
||||
try
|
||||
{
|
||||
await DatabaseContext.Instances.ForEachAsync(
|
||||
@@ -184,16 +186,17 @@ namespace Tgstation.Server.Host.Controllers
|
||||
else
|
||||
earlyOut = CheckInstanceNotChildOf(otherInstance.Path);
|
||||
|
||||
if (earlyOut != null)
|
||||
if (earlyOut != null && !newCancellationToken.IsCancellationRequested)
|
||||
cts.Cancel();
|
||||
},
|
||||
cts.Token)
|
||||
newCancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
}
|
||||
|
||||
if (earlyOut != null)
|
||||
return earlyOut;
|
||||
@@ -353,9 +356,9 @@ namespace Tgstation.Server.Host.Controllers
|
||||
if (model == null)
|
||||
throw new ArgumentNullException(nameof(model));
|
||||
|
||||
var instanceQuery = DatabaseContext.Instances.Where(x => x.Id == model.Id);
|
||||
IQueryable<Models.Instance> InstanceQuery() => DatabaseContext.Instances.Where(x => x.Id == model.Id);
|
||||
|
||||
var moveJob = await instanceQuery
|
||||
var moveJob = await InstanceQuery()
|
||||
.SelectMany(x => x.Jobs).
|
||||
#pragma warning disable CA1307 // Specify StringComparison
|
||||
Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix))
|
||||
@@ -368,9 +371,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
if (moveJob != default)
|
||||
await jobManager.CancelJob(moveJob, AuthenticationContext.User, true, cancellationToken).ConfigureAwait(false); // cancel it now
|
||||
|
||||
var usersInstanceUserTask = instanceQuery.SelectMany(x => x.InstanceUsers).Where(x => x.UserId == AuthenticationContext.User.Id).FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
var originalModel = await instanceQuery
|
||||
var originalModel = await InstanceQuery()
|
||||
.Include(x => x.RepositorySettings)
|
||||
.Include(x => x.ChatSettings)
|
||||
.ThenInclude(x => x.Channels)
|
||||
@@ -428,7 +429,11 @@ namespace Tgstation.Server.Host.Controllers
|
||||
return Forbid();
|
||||
|
||||
// ensure the current user has write privilege on the instance
|
||||
var usersInstanceUser = await usersInstanceUserTask.ConfigureAwait(false);
|
||||
var usersInstanceUser = await InstanceQuery()
|
||||
.SelectMany(x => x.InstanceUsers)
|
||||
.Where(x => x.UserId == AuthenticationContext.User.Id)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (usersInstanceUser == default)
|
||||
{
|
||||
var instanceAdminUser = InstanceAdminUser();
|
||||
@@ -541,20 +546,17 @@ namespace Tgstation.Server.Host.Controllers
|
||||
[ProducesResponseType(410)]
|
||||
public async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = DatabaseContext.Instances.Where(x => x.Id == id);
|
||||
var cantList = !AuthenticationContext.User.InstanceManagerRights.Value.HasFlag(InstanceManagerRights.List);
|
||||
IQueryable<Models.Instance> QueryForUser()
|
||||
{
|
||||
var query = DatabaseContext.Instances.Where(x => x.Id == id);
|
||||
|
||||
if (cantList)
|
||||
query = query.Include(x => x.InstanceUsers);
|
||||
if (cantList)
|
||||
query = query.Include(x => x.InstanceUsers);
|
||||
return query;
|
||||
}
|
||||
|
||||
var moveJobTask = query
|
||||
.SelectMany(x => x.Jobs)
|
||||
#pragma warning disable CA1307 // Specify StringComparison
|
||||
.Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix))
|
||||
#pragma warning restore CA1307 // Specify StringComparison
|
||||
.Include(x => x.StartedBy).ThenInclude(x => x.CreatedBy)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
var instance = await query.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
|
||||
var instance = await QueryForUser().FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (instance == null)
|
||||
return StatusCode((int)HttpStatusCode.Gone);
|
||||
@@ -563,7 +565,16 @@ namespace Tgstation.Server.Host.Controllers
|
||||
return Forbid();
|
||||
|
||||
var api = instance.ToApi();
|
||||
api.MoveJob = (await moveJobTask.ConfigureAwait(false))?.ToApi();
|
||||
|
||||
var moveJob = await QueryForUser()
|
||||
.SelectMany(x => x.Jobs)
|
||||
#pragma warning disable CA1307 // Specify StringComparison
|
||||
.Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix))
|
||||
#pragma warning restore CA1307 // Specify StringComparison
|
||||
.Include(x => x.StartedBy).ThenInclude(x => x.CreatedBy)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
api.MoveJob = moveJob?.ToApi();
|
||||
return Json(api);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Byond.TopicSender;
|
||||
using Cyberboss.AspNetCore.AsyncInitializer;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Cors.Infrastructure;
|
||||
@@ -18,6 +19,7 @@ using Serilog.Formatting.Display;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api;
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Host.Components;
|
||||
@@ -405,6 +407,15 @@ namespace Tgstation.Server.Host.Core
|
||||
// Final point where we wrap exceptions in a 500 (ErrorMessage) response
|
||||
applicationBuilder.UseServerErrorHandling();
|
||||
|
||||
// 503 requests made while the application is starting
|
||||
applicationBuilder.UseAsyncInitialization<IHostApplicationLifetime>(async (applicationLifetime, cancellationToken) =>
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object>();
|
||||
using (cancellationToken.Register(() => tcs.SetCanceled()))
|
||||
using (applicationLifetime.ApplicationStarted.Register(() => tcs.SetResult(null)))
|
||||
await tcs.Task.ConfigureAwait(false);
|
||||
});
|
||||
|
||||
// should anything after this throw an exception, catch it and display a detailed html page
|
||||
if (hostingEnvironment.IsDevelopment())
|
||||
applicationBuilder.UseDeveloperExceptionPage(); // it is not worth it to limit this, you should only ever get it if you're an authorized user
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Host.Database;
|
||||
using Tgstation.Server.Host.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Security
|
||||
{
|
||||
@@ -14,11 +13,6 @@ namespace Tgstation.Server.Host.Security
|
||||
/// <inheritdoc />
|
||||
public IAuthenticationContext CurrentAuthenticationContext { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ISystemIdentityFactory"/> for the <see cref="AuthenticationContextFactory"/>
|
||||
/// </summary>
|
||||
readonly ISystemIdentityFactory systemIdentityFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IDatabaseContext"/> for the <see cref="AuthenticationContextFactory"/>
|
||||
/// </summary>
|
||||
@@ -32,12 +26,10 @@ namespace Tgstation.Server.Host.Security
|
||||
/// <summary>
|
||||
/// Construct an <see cref="AuthenticationContextFactory"/>
|
||||
/// </summary>
|
||||
/// <param name="systemIdentityFactory">The value of <see cref="systemIdentityFactory"/></param>
|
||||
/// <param name="databaseContext">The value of <see cref="databaseContext"/></param>
|
||||
/// <param name="identityCache">The value of <see cref="identityCache"/></param>
|
||||
public AuthenticationContextFactory(ISystemIdentityFactory systemIdentityFactory, IDatabaseContext databaseContext, IIdentityCache identityCache)
|
||||
public AuthenticationContextFactory(IDatabaseContext databaseContext, IIdentityCache identityCache)
|
||||
{
|
||||
this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory));
|
||||
this.databaseContext = databaseContext ?? throw new ArgumentNullException(nameof(databaseContext));
|
||||
this.identityCache = identityCache ?? throw new ArgumentNullException(nameof(identityCache));
|
||||
}
|
||||
@@ -51,16 +43,10 @@ namespace Tgstation.Server.Host.Security
|
||||
if (CurrentAuthenticationContext != null)
|
||||
throw new InvalidOperationException("Authentication context has already been loaded");
|
||||
|
||||
var userQuery = databaseContext.Users.Where(x => x.Id == userId)
|
||||
var user = await databaseContext.Users.Where(x => x.Id == userId)
|
||||
.Include(x => x.CreatedBy)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
var instanceUserQuery = instanceId.HasValue ? databaseContext.InstanceUsers
|
||||
.Where(x => x.UserId == userId && x.InstanceId == instanceId && x.Instance.Online.Value)
|
||||
.Include(x => x.Instance)
|
||||
.FirstOrDefaultAsync(cancellationToken) : Task.FromResult<InstanceUser>(null);
|
||||
|
||||
var user = await userQuery.ConfigureAwait(false);
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (user == default)
|
||||
{
|
||||
CurrentAuthenticationContext = new AuthenticationContext();
|
||||
@@ -83,7 +69,13 @@ namespace Tgstation.Server.Host.Security
|
||||
|
||||
try
|
||||
{
|
||||
var instanceUser = await instanceUserQuery.ConfigureAwait(false);
|
||||
var instanceUser = instanceId.HasValue
|
||||
? await databaseContext.InstanceUsers
|
||||
.Where(x => x.UserId == userId && x.InstanceId == instanceId && x.Instance.Online.Value)
|
||||
.Include(x => x.Instance)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
: null;
|
||||
|
||||
CurrentAuthenticationContext = new AuthenticationContext(systemIdentity, user, instanceUser);
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BetterWin32Errors" Version="0.2.0" />
|
||||
<PackageReference Include="Byond.TopicSender" Version="1.1.4" />
|
||||
<PackageReference Include="Cyberboss.AspNetCore.AsyncInitializer" Version="1.2.0" />
|
||||
<PackageReference Include="Cyberboss.SmartIrc4net.Standard" Version="0.4.6" />
|
||||
<PackageReference Include="Discord.Net.WebSocket" Version="2.2.0" />
|
||||
<PackageReference Include="LibGit2Sharp" Version="0.27.0-preview-0034" />
|
||||
|
||||
@@ -165,70 +165,73 @@ namespace Tgstation.Server.Tests
|
||||
[TestMethod]
|
||||
public async Task TestStandardOperation()
|
||||
{
|
||||
RequireDiscordToken();
|
||||
var server = new TestingServer(clientFactory, null);
|
||||
using (var serverCts = new CancellationTokenSource())
|
||||
while (true)
|
||||
{
|
||||
var cancellationToken = serverCts.Token;
|
||||
var serverTask = server.RunAsync(cancellationToken);
|
||||
try
|
||||
RequireDiscordToken();
|
||||
var server = new TestingServer(clientFactory, null);
|
||||
using (var serverCts = new CancellationTokenSource())
|
||||
{
|
||||
IServerClient adminClient;
|
||||
|
||||
var giveUpAt = DateTimeOffset.Now.AddSeconds(60);
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
adminClient = await clientFactory.CreateServerClient(server.Url, User.AdminName, User.DefaultAdminPassword).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
catch (HttpRequestException)
|
||||
{
|
||||
//migrating, to be expected
|
||||
if (DateTimeOffset.Now > giveUpAt)
|
||||
throw;
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
|
||||
}
|
||||
catch (ServiceUnavailableException)
|
||||
{
|
||||
// migrating, to be expected
|
||||
if (DateTimeOffset.Now > giveUpAt)
|
||||
throw;
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
|
||||
}
|
||||
} while (true);
|
||||
|
||||
using (adminClient)
|
||||
{
|
||||
var serverInfo = await adminClient.Version(default).ConfigureAwait(false);
|
||||
|
||||
Assert.AreEqual(ApiHeaders.Version, serverInfo.ApiVersion);
|
||||
Assert.AreEqual(typeof(IServer).Assembly.GetName().Version, serverInfo.Version);
|
||||
|
||||
//check that modifying the token even slightly fucks up the auth
|
||||
var newToken = new Token
|
||||
{
|
||||
ExpiresAt = adminClient.Token.ExpiresAt,
|
||||
Bearer = adminClient.Token.Bearer + '0'
|
||||
};
|
||||
|
||||
var badClient = clientFactory.CreateServerClient(server.Url, newToken);
|
||||
await Assert.ThrowsExceptionAsync<UnauthorizedException>(() => badClient.Version(cancellationToken)).ConfigureAwait(false);
|
||||
|
||||
await new AdministrationTest(adminClient.Administration).Run(cancellationToken).ConfigureAwait(false);
|
||||
await new UsersTest(adminClient.Users).Run(cancellationToken).ConfigureAwait(false);
|
||||
await new InstanceManagerTest(adminClient.Instances, server.Directory).Run(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
serverCts.Cancel();
|
||||
var cancellationToken = serverCts.Token;
|
||||
var serverTask = server.RunAsync(cancellationToken);
|
||||
try
|
||||
{
|
||||
await serverTask.ConfigureAwait(false);
|
||||
IServerClient adminClient;
|
||||
|
||||
var giveUpAt = DateTimeOffset.Now.AddSeconds(60);
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
adminClient = await clientFactory.CreateServerClient(server.Url, User.AdminName, User.DefaultAdminPassword).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
catch (HttpRequestException)
|
||||
{
|
||||
//migrating, to be expected
|
||||
if (DateTimeOffset.Now > giveUpAt)
|
||||
throw;
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
|
||||
}
|
||||
catch (ServiceUnavailableException)
|
||||
{
|
||||
// migrating, to be expected
|
||||
if (DateTimeOffset.Now > giveUpAt)
|
||||
throw;
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
|
||||
}
|
||||
} while (true);
|
||||
|
||||
using (adminClient)
|
||||
{
|
||||
var serverInfo = await adminClient.Version(default).ConfigureAwait(false);
|
||||
|
||||
Assert.AreEqual(ApiHeaders.Version, serverInfo.ApiVersion);
|
||||
Assert.AreEqual(typeof(IServer).Assembly.GetName().Version, serverInfo.Version);
|
||||
|
||||
//check that modifying the token even slightly fucks up the auth
|
||||
var newToken = new Token
|
||||
{
|
||||
ExpiresAt = adminClient.Token.ExpiresAt,
|
||||
Bearer = adminClient.Token.Bearer + '0'
|
||||
};
|
||||
|
||||
var badClient = clientFactory.CreateServerClient(server.Url, newToken);
|
||||
await Assert.ThrowsExceptionAsync<UnauthorizedException>(() => badClient.Version(cancellationToken)).ConfigureAwait(false);
|
||||
|
||||
await new AdministrationTest(adminClient.Administration).Run(cancellationToken).ConfigureAwait(false);
|
||||
await new UsersTest(adminClient.Users).Run(cancellationToken).ConfigureAwait(false);
|
||||
await new InstanceManagerTest(adminClient.Instances, server.Directory).Run(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
serverCts.Cancel();
|
||||
try
|
||||
{
|
||||
await serverTask.ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user