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));
+ }
}
}