mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-24 21:46:52 +01:00
More UserGroup querying work
This commit is contained in:
@@ -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
|
||||
/// </summary>
|
||||
protected IAuthenticationContext AuthenticationContext { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IDatabaseContext"/> for the <see cref="AuthorityBase"/>.
|
||||
/// </summary>
|
||||
protected IDatabaseContext DatabaseContext { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ILogger"/> for the <see cref="AuthorityBase"/>.
|
||||
/// </summary>
|
||||
@@ -71,12 +77,15 @@ namespace Tgstation.Server.Host.Authority.Core
|
||||
/// Initializes a new instance of the <see cref="AuthorityBase"/> class.
|
||||
/// </summary>
|
||||
/// <param name="authenticationContext">The value of <see cref="AuthenticationContext"/>.</param>
|
||||
/// <param name="databaseContext">The value of <see cref="DatabaseContext"/>.</param>
|
||||
/// <param name="logger">The value of <see cref="Logger"/>.</param>
|
||||
protected AuthorityBase(
|
||||
IAuthenticationContext authenticationContext,
|
||||
IDatabaseContext databaseContext,
|
||||
ILogger<AuthorityBase> logger)
|
||||
{
|
||||
AuthenticationContext = authenticationContext ?? throw new ArgumentNullException(nameof(authenticationContext));
|
||||
DatabaseContext = databaseContext ?? throw new ArgumentNullException(nameof(databaseContext));
|
||||
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
/// </summary>
|
||||
public interface IUserGroupAuthority : IAuthority
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current <see cref="UserGroup"/>.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in a <see cref="UserGroup"/> <see cref="AuthorityResponse{TResult}"/>.</returns>
|
||||
ValueTask<AuthorityResponse<UserGroup>> Read();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="UserGroup"/> with a given <paramref name="id"/>.
|
||||
/// </summary>
|
||||
@@ -21,5 +28,12 @@ namespace Tgstation.Server.Host.Authority
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in a <see cref="User"/> <see cref="AuthorityResponse{TResult}"/>.</returns>
|
||||
[TgsAuthorize(AdministrationRights.ReadUsers)]
|
||||
public ValueTask<AuthorityResponse<UserGroup>> GetId(long id, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all registered <see cref="UserGroup"/>s.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="IQueryable{T}"/> of <see cref="UserGroup"/>s.</returns>
|
||||
[TgsAuthorize(AdministrationRights.ReadUsers)]
|
||||
IQueryable<UserGroup> Queryable();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,11 +31,6 @@ namespace Tgstation.Server.Host.Authority
|
||||
/// </summary>
|
||||
readonly ISystemIdentityFactory systemIdentityFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IDatabaseContext"/> for the <see cref="LoginAuthority"/>.
|
||||
/// </summary>
|
||||
readonly IDatabaseContext databaseContext;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IOAuthProviders"/> for the <see cref="LoginAuthority"/>.
|
||||
/// </summary>
|
||||
@@ -102,29 +97,31 @@ namespace Tgstation.Server.Host.Authority
|
||||
/// Initializes a new instance of the <see cref="LoginAuthority"/> class.
|
||||
/// </summary>
|
||||
/// <param name="authenticationContext">The <see cref="IAuthenticationContext"/> to use.</param>
|
||||
/// <param name="databaseContext">The <see cref="IDatabaseContext"/> to use.</param>
|
||||
/// <param name="logger">The <see cref="ILogger"/> to use.</param>
|
||||
/// <param name="apiHeadersProvider">The value of <see cref="apiHeadersProvider"/>.</param>
|
||||
/// <param name="systemIdentityFactory">The value of <see cref="systemIdentityFactory"/>.</param>
|
||||
/// <param name="databaseContext">The value of <see cref="databaseContext"/>.</param>
|
||||
/// <param name="oAuthProviders">The value of <see cref="oAuthProviders"/>.</param>
|
||||
/// <param name="tokenFactory">The value of <see cref="tokenFactory"/>.</param>
|
||||
/// <param name="cryptographySuite">The value of <see cref="cryptographySuite"/>.</param>
|
||||
/// <param name="identityCache">The value of <see cref="identityCache"/>.</param>
|
||||
public LoginAuthority(
|
||||
IAuthenticationContext authenticationContext,
|
||||
IDatabaseContext databaseContext,
|
||||
ILogger<LoginAuthority> 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<User> query = databaseContext.Users.AsQueryable();
|
||||
IQueryable<User> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,6 @@ namespace Tgstation.Server.Host.Authority
|
||||
/// <inheritdoc cref="IUserAuthority" />
|
||||
sealed class UserAuthority : AuthorityBase, IUserAuthority
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IDatabaseContext"/> for the <see cref="UserAuthority"/>.
|
||||
/// </summary>
|
||||
readonly IDatabaseContext databaseContext;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IUsersDataLoader"/> for the <see cref="UserAuthority"/>.
|
||||
/// </summary>
|
||||
@@ -88,20 +83,22 @@ namespace Tgstation.Server.Host.Authority
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UserAuthority"/> class.
|
||||
/// </summary>
|
||||
/// <param name="authenticationContext">The <see cref="IAuthenticationContext"/> to use.</param>
|
||||
/// <param name="databaseContext">The <see cref="IDatabaseContext"/> to use.</param>
|
||||
/// <param name="logger">The <see cref="ILogger"/> to use.</param>
|
||||
/// <param name="databaseContext">The value of <see cref="databaseContext"/>.</param>
|
||||
/// <param name="usersDataLoader">The value of <see cref="usersDataLoader"/>.</param>
|
||||
/// <param name="oAuthConnectionsDataLoader">The value of <see cref="oAuthConnectionsDataLoader"/>.</param>
|
||||
/// <param name="authenticationContext">The value of <see cref="AuthenticationContext"/>.</param>
|
||||
public UserAuthority(
|
||||
IAuthenticationContext authenticationContext,
|
||||
ILogger<UserAuthority> logger,
|
||||
IDatabaseContext databaseContext,
|
||||
ILogger<UserAuthority> 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<User> Queryable(bool includeJoins, bool allowSystemUser)
|
||||
{
|
||||
var tgsUserCanonicalName = User.CanonicalizeName(User.TgsSystemUserName);
|
||||
var queryable = databaseContext
|
||||
var queryable = DatabaseContext
|
||||
.Users
|
||||
.AsQueryable();
|
||||
|
||||
|
||||
@@ -51,13 +51,18 @@ namespace Tgstation.Server.Host.Authority
|
||||
/// Initializes a new instance of the <see cref="UserGroupAuthority"/> class.
|
||||
/// </summary>
|
||||
/// <param name="authenticationContext">The <see cref="IAuthenticationContext"/> to use.</param>
|
||||
/// <param name="databaseContext">The <see cref="IDatabaseContext"/> to use.</param>
|
||||
/// <param name="logger">The <see cref="ILogger"/> to use.</param>
|
||||
/// <param name="userGroupsDataLoader">The value of <see cref="userGroupsDataLoader"/>.</param>
|
||||
public UserGroupAuthority(
|
||||
IAuthenticationContext authenticationContext,
|
||||
IDatabaseContext databaseContext,
|
||||
ILogger<UserGroupAuthority> 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>(userGroup);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ValueTask<AuthorityResponse<UserGroup>> Read()
|
||||
{
|
||||
var group = AuthenticationContext.User!.Group;
|
||||
if (group == null)
|
||||
return ValueTask.FromResult(NotFound<UserGroup>());
|
||||
|
||||
return ValueTask.FromResult(new AuthorityResponse<UserGroup>(group));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IQueryable<UserGroup> Queryable()
|
||||
=> DatabaseContext
|
||||
.Groups
|
||||
.AsQueryable();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper for accessing <see cref="UserGroup"/>s.
|
||||
/// </summary>
|
||||
public sealed class UserGroups
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current <see cref="User"/>.
|
||||
/// </summary>
|
||||
/// <param name="userGroupAuthority">The <see cref="IGraphQLAuthorityInvoker{TAuthority}"/> <see cref="IUserGroupAuthority"/>.</param>
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in the current <see cref="User"/>'s <see cref="UserGroup"/>.</returns>
|
||||
public ValueTask<UserGroup?> Current(
|
||||
[Service] IGraphQLAuthorityInvoker<IUserGroupAuthority> userGroupAuthority)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(userGroupAuthority);
|
||||
return userGroupAuthority.InvokeTransformable<Models.UserGroup, UserGroup, UserGroupGraphQLTransformer>(authority => authority.Read());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="UserGroup"/> by <see cref="Entity.Id"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="Entity.Id"/> of the <see cref="User"/>.</param>
|
||||
/// <param name="userGroupAuthority">The <see cref="IGraphQLAuthorityInvoker{TAuthority}"/> <see cref="IUserGroupAuthority"/>.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>The <see cref="UserGroup"/> represented by <paramref name="id"/>, if any.</returns>
|
||||
[TgsGraphQLAuthorize<IUserAuthority>(nameof(IUserGroupAuthority.GetId))]
|
||||
public ValueTask<UserGroup?> ById(
|
||||
[ID(nameof(UserGroup))] long id,
|
||||
[Service] IGraphQLAuthorityInvoker<IUserGroupAuthority> userGroupAuthority,
|
||||
CancellationToken cancellationToken)
|
||||
=> UserGroup.GetUserGroup(id, userGroupAuthority, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Queries all registered <see cref="UserGroup"/>s.
|
||||
/// </summary>
|
||||
/// <param name="userGroupAuthority">The <see cref="IGraphQLAuthorityInvoker{TAuthority}"/> <see cref="IUserGroupAuthority"/>.</param>
|
||||
/// <returns>A <see cref="IQueryable{T}"/> of all registered <see cref="UserGroup"/>s.</returns>
|
||||
[UsePaging]
|
||||
[UseFiltering]
|
||||
[UseSorting]
|
||||
[TgsGraphQLAuthorize<IUserGroupAuthority>(nameof(IUserGroupAuthority.Queryable))]
|
||||
public IQueryable<UserGroup>? Queryable(
|
||||
[Service] IGraphQLAuthorityInvoker<IUserGroupAuthority> userGroupAuthority)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(userGroupAuthority);
|
||||
var dtoQueryable = userGroupAuthority.InvokeTransformableQueryable<Models.UserGroup, UserGroup, UserGroupGraphQLTransformer>(authority => authority.Queryable());
|
||||
return dtoQueryable;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,12 @@ namespace Tgstation.Server.Host.GraphQL.Types
|
||||
/// </summary>
|
||||
public sealed class Users
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the swarm's <see cref="UserGroups"/>.
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="UserGroups"/>.</returns>
|
||||
public UserGroups Groups() => new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current <see cref="User"/>.
|
||||
/// </summary>
|
||||
@@ -37,7 +43,7 @@ namespace Tgstation.Server.Host.GraphQL.Types
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a user by <see cref="Entity.Id"/>.
|
||||
/// Gets a <see cref="User"/> by <see cref="Entity.Id"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="Entity.Id"/> of the <see cref="User"/>.</param>
|
||||
/// <param name="userAuthority">The <see cref="IGraphQLAuthorityInvoker{TAuthority}"/> <see cref="IUserAuthority"/>.</param>
|
||||
@@ -45,17 +51,17 @@ namespace Tgstation.Server.Host.GraphQL.Types
|
||||
/// <returns>The <see cref="User"/> represented by <paramref name="id"/>, if any.</returns>
|
||||
[Error(typeof(ErrorMessageException))]
|
||||
[TgsGraphQLAuthorize<IUserAuthority>(nameof(IUserAuthority.GetId))]
|
||||
public async ValueTask<User?> ById(
|
||||
public ValueTask<User?> ById(
|
||||
[ID(nameof(User))] long id,
|
||||
[Service] IGraphQLAuthorityInvoker<IUserAuthority> userAuthority,
|
||||
CancellationToken cancellationToken)
|
||||
=> await User.GetUser(id, userAuthority, cancellationToken);
|
||||
=> User.GetUser(id, userAuthority, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Lists all registered <see cref="User"/>s.
|
||||
/// Queries all registered <see cref="User"/>s.
|
||||
/// </summary>
|
||||
/// <param name="userAuthority">The <see cref="IGraphQLAuthorityInvoker{TAuthority}"/> <see cref="IUserAuthority"/>.</param>
|
||||
/// <returns>A list of all registered <see cref="User"/>s.</returns>
|
||||
/// <returns>A <see cref="IQueryable{T}"/> of all registered <see cref="User"/>s.</returns>
|
||||
[UsePaging]
|
||||
[UseFiltering]
|
||||
[UseSorting]
|
||||
|
||||
Reference in New Issue
Block a user