diff --git a/src/Tgstation.Server.Host/GraphQL/Mutations/Payloads/PermissionSetInput.cs b/src/Tgstation.Server.Host/GraphQL/Mutations/Payloads/PermissionSetInput.cs new file mode 100644 index 0000000000..9e0ffc27a1 --- /dev/null +++ b/src/Tgstation.Server.Host/GraphQL/Mutations/Payloads/PermissionSetInput.cs @@ -0,0 +1,20 @@ +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Host.GraphQL.Mutations.Payloads +{ + /// + /// Updates a set of permissions for the server. values default to their "None" variants. + /// + public sealed class PermissionSetInput + { + /// + /// The for the . + /// + public required AdministrationRights? AdministrationRights { get; init; } + + /// + /// The for the . + /// + public required InstanceManagerRights? InstanceManagerRights { get; init; } + } +} diff --git a/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs b/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs index 9c5baac4e0..6861380169 100644 --- a/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs +++ b/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs @@ -9,7 +9,9 @@ using HotChocolate.Types; using HotChocolate.Types.Relay; using Tgstation.Server.Api.Models.Request; +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.Models.Transformers; using Tgstation.Server.Host.Security; @@ -32,7 +34,7 @@ namespace Tgstation.Server.Host.GraphQL.Mutations /// The owned of the user. /// The . /// The for the operation. - /// A resulting in the created . + /// The created . [TgsGraphQLAuthorize(nameof(IUserAuthority.Create))] [Error(typeof(ErrorMessageException))] public ValueTask CreateUserByPasswordAndPermissionSet( @@ -40,7 +42,7 @@ namespace Tgstation.Server.Host.GraphQL.Mutations string password, bool enabled, IEnumerable? oAuthConnections, - PermissionSet permissionSet, + PermissionSetInput permissionSet, [Service] IGraphQLAuthorityInvoker userAuthority, CancellationToken cancellationToken) { @@ -81,14 +83,14 @@ namespace Tgstation.Server.Host.GraphQL.Mutations /// The owned of the user. /// The . /// The for the operation. - /// A resulting in the created . + /// The created . [TgsGraphQLAuthorize(nameof(IUserAuthority.Create))] [Error(typeof(ErrorMessageException))] public ValueTask CreateUserBySystemIDAndPermissionSet( string systemIdentifier, bool enabled, IEnumerable? oAuthConnections, - PermissionSet permissionSet, + PermissionSetInput permissionSet, [Service] IGraphQLAuthorityInvoker userAuthority, CancellationToken cancellationToken) { @@ -128,7 +130,7 @@ namespace Tgstation.Server.Host.GraphQL.Mutations /// The of the the will belong to. /// The . /// The for the operation. - /// A resulting in the created . + /// The created . [TgsGraphQLAuthorize(nameof(IUserAuthority.Create))] [Error(typeof(ErrorMessageException))] public ValueTask CreateUserByPasswordAndGroup( @@ -175,7 +177,7 @@ namespace Tgstation.Server.Host.GraphQL.Mutations /// The of the the will belong to. /// The . /// The for the operation. - /// A resulting in the created . + /// The created . [TgsGraphQLAuthorize(nameof(IUserAuthority.Create))] [Error(typeof(ErrorMessageException))] public ValueTask CreateUserBySystemIDAndGroup( @@ -209,5 +211,127 @@ namespace Tgstation.Server.Host.GraphQL.Mutations }, cancellationToken)); } + + /// + /// Sets the current user's password. + /// + /// The new password for the current user. + /// The to get the of the user. + /// The . + /// The for the operation. + /// The updated current . + [TgsGraphQLAuthorize(AdministrationRights.WriteUsers | AdministrationRights.EditOwnPassword)] + [Error(typeof(ErrorMessageException))] + public ValueTask SetCurrentUserPassword( + string newPassword, + [Service] IAuthenticationContext authenticationContext, + [Service] IGraphQLAuthorityInvoker userAuthority, + CancellationToken cancellationToken) + { + ArgumentException.ThrowIfNullOrEmpty(newPassword); + ArgumentNullException.ThrowIfNull(userAuthority); + return userAuthority.InvokeTransformable( + async authority => await authority.Update( + new UserUpdateRequest + { + Id = authenticationContext.User.Id, + Password = newPassword, + }, + cancellationToken)); + } + + /// + /// Sets the current user's s. + /// + /// The new s for the current user. + /// The to get the of the user. + /// The . + /// The for the operation. + /// The updated current . + [TgsGraphQLAuthorize(AdministrationRights.WriteUsers | AdministrationRights.EditOwnOAuthConnections)] + [Error(typeof(ErrorMessageException))] + public ValueTask SetCurrentOAuthConnections( + IEnumerable newOAuthConnections, + [Service] IAuthenticationContext authenticationContext, + [Service] IGraphQLAuthorityInvoker userAuthority, + CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(newOAuthConnections); + ArgumentNullException.ThrowIfNull(userAuthority); + return userAuthority.InvokeTransformable( + async authority => await authority.Update( + new UserUpdateRequest + { + Id = authenticationContext.User.Id, + OAuthConnections = newOAuthConnections + .Select(oAuthConnection => new Api.Models.OAuthConnection + { + ExternalUserId = oAuthConnection.ExternalUserId, + Provider = oAuthConnection.Provider, + }) + .ToList(), + }, + cancellationToken)); + } + + /// + /// Updates a user. + /// + /// The of the to update. + /// Optional casing only change to the of the . Only applicable to TGS users. + /// Optional new password for the . Only applicable to TGS users. + /// Optional new status for the . + /// Optional new owned for the user. + /// Optional of the to move the to. + /// Optional new s for the . + /// The . + /// The for the operation. + /// The updated . + [TgsGraphQLAuthorize(AdministrationRights.WriteUsers)] + [Error(typeof(ErrorMessageException))] + public ValueTask UpdateUser( + [ID(nameof(User))] long id, + string? casingOnlyNameChange, + string? newPassword, + bool? enabled, + PermissionSetInput? newPermissionSet, + [ID(nameof(UserGroup))] long? newGroupId, + IEnumerable? newOAuthConnections, + [Service] IGraphQLAuthorityInvoker userAuthority, + CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(newOAuthConnections); + ArgumentNullException.ThrowIfNull(userAuthority); + return userAuthority.InvokeTransformable( + async authority => await authority.Update( + new UserUpdateRequest + { + Id = id, + Name = casingOnlyNameChange, + Password = newPassword, + Enabled = enabled, + PermissionSet = newPermissionSet != null + ? new Api.Models.PermissionSet + { + InstanceManagerRights = newPermissionSet.InstanceManagerRights, + AdministrationRights = newPermissionSet.AdministrationRights, + } + : null, + Group = newGroupId.HasValue + ? new Api.Models.Internal.UserGroup + { + Id = newGroupId.Value, + } + : null, + OAuthConnections = newOAuthConnections + .Select(oAuthConnection => new Api.Models.OAuthConnection + { + ExternalUserId = oAuthConnection.ExternalUserId, + Provider = oAuthConnection.Provider, + }) + .ToList(), + }, + cancellationToken)); + } } }