diff --git a/src/Tgstation.Server.Host/Authority/IUserAuthority.cs b/src/Tgstation.Server.Host/Authority/IUserAuthority.cs index dfc9cdda9d..0d99ca88f2 100644 --- a/src/Tgstation.Server.Host/Authority/IUserAuthority.cs +++ b/src/Tgstation.Server.Host/Authority/IUserAuthority.cs @@ -34,6 +34,14 @@ namespace Tgstation.Server.Host.Authority [TgsAuthorize(AdministrationRights.ReadUsers)] public ValueTask> GetId(long id, bool includeJoins, bool allowSystemUser, CancellationToken cancellationToken); + /// + /// Gets the s for the with a given . + /// + /// The of the . + /// The for the operation. + /// A resulting in an of . + public 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 ddb20d4108..5a6094b0ec 100644 --- a/src/Tgstation.Server.Host/Authority/UserAuthority.cs +++ b/src/Tgstation.Server.Host/Authority/UserAuthority.cs @@ -27,7 +27,12 @@ namespace Tgstation.Server.Host.Authority /// /// The for the . /// - readonly IUsersDataLoader dataLoader; + readonly IUsersDataLoader usersDataLoader; + + /// + /// The for the . + /// + readonly IOAuthConnectionsDataLoader oAuthConnectionsDataLoader; /// /// The for the . @@ -35,7 +40,7 @@ namespace Tgstation.Server.Host.Authority readonly IAuthenticationContext authenticationContext; /// - /// Implements the . + /// Implements the . /// /// The of s to load. /// The to load from. @@ -57,22 +62,52 @@ namespace Tgstation.Server.Host.Authority .ToDictionaryAsync(user => user.Id!.Value, cancellationToken); } + /// + /// Implements the . + /// + /// The of s to load the OAuthConnections for. + /// The to load from. + /// The for the operation. + /// A resulting in a of the requested s. + [DataLoader] + public static async ValueTask> GetOAuthConnections( + IReadOnlyList userIds, + IDatabaseContext databaseContext, + CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(userIds); + ArgumentNullException.ThrowIfNull(databaseContext); + + var list = await databaseContext + .OAuthConnections + .AsQueryable() + .Where(x => userIds.Contains(x.User!.Id!.Value)) + .ToListAsync(cancellationToken); + + return list.ToLookup( + oauthConnection => oauthConnection.UserId, + x => new GraphQL.Types.OAuthConnection(x.ExternalUserId!, x.Provider)); + } + /// /// Initializes a new instance of the class. /// /// The to use. /// The value of . - /// The value of . + /// The value of . + /// The value of . /// The value of . public UserAuthority( ILogger logger, IDatabaseContext databaseContext, - IUsersDataLoader dataLoader, + IUsersDataLoader usersDataLoader, + IOAuthConnectionsDataLoader oAuthConnectionsDataLoader, IAuthenticationContext authenticationContext) : base(logger) { this.databaseContext = databaseContext ?? throw new ArgumentNullException(nameof(databaseContext)); - this.dataLoader = dataLoader ?? throw new ArgumentNullException(nameof(dataLoader)); + this.usersDataLoader = usersDataLoader ?? throw new ArgumentNullException(nameof(usersDataLoader)); + this.oAuthConnectionsDataLoader = oAuthConnectionsDataLoader ?? throw new ArgumentNullException(nameof(oAuthConnectionsDataLoader)); this.authenticationContext = authenticationContext ?? throw new ArgumentNullException(nameof(authenticationContext)); } @@ -93,7 +128,7 @@ namespace Tgstation.Server.Host.Authority cancellationToken); } else - user = await dataLoader.LoadAsync(id, cancellationToken); + user = await usersDataLoader.LoadAsync(id, cancellationToken); if (user == default) return NotFound(); @@ -108,6 +143,11 @@ namespace Tgstation.Server.Host.Authority public IQueryable Queryable(bool includeJoins) => Queryable(includeJoins, false); + /// + public async ValueTask> OAuthConnections(long userId, CancellationToken cancellationToken) + => new AuthorityResponse( + await oAuthConnectionsDataLoader.LoadRequiredAsync(userId, cancellationToken)); + /// /// Gets all registered s. /// diff --git a/src/Tgstation.Server.Host/GraphQL/Types/User.cs b/src/Tgstation.Server.Host/GraphQL/Types/User.cs index ab5df09a79..baedcfaa52 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/User.cs +++ b/src/Tgstation.Server.Host/GraphQL/Types/User.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -94,9 +93,17 @@ namespace Tgstation.Server.Host.GraphQL.Types /// /// List of s associated with the user if OAuth is configured. /// - /// A resulting in a new of s for the if OAuth is configured. - public ValueTask>? OAuthConnections() - => throw new NotImplementedException(); + /// The . + /// The for the operation. + /// A resulting in a new of s for the if OAuth is configured. + public async ValueTask OAuthConnections( + [Service] IGraphQLAuthorityInvoker userAuthority, + CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(userAuthority); + return (await userAuthority.Invoke( + authority => authority.OAuthConnections(Id, cancellationToken)))!; + } /// /// The directly associated with the , if any. diff --git a/src/Tgstation.Server.Host/Models/OAuthConnection.cs b/src/Tgstation.Server.Host/Models/OAuthConnection.cs index 64b1702d8c..7c871b672a 100644 --- a/src/Tgstation.Server.Host/Models/OAuthConnection.cs +++ b/src/Tgstation.Server.Host/Models/OAuthConnection.cs @@ -8,6 +8,11 @@ /// public long Id { get; set; } + /// + /// The of . + /// + public long UserId { get; set; } + /// /// The owning . ///