diff --git a/src/Tgstation.Server.Host/Authority/Core/AuthorityBase.cs b/src/Tgstation.Server.Host/Authority/Core/AuthorityBase.cs index eff31f3c41..898c701eb8 100644 --- a/src/Tgstation.Server.Host/Authority/Core/AuthorityBase.cs +++ b/src/Tgstation.Server.Host/Authority/Core/AuthorityBase.cs @@ -7,6 +7,7 @@ using Octokit; using Tgstation.Server.Api.Models; using Tgstation.Server.Api.Models.Response; +using Tgstation.Server.Host.Database; using Tgstation.Server.Host.Security; namespace Tgstation.Server.Host.Authority.Core @@ -21,6 +22,11 @@ namespace Tgstation.Server.Host.Authority.Core /// protected IAuthenticationContext AuthenticationContext { get; } + /// + /// Gets the for the . + /// + protected IDatabaseContext DatabaseContext { get; } + /// /// Gets the for the . /// @@ -71,12 +77,15 @@ namespace Tgstation.Server.Host.Authority.Core /// Initializes a new instance of the class. /// /// The value of . + /// The value of . /// The value of . protected AuthorityBase( IAuthenticationContext authenticationContext, + IDatabaseContext databaseContext, ILogger logger) { AuthenticationContext = authenticationContext ?? throw new ArgumentNullException(nameof(authenticationContext)); + DatabaseContext = databaseContext ?? throw new ArgumentNullException(nameof(databaseContext)); Logger = logger ?? throw new ArgumentNullException(nameof(logger)); } diff --git a/src/Tgstation.Server.Host/Authority/IUserGroupAuthority.cs b/src/Tgstation.Server.Host/Authority/IUserGroupAuthority.cs index 3afd0c5777..cd4845a5e5 100644 --- a/src/Tgstation.Server.Host/Authority/IUserGroupAuthority.cs +++ b/src/Tgstation.Server.Host/Authority/IUserGroupAuthority.cs @@ -1,4 +1,5 @@ -using System.Threading; +using System.Linq; +using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api.Rights; @@ -13,6 +14,12 @@ namespace Tgstation.Server.Host.Authority /// public interface IUserGroupAuthority : IAuthority { + /// + /// Gets the current . + /// + /// A resulting in a . + ValueTask> Read(); + /// /// Gets the with a given . /// @@ -21,5 +28,12 @@ namespace Tgstation.Server.Host.Authority /// A resulting in a . [TgsAuthorize(AdministrationRights.ReadUsers)] public ValueTask> GetId(long id, CancellationToken cancellationToken); + + /// + /// Gets all registered s. + /// + /// A of s. + [TgsAuthorize(AdministrationRights.ReadUsers)] + IQueryable Queryable(); } } diff --git a/src/Tgstation.Server.Host/Authority/LoginAuthority.cs b/src/Tgstation.Server.Host/Authority/LoginAuthority.cs index d32e066478..3ed1e1315d 100644 --- a/src/Tgstation.Server.Host/Authority/LoginAuthority.cs +++ b/src/Tgstation.Server.Host/Authority/LoginAuthority.cs @@ -31,11 +31,6 @@ namespace Tgstation.Server.Host.Authority /// readonly ISystemIdentityFactory systemIdentityFactory; - /// - /// The for the . - /// - readonly IDatabaseContext databaseContext; - /// /// The for the . /// @@ -102,29 +97,31 @@ namespace Tgstation.Server.Host.Authority /// Initializes a new instance of the class. /// /// The to use. + /// The to use. /// The to use. /// The value of . /// The value of . - /// The value of . /// The value of . /// The value of . /// The value of . /// The value of . public LoginAuthority( IAuthenticationContext authenticationContext, + IDatabaseContext databaseContext, ILogger logger, IApiHeadersProvider apiHeadersProvider, ISystemIdentityFactory systemIdentityFactory, - IDatabaseContext databaseContext, IOAuthProviders oAuthProviders, ITokenFactory tokenFactory, ICryptographySuite cryptographySuite, IIdentityCache identityCache) - : base(authenticationContext, logger) + : base( + authenticationContext, + databaseContext, + logger) { this.apiHeadersProvider = apiHeadersProvider ?? throw new ArgumentNullException(nameof(apiHeadersProvider)); this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory)); - this.databaseContext = databaseContext ?? throw new ArgumentNullException(nameof(databaseContext)); this.oAuthProviders = oAuthProviders ?? throw new ArgumentNullException(nameof(oAuthProviders)); this.tokenFactory = tokenFactory ?? throw new ArgumentNullException(nameof(tokenFactory)); this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite)); @@ -158,7 +155,7 @@ namespace Tgstation.Server.Host.Authority using (systemIdentity) { // Get the user from the database - IQueryable query = databaseContext.Users.AsQueryable(); + IQueryable query = DatabaseContext.Users.AsQueryable(); if (oAuthLogin) { var oAuthProvider = headers.OAuthProvider!.Value; @@ -228,9 +225,9 @@ namespace Tgstation.Server.Host.Authority { Id = user.Id, }; - databaseContext.Users.Attach(updatedUser); + DatabaseContext.Users.Attach(updatedUser); updatedUser.PasswordHash = user.PasswordHash; - await databaseContext.Save(cancellationToken); + await DatabaseContext.Save(cancellationToken); } } else @@ -238,7 +235,7 @@ namespace Tgstation.Server.Host.Authority var usernameMismatch = systemIdentity!.Username != user.Name; if (isLikelyDbUser || usernameMismatch) { - databaseContext.Users.Attach(user); + DatabaseContext.Users.Attach(user); if (isLikelyDbUser) { // cleanup from https://github.com/tgstation/tgstation-server/issues/1528 @@ -255,7 +252,7 @@ namespace Tgstation.Server.Host.Authority user.CanonicalName = User.CanonicalizeName(user.Name); } - await databaseContext.Save(cancellationToken); + await DatabaseContext.Save(cancellationToken); } } diff --git a/src/Tgstation.Server.Host/Authority/UserAuthority.cs b/src/Tgstation.Server.Host/Authority/UserAuthority.cs index a46e244ce2..6cb8ba430d 100644 --- a/src/Tgstation.Server.Host/Authority/UserAuthority.cs +++ b/src/Tgstation.Server.Host/Authority/UserAuthority.cs @@ -20,11 +20,6 @@ namespace Tgstation.Server.Host.Authority /// sealed class UserAuthority : AuthorityBase, IUserAuthority { - /// - /// The for the . - /// - readonly IDatabaseContext databaseContext; - /// /// The for the . /// @@ -88,20 +83,22 @@ namespace Tgstation.Server.Host.Authority /// /// Initializes a new instance of the class. /// + /// The to use. + /// The to use. /// The to use. - /// The value of . /// The value of . /// The value of . - /// The value of . public UserAuthority( IAuthenticationContext authenticationContext, - ILogger logger, IDatabaseContext databaseContext, + ILogger logger, IUsersDataLoader usersDataLoader, IOAuthConnectionsDataLoader oAuthConnectionsDataLoader) - : base(authenticationContext, logger) + : base( + authenticationContext, + databaseContext, + logger) { - this.databaseContext = databaseContext ?? throw new ArgumentNullException(nameof(databaseContext)); this.usersDataLoader = usersDataLoader ?? throw new ArgumentNullException(nameof(usersDataLoader)); this.oAuthConnectionsDataLoader = oAuthConnectionsDataLoader ?? throw new ArgumentNullException(nameof(oAuthConnectionsDataLoader)); } @@ -155,7 +152,7 @@ namespace Tgstation.Server.Host.Authority IQueryable Queryable(bool includeJoins, bool allowSystemUser) { var tgsUserCanonicalName = User.CanonicalizeName(User.TgsSystemUserName); - var queryable = databaseContext + var queryable = DatabaseContext .Users .AsQueryable(); diff --git a/src/Tgstation.Server.Host/Authority/UserGroupAuthority.cs b/src/Tgstation.Server.Host/Authority/UserGroupAuthority.cs index c15b7c4c2e..b6268906fc 100644 --- a/src/Tgstation.Server.Host/Authority/UserGroupAuthority.cs +++ b/src/Tgstation.Server.Host/Authority/UserGroupAuthority.cs @@ -51,13 +51,18 @@ namespace Tgstation.Server.Host.Authority /// Initializes a new instance of the class. /// /// The to use. + /// The to use. /// The to use. /// The value of . public UserGroupAuthority( IAuthenticationContext authenticationContext, + IDatabaseContext databaseContext, ILogger logger, IUserGroupsDataLoader userGroupsDataLoader) - : base(authenticationContext, logger) + : base( + authenticationContext, + databaseContext, + logger) { this.userGroupsDataLoader = userGroupsDataLoader ?? throw new ArgumentNullException(nameof(userGroupsDataLoader)); } @@ -74,5 +79,21 @@ namespace Tgstation.Server.Host.Authority return new AuthorityResponse(userGroup); } + + /// + public ValueTask> Read() + { + var group = AuthenticationContext.User!.Group; + if (group == null) + return ValueTask.FromResult(NotFound()); + + return ValueTask.FromResult(new AuthorityResponse(group)); + } + + /// + public IQueryable Queryable() + => DatabaseContext + .Groups + .AsQueryable(); } } diff --git a/src/Tgstation.Server.Host/GraphQL/Types/UserGroups.cs b/src/Tgstation.Server.Host/GraphQL/Types/UserGroups.cs new file mode 100644 index 0000000000..9cf6a9a58b --- /dev/null +++ b/src/Tgstation.Server.Host/GraphQL/Types/UserGroups.cs @@ -0,0 +1,65 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +using HotChocolate; +using HotChocolate.Data; +using HotChocolate.Types; +using HotChocolate.Types.Relay; + +using Tgstation.Server.Host.Authority; +using Tgstation.Server.Host.Models.Transformers; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.GraphQL.Types +{ + /// + /// Wrapper for accessing s. + /// + public sealed class UserGroups + { + /// + /// Gets the current . + /// + /// The . + /// A resulting in the current 's . + public ValueTask Current( + [Service] IGraphQLAuthorityInvoker userGroupAuthority) + { + ArgumentNullException.ThrowIfNull(userGroupAuthority); + return userGroupAuthority.InvokeTransformable(authority => authority.Read()); + } + + /// + /// Gets a by . + /// + /// The of the . + /// The . + /// The for the operation. + /// The represented by , if any. + [TgsGraphQLAuthorize(nameof(IUserGroupAuthority.GetId))] + public ValueTask ById( + [ID(nameof(UserGroup))] long id, + [Service] IGraphQLAuthorityInvoker userGroupAuthority, + CancellationToken cancellationToken) + => UserGroup.GetUserGroup(id, userGroupAuthority, cancellationToken); + + /// + /// Queries all registered s. + /// + /// The . + /// A of all registered s. + [UsePaging] + [UseFiltering] + [UseSorting] + [TgsGraphQLAuthorize(nameof(IUserGroupAuthority.Queryable))] + public IQueryable? Queryable( + [Service] IGraphQLAuthorityInvoker userGroupAuthority) + { + ArgumentNullException.ThrowIfNull(userGroupAuthority); + var dtoQueryable = userGroupAuthority.InvokeTransformableQueryable(authority => authority.Queryable()); + return dtoQueryable; + } + } +} diff --git a/src/Tgstation.Server.Host/GraphQL/Types/Users.cs b/src/Tgstation.Server.Host/GraphQL/Types/Users.cs index db0a333f53..95d8173efd 100644 --- a/src/Tgstation.Server.Host/GraphQL/Types/Users.cs +++ b/src/Tgstation.Server.Host/GraphQL/Types/Users.cs @@ -21,6 +21,12 @@ namespace Tgstation.Server.Host.GraphQL.Types /// public sealed class Users { + /// + /// Gets the swarm's . + /// + /// A new . + public UserGroups Groups() => new(); + /// /// Gets the current . /// @@ -37,7 +43,7 @@ namespace Tgstation.Server.Host.GraphQL.Types } /// - /// Gets a user by . + /// Gets a by . /// /// The of the . /// The . @@ -45,17 +51,17 @@ namespace Tgstation.Server.Host.GraphQL.Types /// The represented by , if any. [Error(typeof(ErrorMessageException))] [TgsGraphQLAuthorize(nameof(IUserAuthority.GetId))] - public async ValueTask ById( + public ValueTask ById( [ID(nameof(User))] long id, [Service] IGraphQLAuthorityInvoker userAuthority, CancellationToken cancellationToken) - => await User.GetUser(id, userAuthority, cancellationToken); + => User.GetUser(id, userAuthority, cancellationToken); /// - /// Lists all registered s. + /// Queries all registered s. /// /// The . - /// A list of all registered s. + /// A of all registered s. [UsePaging] [UseFiltering] [UseSorting]