From bab9d81aa0a5a74d2d479ae3c06f06bb9d02ee69 Mon Sep 17 00:00:00 2001 From: Dominion Date: Mon, 5 Jun 2023 21:24:12 -0400 Subject: [PATCH 01/29] Fix attempting to wait on a job after the JobService has stopped --- .../Components/Instance.cs | 6 ++--- src/Tgstation.Server.Host/Jobs/JobHandler.cs | 25 +++++++++++-------- src/Tgstation.Server.Host/Jobs/JobService.cs | 10 ++++++++ 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Instance.cs b/src/Tgstation.Server.Host/Components/Instance.cs index 95ca246c42..0e441edb15 100644 --- a/src/Tgstation.Server.Host/Components/Instance.cs +++ b/src/Tgstation.Server.Host/Components/Instance.cs @@ -514,11 +514,9 @@ namespace Tgstation.Server.Host.Components await jobManager.RegisterOperation( repositoryUpdateJob, RepositoryAutoUpdateJob, - cancellationToken) - ; + cancellationToken); - // DCT: First token will cancel the job, second is for cancelling the cancellation, unwanted - await jobManager.WaitForJobCompletion(repositoryUpdateJob, null, cancellationToken, default); + await jobManager.WaitForJobCompletion(repositoryUpdateJob, null, cancellationToken, cancellationToken); Job compileProcessJob; using (var repo = await RepositoryManager.LoadRepository(cancellationToken)) diff --git a/src/Tgstation.Server.Host/Jobs/JobHandler.cs b/src/Tgstation.Server.Host/Jobs/JobHandler.cs index 19941fbefd..98cbe88651 100644 --- a/src/Tgstation.Server.Host/Jobs/JobHandler.cs +++ b/src/Tgstation.Server.Host/Jobs/JobHandler.cs @@ -11,6 +11,21 @@ namespace Tgstation.Server.Host.Jobs /// sealed class JobHandler : IDisposable { + /// + /// If the job has started. + /// + public bool Started => task != null; + + /// + /// The progress of the job. + /// + public int? Progress { get; set; } + + /// + /// The stage of the job. + /// + public string Stage { get; set; } + /// /// The for . /// @@ -39,16 +54,6 @@ namespace Tgstation.Server.Host.Jobs /// public void Dispose() => cancellationTokenSource.Dispose(); - /// - /// The progress of the job. - /// - public int? Progress { get; set; } - - /// - /// The stage of the job. - /// - public string Stage { get; set; } - /// /// Wait for to complete. /// diff --git a/src/Tgstation.Server.Host/Jobs/JobService.cs b/src/Tgstation.Server.Host/Jobs/JobService.cs index c0d01f2dcf..09350a4c4c 100644 --- a/src/Tgstation.Server.Host/Jobs/JobService.cs +++ b/src/Tgstation.Server.Host/Jobs/JobService.cs @@ -256,13 +256,23 @@ namespace Tgstation.Server.Host.Jobs { if (job == null) throw new ArgumentNullException(nameof(job)); + + if (!cancellationToken.CanBeCanceled) + throw new ArgumentException("A cancellable CancellationToken should be provided!", nameof(cancellationToken)); + JobHandler handler; + bool noMoreJobsShouldStart; lock (synchronizationLock) { if (!jobs.TryGetValue(job.Id.Value, out handler)) return; + + noMoreJobsShouldStart = this.noMoreJobsShouldStart; } + if (noMoreJobsShouldStart && !handler.Started) + await Extensions.TaskExtensions.InfiniteTask().WithToken(cancellationToken); + Task cancelTask = null; using (jobCancellationToken.Register(() => cancelTask = CancelJob(job, canceller, true, cancellationToken))) await handler.Wait(cancellationToken); From cca2e4a9b7de11360d30ddad89f069b02f303cf9 Mon Sep 17 00:00:00 2001 From: Dominion Date: Mon, 5 Jun 2023 21:26:41 -0400 Subject: [PATCH 02/29] InfiniteTask from function to property --- .../Components/Watchdog/WatchdogBase.cs | 2 +- .../Extensions/TaskExtensions.cs | 11 +++++------ src/Tgstation.Server.Host/Jobs/JobService.cs | 2 +- src/Tgstation.Server.Host/Swarm/SwarmService.cs | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs index 725f99fb3a..258ab9709c 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs @@ -854,7 +854,7 @@ namespace Tgstation.Server.Host.Components.Watchdog var heartbeatSeconds = ActiveLaunchParameters.HeartbeatSeconds.Value; var heartbeat = heartbeatSeconds == 0 || !controller.DMApiAvailable - ? Extensions.TaskExtensions.InfiniteTask() + ? Extensions.TaskExtensions.InfiniteTask : Task.Delay( TimeSpan.FromSeconds(heartbeatSeconds), cancellationToken); diff --git a/src/Tgstation.Server.Host/Extensions/TaskExtensions.cs b/src/Tgstation.Server.Host/Extensions/TaskExtensions.cs index a8ee467d1e..bd65a629d9 100644 --- a/src/Tgstation.Server.Host/Extensions/TaskExtensions.cs +++ b/src/Tgstation.Server.Host/Extensions/TaskExtensions.cs @@ -14,6 +14,11 @@ namespace Tgstation.Server.Host.Extensions /// static readonly TaskCompletionSource InfiniteTaskCompletionSource = new (); + /// + /// Gets a that never completes. + /// + public static Task InfiniteTask => InfiniteTaskCompletionSource.Task; + /// /// Create a that can be awaited while respecting a given . /// @@ -53,11 +58,5 @@ namespace Tgstation.Server.Host.Extensions return await task; } - - /// - /// Creates a that never completes. - /// - /// A never ending . - public static Task InfiniteTask() => InfiniteTaskCompletionSource.Task; } } diff --git a/src/Tgstation.Server.Host/Jobs/JobService.cs b/src/Tgstation.Server.Host/Jobs/JobService.cs index 09350a4c4c..971150061a 100644 --- a/src/Tgstation.Server.Host/Jobs/JobService.cs +++ b/src/Tgstation.Server.Host/Jobs/JobService.cs @@ -271,7 +271,7 @@ namespace Tgstation.Server.Host.Jobs } if (noMoreJobsShouldStart && !handler.Started) - await Extensions.TaskExtensions.InfiniteTask().WithToken(cancellationToken); + await Extensions.TaskExtensions.InfiniteTask.WithToken(cancellationToken); Task cancelTask = null; using (jobCancellationToken.Register(() => cancelTask = CancelJob(job, canceller, true, cancellationToken))) diff --git a/src/Tgstation.Server.Host/Swarm/SwarmService.cs b/src/Tgstation.Server.Host/Swarm/SwarmService.cs index d53456c8ac..8f8a5ce7a9 100644 --- a/src/Tgstation.Server.Host/Swarm/SwarmService.cs +++ b/src/Tgstation.Server.Host/Swarm/SwarmService.cs @@ -380,7 +380,7 @@ namespace Tgstation.Server.Host.Swarm ? asyncDelayer.Delay( TimeSpan.FromMinutes(UpdateCommitTimeoutMinutes), cancellationToken) - : Extensions.TaskExtensions.InfiniteTask().WithToken(cancellationToken); + : Extensions.TaskExtensions.InfiniteTask.WithToken(cancellationToken); var commitTask = Task.WhenAny(commitTcsTask, timeoutTask); From e71a34a9191c0c02c45f54ee4fe9ccfb504f0947 Mon Sep 17 00:00:00 2001 From: Dominion Date: Mon, 5 Jun 2023 21:27:52 -0400 Subject: [PATCH 03/29] Increase a test timeout --- tests/Tgstation.Server.Tests/Live/Instance/RepositoryTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Tgstation.Server.Tests/Live/Instance/RepositoryTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/RepositoryTest.cs index c35ccd70dd..05f3b90ec2 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/RepositoryTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/RepositoryTest.cs @@ -59,8 +59,8 @@ namespace Tgstation.Server.Tests.Live.Instance if (!targetActiveJob.Progress.HasValue) { - // give it 15 more seconds - targetActiveJob = await WaitForJobProgress(targetActiveJob, 15, cancellationToken); + // give it a few more seconds + targetActiveJob = await WaitForJobProgress(targetActiveJob, 30, cancellationToken); allJobs = await JobsClient.List(null, cancellationToken); } From 69a61b49308b18f8186a594635f2970d491f111d Mon Sep 17 00:00:00 2001 From: Dominion Date: Tue, 6 Jun 2023 08:05:10 -0400 Subject: [PATCH 04/29] Provide a valid CancellationToken --- src/Tgstation.Server.Host/Components/Chat/Providers/Provider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/Provider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/Provider.cs index f9ef3396c6..bfc541e6f9 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/Provider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/Provider.cs @@ -309,7 +309,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers cancellationToken); // DCT: Always wait for the job to complete here - await jobManager.WaitForJobCompletion(job, null, cancellationToken, default); + await jobManager.WaitForJobCompletion(job, null, cancellationToken, cancellationToken); } } catch (OperationCanceledException e) From 257459e49242cf6f25ce3b29d63ec2de0a49a205 Mon Sep 17 00:00:00 2001 From: Dominion Date: Tue, 6 Jun 2023 08:06:20 -0400 Subject: [PATCH 05/29] My Discord name got migrated --- SECURITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SECURITY.md b/SECURITY.md index 5d79c18579..1e624e68bb 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -16,6 +16,6 @@ Vulnerabilities should ideally be reported by directly messaging one of the TGS Here is a list of their discord IDs. -@Cyberboss - Dominion#0444 (<@133295178197893120>) +@Cyberboss - dominion (<@133295178197893120>) Once reported, they will handle the processing of the security advisory. From ff62258507eb9e41fe7d8b3ffccc4f555de3f513 Mon Sep 17 00:00:00 2001 From: Ilya Shipitsin Date: Tue, 6 Jun 2023 23:19:10 +0200 Subject: [PATCH 06/29] CI: skip Doxygen builds on forks --- .github/workflows/ci-suite.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 7fd6134029..0bf8845e46 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -184,7 +184,7 @@ jobs: doxyfile-path: 'docs/Doxyfile' - name: gh-pages push - if: github.event_name == 'push' && github.event.ref == 'refs/heads/dev' + if: github.event_name == 'push' && github.event.ref == 'refs/heads/dev' && env.TGS_RELEASE_NOTES_TOKEN != '' run: | git clone -b gh-pages --single-branch "https://git@github.com/tgstation/tgstation-server" $HOME/tgsdox pushd $HOME/tgsdox From 90af2b6e8d2c43abae677eb8341a0d67d2e27d9f Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Tue, 6 Jun 2023 17:35:19 -0400 Subject: [PATCH 07/29] Switch back to using `windows-latest` in Live tests Closes #1488 --- .github/workflows/ci-suite.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 7fd6134029..2d670304d9 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -322,7 +322,7 @@ jobs: database-type: [ 'SqlServer', 'Sqlite', 'PostgresSql', 'MariaDB', 'MySql' ] watchdog-type: [ 'Basic', 'System' ] configuration: [ 'Debug', 'Release' ] - runs-on: windows-2019 + runs-on: windows-latest env: BYOND_MAJOR: 514 BYOND_MINOR: 1588 From a9074b318616f1273484a4e67ac20c9b1106f52b Mon Sep 17 00:00:00 2001 From: Dominion Date: Tue, 6 Jun 2023 22:49:06 -0400 Subject: [PATCH 08/29] Starting to narrow down that spurious Live tests socket error --- tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs index 3b08f87812..7f251899ee 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs @@ -531,7 +531,7 @@ namespace Tgstation.Server.Tests.Live.Instance TopicResponse topicRequestResult = null; try { - System.Console.WriteLine($"Topic limit test S:{payloadSize}..."); + System.Console.WriteLine($"Topic send limit test S:{currentSize}..."); topicRequestResult = await TopicClientNoLogger.SendTopic( IPAddress.Loopback, $"tgs_integration_test_tactics3={TopicClient.SanitizeString(JsonConvert.SerializeObject(topic, DMApiConstants.SerializerSettings))}", @@ -563,6 +563,8 @@ namespace Tgstation.Server.Tests.Live.Instance Assert.AreEqual(DMApiConstants.MaximumTopicRequestLength, (uint)lastSize); + System.Console.WriteLine("TEST: Receiving Topic tests topics..."); + // Receive baseSize = 1; nextPow = 0; @@ -570,6 +572,7 @@ namespace Tgstation.Server.Tests.Live.Instance while (!cancellationToken.IsCancellationRequested) { var currentSize = baseSize + (int)Math.Pow(2, nextPow); + System.Console.WriteLine($"Topic send limit test S:{currentSize}..."); var topicRequestResult = await TopicClientNoLogger.SendTopic( IPAddress.Loopback, $"tgs_integration_test_tactics4={TopicClient.SanitizeString(currentSize.ToString())}", From eba904e700e92ecefb4be719a818bfe04fab4fd9 Mon Sep 17 00:00:00 2001 From: Dominion Date: Tue, 6 Jun 2023 23:03:58 -0400 Subject: [PATCH 09/29] Fix a log message and dump tests edge case --- .../Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs index 7f251899ee..5a781f50f8 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs @@ -246,7 +246,11 @@ namespace Tgstation.Server.Tests.Live.Instance jobTcs.SetResult(); await killTask; } - Assert.IsTrue(job.ErrorCode == ErrorCode.DreamDaemonOffline || job.ErrorCode == ErrorCode.GCoreFailure, $"{job.ErrorCode}: {job.ExceptionDetails}"); + + // this can also happen + if (!(new PlatformIdentifier().IsWindows && job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: E_ACCESSDENIED: Access is denied."))) + Assert.IsTrue(job.ErrorCode == ErrorCode.DreamDaemonOffline || job.ErrorCode == ErrorCode.GCoreFailure, $"{job.ErrorCode}: {job.ExceptionDetails}"); + await Task.Delay(TimeSpan.FromSeconds(20), cancellationToken); var ddStatus = await instanceClient.DreamDaemon.Read(cancellationToken); @@ -572,7 +576,7 @@ namespace Tgstation.Server.Tests.Live.Instance while (!cancellationToken.IsCancellationRequested) { var currentSize = baseSize + (int)Math.Pow(2, nextPow); - System.Console.WriteLine($"Topic send limit test S:{currentSize}..."); + System.Console.WriteLine($"Topic recieve limit test S:{currentSize}..."); var topicRequestResult = await TopicClientNoLogger.SendTopic( IPAddress.Loopback, $"tgs_integration_test_tactics4={TopicClient.SanitizeString(currentSize.ToString())}", From d41715c512166adf881cfdbbd1e6a0be9b447ee4 Mon Sep 17 00:00:00 2001 From: Dominion Date: Tue, 6 Jun 2023 23:05:27 -0400 Subject: [PATCH 10/29] More dump tests edge cases --- tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs index 5a781f50f8..1be387c72a 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs @@ -247,8 +247,10 @@ namespace Tgstation.Server.Tests.Live.Instance await killTask; } - // this can also happen - if (!(new PlatformIdentifier().IsWindows && job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: E_ACCESSDENIED: Access is denied."))) + // these can also happen + if (!(new PlatformIdentifier().IsWindows + && (job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: E_ACCESSDENIED: Access is denied.") + || job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: E_HANDLE: The handle is invalid.")))) Assert.IsTrue(job.ErrorCode == ErrorCode.DreamDaemonOffline || job.ErrorCode == ErrorCode.GCoreFailure, $"{job.ErrorCode}: {job.ExceptionDetails}"); await Task.Delay(TimeSpan.FromSeconds(20), cancellationToken); From e586102381c6153ec3c01e943925885e65258a98 Mon Sep 17 00:00:00 2001 From: Dominion Date: Tue, 6 Jun 2023 23:17:01 -0400 Subject: [PATCH 11/29] EVEN MORE DUMP TESTS EDGE CASES I fucking hate this test --- tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs index 1be387c72a..c97cac3308 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs @@ -250,7 +250,8 @@ namespace Tgstation.Server.Tests.Live.Instance // these can also happen if (!(new PlatformIdentifier().IsWindows && (job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: E_ACCESSDENIED: Access is denied.") - || job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: E_HANDLE: The handle is invalid.")))) + || job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: E_HANDLE: The handle is invalid.") + || job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: 3489660936: Unknown error (0xd0000008)")))) // kek Assert.IsTrue(job.ErrorCode == ErrorCode.DreamDaemonOffline || job.ErrorCode == ErrorCode.GCoreFailure, $"{job.ErrorCode}: {job.ExceptionDetails}"); await Task.Delay(TimeSpan.FromSeconds(20), cancellationToken); From 12f878028d70e5ea220e2e5936707c114c60b8da Mon Sep 17 00:00:00 2001 From: Dominion Date: Wed, 7 Jun 2023 08:02:54 -0400 Subject: [PATCH 12/29] Move code scanning to its own actions file - It can work and only work on `pull_request`, not `pull_request_target` --- .github/workflows/ci-suite.yml | 38 ------------------------- .github/workflows/code-scanning.yml | 44 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 38 deletions(-) create mode 100644 .github/workflows/code-scanning.yml diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 7fd6134029..ccdaa58af9 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -55,44 +55,6 @@ jobs: - name: GitHub Requires at Least One Step for a Job run: exit 0 - analyze: - name: Code Scanning - needs: start-ci-run-gate - if: "!(cancelled() || failure()) && needs.start-ci-run-gate.result == 'success'" - runs-on: ubuntu-latest - steps: - - name: Install Node 12.X - uses: actions/setup-node@v3 - with: - node-version: 12.x - - - name: Upgrade NPM - run: npm install -g npm - - - name: Checkout (Branch Push) - uses: actions/checkout@v3 - if: github.event_name == 'push' - with: - fetch-depth: 2 - - - name: Checkout (PR Merge) - uses: actions/checkout@v3 - if: github.event_name != 'push' - with: - fetch-depth: 2 - ref: "refs/pull/${{ github.event.number }}/merge" - - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: csharp - - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 - dmapi-build: name: Build DMAPI needs: start-ci-run-gate diff --git a/.github/workflows/code-scanning.yml b/.github/workflows/code-scanning.yml new file mode 100644 index 0000000000..4118c5b1cf --- /dev/null +++ b/.github/workflows/code-scanning.yml @@ -0,0 +1,44 @@ +name: 'Code Scanning' + +on: + schedule: + - cron: 0 23 * * * + push: + branches: + - dev + - master + pull_request: + branches: + - dev + - master + +concurrency: + group: "code-scanning-${{ github.head_ref || github.run_id }}-${{ github.event_name }}" + cancel-in-progress: true + +jobs: + analyze: + name: Code Scanning + runs-on: ubuntu-latest + steps: + - name: Install Node 12.X + uses: actions/setup-node@v3 + with: + node-version: 12.x + + - name: Upgrade NPM + run: npm install -g npm + + - name: Checkout + uses: actions/checkout@v3 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: csharp + + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 From 0a6f68f4539bcea7c922245047b3ed81ac5ff1d4 Mon Sep 17 00:00:00 2001 From: Dominion Date: Wed, 7 Jun 2023 17:42:41 -0400 Subject: [PATCH 13/29] Add missing check for null PasswordHash Fixes #1521 --- src/Tgstation.Server.Host/Controllers/HomeController.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Tgstation.Server.Host/Controllers/HomeController.cs b/src/Tgstation.Server.Host/Controllers/HomeController.cs index 958ea12a2b..ac9e666be8 100644 --- a/src/Tgstation.Server.Host/Controllers/HomeController.cs +++ b/src/Tgstation.Server.Host/Controllers/HomeController.cs @@ -298,8 +298,7 @@ namespace Tgstation.Server.Host.Controllers Enabled = x.Enabled, Name = x.Name, }) - .ToListAsync(cancellationToken) - ; + .ToListAsync(cancellationToken); // Pick the DB user first var user = users @@ -322,7 +321,7 @@ namespace Tgstation.Server.Host.Controllers if (!usingSystemIdentity) { // DB User password check and update - if (!cryptographySuite.CheckUserPassword(user, ApiHeaders.Password)) + if (originalHash == null || !cryptographySuite.CheckUserPassword(user, ApiHeaders.Password)) return Unauthorized(); if (user.PasswordHash != originalHash) { From f8c9776c91ec33b7d1010242e0a2226b809e0381 Mon Sep 17 00:00:00 2001 From: Dominion Date: Wed, 7 Jun 2023 17:43:01 -0400 Subject: [PATCH 14/29] Add some missing null checks --- src/Tgstation.Server.Host/Security/CryptographySuite.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Tgstation.Server.Host/Security/CryptographySuite.cs b/src/Tgstation.Server.Host/Security/CryptographySuite.cs index 27a780453e..21051e2163 100644 --- a/src/Tgstation.Server.Host/Security/CryptographySuite.cs +++ b/src/Tgstation.Server.Host/Security/CryptographySuite.cs @@ -54,6 +54,11 @@ namespace Tgstation.Server.Host.Security /// public bool CheckUserPassword(User user, string password) { + if (user == null) + throw new ArgumentNullException(nameof(user)); + if (password == null) + throw new ArgumentNullException(nameof(password)); + var result = passwordHasher.VerifyHashedPassword(user, user.PasswordHash, password); switch (result) { From d00410e32c001569069736da4589dbc515fc9981 Mon Sep 17 00:00:00 2001 From: Dominion Date: Wed, 7 Jun 2023 18:34:18 -0400 Subject: [PATCH 15/29] Version bump to 5.12.5 --- build/Version.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Version.props b/build/Version.props index 3b58bdbe15..f4ebd0f5a2 100644 --- a/build/Version.props +++ b/build/Version.props @@ -3,7 +3,7 @@ - 5.12.4 + 5.12.5 4.6.0 9.10.2 10.4.1 From 425386a528a33d312823a09b8dffef38720654c7 Mon Sep 17 00:00:00 2001 From: Dominion Date: Wed, 7 Jun 2023 19:31:41 -0400 Subject: [PATCH 16/29] Another dump tests error fix --- .../Live/Instance/WatchdogTest.cs | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs index c97cac3308..aff010522d 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs @@ -224,35 +224,45 @@ namespace Tgstation.Server.Tests.Live.Instance Assert.AreEqual(1, dumpFiles.Length); File.Delete(dumpFiles.Single()); - KillDD(true); - var jobTcs = new TaskCompletionSource(); - var killTaskStarted = new TaskCompletionSource(); - var killTask = Task.Run(() => - { - killTaskStarted.SetResult(); - while (!jobTcs.Task.IsCompleted) - KillDD(false); - }, cancellationToken); - JobResponse job; - try + while (true) { - await killTaskStarted.Task; - var dumpTask = instanceClient.DreamDaemon.CreateDump(cancellationToken); - job = await WaitForJob(await dumpTask, 20, true, null, cancellationToken); - } - finally - { - jobTcs.SetResult(); - await killTask; + KillDD(true); + var jobTcs = new TaskCompletionSource(); + var killTaskStarted = new TaskCompletionSource(); + var killTask = Task.Run(() => + { + killTaskStarted.SetResult(); + while (!jobTcs.Task.IsCompleted) + KillDD(false); + }, cancellationToken); + + try + { + await killTaskStarted.Task; + var dumpTask = instanceClient.DreamDaemon.CreateDump(cancellationToken); + job = await WaitForJob(await dumpTask, 20, true, null, cancellationToken); + } + finally + { + jobTcs.SetResult(); + await killTask; + } + + // these can also happen + + if (!(new PlatformIdentifier().IsWindows + && (job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: E_ACCESSDENIED: Access is denied.") + || job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: E_HANDLE: The handle is invalid.") + || job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: 3489660936: Unknown error (0xd0000008)") + || job.ExceptionDetails.Contains("System.InvalidOperationException: No process is associated with this object.")))) + break; + + var restartJob = await instanceClient.DreamDaemon.Restart(cancellationToken); + await WaitForJob(restartJob, 20, false, null, cancellationToken); } - // these can also happen - if (!(new PlatformIdentifier().IsWindows - && (job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: E_ACCESSDENIED: Access is denied.") - || job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: E_HANDLE: The handle is invalid.") - || job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: 3489660936: Unknown error (0xd0000008)")))) // kek - Assert.IsTrue(job.ErrorCode == ErrorCode.DreamDaemonOffline || job.ErrorCode == ErrorCode.GCoreFailure, $"{job.ErrorCode}: {job.ExceptionDetails}"); + Assert.IsTrue(job.ErrorCode == ErrorCode.DreamDaemonOffline || job.ErrorCode == ErrorCode.GCoreFailure, $"{job.ErrorCode}: {job.ExceptionDetails}"); await Task.Delay(TimeSpan.FromSeconds(20), cancellationToken); From 54b654cabc8cef242997d4aef44054de21ade778 Mon Sep 17 00:00:00 2001 From: Dominion Date: Wed, 7 Jun 2023 20:13:14 -0400 Subject: [PATCH 17/29] Hopefully fixes BYOND caching --- .github/workflows/ci-suite.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 1e2b15aef8..31cb7742fd 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -74,7 +74,7 @@ jobs: uses: actions/cache@v3 id: cache-byond with: - path: $HOME/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }} + path: ~/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }} key: ${{ env.BYOND_MAJOR }}-${{ env.BYOND_MINOR }}-dmapibyond - name: Install BYOND @@ -298,7 +298,7 @@ jobs: uses: actions/cache@v3 id: cache-byond with: - path: $HOME/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }} + path: ~/BYOND-${{ env.BYOND_MAJOR }}.${{ env.BYOND_MINOR }} key: ${{ env.BYOND_MAJOR }}-${{ env.BYOND_MINOR }}-livebyond - name: Download BYOND From a721324d21c193cd87d4f73f252a75d0fa3c57ce Mon Sep 17 00:00:00 2001 From: Dominion Date: Thu, 8 Jun 2023 03:16:23 -0400 Subject: [PATCH 18/29] Fix all remaining bad semicolons Finally, peace. --- .../Components/Byond/ByondInstallerBase.cs | 3 +- .../Components/Chat/Providers/IrcProvider.cs | 6 ++-- .../Components/Deployment/DmbFactory.cs | 12 +++----- .../Components/Deployment/DreamMaker.cs | 30 +++++++------------ .../Deployment/SwappableDmbProvider.cs | 3 +- .../Components/Instance.cs | 18 ++++------- .../Components/InstanceManager.cs | 3 +- .../Repository/GitLabRemoteFeatures.cs | 3 +- .../Components/Repository/Repository.cs | 27 ++++++----------- .../Repository/RepositoryUpdateService.cs | 15 ++++------ .../Components/Session/SessionController.cs | 6 ++-- .../Components/Session/SessionPersistor.cs | 6 ++-- .../Components/StaticFiles/Configuration.cs | 3 +- .../Components/Watchdog/PosixWatchdog.cs | 6 ++-- .../Components/Watchdog/WatchdogBase.cs | 6 ++-- .../Controllers/ApiController.cs | 9 ++---- .../Controllers/ChatController.cs | 6 ++-- .../Controllers/ConfigurationController.cs | 18 ++++------- .../Controllers/DreamDaemonController.cs | 21 +++++-------- .../Controllers/DreamMakerController.cs | 12 +++----- .../Controllers/InstanceController.cs | 27 ++++++----------- .../InstancePermissionSetController.cs | 15 ++++------ .../Controllers/JobController.cs | 6 ++-- .../LimitedFileStreamResultExecutor.cs | 6 ++-- .../Controllers/RepositoryController.cs | 30 +++++++------------ .../Controllers/UserController.cs | 9 ++---- .../Controllers/UserGroupController.cs | 15 ++++------ .../Database/DatabaseSeeder.cs | 12 +++----- src/Tgstation.Server.Host/Jobs/JobService.cs | 3 +- .../Security/AuthenticationContextFactory.cs | 6 ++-- .../Security/ClaimsInjector.cs | 3 +- .../Setup/SetupWizard.cs | 24 +++++---------- .../Swarm/SwarmService.cs | 3 +- .../Utils/PortAllocator.cs | 6 ++-- 34 files changed, 126 insertions(+), 252 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Byond/ByondInstallerBase.cs b/src/Tgstation.Server.Host/Components/Byond/ByondInstallerBase.cs index df32ce6e48..75fe7bcd47 100644 --- a/src/Tgstation.Server.Host/Components/Byond/ByondInstallerBase.cs +++ b/src/Tgstation.Server.Host/Components/Byond/ByondInstallerBase.cs @@ -70,8 +70,7 @@ namespace Tgstation.Server.Host.Components.Byond IOManager.ConcatPath( PathToUserByondFolder, CacheDirectoryName), - cancellationToken) - ; + cancellationToken); } catch (OperationCanceledException) { diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs index 08d4b7a1ce..e8db69db69 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs @@ -383,8 +383,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers cancellationToken, DefaultIOManager.BlockingTaskCreationOptions, TaskScheduler.Current) - .WithToken(cancellationToken) - ; + .WithToken(cancellationToken); cancellationToken.ThrowIfCancellationRequested(); @@ -480,8 +479,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers }, cancellationToken, DefaultIOManager.BlockingTaskCreationOptions, - TaskScheduler.Current) - ; + TaskScheduler.Current); await HardDisconnect(cancellationToken); } catch (OperationCanceledException) diff --git a/src/Tgstation.Server.Host/Components/Deployment/DmbFactory.cs b/src/Tgstation.Server.Host/Components/Deployment/DmbFactory.cs index 7d57085eff..2c5abb0541 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/DmbFactory.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/DmbFactory.cs @@ -145,8 +145,7 @@ namespace Tgstation.Server.Host.Components.Deployment job); await remoteDeploymentManager.StageDeployment( newProvider.CompileJob, - cancellationToken) - ; + cancellationToken); } lock (jobLockCounts) @@ -188,10 +187,8 @@ namespace Tgstation.Server.Host.Components.Deployment .AsQueryable() .Where(x => x.Job.Instance.Id == metadata.Id) .OrderByDescending(x => x.Job.StoppedAt) - .FirstOrDefaultAsync(cancellationToken) - ; - }) - ; + .FirstOrDefaultAsync(cancellationToken); + }); if (cj == default(CompileJob)) return; @@ -267,8 +264,7 @@ namespace Tgstation.Server.Host.Components.Deployment ioManager.ConcatPath( newProvider.Directory, newProvider.DmbName), - cancellationToken) - ; + cancellationToken); if (!dmbExistsAtRoot) { diff --git a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs index 8470210425..31e8e3a956 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs @@ -241,8 +241,7 @@ namespace Tgstation.Server.Host.Components.Deployment StartupTimeout = x.StartupTimeout, LogOutput = x.LogOutput, }) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (ddSettings == default) throw new JobException(ErrorCode.InstanceMissingDreamDaemonSettings); @@ -250,8 +249,7 @@ namespace Tgstation.Server.Host.Components.Deployment .DreamMakerSettings .AsQueryable() .Where(x => x.InstanceId == metadata.Id) - .FirstAsync(cancellationToken) - ; + .FirstAsync(cancellationToken); if (dreamMakerSettings == default) throw new JobException(ErrorCode.InstanceMissingDreamMakerSettings); @@ -267,8 +265,7 @@ namespace Tgstation.Server.Host.Components.Deployment PushTestMergeCommits = x.PushTestMergeCommits, PostTestMergeComment = x.PostTestMergeComment, }) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (repositorySettings == default) throw new JobException(ErrorCode.InstanceMissingRepositorySettings); @@ -291,8 +288,7 @@ namespace Tgstation.Server.Host.Components.Deployment .Include(x => x.ActiveTestMerges) .ThenInclude(x => x.TestMerge) .ThenInclude(x => x.MergedBy) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (revInfo == default) { @@ -319,8 +315,7 @@ namespace Tgstation.Server.Host.Components.Deployment repo?.Dispose(); throw; } - }) - ; + }); var likelyPushedTestMergeCommit = repositorySettings.PushTestMergeCommits.Value @@ -336,8 +331,7 @@ namespace Tgstation.Server.Host.Components.Deployment progressReporter, averageSpan, likelyPushedTestMergeCommit, - cancellationToken) - ; + cancellationToken); var activeCompileJob = compileJobConsumer.LatestCompileJob(); try @@ -379,8 +373,7 @@ namespace Tgstation.Server.Host.Components.Deployment compileJob.Job = fullJob; compileJob.RevisionInformation = fullRevInfo; - }) - ; + }); } catch (Exception ex) { @@ -447,8 +440,7 @@ namespace Tgstation.Server.Host.Components.Deployment StoppedAt = x.Job.StoppedAt, StartedAt = x.Job.StartedAt, }) - .ToListAsync(cancellationToken) - ; + .ToListAsync(cancellationToken); TimeSpan? averageSpan = null; if (previousCompileJobs.Count != 0) @@ -605,8 +597,7 @@ namespace Tgstation.Server.Host.Components.Deployment repoOrigin.ToString(), $"{byondLock.Version.Major}.{byondLock.Version.Minor}", }, - cancellationToken) - ; + cancellationToken); // determine the dme progressReporter.StageName = "Determining .dme"; @@ -978,8 +969,7 @@ namespace Tgstation.Server.Host.Components.Deployment remoteDeploymentManager.FailDeployment( job, FormatExceptionForUsers(exception), - default)) - ; + default)); } } } diff --git a/src/Tgstation.Server.Host/Components/Deployment/SwappableDmbProvider.cs b/src/Tgstation.Server.Host/Components/Deployment/SwappableDmbProvider.cs index 0acc80828c..a794135d13 100644 --- a/src/Tgstation.Server.Host/Components/Deployment/SwappableDmbProvider.cs +++ b/src/Tgstation.Server.Host/Components/Deployment/SwappableDmbProvider.cs @@ -73,8 +73,7 @@ namespace Tgstation.Server.Host.Components.Deployment await symlinkFactory.CreateSymbolicLink( ioManager.ResolvePath(baseProvider.Directory), ioManager.ResolvePath(LiveGameDirectory), - cancellationToken) - ; + cancellationToken); } } } diff --git a/src/Tgstation.Server.Host/Components/Instance.cs b/src/Tgstation.Server.Host/Components/Instance.cs index 0e441edb15..42e18fa43b 100644 --- a/src/Tgstation.Server.Host/Components/Instance.cs +++ b/src/Tgstation.Server.Host/Components/Instance.cs @@ -185,8 +185,7 @@ namespace Tgstation.Server.Host.Components Configuration.StartAsync(cancellationToken), ByondManager.StartAsync(cancellationToken), Chat.StartAsync(cancellationToken), - dmbFactory.StartAsync(cancellationToken)) - ; + dmbFactory.StartAsync(cancellationToken)); // dependent on so many things, its just safer this way await Watchdog.StartAsync(cancellationToken); @@ -310,8 +309,7 @@ namespace Tgstation.Server.Host.Components repositorySettings.AccessUser, repositorySettings.AccessToken, NextProgressReporter("Fetch Origin"), - cancellationToken) - ; + cancellationToken); var hasDbChanges = false; RevisionInformation currentRevInfo = null; @@ -385,8 +383,7 @@ namespace Tgstation.Server.Host.Components repositorySettings.CommitterName, repositorySettings.CommitterEmail, NextProgressReporter("Merge Origin"), - cancellationToken) - ; + cancellationToken); var preserveTestMerges = repositorySettings.AutoUpdatesKeepTestMerges.Value; var remoteDeploymentManager = remoteDeploymentManagerFactory.CreateRemoteDeploymentManager( @@ -435,16 +432,14 @@ namespace Tgstation.Server.Host.Components repositorySettings.AccessToken, repositorySettings.UpdateSubmodules.Value, NextProgressReporter(StageName), - cancellationToken) - ; + cancellationToken); var currentHead = repo.Head; currentRevInfo = await databaseContext.RevisionInformations .AsQueryable() .Where(x => x.CommitSha == currentHead && x.Instance.Id == metadata.Id) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (currentHead != startSha && currentRevInfo == default) await UpdateRevInfo(currentHead, true, null); @@ -555,8 +550,7 @@ namespace Tgstation.Server.Host.Components progressReporter, jobCancellationToken); }, - cancellationToken) - ; + cancellationToken); } await jobManager.WaitForJobCompletion(compileProcessJob, null, default, cancellationToken); diff --git a/src/Tgstation.Server.Host/Components/InstanceManager.cs b/src/Tgstation.Server.Host/Components/InstanceManager.cs index a0c7f8ceec..1ffb944912 100644 --- a/src/Tgstation.Server.Host/Components/InstanceManager.cs +++ b/src/Tgstation.Server.Host/Components/InstanceManager.cs @@ -284,8 +284,7 @@ namespace Tgstation.Server.Host.Components await ioManager.WriteAllBytes( ioManager.ConcatPath(oldPath, InstanceController.InstanceAttachFileName), Array.Empty(), - default) - ; + default); } catch (Exception tripleEx) { diff --git a/src/Tgstation.Server.Host/Components/Repository/GitLabRemoteFeatures.cs b/src/Tgstation.Server.Host/Components/Repository/GitLabRemoteFeatures.cs index b66c98f3e1..fbb43a62ef 100644 --- a/src/Tgstation.Server.Host/Components/Repository/GitLabRemoteFeatures.cs +++ b/src/Tgstation.Server.Host/Components/Repository/GitLabRemoteFeatures.cs @@ -66,8 +66,7 @@ namespace Tgstation.Server.Host.Components.Repository var mr = await client .MergeRequests .GetAsync($"{RemoteRepositoryOwner}/{RemoteRepositoryName}", parameters.Number) - .WithToken(cancellationToken) - ; + .WithToken(cancellationToken); var revisionToUse = parameters.TargetCommitSha == null || mr.Sha.StartsWith(parameters.TargetCommitSha, StringComparison.OrdinalIgnoreCase) diff --git a/src/Tgstation.Server.Host/Components/Repository/Repository.cs b/src/Tgstation.Server.Host/Components/Repository/Repository.cs index 6025be0cff..f0256c05e1 100644 --- a/src/Tgstation.Server.Host/Components/Repository/Repository.cs +++ b/src/Tgstation.Server.Host/Components/Repository/Repository.cs @@ -356,8 +356,7 @@ namespace Tgstation.Server.Host.Components.Repository }), cancellationToken, DefaultIOManager.BlockingTaskCreationOptions, - TaskScheduler.Current) - ; + TaskScheduler.Current); if (updateSubmodules) { @@ -412,8 +411,7 @@ namespace Tgstation.Server.Host.Components.Repository }, cancellationToken, DefaultIOManager.BlockingTaskCreationOptions, - TaskScheduler.Current) - ; + TaskScheduler.Current); if (updateSubmodules) await UpdateSubmodules( @@ -463,8 +461,7 @@ namespace Tgstation.Server.Host.Components.Repository }, cancellationToken, DefaultIOManager.BlockingTaskCreationOptions, - TaskScheduler.Current) - ; + TaskScheduler.Current); } /// @@ -485,8 +482,7 @@ namespace Tgstation.Server.Host.Components.Repository await ResetToSha( trackedBranch.Tip.Sha, progressReporter.CreateSection(null, updateSubmodules ? 2.0 / 3 : 1.0), - cancellationToken) - ; + cancellationToken); if (updateSubmodules) await UpdateSubmodules( @@ -619,8 +615,7 @@ namespace Tgstation.Server.Host.Components.Repository }, cancellationToken, DefaultIOManager.BlockingTaskCreationOptions, - TaskScheduler.Current) - ; + TaskScheduler.Current); if (result.Status == MergeStatus.Conflicts) { @@ -684,8 +679,7 @@ namespace Tgstation.Server.Host.Components.Repository { ioMananger.ResolvePath(), }, - cancellationToken) - ; + cancellationToken); } finally { @@ -702,8 +696,7 @@ namespace Tgstation.Server.Host.Components.Repository }, cancellationToken, DefaultIOManager.BlockingTaskCreationOptions, - TaskScheduler.Current) - ; + TaskScheduler.Current); } var remainingProgressFactor = 0.9; @@ -759,8 +752,7 @@ namespace Tgstation.Server.Host.Components.Repository }, cancellationToken, DefaultIOManager.BlockingTaskCreationOptions, - TaskScheduler.Current) - ; + TaskScheduler.Current); } /// @@ -1040,8 +1032,7 @@ namespace Tgstation.Server.Host.Components.Repository await Task.WhenAll( ioMananger.DeleteDirectory($".git/modules/{submodule.Path}", cancellationToken), - ioMananger.DeleteDirectory(submodule.Path, cancellationToken)) - ; + ioMananger.DeleteDirectory(submodule.Path, cancellationToken)); logger.LogTrace("Second update attempt for submodule {submoduleName}...", submodule.Name); try diff --git a/src/Tgstation.Server.Host/Components/Repository/RepositoryUpdateService.cs b/src/Tgstation.Server.Host/Components/Repository/RepositoryUpdateService.cs index 4f283079de..53de9c45c3 100644 --- a/src/Tgstation.Server.Host/Components/Repository/RepositoryUpdateService.cs +++ b/src/Tgstation.Server.Host/Components/Repository/RepositoryUpdateService.cs @@ -270,8 +270,7 @@ namespace Tgstation.Server.Host.Components.Repository currentModel.CommitterEmail, NextProgressReporter("Sychronize"), true, - cancellationToken) - ; + cancellationToken); postUpdateSha = repo.Head; } else @@ -305,8 +304,7 @@ namespace Tgstation.Server.Host.Components.Repository currentModel.AccessToken, updateSubmodules, NextProgressReporter("Checkout"), - cancellationToken) - ; + cancellationToken); await CallLoadRevInfo(); // we've either seen origin before or what we're checking out is on origin } else @@ -321,8 +319,7 @@ namespace Tgstation.Server.Host.Components.Repository currentModel.AccessToken, updateSubmodules, NextProgressReporter("Reset to Origin"), - cancellationToken) - ; + cancellationToken); await repo.Sychronize( currentModel.AccessUser, currentModel.AccessToken, @@ -330,8 +327,7 @@ namespace Tgstation.Server.Host.Components.Repository currentModel.CommitterEmail, NextProgressReporter("Synchronize"), true, - cancellationToken) - ; + cancellationToken); await CallLoadRevInfo(); // repo head is on origin so force this @@ -545,8 +541,7 @@ namespace Tgstation.Server.Host.Components.Repository currentModel.CommitterEmail, NextProgressReporter("Synchronize"), false, - cancellationToken) - ; + cancellationToken); await UpdateRevInfo(); } diff --git a/src/Tgstation.Server.Host/Components/Session/SessionController.cs b/src/Tgstation.Server.Host/Components/Session/SessionController.cs index 164fb6e218..b7978ff051 100644 --- a/src/Tgstation.Server.Host/Components/Session/SessionController.cs +++ b/src/Tgstation.Server.Host/Components/Session/SessionController.cs @@ -444,8 +444,7 @@ namespace Tgstation.Server.Host.Components.Session { var commandResult = await SendCommand( new TopicParameters(port), - cancellationToken) - ; + cancellationToken); if (commandResult?.ErrorMessage != null) return false; @@ -478,8 +477,7 @@ namespace Tgstation.Server.Host.Components.Session ReattachInformation.RebootState = newRebootState; var result = await SendCommand( new TopicParameters(newRebootState), - cancellationToken) - ; + cancellationToken); return result?.ErrorMessage == null; } diff --git a/src/Tgstation.Server.Host/Components/Session/SessionPersistor.cs b/src/Tgstation.Server.Host/Components/Session/SessionPersistor.cs index 34b46db93c..6b1020fd92 100644 --- a/src/Tgstation.Server.Host/Components/Session/SessionPersistor.cs +++ b/src/Tgstation.Server.Host/Components/Session/SessionPersistor.cs @@ -140,8 +140,7 @@ namespace Tgstation.Server.Host.Components.Session .AsQueryable() .Where(x => x.Id == metadata.Id) .Select(x => x.DreamDaemonSettings.TopicRequestTimeout) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (timeoutMilliseconds == default) { @@ -238,8 +237,7 @@ namespace Tgstation.Server.Host.Components.Session if (instant) await baseQuery - .DeleteAsync(cancellationToken) - ; + .DeleteAsync(cancellationToken); else { var results = await baseQuery.ToListAsync(cancellationToken); diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs index 3ba224a038..55a8aba459 100644 --- a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs +++ b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs @@ -652,8 +652,7 @@ namespace Tgstation.Server.Host.Components.StaticFiles await Task.WhenAll( ioManager.CreateDirectory(CodeModificationsSubdirectory, cancellationToken), ioManager.CreateDirectory(EventScriptsSubdirectory, cancellationToken), - ValidateStaticFolder()) - ; + ValidateStaticFolder()); } /// diff --git a/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdog.cs index f9f3e6bbb4..9eacfc2f17 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/PosixWatchdog.cs @@ -102,8 +102,7 @@ namespace Tgstation.Server.Host.Components.Watchdog await GameIOManager.MoveDirectory( ActiveSwappable.CompileJob.DirectoryName.ToString(), ActiveSwappable.Directory, - cancellationToken) - ; + cancellationToken); hardLinkedDmb = ActiveSwappable; } @@ -131,8 +130,7 @@ namespace Tgstation.Server.Host.Components.Watchdog await GameIOManager.MoveDirectory( hardLink, originalPosition, - default) - ; + default); } catch (Exception ex) { diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs index 258ab9709c..08ba1c8e2d 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogBase.cs @@ -388,8 +388,7 @@ namespace Tgstation.Server.Host.Components.Watchdog await Chat.UpdateTrackingContexts(ct); }, - cancellationToken) - ; + cancellationToken); } /// @@ -466,8 +465,7 @@ namespace Tgstation.Server.Host.Components.Watchdog var notification = new EventNotification(eventType, parameters); var result = await activeServer.SendCommand( new TopicParameters(notification), - cancellationToken) - ; + cancellationToken); HandleChatResponses(result); } diff --git a/src/Tgstation.Server.Host/Controllers/ApiController.cs b/src/Tgstation.Server.Host/Controllers/ApiController.cs index c563525265..6f03152921 100644 --- a/src/Tgstation.Server.Host/Controllers/ApiController.cs +++ b/src/Tgstation.Server.Host/Controllers/ApiController.cs @@ -122,8 +122,7 @@ namespace Tgstation.Server.Host.Controllers await StatusCode( HttpStatusCode.UpgradeRequired, new ErrorMessageResponse(ErrorCode.ApiMismatch)) - .ExecuteResultAsync(context) - ; + .ExecuteResultAsync(context); return; } @@ -139,8 +138,7 @@ namespace Tgstation.Server.Host.Controllers if (requireHeaders) { await HeadersIssue(false) - .ExecuteResultAsync(context) - ; + .ExecuteResultAsync(context); return; } } @@ -395,8 +393,7 @@ namespace Tgstation.Server.Host.Controllers { totalResults = await paginationResult.Results.CountAsync(cancellationToken); pagedResults = await queriedResults - .ToListAsync(cancellationToken) - ; + .ToListAsync(cancellationToken); } else { diff --git a/src/Tgstation.Server.Host/Controllers/ChatController.cs b/src/Tgstation.Server.Host/Controllers/ChatController.cs index 22605192ad..3202f8924c 100644 --- a/src/Tgstation.Server.Host/Controllers/ChatController.cs +++ b/src/Tgstation.Server.Host/Controllers/ChatController.cs @@ -187,8 +187,7 @@ namespace Tgstation.Server.Host.Controllers .ChatBots .AsQueryable() .Where(x => x.Id == id) - .DeleteAsync(cancellationToken)) - ; + .DeleteAsync(cancellationToken)); return null; }) @@ -363,8 +362,7 @@ namespace Tgstation.Server.Host.Controllers await chat.ChangeChannels(current.Id.Value, current.Channels, cancellationToken); return null; - }) - ; + }); if (earlyOut != null) return earlyOut; diff --git a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs index cc401330f4..d1ea19855c 100644 --- a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs +++ b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs @@ -84,12 +84,10 @@ namespace Tgstation.Server.Host.Controllers model.Path, systemIdentity, model.LastReadHash, - cancellationToken) - ; + cancellationToken); return model.LastReadHash == null ? Accepted(newFile) : Json(newFile); - }) - ; + }); } catch (IOException e) { @@ -129,14 +127,12 @@ namespace Tgstation.Server.Host.Controllers { var result = await instance .Configuration - .Read(filePath, systemIdentity, cancellationToken) - ; + .Read(filePath, systemIdentity, cancellationToken); if (result == null) return Gone(); return Json(result); - }) - ; + }); } catch (IOException e) { @@ -183,8 +179,7 @@ namespace Tgstation.Server.Host.Controllers { var result = await instance .Configuration - .ListDirectory(directoryPath, systemIdentity, cancellationToken) - ; + .ListDirectory(directoryPath, systemIdentity, cancellationToken); if (result == null) return new PaginatableResult(Gone()); @@ -257,8 +252,7 @@ namespace Tgstation.Server.Host.Controllers .Configuration .CreateDirectory(model.Path, systemIdentity, cancellationToken) ? Json(resultModel) - : Created(resultModel)) - ; + : Created(resultModel)); } catch (IOException e) { diff --git a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs index 068b65d825..ed749e9ee0 100644 --- a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs +++ b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs @@ -94,8 +94,7 @@ namespace Tgstation.Server.Host.Controllers await jobManager.RegisterOperation( job, (core, databaseContextFactory, paramJob, progressHandler, innerCt) => core.Watchdog.Launch(innerCt), - cancellationToken) - ; + cancellationToken); return Accepted(job.ToApi()); }); @@ -170,8 +169,7 @@ namespace Tgstation.Server.Host.Controllers .AsQueryable() .Where(x => x.Id == Instance.Id) .Select(x => x.DreamDaemonSettings) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (current == default) return Gone(); @@ -182,8 +180,7 @@ namespace Tgstation.Server.Host.Controllers .GetAvailablePort( model.Port.Value, true, - cancellationToken) - ; + cancellationToken); if (verifiedPort != model.Port) return Conflict(new ErrorMessageResponse(ErrorCode.PortNotAvailable)); } @@ -242,8 +239,7 @@ namespace Tgstation.Server.Host.Controllers await watchdog.ResetRebootState(cancellationToken); return await ReadImpl(current, cancellationToken); - }) - ; + }); } #pragma warning restore CA1506 #pragma warning restore CA1502 @@ -277,8 +273,7 @@ namespace Tgstation.Server.Host.Controllers await jobManager.RegisterOperation( job, (core, paramJob, databaseContextFactory, progressReporter, ct) => core.Watchdog.Restart(false, ct), - cancellationToken) - ; + cancellationToken); return Accepted(job.ToApi()); }); @@ -311,8 +306,7 @@ namespace Tgstation.Server.Host.Controllers await jobManager.RegisterOperation( job, (core, databaseContextFactory, paramJob, progressReporter, ct) => core.Watchdog.CreateDump(ct), - cancellationToken) - ; + cancellationToken); return Accepted(job.ToApi()); }); @@ -337,8 +331,7 @@ namespace Tgstation.Server.Host.Controllers .AsQueryable() .Where(x => x.Id == Instance.Id) .Select(x => x.DreamDaemonSettings) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (settings == default) return Gone(); } diff --git a/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs index acd862ee72..60a99ef797 100644 --- a/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs +++ b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs @@ -79,8 +79,7 @@ namespace Tgstation.Server.Host.Controllers .DreamMakerSettings .AsQueryable() .Where(x => x.InstanceId == Instance.Id) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); return Json(dreamMakerSettings.ToApi()); } @@ -152,8 +151,7 @@ namespace Tgstation.Server.Host.Controllers job, (core, databaseContextFactory, paramJob, progressReporter, jobCancellationToken) => core.DreamMaker.DeploymentProcess(paramJob, databaseContextFactory, progressReporter, jobCancellationToken), - cancellationToken) - ; + cancellationToken); return Accepted(job.ToApi()); } @@ -188,8 +186,7 @@ namespace Tgstation.Server.Host.Controllers .DreamMakerSettings .AsQueryable() .Where(x => x.InstanceId == Instance.Id) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (hostModel == null) return Gone(); @@ -214,8 +211,7 @@ namespace Tgstation.Server.Host.Controllers .GetAvailablePort( model.ApiValidationPort.Value, true, - cancellationToken) - ; + cancellationToken); if (verifiedPort != model.ApiValidationPort) return Conflict(new ErrorMessageResponse(ErrorCode.PortNotAvailable)); diff --git a/src/Tgstation.Server.Host/Controllers/InstanceController.cs b/src/Tgstation.Server.Host/Controllers/InstanceController.cs index 60be70f7d2..2af7714f42 100644 --- a/src/Tgstation.Server.Host/Controllers/InstanceController.cs +++ b/src/Tgstation.Server.Host/Controllers/InstanceController.cs @@ -182,8 +182,7 @@ namespace Tgstation.Server.Host.Controllers if (earlyOut != null && !newCancellationToken.IsCancellationRequested) cts.Cancel(); }, - newCancellationToken) - ; + newCancellationToken); } catch (OperationCanceledException) { @@ -419,8 +418,7 @@ namespace Tgstation.Server.Host.Controllers .ChatBots .AsQueryable() .Where(x => x.InstanceId == originalModel.Id) - .CountAsync(cancellationToken) - ; + .CountAsync(cancellationToken); if (countOfExistingChatBots > model.ChatBotLimit.Value) return Conflict(new ErrorMessageResponse(ErrorCode.ChatBotMax)); @@ -486,8 +484,7 @@ namespace Tgstation.Server.Host.Controllers job, (core, databaseContextFactory, paramJob, progressHandler, ct) // core will be null here since the instance is offline => InstanceOperations.MoveInstance(originalModel, originalModelPath, ct), - cancellationToken) - ; + cancellationToken); api.MoveJob = job.ToApi(); } @@ -548,8 +545,7 @@ namespace Tgstation.Server.Host.Controllers .Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix)) .Include(x => x.StartedBy).ThenInclude(x => x.CreatedBy) .Include(x => x.Instance) - .ToListAsync(cancellationToken) - ; + .ToListAsync(cancellationToken); var needsUpdate = false; var result = await Paginated( @@ -565,8 +561,7 @@ namespace Tgstation.Server.Host.Controllers }, page, pageSize, - cancellationToken) - ; + cancellationToken); if (needsUpdate) await DatabaseContext.Save(cancellationToken); @@ -625,8 +620,7 @@ namespace Tgstation.Server.Host.Controllers .SelectMany(x => x.Jobs) .Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix)) .Include(x => x.StartedBy).ThenInclude(x => x.CreatedBy) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); api.MoveJob = moveJob?.ToApi(); await CheckAccessible(api, cancellationToken); return Json(api); @@ -654,14 +648,12 @@ namespace Tgstation.Server.Host.Controllers var usersInstancePermissionSet = await BaseQuery() .SelectMany(x => x.InstancePermissionSets) .Where(x => x.PermissionSetId == AuthenticationContext.PermissionSet.Id.Value) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (usersInstancePermissionSet == default) { // does the instance actually exist? var instanceExists = await BaseQuery() - .AnyAsync(cancellationToken) - ; + .AnyAsync(cancellationToken); if (!instanceExists) return Gone(); @@ -700,8 +692,7 @@ namespace Tgstation.Server.Host.Controllers .GetAvailablePort( Math.Min((ushort)(ddPort.Value + 1), DefaultApiValidationPort), false, - cancellationToken) - ; + cancellationToken); if (!dmPort.HasValue) return null; diff --git a/src/Tgstation.Server.Host/Controllers/InstancePermissionSetController.cs b/src/Tgstation.Server.Host/Controllers/InstancePermissionSetController.cs index c04d169e3d..9caea8b34f 100644 --- a/src/Tgstation.Server.Host/Controllers/InstancePermissionSetController.cs +++ b/src/Tgstation.Server.Host/Controllers/InstancePermissionSetController.cs @@ -73,8 +73,7 @@ namespace Tgstation.Server.Host.Controllers { UserId = x.UserId, }) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (existingPermissionSet == default) return Gone(); @@ -86,8 +85,7 @@ namespace Tgstation.Server.Host.Controllers .AsQueryable() .Where(x => x.Id == existingPermissionSet.UserId.Value) .Select(x => x.CanonicalName) - .FirstAsync(cancellationToken) - ; + .FirstAsync(cancellationToken); if (userCanonicalName == Models.User.CanonicalizeName(Models.User.TgsSystemUserName)) return Forbid(); @@ -137,8 +135,7 @@ namespace Tgstation.Server.Host.Controllers .Where(x => x.Id == Instance.Id) .SelectMany(x => x.InstancePermissionSets) .Where(x => x.PermissionSetId == model.PermissionSetId) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (originalPermissionSet == null) return Gone(); @@ -219,8 +216,7 @@ namespace Tgstation.Server.Host.Controllers .Where(x => x.Id == Instance.Id) .SelectMany(x => x.InstancePermissionSets) .Where(x => x.PermissionSetId == id) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (permissionSet == default) return Gone(); return Json(permissionSet.ToApi()); @@ -246,8 +242,7 @@ namespace Tgstation.Server.Host.Controllers .Where(x => x.Id == Instance.Id) .SelectMany(x => x.InstancePermissionSets) .Where(x => x.PermissionSetId == id) - .DeleteAsync(cancellationToken) - ; + .DeleteAsync(cancellationToken); return numDeleted > 0 ? NoContent() : Gone(); } } diff --git a/src/Tgstation.Server.Host/Controllers/JobController.cs b/src/Tgstation.Server.Host/Controllers/JobController.cs index 577c3113bb..e361562e9e 100644 --- a/src/Tgstation.Server.Host/Controllers/JobController.cs +++ b/src/Tgstation.Server.Host/Controllers/JobController.cs @@ -127,8 +127,7 @@ namespace Tgstation.Server.Host.Controllers .AsQueryable() .Include(x => x.StartedBy) .Where(x => x.Id == id && x.Instance.Id == Instance.Id) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (job == default) return NotFound(); @@ -162,8 +161,7 @@ namespace Tgstation.Server.Host.Controllers .Where(x => x.Id == id && x.Instance.Id == Instance.Id) .Include(x => x.StartedBy) .Include(x => x.CancelledBy) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (job == default) return NotFound(); var api = job.ToApi(); diff --git a/src/Tgstation.Server.Host/Controllers/LimitedFileStreamResultExecutor.cs b/src/Tgstation.Server.Host/Controllers/LimitedFileStreamResultExecutor.cs index d26b0e355a..8af60dcb06 100644 --- a/src/Tgstation.Server.Host/Controllers/LimitedFileStreamResultExecutor.cs +++ b/src/Tgstation.Server.Host/Controllers/LimitedFileStreamResultExecutor.cs @@ -50,8 +50,7 @@ namespace Tgstation.Server.Host.Controllers outputStream, contentLength, BufferSize, - cancellationToken) - ; + cancellationToken); } else { @@ -61,8 +60,7 @@ namespace Tgstation.Server.Host.Controllers outputStream, rangeLength, BufferSize, - cancellationToken) - ; + cancellationToken); } } catch (OperationCanceledException) diff --git a/src/Tgstation.Server.Host/Controllers/RepositoryController.cs b/src/Tgstation.Server.Host/Controllers/RepositoryController.cs index 269165b4fd..10d671a134 100644 --- a/src/Tgstation.Server.Host/Controllers/RepositoryController.cs +++ b/src/Tgstation.Server.Host/Controllers/RepositoryController.cs @@ -103,8 +103,7 @@ namespace Tgstation.Server.Host.Controllers .RepositorySettings .AsQueryable() .Where(x => x.InstanceId == Instance.Id) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (currentModel == default) return Gone(); @@ -178,19 +177,16 @@ namespace Tgstation.Server.Host.Controllers databaseContext.Instances.Attach(instance); if (await PopulateApi(api, repos, databaseContext, instance, ct)) await databaseContext.Save(ct); - }) - ; + }); }, - cancellationToken) - ; + cancellationToken); api.Origin = model.Origin; api.Reference = model.Reference; api.ActiveJob = job.ToApi(); return Created(api); - }) - ; + }); } /// @@ -210,8 +206,7 @@ namespace Tgstation.Server.Host.Controllers .RepositorySettings .AsQueryable() .Where(x => x.InstanceId == Instance.Id) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (currentModel == default) return Gone(); @@ -233,8 +228,7 @@ namespace Tgstation.Server.Host.Controllers await jobManager.RegisterOperation( job, (core, databaseContextFactory, paramJob, progressReporter, ct) => core.RepositoryManager.DeleteRepository(ct), - cancellationToken) - ; + cancellationToken); api.ActiveJob = job.ToApi(); return Accepted(api); } @@ -258,8 +252,7 @@ namespace Tgstation.Server.Host.Controllers .RepositorySettings .AsQueryable() .Where(x => x.InstanceId == Instance.Id) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (currentModel == default) return Gone(); @@ -286,8 +279,7 @@ namespace Tgstation.Server.Host.Controllers } return Json(api); - }) - ; + }); } /// @@ -347,8 +339,7 @@ namespace Tgstation.Server.Host.Controllers .RepositorySettings .AsQueryable() .Where(x => x.InstanceId == Instance.Id) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (currentModel == default) return Gone(); @@ -412,8 +403,7 @@ namespace Tgstation.Server.Host.Controllers await PopulateApi(api, repo, DatabaseContext, Instance, cancellationToken); return null; - }) - ; + }); if (earlyOut != null) return earlyOut; diff --git a/src/Tgstation.Server.Host/Controllers/UserController.cs b/src/Tgstation.Server.Host/Controllers/UserController.cs index 78ed4839c7..f4cfd58898 100644 --- a/src/Tgstation.Server.Host/Controllers/UserController.cs +++ b/src/Tgstation.Server.Host/Controllers/UserController.cs @@ -195,8 +195,7 @@ namespace Tgstation.Server.Host.Controllers .Include(x => x.Group) .ThenInclude(x => x.PermissionSet) .Include(x => x.PermissionSet) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (originalUser == default) return NotFound(); @@ -265,8 +264,7 @@ namespace Tgstation.Server.Host.Controllers .AsQueryable() .Where(x => x.Id == model.Group.Id) .Include(x => x.PermissionSet) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (originalUser.Group == default) return Gone(); @@ -408,8 +406,7 @@ namespace Tgstation.Server.Host.Controllers .AsQueryable() .Where(x => x.Id == model.Group.Id) .Include(x => x.PermissionSet) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); else permissionSet = new Models.PermissionSet { diff --git a/src/Tgstation.Server.Host/Controllers/UserGroupController.cs b/src/Tgstation.Server.Host/Controllers/UserGroupController.cs index 1e38468534..a7269ebaf4 100644 --- a/src/Tgstation.Server.Host/Controllers/UserGroupController.cs +++ b/src/Tgstation.Server.Host/Controllers/UserGroupController.cs @@ -75,8 +75,7 @@ namespace Tgstation.Server.Host.Controllers var totalGroups = await DatabaseContext .Groups .AsQueryable() - .CountAsync(cancellationToken) - ; + .CountAsync(cancellationToken); if (totalGroups >= generalConfiguration.UserGroupLimit) return Conflict(new ErrorMessageResponse(ErrorCode.UserGroupLimitReached)); @@ -121,8 +120,7 @@ namespace Tgstation.Server.Host.Controllers .Where(x => x.Id == model.Id) .Include(x => x.PermissionSet) .Include(x => x.Users) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (currentGroup == default) return Gone(); @@ -167,8 +165,7 @@ namespace Tgstation.Server.Host.Controllers .Where(x => x.Id == id) .Include(x => x.Users) .Include(x => x.PermissionSet) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (group == default) return Gone(); return Json(group.ToApi(true)); @@ -220,8 +217,7 @@ namespace Tgstation.Server.Host.Controllers .Groups .AsQueryable() .Where(x => x.Id == id && x.Users.Count == 0) - .DeleteAsync(cancellationToken) - ; + .DeleteAsync(cancellationToken); if (numDeleted > 0) return NoContent(); @@ -231,8 +227,7 @@ namespace Tgstation.Server.Host.Controllers .Groups .AsQueryable() .Where(x => x.Id == id) - .AnyAsync(cancellationToken) - ; + .AnyAsync(cancellationToken); return groupExists ? Conflict(new ErrorMessageResponse(ErrorCode.UserGroupNotEmpty)) diff --git a/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs b/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs index b0dd4eb436..66e74968e0 100644 --- a/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs +++ b/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs @@ -215,8 +215,7 @@ namespace Tgstation.Server.Host.Database .Users .AsQueryable() .Where(x => x.CanonicalName == User.CanonicalizeName(User.TgsSystemUserName)) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (tgsUser != null) logger.LogError( @@ -233,8 +232,7 @@ namespace Tgstation.Server.Host.Database var allInstances = await databaseContext .Instances .AsQueryable() - .ToListAsync(cancellationToken) - ; + .ToListAsync(cancellationToken); foreach (var instance in allInstances) instance.Path = instance.Path.Replace('\\', '/'); } @@ -246,8 +244,7 @@ namespace Tgstation.Server.Host.Database .AsQueryable() .Where(x => x.TopicRequestTimeout == 0) .Select(x => x.Id) - .ToListAsync(cancellationToken) - ; + .ToListAsync(cancellationToken); var rowsUpdated = ids.Count; foreach (var id in ids) @@ -318,8 +315,7 @@ namespace Tgstation.Server.Host.Database .Include(x => x.CreatedBy) .Include(x => x.PermissionSet) .Include(x => x.Group) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (admin == default) SeedAdminUser(databaseContext); diff --git a/src/Tgstation.Server.Host/Jobs/JobService.cs b/src/Tgstation.Server.Host/Jobs/JobService.cs index 971150061a..efa0b09137 100644 --- a/src/Tgstation.Server.Host/Jobs/JobService.cs +++ b/src/Tgstation.Server.Host/Jobs/JobService.cs @@ -108,8 +108,7 @@ namespace Tgstation.Server.Host.Jobs if (job.StartedBy == null) job.StartedBy = await databaseContext .Users - .GetTgsUser(cancellationToken) - ; + .GetTgsUser(cancellationToken); else job.StartedBy = new User { diff --git a/src/Tgstation.Server.Host/Security/AuthenticationContextFactory.cs b/src/Tgstation.Server.Host/Security/AuthenticationContextFactory.cs index c28d1e1da1..24ab9707ea 100644 --- a/src/Tgstation.Server.Host/Security/AuthenticationContextFactory.cs +++ b/src/Tgstation.Server.Host/Security/AuthenticationContextFactory.cs @@ -76,8 +76,7 @@ namespace Tgstation.Server.Host.Security .Include(x => x.Group) .ThenInclude(x => x.PermissionSet) .Include(x => x.OAuthConnections) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (user == default) { logger.LogWarning("Unable to find user with ID {0}!", userId); @@ -110,8 +109,7 @@ namespace Tgstation.Server.Host.Security .AsQueryable() .Where(x => x.PermissionSetId == userPermissionSet.Id && x.InstanceId == instanceId && x.Instance.SwarmIdentifer == swarmConfiguration.Identifier) .Include(x => x.Instance) - .FirstOrDefaultAsync(cancellationToken) - ; + .FirstOrDefaultAsync(cancellationToken); if (instancePermissionSet == null) logger.LogDebug("User {0} does not have permissions on instance {1}!", userId, instanceId.Value); diff --git a/src/Tgstation.Server.Host/Security/ClaimsInjector.cs b/src/Tgstation.Server.Host/Security/ClaimsInjector.cs index 933e073ec0..de5dacc66e 100644 --- a/src/Tgstation.Server.Host/Security/ClaimsInjector.cs +++ b/src/Tgstation.Server.Host/Security/ClaimsInjector.cs @@ -68,8 +68,7 @@ namespace Tgstation.Server.Host.Security userId, apiHeaders.InstanceId, tokenValidatedContext.SecurityToken.ValidFrom, - cancellationToken) - ; + cancellationToken); var authenticationContext = authenticationContextFactory.CurrentAuthenticationContext; diff --git a/src/Tgstation.Server.Host/Setup/SetupWizard.cs b/src/Tgstation.Server.Host/Setup/SetupWizard.cs index 629dbf0a41..c4633f0846 100644 --- a/src/Tgstation.Server.Host/Setup/SetupWizard.cs +++ b/src/Tgstation.Server.Host/Setup/SetupWizard.cs @@ -176,8 +176,7 @@ namespace Tgstation.Server.Host.Setup await console.WriteAsync( $"API Port (leave blank for default of {GeneralConfiguration.DefaultApiPort}): ", false, - cancellationToken) - ; + cancellationToken); var portString = await console.ReadLineAsync(false, cancellationToken); if (String.IsNullOrWhiteSpace(portString)) return null; @@ -319,8 +318,7 @@ namespace Tgstation.Server.Host.Setup await console.WriteAsync(resolvedPath, true, cancellationToken); bool writeResolved = await PromptYesNo( "Would you like to save the relative path in the configuration? If not, the full path will be saved. (y/n): ", - cancellationToken) - ; + cancellationToken); if (writeResolved) databaseName = resolvedPath; @@ -344,23 +342,19 @@ namespace Tgstation.Server.Host.Setup await console.WriteAsync( "NOTE: It is HIGHLY reccommended that TGS runs on a complete relational database, specfically *NOT* Sqlite.", true, - cancellationToken) - ; + cancellationToken); await console.WriteAsync( "Sqlite, by nature cannot perform several DDL operations. Because of this future compatiblility cannot be guaranteed.", true, - cancellationToken) - ; + cancellationToken); await console.WriteAsync( "This means that you may not be able to update to the next minor version of TGS without a clean re-installation!", true, - cancellationToken) - ; + cancellationToken); await console.WriteAsync( "Please consider taking the time to set up a relational database if this is meant to be a long-standing server.", true, - cancellationToken) - ; + cancellationToken); await console.WriteAsync(String.Empty, true, cancellationToken); await asyncDelayer.Delay(TimeSpan.FromSeconds(3), cancellationToken); @@ -379,8 +373,7 @@ namespace Tgstation.Server.Host.Setup DatabaseType.SqlServer, DatabaseType.Sqlite), false, - cancellationToken) - ; + cancellationToken); var databaseTypeString = await console.ReadLineAsync(false, cancellationToken); if (Enum.TryParse(databaseTypeString, out var databaseType)) return databaseType; @@ -1007,8 +1000,7 @@ namespace Tgstation.Server.Host.Setup elasticSearchConfiguration, controlPanelConfiguration, swarmConfiguration, - cancellationToken) - ; + cancellationToken); } /// diff --git a/src/Tgstation.Server.Host/Swarm/SwarmService.cs b/src/Tgstation.Server.Host/Swarm/SwarmService.cs index 8f8a5ce7a9..14bb870554 100644 --- a/src/Tgstation.Server.Host/Swarm/SwarmService.cs +++ b/src/Tgstation.Server.Host/Swarm/SwarmService.cs @@ -965,8 +965,7 @@ namespace Tgstation.Server.Host.Swarm await Task.WhenAll( currentSwarmServers .Where(x => !x.Controller) - .Select(HealthRequestForServer)) - ; + .Select(HealthRequestForServer)); lock (swarmServers) if (swarmServers.Count != currentSwarmServers.Count) diff --git a/src/Tgstation.Server.Host/Utils/PortAllocator.cs b/src/Tgstation.Server.Host/Utils/PortAllocator.cs index 87ac3ecd05..9b125103d0 100644 --- a/src/Tgstation.Server.Host/Utils/PortAllocator.cs +++ b/src/Tgstation.Server.Host/Utils/PortAllocator.cs @@ -67,16 +67,14 @@ namespace Tgstation.Server.Host.Utils .AsQueryable() .Where(x => x.Instance.SwarmIdentifer == swarmConfiguration.Identifier) .Select(x => x.Port) - .ToListAsync(cancellationToken) - ; + .ToListAsync(cancellationToken); var dmPorts = await databaseContext .DreamMakerSettings .AsQueryable() .Where(x => x.Instance.SwarmIdentifer == swarmConfiguration.Identifier) .Select(x => x.ApiValidationPort) - .ToListAsync(cancellationToken) - ; + .ToListAsync(cancellationToken); var exceptions = new List(); ushort port = 0; From a02f9966c2ca37d9c215c584326cd970f066ce08 Mon Sep 17 00:00:00 2001 From: Dominion Date: Thu, 8 Jun 2023 03:37:17 -0400 Subject: [PATCH 19/29] Fixes #1527 --- .../Security/WindowsSystemIdentityFactory.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs b/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs index 3646d71cd1..774170c4a4 100644 --- a/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs +++ b/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs @@ -63,11 +63,15 @@ namespace Tgstation.Server.Host.Security PrincipalContext pc = null; UserPrincipal principal = null; + GetUserAndDomainName(user.SystemIdentifier, out _, out var domainName); + bool TryGetPrincipalFromContextType(ContextType contextType) { try { - pc = new PrincipalContext(contextType); + pc = domainName != null + ? new PrincipalContext(contextType, domainName) + : new PrincipalContext(contextType); cancellationToken.ThrowIfCancellationRequested(); principal = UserPrincipal.FindByIdentity(pc, user.SystemIdentifier); } From db32ca5f2973ff4301511926c72d2e71ea6b5a8e Mon Sep 17 00:00:00 2001 From: Dominion Date: Thu, 8 Jun 2023 03:38:55 -0400 Subject: [PATCH 20/29] Improve logging here --- .../Security/WindowsSystemIdentityFactory.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs b/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs index 774170c4a4..47ec0f3e3d 100644 --- a/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs +++ b/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs @@ -79,9 +79,13 @@ namespace Tgstation.Server.Host.Security { throw; } - catch (Exception e) + catch (Exception ex) { - logger.LogWarning(e, "Error loading user for context type {0}!", contextType); + logger.LogDebug( + ex, + "Error loading user for context type {contextType} and principal \"{domainName}\"!", + contextType, + domainName); } finally { From 2e02c1a239283f6b85f882059ac0cfe671576826 Mon Sep 17 00:00:00 2001 From: Dominion Date: Thu, 8 Jun 2023 03:45:36 -0400 Subject: [PATCH 21/29] Trace log these exceptions --- .../Controllers/ApiController.cs | 7 ++++++- .../Controllers/ConfigurationController.cs | 20 +++++++++---------- .../Controllers/HomeController.cs | 2 +- .../Controllers/UserController.cs | 4 ++-- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/Tgstation.Server.Host/Controllers/ApiController.cs b/src/Tgstation.Server.Host/Controllers/ApiController.cs index 6f03152921..46c9730b4c 100644 --- a/src/Tgstation.Server.Host/Controllers/ApiController.cs +++ b/src/Tgstation.Server.Host/Controllers/ApiController.cs @@ -219,8 +219,13 @@ namespace Tgstation.Server.Host.Controllers /// /// Generic 501 response. /// + /// The that was thrown. /// An with . - protected ObjectResult RequiresPosixSystemIdentity() => StatusCode(HttpStatusCode.NotImplemented, new ErrorMessageResponse(ErrorCode.RequiresPosixSystemIdentity)); + protected ObjectResult RequiresPosixSystemIdentity(NotImplementedException ex) + { + Logger.LogTrace(ex, "System identities not implemented!"); + return StatusCode(HttpStatusCode.NotImplemented, new ErrorMessageResponse(ErrorCode.RequiresPosixSystemIdentity)); + } /// /// Strongly type calls to . diff --git a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs index d1ea19855c..b563293511 100644 --- a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs +++ b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs @@ -97,9 +97,9 @@ namespace Tgstation.Server.Host.Controllers AdditionalData = e.Message, }); } - catch (NotImplementedException) + catch (NotImplementedException ex) { - return RequiresPosixSystemIdentity(); + return RequiresPosixSystemIdentity(ex); } } @@ -142,9 +142,9 @@ namespace Tgstation.Server.Host.Controllers AdditionalData = e.Message, }); } - catch (NotImplementedException) + catch (NotImplementedException ex) { - return RequiresPosixSystemIdentity(); + return RequiresPosixSystemIdentity(ex); } } @@ -188,10 +188,10 @@ namespace Tgstation.Server.Host.Controllers .AsQueryable() .OrderBy(x => x.Path)); } - catch (NotImplementedException) + catch (NotImplementedException ex) { return new PaginatableResult( - RequiresPosixSystemIdentity()); + RequiresPosixSystemIdentity(ex)); } catch (UnauthorizedAccessException) { @@ -262,9 +262,9 @@ namespace Tgstation.Server.Host.Controllers Message = e.Message, }); } - catch (NotImplementedException) + catch (NotImplementedException ex) { - return RequiresPosixSystemIdentity(); + return RequiresPosixSystemIdentity(ex); } catch (UnauthorizedAccessException) { @@ -302,9 +302,9 @@ namespace Tgstation.Server.Host.Controllers ? NoContent() : Conflict(new ErrorMessageResponse(ErrorCode.ConfigurationDirectoryNotEmpty))); } - catch (NotImplementedException) + catch (NotImplementedException ex) { - return RequiresPosixSystemIdentity(); + return RequiresPosixSystemIdentity(ex); } catch (UnauthorizedAccessException) { diff --git a/src/Tgstation.Server.Host/Controllers/HomeController.cs b/src/Tgstation.Server.Host/Controllers/HomeController.cs index ac9e666be8..0bce69891c 100644 --- a/src/Tgstation.Server.Host/Controllers/HomeController.cs +++ b/src/Tgstation.Server.Host/Controllers/HomeController.cs @@ -241,7 +241,7 @@ namespace Tgstation.Server.Host.Controllers } catch (NotImplementedException ex) { - Logger.LogTrace(ex, "System identities not implemented!"); + RequiresPosixSystemIdentity(ex); } using (systemIdentity) diff --git a/src/Tgstation.Server.Host/Controllers/UserController.cs b/src/Tgstation.Server.Host/Controllers/UserController.cs index f4cfd58898..c9c8a33714 100644 --- a/src/Tgstation.Server.Host/Controllers/UserController.cs +++ b/src/Tgstation.Server.Host/Controllers/UserController.cs @@ -127,9 +127,9 @@ namespace Tgstation.Server.Host.Controllers dbUser.Name = sysIdentity.Username; dbUser.SystemIdentifier = sysIdentity.Uid; } - catch (NotImplementedException) + catch (NotImplementedException ex) { - return RequiresPosixSystemIdentity(); + return RequiresPosixSystemIdentity(ex); } else if (!(model.Password?.Length == 0 && model.OAuthConnections?.Any() == true)) { From 0af376825afd1f6aad25c64e6a6c74edff548a6e Mon Sep 17 00:00:00 2001 From: Dominion Date: Thu, 8 Jun 2023 03:50:15 -0400 Subject: [PATCH 22/29] Fixes #1528 --- src/Tgstation.Server.Host/Controllers/UserController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Controllers/UserController.cs b/src/Tgstation.Server.Host/Controllers/UserController.cs index c9c8a33714..d5769889da 100644 --- a/src/Tgstation.Server.Host/Controllers/UserController.cs +++ b/src/Tgstation.Server.Host/Controllers/UserController.cs @@ -219,7 +219,7 @@ namespace Tgstation.Server.Host.Controllers if (model.Password != null) { - if (model.SystemIdentifier != null) + if (originalUser.SystemIdentifier != null) return BadRequest(new ErrorMessageResponse(ErrorCode.UserMismatchPasswordSid)); var result = TrySetPassword(originalUser, model.Password, false); From 685742012c560e72189296d1e5ce609f280bf7dd Mon Sep 17 00:00:00 2001 From: Dominion Date: Thu, 8 Jun 2023 04:02:01 -0400 Subject: [PATCH 23/29] Clean up side effects of #1528 --- .../Controllers/HomeController.cs | 37 ++++++++++++++----- .../Controllers/UserController.cs | 13 ++++++- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/Tgstation.Server.Host/Controllers/HomeController.cs b/src/Tgstation.Server.Host/Controllers/HomeController.cs index 0bce69891c..731be3182f 100644 --- a/src/Tgstation.Server.Host/Controllers/HomeController.cs +++ b/src/Tgstation.Server.Host/Controllers/HomeController.cs @@ -297,6 +297,7 @@ namespace Tgstation.Server.Host.Controllers PasswordHash = x.PasswordHash, Enabled = x.Enabled, Name = x.Name, + SystemIdentifier = x.SystemIdentifier, }) .ToListAsync(cancellationToken); @@ -315,13 +316,13 @@ namespace Tgstation.Server.Host.Controllers // FALLBACK TO THE DB USER HERE, DO NOT REVEAL A SYSTEM LOGIN!!! // This of course, allows system users to discover TGS users in this (HIGHLY IMPROBABLE) case but that is not our fault var originalHash = user.PasswordHash; - var isDbUser = originalHash != null; - bool usingSystemIdentity = systemIdentity != null && !isDbUser; + var isLikelyDbUser = originalHash != null; + bool usingSystemIdentity = systemIdentity != null && !isLikelyDbUser; if (!oAuthLogin) if (!usingSystemIdentity) { // DB User password check and update - if (originalHash == null || !cryptographySuite.CheckUserPassword(user, ApiHeaders.Password)) + if (!isLikelyDbUser || !cryptographySuite.CheckUserPassword(user, ApiHeaders.Password)) return Unauthorized(); if (user.PasswordHash != originalHash) { @@ -335,14 +336,30 @@ namespace Tgstation.Server.Host.Controllers await DatabaseContext.Save(cancellationToken); } } - else if (systemIdentity.Username != user.Name) + else { - // System identity username change update - Logger.LogDebug("User ID {userId}'s system identity needs a refresh, updating database.", user.Id); - DatabaseContext.Users.Attach(user); - user.Name = systemIdentity.Username; - user.CanonicalName = Models.User.CanonicalizeName(user.Name); - await DatabaseContext.Save(cancellationToken); + var usernameMismatch = systemIdentity.Username != user.Name; + if (isLikelyDbUser || usernameMismatch) + { + DatabaseContext.Users.Attach(user); + if (isLikelyDbUser) + { + // cleanup from https://github.com/tgstation/tgstation-server/issues/1528 + Logger.LogDebug("System user ID {userId}'s PasswordHash is polluted, updating database.", user.Id); + user.PasswordHash = null; + user.LastPasswordUpdate = DateTimeOffset.UtcNow; + } + + if (usernameMismatch) + { + // System identity username change update + Logger.LogDebug("User ID {userId}'s system identity needs a refresh, updating database.", user.Id); + user.Name = systemIdentity.Username; + user.CanonicalName = Models.User.CanonicalizeName(user.Name); + } + + await DatabaseContext.Save(cancellationToken); + } } // Now that the bookeeping is done, tell them to fuck off if necessary diff --git a/src/Tgstation.Server.Host/Controllers/UserController.cs b/src/Tgstation.Server.Host/Controllers/UserController.cs index d5769889da..6b4dee0594 100644 --- a/src/Tgstation.Server.Host/Controllers/UserController.cs +++ b/src/Tgstation.Server.Host/Controllers/UserController.cs @@ -144,7 +144,7 @@ namespace Tgstation.Server.Host.Controllers await DatabaseContext.Save(cancellationToken); - Logger.LogInformation("Created new user {0} ({1})", dbUser.Name, dbUser.Id); + Logger.LogInformation("Created new user {name} ({id})", dbUser.Name, dbUser.Id); return Created(dbUser.ToApi()); } @@ -214,12 +214,21 @@ namespace Tgstation.Server.Host.Controllers || (!oAuthEdit && model.OAuthConnections != null)) return Forbid(); + var originalUserHasSid = originalUser.SystemIdentifier != null; + if (originalUserHasSid && originalUser.PasswordHash != null) + { + // cleanup from https://github.com/tgstation/tgstation-server/issues/1528 + Logger.LogDebug("System user ID {userId}'s PasswordHash is polluted, updating database.", originalUser.Id); + originalUser.PasswordHash = null; + originalUser.LastPasswordUpdate = DateTimeOffset.UtcNow; + } + if (model.SystemIdentifier != null && model.SystemIdentifier != originalUser.SystemIdentifier) return BadRequest(new ErrorMessageResponse(ErrorCode.UserSidChange)); if (model.Password != null) { - if (originalUser.SystemIdentifier != null) + if (originalUserHasSid) return BadRequest(new ErrorMessageResponse(ErrorCode.UserMismatchPasswordSid)); var result = TrySetPassword(originalUser, model.Password, false); From 5d129e2fd5be48ca1107357ad3df06e07083dfbf Mon Sep 17 00:00:00 2001 From: Dominion Date: Fri, 9 Jun 2023 09:29:57 -0400 Subject: [PATCH 24/29] Only run code scanning if a repository variable is present Closes #1524 --- .github/workflows/code-scanning.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/code-scanning.yml b/.github/workflows/code-scanning.yml index 4118c5b1cf..6f663d5153 100644 --- a/.github/workflows/code-scanning.yml +++ b/.github/workflows/code-scanning.yml @@ -20,6 +20,7 @@ jobs: analyze: name: Code Scanning runs-on: ubuntu-latest + if: ${{ vars.TGS_ENABLE_CODE_QL }} == 'true' steps: - name: Install Node 12.X uses: actions/setup-node@v3 From 8c8328567cd079fb7770c493a508fd20fde3fd24 Mon Sep 17 00:00:00 2001 From: Dominion Date: Fri, 9 Jun 2023 09:56:49 -0400 Subject: [PATCH 25/29] CONTRIBUTING.md updates --- .github/CONTRIBUTING.md | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 934067d85f..06893a9fa1 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -41,14 +41,35 @@ The recommended IDE is Visual Studio 2019 which has installation options for bot In order to run the integration tests you must have the following environment variables set. To run them more accurately, include the optional ones. - `TGS_TEST_DATABASE_TYPE`: `MySql`, `MariaDB`, `PostgresSql`, or `SqlServer`. - `TGS_TEST_CONNECTION_STRING`: To a valid database connection string. You can use the setup wizard to create one. -- `TGS_TEST_BRANCH`: Should be either `dev` or `master` depending on what you are working off of. Used for repository tests. - (Optional) `TGS_TEST_GITHUB_TOKEN`: A GitHub personal access token with no scopes used to bypass rate limits. - (Optional) The following variables are all interdependent, so if one is set they all must be. - - `TSG_TEST_DISCORD_TOKEN`: To a valid discord bot token. + - `TGS_TEST_DISCORD_TOKEN`: To a valid discord bot token. - `TGS_TEST_DISCORD_CHANNEL`: To a valid discord channel ID that the above bot can access. - `TGS_TEST_IRC_CONNECTION_STRING`: To a valid IRC connection string. See the code for [IrcConnectionStringBuilder](../src/Tgstation.Server.Api/Models/IrcConnectionStringBuilder.cs) for details. - `TGS_TEST_IRC_CHANNEL`: To a valid IRC channel accessible with the above connection. +### Notes About Forks + +For the full CI gambit, the following repository configuration must be set: + +- Setting `Workflow Permissions` to `Read and write permissions`: Enables CodeQL uploads and GitHub Actions comments. +![image](https://github.com/tgstation/tgstation-server/assets/8171642/ab17fa74-364f-4e66-b7c4-b9bb24c6a599) +- Label `CI Cleared`: To allow PRs from forks to run CI with secrets after approval. +- Integration [CodeCov](https://github.com/apps/codecov): Enables CodeCov status checks. +- Variable `TGS_ENABLE_CODE_QL` to `true`: Enables CodeQL scanning in actions. +- Secret `LIVE_TESTS_TOKEN`: A GitHub token with read access to the repository and write access to https://github.com/Cyberboss/common_core (TODO: Make the target repository here configurable). Despite it's name, it may be used across the entire test suite. +- Secret `TGS_TEST_DISCORD_TOKEN`: See above note about test environment variables. +- Secret `TGS_TEST_DISCORD_CHANNEL`: See above note about test environment variables. +- Secret `TGS_TEST_IRC_CONNECTION_STRING`: See above note about test environment variables. +- Secret `TGS_TEST_IRC_CHANNEL`: See above note about test environment variables. + +If you don't plan on deploying TGS, the following secrets can be omitted: + +- Secret `DEV_PUSH_TOKEN`: A GitHub token with write access to the repository. Enables doxygen pushes to `gh-pages` branch, and releases creation. +- Secret `DOCKER_USERNAME`: Login username for Docker image push. +- Secret `DOCKER_PASSWORD`: Login password for Docker image push. +- Secret `NUGET_API_KEY`: Nuget.org API Key for client libraries push. + ### Know your Code - All feature work should be submitted to the `dev` branch for the next minor release. @@ -222,6 +243,7 @@ We have several subcomponent APIs we ship with the core server that have their o - HTTP API - DreamMaker API +- Interop API - Configuration File - Host Watchdog - Web Control Panel @@ -235,19 +257,19 @@ All versions are stored in the master file [build/Version.props](../build/Versio The NuGet package Tgstation.Server.Client is another part of the suite which should be versioned separately. However, Tgstation.Server.Api is also a package that is published, and breaking changes can happen independantly of each other. - Consider Tgstation.Server.Client it's own product, perform major and minor bumps according to semver semantics including the Tgstation.Server.Api code (but not the version). -- Tgstation.Server.Api is a bit tricky as breaking code changes may occur without affecting the actual HTTP contract. For this reason, all code changes that do this should be pushed out as patches, even if they contain breaking changes. +- Tgstation.Server.Api is a bit tricky as breaking code changes may occur without affecting the actual HTTP contract. For this reason, the library itself is versioned separately from the API contract. ## Triage, Deployment, and Releasing _This section mainly applies to people with write access to the repository. Anyone is free to propose their work and maintainers will triage it appropriately._ -When issues affecting the server come in, they should be lebeled appropriately and either put into the `Backlog` milestone or current patch milestone depending on if it's a feature request or bug. +When issues affecting the server come in, they should be labeled appropriately and either put into the `Backlog` milestone or current patch milestone depending on if it's a feature request or bug. After a minor release, the team should decide at that time what will go into it and setup the milestone accordingly. At this point the `Backlog` label should be removed and replaced with `Ready` and the milestone changed from `Backlog` to `vX.Y.0` with X/Y being the major/minor release versions respectively. Assign work before beginning on it. When work is started, replace the `Ready` label with the `Work In Progress` label. -Word commit names descriptively. Only submit work through pull requests. When doing so, link the issue you'll be closing and set the milestone appropriately. Don't forget a changelog. **WARNING** Remember to submit patches to the `master` branch. Also at the time of this writing there appears to be an issue where GitHub won't close issues with PRs to the non-default branch. Maintainers may need to do this manually after merging. +Word commit names descriptively. Only submit work through pull requests (With the exception of resolving conflicts in the `master` -> `dev` automatic merge). When doing so, link the issue you'll be closing and set the milestone appropriately. Don't forget a changelog. **WARNING** Remember to submit patches to the `master` branch. Also at the time of this writing there appears to be an issue where GitHub won't close issues with PRs to the non-default branch. Maintainers may need to do this manually after merging. Alternatively, add closing keywords to the commit message and let the `master` -> `dev` merge do the closing. At the time of this writing, the repository is configured to automate much of the deployment/release process. @@ -257,10 +279,10 @@ That step should be taken for the latest API and client before releasing the cor Before releasing the core version, ensure the following: -- For minor releases, ensure your changes are merging `dev` into `master`. +- For minor/major releases, ensure your changes are merging `dev` into `master`. - Ensure all issues and pull requests in the associated milestone are closed (aside from the PR you are using to cut the release). -To perform the release, merge the PR with `[TGSDeploy]` in the commit message. The build system will handle generating release notes, packaging, and pushing the build to GitHub releases. This will also make it available for servers to self update. +To perform the release, merge the PR with `[TGSDeploy]` in the commit message. The build system will handle generating release notes, packaging, and pushing the build to GitHub releases. This will also make it available for servers to self update. Note that `[TGSDeploy]` only affects commits on the `master` branch. The build system will also handle closing the current milestone and creating new minor/patch milestones where applicable. From 472a1c551ecbbb1e334b7c8b5f45758d9ef26871 Mon Sep 17 00:00:00 2001 From: Dominion Date: Fri, 9 Jun 2023 09:57:01 -0400 Subject: [PATCH 26/29] Only auto-approve my work on main or my fork --- .github/workflows/auto-approve-dominions-prs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/auto-approve-dominions-prs.yml b/.github/workflows/auto-approve-dominions-prs.yml index b9fd7da731..c4dfd797cf 100644 --- a/.github/workflows/auto-approve-dominions-prs.yml +++ b/.github/workflows/auto-approve-dominions-prs.yml @@ -12,7 +12,7 @@ on: jobs: approve-pr-if-dominion-is-author: name: Approve PR if Dominion is Author - if: github.event.pull_request.user.login == 'Cyberboss' + if: github.event.pull_request.user.login == 'Cyberboss' && (github.event.pull_request.base.repo.owner.login == 'tgstation' || && github.event.pull_request.base.repo.owner.login == 'Cyberboss') runs-on: ubuntu-latest steps: - name: GitHub API Call From d6faba8159d3c591fe333841a68f475bac842d9c Mon Sep 17 00:00:00 2001 From: Dominion Date: Fri, 9 Jun 2023 10:21:34 -0400 Subject: [PATCH 27/29] Add secrets.CODECOV_TOKEN --- .github/CONTRIBUTING.md | 3 ++- .github/workflows/ci-suite.yml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 06893a9fa1..b94bfc9870 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -55,8 +55,9 @@ For the full CI gambit, the following repository configuration must be set: - Setting `Workflow Permissions` to `Read and write permissions`: Enables CodeQL uploads and GitHub Actions comments. ![image](https://github.com/tgstation/tgstation-server/assets/8171642/ab17fa74-364f-4e66-b7c4-b9bb24c6a599) - Label `CI Cleared`: To allow PRs from forks to run CI with secrets after approval. -- Integration [CodeCov](https://github.com/apps/codecov): Enables CodeCov status checks. - Variable `TGS_ENABLE_CODE_QL` to `true`: Enables CodeQL scanning in actions. +- Integration [CodeCov](https://github.com/apps/codecov): Enables CodeCov status checks. +- Secret `CODECOV_TOKEN`: A CodeCov repo token to work around https://github.com/codecov/codecov-action/issues/837. - Secret `LIVE_TESTS_TOKEN`: A GitHub token with read access to the repository and write access to https://github.com/Cyberboss/common_core (TODO: Make the target repository here configurable). Despite it's name, it may be used across the entire test suite. - Secret `TGS_TEST_DISCORD_TOKEN`: See above note about test environment variables. - Secret `TGS_TEST_DISCORD_CHANNEL`: See above note about test environment variables. diff --git a/.github/workflows/ci-suite.yml b/.github/workflows/ci-suite.yml index 1a4447aed3..05dff98f23 100644 --- a/.github/workflows/ci-suite.yml +++ b/.github/workflows/ci-suite.yml @@ -19,6 +19,7 @@ env: TGS_DOTNET_VERSION: 6.0.x TGS_TEST_GITHUB_TOKEN: ${{ secrets.LIVE_TESTS_TOKEN }} TGS_RELEASE_NOTES_TOKEN: ${{ secrets.DEV_PUSH_TOKEN }} + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} concurrency: group: "ci-${{ github.head_ref || github.run_id }}-${{ github.event_name }}" From 820b76c0aa87d852fb918021f363943cfbc776b5 Mon Sep 17 00:00:00 2001 From: Dominion Date: Fri, 9 Jun 2023 11:51:02 -0400 Subject: [PATCH 28/29] Probably fixes code scanning permissions --- .github/workflows/code-scanning.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/code-scanning.yml b/.github/workflows/code-scanning.yml index 6f663d5153..617148d18a 100644 --- a/.github/workflows/code-scanning.yml +++ b/.github/workflows/code-scanning.yml @@ -2,7 +2,7 @@ name: 'Code Scanning' on: schedule: - - cron: 0 23 * * * + - cron: 0 23 * * 1 push: branches: - dev @@ -20,6 +20,10 @@ jobs: analyze: name: Code Scanning runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write if: ${{ vars.TGS_ENABLE_CODE_QL }} == 'true' steps: - name: Install Node 12.X @@ -43,3 +47,5 @@ jobs: - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 + with: + category: "/language:csharp" From eb0c631e849af8de346682ded619eabddf268225 Mon Sep 17 00:00:00 2001 From: Dominion Date: Fri, 9 Jun 2023 13:41:34 -0400 Subject: [PATCH 29/29] Handler for another possible DumpTests exception --- tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs index aff010522d..1621b8fe7f 100644 --- a/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs +++ b/tests/Tgstation.Server.Tests/Live/Instance/WatchdogTest.cs @@ -255,7 +255,8 @@ namespace Tgstation.Server.Tests.Live.Instance && (job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: E_ACCESSDENIED: Access is denied.") || job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: E_HANDLE: The handle is invalid.") || job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: 3489660936: Unknown error (0xd0000008)") - || job.ExceptionDetails.Contains("System.InvalidOperationException: No process is associated with this object.")))) + || job.ExceptionDetails.Contains("System.InvalidOperationException: No process is associated with this object.") + || job.ExceptionDetails.Contains("BetterWin32Errors.Win32Exception: 2147942424: The program issued a command but the command length is incorrect.")))) break; var restartJob = await instanceClient.DreamDaemon.Restart(cancellationToken);