mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-28 23:52:36 +01:00
Rework the 0-length password on create with OAuth scenario to be a little bit saner
This commit is contained in:
@@ -55,10 +55,14 @@ namespace Tgstation.Server.Host.Authority
|
||||
/// Creates a <see cref="User"/>.
|
||||
/// </summary>
|
||||
/// <param name="createRequest">The <see cref="UserCreateRequest"/>.</param>
|
||||
/// <param name="needZeroLengthPasswordWithOAuthConnections">If a zero-length <see cref="UserUpdateRequest.Password"/> indicates and OAuth only user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in am <see cref="AuthorityResponse{TResult}"/> for the created <see cref="User"/>.</returns>
|
||||
[TgsAuthorize(AdministrationRights.WriteUsers)]
|
||||
ValueTask<AuthorityResponse<User>> Create(UserCreateRequest createRequest, CancellationToken cancellationToken);
|
||||
ValueTask<AuthorityResponse<User>> Create(
|
||||
UserCreateRequest createRequest,
|
||||
bool? needZeroLengthPasswordWithOAuthConnections,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Updates a <see cref="User"/>.
|
||||
|
||||
@@ -202,6 +202,7 @@ namespace Tgstation.Server.Host.Authority
|
||||
/// <inheritdoc />
|
||||
public async ValueTask<AuthorityResponse<User>> 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<User>(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<User>(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<User>(ErrorCode.ModelValidationFailure);
|
||||
}
|
||||
else if (hasZeroLengthPassword)
|
||||
return BadRequest<User>(ErrorCode.ModelValidationFailure);
|
||||
}
|
||||
|
||||
if (createRequest.Group != null && createRequest.PermissionSet != null)
|
||||
return BadRequest<User>(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!);
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
[TgsRestAuthorize<IUserAuthority>(nameof(IUserAuthority.Create))]
|
||||
[ProducesResponseType(typeof(UserResponse), 201)]
|
||||
public ValueTask<IActionResult> Create([FromBody] UserCreateRequest model, CancellationToken cancellationToken)
|
||||
=> userAuthority.InvokeTransformable<User, UserResponse>(this, authority => authority.Create(model, cancellationToken));
|
||||
=> userAuthority.InvokeTransformable<User, UserResponse>(this, authority => authority.Create(model, null, cancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Update a <see cref="User"/>.
|
||||
|
||||
@@ -72,53 +72,7 @@ namespace Tgstation.Server.Host.GraphQL.Mutations
|
||||
})
|
||||
.ToList(),
|
||||
},
|
||||
cancellationToken));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a system user specifying a personal <see cref="PermissionSet"/>.
|
||||
/// </summary>
|
||||
/// <param name="systemIdentifier">The <see cref="User.SystemIdentifier"/> of the <see cref="User"/>.</param>
|
||||
/// <param name="enabled">If the <see cref="User"/> is <see cref="User.Enabled"/>.</param>
|
||||
/// <param name="oAuthConnections">The <see cref="OAuthConnection"/>s for the user.</param>
|
||||
/// <param name="permissionSet">The owned <see cref="PermissionSet"/> of the user.</param>
|
||||
/// <param name="userAuthority">The <see cref="IGraphQLAuthorityInvoker{TAuthority}"/> for the <see cref="IUserAuthority"/>.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>The created <see cref="User"/>.</returns>
|
||||
[TgsGraphQLAuthorize<IUserAuthority>(nameof(IUserAuthority.Create))]
|
||||
[Error(typeof(ErrorMessageException))]
|
||||
public ValueTask<User> CreateUserBySystemIDAndPermissionSet(
|
||||
string systemIdentifier,
|
||||
bool? enabled,
|
||||
IEnumerable<OAuthConnection>? oAuthConnections,
|
||||
PermissionSetInput permissionSet,
|
||||
[Service] IGraphQLAuthorityInvoker<IUserAuthority> userAuthority,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(systemIdentifier);
|
||||
ArgumentNullException.ThrowIfNull(userAuthority);
|
||||
|
||||
return userAuthority.InvokeTransformable<Models.User, User, UserGraphQLTransformer>(
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a TGS user authenticated with one or mor <see cref="OAuthConnection"/>s specifying a personal <see cref="PermissionSet"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">The <see cref="NamedEntity.Name"/> of the <see cref="User"/>.</param>
|
||||
/// <param name="oAuthConnections">The <see cref="OAuthConnection"/>s for the user.</param>
|
||||
/// <param name="enabled">If the <see cref="User"/> is <see cref="User.Enabled"/>.</param>
|
||||
/// <param name="permissionSet">The owned <see cref="PermissionSet"/> of the user.</param>
|
||||
/// <param name="userAuthority">The <see cref="IGraphQLAuthorityInvoker{TAuthority}"/> for the <see cref="IUserAuthority"/>.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>The created <see cref="User"/>.</returns>
|
||||
[TgsGraphQLAuthorize<IUserAuthority>(nameof(IUserAuthority.Create))]
|
||||
[Error(typeof(ErrorMessageException))]
|
||||
public ValueTask<User> CreateUserByOAuthAndPermissionSet(
|
||||
string name,
|
||||
IEnumerable<OAuthConnection> oAuthConnections,
|
||||
bool? enabled,
|
||||
PermissionSetInput? permissionSet,
|
||||
[Service] IGraphQLAuthorityInvoker<IUserAuthority> userAuthority,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(name);
|
||||
ArgumentNullException.ThrowIfNull(oAuthConnections);
|
||||
ArgumentNullException.ThrowIfNull(userAuthority);
|
||||
|
||||
return userAuthority.InvokeTransformable<Models.User, User, UserGraphQLTransformer>(
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a TGS user specifying the <see cref="UserGroup"/> they will belong to.
|
||||
/// </summary>
|
||||
/// <param name="name">The <see cref="NamedEntity.Name"/> of the <see cref="User"/>.</param>
|
||||
/// <param name="oAuthConnections">The <see cref="OAuthConnection"/>s for the user.</param>
|
||||
/// <param name="groupId">The <see cref="Entity.Id"/> of the <see cref="UserGroup"/> the <see cref="User"/> will belong to.</param>
|
||||
/// <param name="enabled">If the <see cref="User"/> is <see cref="User.Enabled"/>.</param>
|
||||
/// <param name="userAuthority">The <see cref="IGraphQLAuthorityInvoker{TAuthority}"/> for the <see cref="IUserAuthority"/>.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>The created <see cref="User"/>.</returns>
|
||||
[TgsGraphQLAuthorize<IUserAuthority>(nameof(IUserAuthority.Create))]
|
||||
[Error(typeof(ErrorMessageException))]
|
||||
public ValueTask<User> CreateUserByOAuthAndGroup(
|
||||
string name,
|
||||
IEnumerable<OAuthConnection> oAuthConnections,
|
||||
[ID(nameof(UserGroup))] long groupId,
|
||||
bool? enabled,
|
||||
[Service] IGraphQLAuthorityInvoker<IUserAuthority> userAuthority,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(name);
|
||||
ArgumentNullException.ThrowIfNull(oAuthConnections);
|
||||
ArgumentNullException.ThrowIfNull(userAuthority);
|
||||
|
||||
return userAuthority.InvokeTransformable<Models.User, User, UserGraphQLTransformer>(
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a system user specifying a personal <see cref="PermissionSet"/>.
|
||||
/// </summary>
|
||||
/// <param name="systemIdentifier">The <see cref="User.SystemIdentifier"/> of the <see cref="User"/>.</param>
|
||||
/// <param name="enabled">If the <see cref="User"/> is <see cref="User.Enabled"/>.</param>
|
||||
/// <param name="oAuthConnections">The <see cref="OAuthConnection"/>s for the user.</param>
|
||||
/// <param name="permissionSet">The owned <see cref="PermissionSet"/> of the user.</param>
|
||||
/// <param name="userAuthority">The <see cref="IGraphQLAuthorityInvoker{TAuthority}"/> for the <see cref="IUserAuthority"/>.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>The created <see cref="User"/>.</returns>
|
||||
[TgsGraphQLAuthorize<IUserAuthority>(nameof(IUserAuthority.Create))]
|
||||
[Error(typeof(ErrorMessageException))]
|
||||
public ValueTask<User> CreateUserBySystemIDAndPermissionSet(
|
||||
string systemIdentifier,
|
||||
bool? enabled,
|
||||
IEnumerable<OAuthConnection>? oAuthConnections,
|
||||
PermissionSetInput permissionSet,
|
||||
[Service] IGraphQLAuthorityInvoker<IUserAuthority> userAuthority,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(systemIdentifier);
|
||||
ArgumentNullException.ThrowIfNull(userAuthority);
|
||||
|
||||
return userAuthority.InvokeTransformable<Models.User, User, UserGraphQLTransformer>(
|
||||
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
|
||||
/// </summary>
|
||||
/// <param name="systemIdentifier">The <see cref="User.SystemIdentifier"/> of the <see cref="User"/>.</param>
|
||||
/// <param name="enabled">If the <see cref="User"/> is <see cref="User.Enabled"/>.</param>
|
||||
/// <param name="oAuthConnections">The <see cref="OAuthConnection"/>s for the user.</param>
|
||||
/// <param name="groupId">The <see cref="Entity.Id"/> of the <see cref="UserGroup"/> the <see cref="User"/> will belong to.</param>
|
||||
/// <param name="oAuthConnections">The <see cref="OAuthConnection"/>s for the user.</param>
|
||||
/// <param name="userAuthority">The <see cref="IGraphQLAuthorityInvoker{TAuthority}"/> for the <see cref="IUserAuthority"/>.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>The created <see cref="User"/>.</returns>
|
||||
@@ -185,8 +285,8 @@ namespace Tgstation.Server.Host.GraphQL.Mutations
|
||||
public ValueTask<User> CreateUserBySystemIDAndGroup(
|
||||
string systemIdentifier,
|
||||
bool? enabled,
|
||||
IEnumerable<OAuthConnection>? oAuthConnections,
|
||||
[ID(nameof(UserGroup))] long groupId,
|
||||
IEnumerable<OAuthConnection>? oAuthConnections,
|
||||
[Service] IGraphQLAuthorityInvoker<IUserAuthority> userAuthority,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -211,6 +311,7 @@ namespace Tgstation.Server.Host.GraphQL.Mutations
|
||||
})
|
||||
.ToList(),
|
||||
},
|
||||
false,
|
||||
cancellationToken));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user