elsa-core/test/unit/Elsa.Identity.UnitTests/Services/UserManagerTests.cs
Sipke Schoorstra 83af7309f2
fix(identity): stop returning password hashes and salts from user creation (#8041)
The POST /identity/users response serialized the plain-text password
(including one the caller supplied), the password hash, and the salt.
The response now carries only id, name, roles, tenantId and a nullable
generatedPassword that is populated once, and only when Core generated
the password because none was supplied.

- CreateUserResult gains IsPasswordGenerated so the endpoint can tell a
  generated password from a supplied one without re-deriving it.
- Response.FromResult centralises the mapping and omits credential
  material.
- Expose Elsa.Identity internals to Elsa.Identity.UnitTests and add
  contract tests covering the response shape, the no-echo rule, the
  serialized JSON, and UserManager's generated-password flag.

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 00:26:47 +02:00

56 lines
1.9 KiB
C#

using Elsa.Identity.Contracts;
using Elsa.Identity.Entities;
using Elsa.Identity.Services;
using Elsa.Testing.Shared.Multitenancy;
using Elsa.Workflows;
using NSubstitute;
namespace Elsa.Identity.UnitTests.Services;
public class UserManagerTests
{
private readonly ISecretGenerator _secretGenerator = Substitute.For<ISecretGenerator>();
private readonly IUserStore _userStore = Substitute.For<IUserStore>();
private readonly UserManager _manager;
public UserManagerTests()
{
var identityGenerator = Substitute.For<IIdentityGenerator>();
identityGenerator.GenerateId().Returns("user-1");
_secretGenerator.Generate(Arg.Any<int>()).Returns("generated-secret");
_manager = new(identityGenerator, _secretGenerator, new DefaultSecretHasher(), _userStore, new TestTenantAccessor("tenant-a"));
}
[Fact]
public async Task CreateUserAsyncWithSuppliedPasswordMarksItAsNotGenerated()
{
var result = await _manager.CreateUserAsync("alice", " supplied-secret ", ["admin"]);
Assert.False(result.IsPasswordGenerated);
Assert.Equal("supplied-secret", result.Password);
_secretGenerator.DidNotReceive().Generate(Arg.Any<int>());
AssertStoredCredentials(result.User);
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public async Task CreateUserAsyncWithoutPasswordGeneratesOneAndMarksIt(string? password)
{
var result = await _manager.CreateUserAsync("alice", password);
Assert.True(result.IsPasswordGenerated);
Assert.Equal("generated-secret", result.Password);
AssertStoredCredentials(result.User);
}
private static void AssertStoredCredentials(User user)
{
Assert.Equal("user-1", user.Id);
Assert.Equal("tenant-a", user.TenantId);
Assert.False(string.IsNullOrEmpty(user.HashedPassword));
Assert.False(string.IsNullOrEmpty(user.HashedPasswordSalt));
}
}