From ed2de22f798e665761c0969e87914a24d9b76342 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Tue, 10 Sep 2024 23:12:39 -0400 Subject: [PATCH] More GraphQL development - Setup query structure for the potential for `RemoteGateway`s. - Add new `ErrorCode` indicating they are not implemented. - Rename `ServerInformationBase` to `GatewayInformationBase`. - Fix `TgsRestAuthorizeAttribute` lacking an `IAuthorizationFilter`. --- build/Version.props | 6 +-- src/Tgstation.Server.Api/Models/ErrorCode.cs | 8 +++- ...erInformation.cs => GatewayInformation.cs} | 2 +- ...ationBase.cs => GatewayInformationBase.cs} | 2 +- .../Models/Internal/SwarmServer.cs | 9 +--- .../Models/Internal/SwarmServerInformation.cs | 11 +---- .../Response/ServerInformationResponse.cs | 2 +- .../GQL/Queries/ServerInformation.graphql | 38 +++++++++++++++ .../Queries/ServerInformationQuery.graphql | 34 -------------- .../Configuration/GeneralConfiguration.cs | 10 ++-- src/Tgstation.Server.Host/Core/Application.cs | 3 ++ .../GraphQL/ErrorMessageException.cs | 9 ++++ src/Tgstation.Server.Host/GraphQL/Query.cs | 8 ++-- .../GraphQL/Types/IGateway.cs | 31 ++++++++++++ .../Types/{LocalServer.cs => LocalGateway.cs} | 19 +++----- .../GraphQL/Types/Node.cs | 47 +++++++++++++++++++ .../GraphQL/Types/NodeInformation.cs | 47 +++++++++++++++++++ .../GraphQL/Types/RemoteGateway.cs | 34 ++++++++++++++ .../Types/{ServerSwarm.cs => Swarm.cs} | 41 +++++++++++----- .../GraphQL/Types/SwarmMetadata.cs | 2 +- .../Security/TgsAuthorizeAttribute.cs | 45 +++++++++++------- .../TgsRestAuthorizeAttribute{TAuthority}.cs | 7 ++- .../Live/TestLiveServer.cs | 28 +++++------ 23 files changed, 316 insertions(+), 127 deletions(-) rename src/Tgstation.Server.Api/Models/Internal/{LocalServerInformation.cs => GatewayInformation.cs} (89%) rename src/Tgstation.Server.Api/Models/Internal/{ServerInformationBase.cs => GatewayInformationBase.cs} (95%) create mode 100644 src/Tgstation.Server.Client.GraphQL/GQL/Queries/ServerInformation.graphql delete mode 100644 src/Tgstation.Server.Client.GraphQL/GQL/Queries/ServerInformationQuery.graphql create mode 100644 src/Tgstation.Server.Host/GraphQL/Types/IGateway.cs rename src/Tgstation.Server.Host/GraphQL/Types/{LocalServer.cs => LocalGateway.cs} (62%) create mode 100644 src/Tgstation.Server.Host/GraphQL/Types/Node.cs create mode 100644 src/Tgstation.Server.Host/GraphQL/Types/NodeInformation.cs create mode 100644 src/Tgstation.Server.Host/GraphQL/Types/RemoteGateway.cs rename src/Tgstation.Server.Host/GraphQL/Types/{ServerSwarm.cs => Swarm.cs} (53%) diff --git a/build/Version.props b/build/Version.props index 26c9fcf515..d0a850c6de 100644 --- a/build/Version.props +++ b/build/Version.props @@ -5,10 +5,10 @@ 6.10.0 5.2.0 - 10.9.0 + 10.10.0 7.0.0 - 15.0.0 - 18.0.0 + 16.0.0 + 19.0.0 7.3.0 5.10.0 1.5.0 diff --git a/src/Tgstation.Server.Api/Models/ErrorCode.cs b/src/Tgstation.Server.Api/Models/ErrorCode.cs index 052941d922..7fe2f24992 100644 --- a/src/Tgstation.Server.Api/Models/ErrorCode.cs +++ b/src/Tgstation.Server.Api/Models/ErrorCode.cs @@ -461,7 +461,7 @@ namespace Tgstation.Server.Api.Models RepoTestMergeConflict, /// - /// Attempted to create an instance outside of the . + /// Attempted to create an instance outside of the . /// [Description("The new instance's path is not under a white-listed path.")] InstanceNotAtWhitelistedPath, @@ -663,5 +663,11 @@ namespace Tgstation.Server.Api.Models /// [Description("Provided repository username doesn't match the user of the corresponding access token!")] RepoTokenUsernameMismatch, + + /// + /// Attempted to make a cross swarm server request using the GraphQL API. + /// + [Description("GraphQL swarm remote gateways not implemented!")] + RemoteGatewaysNotImplemented, } } diff --git a/src/Tgstation.Server.Api/Models/Internal/LocalServerInformation.cs b/src/Tgstation.Server.Api/Models/Internal/GatewayInformation.cs similarity index 89% rename from src/Tgstation.Server.Api/Models/Internal/LocalServerInformation.cs rename to src/Tgstation.Server.Api/Models/Internal/GatewayInformation.cs index ae8d5ca34e..48939aba6f 100644 --- a/src/Tgstation.Server.Api/Models/Internal/LocalServerInformation.cs +++ b/src/Tgstation.Server.Api/Models/Internal/GatewayInformation.cs @@ -5,7 +5,7 @@ namespace Tgstation.Server.Api.Models.Internal /// /// Information about the local tgstation-server. /// - public class LocalServerInformation : ServerInformationBase + public class GatewayInformation : GatewayInformationBase { /// /// If the server is running on a windows operating system. diff --git a/src/Tgstation.Server.Api/Models/Internal/ServerInformationBase.cs b/src/Tgstation.Server.Api/Models/Internal/GatewayInformationBase.cs similarity index 95% rename from src/Tgstation.Server.Api/Models/Internal/ServerInformationBase.cs rename to src/Tgstation.Server.Api/Models/Internal/GatewayInformationBase.cs index 30d379a323..f1f53b26bb 100644 --- a/src/Tgstation.Server.Api/Models/Internal/ServerInformationBase.cs +++ b/src/Tgstation.Server.Api/Models/Internal/GatewayInformationBase.cs @@ -5,7 +5,7 @@ namespace Tgstation.Server.Api.Models.Internal /// /// Base class for . /// - public abstract class ServerInformationBase + public abstract class GatewayInformationBase { /// /// Minimum length of database user passwords. diff --git a/src/Tgstation.Server.Api/Models/Internal/SwarmServer.cs b/src/Tgstation.Server.Api/Models/Internal/SwarmServer.cs index 9205e58a09..998c519b19 100644 --- a/src/Tgstation.Server.Api/Models/Internal/SwarmServer.cs +++ b/src/Tgstation.Server.Api/Models/Internal/SwarmServer.cs @@ -6,7 +6,7 @@ namespace Tgstation.Server.Api.Models.Internal /// /// Information about a server in the swarm. /// - public abstract class SwarmServer : IEquatable + public abstract class SwarmServer { /// /// The public address of the server. @@ -47,12 +47,5 @@ namespace Tgstation.Server.Api.Models.Internal PublicAddress = copy.PublicAddress; Identifier = copy.Identifier; } - - /// - public bool Equals(SwarmServer other) - => other != null - && other.Identifier == Identifier - && other.PublicAddress == PublicAddress - && other.Address == Address; } } diff --git a/src/Tgstation.Server.Api/Models/Internal/SwarmServerInformation.cs b/src/Tgstation.Server.Api/Models/Internal/SwarmServerInformation.cs index fb24f74ad1..89238b3099 100644 --- a/src/Tgstation.Server.Api/Models/Internal/SwarmServerInformation.cs +++ b/src/Tgstation.Server.Api/Models/Internal/SwarmServerInformation.cs @@ -1,13 +1,11 @@ -using System; - -using Tgstation.Server.Api.Models.Response; +using Tgstation.Server.Api.Models.Response; namespace Tgstation.Server.Api.Models.Internal { /// /// Represents information about a running . /// - public class SwarmServerInformation : SwarmServer, IEquatable + public class SwarmServerInformation : SwarmServer { /// /// If the is the controller. @@ -30,10 +28,5 @@ namespace Tgstation.Server.Api.Models.Internal { Controller = copy.Controller; } - - /// - public bool Equals(SwarmServerInformation other) - => base.Equals(other) - && other.Controller == Controller; } } diff --git a/src/Tgstation.Server.Api/Models/Response/ServerInformationResponse.cs b/src/Tgstation.Server.Api/Models/Response/ServerInformationResponse.cs index a33b2b8be2..a912fa127e 100644 --- a/src/Tgstation.Server.Api/Models/Response/ServerInformationResponse.cs +++ b/src/Tgstation.Server.Api/Models/Response/ServerInformationResponse.cs @@ -6,7 +6,7 @@ namespace Tgstation.Server.Api.Models.Response /// /// Represents basic server information. /// - public sealed class ServerInformationResponse : Internal.LocalServerInformation + public sealed class ServerInformationResponse : Internal.GatewayInformation { /// /// The version of the host. diff --git a/src/Tgstation.Server.Client.GraphQL/GQL/Queries/ServerInformation.graphql b/src/Tgstation.Server.Client.GraphQL/GQL/Queries/ServerInformation.graphql new file mode 100644 index 0000000000..5e04dc4a66 --- /dev/null +++ b/src/Tgstation.Server.Client.GraphQL/GQL/Queries/ServerInformation.graphql @@ -0,0 +1,38 @@ +query ServerInformation { + swarm { + metadata { + apiVersion + dmApiVersion + updateInProgress + version + } + currentNode { + gateway { + information { + instanceLimit + minimumPasswordLength + userGroupLimit + userLimit + validInstancePaths + windowsHost + oAuthProviderInfos { + value { + clientId + redirectUri + serverUrl + } + key + } + } + } + } + nodes { + info { + address + controller + identifier + publicAddress + } + } + } +} diff --git a/src/Tgstation.Server.Client.GraphQL/GQL/Queries/ServerInformationQuery.graphql b/src/Tgstation.Server.Client.GraphQL/GQL/Queries/ServerInformationQuery.graphql deleted file mode 100644 index 6f130e4d24..0000000000 --- a/src/Tgstation.Server.Client.GraphQL/GQL/Queries/ServerInformationQuery.graphql +++ /dev/null @@ -1,34 +0,0 @@ -query ServerInformationQuery { - swarm { - metadata { - apiVersion - dmApiVersion - updateInProgress - version - } - localServer { - information { - instanceLimit - minimumPasswordLength - userGroupLimit - userLimit - validInstancePaths - windowsHost - oAuthProviderInfos { - key - value { - clientId - redirectUri - serverUrl - } - } - } - } - servers { - address - controller - identifier - publicAddress - } - } -} diff --git a/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs b/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs index 1790316e37..3a5ccd8ca2 100644 --- a/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs +++ b/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs @@ -16,7 +16,7 @@ namespace Tgstation.Server.Host.Configuration /// /// General configuration options. /// - public sealed class GeneralConfiguration : ServerInformationBase + public sealed class GeneralConfiguration : GatewayInformationBase { /// /// The key for the the resides in. @@ -29,22 +29,22 @@ namespace Tgstation.Server.Host.Configuration public const ushort DefaultApiPort = 5000; /// - /// The default value for . + /// The default value for . /// const uint DefaultMinimumPasswordLength = 15; /// - /// The default value for . + /// The default value for . /// const uint DefaultInstanceLimit = 10; /// - /// The default value for . + /// The default value for . /// const uint DefaultUserLimit = 100; /// - /// The default value for . + /// The default value for . /// const uint DefaultUserGroupLimit = 25; diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs index 33e10e3d0d..2ef310428e 100644 --- a/src/Tgstation.Server.Host/Core/Application.cs +++ b/src/Tgstation.Server.Host/Core/Application.cs @@ -56,6 +56,7 @@ using Tgstation.Server.Host.Controllers.Results; using Tgstation.Server.Host.Database; using Tgstation.Server.Host.Extensions; using Tgstation.Server.Host.GraphQL; +using Tgstation.Server.Host.GraphQL.Types; using Tgstation.Server.Host.GraphQL.Types.Scalars; using Tgstation.Server.Host.IO; using Tgstation.Server.Host.Jobs; @@ -297,6 +298,8 @@ namespace Tgstation.Server.Host.Core .AddAuthorization() .AddMutationConventions() .AddErrorFilter() + .AddType() + .AddType() .AddType() .BindRuntimeType() .AddQueryType() diff --git a/src/Tgstation.Server.Host/GraphQL/ErrorMessageException.cs b/src/Tgstation.Server.Host/GraphQL/ErrorMessageException.cs index 20c8550b6b..38315ca79e 100644 --- a/src/Tgstation.Server.Host/GraphQL/ErrorMessageException.cs +++ b/src/Tgstation.Server.Host/GraphQL/ErrorMessageException.cs @@ -33,5 +33,14 @@ namespace Tgstation.Server.Host.GraphQL ErrorCode = errorMessage.ErrorCode != default ? errorMessage.ErrorCode : null; AdditionalData = errorMessage.AdditionalData; } + + /// + /// Initializes a new instance of the class. + /// + /// The . + public ErrorMessageException(ErrorCode errorCode) + : this(new ErrorMessageResponse(errorCode), String.Empty) + { + } } } diff --git a/src/Tgstation.Server.Host/GraphQL/Query.cs b/src/Tgstation.Server.Host/GraphQL/Query.cs index 3b57958392..1b5e581ead 100644 --- a/src/Tgstation.Server.Host/GraphQL/Query.cs +++ b/src/Tgstation.Server.Host/GraphQL/Query.cs @@ -1,7 +1,5 @@ #pragma warning disable CA1724 -using Tgstation.Server.Host.GraphQL.Types; - namespace Tgstation.Server.Host.GraphQL { /// @@ -10,9 +8,9 @@ namespace Tgstation.Server.Host.GraphQL public sealed class Query { /// - /// Gets the . + /// Gets the . /// - /// A new . - public ServerSwarm Swarm() => new(); + /// A new . + public Types.Swarm Swarm() => new(); } } diff --git a/src/Tgstation.Server.Host/GraphQL/Types/IGateway.cs b/src/Tgstation.Server.Host/GraphQL/Types/IGateway.cs new file mode 100644 index 0000000000..45b83f87c7 --- /dev/null +++ b/src/Tgstation.Server.Host/GraphQL/Types/IGateway.cs @@ -0,0 +1,31 @@ +using HotChocolate; +using HotChocolate.Authorization; + +using Microsoft.Extensions.Options; + +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Host.Configuration; +using Tgstation.Server.Host.Security.OAuth; +using Tgstation.Server.Host.System; + +namespace Tgstation.Server.Host.GraphQL.Types +{ + /// + /// Management interface for the parent . + /// + public interface IGateway + { + /// + /// Gets . + /// + /// The to use. + /// The to use. + /// The containing the to use. + /// A new . + [AllowAnonymous] + GatewayInformation Information( + [Service] IOAuthProviders oAuthProviders, + [Service] IPlatformIdentifier platformIdentifier, + [Service] IOptionsSnapshot generalConfigurationOptions); + } +} diff --git a/src/Tgstation.Server.Host/GraphQL/Types/LocalServer.cs b/src/Tgstation.Server.Host/GraphQL/Types/LocalGateway.cs similarity index 62% rename from src/Tgstation.Server.Host/GraphQL/Types/LocalServer.cs rename to src/Tgstation.Server.Host/GraphQL/Types/LocalGateway.cs index fab7784385..5892a49f01 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/LocalServer.cs +++ b/src/Tgstation.Server.Host/GraphQL/Types/LocalGateway.cs @@ -1,9 +1,9 @@ using System; using HotChocolate; -using HotChocolate.Authorization; using Microsoft.Extensions.Options; + using Tgstation.Server.Api.Models.Internal; using Tgstation.Server.Host.Configuration; using Tgstation.Server.Host.Security.OAuth; @@ -12,19 +12,12 @@ using Tgstation.Server.Host.System; namespace Tgstation.Server.Host.GraphQL.Types { /// - /// Represents the local tgstation-server. + /// for the this query is executing on. /// - public sealed class LocalServer + public sealed class LocalGateway : IGateway { - /// - /// Gets . - /// - /// The to use. - /// The to use. - /// The containing the to use. - /// A new . - [AllowAnonymous] - public LocalServerInformation Information( + /// + public GatewayInformation Information( [Service] IOAuthProviders oAuthProviders, [Service] IPlatformIdentifier platformIdentifier, [Service] IOptionsSnapshot generalConfigurationOptions) @@ -34,7 +27,7 @@ namespace Tgstation.Server.Host.GraphQL.Types ArgumentNullException.ThrowIfNull(generalConfigurationOptions); var generalConfiguration = generalConfigurationOptions.Value; - return new LocalServerInformation + return new GatewayInformation { MinimumPasswordLength = generalConfiguration.MinimumPasswordLength, InstanceLimit = generalConfiguration.InstanceLimit, diff --git a/src/Tgstation.Server.Host/GraphQL/Types/Node.cs b/src/Tgstation.Server.Host/GraphQL/Types/Node.cs new file mode 100644 index 0000000000..dac5cd5a84 --- /dev/null +++ b/src/Tgstation.Server.Host/GraphQL/Types/Node.cs @@ -0,0 +1,47 @@ +using System; + +using HotChocolate; + +using Microsoft.Extensions.Options; + +using Tgstation.Server.Host.Configuration; + +namespace Tgstation.Server.Host.GraphQL.Types +{ + /// + /// Represents a node server in a swarm. + /// + public sealed class Node + { + /// + /// Gets the . + /// + public NodeInformation? Info { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The value of . + public Node(NodeInformation? info) + { + Info = info; + } + + /// + /// Gets the 's . + /// + /// The containing the current . + /// A new . + /// The 's . + public IGateway? Gateway([Service] IOptionsSnapshot swarmConfigurationOptions) + { + ArgumentNullException.ThrowIfNull(swarmConfigurationOptions); + + bool local = Info == null || Info.Identifier == swarmConfigurationOptions.Value.Identifier; + if (local) + return new LocalGateway(); + + return new RemoteGateway(); + } + } +} diff --git a/src/Tgstation.Server.Host/GraphQL/Types/NodeInformation.cs b/src/Tgstation.Server.Host/GraphQL/Types/NodeInformation.cs new file mode 100644 index 0000000000..0661bf92c9 --- /dev/null +++ b/src/Tgstation.Server.Host/GraphQL/Types/NodeInformation.cs @@ -0,0 +1,47 @@ +using System; + +using HotChocolate.Types.Relay; + +namespace Tgstation.Server.Host.GraphQL.Types +{ + /// + /// Represent a server in the TGS server swarm. + /// + public sealed class NodeInformation + { + /// + /// The swarm server ID. + /// + [ID] + public string Identifier { get; } + + /// + /// The swarm server's internal . + /// + public Uri Address { get; } + + /// + /// The swarm server's optional public address. + /// + public Uri? PublicAddress { get; } + + /// + /// Whether or not the server is the swarm's controller. + /// + public bool Controller { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The to build from. + public NodeInformation(Api.Models.Internal.SwarmServerInformation swarmServerInformation) + { + ArgumentNullException.ThrowIfNull(swarmServerInformation); + + Identifier = swarmServerInformation.Identifier!; + Address = swarmServerInformation.Address!; + PublicAddress = swarmServerInformation.PublicAddress; + Controller = swarmServerInformation.Controller; + } + } +} diff --git a/src/Tgstation.Server.Host/GraphQL/Types/RemoteGateway.cs b/src/Tgstation.Server.Host/GraphQL/Types/RemoteGateway.cs new file mode 100644 index 0000000000..ffe7731895 --- /dev/null +++ b/src/Tgstation.Server.Host/GraphQL/Types/RemoteGateway.cs @@ -0,0 +1,34 @@ +using System; + +using HotChocolate; + +using Microsoft.Extensions.Options; + +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Host.Configuration; +using Tgstation.Server.Host.Security.OAuth; +using Tgstation.Server.Host.System; + +namespace Tgstation.Server.Host.GraphQL.Types +{ + /// + /// for accessing remote s. + /// + /// This is currently unimplemented. + public sealed class RemoteGateway : IGateway + { + /// + public GatewayInformation Information( + [Service] IOAuthProviders oAuthProviders, + [Service] IPlatformIdentifier platformIdentifier, + [Service] IOptionsSnapshot generalConfigurationOptions) + { + ArgumentNullException.ThrowIfNull(oAuthProviders); + ArgumentNullException.ThrowIfNull(platformIdentifier); + ArgumentNullException.ThrowIfNull(generalConfigurationOptions); + + throw new ErrorMessageException(ErrorCode.RemoteGatewaysNotImplemented); + } + } +} diff --git a/src/Tgstation.Server.Host/GraphQL/Types/ServerSwarm.cs b/src/Tgstation.Server.Host/GraphQL/Types/Swarm.cs similarity index 53% rename from src/Tgstation.Server.Host/GraphQL/Types/ServerSwarm.cs rename to src/Tgstation.Server.Host/GraphQL/Types/Swarm.cs index f2ec6c6151..896d5f538e 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/ServerSwarm.cs +++ b/src/Tgstation.Server.Host/GraphQL/Types/Swarm.cs @@ -1,9 +1,12 @@ using System; using System.Collections.Generic; +using System.Linq; using HotChocolate; -using Tgstation.Server.Api.Models.Internal; +using Microsoft.Extensions.Options; + +using Tgstation.Server.Host.Configuration; using Tgstation.Server.Host.Core; using Tgstation.Server.Host.Swarm; using Tgstation.Server.Host.System; @@ -13,7 +16,7 @@ namespace Tgstation.Server.Host.GraphQL.Types /// /// Represents a tgstation-server swarm. /// - public sealed class ServerSwarm + public sealed class Swarm { /// /// Gets the for the swarm. @@ -30,12 +33,6 @@ namespace Tgstation.Server.Host.GraphQL.Types return new SwarmMetadata(assemblyInformationProvider, serverControl.UpdateInProgress); } - /// - /// Gets the local . - /// - /// A new . - public LocalServer LocalServer() => new(); - /// /// Gets the swarm's . /// @@ -43,15 +40,35 @@ namespace Tgstation.Server.Host.GraphQL.Types public Users Users() => new(); /// - /// Gets the for all servers in a swarm. + /// Gets the connected server. /// /// The to use. - /// A of s if the local server is part of a swarm, otherwise. - public List? Servers( + /// The containing the current . + /// A new . + public Node CurrentNode( + [Service] ISwarmService swarmService, + [Service] IOptionsSnapshot swarmConfigurationOptions) + { + ArgumentNullException.ThrowIfNull(swarmService); + ArgumentNullException.ThrowIfNull(swarmConfigurationOptions); + + var nodeInfos = Nodes(swarmService); + if (nodeInfos != null) + return nodeInfos.First(x => x.Info!.Identifier == swarmConfigurationOptions.Value.Identifier); + + return new Node(null); + } + + /// + /// Gets all servers in the swarm. + /// + /// The to use. + /// A of s if the local server is part of a swarm, otherwise. + public List? Nodes( [Service] ISwarmService swarmService) { ArgumentNullException.ThrowIfNull(swarmService); - return swarmService.GetSwarmServers(); + return swarmService.GetSwarmServers()?.Select(x => new Node(new NodeInformation(x))).ToList(); } } } diff --git a/src/Tgstation.Server.Host/GraphQL/Types/SwarmMetadata.cs b/src/Tgstation.Server.Host/GraphQL/Types/SwarmMetadata.cs index 19fe192b23..f0ee0bbdf0 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/SwarmMetadata.cs +++ b/src/Tgstation.Server.Host/GraphQL/Types/SwarmMetadata.cs @@ -7,7 +7,7 @@ using Tgstation.Server.Host.System; namespace Tgstation.Server.Host.GraphQL.Types { /// - /// Represents information that is constant across all servers in a . + /// Represents information that is constant across all servers in a . /// public sealed class SwarmMetadata { diff --git a/src/Tgstation.Server.Host/Security/TgsAuthorizeAttribute.cs b/src/Tgstation.Server.Host/Security/TgsAuthorizeAttribute.cs index 0dda24b472..6619f3f121 100644 --- a/src/Tgstation.Server.Host/Security/TgsAuthorizeAttribute.cs +++ b/src/Tgstation.Server.Host/Security/TgsAuthorizeAttribute.cs @@ -23,6 +23,32 @@ namespace Tgstation.Server.Host.Security /// public RightsType? RightsType { get; } + /// + /// Implementation of . + /// + /// The . + public static void OnAuthorizationHelper(AuthorizationFilterContext context) + { + ArgumentNullException.ThrowIfNull(context); + + var services = context.HttpContext.RequestServices; + var authenticationContext = services.GetRequiredService(); + var logger = services.GetRequiredService>(); + + if (!authenticationContext.Valid) + { + logger.LogTrace("authenticationContext is invalid!"); + context.Result = new UnauthorizedResult(); + return; + } + + if (authenticationContext.User.Require(x => x.Enabled)) + return; + + logger.LogTrace("authenticationContext is for a disabled user!"); + context.Result = new ForbidResult(); + } + /// /// Initializes a new instance of the class. /// @@ -122,23 +148,6 @@ namespace Tgstation.Server.Host.Security /// public void OnAuthorization(AuthorizationFilterContext context) - { - var services = context.HttpContext.RequestServices; - var authenticationContext = services.GetRequiredService(); - var logger = services.GetRequiredService>(); - - if (!authenticationContext.Valid) - { - logger.LogTrace("authenticationContext is invalid!"); - context.Result = new UnauthorizedResult(); - return; - } - - if (authenticationContext.User.Require(x => x.Enabled)) - return; - - logger.LogTrace("authenticationContext is for a disabled user!"); - context.Result = new ForbidResult(); - } + => OnAuthorizationHelper(context); } } diff --git a/src/Tgstation.Server.Host/Security/TgsRestAuthorizeAttribute{TAuthority}.cs b/src/Tgstation.Server.Host/Security/TgsRestAuthorizeAttribute{TAuthority}.cs index 989de21e8e..7a3ac6cdf5 100644 --- a/src/Tgstation.Server.Host/Security/TgsRestAuthorizeAttribute{TAuthority}.cs +++ b/src/Tgstation.Server.Host/Security/TgsRestAuthorizeAttribute{TAuthority}.cs @@ -2,6 +2,7 @@ using System.Reflection; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc.Filters; using Tgstation.Server.Host.Authority.Core; @@ -12,7 +13,7 @@ namespace Tgstation.Server.Host.Security /// /// The being wrapped. [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)] - public sealed class TgsRestAuthorizeAttribute : AuthorizeAttribute + public sealed class TgsRestAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter where TAuthority : IAuthority { /// @@ -40,5 +41,9 @@ namespace Tgstation.Server.Host.Security MethodName = methodName; Roles = authorizeAttribute.Roles; } + + /// + public void OnAuthorization(AuthorizationFilterContext context) + => TgsAuthorizeAttribute.OnAuthorizationHelper(context); } } diff --git a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs index 9dcb363e63..f101d2cd81 100644 --- a/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs +++ b/tests/Tgstation.Server.Tests/Live/TestLiveServer.cs @@ -1380,27 +1380,27 @@ namespace Tgstation.Server.Tests.Live await multiClient.ExecuteReadOnlyConfirmEquivalence( restClient => restClient.ServerInformation(cancellationToken), - async gqlClient => (await gqlClient.ServerInformationQuery.ExecuteAsync(cancellationToken)).Data, + async gqlClient => (await gqlClient.ServerInformation.ExecuteAsync(cancellationToken)).Data, (restServerInfo, gqlServerInfo) => restServerInfo.UpdateInProgress == gqlServerInfo.Swarm.Metadata.UpdateInProgress && restServerInfo.Version == gqlServerInfo.Swarm.Metadata.Version && restServerInfo.DMApiVersion == gqlServerInfo.Swarm.Metadata.DmApiVersion - && restServerInfo.InstanceLimit == gqlServerInfo.Swarm.LocalServer.Information.InstanceLimit - && restServerInfo.UserGroupLimit == gqlServerInfo.Swarm.LocalServer.Information.UserGroupLimit - && restServerInfo.ValidInstancePaths.SequenceEqual(gqlServerInfo.Swarm.LocalServer.Information.ValidInstancePaths) - && restServerInfo.UserLimit == gqlServerInfo.Swarm.LocalServer.Information.UserLimit - && restServerInfo.MinimumPasswordLength == gqlServerInfo.Swarm.LocalServer.Information.MinimumPasswordLength - && (restServerInfo.SwarmServers == gqlServerInfo.Swarm.Servers - || restServerInfo.SwarmServers.SequenceEqual(gqlServerInfo.Swarm.Servers.Select(x => new SwarmServerResponse(new Api.Models.Internal.SwarmServerInformation + && restServerInfo.InstanceLimit == gqlServerInfo.Swarm.CurrentNode.Gateway.Information.InstanceLimit + && restServerInfo.UserGroupLimit == gqlServerInfo.Swarm.CurrentNode.Gateway.Information.UserGroupLimit + && restServerInfo.ValidInstancePaths.SequenceEqual(gqlServerInfo.Swarm.CurrentNode.Gateway.Information.ValidInstancePaths) + && restServerInfo.UserLimit == gqlServerInfo.Swarm.CurrentNode.Gateway.Information.UserLimit + && restServerInfo.MinimumPasswordLength == gqlServerInfo.Swarm.CurrentNode.Gateway.Information.MinimumPasswordLength + && ((object)restServerInfo.SwarmServers == gqlServerInfo.Swarm.Nodes + || restServerInfo.SwarmServers.SequenceEqual(gqlServerInfo.Swarm.Nodes.Select(x => new SwarmServerResponse(new Api.Models.Internal.SwarmServerInformation { - Address = x.Address, - PublicAddress = x.PublicAddress, - Controller = x.Controller, - Identifier = x.Identifier, + Address = x.Info.Address, + PublicAddress = x.Info.PublicAddress, + Controller = x.Info.Controller, + Identifier = x.Info.Identifier, })))) - && (restServerInfo.OAuthProviderInfos == gqlServerInfo.Swarm.LocalServer.Information.OAuthProviderInfos + && (restServerInfo.OAuthProviderInfos == gqlServerInfo.Swarm.CurrentNode.Gateway.Information.OAuthProviderInfos || restServerInfo.OAuthProviderInfos.All(kvp => { - var info = gqlServerInfo.Swarm.LocalServer.Information.OAuthProviderInfos.FirstOrDefault(x => (int)x.Key == (int)kvp.Key); + var info = gqlServerInfo.Swarm.CurrentNode.Gateway.Information.OAuthProviderInfos.FirstOrDefault(x => (int)x.Key == (int)kvp.Key); return info != null && info.Value.ServerUrl == kvp.Value.ServerUrl && info.Value.ClientId == kvp.Value.ClientId