From 06c1c8d827d3ce93cb07606cc81bdbfd06562c16 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 10 Aug 2021 20:35:46 -0400 Subject: [PATCH] Adds the accessible property to InstanceResponse --- build/Version.props | 6 +-- .../Models/Response/InstanceResponse.cs | 5 +++ .../Controllers/ApiController.cs | 14 +++---- .../Controllers/ChatController.cs | 2 + .../Controllers/InstanceController.cs | 21 ++++++++++- .../Controllers/JobController.cs | 4 +- .../InstanceManagerTest.cs | 37 +++++++++++-------- 7 files changed, 62 insertions(+), 27 deletions(-) diff --git a/build/Version.props b/build/Version.props index 5ea3946d4c..fe06dbb32e 100644 --- a/build/Version.props +++ b/build/Version.props @@ -5,9 +5,9 @@ 4.12.1 3.1.0 - 9.0.1 - 9.0.0 - 10.0.0 + 9.1.0 + 9.1.0 + 10.1.0 6.0.4 5.3.0 1.1.1 diff --git a/src/Tgstation.Server.Api/Models/Response/InstanceResponse.cs b/src/Tgstation.Server.Api/Models/Response/InstanceResponse.cs index 6ca3937782..3c6908e76b 100644 --- a/src/Tgstation.Server.Api/Models/Response/InstanceResponse.cs +++ b/src/Tgstation.Server.Api/Models/Response/InstanceResponse.cs @@ -11,5 +11,10 @@ /// Due to how s are children of s but moving one requires the to be offline, interactions with this are performed in a non-standard fashion. The is read by querying the again (either via list or ID lookup) and cancelled by making any sort of update to the . Once the comes back it can be queried like a normal job. [ResponseOptions] public JobResponse? MoveJob { get; set; } + + /// + /// If the querying user may access this instance. + /// + public bool Accessible { get; set; } } } diff --git a/src/Tgstation.Server.Host/Controllers/ApiController.cs b/src/Tgstation.Server.Host/Controllers/ApiController.cs index d1c9f9f0ea..6e591ca574 100644 --- a/src/Tgstation.Server.Host/Controllers/ApiController.cs +++ b/src/Tgstation.Server.Host/Controllers/ApiController.cs @@ -296,14 +296,14 @@ namespace Tgstation.Server.Host.Controllers /// /// The of model being generated and returned. /// A resulting in a resulting in the generated . - /// An to transform the s after being queried. + /// A to transform the s after being queried. /// The requested page from the query. /// The requested page size from the query. /// The for the operation. /// A resulting in the for the operation. protected Task Paginated( Func>> queryGenerator, - Action resultTransformer, + Func resultTransformer, int? pageQuery, int? pageSizeQuery, CancellationToken cancellationToken) => PaginatedImpl( @@ -319,14 +319,14 @@ namespace Tgstation.Server.Host.Controllers /// The of model being generated. /// The of model being returned. /// A resulting in a resulting in the generated . - /// An to transform the s after being queried. + /// A to transform the s after being queried. /// The requested page from the query. /// The requested page size from the query. /// The for the operation. /// A resulting in the for the operation. protected Task Paginated( Func>> queryGenerator, - Action resultTransformer, + Func resultTransformer, int? pageQuery, int? pageSizeQuery, CancellationToken cancellationToken) @@ -344,14 +344,14 @@ namespace Tgstation.Server.Host.Controllers /// The of model being generated. If different from , must implement for . /// The of model being returned. /// A resulting in a resulting in the generated . - /// An to transform the s after being queried. + /// A to transform the s after being queried. /// The requested page from the query. /// The requested page size from the query. /// The for the operation. /// A resulting in the for the operation. async Task PaginatedImpl( Func>> queryGenerator, - Action resultTransformer, + Func resultTransformer, int? pageQuery, int? pageSizeQuery, CancellationToken cancellationToken) @@ -406,7 +406,7 @@ namespace Tgstation.Server.Host.Controllers if (resultTransformer != null) foreach (var finalResult in finalResults) - resultTransformer(finalResult); + await resultTransformer(finalResult).ConfigureAwait(false); var carryTheOne = totalResults % pageSize != 0 ? 1 diff --git a/src/Tgstation.Server.Host/Controllers/ChatController.cs b/src/Tgstation.Server.Host/Controllers/ChatController.cs index 75a1e52459..81f1968110 100644 --- a/src/Tgstation.Server.Host/Controllers/ChatController.cs +++ b/src/Tgstation.Server.Host/Controllers/ChatController.cs @@ -198,6 +198,8 @@ namespace Tgstation.Server.Host.Controllers { if (connectionStrings) chatBot.ConnectionString = null; + + return Task.CompletedTask; }, page, pageSize, diff --git a/src/Tgstation.Server.Host/Controllers/InstanceController.cs b/src/Tgstation.Server.Host/Controllers/InstanceController.cs index 6211183321..5c3d809f88 100644 --- a/src/Tgstation.Server.Host/Controllers/InstanceController.cs +++ b/src/Tgstation.Server.Host/Controllers/InstanceController.cs @@ -264,6 +264,7 @@ namespace Tgstation.Server.Host.Controllers Logger.LogInformation("{0} {1} instance {2}: {3} ({4})", AuthenticationContext.User.Name, attached ? "attached" : "created", newInstance.Name, newInstance.Id, newInstance.Path); var api = newInstance.ToApi(); + api.Accessible = true; // instances are always accessible by their creator return attached ? (IActionResult)Json(api) : Created(api); } @@ -495,6 +496,7 @@ namespace Tgstation.Server.Host.Controllers await componentInstance.SetAutoUpdateInterval(model.AutoUpdateInterval.Value).ConfigureAwait(false); } + await CheckAccessible(api, cancellationToken).ConfigureAwait(false); return moving ? (IActionResult)Accepted(api) : Json(api); } #pragma warning restore CA1502 @@ -552,10 +554,11 @@ namespace Tgstation.Server.Host.Controllers new PaginatableResult( GetBaseQuery() .OrderBy(x => x.Id))), - instance => + async instance => { needsUpdate |= InstanceRequiredController.ValidateInstanceOnlineStatus(instanceManager, Logger, instance); instance.MoveJob = moveJobs.FirstOrDefault(x => x.Instance.Id == instance.Id)?.ToApi(); + await CheckAccessible(instance, cancellationToken).ConfigureAwait(false); }, page, pageSize, @@ -623,6 +626,7 @@ namespace Tgstation.Server.Host.Controllers .FirstOrDefaultAsync(cancellationToken) .ConfigureAwait(false); api.MoveJob = moveJob?.ToApi(); + await CheckAccessible(api, cancellationToken).ConfigureAwait(false); return Json(api); } @@ -785,5 +789,20 @@ namespace Tgstation.Server.Host.Controllers return path; } + + /// + /// Populate the property of a given . + /// + /// The to populate. + /// The for the operation. + /// A representing the running operation. + async Task CheckAccessible(InstanceResponse instanceResponse, CancellationToken cancellationToken) + { + instanceResponse.Accessible = await DatabaseContext + .InstancePermissionSets + .AsQueryable() + .Where(x => x.InstanceId == instanceResponse.Id && x.PermissionSetId == AuthenticationContext.PermissionSet.Id) + .AnyAsync(cancellationToken); + } } } diff --git a/src/Tgstation.Server.Host/Controllers/JobController.cs b/src/Tgstation.Server.Host/Controllers/JobController.cs index 1314d498e4..fe8e8322a9 100644 --- a/src/Tgstation.Server.Host/Controllers/JobController.cs +++ b/src/Tgstation.Server.Host/Controllers/JobController.cs @@ -175,9 +175,11 @@ namespace Tgstation.Server.Host.Controllers /// Supplements s with their . /// /// The to augment. - private void AddJobProgressResponseTransformer(JobResponse jobResponse) + /// A representing the running operation. + private Task AddJobProgressResponseTransformer(JobResponse jobResponse) { jobResponse.Progress = jobManager.JobProgress(jobResponse); + return Task.CompletedTask; } } } diff --git a/tests/Tgstation.Server.Tests/InstanceManagerTest.cs b/tests/Tgstation.Server.Tests/InstanceManagerTest.cs index 7aed5d0fe7..0d26de39ef 100644 --- a/tests/Tgstation.Server.Tests/InstanceManagerTest.cs +++ b/tests/Tgstation.Server.Tests/InstanceManagerTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Linq; using System.Net; @@ -181,22 +181,20 @@ namespace Tgstation.Server.Tests var token = serverClient.Token.Bearer; // check that 400s are returned appropriately using var httpClient = new HttpClient(); - using (var request = new HttpRequestMessage(HttpMethod.Get, url.ToString() + Routes.ListRoute(Routes.InstanceManager).Substring(1) + "?pageSize=2")) - { - request.Headers.Accept.Clear(); - request.Headers.UserAgent.Add(new ProductInfoHeaderValue("RegressionTest1256", "1.0.0")); - request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json)); - request.Headers.Add(ApiHeaders.ApiVersionHeader, "Tgstation.Server.Api/" + ApiHeaders.Version); - request.Headers.Authorization = new AuthenticationHeaderValue(ApiHeaders.BearerAuthenticationScheme, token); - using var response = await httpClient.SendAsync(request, cancellationToken); - response.EnsureSuccessStatusCode(); + using var request = new HttpRequestMessage(HttpMethod.Get, url.ToString() + Routes.ListRoute(Routes.InstanceManager).Substring(1) + "?pageSize=2"); + request.Headers.Accept.Clear(); + request.Headers.UserAgent.Add(new ProductInfoHeaderValue("RegressionTest1256", "1.0.0")); + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json)); + request.Headers.Add(ApiHeaders.ApiVersionHeader, "Tgstation.Server.Api/" + ApiHeaders.Version); + request.Headers.Authorization = new AuthenticationHeaderValue(ApiHeaders.BearerAuthenticationScheme, token); + using var response = await httpClient.SendAsync(request, cancellationToken); + response.EnsureSuccessStatusCode(); - var json = await response.Content.ReadAsStringAsync(); - var paginated = JsonConvert.DeserializeObject>(json); + var json = await response.Content.ReadAsStringAsync(); + var paginated = JsonConvert.DeserializeObject>(json); - Assert.AreEqual(2, paginated.PageSize); - Assert.AreEqual(3, paginated.TotalPages); - } + Assert.AreEqual(2, paginated.PageSize); + Assert.AreEqual(3, paginated.TotalPages); } public async Task RunPostTest(CancellationToken cancellationToken) @@ -205,16 +203,25 @@ namespace Tgstation.Server.Tests var firstTest = instances.Single(x => x.Name == TestInstanceName); var instanceClient = instanceManagerClient.CreateClient(firstTest); + Assert.IsTrue(firstTest.Accessible); + //can regain permissions on instance without instance user var ourInstanceUser = await instanceClient.PermissionSets.Read(cancellationToken).ConfigureAwait(false); await instanceClient.PermissionSets.Delete(ourInstanceUser, cancellationToken).ConfigureAwait(false); + firstTest = await instanceManagerClient.GetId(firstTest, cancellationToken); + Assert.IsFalse(firstTest.Accessible); + await Assert.ThrowsExceptionAsync(() => instanceClient.PermissionSets.Read(cancellationToken)).ConfigureAwait(false); await instanceManagerClient.GrantPermissions(new InstanceUpdateRequest { Id = firstTest.Id }, cancellationToken).ConfigureAwait(false); + + firstTest = await instanceManagerClient.GetId(firstTest, cancellationToken); + Assert.IsTrue(firstTest.Accessible); + ourInstanceUser = await instanceClient.PermissionSets.Read(cancellationToken).ConfigureAwait(false); Assert.AreEqual(RightsHelper.AllRights(), ourInstanceUser.DreamDaemonRights.Value);