diff --git a/src/Tgstation.Server.Host/Authority/IUserAuthority.cs b/src/Tgstation.Server.Host/Authority/IUserAuthority.cs
index d95d7d58a1..b0a67681cd 100644
--- a/src/Tgstation.Server.Host/Authority/IUserAuthority.cs
+++ b/src/Tgstation.Server.Host/Authority/IUserAuthority.cs
@@ -55,10 +55,14 @@ namespace Tgstation.Server.Host.Authority
/// Creates a .
///
/// The .
+ /// If a zero-length indicates and OAuth only user.
/// The for the operation.
/// A resulting in am for the created .
[TgsAuthorize(AdministrationRights.WriteUsers)]
- ValueTask> Create(UserCreateRequest createRequest, CancellationToken cancellationToken);
+ ValueTask> Create(
+ UserCreateRequest createRequest,
+ bool? needZeroLengthPasswordWithOAuthConnections,
+ CancellationToken cancellationToken);
///
/// Updates a .
diff --git a/src/Tgstation.Server.Host/Authority/UserAuthority.cs b/src/Tgstation.Server.Host/Authority/UserAuthority.cs
index ed7dba0997..b670c01243 100644
--- a/src/Tgstation.Server.Host/Authority/UserAuthority.cs
+++ b/src/Tgstation.Server.Host/Authority/UserAuthority.cs
@@ -202,6 +202,7 @@ namespace Tgstation.Server.Host.Authority
///
public async ValueTask> Create(
UserCreateRequest createRequest,
+ bool? needZeroLengthPasswordWithOAuthConnections,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(createRequest);
@@ -209,10 +210,28 @@ namespace Tgstation.Server.Host.Authority
if (createRequest.OAuthConnections?.Any(x => x == null) == true)
return BadRequest(ErrorCode.ModelValidationFailure);
- if ((createRequest.Password != null && createRequest.SystemIdentifier != null)
- || (createRequest.Password == null && createRequest.SystemIdentifier == null && (createRequest.OAuthConnections?.Count > 0) != true))
+ var hasNonNullPassword = createRequest.Password != null;
+ var hasNonNullSystemIdentifier = createRequest.SystemIdentifier != null;
+ var hasOAuthConnections = (createRequest.OAuthConnections?.Count > 0) == true;
+ if ((hasNonNullPassword && hasNonNullSystemIdentifier)
+ || (!hasNonNullPassword && !hasNonNullSystemIdentifier && !hasOAuthConnections))
return BadRequest(ErrorCode.UserMismatchPasswordSid);
+ var hasZeroLengthPassword = createRequest.Password?.Length == 0;
+ if (needZeroLengthPasswordWithOAuthConnections.HasValue)
+ {
+ if (needZeroLengthPasswordWithOAuthConnections.Value)
+ {
+ if (createRequest.OAuthConnections == null)
+ throw new InvalidOperationException($"Expected {nameof(UserCreateRequest.OAuthConnections)} to be set here!");
+
+ if (createRequest.OAuthConnections.Count == 0)
+ return BadRequest(ErrorCode.ModelValidationFailure);
+ }
+ else if (hasZeroLengthPassword)
+ return BadRequest(ErrorCode.ModelValidationFailure);
+ }
+
if (createRequest.Group != null && createRequest.PermissionSet != null)
return BadRequest(ErrorCode.UserGroupAndPermissionSet);
@@ -254,11 +273,14 @@ namespace Tgstation.Server.Host.Authority
new ErrorMessageResponse(ErrorCode.RequiresPosixSystemIdentity),
HttpFailureResponse.NotImplemented);
}
- else if (!(createRequest.Password?.Length == 0 && (createRequest.OAuthConnections?.Count > 0) == true))
+ else
{
- var result = TrySetPassword(dbUser, createRequest.Password!, true);
- if (result != null)
- return result;
+ if (!(needZeroLengthPasswordWithOAuthConnections != false && hasZeroLengthPassword && hasOAuthConnections)) // special case allow PasswordHash to be null by setting Password to "" if OAuthConnections are set
+ {
+ var result = TrySetPassword(dbUser, createRequest.Password!, true);
+ if (result != null)
+ return result;
+ }
}
dbUser.CanonicalName = User.CanonicalizeName(dbUser.Name!);
diff --git a/src/Tgstation.Server.Host/Controllers/UserController.cs b/src/Tgstation.Server.Host/Controllers/UserController.cs
index be6866f928..f55218ef64 100644
--- a/src/Tgstation.Server.Host/Controllers/UserController.cs
+++ b/src/Tgstation.Server.Host/Controllers/UserController.cs
@@ -67,7 +67,7 @@ namespace Tgstation.Server.Host.Controllers
[TgsRestAuthorize(nameof(IUserAuthority.Create))]
[ProducesResponseType(typeof(UserResponse), 201)]
public ValueTask Create([FromBody] UserCreateRequest model, CancellationToken cancellationToken)
- => userAuthority.InvokeTransformable(this, authority => authority.Create(model, cancellationToken));
+ => userAuthority.InvokeTransformable(this, authority => authority.Create(model, null, cancellationToken));
///
/// Update a .
diff --git a/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs b/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs
index 177ddbd1f1..50c8587e0a 100644
--- a/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs
+++ b/src/Tgstation.Server.Host/GraphQL/Mutations/UserMutations.cs
@@ -72,53 +72,7 @@ namespace Tgstation.Server.Host.GraphQL.Mutations
})
.ToList(),
},
- cancellationToken));
- }
-
- ///
- /// Creates a system user specifying a personal .
- ///
- /// The of the .
- /// If the is .
- /// The s for the user.
- /// The owned of the user.
- /// The for the .
- /// The for the operation.
- /// The created .
- [TgsGraphQLAuthorize(nameof(IUserAuthority.Create))]
- [Error(typeof(ErrorMessageException))]
- public ValueTask CreateUserBySystemIDAndPermissionSet(
- string systemIdentifier,
- bool? enabled,
- IEnumerable? oAuthConnections,
- PermissionSetInput permissionSet,
- [Service] IGraphQLAuthorityInvoker userAuthority,
- CancellationToken cancellationToken)
- {
- ArgumentException.ThrowIfNullOrWhiteSpace(systemIdentifier);
- ArgumentNullException.ThrowIfNull(userAuthority);
-
- return userAuthority.InvokeTransformable(
- authority => authority.Create(
- new UserCreateRequest
- {
- SystemIdentifier = systemIdentifier,
- Enabled = enabled,
- PermissionSet = permissionSet != null
- ? new Api.Models.PermissionSet
- {
- AdministrationRights = permissionSet.AdministrationRights,
- InstanceManagerRights = permissionSet.InstanceManagerRights,
- }
- : null,
- OAuthConnections = oAuthConnections
- ?.Select(oAuthConnection => new Api.Models.OAuthConnection
- {
- ExternalUserId = oAuthConnection.ExternalUserId,
- Provider = oAuthConnection.Provider,
- })
- .ToList(),
- },
+ false,
cancellationToken));
}
@@ -167,6 +121,152 @@ namespace Tgstation.Server.Host.GraphQL.Mutations
})
.ToList(),
},
+ false,
+ cancellationToken));
+ }
+
+ ///
+ /// Creates a TGS user authenticated with one or mor s specifying a personal .
+ ///
+ /// The of the .
+ /// The s for the user.
+ /// If the is .
+ /// The owned of the user.
+ /// The for the .
+ /// The for the operation.
+ /// The created .
+ [TgsGraphQLAuthorize(nameof(IUserAuthority.Create))]
+ [Error(typeof(ErrorMessageException))]
+ public ValueTask CreateUserByOAuthAndPermissionSet(
+ string name,
+ IEnumerable oAuthConnections,
+ bool? enabled,
+ PermissionSetInput? permissionSet,
+ [Service] IGraphQLAuthorityInvoker userAuthority,
+ CancellationToken cancellationToken)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(name);
+ ArgumentNullException.ThrowIfNull(oAuthConnections);
+ ArgumentNullException.ThrowIfNull(userAuthority);
+
+ return userAuthority.InvokeTransformable(
+ authority => authority.Create(
+ new UserCreateRequest
+ {
+ Name = name,
+ Password = String.Empty,
+ Enabled = enabled,
+ PermissionSet = permissionSet != null
+ ? new Api.Models.PermissionSet
+ {
+ AdministrationRights = permissionSet.AdministrationRights,
+ InstanceManagerRights = permissionSet.InstanceManagerRights,
+ }
+ : null,
+ OAuthConnections = oAuthConnections
+ .Select(oAuthConnection => new Api.Models.OAuthConnection
+ {
+ ExternalUserId = oAuthConnection.ExternalUserId,
+ Provider = oAuthConnection.Provider,
+ })
+ .ToList(),
+ },
+ true,
+ cancellationToken));
+ }
+
+ ///
+ /// Creates a TGS user specifying the they will belong to.
+ ///
+ /// The of the .
+ /// The s for the user.
+ /// The of the the will belong to.
+ /// If the is .
+ /// The for the .
+ /// The for the operation.
+ /// The created .
+ [TgsGraphQLAuthorize(nameof(IUserAuthority.Create))]
+ [Error(typeof(ErrorMessageException))]
+ public ValueTask CreateUserByOAuthAndGroup(
+ string name,
+ IEnumerable oAuthConnections,
+ [ID(nameof(UserGroup))] long groupId,
+ bool? enabled,
+ [Service] IGraphQLAuthorityInvoker userAuthority,
+ CancellationToken cancellationToken)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(name);
+ ArgumentNullException.ThrowIfNull(oAuthConnections);
+ ArgumentNullException.ThrowIfNull(userAuthority);
+
+ return userAuthority.InvokeTransformable(
+ authority => authority.Create(
+ new UserCreateRequest
+ {
+ Name = name,
+ Password = String.Empty,
+ Enabled = enabled,
+ Group = new Api.Models.Internal.UserGroup
+ {
+ Id = groupId,
+ },
+ OAuthConnections = oAuthConnections
+ .Select(oAuthConnection => new Api.Models.OAuthConnection
+ {
+ ExternalUserId = oAuthConnection.ExternalUserId,
+ Provider = oAuthConnection.Provider,
+ })
+ .ToList(),
+ },
+ true,
+ cancellationToken));
+ }
+
+ ///
+ /// Creates a system user specifying a personal .
+ ///
+ /// The of the .
+ /// If the is .
+ /// The s for the user.
+ /// The owned of the user.
+ /// The for the .
+ /// The for the operation.
+ /// The created .
+ [TgsGraphQLAuthorize(nameof(IUserAuthority.Create))]
+ [Error(typeof(ErrorMessageException))]
+ public ValueTask CreateUserBySystemIDAndPermissionSet(
+ string systemIdentifier,
+ bool? enabled,
+ IEnumerable? oAuthConnections,
+ PermissionSetInput permissionSet,
+ [Service] IGraphQLAuthorityInvoker userAuthority,
+ CancellationToken cancellationToken)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(systemIdentifier);
+ ArgumentNullException.ThrowIfNull(userAuthority);
+
+ return userAuthority.InvokeTransformable(
+ authority => authority.Create(
+ new UserCreateRequest
+ {
+ SystemIdentifier = systemIdentifier,
+ Enabled = enabled,
+ PermissionSet = permissionSet != null
+ ? new Api.Models.PermissionSet
+ {
+ AdministrationRights = permissionSet.AdministrationRights,
+ InstanceManagerRights = permissionSet.InstanceManagerRights,
+ }
+ : null,
+ OAuthConnections = oAuthConnections
+ ?.Select(oAuthConnection => new Api.Models.OAuthConnection
+ {
+ ExternalUserId = oAuthConnection.ExternalUserId,
+ Provider = oAuthConnection.Provider,
+ })
+ .ToList(),
+ },
+ false,
cancellationToken));
}
@@ -175,8 +275,8 @@ namespace Tgstation.Server.Host.GraphQL.Mutations
///
/// The of the .
/// If the is .
- /// The s for the user.
/// The of the the will belong to.
+ /// The s for the user.
/// The for the .
/// The for the operation.
/// The created .
@@ -185,8 +285,8 @@ namespace Tgstation.Server.Host.GraphQL.Mutations
public ValueTask CreateUserBySystemIDAndGroup(
string systemIdentifier,
bool? enabled,
- IEnumerable? oAuthConnections,
[ID(nameof(UserGroup))] long groupId,
+ IEnumerable? oAuthConnections,
[Service] IGraphQLAuthorityInvoker userAuthority,
CancellationToken cancellationToken)
{
@@ -211,6 +311,7 @@ namespace Tgstation.Server.Host.GraphQL.Mutations
})
.ToList(),
},
+ false,
cancellationToken));
}