diff --git a/src/administration/Administration.Service/BusinessLogic/UserBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/UserBusinessLogic.cs index 002ad52dc5..a611a29dfb 100644 --- a/src/administration/Administration.Service/BusinessLogic/UserBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/UserBusinessLogic.cs @@ -158,7 +158,31 @@ public async Task CreateOwnCompanyIdpUserAsync(Guid identityProviderId, Us userCreationInfo.UserId, UserStatusId.ACTIVE, true - ), 1).ToAsyncEnumerable()) + ), 1).ToAsyncEnumerable(), + creationData => + { + var mailParameters = ImmutableDictionary.CreateBuilder(); + mailParameters.AddRange([ + new("companyName", displayName), + new("nameCreatedBy", nameCreatedBy), + new("url", _settings.Portal.BasePortalAddress), + new("idpAlias", displayName), + ]); + + IEnumerable mailTemplates = companyNameIdpAliasData.IsSharedIdp + ? ["NewUserTemplate", "NewUserPasswordTemplate"] + : ["NewUserExternalIdpTemplate"]; + + if (companyNameIdpAliasData.IsSharedIdp) + { + mailParameters.Add(new("password", creationData.Password ?? throw new UnexpectedConditionException("password should never be null here"))); + } + + foreach (var template in mailTemplates) + { + mailingProcessCreation.CreateMailProcess(creationData.UserCreationInfo.Email, template, mailParameters.ToImmutable()); + } + }) .FirstAsync() .ConfigureAwait(false); @@ -166,30 +190,6 @@ public async Task CreateOwnCompanyIdpUserAsync(Guid identityProviderId, Us { throw result.Error; } - - var mailParameters = new Dictionary - { - { "companyName", displayName }, - { "nameCreatedBy", nameCreatedBy }, - { "url", _settings.Portal.BasePortalAddress }, - { "idpAlias", displayName }, - }; - - var mailTemplates = companyNameIdpAliasData.IsSharedIdp - ? new[] { "NewUserTemplate", "NewUserPasswordTemplate" } - : new[] { "NewUserExternalIdpTemplate" }; - - if (companyNameIdpAliasData.IsSharedIdp) - { - mailParameters["password"] = result.Password; - } - - foreach (var template in mailTemplates) - { - mailingProcessCreation.CreateMailProcess(userCreationInfo.Email, template, mailParameters.ToImmutableDictionary()); - } - - await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); return result.CompanyUserId; } diff --git a/src/administration/Administration.Service/BusinessLogic/UserUploadBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/UserUploadBusinessLogic.cs index 63513c11f7..eb32fdfe2a 100644 --- a/src/administration/Administration.Service/BusinessLogic/UserUploadBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/UserUploadBusinessLogic.cs @@ -30,7 +30,6 @@ using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library.Models; using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library.Service; using System.Collections.Immutable; -using System.Runtime.CompilerServices; namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLogic; @@ -78,6 +77,10 @@ private async ValueTask UploadOwnCompanyIdpUsersInternalAsync var validRoleData = new List(); + var displayName = companyNameIdpAliasData.IsSharedIdp + ? null + : await _userProvisioningService.GetIdentityProviderDisplayName(companyNameIdpAliasData.IdpAlias).ConfigureAwait(ConfigureAwaitOptions.None) ?? companyNameIdpAliasData.IdpAlias; + var (numCreated, numLines, errors) = await CsvParser.ProcessCsvAsync( stream, line => @@ -99,17 +102,23 @@ await GetUserRoleDatas(parsed.Roles, validRoleData, _identityData.CompanyId).Con UserStatusId.ACTIVE, true); }, - lines => (companyNameIdpAliasData.IsSharedIdp - ? _userProvisioningService + lines => + _userProvisioningService .CreateOwnCompanyIdpUsersAsync( companyNameIdpAliasData, lines, + creationData => + { + if (companyNameIdpAliasData.IsSharedIdp) + return; + var mailParameters = ImmutableDictionary.CreateRange([ + new("nameCreatedBy", nameCreatedBy), + new("url", _settings.Portal.BasePortalAddress), + new("idpAlias", displayName ?? throw new UnexpectedConditionException("displayname should never be null here")) + ]); + _mailingProcessCreation.CreateMailProcess(creationData.UserCreationInfo.Email, "NewUserExternalIdpTemplate", mailParameters); + }, cancellationToken) - : CreateOwnCompanyIdpUsersWithEmailAsync( - nameCreatedBy, - companyNameIdpAliasData, - lines, - cancellationToken)) .Select(x => (x.CompanyUserId != Guid.Empty, x.Error)), cancellationToken).ConfigureAwait(false); @@ -120,53 +129,6 @@ await GetUserRoleDatas(parsed.Roles, validRoleData, _identityData.CompanyId).Con errors.Select(error => CreateUserCreationError(error.Line, error.Error))); } - private async IAsyncEnumerable<(Guid CompanyUserId, string UserName, string? Password, Exception? Error)> CreateOwnCompanyIdpUsersWithEmailAsync(string nameCreatedBy, CompanyNameIdpAliasData companyNameIdpAliasData, IAsyncEnumerable userCreationInfos, [EnumeratorCancellation] CancellationToken cancellationToken) - { - if (companyNameIdpAliasData.IsSharedIdp) - { - throw new UnexpectedConditionException($"unexpected call to {nameof(CreateOwnCompanyIdpUsersWithEmailAsync)} for shared-idp"); - } - - UserCreationRoleDataIdpInfo? userCreationInfo = null; - - var displayName = await _userProvisioningService.GetIdentityProviderDisplayName(companyNameIdpAliasData.IdpAlias).ConfigureAwait(ConfigureAwaitOptions.None) ?? companyNameIdpAliasData.IdpAlias; - - await foreach (var result in - _userProvisioningService - .CreateOwnCompanyIdpUsersAsync( - companyNameIdpAliasData, - userCreationInfos - .Select(info => - { - userCreationInfo = info; - return info; - }), - cancellationToken) - .WithCancellation(cancellationToken) - .ConfigureAwait(false)) - { - if (userCreationInfo == null) - { - throw new UnexpectedConditionException("userCreationInfo should never be null here"); - } - if (result.Error != null || result.CompanyUserId == Guid.Empty || string.IsNullOrEmpty(userCreationInfo.Email)) - { - yield return result; - continue; - } - - var mailParameters = ImmutableDictionary.CreateRange(new[] - { - KeyValuePair.Create("nameCreatedBy", nameCreatedBy), - KeyValuePair.Create("url", _settings.Portal.BasePortalAddress), - KeyValuePair.Create("idpAlias", displayName) - }); - _mailingProcessCreation.CreateMailProcess(userCreationInfo.Email, "NewUserExternalIdpTemplate", mailParameters); - - yield return (result.CompanyUserId, result.UserName, result.Password, null); - } - } - private static void ValidateUserCreationRoles(IEnumerable roles) { if (!roles.Any()) @@ -199,7 +161,8 @@ private async ValueTask UploadOwnCompanySharedIdpUsersInterna { using var stream = document.OpenReadStream(); - var (companyNameIdpAliasData, _) = await _userProvisioningService.GetCompanyNameSharedIdpAliasData(_identityData.IdentityId).ConfigureAwait(ConfigureAwaitOptions.None); + var (companyNameIdpAliasData, nameCreatedBy) = await _userProvisioningService.GetCompanyNameSharedIdpAliasData(_identityData.IdentityId).ConfigureAwait(ConfigureAwaitOptions.None); + var displayName = await _userProvisioningService.GetIdentityProviderDisplayName(companyNameIdpAliasData.IdpAlias).ConfigureAwait(ConfigureAwaitOptions.None) ?? companyNameIdpAliasData.IdpAlias; var validRoleData = new List(); @@ -229,6 +192,18 @@ await GetUserRoleDatas(parsed.Roles, validRoleData, _identityData.CompanyId).Con .CreateOwnCompanyIdpUsersAsync( companyNameIdpAliasData, lines, + creationData => + { + var mailParameters = ImmutableDictionary.CreateRange([ + new("password", creationData.Password ?? ""), + new("companyName", displayName), + new("nameCreatedBy", nameCreatedBy), + new("url", _settings.Portal.BasePortalAddress), + new("passwordResendUrl", _settings.Portal.PasswordResendAddress), + ]); + _mailingProcessCreation.CreateMailProcess(creationData.UserCreationInfo.Email, "NewUserTemplate", mailParameters); + _mailingProcessCreation.CreateMailProcess(creationData.UserCreationInfo.Email, "NewUserPasswordTemplate", mailParameters); + }, cancellationToken) .Select(x => (x.CompanyUserId != Guid.Empty, x.Error)), cancellationToken).ConfigureAwait(false); diff --git a/src/processes/Invitation.Executor/InvitationProcessService.cs b/src/processes/Invitation.Executor/InvitationProcessService.cs index 4a88d14e0f..241ad5561f 100644 --- a/src/processes/Invitation.Executor/InvitationProcessService.cs +++ b/src/processes/Invitation.Executor/InvitationProcessService.cs @@ -340,7 +340,7 @@ await _idpManagement true )}.ToAsyncEnumerable(); - var (companyUserId, _, password, error) = await _userProvisioningService.CreateOwnCompanyIdpUsersAsync(companyNameIdpAliasData, userCreationInfoIdps, cancellationToken).SingleAsync(cancellationToken).ConfigureAwait(false); + var (companyUserId, _, password, error) = await _userProvisioningService.CreateOwnCompanyIdpUsersAsync(companyNameIdpAliasData, userCreationInfoIdps, cancellationToken: cancellationToken).SingleAsync(cancellationToken).ConfigureAwait(false); if (error is not null) { diff --git a/src/provisioning/Provisioning.Library/Models/UserCreationRoleDataIdpInfo.cs b/src/provisioning/Provisioning.Library/Models/UserCreationRoleDataIdpInfo.cs index 455e22e558..3919e89730 100644 --- a/src/provisioning/Provisioning.Library/Models/UserCreationRoleDataIdpInfo.cs +++ b/src/provisioning/Provisioning.Library/Models/UserCreationRoleDataIdpInfo.cs @@ -33,3 +33,8 @@ public record UserCreationRoleDataIdpInfo( UserStatusId UserStatusId, bool Enabled ); + +public record UserCreationCallbackData( + UserCreationRoleDataIdpInfo UserCreationInfo, + string? Password +); diff --git a/src/provisioning/Provisioning.Library/Service/IUserProvisioningService.cs b/src/provisioning/Provisioning.Library/Service/IUserProvisioningService.cs index a5fc5aa8ca..b40bbcff84 100644 --- a/src/provisioning/Provisioning.Library/Service/IUserProvisioningService.cs +++ b/src/provisioning/Provisioning.Library/Service/IUserProvisioningService.cs @@ -27,7 +27,7 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library.Service; public interface IUserProvisioningService { - IAsyncEnumerable<(Guid CompanyUserId, string UserName, string? Password, Exception? Error)> CreateOwnCompanyIdpUsersAsync(CompanyNameIdpAliasData companyNameIdpAliasData, IAsyncEnumerable userCreationInfos, CancellationToken cancellationToken = default); + IAsyncEnumerable<(Guid CompanyUserId, string UserName, string? Password, Exception? Error)> CreateOwnCompanyIdpUsersAsync(CompanyNameIdpAliasData companyNameIdpAliasData, IAsyncEnumerable userCreationInfos, Action? onSuccessfulCreation = null, CancellationToken cancellationToken = default); Task HandleCentralKeycloakCreation(UserCreationRoleDataIdpInfo user, Guid companyUserId, string companyName, string? businessPartnerNumber, Identity? identity, IEnumerable identityProviderLinks, IUserRepository userRepository, IUserRolesRepository userRolesRepository); Task<(CompanyNameIdpAliasData IdpAliasData, string NameCreatedBy)> GetCompanyNameIdpAliasData(Guid identityProviderId, Guid companyUserId); Task<(CompanyNameIdpAliasData IdpAliasData, string NameCreatedBy)> GetCompanyNameSharedIdpAliasData(Guid companyUserId, Guid? applicationId = null); diff --git a/src/provisioning/Provisioning.Library/Service/UserProvisioningService.cs b/src/provisioning/Provisioning.Library/Service/UserProvisioningService.cs index 9f6740e195..dcccba985c 100644 --- a/src/provisioning/Provisioning.Library/Service/UserProvisioningService.cs +++ b/src/provisioning/Provisioning.Library/Service/UserProvisioningService.cs @@ -53,6 +53,7 @@ public UserProvisioningService(IProvisioningManager provisioningManager, IPortal public async IAsyncEnumerable<(Guid CompanyUserId, string UserName, string? Password, Exception? Error)> CreateOwnCompanyIdpUsersAsync( CompanyNameIdpAliasData companyNameIdpAliasData, IAsyncEnumerable userCreationInfos, + Action? onSuccessfulCreation = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { var userRepository = _portalRepositories.GetInstance(); @@ -76,6 +77,7 @@ public UserProvisioningService(IProvisioningManager provisioningManager, IPortal var providerUserId = await CreateSharedIdpUserOrReturnUserId(user, alias, nextPassword, isSharedIdp).ConfigureAwait(ConfigureAwaitOptions.None); await HandleCentralKeycloakCreation(user, companyUserId, companyName, businessPartnerNumber, identity, Enumerable.Repeat(new IdentityProviderLink(alias, providerUserId, user.UserName), 1), userRepository, userRolesRepository).ConfigureAwait(ConfigureAwaitOptions.None); + onSuccessfulCreation?.Invoke(new(user, nextPassword)); } catch (Exception e) when (e is not OperationCanceledException) { diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/UserBusinessLogicTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/UserBusinessLogicTests.cs index 265b4c6953..c8880aa92b 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/UserBusinessLogicTests.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/UserBusinessLogicTests.cs @@ -76,7 +76,7 @@ public class UserBusinessLogicTests private readonly IIdentityData _identity; private readonly ICollection _addedRoles = new HashSet(); private readonly ICollection _removedRoles = new HashSet(); - private readonly Func _processLine; + private readonly Func?, (Guid CompanyUserId, string UserName, string? Password, Exception? Error)> _processLine; private readonly Func _companyUserSelectFunction; private readonly Exception _error; private readonly UserSettings _settings; @@ -122,7 +122,7 @@ public UserBusinessLogicTests() _createdCentralCompanyId = Guid.NewGuid(); _displayName = _fixture.Create(); - _processLine = A.Fake>(); + _processLine = A.Fake?, (Guid CompanyUserId, string UserName, string? Password, Exception? Error)>>(); _companyUserSelectFunction = A.Fake>(); _identity = A.Fake(); @@ -276,12 +276,15 @@ public async Task TestUserCreationCreationError() CreateUserCreationInfo() }; - A.CallTo(() => _processLine(A.That.Matches(u => - u.FirstName == userCreationInfo.firstName && - u.LastName == userCreationInfo.lastName && - u.Email == userCreationInfo.eMail - ))).ReturnsLazily( - (UserCreationRoleDataIdpInfo creationInfo) => _fixture.Build<(Guid CompanyUserId, string UserName, string? Password, Exception? Error)>() + A.CallTo(() => _processLine( + A._, + A.That.Matches(u => + u.FirstName == userCreationInfo.firstName && + u.LastName == userCreationInfo.lastName && + u.Email == userCreationInfo.eMail), + A>._) + ).ReturnsLazily((CompanyNameIdpAliasData _, UserCreationRoleDataIdpInfo creationInfo, Action? _) => + _fixture.Build<(Guid CompanyUserId, string UserName, string? Password, Exception? Error)>() .With(x => x.UserName, creationInfo.UserName) .With(x => x.Error, error) .Create()); @@ -299,11 +302,14 @@ public async Task TestUserCreationCreationError() var result = await sut.CreateOwnCompanyUsersAsync(userList).ToListAsync(); - A.CallTo(() => _processLine(A.That.Matches(u => - u.FirstName == userCreationInfo.firstName && - u.LastName == userCreationInfo.lastName && - u.Email == userCreationInfo.eMail - ))).MustHaveHappenedOnceExactly(); + A.CallTo(() => _processLine( + A._, + A.That.Matches(u => + u.FirstName == userCreationInfo.firstName && + u.LastName == userCreationInfo.lastName && + u.Email == userCreationInfo.eMail), + A>._) + ).MustHaveHappenedOnceExactly(); A.CallTo(() => _mockLogger.Log(A.That.IsEqualTo(LogLevel.Error), A._, A._)).MustHaveHappenedOnceExactly(); @@ -331,11 +337,14 @@ public async Task TestUserCreationCreationThrows() CreateUserCreationInfo() }; - A.CallTo(() => _processLine(A.That.Matches(u => - u.FirstName == userCreationInfo.firstName && - u.LastName == userCreationInfo.lastName && - u.Email == userCreationInfo.eMail - ))).Throws(() => expected); + A.CallTo(() => _processLine( + A._, + A.That.Matches(u => + u.FirstName == userCreationInfo.firstName && + u.LastName == userCreationInfo.lastName && + u.Email == userCreationInfo.eMail), + A>._) + ).Throws(() => expected); var sut = new UserBusinessLogic( null!, @@ -352,11 +361,14 @@ public async Task TestUserCreationCreationThrows() var error = await Assert.ThrowsAsync(Act); - A.CallTo(() => _processLine(A.That.Matches(u => + A.CallTo(() => _processLine( + A._, + A.That.Matches(u => u.FirstName == userCreationInfo.firstName && u.LastName == userCreationInfo.lastName && - u.Email == userCreationInfo.eMail - ))).MustHaveHappenedOnceExactly(); + u.Email == userCreationInfo.eMail), + A>._) + ).MustHaveHappenedOnceExactly(); A.CallTo(() => _mockLogger.Log(A.That.IsEqualTo(LogLevel.Error), A._, A._)).MustNotHaveHappened(); A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, "NewUserTemplate", A>._)) @@ -433,8 +445,12 @@ public async Task TestCreateOwnCompanyIdpUserAsyncError() var userCreationInfoIdp = CreateUserCreationInfoIdp(); - A.CallTo(() => _processLine(A.That.Matches(u => u.FirstName == userCreationInfoIdp.FirstName))).ReturnsLazily( - (UserCreationRoleDataIdpInfo creationInfo) => _fixture.Build<(Guid CompanyUserId, string UserName, string? Password, Exception? Error)>() + A.CallTo(() => _processLine( + A._, + A.That.Matches(u => u.FirstName == userCreationInfoIdp.FirstName), + A>._) + ).ReturnsLazily((CompanyNameIdpAliasData _, UserCreationRoleDataIdpInfo creationInfo, Action? onSuccess) => + _fixture.Build<(Guid CompanyUserId, string UserName, string? Password, Exception? Error)>() .With(x => x.UserName, creationInfo.UserName) .With(x => x.Error, _error) .Create()); @@ -465,7 +481,11 @@ public async Task TestCreateOwnCompanyIdpUserAsyncThrows() var userCreationInfoIdp = CreateUserCreationInfoIdp(); - A.CallTo(() => _processLine(A.That.Matches(u => u.FirstName == userCreationInfoIdp.FirstName))).Throws(_error); + A.CallTo(() => _processLine( + A._, + A.That.Matches(u => u.FirstName == userCreationInfoIdp.FirstName), + A>._) + ).Throws(_error); var sut = new UserBusinessLogic( null!, @@ -1153,7 +1173,7 @@ public async Task GetOwnCompanyAppUsersAsync_ReturnsExpectedResult() A.CallTo(() => _identity.IdentityId).Returns(userId); A.CallTo(() => _userRepository.GetOwnCompanyAppUsersPaginationSourceAsync(A._, A._, A>._, A>._, A._)) - .Returns((skip, take) => Task.FromResult(new Pagination.Source(companyUsers.Count(), companyUsers.Skip(skip).Take(take)))); + .Returns((skip, take) => Task.FromResult?>(new Pagination.Source(companyUsers.Count(), companyUsers.Skip(skip).Take(take)))); var sut = new UserBusinessLogic(null!, null!, null!, _portalRepositories, _identityService, null!, null!, null!, A.Fake>()); // Act @@ -1178,7 +1198,7 @@ public async Task GetOwnCompanyAppUsersAsync_SecondPage_ReturnsExpectedResult() A.CallTo(() => _identity.IdentityId).Returns(userId); A.CallTo(() => _userRepository.GetOwnCompanyAppUsersPaginationSourceAsync(A._, A._, A>._, A>._, A._)) - .Returns((skip, take) => Task.FromResult(new Pagination.Source(companyUsers.Count(), companyUsers.Skip(skip).Take(take)))); + .Returns((skip, take) => Task.FromResult?>(new Pagination.Source(companyUsers.Count(), companyUsers.Skip(skip).Take(take)))); var sut = new UserBusinessLogic(null!, null!, null!, _portalRepositories, _identityService, null!, null!, null!, A.Fake>()); // Act @@ -1540,17 +1560,23 @@ private void SetupFakesForUserCreation(bool isBulkUserCreation) A.CallTo(() => _userProvisioningService.GetCompanyNameIdpAliasData(A._, A._)).Returns((_fixture.Create(), _fixture.Create())); } - A.CallTo(() => _userProvisioningService.CreateOwnCompanyIdpUsersAsync(A._, A>._, A._)) - .ReturnsLazily((CompanyNameIdpAliasData _, IAsyncEnumerable userCreationInfos, CancellationToken _) => - userCreationInfos.Select(userCreationInfo => _processLine(userCreationInfo))); + A.CallTo(() => _userProvisioningService.CreateOwnCompanyIdpUsersAsync(A._, A>._, A>._, A._)) + .ReturnsLazily((CompanyNameIdpAliasData idpAliasData, IAsyncEnumerable userCreationInfos, Action? onSuccess, CancellationToken _) => + userCreationInfos.Select(userCreationInfo => _processLine(idpAliasData, userCreationInfo, onSuccess))); A.CallTo(() => _userProvisioningService.GetIdentityProviderDisplayName(A._)).Returns(_displayName); - A.CallTo(() => _processLine(A._)).ReturnsLazily( - (UserCreationRoleDataIdpInfo creationInfo) => _fixture.Build<(Guid CompanyUserId, string UserName, string? Password, Exception? Error)>() - .With(x => x.UserName, creationInfo.UserName) - .With(x => x.Error, default(Exception?)) - .Create()); + A.CallTo(() => _processLine(A._, A._, A>._)) + .ReturnsLazily((CompanyNameIdpAliasData aliasData, UserCreationRoleDataIdpInfo creationInfo, Action? onSucess) => + { + var password = aliasData.IsSharedIdp ? _fixture.Create() : null; + onSucess?.Invoke(new(creationInfo, password)); + return _fixture.Build<(Guid CompanyUserId, string UserName, string? Password, Exception? Error)>() + .With(x => x.UserName, creationInfo.UserName) + .With(x => x.Password, password) + .With(x => x.Error, default(Exception?)) + .Create(); + }); } private void SetupFakesForUserDeletion() diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/UserUploadBusinessLogicTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/UserUploadBusinessLogicTests.cs index 778e95aa4d..fdd434b18a 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/UserUploadBusinessLogicTests.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/UserUploadBusinessLogicTests.cs @@ -46,7 +46,7 @@ public class UserUploadBusinessLogicTests private readonly IMailingProcessCreation _mailingProcessCreation; private readonly UserSettings _settings; private readonly Encoding _encoding; - private readonly Func _processLine; + private readonly Func?, (Guid CompanyUserId, string UserName, string? Password, Exception? Error)> _processLine; private readonly Exception _error; private readonly Random _random; private readonly IIdentityService _identityService; @@ -82,7 +82,7 @@ public UserUploadBusinessLogicTests() A.CallTo(() => _errorMessageService.GetMessage(typeof(ProvisioningServiceErrors), A._)) .ReturnsLazily((Type type, int code) => $"type: {type.Name} code: {Enum.GetName(type, code)} userName: {{userName}} realm: {{realm}}"); - _processLine = A.Fake>(); + _processLine = A.Fake?, (Guid CompanyUserId, string UserName, string? Password, Exception? Error)>>(); _error = _fixture.Create(); } @@ -92,7 +92,7 @@ public UserUploadBusinessLogicTests() [Fact] public async Task TestSetup() { - SetupFakes(new[] { HeaderLine() }); + SetupFakes([HeaderLine()]); var sut = new UserUploadBusinessLogic(_userProvisioningService, _mailingProcessCreation, _identityService, _errorMessageService, _options); @@ -111,14 +111,14 @@ public async Task TestSetup() [Fact] public async Task TestUserCreationAllSuccess() { - SetupFakes(new[] { + SetupFakes([ HeaderLine(), NextLine(), NextLine(), NextLine(), NextLine(), NextLine() - }); + ]); var sut = new UserUploadBusinessLogic(_userProvisioningService, _mailingProcessCreation, _identityService, _errorMessageService, _options); @@ -129,7 +129,7 @@ public async Task TestUserCreationAllSuccess() result.Error.Should().Be(0); result.Total.Should().Be(5); result.Errors.Should().BeEmpty(); - A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, A._, A>._)) + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, "NewUserExternalIdpTemplate", A>._)) .MustHaveHappened(5, Times.Exactly); } @@ -138,14 +138,14 @@ public async Task TestUserCreationHeaderParsingThrows() { var invalidHeader = _fixture.Create(); - SetupFakes(new[] { + SetupFakes([ invalidHeader, NextLine(), NextLine(), NextLine(), NextLine(), NextLine() - }); + ]); var sut = new UserUploadBusinessLogic(_userProvisioningService, _mailingProcessCreation, _identityService, _errorMessageService, _options); @@ -153,6 +153,8 @@ public async Task TestUserCreationHeaderParsingThrows() var error = await Assert.ThrowsAsync(Act); error.Message.Should().Be($"invalid format: expected 'FirstName', got '{invalidHeader}' (Parameter 'document')"); + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, A._, A>._)) + .MustNotHaveHappened(); } [Fact] @@ -161,18 +163,18 @@ public async Task TestUserCreationCreationError() var creationInfo = _fixture.Create(); var detailError = ConflictException.Create(ProvisioningServiceErrors.USER_CREATION_CONFLICT, new ErrorParameter[] { new("userName", "foo"), new("realm", "bar") }); - SetupFakes(new[] { + SetupFakes([ HeaderLine(), NextLine(), NextLine(), NextLine(creationInfo), NextLine(), NextLine() - }); + ]); - A.CallTo(() => _processLine(A.That.Matches(info => CreationInfoMatches(info, creationInfo)))) + A.CallTo(() => _processLine(A._, A.That.Matches(info => CreationInfoMatches(info, creationInfo)), A>._)) .ReturnsLazily( - (UserCreationRoleDataIdpInfo creationInfo) => _fixture.Build<(Guid CompanyUserId, string UserName, string? Password, Exception? Error)>() + (CompanyNameIdpAliasData _, UserCreationRoleDataIdpInfo creationInfo, Action _) => _fixture.Build<(Guid CompanyUserId, string UserName, string? Password, Exception? Error)>() .With(x => x.CompanyUserId, Guid.Empty) .With(x => x.UserName, creationInfo.UserName) .With(x => x.Error, detailError) @@ -182,7 +184,7 @@ public async Task TestUserCreationCreationError() var result = await sut.UploadOwnCompanyIdpUsersAsync(_identityProviderId, _document, CancellationToken.None); - A.CallTo(() => _processLine(A.That.Matches(info => CreationInfoMatches(info, creationInfo)))).MustHaveHappened(); + A.CallTo(() => _processLine(A._, A.That.Matches(info => CreationInfoMatches(info, creationInfo)), A>._)).MustHaveHappened(); result.Should().NotBeNull(); result.Created.Should().Be(4); @@ -200,7 +202,7 @@ public async Task TestUserCreationCreationError() x.Parameters.Count() == 2 && x.Parameters.First(p => p.Name == "userName").Value == "foo" && x.Parameters.First(p => p.Name == "realm").Value == "bar"); - A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, A._, A>._)) + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, "NewUserExternalIdpTemplate", A>._)) .MustHaveHappened(4, Times.Exactly); } @@ -211,42 +213,42 @@ public async Task TestUserCreationCreationNoRolesError() .With(x => x.RoleDatas, Enumerable.Empty()) .Create(); - SetupFakes(new[] { + SetupFakes([ HeaderLine(), NextLine(), NextLine(), NextLine(creationInfo), NextLine(), NextLine() - }); + ]); var sut = new UserUploadBusinessLogic(_userProvisioningService, _mailingProcessCreation, _identityService, _errorMessageService, _options); var result = await sut.UploadOwnCompanyIdpUsersAsync(_identityProviderId, _document, CancellationToken.None); - A.CallTo(() => _processLine(A.That.Matches(info => CreationInfoMatches(info, creationInfo)))).MustNotHaveHappened(); - A.CallTo(() => _processLine(A.That.Not.Matches(info => CreationInfoMatches(info, creationInfo)))).MustHaveHappened(4, Times.Exactly); + A.CallTo(() => _processLine(A._, A.That.Matches(info => CreationInfoMatches(info, creationInfo)), A>._)).MustNotHaveHappened(); + A.CallTo(() => _processLine(A._, A.That.Not.Matches(info => CreationInfoMatches(info, creationInfo)), A>._)).MustHaveHappened(4, Times.Exactly); result.Should().NotBeNull(); result.Created.Should().Be(4); result.Error.Should().Be(1); result.Total.Should().Be(5); result.Errors.Should().ContainSingle().Which.Should().Match(x => x.Line == 3 && x.Message == "at least one role must be specified"); - A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, A._, A>._)) + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, "NewUserExternalIdpTemplate", A>._)) .MustHaveHappened(4, Times.Exactly); } [Fact] public async Task TestUserCreationParsingError() { - SetupFakes(new[] { + SetupFakes([ HeaderLine(), NextLine(), NextLine(), _fixture.Create(), NextLine(), NextLine() - }); + ]); var sut = new UserUploadBusinessLogic(_userProvisioningService, _mailingProcessCreation, _identityService, _errorMessageService, _options); @@ -257,7 +259,7 @@ public async Task TestUserCreationParsingError() result.Error.Should().Be(1); result.Total.Should().Be(5); result.Errors.Should().ContainSingle().Which.Should().Match(x => x.Line == 3 && x.Message == "value for LastName type string expected (Parameter 'document')"); - A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, A._, A>._)) + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, "NewUserExternalIdpTemplate", A>._)) .MustHaveHappened(4, Times.Exactly); } @@ -266,30 +268,30 @@ public async Task TestUserCreationCreationThrows() { var creationInfo = _fixture.Create(); - SetupFakes(new[] { + SetupFakes([ HeaderLine(), NextLine(), NextLine(), NextLine(creationInfo), NextLine(), NextLine() - }); + ]); - A.CallTo(() => _processLine(A.That.Matches(info => CreationInfoMatches(info, creationInfo)))) + A.CallTo(() => _processLine(A._, A.That.Matches(info => CreationInfoMatches(info, creationInfo)), A>._)) .Throws(_error); var sut = new UserUploadBusinessLogic(_userProvisioningService, _mailingProcessCreation, _identityService, _errorMessageService, _options); var result = await sut.UploadOwnCompanyIdpUsersAsync(_identityProviderId, _document, CancellationToken.None); - A.CallTo(() => _processLine(A.That.Matches(info => CreationInfoMatches(info, creationInfo)))).MustHaveHappened(); + A.CallTo(() => _processLine(A._, A.That.Matches(info => CreationInfoMatches(info, creationInfo)), A>._)).MustHaveHappened(); result.Should().NotBeNull(); result.Created.Should().Be(2); result.Error.Should().Be(1); result.Total.Should().Be(3); result.Errors.Should().ContainSingle().Which.Should().Match(x => x.Line == 3 && x.Message == _error.Message); - A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, A._, A>._)) + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, "NewUserExternalIdpTemplate", A>._)) .MustHaveHappened(2, Times.Exactly); } @@ -300,7 +302,7 @@ public async Task TestUserCreationCreationThrows() [Fact] public async Task TestSetupSharedIdp() { - SetupFakes(new[] { HeaderLineSharedIdp() }); + SetupFakes([HeaderLineSharedIdp()]); var sut = new UserUploadBusinessLogic(_userProvisioningService, _mailingProcessCreation, _identityService, _errorMessageService, _options); @@ -317,14 +319,14 @@ public async Task TestSetupSharedIdp() [Fact] public async Task TestUserCreationSharedIdpAllSuccess() { - SetupFakes(new[] { + SetupFakes([ HeaderLineSharedIdp(), NextLineSharedIdp(), NextLineSharedIdp(), NextLineSharedIdp(), NextLineSharedIdp(), NextLineSharedIdp() - }); + ]); var sut = new UserUploadBusinessLogic(_userProvisioningService, _mailingProcessCreation, _identityService, _errorMessageService, _options); @@ -335,8 +337,10 @@ public async Task TestUserCreationSharedIdpAllSuccess() result.Error.Should().Be(0); result.Total.Should().Be(5); result.Errors.Should().BeEmpty(); - A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, A._, A>._)) - .MustNotHaveHappened(); + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, "NewUserTemplate", A>._)) + .MustHaveHappened(5, Times.Exactly); + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, "NewUserPasswordTemplate", A>._)) + .MustHaveHappened(5, Times.Exactly); } [Fact] @@ -344,14 +348,14 @@ public async Task TestUserCreationSharedIdpHeaderParsingThrows() { var invalidHeader = _fixture.Create(); - SetupFakes(new[] { + SetupFakes([ invalidHeader, NextLineSharedIdp(), NextLineSharedIdp(), NextLineSharedIdp(), NextLineSharedIdp(), NextLineSharedIdp() - }); + ]); var sut = new UserUploadBusinessLogic(_userProvisioningService, _mailingProcessCreation, _identityService, _errorMessageService, _options); @@ -359,6 +363,8 @@ public async Task TestUserCreationSharedIdpHeaderParsingThrows() var error = await Assert.ThrowsAsync(Act); error.Message.Should().Be($"invalid format: expected 'FirstName', got '{invalidHeader}' (Parameter 'document')"); + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, A._, A>._)) + .MustNotHaveHappened(); } [Fact] @@ -368,21 +374,21 @@ public async Task TestUserCreationSharedIdpNoRolesError() .With(x => x.RoleDatas, Enumerable.Empty()) .Create(); - SetupFakes(new[] { + SetupFakes([ HeaderLineSharedIdp(), NextLineSharedIdp(), NextLineSharedIdp(), NextLineSharedIdp(creationInfo), NextLineSharedIdp(), NextLineSharedIdp(), - }); + ]); var sut = new UserUploadBusinessLogic(_userProvisioningService, _mailingProcessCreation, _identityService, _errorMessageService, _options); var result = await sut.UploadOwnCompanySharedIdpUsersAsync(_document, CancellationToken.None); - A.CallTo(() => _processLine(A.That.Matches(info => CreationInfoMatchesSharedIdp(info, creationInfo)))).MustNotHaveHappened(); - A.CallTo(() => _processLine(A.That.Not.Matches(info => CreationInfoMatchesSharedIdp(info, creationInfo)))).MustHaveHappened(4, Times.Exactly); + A.CallTo(() => _processLine(A._, A.That.Matches(info => CreationInfoMatchesSharedIdp(info, creationInfo)), A>._)).MustNotHaveHappened(); + A.CallTo(() => _processLine(A._, A.That.Not.Matches(info => CreationInfoMatchesSharedIdp(info, creationInfo)), A>._)).MustHaveHappened(4, Times.Exactly); result.Should().NotBeNull(); result.Created.Should().Be(4); @@ -390,26 +396,30 @@ public async Task TestUserCreationSharedIdpNoRolesError() result.Total.Should().Be(5); result.Errors.Should().HaveCount(1); result.Errors.Should().ContainSingle().Which.Should().Match(x => x.Line == 3 && x.Message == "at least one role must be specified"); + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, "NewUserTemplate", A>._)) + .MustHaveHappened(4, Times.Exactly); + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, "NewUserPasswordTemplate", A>._)) + .MustHaveHappened(4, Times.Exactly); } [Fact] public async Task TestUserCreationSharedIdpCreationError() { var creationInfo = _fixture.Create(); - var detailError = ConflictException.Create(ProvisioningServiceErrors.USER_CREATION_FAILURE, new ErrorParameter[] { new("userName", "foo"), new("realm", "bar") }); + var detailError = ConflictException.Create(ProvisioningServiceErrors.USER_CREATION_FAILURE, [new("userName", "foo"), new("realm", "bar")]); - SetupFakes(new[] { + SetupFakes([ HeaderLineSharedIdp(), NextLineSharedIdp(), NextLineSharedIdp(), NextLineSharedIdp(creationInfo), NextLineSharedIdp(), NextLineSharedIdp() - }); + ]); - A.CallTo(() => _processLine(A.That.Matches(info => CreationInfoMatchesSharedIdp(info, creationInfo)))) + A.CallTo(() => _processLine(A._, A.That.Matches(info => CreationInfoMatchesSharedIdp(info, creationInfo)), A>._)) .ReturnsLazily( - (UserCreationRoleDataIdpInfo creationInfo) => _fixture.Build<(Guid CompanyUserId, string UserName, string? Password, Exception? Error)>() + (CompanyNameIdpAliasData _, UserCreationRoleDataIdpInfo creationInfo, Action? _) => _fixture.Build<(Guid CompanyUserId, string UserName, string? Password, Exception? Error)>() .With(x => x.CompanyUserId, Guid.Empty) .With(x => x.UserName, creationInfo.UserName) .With(x => x.Error, detailError) @@ -419,7 +429,7 @@ public async Task TestUserCreationSharedIdpCreationError() var result = await sut.UploadOwnCompanySharedIdpUsersAsync(_document, CancellationToken.None); - A.CallTo(() => _processLine(A.That.Matches(info => CreationInfoMatchesSharedIdp(info, creationInfo)))).MustHaveHappened(); + A.CallTo(() => _processLine(A._, A.That.Matches(info => CreationInfoMatchesSharedIdp(info, creationInfo)), A>._)).MustHaveHappened(); result.Should().NotBeNull(); result.Created.Should().Be(4); @@ -437,19 +447,23 @@ public async Task TestUserCreationSharedIdpCreationError() x.Parameters.Count() == 2 && x.Parameters.First(p => p.Name == "userName").Value == "foo" && x.Parameters.First(p => p.Name == "realm").Value == "bar"); + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, "NewUserTemplate", A>._)) + .MustHaveHappened(4, Times.Exactly); + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, "NewUserPasswordTemplate", A>._)) + .MustHaveHappened(4, Times.Exactly); } [Fact] public async Task TestUserCreationSharedIdpParsingError() { - SetupFakes(new[] { + SetupFakes([ HeaderLineSharedIdp(), NextLineSharedIdp(), NextLineSharedIdp(), _fixture.Create(), NextLineSharedIdp(), NextLineSharedIdp() - }); + ]); var sut = new UserUploadBusinessLogic(_userProvisioningService, _mailingProcessCreation, _identityService, _errorMessageService, _options); @@ -461,6 +475,10 @@ public async Task TestUserCreationSharedIdpParsingError() result.Total.Should().Be(5); result.Errors.Should().HaveCount(1); result.Errors.Should().ContainSingle().Which.Should().Match(x => x.Line == 3 && x.Message == "value for LastName type string expected (Parameter 'document')"); + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, "NewUserTemplate", A>._)) + .MustHaveHappened(4, Times.Exactly); + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, "NewUserPasswordTemplate", A>._)) + .MustHaveHappened(4, Times.Exactly); } [Fact] @@ -468,29 +486,33 @@ public async Task TestUserCreationSharedIdpCreationThrows() { var creationInfo = _fixture.Create(); - SetupFakes(new[] { + SetupFakes([ HeaderLineSharedIdp(), NextLineSharedIdp(), NextLineSharedIdp(), NextLineSharedIdp(creationInfo), NextLineSharedIdp(), NextLineSharedIdp() - }); + ]); - A.CallTo(() => _processLine(A.That.Matches(info => CreationInfoMatchesSharedIdp(info, creationInfo)))) + A.CallTo(() => _processLine(A._, A.That.Matches(info => CreationInfoMatchesSharedIdp(info, creationInfo)), A>._)) .Throws(_error); var sut = new UserUploadBusinessLogic(_userProvisioningService, _mailingProcessCreation, _identityService, _errorMessageService, _options); var result = await sut.UploadOwnCompanySharedIdpUsersAsync(_document, CancellationToken.None); - A.CallTo(() => _processLine(A.That.Matches(info => CreationInfoMatchesSharedIdp(info, creationInfo)))).MustHaveHappened(); + A.CallTo(() => _processLine(A._, A.That.Matches(info => CreationInfoMatchesSharedIdp(info, creationInfo)), A>._)).MustHaveHappened(); result.Should().NotBeNull(); result.Created.Should().Be(2); result.Error.Should().Be(1); result.Total.Should().Be(3); result.Errors.Should().ContainSingle().Which.Should().Match(x => x.Line == 3 && x.Message == _error.Message); + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, "NewUserTemplate", A>._)) + .MustHaveHappened(2, Times.Exactly); + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, "NewUserPasswordTemplate", A>._)) + .MustHaveHappened(2, Times.Exactly); } #endregion @@ -514,15 +536,21 @@ private void SetupFakes(IEnumerable lines) .ReturnsLazily((string clientId, IEnumerable roles, Guid _) => roles.Select(role => _fixture.Build().With(x => x.ClientClientId, clientId).With(x => x.UserRoleText, role).Create())); - A.CallTo(() => _userProvisioningService.CreateOwnCompanyIdpUsersAsync(A._, A>._, A._)) - .ReturnsLazily((CompanyNameIdpAliasData _, IAsyncEnumerable userCreationInfos, CancellationToken _) => - userCreationInfos.Select(userCreationInfo => _processLine(userCreationInfo))); + A.CallTo(() => _userProvisioningService.CreateOwnCompanyIdpUsersAsync(A._, A>._, A>._, A._)) + .ReturnsLazily((CompanyNameIdpAliasData idpAliasData, IAsyncEnumerable userCreationInfos, Action? onSuccess, CancellationToken _) => + userCreationInfos.Select(userCreationInfo => _processLine(idpAliasData, userCreationInfo, onSuccess))); - A.CallTo(() => _processLine(A._)).ReturnsLazily( - (UserCreationRoleDataIdpInfo creationInfo) => _fixture.Build<(Guid CompanyUserId, string UserName, string? Password, Exception? Error)>() - .With(x => x.UserName, creationInfo.UserName) - .With(x => x.Error, default(Exception?)) - .Create()); + A.CallTo(() => _processLine(A._, A._, A>._)).ReturnsLazily( + (CompanyNameIdpAliasData aliasData, UserCreationRoleDataIdpInfo creationInfo, Action? onSucess) => + { + var password = aliasData.IsSharedIdp ? _fixture.Create() : null; + onSucess?.Invoke(new(creationInfo, password)); + return _fixture.Build<(Guid CompanyUserId, string UserName, string? Password, Exception? Error)>() + .With(x => x.UserName, creationInfo.UserName) + .With(x => x.Password, password) + .With(x => x.Error, default(Exception?)) + .Create(); + }); } private static string HeaderLine() => diff --git a/tests/processes/Invitation.Executor.Tests/InvitationProcessServiceTests.cs b/tests/processes/Invitation.Executor.Tests/InvitationProcessServiceTests.cs index 5d78193614..c38cd76a91 100644 --- a/tests/processes/Invitation.Executor.Tests/InvitationProcessServiceTests.cs +++ b/tests/processes/Invitation.Executor.Tests/InvitationProcessServiceTests.cs @@ -688,7 +688,7 @@ public async Task CreateUser_WithValid_ReturnsExpected() .Returns((true, applicationId, companyId, "testCorp", Enumerable.Repeat((Guid.NewGuid(), "idp"), 1), new UserInvitationInformation("tony", "stark", "tony@stark.com", "ironman"))); A.CallTo(() => _userProvisioningService.GetRoleDatas(A>._)) .Returns(Enumerable.Repeat(new UserRoleData(Guid.NewGuid(), "cl1", "ur 1"), 1).ToAsyncEnumerable()); - A.CallTo(() => _userProvisioningService.CreateOwnCompanyIdpUsersAsync(A._, A>._, A._)) + A.CallTo(() => _userProvisioningService.CreateOwnCompanyIdpUsersAsync(A._, A>._, A>._, A._)) .Returns(Enumerable.Repeat>((companyUserId, "ironman", "testPw", null), 1).ToAsyncEnumerable()); A.CallTo(() => _companyInvitationRepository.AttachAndModifyCompanyInvitation(companyInvitation.Id, A>._, A>._)) .Invokes((Guid _, Action? initialize, Action modify) => @@ -718,7 +718,7 @@ public async Task CreateUser_WithCreateOwnCompanyIdpUserThrowsException_ThrowsEx .Returns((true, applicationId, companyId, "testCorp", Enumerable.Repeat((Guid.NewGuid(), "idp"), 1), new UserInvitationInformation("tony", "stark", "tony@stark.com", "ironman"))); A.CallTo(() => _userProvisioningService.GetRoleDatas(A>._)) .Returns(Enumerable.Repeat(new UserRoleData(Guid.NewGuid(), "cl1", "ur 1"), 1).ToAsyncEnumerable()); - A.CallTo(() => _userProvisioningService.CreateOwnCompanyIdpUsersAsync(A._, A>._, A._)) + A.CallTo(() => _userProvisioningService.CreateOwnCompanyIdpUsersAsync(A._, A>._, A>._, A._)) .Returns(Enumerable.Repeat>((companyId, "ironman", "testPw", new ConflictException("test")), 1).ToAsyncEnumerable()); // Act diff --git a/tests/provisioning/Provisioning.Library.Tests/UserProvisioningServiceCreateUsersTests.cs b/tests/provisioning/Provisioning.Library.Tests/UserProvisioningServiceCreateUsersTests.cs index 39514d31bf..15d866d146 100644 --- a/tests/provisioning/Provisioning.Library.Tests/UserProvisioningServiceCreateUsersTests.cs +++ b/tests/provisioning/Provisioning.Library.Tests/UserProvisioningServiceCreateUsersTests.cs @@ -27,6 +27,7 @@ using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library.ErrorHandling; using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library.Models; using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library.Service; +using System.Collections.Immutable; namespace Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library.Tests; @@ -90,9 +91,16 @@ public async Task TestFixtureSetup() var userCreationInfoIdp = CreateUserCreationInfoIdp().ToAsyncEnumerable(); + var onSuccessArguments = ImmutableList.CreateBuilder(); + void OnSuccess(UserCreationCallbackData data) + { + onSuccessArguments.Add(data); + } + await sut.CreateOwnCompanyIdpUsersAsync( _companyNameIdpAliasData, userCreationInfoIdp, + OnSuccess, _cancellationTokenSource.Token ).ToListAsync(); @@ -102,6 +110,10 @@ await sut.CreateOwnCompanyIdpUsersAsync( A.CallTo(() => _provisioningManager.AddProviderUserLinkToCentralUserAsync(A._, A._)).MustHaveHappened(); A.CallTo(() => _provisioningManager.CreateSharedRealmUserAsync(A._, A._)).MustNotHaveHappened(); A.CallTo(() => _userRepository.AttachAndModifyIdentity(A._, null, A>._)).MustNotHaveHappened(); + + onSuccessArguments.ToImmutable() + .Should().HaveCount(_numUsers) + .And.AllSatisfy(x => x.Password.Should().BeNull()); } [Fact] @@ -111,9 +123,16 @@ public async Task TestSharedIdpFixtureSetup() var userCreationInfoIdp = CreateUserCreationInfoIdp().ToAsyncEnumerable(); + var onSuccessArguments = ImmutableList.CreateBuilder(); + void OnSuccess(UserCreationCallbackData data) + { + onSuccessArguments.Add(data); + } + await sut.CreateOwnCompanyIdpUsersAsync( _companyNameIdpAliasDataSharedIdp, userCreationInfoIdp, + OnSuccess, _cancellationTokenSource.Token ).ToListAsync(); @@ -123,6 +142,10 @@ await sut.CreateOwnCompanyIdpUsersAsync( A.CallTo(() => _provisioningManager.AddProviderUserLinkToCentralUserAsync(A._, A._)).MustHaveHappened(); A.CallTo(() => _provisioningManager.CreateSharedRealmUserAsync(A._, A._)).MustHaveHappened(); A.CallTo(() => _userRepository.AttachAndModifyIdentity(A._, null, A>._)).MustNotHaveHappened(); + + onSuccessArguments.ToImmutable() + .Should().HaveCount(_numUsers) + .And.AllSatisfy(x => x.Password.Should().NotBeNullOrEmpty()); } [Fact] @@ -131,16 +154,23 @@ public async Task TestCreateUsersAllSuccess() var sut = new UserProvisioningService(_provisioningManager, _portalRepositories); var userCreationInfoIdp = CreateUserCreationInfoIdp().ToList(); + var onSuccessArguments = ImmutableList.CreateBuilder(); + void OnSuccess(UserCreationCallbackData data) + { + onSuccessArguments.Add(data); + } var result = await sut.CreateOwnCompanyIdpUsersAsync( _companyNameIdpAliasData, userCreationInfoIdp.ToAsyncEnumerable(), + OnSuccess, _cancellationTokenSource.Token ).ToListAsync(); result.Should().HaveCount(_numUsers); - result.Select(r => r.UserName).Should().ContainInOrder(userCreationInfoIdp.Select(u => u.UserName)); + result.Select(r => r.UserName).Should().ContainInConsecutiveOrder(userCreationInfoIdp.Select(u => u.UserName)); result.Should().AllSatisfy(r => r.Error.Should().BeNull()); + onSuccessArguments.ToImmutable().Select(x => x.UserCreationInfo).Should().ContainInConsecutiveOrder(userCreationInfoIdp); } [Fact] @@ -164,9 +194,16 @@ public async Task TestCreateUsersRolesAssignmentError() A.CallTo(() => _provisioningManager.AssignClientRolesToCentralUserAsync(iamUserId, A>>._)) .Returns(new (string, IEnumerable, Exception?)[] { (_clientId, Enumerable.Empty(), new Exception("some error")) }.ToAsyncEnumerable()); + var onSuccessArgumentsBuilder = ImmutableList.CreateBuilder(); + void OnSuccess(UserCreationCallbackData data) + { + onSuccessArgumentsBuilder.Add(data); + } + var result = await sut.CreateOwnCompanyIdpUsersAsync( _companyNameIdpAliasData, userCreationInfoIdp.ToAsyncEnumerable(), + OnSuccess, _cancellationTokenSource.Token ).ToListAsync(); @@ -177,6 +214,8 @@ public async Task TestCreateUsersRolesAssignmentError() error.Should().NotBeNull(); error.Should().BeOfType(typeof(ConflictException)); error!.Message.Should().Be($"invalid role data [{string.Join(", ", specialUser.RoleDatas.Select(roleData => $"clientId: {roleData.ClientClientId}, role: {roleData.UserRoleText}, error: some error"))}] has not been assigned in keycloak"); + + onSuccessArgumentsBuilder.ToImmutable().Should().HaveCount(_numUsers - 1); } [Fact] @@ -197,9 +236,16 @@ public async Task TestCreateUsersRolesAssignmentNoRolesAssignedSuccess() A.CallTo(() => _provisioningManager.CreateCentralUserAsync(A.That.Matches(p => p.UserName == centralUserName), A)>>._)) .Returns(iamUserId); + var onSuccessArguments = ImmutableList.CreateBuilder(); + void OnSuccess(UserCreationCallbackData data) + { + onSuccessArguments.Add(data); + } + var result = await sut.CreateOwnCompanyIdpUsersAsync( _companyNameIdpAliasData, userCreationInfoIdp.ToAsyncEnumerable(), + OnSuccess, _cancellationTokenSource.Token ).ToListAsync(); @@ -208,6 +254,7 @@ public async Task TestCreateUsersRolesAssignmentNoRolesAssignedSuccess() result.Should().HaveCount(_numUsers); result.Should().AllSatisfy(r => r.Error.Should().BeNull()); + onSuccessArguments.ToImmutable().Should().HaveCount(_numUsers); } [Fact] @@ -230,9 +277,16 @@ public async Task TestCreateUsersCentralUserDuplicateKeycloakUserError() A.CallTo(() => _provisioningManager.GetProviderUserLinkDataForCentralUserIdAsync(iamUserId)) .Returns(new[] { new IdentityProviderLink(_companyNameIdpAliasData.IdpAlias, userInfo.UserId, userInfo.UserName) }.ToAsyncEnumerable()); + var onSuccessArguments = ImmutableList.CreateBuilder(); + void OnSuccess(UserCreationCallbackData data) + { + onSuccessArguments.Add(data); + } + var result = await sut.CreateOwnCompanyIdpUsersAsync( _companyNameIdpAliasData, userCreationInfoIdp.ToAsyncEnumerable(), + OnSuccess, _cancellationTokenSource.Token ).ToListAsync(); @@ -267,9 +321,16 @@ public async Task TestCreateUsersCentralUserPotentialMatchWithoutMatchingKeycloa A.CallTo(() => _provisioningManager.GetProviderUserLinkDataForCentralUserIdAsync(iamUserId)) .Returns(_fixture.CreateMany(3).ToAsyncEnumerable()); + var onSuccessArguments = ImmutableList.CreateBuilder(); + void OnSuccess(UserCreationCallbackData data) + { + onSuccessArguments.Add(data); + } + var result = await sut.CreateOwnCompanyIdpUsersAsync( _companyNameIdpAliasData, userCreationInfoIdp.ToAsyncEnumerable(), + OnSuccess, _cancellationTokenSource.Token ).ToListAsync(); @@ -277,6 +338,8 @@ public async Task TestCreateUsersCentralUserPotentialMatchWithoutMatchingKeycloa result.Should().HaveCount(_numUsers) .And.AllSatisfy(r => r.Error.Should().BeNull()); + + onSuccessArguments.ToImmutable().Should().HaveCount(_numUsers); } [Fact] @@ -291,9 +354,16 @@ public async Task TestCreateUsersKeycloakConflictError() A.CallTo(() => _provisioningManager.CreateCentralUserAsync(A.That.Matches(x => x.FirstName == userInfo.FirstName), A Values)>>._)) .Throws(ConflictException.Create(ProvisioningServiceErrors.USER_CREATION_CONFLICT, new ErrorParameter[] { new("userName", "foo"), new("realm", "bar") })); + var onSuccessArguments = ImmutableList.CreateBuilder(); + void OnSuccess(UserCreationCallbackData data) + { + onSuccessArguments.Add(data); + } + var result = await sut.CreateOwnCompanyIdpUsersAsync( _companyNameIdpAliasData, userCreationInfoIdp.ToAsyncEnumerable(), + OnSuccess, _cancellationTokenSource.Token ).ToListAsync(); @@ -308,6 +378,8 @@ public async Task TestCreateUsersKeycloakConflictError() x.Parameters.Count() == 2 && x.Parameters.First(p => p.Name == "userName").Value == "foo" && x.Parameters.First(p => p.Name == "realm").Value == "bar"); + + onSuccessArguments.ToImmutable().Should().HaveCount(_numUsers - 1); } [Fact] @@ -337,9 +409,16 @@ public async Task TestCreateUsersNotExistingCompanyUserWithoutKeycloakUserSucces A.CallTo(() => _provisioningManager.CreateCentralUserAsync(A.That.Matches(u => u.FirstName == userInfo.FirstName), A)>>._)) .Returns(centralUserId); + var onSuccessArguments = ImmutableList.CreateBuilder(); + void OnSuccess(UserCreationCallbackData data) + { + onSuccessArguments.Add(data); + } + var result = await sut.CreateOwnCompanyIdpUsersAsync( _companyNameIdpAliasData, userCreationInfoIdp.ToAsyncEnumerable(), + OnSuccess, _cancellationTokenSource.Token ).ToListAsync(); @@ -356,6 +435,8 @@ public async Task TestCreateUsersNotExistingCompanyUserWithoutKeycloakUserSucces result.Should().HaveCount(_numUsers); result.Should().AllSatisfy(r => r.Error.Should().BeNull()); + + onSuccessArguments.ToImmutable().Should().HaveCount(_numUsers); } [Fact] @@ -376,9 +457,16 @@ public async Task TestCreateUsersExistingCompanyUserWithoutKeycloakUserSuccess() A.CallTo(() => _provisioningManager.CreateCentralUserAsync(A.That.Matches(u => u.FirstName == userInfo.FirstName), A)>>._)) .Returns(centralUserId); + var onSuccessArguments = ImmutableList.CreateBuilder(); + void OnSuccess(UserCreationCallbackData data) + { + onSuccessArguments.Add(data); + } + var result = await sut.CreateOwnCompanyIdpUsersAsync( _companyNameIdpAliasData, userCreationInfoIdp.ToAsyncEnumerable(), + OnSuccess, _cancellationTokenSource.Token ).ToListAsync(); @@ -393,6 +481,8 @@ public async Task TestCreateUsersExistingCompanyUserWithoutKeycloakUserSuccess() result.Should().HaveCount(_numUsers); result.Should().AllSatisfy(r => r.Error.Should().BeNull()); + + onSuccessArguments.ToImmutable().Should().HaveCount(_numUsers); } #endregion diff --git a/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs b/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs index f21f2f66c2..a08c51cb98 100644 --- a/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs +++ b/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs @@ -1554,7 +1554,7 @@ public async Task TestInviteNewUserAsyncSuccess() await sut.InviteNewUserAsync(_existingApplicationId, userCreationInfo); - A.CallTo(() => _userProvisioningService.CreateOwnCompanyIdpUsersAsync(A._, A>._, A._)).MustHaveHappened(); + A.CallTo(() => _userProvisioningService.CreateOwnCompanyIdpUsersAsync(A._, A>._, A>._, A._)).MustHaveHappened(); A.CallTo(() => _applicationRepository.CreateInvitation(A.That.IsEqualTo(_existingApplicationId), A._)).MustHaveHappened(); A.CallTo(() => _applicationRepository.AttachAndModifyCompanyApplication(_existingApplicationId, A>._)).MustHaveHappenedOnceExactly(); A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappened(); @@ -1655,7 +1655,7 @@ public async Task TestInviteNewUserAsyncCreationErrorThrows() var error = await Assert.ThrowsAsync(Act); error.Message.Should().Be(_error.Message); - A.CallTo(() => _userProvisioningService.CreateOwnCompanyIdpUsersAsync(A._, A>._, A._)).MustHaveHappened(); + A.CallTo(() => _userProvisioningService.CreateOwnCompanyIdpUsersAsync(A._, A>._, A>._, A._)).MustHaveHappened(); A.CallTo(() => _applicationRepository.CreateInvitation(A.That.IsEqualTo(_existingApplicationId), A._)).MustNotHaveHappened(); A.CallTo(() => _portalRepositories.SaveAsync()).MustNotHaveHappened(); A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A.That.IsEqualTo(userCreationInfo.eMail), A._, A>._)) @@ -3258,8 +3258,8 @@ private void SetupRepositories() private void SetupFakesForInvitation() { - A.CallTo(() => _userProvisioningService.CreateOwnCompanyIdpUsersAsync(A._, A>._, A._)) - .ReturnsLazily((CompanyNameIdpAliasData _, IAsyncEnumerable userCreationInfos, CancellationToken _) => + A.CallTo(() => _userProvisioningService.CreateOwnCompanyIdpUsersAsync(A._, A>._, A>._, A._)) + .ReturnsLazily((CompanyNameIdpAliasData _, IAsyncEnumerable userCreationInfos, Action? onSuccess, CancellationToken _) => userCreationInfos.Select(userCreationInfo => _processLine(userCreationInfo))); A.CallTo(() => _userProvisioningService.GetRoleDatas(A>._))