diff --git a/src/Tgstation.Server.Host/Authority/IUserAuthority.cs b/src/Tgstation.Server.Host/Authority/IUserAuthority.cs index b0a67681cd..4b67a3e55b 100644 --- a/src/Tgstation.Server.Host/Authority/IUserAuthority.cs +++ b/src/Tgstation.Server.Host/Authority/IUserAuthority.cs @@ -6,6 +6,7 @@ using Tgstation.Server.Api.Models; using Tgstation.Server.Api.Models.Request; using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Authority.Core; +using Tgstation.Server.Host.GraphQL.Types.OAuth; using Tgstation.Server.Host.Models; using Tgstation.Server.Host.Security; @@ -40,8 +41,8 @@ namespace Tgstation.Server.Host.Authority /// /// The of the . /// The for the operation. - /// A resulting in an of . - ValueTask> OAuthConnections(long userId, CancellationToken cancellationToken); + /// A resulting in an of . + ValueTask> OAuthConnections(long userId, CancellationToken cancellationToken); /// /// Gets all registered s. diff --git a/src/Tgstation.Server.Host/Authority/UserAuthority.cs b/src/Tgstation.Server.Host/Authority/UserAuthority.cs index 753a180d30..686eb10f47 100644 --- a/src/Tgstation.Server.Host/Authority/UserAuthority.cs +++ b/src/Tgstation.Server.Host/Authority/UserAuthority.cs @@ -102,7 +102,7 @@ namespace Tgstation.Server.Host.Authority /// The for the operation. /// A resulting in a of the requested s. [DataLoader] - public static async ValueTask> GetOAuthConnections( + public static async ValueTask> GetOAuthConnections( IReadOnlyList userIds, IDatabaseContext databaseContext, CancellationToken cancellationToken) @@ -118,7 +118,7 @@ namespace Tgstation.Server.Host.Authority return list.ToLookup( oauthConnection => oauthConnection.UserId, - x => new GraphQL.Types.OAuthConnection(x.ExternalUserId!, x.Provider)); + x => new GraphQL.Types.OAuth.OAuthConnection(x.ExternalUserId!, x.Provider)); } /// @@ -285,8 +285,8 @@ namespace Tgstation.Server.Host.Authority => Queryable(includeJoins, false); /// - public async ValueTask> OAuthConnections(long userId, CancellationToken cancellationToken) - => new AuthorityResponse( + public async ValueTask> OAuthConnections(long userId, CancellationToken cancellationToken) + => new AuthorityResponse( await oAuthConnectionsDataLoader.LoadRequiredAsync(userId, cancellationToken)); /// diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs index aa7753c05c..3a310a7ec9 100644 --- a/src/Tgstation.Server.Host/Core/Application.cs +++ b/src/Tgstation.Server.Host/Core/Application.cs @@ -57,10 +57,10 @@ 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.Interceptors; +using Tgstation.Server.Host.GraphQL.Scalars; using Tgstation.Server.Host.GraphQL.Subscriptions; using Tgstation.Server.Host.GraphQL.Types; -using Tgstation.Server.Host.GraphQL.Types.Interceptors; -using Tgstation.Server.Host.GraphQL.Types.Scalars; using Tgstation.Server.Host.IO; using Tgstation.Server.Host.Jobs; using Tgstation.Server.Host.Properties; diff --git a/src/Tgstation.Server.Host/GraphQL/Types/Interceptors/RightsTypeInterceptor.cs b/src/Tgstation.Server.Host/GraphQL/Interceptors/RightsTypeInterceptor.cs similarity index 97% rename from src/Tgstation.Server.Host/GraphQL/Types/Interceptors/RightsTypeInterceptor.cs rename to src/Tgstation.Server.Host/GraphQL/Interceptors/RightsTypeInterceptor.cs index 538c32b3ad..5fea9058e3 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/Interceptors/RightsTypeInterceptor.cs +++ b/src/Tgstation.Server.Host/GraphQL/Interceptors/RightsTypeInterceptor.cs @@ -6,10 +6,10 @@ using System.Reflection; using HotChocolate.Configuration; using HotChocolate.Types; using HotChocolate.Types.Descriptors.Definitions; - using Tgstation.Server.Api.Rights; +using Tgstation.Server.Host.GraphQL.Types; -namespace Tgstation.Server.Host.GraphQL.Types.Interceptors +namespace Tgstation.Server.Host.GraphQL.Interceptors { /// /// Fixes the names used for the default flags types in API rights. diff --git a/src/Tgstation.Server.Host/GraphQL/Mutations/Payloads/LoginPayload.cs b/src/Tgstation.Server.Host/GraphQL/Mutations/Payloads/LoginPayload.cs index 8d5cd5e683..407ebab3b7 100644 --- a/src/Tgstation.Server.Host/GraphQL/Mutations/Payloads/LoginPayload.cs +++ b/src/Tgstation.Server.Host/GraphQL/Mutations/Payloads/LoginPayload.cs @@ -1,6 +1,6 @@ using HotChocolate; using Tgstation.Server.Api.Models.Response; -using Tgstation.Server.Host.GraphQL.Types.Scalars; +using Tgstation.Server.Host.GraphQL.Scalars; using Tgstation.Server.Host.Models; namespace Tgstation.Server.Host.GraphQL.Mutations.Payloads diff --git a/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs b/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs index 5c51fd8bc3..77fc7e3445 100644 --- a/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs +++ b/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs @@ -13,6 +13,7 @@ using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Authority; using Tgstation.Server.Host.GraphQL.Mutations.Payloads; using Tgstation.Server.Host.GraphQL.Types; +using Tgstation.Server.Host.GraphQL.Types.OAuth; using Tgstation.Server.Host.Models.Transformers; using Tgstation.Server.Host.Security; diff --git a/src/Tgstation.Server.Host/GraphQL/Types/Scalars/JwtType.cs b/src/Tgstation.Server.Host/GraphQL/Scalars/JwtType.cs similarity index 94% rename from src/Tgstation.Server.Host/GraphQL/Types/Scalars/JwtType.cs rename to src/Tgstation.Server.Host/GraphQL/Scalars/JwtType.cs index 47f10ccde3..170a744a42 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/Scalars/JwtType.cs +++ b/src/Tgstation.Server.Host/GraphQL/Scalars/JwtType.cs @@ -3,7 +3,7 @@ using HotChocolate.Language; using HotChocolate.Types; -namespace Tgstation.Server.Host.GraphQL.Types.Scalars +namespace Tgstation.Server.Host.GraphQL.Scalars { /// /// A for encoded JSON Web Tokens. diff --git a/src/Tgstation.Server.Host/GraphQL/Types/Scalars/SemverType.cs b/src/Tgstation.Server.Host/GraphQL/Scalars/SemverType.cs similarity index 97% rename from src/Tgstation.Server.Host/GraphQL/Types/Scalars/SemverType.cs rename to src/Tgstation.Server.Host/GraphQL/Scalars/SemverType.cs index dce31b4751..de8c1e530a 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/Scalars/SemverType.cs +++ b/src/Tgstation.Server.Host/GraphQL/Scalars/SemverType.cs @@ -2,10 +2,9 @@ using HotChocolate.Language; using HotChocolate.Types; - using Tgstation.Server.Common.Extensions; -namespace Tgstation.Server.Host.GraphQL.Types.Scalars +namespace Tgstation.Server.Host.GraphQL.Scalars { /// /// A for semantic s. diff --git a/src/Tgstation.Server.Host/GraphQL/Types/GatewayInformation.cs b/src/Tgstation.Server.Host/GraphQL/Types/GatewayInformation.cs index 727e38c5f1..9a1aee31cf 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/GatewayInformation.cs +++ b/src/Tgstation.Server.Host/GraphQL/Types/GatewayInformation.cs @@ -10,6 +10,7 @@ using Tgstation.Server.Api.Models; using Tgstation.Server.Api.Rights; using Tgstation.Server.Host.Components.Interop; using Tgstation.Server.Host.Configuration; +using Tgstation.Server.Host.GraphQL.Types.OAuth; using Tgstation.Server.Host.Properties; using Tgstation.Server.Host.Security; using Tgstation.Server.Host.Security.OAuth; @@ -148,12 +149,12 @@ namespace Tgstation.Server.Host.GraphQL.Types /// Gets the information needed to perform open authentication with the . /// /// The to use. - /// A map of enabled s to their . - public IReadOnlyDictionary OAuthProviderInfos( + /// A map of enabled s to their . + public OAuthProviderInfos OAuthProviderInfos( [Service] IOAuthProviders oAuthProviders) { ArgumentNullException.ThrowIfNull(oAuthProviders); - return oAuthProviders.ProviderInfos(); + return new OAuthProviderInfos(oAuthProviders); } } } diff --git a/src/Tgstation.Server.Host/GraphQL/Types/OAuth/BasicOAuthProviderInfo.cs b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/BasicOAuthProviderInfo.cs new file mode 100644 index 0000000000..f503d7adc8 --- /dev/null +++ b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/BasicOAuthProviderInfo.cs @@ -0,0 +1,28 @@ +using System; + +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Host.GraphQL.Types.OAuth +{ + /// + /// Basic OAuth provider info. + /// + public class BasicOAuthProviderInfo + { + /// + /// The client ID. + /// + public string ClientID { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The to build from. + public BasicOAuthProviderInfo(OAuthProviderInfo providerInfo) + { + ArgumentNullException.ThrowIfNull(providerInfo); + + ClientID = providerInfo.ClientId ?? throw new InvalidOperationException("ClientID not set!"); + } + } +} diff --git a/src/Tgstation.Server.Host/GraphQL/Types/OAuth/FullOAuthProviderInfo.cs b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/FullOAuthProviderInfo.cs new file mode 100644 index 0000000000..cd750f665b --- /dev/null +++ b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/FullOAuthProviderInfo.cs @@ -0,0 +1,29 @@ +using System; + +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Host.GraphQL.Types.OAuth +{ + /// + /// OAuth provider info with a and . + /// + public sealed class FullOAuthProviderInfo : RedirectOAuthProviderInfo + { + /// + /// The remote service URL. + /// + public Uri ServerUrl { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The to build from. + public FullOAuthProviderInfo(OAuthProviderInfo providerInfo) + : base(providerInfo) + { + ArgumentNullException.ThrowIfNull(providerInfo); + + ServerUrl = providerInfo.ServerUrl ?? throw new InvalidOperationException("Missing OAuthProviderInfo ServerUrl!"); + } + } +} diff --git a/src/Tgstation.Server.Host/GraphQL/Types/OAuthConnection.cs b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/OAuthConnection.cs similarity index 94% rename from src/Tgstation.Server.Host/GraphQL/Types/OAuthConnection.cs rename to src/Tgstation.Server.Host/GraphQL/Types/OAuth/OAuthConnection.cs index 7b0732fe92..cae66dec1a 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/OAuthConnection.cs +++ b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/OAuthConnection.cs @@ -1,8 +1,7 @@ using System; - using Tgstation.Server.Api.Models; -namespace Tgstation.Server.Host.GraphQL.Types +namespace Tgstation.Server.Host.GraphQL.Types.OAuth { /// /// Represents a valid OAuth connection. diff --git a/src/Tgstation.Server.Host/GraphQL/Types/OAuth/OAuthProviderInfos.cs b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/OAuthProviderInfos.cs new file mode 100644 index 0000000000..da9bffe4b3 --- /dev/null +++ b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/OAuthProviderInfos.cs @@ -0,0 +1,66 @@ +using System; + +using Tgstation.Server.Api.Models; +using Tgstation.Server.Host.Security.OAuth; + +namespace Tgstation.Server.Host.GraphQL.Types.OAuth +{ + /// + /// Description of configured OAuth services. + /// + public sealed class OAuthProviderInfos + { + /// + /// https://discord.com. + /// + public BasicOAuthProviderInfo? Discord { get; } + + /// + /// https://github.com. + /// + public RedirectOAuthProviderInfo? GitHub { get; } + + /// + /// https://tgstation13.org. + /// + public RedirectOAuthProviderInfo? TGForums { get; } + + /// + /// https://www.keycloak.org. + /// + public FullOAuthProviderInfo? Keycloak { get; } + + /// + /// https://invisioncommunity.com. + /// + public FullOAuthProviderInfo? InvisionCommunity { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The to get data from. + public OAuthProviderInfos(IOAuthProviders oAuthProviders) + { + ArgumentNullException.ThrowIfNull(oAuthProviders); + + var dic = oAuthProviders.ProviderInfos(); + + TProviderInfo? TryBuild(OAuthProvider oAuthProvider, Func contructor) + where TProviderInfo : BasicOAuthProviderInfo + { + if (dic.TryGetValue(oAuthProvider, out var providerInfo)) + { + return contructor(providerInfo); + } + + return null; + } + + Discord = TryBuild(OAuthProvider.Discord, info => new BasicOAuthProviderInfo(info)); + GitHub = TryBuild(OAuthProvider.GitHub, info => new RedirectOAuthProviderInfo(info)); + TGForums = TryBuild(OAuthProvider.TGForums, info => new RedirectOAuthProviderInfo(info)); + Keycloak = TryBuild(OAuthProvider.Keycloak, info => new FullOAuthProviderInfo(info)); + InvisionCommunity = TryBuild(OAuthProvider.InvisionCommunity, info => new FullOAuthProviderInfo(info)); + } + } +} diff --git a/src/Tgstation.Server.Host/GraphQL/Types/OAuth/RedirectOAuthProviderInfo.cs b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/RedirectOAuthProviderInfo.cs new file mode 100644 index 0000000000..ec0b00f6a0 --- /dev/null +++ b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/RedirectOAuthProviderInfo.cs @@ -0,0 +1,27 @@ +using System; + +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Host.GraphQL.Types.OAuth +{ + /// + /// OAuth provider info with a . + /// + public class RedirectOAuthProviderInfo : BasicOAuthProviderInfo + { + /// + /// The authentication server URL. + /// + public Uri RedirectUri { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The to build from. + public RedirectOAuthProviderInfo(OAuthProviderInfo providerInfo) + : base(providerInfo) + { + RedirectUri = providerInfo!.RedirectUri ?? throw new InvalidOperationException("RedirectUri not set!"); + } + } +} diff --git a/src/Tgstation.Server.Host/GraphQL/Types/OAuth/ServerUrlOAuthProviderInfo.cs b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/ServerUrlOAuthProviderInfo.cs new file mode 100644 index 0000000000..2add1c2bae --- /dev/null +++ b/src/Tgstation.Server.Host/GraphQL/Types/OAuth/ServerUrlOAuthProviderInfo.cs @@ -0,0 +1,27 @@ +using System; + +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Host.GraphQL.Types.OAuth +{ + /// + /// OAuth provider info with a . + /// + public sealed class ServerUrlOAuthProviderInfo : BasicOAuthProviderInfo + { + /// + /// The remote service URL. + /// + public Uri ServerUrl { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The to build from. + public ServerUrlOAuthProviderInfo(OAuthProviderInfo providerInfo) + : base(providerInfo) + { + ServerUrl = providerInfo!.ServerUrl ?? throw new InvalidOperationException("ServerUrl not set!"); + } + } +} diff --git a/src/Tgstation.Server.Host/GraphQL/Types/User.cs b/src/Tgstation.Server.Host/GraphQL/Types/User.cs index 7e993fd358..721fbe9eef 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/User.cs +++ b/src/Tgstation.Server.Host/GraphQL/Types/User.cs @@ -7,6 +7,7 @@ using HotChocolate.Types.Relay; using Tgstation.Server.Host.Authority; using Tgstation.Server.Host.GraphQL.Interfaces; +using Tgstation.Server.Host.GraphQL.Types.OAuth; using Tgstation.Server.Host.Models.Transformers; using Tgstation.Server.Host.Security;