From dfeca44873b36268d15bdb471f987a5ea0443708 Mon Sep 17 00:00:00 2001
From: Phil Schneider
Date: Tue, 10 Oct 2023 12:34:22 +0200
Subject: [PATCH] feat(n2n): move keycloak user creation to process (#280)
* feat(n2n): move keycloak user creation to process
Refs: CPLP-3295
* feat(n2n): add idp to welcome mails
Refs: CPLP-2639
---------
Co-authored-by: Norbert Truchsess
Reviewed-by: Norbert Truchsess
---
.../BusinessLogic/NetworkBusinessLogic.cs | 46 +++++-------
.../BusinessLogic/UserBusinessLogic.cs | 1 +
.../EmailTemplates/osp_welcome_email.html | 2 +-
.../Repositories/INetworkRepository.cs | 1 +
.../Repositories/NetworkRepository.cs | 5 ++
.../NetworkRegistrationHandlerExtensions.cs | 5 +-
.../INetworkRegistrationHandler.cs | 1 -
.../INetworkRegistrationProcessHelper.cs | 1 -
.../Models/UserMailInformation.cs | 24 +++++++
.../NetworkRegistration.Library.csproj | 1 +
.../NetworkRegistrationHandler.cs | 62 ++++++++++++----
.../NetworkRegistrationProcessHelper.cs | 2 -
.../Service/IUserProvisioningService.cs | 5 +-
.../Service/UserProvisioningService.cs | 72 ++++++++++++-------
.../NetworkBusinessLogicTests.cs | 19 ++---
.../NetworkRegistrationHandlerTests.cs | 48 ++++++++++---
16 files changed, 200 insertions(+), 95 deletions(-)
create mode 100644 src/processes/NetworkRegistration.Library/Models/UserMailInformation.cs
diff --git a/src/administration/Administration.Service/BusinessLogic/NetworkBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/NetworkBusinessLogic.cs
index a181587251..51522cb5b7 100644
--- a/src/administration/Administration.Service/BusinessLogic/NetworkBusinessLogic.cs
+++ b/src/administration/Administration.Service/BusinessLogic/NetworkBusinessLogic.cs
@@ -23,7 +23,6 @@
using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling;
using Org.Eclipse.TractusX.Portal.Backend.Framework.Linq;
using Org.Eclipse.TractusX.Portal.Backend.Framework.Models;
-using Org.Eclipse.TractusX.Portal.Backend.Mailing.SendMail;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories;
@@ -46,16 +45,14 @@ public class NetworkBusinessLogic : INetworkBusinessLogic
private readonly IIdentityService _identityService;
private readonly IUserProvisioningService _userProvisioningService;
private readonly INetworkRegistrationProcessHelper _processHelper;
- private readonly IMailingService _mailingService;
private readonly PartnerRegistrationSettings _settings;
- public NetworkBusinessLogic(IPortalRepositories portalRepositories, IIdentityService identityService, IUserProvisioningService userProvisioningService, INetworkRegistrationProcessHelper processHelper, IMailingService mailingService, IOptions options)
+ public NetworkBusinessLogic(IPortalRepositories portalRepositories, IIdentityService identityService, IUserProvisioningService userProvisioningService, INetworkRegistrationProcessHelper processHelper, IOptions options)
{
_portalRepositories = portalRepositories;
_identityService = identityService;
_userProvisioningService = userProvisioningService;
_processHelper = processHelper;
- _mailingService = mailingService;
_settings = options.Value;
}
@@ -69,8 +66,6 @@ public async Task HandlePartnerRegistration(PartnerRegistrationData data)
var (roleData, identityProviderIdAliase, singleIdentityProviderIdAlias, allIdentityProviderIds) = await ValidatePartnerRegistrationData(data, networkRepository, identityProviderRepository, ownerCompanyId).ConfigureAwait(false);
- var (_, companyName) = await companyRepository.GetCompanyNameUntrackedAsync(ownerCompanyId).ConfigureAwait(false);
-
var companyId = CreatePartnerCompany(companyRepository, data);
var applicationId = _portalRepositories.GetInstance().CreateCompanyApplication(companyId, CompanyApplicationStatusId.CREATED, CompanyApplicationTypeId.EXTERNAL,
@@ -96,13 +91,27 @@ string GetIdpAlias(Guid? identityProviderId) =>
? singleIdentityProviderIdAlias?.Alias ?? throw new UnexpectedConditionException("singleIdentityProviderIdAlias should never be null here")
: identityProviderIdAliase?[identityProviderId.Value] ?? throw new UnexpectedConditionException("identityProviderIdAliase should never be null here and should always contain an entry for identityProviderId");
- async IAsyncEnumerable<(Guid CompanyUserId, string UserName, string? Password, Exception? Error)> CreateUsers()
+ async IAsyncEnumerable<(Guid CompanyUserId, Exception? Error)> CreateUsers()
{
- foreach (var user in GetUserCreationData(companyId, GetIdpId, GetIdpAlias, data, roleData))
+ var userRepository = _portalRepositories.GetInstance();
+ await foreach (var (aliasData, creationInfos) in GetUserCreationData(companyId, GetIdpId, GetIdpAlias, data, roleData).ToAsyncEnumerable())
{
- await foreach (var result in _userProvisioningService.CreateOwnCompanyIdpUsersAsync(user.AliasData, user.CreationInfos.ToAsyncEnumerable()))
+ foreach (var creationInfo in creationInfos)
{
- yield return result;
+ var identityId = Guid.Empty;
+ Exception? error = null;
+ try
+ {
+ var (_, companyUserId) = await _userProvisioningService.GetOrCreateCompanyUser(userRepository, aliasData.IdpAlias,
+ creationInfo, companyId, aliasData.IdpId, data.Bpn).ConfigureAwait(false);
+ identityId = companyUserId;
+ }
+ catch (Exception ex)
+ {
+ error = ex;
+ }
+
+ yield return (identityId, error);
}
}
}
@@ -111,7 +120,6 @@ string GetIdpAlias(Guid? identityProviderId) =>
userCreationErrors.IfAny(errors => throw new ServiceException($"Errors occured while saving the users: ${string.Join("", errors.Select(x => x.Message))}", errors.First()));
await _portalRepositories.SaveAsync().ConfigureAwait(false);
- await SendMails(data.UserDetails.Select(x => new ValueTuple(x.Email, x.FirstName, x.LastName)), companyName).ConfigureAwait(false);
}
private Guid CreatePartnerCompany(ICompanyRepository companyRepository, PartnerRegistrationData data)
@@ -167,22 +175,6 @@ private Guid CreatePartnerCompany(ICompanyRepository companyRepository, PartnerR
return (AliasData: companyNameIdpAliasData, CreationInfos: userCreationInfos);
});
- private async Task SendMails(IEnumerable<(string Email, string? FirstName, string? LastName)> companyUserWithRoleIdForCompany, string ospName)
- {
- foreach (var (receiver, firstName, lastName) in companyUserWithRoleIdForCompany)
- {
- var userName = string.Join(" ", firstName, lastName);
- var mailParameters = new Dictionary
- {
- { "userName", !string.IsNullOrWhiteSpace(userName) ? userName : receiver },
- { "hostname", _settings.BasePortalAddress },
- { "osp", ospName },
- { "url", _settings.BasePortalAddress }
- };
- await _mailingService.SendMails(receiver, mailParameters, Enumerable.Repeat("OspWelcomeMail", 1)).ConfigureAwait(false);
- }
- }
-
public Task RetriggerProcessStep(Guid externalId, ProcessStepTypeId processStepTypeId) =>
_processHelper.TriggerProcessStep(externalId, processStepTypeId);
diff --git a/src/administration/Administration.Service/BusinessLogic/UserBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/UserBusinessLogic.cs
index bf8ad2fa48..18b9d3fc79 100644
--- a/src/administration/Administration.Service/BusinessLogic/UserBusinessLogic.cs
+++ b/src/administration/Administration.Service/BusinessLogic/UserBusinessLogic.cs
@@ -154,6 +154,7 @@ private Task> GetOwnCompanyUserRoleData(IEnumerable());
}
+
return _userProvisioningService.GetOwnCompanyPortalRoleDatas(_settings.Portal.KeycloakClientID, roles, companyId);
}
diff --git a/src/mailing/Mailing.Template/EmailTemplates/osp_welcome_email.html b/src/mailing/Mailing.Template/EmailTemplates/osp_welcome_email.html
index b78482a1bc..f4eea5098a 100644
--- a/src/mailing/Mailing.Template/EmailTemplates/osp_welcome_email.html
+++ b/src/mailing/Mailing.Template/EmailTemplates/osp_welcome_email.html
@@ -92,7 +92,7 @@
style="Margin:0;padding-top:20px;padding-bottom:20px;padding-left:30px;padding-right:30px;text-align: left;">
- Dear {userName},
your registration at the Catena-X dataspace got based on your request successfully generated by {osp}.
We have created your registration request. Before the registration validation is taking place, a final check from your side confirming your registration data and confirming the terms & conditions is needed.
Please follow the link below to access your registration data and to confirm the company role related terms & conditions.
You may want to update the company roles by selecting additional roles.
+ Dear {userName},
your registration at the Catena-X dataspace got based on your request successfully generated by {osp} for idps {idpAliasse}.
We have created your registration request. Before the registration validation is taking place, a final check from your side confirming your registration data and confirming the terms & conditions is needed.
Please follow the link below to access your registration data and to confirm the company role related terms & conditions.
You may want to update the company roles by selecting additional roles.
diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/INetworkRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/INetworkRepository.cs
index 90a6f94073..46e4991a9d 100644
--- a/src/portalbackend/PortalBackend.DBAccess/Repositories/INetworkRepository.cs
+++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/INetworkRepository.cs
@@ -32,4 +32,5 @@ public interface INetworkRepository
Task<(bool RegistrationIdExists, VerifyProcessData processData)> IsValidRegistration(Guid externalId, IEnumerable processStepTypeIds);
Task<(bool Exists, IEnumerable<(Guid CompanyApplicationId, CompanyApplicationStatusId CompanyApplicationStatusId, string? CallbackUrl)> CompanyApplications, IEnumerable<(CompanyRoleId CompanyRoleId, IEnumerable AgreementIds)> CompanyRoleAgreementIds, Guid? ProcessId)> GetSubmitData(Guid companyId);
Task<(OspDetails? OspDetails, Guid? ExternalId, string? Bpn, Guid ApplicationId, IEnumerable Comments)> GetCallbackData(Guid networkRegistrationId, ProcessStepTypeId processStepTypeId);
+ Task GetOspCompanyName(Guid networkRegistrationId);
}
diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/NetworkRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/NetworkRepository.cs
index 45cf0f094e..7a7b149e59 100644
--- a/src/portalbackend/PortalBackend.DBAccess/Repositories/NetworkRepository.cs
+++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/NetworkRepository.cs
@@ -108,4 +108,9 @@ public Task GetNetworkRegistrationDataForProcessIdAsync(Guid processId) =>
.Select(step => step.Message!)
: new List()))
.SingleOrDefaultAsync();
+
+ public Task GetOspCompanyName(Guid networkRegistrationId) =>
+ _context.NetworkRegistrations.Where(x => x.Id == networkRegistrationId)
+ .Select(x => x.OnboardingServiceProvider!.Name)
+ .SingleOrDefaultAsync();
}
diff --git a/src/processes/NetworkRegistration.Library/DependencyInjection/NetworkRegistrationHandlerExtensions.cs b/src/processes/NetworkRegistration.Library/DependencyInjection/NetworkRegistrationHandlerExtensions.cs
index 5adc8ae9f1..fd1bdb32dd 100644
--- a/src/processes/NetworkRegistration.Library/DependencyInjection/NetworkRegistrationHandlerExtensions.cs
+++ b/src/processes/NetworkRegistration.Library/DependencyInjection/NetworkRegistrationHandlerExtensions.cs
@@ -1,5 +1,4 @@
/********************************************************************************
- * Copyright (c) 2021, 2023 BMW Group AG
* Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
@@ -32,6 +31,9 @@ public class NetworkRegistrationProcessSettings
[Required]
[DistinctValues("x => x.ClientId")]
public IEnumerable InitialRoles { get; set; } = null!;
+
+ [Required(AllowEmptyStrings = false)]
+ public string BasePortalAddress { get; set; } = null!;
}
public static class NetworkRegistrationHandlerExtensions
@@ -41,6 +43,7 @@ public static IServiceCollection AddNetworkRegistrationHandler(this IServiceColl
var section = config.GetSection("NetworkRegistration");
services.AddOptions()
.Bind(section)
+ .ValidateDataAnnotations()
.ValidateDistinctValues(section)
.ValidateOnStart();
diff --git a/src/processes/NetworkRegistration.Library/INetworkRegistrationHandler.cs b/src/processes/NetworkRegistration.Library/INetworkRegistrationHandler.cs
index 4f4133fb06..f562464594 100644
--- a/src/processes/NetworkRegistration.Library/INetworkRegistrationHandler.cs
+++ b/src/processes/NetworkRegistration.Library/INetworkRegistrationHandler.cs
@@ -1,5 +1,4 @@
/********************************************************************************
- * Copyright (c) 2021, 2023 BMW Group AG
* Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
diff --git a/src/processes/NetworkRegistration.Library/INetworkRegistrationProcessHelper.cs b/src/processes/NetworkRegistration.Library/INetworkRegistrationProcessHelper.cs
index 413d60a6ec..8c014ee160 100644
--- a/src/processes/NetworkRegistration.Library/INetworkRegistrationProcessHelper.cs
+++ b/src/processes/NetworkRegistration.Library/INetworkRegistrationProcessHelper.cs
@@ -1,5 +1,4 @@
/********************************************************************************
- * Copyright (c) 2021, 2023 BMW Group AG
* Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
diff --git a/src/processes/NetworkRegistration.Library/Models/UserMailInformation.cs b/src/processes/NetworkRegistration.Library/Models/UserMailInformation.cs
new file mode 100644
index 0000000000..d07af71a83
--- /dev/null
+++ b/src/processes/NetworkRegistration.Library/Models/UserMailInformation.cs
@@ -0,0 +1,24 @@
+/********************************************************************************
+ * Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ********************************************************************************/
+
+using System.Collections.Generic;
+
+namespace Org.Eclipse.TractusX.Portal.Backend.Processes.NetworkRegistration.Library.Models;
+
+public record UserMailInformation(string Email, string? FirstName, string? LastName, IEnumerable IdpAliasse);
diff --git a/src/processes/NetworkRegistration.Library/NetworkRegistration.Library.csproj b/src/processes/NetworkRegistration.Library/NetworkRegistration.Library.csproj
index 4db5590cf9..9babed5eed 100644
--- a/src/processes/NetworkRegistration.Library/NetworkRegistration.Library.csproj
+++ b/src/processes/NetworkRegistration.Library/NetworkRegistration.Library.csproj
@@ -29,6 +29,7 @@
+
diff --git a/src/processes/NetworkRegistration.Library/NetworkRegistrationHandler.cs b/src/processes/NetworkRegistration.Library/NetworkRegistrationHandler.cs
index ce2df221d0..62e95ead15 100644
--- a/src/processes/NetworkRegistration.Library/NetworkRegistrationHandler.cs
+++ b/src/processes/NetworkRegistration.Library/NetworkRegistrationHandler.cs
@@ -1,5 +1,4 @@
/********************************************************************************
- * Copyright (c) 2021, 2023 BMW Group AG
* Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
@@ -20,10 +19,12 @@
using Microsoft.Extensions.Options;
using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling;
+using Org.Eclipse.TractusX.Portal.Backend.Mailing.SendMail;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums;
using Org.Eclipse.TractusX.Portal.Backend.Processes.NetworkRegistration.Library.DependencyInjection;
+using Org.Eclipse.TractusX.Portal.Backend.Processes.NetworkRegistration.Library.Models;
using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library;
using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library.Models;
using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library.Service;
@@ -36,16 +37,19 @@ public class NetworkRegistrationHandler : INetworkRegistrationHandler
private readonly IUserProvisioningService _userProvisioningService;
private readonly IProvisioningManager _provisioningManager;
private readonly NetworkRegistrationProcessSettings _settings;
+ private readonly IMailingService _mailingService;
public NetworkRegistrationHandler(
IPortalRepositories portalRepositories,
IUserProvisioningService userProvisioningService,
IProvisioningManager provisioningManager,
+ IMailingService mailingService,
IOptions options)
{
_portalRepositories = portalRepositories;
_userProvisioningService = userProvisioningService;
_provisioningManager = provisioningManager;
+ _mailingService = mailingService;
_settings = options.Value;
}
@@ -53,6 +57,13 @@ public NetworkRegistrationHandler(
public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> SynchronizeUser(Guid networkRegistrationId)
{
var userRepository = _portalRepositories.GetInstance();
+ var userRoleRepository = _portalRepositories.GetInstance();
+ var ospName = await _portalRepositories.GetInstance().GetOspCompanyName(networkRegistrationId).ConfigureAwait(false);
+ if (string.IsNullOrWhiteSpace(ospName))
+ {
+ throw new UnexpectedConditionException("Onboarding Service Provider name must be set");
+ }
+
var companyAssignedIdentityProviders = await userRepository
.GetUserAssignedIdentityProviderForNetworkRegistration(networkRegistrationId)
.ToListAsync()
@@ -75,19 +86,25 @@ public NetworkRegistrationHandler(
try
{
- var userId = await _provisioningManager.GetUserByUserName(cu.CompanyUserId.ToString()).ConfigureAwait(false) ??
- await _userProvisioningService.CreateCentralUserWithProviderLinks(cu.CompanyUserId, new UserCreationRoleDataIdpInfo(cu.FirstName!, cu.LastName!, cu.Email!, roleData, string.Empty, string.Empty, UserStatusId.ACTIVE, true), cu.CompanyName, cu.Bpn, cu.ProviderLinkData.Select(x => new IdentityProviderLink(x.Alias!, x.ProviderUserId, x.UserName)));
+ var userId = await _provisioningManager.GetUserByUserName(cu.CompanyUserId.ToString()).ConfigureAwait(false);
+ if (!string.IsNullOrWhiteSpace(userId))
+ {
+ userRepository.AttachAndModifyIdentity(cu.CompanyUserId, i =>
+ {
+ i.UserStatusId = UserStatusId.PENDING;
+ i.UserEntityId = null;
+ },
+ i =>
+ {
+ i.UserStatusId = UserStatusId.ACTIVE;
+ i.UserEntityId = userId;
+ });
- userRepository.AttachAndModifyIdentity(cu.CompanyUserId, i =>
- {
- i.UserStatusId = UserStatusId.PENDING;
- i.UserEntityId = null;
- },
- i =>
- {
- i.UserStatusId = UserStatusId.ACTIVE;
- i.UserEntityId = userId;
- });
+ await _userProvisioningService.AssignRolesToNewUserAsync(userRoleRepository, roleData, (userId, cu.CompanyUserId)).ConfigureAwait(false);
+ continue;
+ }
+
+ await _userProvisioningService.HandleCentralKeycloakCreation(new UserCreationRoleDataIdpInfo(cu.FirstName!, cu.LastName!, cu.Email!, roleData, string.Empty, string.Empty, UserStatusId.ACTIVE, true), cu.CompanyUserId, cu.CompanyName, cu.Bpn, null, cu.ProviderLinkData.Select(x => new IdentityProviderLink(x.Alias!, x.ProviderUserId, x.UserName)), userRepository, userRoleRepository).ConfigureAwait(false);
}
catch (Exception e)
{
@@ -95,10 +112,29 @@ public NetworkRegistrationHandler(
}
}
+ await SendMails(companyAssignedIdentityProviders.Select(x => new UserMailInformation(x.Email!, x.FirstName, x.LastName, x.ProviderLinkData.Select(pld => pld.Alias!))), ospName).ConfigureAwait(false);
return new ValueTuple?, ProcessStepStatusId, bool, string?>(
null,
ProcessStepStatusId.DONE,
false,
null);
}
+
+ private async Task SendMails(IEnumerable companyUserWithRoleIdForCompany, string ospName)
+ {
+ var templates = Enumerable.Repeat("OspWelcomeMail", 1);
+ foreach (var (receiver, firstName, lastName, idpAliasse) in companyUserWithRoleIdForCompany)
+ {
+ var userName = string.Join(" ", firstName, lastName);
+ var mailParameters = new Dictionary
+ {
+ { "userName", !string.IsNullOrWhiteSpace(userName) ? userName : receiver },
+ { "hostname", _settings.BasePortalAddress },
+ { "osp", ospName },
+ { "url", _settings.BasePortalAddress },
+ { "idpAliasse", string.Join(",", idpAliasse) }
+ };
+ await _mailingService.SendMails(receiver, mailParameters, templates).ConfigureAwait(false);
+ }
+ }
}
diff --git a/src/processes/NetworkRegistration.Library/NetworkRegistrationProcessHelper.cs b/src/processes/NetworkRegistration.Library/NetworkRegistrationProcessHelper.cs
index afd1380dd2..c5c968c5aa 100644
--- a/src/processes/NetworkRegistration.Library/NetworkRegistrationProcessHelper.cs
+++ b/src/processes/NetworkRegistration.Library/NetworkRegistrationProcessHelper.cs
@@ -1,5 +1,4 @@
/********************************************************************************
- * Copyright (c) 2021, 2023 BMW Group AG
* Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
@@ -20,7 +19,6 @@
using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess;
-using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums;
using Org.Eclipse.TractusX.Portal.Backend.Processes.Library;
diff --git a/src/provisioning/Provisioning.Library/Service/IUserProvisioningService.cs b/src/provisioning/Provisioning.Library/Service/IUserProvisioningService.cs
index f686052f71..3dc302b5db 100644
--- a/src/provisioning/Provisioning.Library/Service/IUserProvisioningService.cs
+++ b/src/provisioning/Provisioning.Library/Service/IUserProvisioningService.cs
@@ -22,7 +22,6 @@
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Entities;
-using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums;
using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library.Models;
namespace Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library.Service;
@@ -30,10 +29,12 @@ 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);
+ 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);
Task GetIdentityProviderDisplayName(string idpAlias);
IAsyncEnumerable GetRoleDatas(IEnumerable clientRoles);
Task> GetOwnCompanyPortalRoleDatas(string clientId, IEnumerable roles, Guid companyId);
- Task CreateCentralUserWithProviderLinks(Guid companyUserId, UserCreationRoleDataIdpInfo user, string companyName, string? businessPartnerNumber, IEnumerable identityProviderLinks);
+ Task<(Identity? identity, Guid companyUserId)> GetOrCreateCompanyUser(IUserRepository userRepository, string alias, UserCreationRoleDataIdpInfo user, Guid companyId, Guid identityProviderId, string? businessPartnerNumber);
+ Task AssignRolesToNewUserAsync(IUserRolesRepository userRolesRepository, IEnumerable roleDatas, (string UserEntityId, Guid CompanyUserId) userdata);
}
diff --git a/src/provisioning/Provisioning.Library/Service/UserProvisioningService.cs b/src/provisioning/Provisioning.Library/Service/UserProvisioningService.cs
index eb94a9db03..8958418f68 100644
--- a/src/provisioning/Provisioning.Library/Service/UserProvisioningService.cs
+++ b/src/provisioning/Provisioning.Library/Service/UserProvisioningService.cs
@@ -36,6 +36,7 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library.Service;
public class UserProvisioningService : IUserProvisioningService
{
+ private static readonly IEnumerable ValidCompanyUserStatusIds = new[] { UserStatusId.ACTIVE, UserStatusId.INACTIVE, UserStatusId.PENDING };
private readonly IProvisioningManager _provisioningManager;
private readonly IPortalRepositories _portalRepositories;
@@ -68,35 +69,26 @@ public UserProvisioningService(IProvisioningManager provisioningManager, IPortal
Exception? error = null;
var nextPassword = passwordProvider.NextOptionalPassword();
-
try
{
- var (identity, companyUserId) = await GetOrCreateCompanyUser(userRepository, alias, user, companyId, businessPartnerNumber);
-
- cancellationToken.ThrowIfCancellationRequested();
+ var (identity, companyUserId) = await GetOrCreateCompanyUser(userRepository, alias, user, companyId, identityProviderId, businessPartnerNumber);
- userRepository.AddCompanyUserAssignedIdentityProvider(companyUserId, identityProviderId, user.UserId, user.UserName);
- var providerUserId = await CreateSharedIdpUserOrReturnUserId(user, alias, nextPassword, isSharedIdp).ConfigureAwait(false);
- var centralUserId = await CreateCentralUserWithProviderLinks(companyUserId, user, companyName, businessPartnerNumber, Enumerable.Repeat(new IdentityProviderLink(alias, providerUserId, user.UserName), 1));
- userdata = new(centralUserId, companyUserId);
- if (identity == null)
+ userdata.CompanyUserId = companyUserId;
+ if (!string.IsNullOrWhiteSpace(identity?.UserEntityId))
{
- userRepository.AttachAndModifyIdentity(companyUserId, null, cu =>
- {
- cu.UserEntityId = centralUserId;
- });
- }
- else
- {
- identity.UserEntityId = centralUserId;
+ userdata.UserEntityId = identity.UserEntityId;
}
- await AssignRolesToNewUserAsync(userRolesRepository, user.RoleDatas, userdata).ConfigureAwait(false);
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var providerUserId = await CreateSharedIdpUserOrReturnUserId(user, alias, nextPassword, isSharedIdp).ConfigureAwait(false);
+ await HandleCentralKeycloakCreation(user, companyUserId, companyName, businessPartnerNumber, identity, Enumerable.Repeat(new IdentityProviderLink(alias, providerUserId, user.UserName), 1), userRepository, userRolesRepository).ConfigureAwait(false);
}
catch (Exception e) when (e is not OperationCanceledException)
{
error = e;
}
+
if (userdata == default && error == null)
{
error = new UnexpectedConditionException($"failed to create companyUser for provider userid {user.UserId}, username {user.UserName} while not throwing any error");
@@ -108,7 +100,27 @@ public UserProvisioningService(IProvisioningManager provisioningManager, IPortal
}
}
- public async Task CreateCentralUserWithProviderLinks(Guid companyUserId, UserCreationRoleDataIdpInfo user, string companyName, string? businessPartnerNumber, IEnumerable identityProviderLinks)
+ public async Task HandleCentralKeycloakCreation(UserCreationRoleDataIdpInfo user, Guid companyUserId, string companyName, string? businessPartnerNumber, Identity? identity, IEnumerable identityProviderLinks, IUserRepository userRepository, IUserRolesRepository userRolesRepository)
+ {
+ var centralUserId = await CreateCentralUserWithProviderLinks(companyUserId, user, companyName, businessPartnerNumber, identityProviderLinks).ConfigureAwait(false);
+ if (identity == null)
+ {
+ userRepository.AttachAndModifyIdentity(companyUserId, null, cu =>
+ {
+ cu.UserEntityId = centralUserId;
+ cu.UserStatusId = user.UserStatusId;
+ });
+ }
+ else
+ {
+ identity.UserEntityId = centralUserId;
+ identity.UserStatusId = user.UserStatusId;
+ }
+
+ await AssignRolesToNewUserAsync(userRolesRepository, user.RoleDatas, (centralUserId, companyUserId)).ConfigureAwait(false);
+ }
+
+ private async Task CreateCentralUserWithProviderLinks(Guid companyUserId, UserCreationRoleDataIdpInfo user, string companyName, string? businessPartnerNumber, IEnumerable identityProviderLinks)
{
var centralUserId = await _provisioningManager.CreateCentralUserAsync(
new UserProfile(
@@ -133,11 +145,12 @@ await _provisioningManager.AddProviderUserLinkToCentralUserAsync(centralUserId,
return centralUserId;
}
- private async Task<(Identity? identity, Guid companyUserId)> GetOrCreateCompanyUser(
+ public async Task<(Identity? identity, Guid companyUserId)> GetOrCreateCompanyUser(
IUserRepository userRepository,
string alias,
UserCreationRoleDataIdpInfo user,
Guid companyId,
+ Guid identityProviderId,
string? businessPartnerNumber)
{
var businessPartnerRepository = _portalRepositories.GetInstance();
@@ -156,6 +169,8 @@ await _provisioningManager.AddProviderUserLinkToCentralUserAsync(centralUserId,
businessPartnerRepository.CreateCompanyUserAssignedBusinessPartner(companyUserId, businessPartnerNumber);
}
+ userRepository.AddCompanyUserAssignedIdentityProvider(companyUserId, identityProviderId, user.UserId, user.UserName);
+
return (identity, companyUserId);
}
@@ -191,6 +206,7 @@ private Task CreateSharedIdpUserOrReturnUserId(UserCreationRoleDataIdpIn
{
throw new ControllerArgumentException($"user {companyUserId} does not exist");
}
+
var (company, companyUser, identityProvider) = result;
if (identityProvider.IdpAlias == null)
{
@@ -216,15 +232,18 @@ private Task CreateSharedIdpUserOrReturnUserId(UserCreationRoleDataIdpIn
? new ControllerArgumentException($"user {companyUserId} does not exist")
: new ControllerArgumentException($"user {companyUserId} is not associated with application {applicationId}");
}
+
var (company, companyUser, idpAliase) = result;
if (company.CompanyName == null)
{
throw new ConflictException($"assertion failed: companyName of company {company.CompanyId} should never be null here");
}
+
if (!idpAliase.Any())
{
throw new ConflictException($"user {companyUserId} is not associated with any shared idp");
}
+
if (idpAliase.Count() > 1)
{
throw new ConflictException($"user {companyUserId} is associated with more than one shared idp");
@@ -243,14 +262,17 @@ private static string CreateNameString(string? firstName, string? lastName, stri
{
sb.Append(firstName);
}
+
if (lastName != null)
{
sb.AppendFormat((firstName == null ? "{0}" : ", {0}"), lastName);
}
+
if (email != null)
{
sb.AppendFormat((firstName == null && lastName == null) ? "{0}" : " ({0})", email);
}
+
return firstName == null && lastName == null && email == null ? "Dear User" : sb.ToString();
}
@@ -261,9 +283,7 @@ private async Task ValidateDuplicateIdpUsersAsync(IUserRepository userRepo
{
var existingCompanyUserId = Guid.Empty;
- var validCompanyUserStatusIds = new[] { UserStatusId.ACTIVE, UserStatusId.INACTIVE };
-
- await foreach (var (userEntityId, companyUserId) in userRepository.GetMatchingCompanyIamUsersByNameEmail(user.FirstName, user.LastName, user.Email, companyId, validCompanyUserStatusIds).ConfigureAwait(false))
+ await foreach (var (userEntityId, companyUserId) in userRepository.GetMatchingCompanyIamUsersByNameEmail(user.FirstName, user.LastName, user.Email, companyId, ValidCompanyUserStatusIds).ConfigureAwait(false))
{
if (userEntityId == null)
{
@@ -271,8 +291,10 @@ private async Task ValidateDuplicateIdpUsersAsync(IUserRepository userRepo
{
existingCompanyUserId = companyUserId;
}
+
continue;
}
+
try
{
if (await _provisioningManager.GetProviderUserLinkDataForCentralUserIdAsync(userEntityId).AnyAsync(link =>
@@ -286,10 +308,11 @@ private async Task ValidateDuplicateIdpUsersAsync(IUserRepository userRepo
// when searching for duplicates this is not a validation-error
}
}
+
return existingCompanyUserId;
}
- private async Task AssignRolesToNewUserAsync(IUserRolesRepository userRolesRepository, IEnumerable roleDatas, (string UserEntityId, Guid CompanyUserId) userdata)
+ public async Task AssignRolesToNewUserAsync(IUserRolesRepository userRolesRepository, IEnumerable roleDatas, (string UserEntityId, Guid CompanyUserId) userdata)
{
if (roleDatas.Any())
{
@@ -304,6 +327,7 @@ private async Task AssignRolesToNewUserAsync(IUserRolesRepository userRolesRepos
var roleId = roleDatas.First(roleInfo => roleInfo.ClientClientId == assigned.Client && roleInfo.UserRoleText == role).UserRoleId;
userRolesRepository.CreateIdentityAssignedRole(userdata.CompanyUserId, roleId);
}
+
messages.AddRange(clientRoleNames[assigned.Client].Except(assigned.Roles).Select(roleName => $"clientId: {assigned.Client}, role: {roleName}"));
}
diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/NetworkBusinessLogicTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/NetworkBusinessLogicTests.cs
index d449c14eed..5c32ed6216 100644
--- a/tests/administration/Administration.Service.Tests/BusinessLogic/NetworkBusinessLogicTests.cs
+++ b/tests/administration/Administration.Service.Tests/BusinessLogic/NetworkBusinessLogicTests.cs
@@ -23,7 +23,6 @@
using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Models;
using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling;
using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Configuration;
-using Org.Eclipse.TractusX.Portal.Backend.Mailing.SendMail;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories;
@@ -52,7 +51,6 @@ public class NetworkBusinessLogicTests
private readonly IIdentityService _identityService;
private readonly IUserProvisioningService _userProvisioningService;
private readonly INetworkRegistrationProcessHelper _networkRegistrationProcessHelper;
- private readonly IMailingService _mailingService;
private readonly IPortalRepositories _portalRepositories;
private readonly ICompanyRepository _companyRepository;
@@ -75,7 +73,6 @@ public NetworkBusinessLogicTests()
_portalRepositories = A.Fake();
_identityService = A.Fake();
_networkRegistrationProcessHelper = A.Fake();
- _mailingService = A.Fake();
_companyRepository = A.Fake();
_companyRolesRepository = A.Fake();
@@ -102,7 +99,7 @@ public NetworkBusinessLogicTests()
A.CallTo(() => _portalRepositories.GetInstance()).Returns(_identityProviderRepository);
A.CallTo(() => _portalRepositories.GetInstance()).Returns(_countryRepository);
- _sut = new NetworkBusinessLogic(_portalRepositories, _identityService, _userProvisioningService, _networkRegistrationProcessHelper, _mailingService, options);
+ _sut = new NetworkBusinessLogic(_portalRepositories, _identityService, _userProvisioningService, _networkRegistrationProcessHelper, options);
SetupRepos();
}
@@ -362,8 +359,8 @@ public async Task HandlePartnerRegistration_WithUserCreationThrowsException_Thro
A.CallTo(() => _processStepRepository.CreateProcess(ProcessTypeId.PARTNER_REGISTRATION))
.Returns(new Process(processId, default, default));
- A.CallTo(() => _userProvisioningService.CreateOwnCompanyIdpUsersAsync(A._, A>._, A._))
- .Returns(new[] { (Guid.Empty, "", (string?)null, (Exception?)new UnexpectedConditionException("Test")) }.ToAsyncEnumerable());
+ A.CallTo(() => _userProvisioningService.GetOrCreateCompanyUser(A._, A._, A._, A._, A._, "BPNL00000001TEST"))
+ .Throws(new UnexpectedConditionException("Test message"));
// Act
async Task Act() => await _sut.HandlePartnerRegistration(data).ConfigureAwait(false);
@@ -528,13 +525,11 @@ public async Task HandlePartnerRegistration_WithIdpNotSetAndOnlyOneIdp_CallsExpe
x.ProcessId == newProcessId &&
x.ApplicationId == newApplicationId);
- A.CallTo(() => _userProvisioningService.CreateOwnCompanyIdpUsersAsync(A._, A>._, A._))
+ A.CallTo(() => _userProvisioningService.GetOrCreateCompanyUser(A._, "test-alias", A._, newCompanyId, IdpId, Bpnl))
.MustHaveHappenedOnceExactly();
A.CallTo(() => _identityProviderRepository.CreateCompanyIdentityProviders(A>.That.IsSameSequenceAs(new[] { new ValueTuple(newCompanyId, IdpId) })))
.MustHaveHappenedOnceExactly();
A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappenedOnceExactly();
- A.CallTo(() => _mailingService.SendMails(A._, A>._, A>.That.IsSameSequenceAs(new[] { "OspWelcomeMail" })))
- .MustHaveHappenedOnceExactly();
}
[Fact]
@@ -628,8 +623,6 @@ public async Task HandlePartnerRegistration_WithValidData_CallsExpected()
{
networkRegistrations.Add(new NetworkRegistration(Guid.NewGuid(), externalId, companyId, pId, ospId, companyApplicationId, DateTimeOffset.UtcNow));
});
- A.CallTo(() => _userProvisioningService.CreateOwnCompanyIdpUsersAsync(A._, A>._, A._))
- .Returns(new[] { (Guid.NewGuid(), "ironman", (string?)"testpw", (Exception?)null) }.ToAsyncEnumerable());
// Act
await _sut.HandlePartnerRegistration(data).ConfigureAwait(false);
@@ -663,13 +656,11 @@ public async Task HandlePartnerRegistration_WithValidData_CallsExpected()
x.ProcessId == newProcessId &&
x.ApplicationId == newApplicationId);
- A.CallTo(() => _userProvisioningService.CreateOwnCompanyIdpUsersAsync(A._, A>._, A._))
+ A.CallTo(() => _userProvisioningService.GetOrCreateCompanyUser(A._, "test-alias", A._, newCompanyId, IdpId, Bpnl))
.MustHaveHappenedOnceExactly();
A.CallTo(() => _identityProviderRepository.CreateCompanyIdentityProviders(A>.That.IsSameSequenceAs(new[] { new ValueTuple(newCompanyId, IdpId) })))
.MustHaveHappenedOnceExactly();
A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappenedOnceExactly();
- A.CallTo(() => _mailingService.SendMails(A._, A>._, A>.That.IsSameSequenceAs(new[] { "OspWelcomeMail" })))
- .MustHaveHappenedOnceExactly();
}
#endregion
diff --git a/tests/processes/NetworkRegistration.Library.Tests/NetworkRegistrationHandlerTests.cs b/tests/processes/NetworkRegistration.Library.Tests/NetworkRegistrationHandlerTests.cs
index 93fe9a3710..6d7b38c446 100644
--- a/tests/processes/NetworkRegistration.Library.Tests/NetworkRegistrationHandlerTests.cs
+++ b/tests/processes/NetworkRegistration.Library.Tests/NetworkRegistrationHandlerTests.cs
@@ -21,6 +21,7 @@
using Microsoft.Extensions.Options;
using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling;
using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Configuration;
+using Org.Eclipse.TractusX.Portal.Backend.Mailing.SendMail;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models;
using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories;
@@ -42,28 +43,47 @@ public class NetworkRegistrationHandlerTests
private readonly IProvisioningManager _provisioningManger;
private readonly IUserRepository _userRepository;
+ private readonly INetworkRepository _networkRepository;
private readonly NetworkRegistrationHandler _sut;
- private readonly NetworkRegistrationProcessSettings _settings;
+ private readonly IMailingService _mailingService;
public NetworkRegistrationHandlerTests()
{
var portalRepositories = A.Fake();
_userRepository = A.Fake();
+ _networkRepository = A.Fake();
_userProvisioningService = A.Fake();
_provisioningManger = A.Fake();
+ _mailingService = A.Fake();
- _settings = new NetworkRegistrationProcessSettings
+ var settings = new NetworkRegistrationProcessSettings
{
InitialRoles = Enumerable.Repeat(new UserRoleConfig("cl1", Enumerable.Repeat("Company Admin", 1)), 1)
};
var options = A.Fake>();
- A.CallTo(() => options.Value).Returns(_settings);
+ A.CallTo(() => options.Value).Returns(settings);
A.CallTo(() => portalRepositories.GetInstance()).Returns(_userRepository);
+ A.CallTo(() => portalRepositories.GetInstance()).Returns(_networkRepository);
- _sut = new NetworkRegistrationHandler(portalRepositories, _userProvisioningService, _provisioningManger, options);
+ _sut = new NetworkRegistrationHandler(portalRepositories, _userProvisioningService, _provisioningManger, _mailingService, options);
+ }
+
+ [Fact]
+ public async Task SynchronizeUser_WithoutOspName_ThrowsUnexpectedConditionException()
+ {
+ // Arrange
+ A.CallTo(() => _networkRepository.GetOspCompanyName(NetworkRegistrationId))
+ .Returns((string?)null);
+
+ // Act
+ async Task Act() => await _sut.SynchronizeUser(NetworkRegistrationId).ConfigureAwait(false);
+
+ // Assert
+ var ex = await Assert.ThrowsAsync(Act);
+ ex.Message.Should().Be("Onboarding Service Provider name must be set");
}
[Theory]
@@ -78,6 +98,8 @@ public async Task SynchronizeUser_WithUserDataNull_ThrowsConflictException(strin
"123456789", "Test Company", "BPNL00000001TEST",
Enumerable.Repeat(new ProviderLinkData("ironman", "idp1", "id1234"), 1));
+ A.CallTo(() => _networkRepository.GetOspCompanyName(NetworkRegistrationId))
+ .Returns("Onboarding Service Provider");
A.CallTo(() => _userRepository.GetUserAssignedIdentityProviderForNetworkRegistration(NetworkRegistrationId))
.Returns(new[]
{
@@ -102,10 +124,12 @@ public async Task SynchronizeUser_WithAliasNull_ThrowsConflictException()
var user1 = new CompanyUserIdentityProviderProcessData(user1Id, "tony", "stark", "tony@stark.com", "123456789", "Test Company", "BPNL00000001TEST",
Enumerable.Repeat(new ProviderLinkData("ironman", null, "id1234"), 1));
+ A.CallTo(() => _networkRepository.GetOspCompanyName(NetworkRegistrationId))
+ .Returns("Onboarding Service Provider");
A.CallTo(() => _userRepository.GetUserAssignedIdentityProviderForNetworkRegistration(NetworkRegistrationId))
.Returns(new[]
{
- user1,
+ user1
}.ToAsyncEnumerable());
A.CallTo(() => _userProvisioningService.GetRoleDatas(A>._))
.Returns(Enumerable.Repeat(new UserRoleData(UserRoleIds, "cl1", "Company Admin"), 1).ToAsyncEnumerable());
@@ -123,7 +147,6 @@ public async Task SynchronizeUser_WithValidData_ReturnsExpected()
{
// Arrange
var user1Id = Guid.NewGuid().ToString();
- var user2Id = Guid.NewGuid().ToString();
var user1 = new CompanyUserIdentityProviderProcessData(Guid.NewGuid(), "tony", "stark", "tony@stark.com",
"123456789", "Test Company", "BPNL00000001TEST",
Enumerable.Repeat(new ProviderLinkData("ironman", "idp1", "id1234"), 1));
@@ -131,14 +154,14 @@ public async Task SynchronizeUser_WithValidData_ReturnsExpected()
"steven@strange.com", "987654321", "Test Company", "BPNL00000001TEST",
Enumerable.Repeat(new ProviderLinkData("drstrange", "idp1", "id9876"), 1));
+ A.CallTo(() => _networkRepository.GetOspCompanyName(NetworkRegistrationId))
+ .Returns("Onboarding Service Provider");
A.CallTo(() => _userRepository.GetUserAssignedIdentityProviderForNetworkRegistration(NetworkRegistrationId))
.Returns(new[]
{
user1,
user2
}.ToAsyncEnumerable());
- A.CallTo(() => _userProvisioningService.CreateCentralUserWithProviderLinks(user2.CompanyUserId, A._, A._, A._, A>._))
- .Returns(user2Id);
A.CallTo(() => _userProvisioningService.GetRoleDatas(A>._))
.Returns(Enumerable.Repeat(new UserRoleData(UserRoleIds, "cl1", "Company Admin"), 1).ToAsyncEnumerable());
A.CallTo(() => _provisioningManger.GetUserByUserName(user1.CompanyUserId.ToString())).Returns(user1Id);
@@ -148,12 +171,19 @@ public async Task SynchronizeUser_WithValidData_ReturnsExpected()
var result = await _sut.SynchronizeUser(NetworkRegistrationId).ConfigureAwait(false);
// Assert
- A.CallTo(() => _userProvisioningService.CreateCentralUserWithProviderLinks(user2.CompanyUserId, A._, A._, A._, A>._))
+ A.CallTo(() => _userProvisioningService.HandleCentralKeycloakCreation(A._, user1.CompanyUserId, A._, A._, null, A>._, A._, A._))
+ .MustNotHaveHappened();
+ A.CallTo(() => _userProvisioningService.HandleCentralKeycloakCreation(A._, user2.CompanyUserId, A._, A._, null, A>._, A._, A._))
.MustHaveHappenedOnceExactly();
A.CallTo(() => _userRepository.AttachAndModifyIdentity(user1.CompanyUserId, A>._, A>._))
.MustHaveHappenedOnceExactly();
A.CallTo(() => _userRepository.AttachAndModifyIdentity(user2.CompanyUserId, A>._, A>._))
+ .MustNotHaveHappened();
+ A.CallTo(() => _mailingService.SendMails("tony@stark.com", A>._, A>._))
+ .MustHaveHappenedOnceExactly();
+ A.CallTo(() => _mailingService.SendMails("steven@strange.com", A>._, A>._))
.MustHaveHappenedOnceExactly();
+
result.modified.Should().BeFalse();
result.processMessage.Should().BeNull();
result.stepStatusId.Should().Be(ProcessStepStatusId.DONE);