From 7bac0591f654d0dbc0924c6b1338348bb5f3bfaf Mon Sep 17 00:00:00 2001 From: leandro-cavalcante <162341380+leandro-cavalcante@users.noreply.github.com> Date: Mon, 30 Jun 2025 09:03:38 +0200 Subject: [PATCH 01/38] feat: Changed onboard to add the wallet from the company (#3) --- .../RegistrationBusinessLogic.cs | 10 +- .../Repositories/CompanyRepository.cs | 25 ++++ .../Repositories/ICompanyRepository.cs | 6 +- .../Registration.Common/RegistrationData.cs | 3 +- .../RegistrationBusinessLogic.cs | 11 +- .../Model/CompanyDetailData.cs | 5 +- .../RegistrationBusinessLogicTest.cs | 51 +++++++++ .../RegistrationBusinessLogicTest.cs | 108 ++++++++++++++++++ 8 files changed, 212 insertions(+), 7 deletions(-) diff --git a/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs index 76908fb009..ee5e4171ff 100644 --- a/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs @@ -298,6 +298,11 @@ private async Task UpdateCompanyBpnInternal(Guid applicationId, string bpn) private ProcessStepTypeId CreateWalletStep() => _settings.UseDimWallet ? ProcessStepTypeId.CREATE_DIM_WALLET : ProcessStepTypeId.CREATE_IDENTITY_WALLET; + private async Task CreateWalletOrBpnCredentialStepAsync(Guid applicationId) + { + var isWalletCustomerProvider = await portalRepositories.GetInstance().IsBringYourOwnWallet(applicationId); + return isWalletCustomerProvider ? ProcessStepTypeId.REQUEST_BPN_CREDENTIAL : CreateWalletStep(); + } /// public async Task ProcessClearinghouseResponseAsync(ClearinghouseResponseData data, string bpn, CancellationToken cancellationToken) { @@ -428,6 +433,7 @@ public async Task ProcessClearinghouseSelfDescription(SelfDescriptionResponseDat /// public async Task ApproveRegistrationVerification(Guid applicationId) { + var createWalletOrBpnCredentalStep = await CreateWalletOrBpnCredentialStepAsync(applicationId); var context = await checklistService .VerifyChecklistEntryAndProcessSteps( applicationId, @@ -435,7 +441,7 @@ public async Task ApproveRegistrationVerification(Guid applicationId) [ApplicationChecklistEntryStatusId.TO_DO], ProcessStepTypeId.MANUAL_VERIFY_REGISTRATION, [ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER], - [CreateWalletStep()]) + [createWalletOrBpnCredentalStep]) .ConfigureAwait(ConfigureAwaitOptions.None); var businessPartnerSuccess = context.Checklist[ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER] == new ValueTuple(ApplicationChecklistEntryStatusId.DONE, null); @@ -448,7 +454,7 @@ public async Task ApproveRegistrationVerification(Guid applicationId) entry.ApplicationChecklistEntryStatusId = ApplicationChecklistEntryStatusId.DONE; }, businessPartnerSuccess - ? new[] { CreateWalletStep() } + ? new[] { createWalletOrBpnCredentalStep } : null); await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs index 1befe0b478..2b0f53485c 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs @@ -67,7 +67,32 @@ Address ICompanyRepository.CreateAddress(string city, string streetname, string setOptionalParameters?.Invoke(address); return context.Addresses.Add(address).Entity; } + public void CreateCustomerWallet(Guid companyId, string did) + { + var wallet = new CompanyWalletData( + Guid.NewGuid(), + companyId, + did, + JsonDocument.Parse("{}"), + "BYOWCLIENT_ID", + new byte[1], + new byte[1], + default, + "NOT_SET"); + + context.CompanyWalletDatas.Add(wallet); + } + public Task IsBringYourOwnWallet(Guid applicationId) + { + return context.CompanyApplications + .Where(app => app.Id == applicationId) + .Join(context.CompanyWalletDatas, + app => app.CompanyId, + wallet => wallet.CompanyId, + (app, wallet) => wallet) + .AnyAsync(wallet => wallet.ClientId == "BYOWCLIENT_ID"); + } public void AttachAndModifyAddress(Guid addressId, Action
? initialize, Action
modify) { var address = new Address(addressId, null!, null!, null!, null!, default); diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs index 34a6ea4846..b2d6bf5053 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs @@ -17,6 +17,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; using Org.Eclipse.TractusX.Portal.Backend.Framework.Models; using Org.Eclipse.TractusX.Portal.Backend.Framework.Processes.Library.Models; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; @@ -41,8 +42,11 @@ public interface ICompanyRepository void AttachAndModifyCompany(Guid companyId, Action? initialize, Action modify); - Address CreateAddress(string city, string streetname, string region, string countryAlpha2Code, Action
? setOptionalParameters = null); + void CreateCustomerWallet(Guid companyId, string did); + + Task IsBringYourOwnWallet(Guid applicationId); + Address CreateAddress(string city, string streetname, string region, string countryAlpha2Code, Action
? setOptionalParameters = null); void AttachAndModifyAddress(Guid addressId, Action
? initialize, Action
modify); void CreateUpdateDeleteIdentifiers(Guid companyId, IEnumerable<(UniqueIdentifierId UniqueIdentifierId, string Value)> initialItems, IEnumerable<(UniqueIdentifierId UniqueIdentifierId, string Value)> modifiedItems); diff --git a/src/registration/Registration.Common/RegistrationData.cs b/src/registration/Registration.Common/RegistrationData.cs index 83748f0012..393347d405 100644 --- a/src/registration/Registration.Common/RegistrationData.cs +++ b/src/registration/Registration.Common/RegistrationData.cs @@ -33,7 +33,8 @@ public record RegistrationData( string? StreetAdditional, string? StreetNumber, string? ZipCode, - IEnumerable UniqueIds + IEnumerable UniqueIds, + string? HolderDid = null ); public record CompanyUniqueIdData( diff --git a/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs b/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs index 19fec73a16..e92f105c62 100644 --- a/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs +++ b/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs @@ -43,6 +43,7 @@ using Org.Eclipse.TractusX.Portal.Backend.Registration.Service.Model; using System.Collections.Immutable; using System.Text.RegularExpressions; +using System.Web; namespace Org.Eclipse.TractusX.Portal.Backend.Registration.Service.BusinessLogic; @@ -269,9 +270,16 @@ await companyDetails.ValidateDatabaseData( { await identityProviderProvisioningService.UpdateCompanyNameInSharedIdentityProvider(_identityData.CompanyId, companyDetails.Name).ConfigureAwait(ConfigureAwaitOptions.None); } + if (!string.IsNullOrEmpty(companyDetails.HolderDid)) + { + CreateCustomerWallet(companyDetails.CompanyId, companyDetails.HolderDid, companyRepository); + } await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); } + private static void CreateCustomerWallet(Guid companyId, string did, ICompanyRepository companyRepository) => companyRepository.CreateCustomerWallet( + companyId, + did); private async Task GetAndValidateApplicationData(Guid applicationId, CompanyDetailData companyDetails, IApplicationRepository applicationRepository) { var companyApplicationData = await applicationRepository @@ -352,7 +360,8 @@ private static void ModifyCompany(Guid addressId, CompanyApplicationDetailData i c.Shortname = modifyData.ShortName; c.CompanyStatusId = CompanyStatusId.PENDING; c.AddressId = addressId; - }); + } + ); public Task InviteNewUserAsync(Guid applicationId, UserCreationInfoWithMessage userCreationInfo) { diff --git a/src/registration/Registration.Service/Model/CompanyDetailData.cs b/src/registration/Registration.Service/Model/CompanyDetailData.cs index 023ed51067..831506bcb3 100644 --- a/src/registration/Registration.Service/Model/CompanyDetailData.cs +++ b/src/registration/Registration.Service/Model/CompanyDetailData.cs @@ -33,5 +33,6 @@ public record CompanyDetailData( string? StreetAdditional, string? StreetNumber, string? ZipCode, - IEnumerable UniqueIds -) : RegistrationData(Name, City, StreetName, CountryAlpha2Code, BusinessPartnerNumber, ShortName, Region, StreetAdditional, StreetNumber, ZipCode, UniqueIds); + IEnumerable UniqueIds, + string? HolderDid = null +) : RegistrationData(Name, City, StreetName, CountryAlpha2Code, BusinessPartnerNumber, ShortName, Region, StreetAdditional, StreetNumber, ZipCode, UniqueIds, HolderDid); diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs index acf817e0f4..09b39d2bcc 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs @@ -465,8 +465,59 @@ public async Task SetRegistrationVerification_WithApproval_CallsExpected(bool us .MustHaveHappenedOnceExactly(); } + [Theory] + [InlineData(true, ProcessStepTypeId.REQUEST_BPN_CREDENTIAL)] + [InlineData(false, ProcessStepTypeId.REQUEST_BPN_CREDENTIAL)] + public async Task SetRegistrationVerification_WithApproval_BYOW_CallsExpected(bool useDimWallet, ProcessStepTypeId expectedTypeId) + { + // Arrange + var options = Options.Create(new RegistrationSettings { UseDimWallet = useDimWallet }); + var logic = new RegistrationBusinessLogic(_portalRepositories, options, _checklistService, null!, null!, _dimBusinessLogic, null!, null!, null!, null!); + var entry = new ApplicationChecklistEntry(IdWithBpn, ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.TO_DO, DateTimeOffset.UtcNow); + SetupForApproveRegistrationVerification(entry); + A.CallTo(() => _companyRepository.IsBringYourOwnWallet(IdWithBpn)) + .Returns(true); + // Act + await logic.ApproveRegistrationVerification(IdWithBpn); + + // Assert + A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappenedOnceExactly(); + entry.Comment.Should().BeNull(); + entry.ApplicationChecklistEntryStatusId.Should().Be(ApplicationChecklistEntryStatusId.DONE); + + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, A._, A>._)) + .MustNotHaveHappened(); + A.CallTo(() => _checklistService.FinalizeChecklistEntryAndProcessSteps( + A._, + null, + A>._, + A>.That.Matches(x => x.Count(y => y == expectedTypeId) == 1))) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _companyRepository.IsBringYourOwnWallet(IdWithBpn)).MustHaveHappenedOnceExactly(); + } + [Fact] public async Task SetRegistrationVerification_WithBpnNotDone_CallsExpected() + { + // Arrange + var entry = new ApplicationChecklistEntry(IdWithoutBpn, ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.TO_DO, DateTimeOffset.UtcNow); + SetupForApproveRegistrationVerification(entry); + A.CallTo(() => _companyRepository.IsBringYourOwnWallet(IdWithBpn)) + .Returns(true); + // Act + await _logic.ApproveRegistrationVerification(IdWithoutBpn); + + // Assert + A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappenedOnceExactly(); + entry.Comment.Should().BeNull(); + entry.ApplicationChecklistEntryStatusId.Should().Be(ApplicationChecklistEntryStatusId.DONE); + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, A._, A>._)) + .MustNotHaveHappened(); + A.CallTo(() => _checklistService.FinalizeChecklistEntryAndProcessSteps(A._, null, A>._, null)).MustHaveHappenedOnceExactly(); + } + + [Fact] + public async Task SetRegistrationVerification_BYOW_WithBpnNotDone_CallsExpected() { // Arrange var entry = new ApplicationChecklistEntry(IdWithoutBpn, ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.TO_DO, DateTimeOffset.UtcNow); diff --git a/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs b/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs index 1a6fdea939..b5328863d5 100644 --- a/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs +++ b/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs @@ -989,6 +989,7 @@ public async Task SetCompanyWithAddressAsync_WithoutInitialCompanyAddress_Create .With(x => x.CountryAlpha2Code, _alpha2code) .With(x => x.Region, _region) .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, _vatId)]) + .With(x => x.HolderDid, () => (string?)null) .Create(); var existingData = _fixture.Build() @@ -1054,6 +1055,9 @@ public async Task SetCompanyWithAddressAsync_WithoutInitialCompanyAddress_Create A.CallTo(() => _companyRepository.AttachAndModifyAddress(A._, A>._, A>._)) .MustNotHaveHappened(); A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappenedOnceExactly(); + A.CallTo(() => _companyRepository.CreateCustomerWallet(A._, A._)) + .MustNotHaveHappened(); + company.Should().NotBeNull(); address.Should().NotBeNull(); @@ -1163,6 +1167,110 @@ public async Task SetCompanyWithAddressAsync_WithInitialCompanyAddress_ModifyAdd a.Streetnumber == companyData.StreetNumber && a.Zipcode == companyData.ZipCode); } + [Fact] + public async Task SetCompanyWithAddressAsync_WithoutInitialCompanyAddress_CreatesAddressAndAddCustomerWallet() + { + //Arrange + var applicationId = Guid.NewGuid(); + var companyId = Guid.NewGuid(); + var addressId = Guid.NewGuid(); + var identityData = A.Fake(); + A.CallTo(() => identityData.IdentityId).Returns(Guid.NewGuid()); + A.CallTo(() => identityData.IdentityTypeId).Returns(IdentityTypeId.COMPANY_USER); + A.CallTo(() => identityData.CompanyId).Returns(companyId); + + A.CallTo(() => _identityService.IdentityData).Returns(identityData); + var companyData = _fixture.Build() + .With(x => x.BusinessPartnerNumber, default(string?)) + .With(x => x.CompanyId, companyId) + .With(x => x.CountryAlpha2Code, _alpha2code) + .With(x => x.Region, _region) + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, _vatId)]) + .With(x => x.HolderDid, "https://did.example.com/holder") + .Create(); + + var existingData = _fixture.Build() + .With(x => x.CompanyId, companyId) + .With(x => x.AddressId, default(Guid?)) + .With(x => x.City, default(string?)) + .With(x => x.CountryAlpha2Code, default(string?)) + .With(x => x.Region, default(string?)) + .With(x => x.Streetadditional, default(string?)) + .With(x => x.Streetname, default(string?)) + .With(x => x.Streetnumber, default(string?)) + .With(x => x.Zipcode, default(string?)) + .With(x => x.IsUserOfCompany, true) + .Create(); + + Company? company = null; + Address? address = null; + + var sut = new RegistrationBusinessLogic( + _options, + null!, + _userProvisioningService, + _identityProviderProvisioningService, + null!, + _portalRepositories, + null!, + _identityService, + _dateTimeProvider, + _mailingProcessCreation); + + A.CallTo(() => _applicationRepository.GetCompanyApplicationDetailDataAsync(applicationId, A._, companyId)) + .Returns(existingData); + + A.CallTo(() => _companyRepository.AttachAndModifyCompany(A._, A>._, A>._)) + .Invokes((Guid companyId, Action? initialize, Action modify) => + { + company = new Company(companyId, null!, default, default); + initialize?.Invoke(company); + modify(company); + }); + + A.CallTo(() => _companyRepository.CreateAddress(A._, A._, A._, A._, A>._)) + .ReturnsLazily((string city, string streetName, string region, string alpha2Code, Action
? setParameters) => + { + address = new Address(addressId, city, streetName, region, alpha2Code, default); + setParameters?.Invoke(address); + return address; + }); + + // Act + await sut.SetCompanyDetailDataAsync(applicationId, companyData); + + // Assert + A.CallTo(() => _companyRepository.CreateAddress(A._, A._, A._, A._, A>._)) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _companyRepository.CreateAddress(companyData.City, companyData.StreetName, companyData.Region, + companyData.CountryAlpha2Code, A>._)) + .MustHaveHappened(); + A.CallTo(() => _companyRepository.AttachAndModifyCompany(A._, A>._, A>._)) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _companyRepository.AttachAndModifyCompany(companyId, A>._, A>._)) + .MustHaveHappened(); + A.CallTo(() => _companyRepository.AttachAndModifyAddress(A._, A>._, A>._)) + .MustNotHaveHappened(); + A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappenedOnceExactly(); + A.CallTo(() => _companyRepository.CreateCustomerWallet(A._, A._)) + .MustHaveHappenedOnceExactly(); + + company.Should().NotBeNull(); + address.Should().NotBeNull(); + + company!.Id.Should().Be(companyId); + company.AddressId.Should().Be(addressId); + + address.Should().Match
(a => + a.Id == addressId && + a.City == companyData.City && + a.CountryAlpha2Code == companyData.CountryAlpha2Code && + a.Region == companyData.Region && + a.Streetadditional == companyData.StreetAdditional && + a.Streetname == companyData.StreetName && + a.Streetnumber == companyData.StreetNumber && + a.Zipcode == companyData.ZipCode); + } [Fact] public async Task SetCompanyWithAddressAsync_WithUniqueIdentifiers_CreateModifyDeleteExpected() From d2f90816ffe7cabaab3b2f02e413114d27dd25f4 Mon Sep 17 00:00:00 2001 From: Nitin Date: Mon, 30 Jun 2025 16:41:05 +0530 Subject: [PATCH 02/38] feat: add support for bring your own wallet in issuer credentials --- .../IssuerComponentBusinessLogic.cs | 23 +++++-- .../IssuerComponentBusinessLogicTests.cs | 67 ++++++++++++++++++- 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/src/externalsystems/IssuerComponent.Library/BusinessLogic/IssuerComponentBusinessLogic.cs b/src/externalsystems/IssuerComponent.Library/BusinessLogic/IssuerComponentBusinessLogic.cs index d01a27a38d..9162d9cec4 100644 --- a/src/externalsystems/IssuerComponent.Library/BusinessLogic/IssuerComponentBusinessLogic.cs +++ b/src/externalsystems/IssuerComponent.Library/BusinessLogic/IssuerComponentBusinessLogic.cs @@ -44,6 +44,7 @@ public class IssuerComponentBusinessLogic( { var applicationId = context.ApplicationId; var (exists, holder, businessPartnerNumber, walletInformation) = await repositories.GetInstance().GetBpnlCredentialIformationByApplicationId(applicationId).ConfigureAwait(false); + var isBringYourOwnWallet = await repositories.GetInstance().IsBringYourOwnWallet(applicationId).ConfigureAwait(false); if (!exists) { throw new NotFoundException($"CompanyApplication {applicationId} does not exist"); @@ -68,7 +69,11 @@ public class IssuerComponentBusinessLogic( var secret = CryptoHelper.Decrypt(walletInformation.ClientSecret, walletInformation.InitializationVector, Convert.FromHexString(cryptoConfig.EncryptionKey), cryptoConfig.CipherMode, cryptoConfig.PaddingMode); var callbackUrl = $"{_settings.CallbackBaseUrl}/api/administration/registration/issuer/bpncredential"; - var data = new CreateBpnCredentialRequest(holder, businessPartnerNumber, new TechnicalUserDetails(walletInformation.WalletUrl, walletInformation.ClientId, secret), callbackUrl); + var data = new CreateBpnCredentialRequest(holder, businessPartnerNumber, + isBringYourOwnWallet + ? null + : new TechnicalUserDetails(walletInformation.WalletUrl, walletInformation.ClientId, secret), callbackUrl); + await service.CreateBpnlCredential(data, cancellationToken).ConfigureAwait(false); return new IApplicationChecklistService.WorkerChecklistProcessStepExecutionResult( ProcessStepStatusId.DONE, @@ -112,6 +117,7 @@ public async Task StoreBpnlCredentialResponse(Guid applicationId, IssuerResponse { var applicationId = context.ApplicationId; var (exists, holder, businessPartnerNumber, walletInformation) = await repositories.GetInstance().GetBpnlCredentialIformationByApplicationId(applicationId).ConfigureAwait(false); + var isBringYourOwnWallet = await repositories.GetInstance().IsBringYourOwnWallet(applicationId).ConfigureAwait(false); if (!exists) { throw new NotFoundException($"CompanyApplication {applicationId} does not exist"); @@ -136,7 +142,12 @@ public async Task StoreBpnlCredentialResponse(Guid applicationId, IssuerResponse var secret = CryptoHelper.Decrypt(walletInformation.ClientSecret, walletInformation.InitializationVector, Convert.FromHexString(cryptoConfig.EncryptionKey), cryptoConfig.CipherMode, cryptoConfig.PaddingMode); var callbackUrl = $"{_settings.CallbackBaseUrl}/api/administration/registration/issuer/membershipcredential"; - var data = new CreateMembershipCredentialRequest(holder, businessPartnerNumber, "catena-x", new TechnicalUserDetails(walletInformation.WalletUrl, walletInformation.ClientId, secret), callbackUrl); + + var data = new CreateMembershipCredentialRequest(holder, businessPartnerNumber, "catena-x", + isBringYourOwnWallet + ? null + : new TechnicalUserDetails(walletInformation.WalletUrl, walletInformation.ClientId, secret), callbackUrl); + await service.CreateMembershipCredential(data, cancellationToken).ConfigureAwait(false); return new IApplicationChecklistService.WorkerChecklistProcessStepExecutionResult( ProcessStepStatusId.DONE, @@ -179,6 +190,7 @@ public async Task StoreMembershipCredentialResponse(Guid applicationId, IssuerRe public async Task CreateFrameworkCredentialData(Guid useCaseFrameworkVersionId, string frameworkId, Guid identityId, string token, CancellationToken cancellationToken) { var (holder, businessPartnerNumber, walletInformation) = await repositories.GetInstance().GetWalletData(identityId).ConfigureAwait(false); + var isBringYourOwnWallet = await repositories.GetInstance().IsBringYourOwnWallet(identityId).ConfigureAwait(false); if (holder is null) { throw new ConflictException("The holder must be set"); @@ -196,8 +208,11 @@ public async Task CreateFrameworkCredentialData(Guid useCaseFrameworkVersi var cryptoConfig = _settings.EncryptionConfigs.SingleOrDefault(x => x.Index == walletInformation.EncryptionMode) ?? throw new ConfigurationException($"EncryptionModeIndex {walletInformation.EncryptionMode} is not configured"); var secret = CryptoHelper.Decrypt(walletInformation.ClientSecret, walletInformation.InitializationVector, Convert.FromHexString(cryptoConfig.EncryptionKey), cryptoConfig.CipherMode, cryptoConfig.PaddingMode); - - var data = new CreateFrameworkCredentialRequest(holder, businessPartnerNumber, frameworkId, useCaseFrameworkVersionId, new TechnicalUserDetails(walletInformation.WalletUrl, walletInformation.ClientId, secret), null); + + var data = new CreateFrameworkCredentialRequest(holder, businessPartnerNumber, frameworkId, useCaseFrameworkVersionId, + isBringYourOwnWallet + ? null : + new TechnicalUserDetails(walletInformation.WalletUrl, walletInformation.ClientId, secret), null); return await service.CreateFrameworkCredential(data, token, cancellationToken).ConfigureAwait(false); } } diff --git a/tests/externalsystems/IssuerComponent.Library.Tests/IssuerComponentBusinessLogicTests.cs b/tests/externalsystems/IssuerComponent.Library.Tests/IssuerComponentBusinessLogicTests.cs index 3f14bb93ef..25aee05213 100644 --- a/tests/externalsystems/IssuerComponent.Library.Tests/IssuerComponentBusinessLogicTests.cs +++ b/tests/externalsystems/IssuerComponent.Library.Tests/IssuerComponentBusinessLogicTests.cs @@ -117,6 +117,8 @@ public async Task CreateBpnlCredential_WithValid_CallsExpected() A.CallTo(() => _applicationRepository.GetBpnlCredentialIformationByApplicationId(A._)) .Returns((true, "did:123:testabc", ValidBpn, new WalletInformation("cl1", secret, vector, 0, "https://example.com/wallet"))); + //bring your own wallet false + A.CallTo(() => _companyRepository.IsBringYourOwnWallet(A._)).Returns(false); // Act var result = await _sut.CreateBpnlCredential(context, CancellationToken.None); @@ -133,8 +135,24 @@ public async Task CreateBpnlCredential_WithValid_CallsExpected() x.CallbackUrl == "https://example.org/callback/api/administration/registration/issuer/bpncredential"), A._)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _applicationRepository.GetBpnlCredentialIformationByApplicationId(IdWithBpn)) + + //bring your own wallet true + A.CallTo(() => _companyRepository.IsBringYourOwnWallet(A._)).Returns(true); + result = await _sut.CreateBpnlCredential(context, CancellationToken.None); + + A.CallTo(() => _issuerComponentService + .CreateBpnlCredential( + A.That.Matches(x => + x.Holder == "did:123:testabc" && + x.BusinessPartnerNumber == ValidBpn && + x.TechnicalUserDetails == null + ), + A._)) .MustHaveHappenedOnceExactly(); + + //call should be 2 times now + A.CallTo(() => _applicationRepository.GetBpnlCredentialIformationByApplicationId(IdWithBpn)) + .MustHaveHappenedTwiceExactly(); result.StepStatusId.Should().Be(ProcessStepStatusId.DONE); result.ScheduleStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.AWAIT_BPN_CREDENTIAL_RESPONSE); result.Modified.Should().BeTrue(); @@ -335,6 +353,9 @@ public async Task CreateMembershipCredential_WithValid_CallsExpected() A.CallTo(() => _applicationRepository.GetBpnlCredentialIformationByApplicationId(A._)) .Returns((true, "did:123:testabc", ValidBpn, new WalletInformation("cl1", secret, vector, 0, "https://example.com/wallet"))); + //bring your own wallet false + A.CallTo(() => _companyRepository.IsBringYourOwnWallet(A._)).Returns(false); + // Act var result = await _sut.CreateMembershipCredential(context, CancellationToken.None); @@ -352,8 +373,24 @@ public async Task CreateMembershipCredential_WithValid_CallsExpected() ), A._)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _applicationRepository.GetBpnlCredentialIformationByApplicationId(IdWithBpn)) + + //bring your own wallet false + A.CallTo(() => _companyRepository.IsBringYourOwnWallet(A._)).Returns(true); + + // Act + result = await _sut.CreateMembershipCredential(context, CancellationToken.None); + A.CallTo(() => _issuerComponentService + .CreateMembershipCredential( + A.That.Matches(x => + x.Holder == "did:123:testabc" && + x.HolderBpn == ValidBpn && + x.TechnicalUserDetails == null + ), + A._)) .MustHaveHappenedOnceExactly(); + //call should be 2 times now + A.CallTo(() => _applicationRepository.GetBpnlCredentialIformationByApplicationId(IdWithBpn)) + .MustHaveHappenedTwiceExactly(); result.StepStatusId.Should().Be(ProcessStepStatusId.DONE); result.ScheduleStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.AWAIT_MEMBERSHIP_CREDENTIAL_RESPONSE); result.Modified.Should().BeTrue(); @@ -549,6 +586,9 @@ public async Task CreateFrameworkCredential_WithValid_CallsExpected() A.CallTo(() => _issuerComponentService.CreateFrameworkCredential(A._, A._, A._)) .Returns(credentialId); + //bring your own wallet false + A.CallTo(() => _companyRepository.IsBringYourOwnWallet(A._)).Returns(false); + // Act var result = await _sut.CreateFrameworkCredentialData(useCaseFrameworkVersionId, "TRACEABILITY_FRAMEWORK", identityId, Token, CancellationToken.None); @@ -567,8 +607,29 @@ public async Task CreateFrameworkCredential_WithValid_CallsExpected() Token, A._)) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _companyRepository.GetWalletData(identityId)) + + //bring your own wallet true + A.CallTo(() => _companyRepository.IsBringYourOwnWallet(A._)).Returns(true); + + // Act + result = await _sut.CreateFrameworkCredentialData(useCaseFrameworkVersionId, "TRACEABILITY_FRAMEWORK", identityId, Token, CancellationToken.None); + + //assert + A.CallTo(() => _issuerComponentService + .CreateFrameworkCredential( + A.That.Matches(x => + x.Holder == "did:123:testabc" && + x.HolderBpn == ValidBpn && + x.TechnicalUserDetails == null && + x.CallbackUrl == null + ), + Token, + A._)) .MustHaveHappenedOnceExactly(); + + //call is 2 times + A.CallTo(() => _companyRepository.GetWalletData(identityId)) + .MustHaveHappenedTwiceExactly(); result.Should().Be(credentialId); } From f84b87a371b63e8082491761051408198768f934 Mon Sep 17 00:00:00 2001 From: leandro-cavalcante <162341380+leandro-cavalcante@users.noreply.github.com> Date: Fri, 4 Jul 2025 08:50:15 +0200 Subject: [PATCH 03/38] feat: CS 3640 add controller to validate did (#2) Co-authored-by: Shumaila Malik Co-authored-by: Nitin --- docs/api/administration-service.yaml | 3 + docs/api/registration-service.yaml | 74 +++++++ src/Portal.Backend.sln | 126 +++++++----- ...alDidResolverServiceCollectionExtension.cs | 56 +++++ .../UniversalDidResolverSettings.cs | 27 +++ .../IUniversalDidResolverService.cs | 37 ++++ .../Models/DidValidationResult.cs | 34 +++ .../Schemas/DidDocument.schema.json | 89 ++++++++ .../UniversalDidResolver.Library.csproj | 40 ++++ .../UniversalDidResolverService.cs | 83 ++++++++ .../Models/BringYourOwnWalletClientFields.cs | 8 + .../Repositories/CompanyRepository.cs | 6 +- .../BringYourOwnWalletBusinessLogic.cs | 47 +++++ .../IBringYourOwnWalletBusinessLogic.cs | 26 +++ .../BringYourOwnWalletController.cs | 45 ++++ .../Registration.Service/Program.cs | 7 +- .../Registration.Service.csproj | 1 + .../UniversalDidResolver.Library.Tests.csproj | 41 ++++ .../UniversalDidResolverServiceTest.cs | 193 ++++++++++++++++++ .../CompanyRepositoryTests.cs | 47 ++++- .../Seeder/Data/companies.unittest.json | 10 + .../Data/company_applications.unittest.json | 10 + .../Data/company_wallet_data.unittest.json | 12 ++ .../Seeder/Data/process_steps.unittest.json | 16 ++ .../Seeder/Data/processes.unittest.json | 6 + .../BringYourOwnWalletBuisinessLogicTests.cs | 117 +++++++++++ .../RegistrationBusinessLogicTest.cs | 1 - .../BringYourOwnWalletControllerTests.cs | 78 +++++++ 28 files changed, 1187 insertions(+), 53 deletions(-) create mode 100644 src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs create mode 100644 src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverSettings.cs create mode 100644 src/externalsystems/UniversalDidResolver.Library/IUniversalDidResolverService.cs create mode 100644 src/externalsystems/UniversalDidResolver.Library/Models/DidValidationResult.cs create mode 100644 src/externalsystems/UniversalDidResolver.Library/Schemas/DidDocument.schema.json create mode 100644 src/externalsystems/UniversalDidResolver.Library/UniversalDidResolver.Library.csproj create mode 100644 src/externalsystems/UniversalDidResolver.Library/UniversalDidResolverService.cs create mode 100644 src/portalbackend/PortalBackend.DBAccess/Models/BringYourOwnWalletClientFields.cs create mode 100644 src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs create mode 100644 src/registration/Registration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs create mode 100644 src/registration/Registration.Service/Controllers/BringYourOwnWalletController.cs create mode 100644 tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolver.Library.Tests.csproj create mode 100644 tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolverServiceTest.cs create mode 100644 tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs create mode 100644 tests/registration/Registration.Service.Tests/Controller/BringYourOwnWalletControllerTests.cs diff --git a/docs/api/administration-service.yaml b/docs/api/administration-service.yaml index d3e6680572..c5c6fb7ef3 100644 --- a/docs/api/administration-service.yaml +++ b/docs/api/administration-service.yaml @@ -7655,6 +7655,9 @@ components: type: array items: $ref: '#/components/schemas/CompanyUniqueIdData' + holderDid: + type: string + nullable: true externalId: type: string userDetails: diff --git a/docs/api/registration-service.yaml b/docs/api/registration-service.yaml index 3514f90452..208eeed66a 100644 --- a/docs/api/registration-service.yaml +++ b/docs/api/registration-service.yaml @@ -3,6 +3,70 @@ info: title: Org.Eclipse.TractusX.Portal.Backend.Registration.Service version: v2.5.0 paths: + '/api/registration/bringYourOwnWallet/{did}/validateDid': + post: + tags: + - BringYourOwnWallet + summary: ' (Authorization required - Roles: submit_registration)' + parameters: + - name: did + in: path + required: true + schema: + type: string + responses: + '204': + description: No Content + content: + text/plain: + schema: + $ref: '#/components/schemas/NoContentResult' + application/json: + schema: + $ref: '#/components/schemas/NoContentResult' + text/json: + schema: + $ref: '#/components/schemas/NoContentResult' + '415': + description: Unsupported Media Type + content: + text/plain: + schema: + $ref: '#/components/schemas/ErrorResponse' + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + text/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Not Found + content: + text/plain: + schema: + $ref: '#/components/schemas/ErrorResponse' + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + text/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '502': + description: Bad Gateway + content: + text/plain: + schema: + $ref: '#/components/schemas/ErrorResponse' + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + text/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal Server Error + '401': + description: The User is unauthorized /api/registration/errormessage: get: tags: @@ -1333,6 +1397,9 @@ components: type: array items: $ref: '#/components/schemas/CompanyUniqueIdData' + holderDid: + type: string + nullable: true companyId: type: string format: uuid @@ -1589,6 +1656,13 @@ components: items: type: string additionalProperties: false + NoContentResult: + type: object + properties: + statusCode: + type: integer + format: int32 + additionalProperties: false OspDeclineReason: enum: - REGISTRATION_NOT_REQUESTED diff --git a/src/Portal.Backend.sln b/src/Portal.Backend.sln index 5504c5d307..525c6414ec 100644 --- a/src/Portal.Backend.sln +++ b/src/Portal.Backend.sln @@ -1,3 +1,4 @@ + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32407.343 @@ -290,6 +291,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Framework.Processes.Worker. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Framework.Processes.Library", "framework\Framework.Processes.Library\Framework.Processes.Library.csproj", "{28007753-C8F8-43B0-A1C3-8FDC22727F44}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversalDidResolver.Library", "externalsystems\UniversalDidResolver.Library\UniversalDidResolver.Library.csproj", "{1D666BBA-1F72-422D-B7D4-6355E2FB734A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "externalsystems", "externalsystems", "{6C41D4C8-B50D-D926-DC14-09C2931EC54F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniversalDidResolver.Library.Tests", "..\tests\externalsystems\UniversalDidResolver.Library.Tests\UniversalDidResolver.Library.Tests.csproj", "{24D3F795-29AD-47B3-84B8-64644F7FE742}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -900,18 +907,6 @@ Global {B787DF92-23F7-410A-B592-95701E4B423D}.Release|x64.Build.0 = Release|Any CPU {B787DF92-23F7-410A-B592-95701E4B423D}.Release|x86.ActiveCfg = Release|Any CPU {B787DF92-23F7-410A-B592-95701E4B423D}.Release|x86.Build.0 = Release|Any CPU - {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Debug|x64.ActiveCfg = Debug|Any CPU - {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Debug|x64.Build.0 = Debug|Any CPU - {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Debug|x86.ActiveCfg = Debug|Any CPU - {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Debug|x86.Build.0 = Debug|Any CPU - {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Release|Any CPU.Build.0 = Release|Any CPU - {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Release|x64.ActiveCfg = Release|Any CPU - {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Release|x64.Build.0 = Release|Any CPU - {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Release|x86.ActiveCfg = Release|Any CPU - {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Release|x86.Build.0 = Release|Any CPU {7985B208-CE41-49DA-B749-B94B582612E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7985B208-CE41-49DA-B749-B94B582612E6}.Debug|Any CPU.Build.0 = Debug|Any CPU {7985B208-CE41-49DA-B749-B94B582612E6}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -924,6 +919,18 @@ Global {7985B208-CE41-49DA-B749-B94B582612E6}.Release|x64.Build.0 = Release|Any CPU {7985B208-CE41-49DA-B749-B94B582612E6}.Release|x86.ActiveCfg = Release|Any CPU {7985B208-CE41-49DA-B749-B94B582612E6}.Release|x86.Build.0 = Release|Any CPU + {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Debug|x64.ActiveCfg = Debug|Any CPU + {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Debug|x64.Build.0 = Debug|Any CPU + {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Debug|x86.ActiveCfg = Debug|Any CPU + {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Debug|x86.Build.0 = Debug|Any CPU + {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Release|Any CPU.Build.0 = Release|Any CPU + {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Release|x64.ActiveCfg = Release|Any CPU + {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Release|x64.Build.0 = Release|Any CPU + {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Release|x86.ActiveCfg = Release|Any CPU + {4C7E9EAC-222B-4C13-B8B1-5987406817A0}.Release|x86.Build.0 = Release|Any CPU {15BA8836-E9FE-4F64-AD97-261A524779A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {15BA8836-E9FE-4F64-AD97-261A524779A5}.Debug|Any CPU.Build.0 = Debug|Any CPU {15BA8836-E9FE-4F64-AD97-261A524779A5}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -1560,6 +1567,18 @@ Global {690B95C5-5BDD-472B-93E3-414F46C4EE94}.Release|x64.Build.0 = Release|Any CPU {690B95C5-5BDD-472B-93E3-414F46C4EE94}.Release|x86.ActiveCfg = Release|Any CPU {690B95C5-5BDD-472B-93E3-414F46C4EE94}.Release|x86.Build.0 = Release|Any CPU + {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Debug|x64.ActiveCfg = Debug|Any CPU + {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Debug|x64.Build.0 = Debug|Any CPU + {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Debug|x86.ActiveCfg = Debug|Any CPU + {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Debug|x86.Build.0 = Debug|Any CPU + {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Release|Any CPU.Build.0 = Release|Any CPU + {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Release|x64.ActiveCfg = Release|Any CPU + {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Release|x64.Build.0 = Release|Any CPU + {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Release|x86.ActiveCfg = Release|Any CPU + {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Release|x86.Build.0 = Release|Any CPU {0551F0AF-5373-4516-87AA-01D7FFD29C0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0551F0AF-5373-4516-87AA-01D7FFD29C0D}.Debug|Any CPU.Build.0 = Debug|Any CPU {0551F0AF-5373-4516-87AA-01D7FFD29C0D}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -1644,18 +1663,6 @@ Global {07BDC20D-23DD-4C0E-9C3D-B1D232AF17E3}.Release|x64.Build.0 = Release|Any CPU {07BDC20D-23DD-4C0E-9C3D-B1D232AF17E3}.Release|x86.ActiveCfg = Release|Any CPU {07BDC20D-23DD-4C0E-9C3D-B1D232AF17E3}.Release|x86.Build.0 = Release|Any CPU - {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Release|x86.ActiveCfg = Release|Any CPU - {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Release|x86.Build.0 = Release|Any CPU - {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Debug|x64.ActiveCfg = Debug|Any CPU - {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Debug|x64.Build.0 = Debug|Any CPU - {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Debug|x86.ActiveCfg = Debug|Any CPU - {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Debug|x86.Build.0 = Debug|Any CPU - {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Release|Any CPU.Build.0 = Release|Any CPU - {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Release|x64.ActiveCfg = Release|Any CPU - {D666EADA-770A-42FF-B891-5745F7A6BC2F}.Release|x64.Build.0 = Release|Any CPU {59E4B63B-BEA2-4CDA-98F0-13962146AEA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {59E4B63B-BEA2-4CDA-98F0-13962146AEA5}.Debug|Any CPU.Build.0 = Debug|Any CPU {59E4B63B-BEA2-4CDA-98F0-13962146AEA5}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -1860,31 +1867,35 @@ Global {28007753-C8F8-43B0-A1C3-8FDC22727F44}.Release|x64.Build.0 = Release|Any CPU {28007753-C8F8-43B0-A1C3-8FDC22727F44}.Release|x86.ActiveCfg = Release|Any CPU {28007753-C8F8-43B0-A1C3-8FDC22727F44}.Release|x86.Build.0 = Release|Any CPU + {1D666BBA-1F72-422D-B7D4-6355E2FB734A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1D666BBA-1F72-422D-B7D4-6355E2FB734A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D666BBA-1F72-422D-B7D4-6355E2FB734A}.Debug|x64.ActiveCfg = Debug|Any CPU + {1D666BBA-1F72-422D-B7D4-6355E2FB734A}.Debug|x64.Build.0 = Debug|Any CPU + {1D666BBA-1F72-422D-B7D4-6355E2FB734A}.Debug|x86.ActiveCfg = Debug|Any CPU + {1D666BBA-1F72-422D-B7D4-6355E2FB734A}.Debug|x86.Build.0 = Debug|Any CPU + {1D666BBA-1F72-422D-B7D4-6355E2FB734A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1D666BBA-1F72-422D-B7D4-6355E2FB734A}.Release|Any CPU.Build.0 = Release|Any CPU + {1D666BBA-1F72-422D-B7D4-6355E2FB734A}.Release|x64.ActiveCfg = Release|Any CPU + {1D666BBA-1F72-422D-B7D4-6355E2FB734A}.Release|x64.Build.0 = Release|Any CPU + {1D666BBA-1F72-422D-B7D4-6355E2FB734A}.Release|x86.ActiveCfg = Release|Any CPU + {1D666BBA-1F72-422D-B7D4-6355E2FB734A}.Release|x86.Build.0 = Release|Any CPU + {24D3F795-29AD-47B3-84B8-64644F7FE742}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24D3F795-29AD-47B3-84B8-64644F7FE742}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24D3F795-29AD-47B3-84B8-64644F7FE742}.Debug|x64.ActiveCfg = Debug|Any CPU + {24D3F795-29AD-47B3-84B8-64644F7FE742}.Debug|x64.Build.0 = Debug|Any CPU + {24D3F795-29AD-47B3-84B8-64644F7FE742}.Debug|x86.ActiveCfg = Debug|Any CPU + {24D3F795-29AD-47B3-84B8-64644F7FE742}.Debug|x86.Build.0 = Debug|Any CPU + {24D3F795-29AD-47B3-84B8-64644F7FE742}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24D3F795-29AD-47B3-84B8-64644F7FE742}.Release|Any CPU.Build.0 = Release|Any CPU + {24D3F795-29AD-47B3-84B8-64644F7FE742}.Release|x64.ActiveCfg = Release|Any CPU + {24D3F795-29AD-47B3-84B8-64644F7FE742}.Release|x64.Build.0 = Release|Any CPU + {24D3F795-29AD-47B3-84B8-64644F7FE742}.Release|x86.ActiveCfg = Release|Any CPU + {24D3F795-29AD-47B3-84B8-64644F7FE742}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {2EB6265F-323A-4BF3-969E-003D64A14B64} - EndGlobalSection GlobalSection(NestedProjects) = preSolution - {0CBCC851-99A1-4005-9BBA-E6A20A0AEDAA} = {323C198D-A8C6-4EB0-8B79-72624275E35F} - {CD76A7FF-D003-41DE-9442-F9AB223C6051} = {323C198D-A8C6-4EB0-8B79-72624275E35F} - {A43B5ACA-1209-46E9-84DB-A48553ED623E} = {323C198D-A8C6-4EB0-8B79-72624275E35F} - {A5BEDD89-7280-466E-8D14-EC5E177AAD07} = {323C198D-A8C6-4EB0-8B79-72624275E35F} - {5E80DEEA-B254-425C-8220-27EEF47C10BD} = {323C198D-A8C6-4EB0-8B79-72624275E35F} - {B787DF92-23F7-410A-B592-95701E4B423D} = {323C198D-A8C6-4EB0-8B79-72624275E35F} - {0C039C14-74CA-484C-B8D9-A307C97EC312} = {323C198D-A8C6-4EB0-8B79-72624275E35F} - {2B1218E4-E1A4-418E-A55F-16B7F30235A7} = {323C198D-A8C6-4EB0-8B79-72624275E35F} - {6C2F85D7-0443-4711-96DF-66EC46CA1A98} = {323C198D-A8C6-4EB0-8B79-72624275E35F} - {217FE6D7-0DE3-43CF-AFC4-7FA12700F447} = {323C198D-A8C6-4EB0-8B79-72624275E35F} - {B8E70529-7A8C-44F8-BE8C-1369754291AF} = {323C198D-A8C6-4EB0-8B79-72624275E35F} - {53E03A1A-E59F-4C67-80B4-5A232BF01A81} = {323C198D-A8C6-4EB0-8B79-72624275E35F} - {F995582E-729F-4EA0-831F-6CA5058114EF} = {323C198D-A8C6-4EB0-8B79-72624275E35F} - {12F16E1B-0275-4F41-8353-C2C9A79BA4E9} = {323C198D-A8C6-4EB0-8B79-72624275E35F} - {B6DE0855-385D-4A8C-BC22-04BE2105A2F4} = {323C198D-A8C6-4EB0-8B79-72624275E35F} - {F0EFB95B-39DD-4FDB-A044-0BB9302DDC41} = {323C198D-A8C6-4EB0-8B79-72624275E35F} - {1EFC9D98-C8EE-4399-9B2D-876CDDE8CFD3} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {06418D5E-5963-4D46-8F09-A0E132721C64} = {E0B99F7E-D108-4054-92C2-31304C5060DE} {69004CBA-5B0C-42C7-A4DA-4727F14AA20A} = {46383371-8252-4598-9350-A97692851408} {C482693F-A8D8-40FA-AD93-00B03CA6DC31} = {46383371-8252-4598-9350-A97692851408} @@ -1905,7 +1916,18 @@ Global {C512740F-48A2-4B5B-83F2-EB7753EAE028} = {AE4A5C54-72F3-4B55-BB86-09DFA3AA3D7B} {22DEE4A2-15ED-4176-B912-B357D474D2AC} = {AE4A5C54-72F3-4B55-BB86-09DFA3AA3D7B} {FBEA925C-EE3C-4D81-A492-0B2D386C161E} = {AB9C5AA2-DD5D-4A38-97C0-674A995C0AE0} + {1EFC9D98-C8EE-4399-9B2D-876CDDE8CFD3} = {323C198D-A8C6-4EB0-8B79-72624275E35F} + {F0EFB95B-39DD-4FDB-A044-0BB9302DDC41} = {323C198D-A8C6-4EB0-8B79-72624275E35F} + {F995582E-729F-4EA0-831F-6CA5058114EF} = {323C198D-A8C6-4EB0-8B79-72624275E35F} + {6C2F85D7-0443-4711-96DF-66EC46CA1A98} = {323C198D-A8C6-4EB0-8B79-72624275E35F} + {B6DE0855-385D-4A8C-BC22-04BE2105A2F4} = {323C198D-A8C6-4EB0-8B79-72624275E35F} + {12F16E1B-0275-4F41-8353-C2C9A79BA4E9} = {323C198D-A8C6-4EB0-8B79-72624275E35F} + {53E03A1A-E59F-4C67-80B4-5A232BF01A81} = {323C198D-A8C6-4EB0-8B79-72624275E35F} + {B8E70529-7A8C-44F8-BE8C-1369754291AF} = {323C198D-A8C6-4EB0-8B79-72624275E35F} + {217FE6D7-0DE3-43CF-AFC4-7FA12700F447} = {323C198D-A8C6-4EB0-8B79-72624275E35F} + {2B1218E4-E1A4-418E-A55F-16B7F30235A7} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {3F7A02D4-073C-40FE-B228-8E1BA96B1946} = {AE4A5C54-72F3-4B55-BB86-09DFA3AA3D7B} + {0C039C14-74CA-484C-B8D9-A307C97EC312} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {3828FF08-4CD7-4FF8-B94A-ED5D5FFA0382} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {8B7D8210-05A4-4C0A-AB19-D695E6E97281} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {CF80BBB1-C07D-425C-9A5B-54C5E2890CC4} = {282CEF03-292F-4A49-83C6-997567D0FF5F} @@ -1923,20 +1945,24 @@ Global {96D96CA7-35C0-40C6-A8C8-91E0C4456660} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {C3E5E7C8-69D3-4ECB-A4FA-53A9A780EFF0} = {282CEF03-292F-4A49-83C6-997567D0FF5F} {0221E83B-B26B-442F-ACAD-B1043DF9993A} = {282CEF03-292F-4A49-83C6-997567D0FF5F} - {4C7E9EAC-222B-4C13-B8B1-5987406817A0} = {282CEF03-292F-4A49-83C6-997567D0FF5F} + {B787DF92-23F7-410A-B592-95701E4B423D} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {7985B208-CE41-49DA-B749-B94B582612E6} = {323C198D-A8C6-4EB0-8B79-72624275E35F} + {4C7E9EAC-222B-4C13-B8B1-5987406817A0} = {282CEF03-292F-4A49-83C6-997567D0FF5F} {15BA8836-E9FE-4F64-AD97-261A524779A5} = {282CEF03-292F-4A49-83C6-997567D0FF5F} {1EE0ED29-2076-49B0-8110-FF43C9612888} = {C8957230-4203-452C-A085-34091C5E370B} {B682C5B9-AFAB-474D-95AD-B86099FC5EC7} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {C356AA93-5BEE-44CD-A905-EFD325ED7578} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {CC063A63-8282-4293-9A0E-2598ADCA747B} = {323C198D-A8C6-4EB0-8B79-72624275E35F} + {5E80DEEA-B254-425C-8220-27EEF47C10BD} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {EC493B36-9E14-4CAF-973F-FB96FDAF546F} = {A878BDF1-6DB6-4BA5-A724-92885A710856} {1694E75F-ABCE-4573-B805-18ED50F148FD} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {E1D41A07-F468-4D13-8185-35F127230B17} = {46383371-8252-4598-9350-A97692851408} + {A5BEDD89-7280-466E-8D14-EC5E177AAD07} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {19639645-A115-4824-865F-5559DA8B892A} = {282CEF03-292F-4A49-83C6-997567D0FF5F} {3B41408A-CDFE-4EEE-9660-FE6755FD2075} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {C53EAB34-1F66-48F8-88AB-226BE142D1CF} = {282CEF03-292F-4A49-83C6-997567D0FF5F} {B06A4F63-FE8C-40D2-B39F-8C15031A55CC} = {C8957230-4203-452C-A085-34091C5E370B} + {A43B5ACA-1209-46E9-84DB-A48553ED623E} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {F1A5A73C-2B8C-4959-A128-CC5A8DECCB1B} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {09EF5799-B375-49F1-B78F-0A94D8109F8B} = {AB9C5AA2-DD5D-4A38-97C0-674A995C0AE0} {C7ACF748-DEF4-4646-A791-F1DA437CC965} = {323C198D-A8C6-4EB0-8B79-72624275E35F} @@ -1975,6 +2001,7 @@ Global {AD0C9BAD-E68F-40CF-86B4-31AC61856658} = {B42CFF96-B8DB-48A6-A9CA-72BFB5F0117B} {C3F19E18-B8B9-4BCB-988D-8D1ADCE41D66} = {C8957230-4203-452C-A085-34091C5E370B} {690B95C5-5BDD-472B-93E3-414F46C4EE94} = {323C198D-A8C6-4EB0-8B79-72624275E35F} + {D666EADA-770A-42FF-B891-5745F7A6BC2F} = {282CEF03-292F-4A49-83C6-997567D0FF5F} {0551F0AF-5373-4516-87AA-01D7FFD29C0D} = {282CEF03-292F-4A49-83C6-997567D0FF5F} {4693D24A-E6AA-4C3D-9990-5F095EACBD0B} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {BF8EB6BB-E8CA-423C-AC50-01FB44D0D08B} = {323C198D-A8C6-4EB0-8B79-72624275E35F} @@ -1982,7 +2009,6 @@ Global {B6A2A753-5876-4C6C-8CC2-151B022C87F7} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {F2F9E057-668E-43BB-B3EB-10E8DE8E7BB4} = {282CEF03-292F-4A49-83C6-997567D0FF5F} {07BDC20D-23DD-4C0E-9C3D-B1D232AF17E3} = {323C198D-A8C6-4EB0-8B79-72624275E35F} - {D666EADA-770A-42FF-B891-5745F7A6BC2F} = {282CEF03-292F-4A49-83C6-997567D0FF5F} {59E4B63B-BEA2-4CDA-98F0-13962146AEA5} = {C8957230-4203-452C-A085-34091C5E370B} {C6DE5F4C-DB67-48B9-BE0E-0C557AB704AC} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {ED7073C2-1D64-4FB4-A363-1EC2CBCB1302} = {C8957230-4203-452C-A085-34091C5E370B} @@ -1991,6 +2017,8 @@ Global {143433B2-2792-4C5F-A3C2-E5C91D68E30D} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {9636BEC8-6929-4852-8DC8-8B41609630A3} = {282CEF03-292F-4A49-83C6-997567D0FF5F} {E5494227-BDFE-41F2-A12F-54292D76C29F} = {282CEF03-292F-4A49-83C6-997567D0FF5F} + {CD76A7FF-D003-41DE-9442-F9AB223C6051} = {323C198D-A8C6-4EB0-8B79-72624275E35F} + {0CBCC851-99A1-4005-9BBA-E6A20A0AEDAA} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {D8EBE555-F103-4D05-9697-5381E4DE1DFE} = {282CEF03-292F-4A49-83C6-997567D0FF5F} {AA14B842-6A65-40BB-818A-D450F66F4101} = {323C198D-A8C6-4EB0-8B79-72624275E35F} {6ED01D72-BE48-45A6-A615-BFA83DF99EF4} = {B42CFF96-B8DB-48A6-A9CA-72BFB5F0117B} @@ -1998,5 +2026,11 @@ Global {96F978B7-71C1-4866-BFB5-8631F4743FA5} = {B42CFF96-B8DB-48A6-A9CA-72BFB5F0117B} {BD0268EB-65A9-4A0A-B724-214A6D2591B5} = {B42CFF96-B8DB-48A6-A9CA-72BFB5F0117B} {28007753-C8F8-43B0-A1C3-8FDC22727F44} = {B42CFF96-B8DB-48A6-A9CA-72BFB5F0117B} + {1D666BBA-1F72-422D-B7D4-6355E2FB734A} = {C8957230-4203-452C-A085-34091C5E370B} + {6C41D4C8-B50D-D926-DC14-09C2931EC54F} = {323C198D-A8C6-4EB0-8B79-72624275E35F} + {24D3F795-29AD-47B3-84B8-64644F7FE742} = {323C198D-A8C6-4EB0-8B79-72624275E35F} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2EB6265F-323A-4BF3-969E-003D64A14B64} EndGlobalSection EndGlobal diff --git a/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs b/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs new file mode 100644 index 0000000000..97e3698f71 --- /dev/null +++ b/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs @@ -0,0 +1,56 @@ +/******************************************************************************** + * Copyright (c) 2025 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 Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Org.Eclipse.TractusX.Portal.Backend.Framework.HttpClientExtensions; + +namespace Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library.DependencyInjection; + +public static class UniversalDidResolverServiceCollectionExtension +{ + public static IServiceCollection AddUniversalDidResolverService(this IServiceCollection services, IConfigurationSection section) + { + services.AddOptions() + .Bind(section); + + services.AddTransient>(); + + var sp = services.BuildServiceProvider(); + var settings = sp.GetRequiredService>(); + + RegisterUniversalResolver(settings.Value, services); + services + .AddTransient(); + + return services; + } + + private static void RegisterUniversalResolver(UniversalDidResolverSettings settings, IServiceCollection services) + { + var baseAddress = settings.UniversalResolverAddress.EndsWith("/") + ? settings.UniversalResolverAddress + : $"{settings.UniversalResolverAddress}/"; + services.AddHttpClient("universalResolver", c => + { + c.BaseAddress = new Uri(baseAddress); + }); + } +} diff --git a/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverSettings.cs b/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverSettings.cs new file mode 100644 index 0000000000..7722a66e25 --- /dev/null +++ b/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverSettings.cs @@ -0,0 +1,27 @@ +/******************************************************************************** + * Copyright (c) 2025 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.ComponentModel.DataAnnotations; + +namespace Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library.DependencyInjection; + +public class UniversalDidResolverSettings +{ + [Required(AllowEmptyStrings = false)] + public string UniversalResolverAddress { get; set; } = null!; +} diff --git a/src/externalsystems/UniversalDidResolver.Library/IUniversalDidResolverService.cs b/src/externalsystems/UniversalDidResolver.Library/IUniversalDidResolverService.cs new file mode 100644 index 0000000000..ed4e7a1c65 --- /dev/null +++ b/src/externalsystems/UniversalDidResolver.Library/IUniversalDidResolverService.cs @@ -0,0 +1,37 @@ +/******************************************************************************** + * Copyright (c) 2025 Cofinity-X GmbH + * Copyright (c) 2025 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 Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library.Models; +using System.Text.Json; + +namespace Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library; + +public interface IUniversalDidResolverService +{ + /// + /// Validates the did using the universal resolver + /// + /// The did that should be checked + /// The CancellationToken + /// true if the did is valid, otherwise false + Task ValidateDid(string did, CancellationToken cancellationToken); + + Task ValidateSchema(JsonElement content, CancellationToken cancellationToken); +} diff --git a/src/externalsystems/UniversalDidResolver.Library/Models/DidValidationResult.cs b/src/externalsystems/UniversalDidResolver.Library/Models/DidValidationResult.cs new file mode 100644 index 0000000000..228cbae03e --- /dev/null +++ b/src/externalsystems/UniversalDidResolver.Library/Models/DidValidationResult.cs @@ -0,0 +1,34 @@ +/******************************************************************************** + * Copyright (c) 2025 Cofinity-X GmbH + * Copyright (c) 2025 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.Text.Json; +using System.Text.Json.Serialization; + +namespace Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library.Models; + +public record DidValidationResult( + [property: JsonPropertyName("didResolutionMetadata")] DidResolutionMetadata DidResolutionMetadata, + [property: JsonPropertyName("didDocument")] JsonElement DidDocument + +); + +public record DidResolutionMetadata( + [property: JsonPropertyName("error")] string? Error +); diff --git a/src/externalsystems/UniversalDidResolver.Library/Schemas/DidDocument.schema.json b/src/externalsystems/UniversalDidResolver.Library/Schemas/DidDocument.schema.json new file mode 100644 index 0000000000..5675d8885d --- /dev/null +++ b/src/externalsystems/UniversalDidResolver.Library/Schemas/DidDocument.schema.json @@ -0,0 +1,89 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://eclipse-tractusx.github.io/DidDocument.schema.json", + "type": "object", + "required": ["@context", "id", "service", "verificationMethod"], + "properties": { + "@context": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "string" + }, + "service": { + "type": "array", + "items": { + "type": "object", + "required": ["id", "type"], + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + }, + "serviceEndpoint": { + "type": "string" + } + } + } + }, + "verificationMethod": { + "type": "array", + "items": { + "type": "object", + "required": ["id", "type", "publicKeyJwk"], + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + }, + "controller": { + "type": "string" + }, + "publicKeyJwk": { + "type": "object", + "required": ["kty", "crv", "x", "y"], + "properties": { + "kty": { + "type": "string" + }, + "crv": { + "type": "string" + }, + "x": { + "type": "string" + }, + "y": { + "type": "string" + } + } + } + } + } + }, + "authentication": { + "type": "array", + "items": { + "type": "string" + } + }, + "assertionMethod": { + "type": "array", + "items": { + "type": "string" + } + }, + "keyAgreement": { + "type": "array", + "items": { + "type": "string" + } + } + } +} diff --git a/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolver.Library.csproj b/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolver.Library.csproj new file mode 100644 index 0000000000..137b9cddfb --- /dev/null +++ b/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolver.Library.csproj @@ -0,0 +1,40 @@ + + + + + net9.0 + enable + enable + Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library + Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library + + + + + + + + + + + + + diff --git a/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolverService.cs b/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolverService.cs new file mode 100644 index 0000000000..af17714789 --- /dev/null +++ b/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolverService.cs @@ -0,0 +1,83 @@ +/******************************************************************************** + * Copyright (c) 2025 Cofinity-X GmbH + * Copyright (c) 2025 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 Json.Schema; +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.Framework.HttpClientExtensions; +using Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library.Models; +using System.Net.Http.Json; +using System.Net.Sockets; +using System.Reflection; +using System.Text.Json; +namespace Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library; + +public class UniversalDidResolverService(IHttpClientFactory httpClientFactory) : IUniversalDidResolverService +{ + private static readonly JsonSerializerOptions Options = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; + public async Task ValidateDid(string did, CancellationToken cancellationToken) + { + using var httpClient = httpClientFactory.CreateClient("universalResolver"); + HttpResponseMessage result; + try + { + result = await httpClient.GetAsync($"1.0/identifiers/{Uri.EscapeDataString(did)}", cancellationToken) + .CatchingIntoServiceExceptionFor("validate-did", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE).ConfigureAwait(false); + + } + catch (ServiceException ex) + { + if (ex.InnerException is SocketException) + { + throw new NotFoundException("Universal resolver is not reachable"); + } + throw new NotFoundException("DID URL could not be reached by the external resolver, 404 error", ex); + } + + if (!result.IsSuccessStatusCode) + { + throw new NotFoundException($"Did validation failed with status code {result.StatusCode} and reason {result.ReasonPhrase}. Did: {did}"); + } + + var validationResult = await result.Content.ReadFromJsonAsync(Options, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + if (validationResult == null) + { + throw new NotFoundException("DID validation failed: No result returned."); + } + + if (!string.IsNullOrWhiteSpace(validationResult.DidResolutionMetadata.Error)) + { + throw new UnsupportedMediaTypeException("DID validation failed during validation"); + } + + return validationResult; + } + + public async Task ValidateSchema(JsonElement content, CancellationToken cancellationToken) + { + var location = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? throw new UnexpectedConditionException("Assembly location must be set"); + + var path = Path.Combine(location, "Schemas", "DidDocument.schema.json"); + var schemaJson = await File.ReadAllTextAsync(path, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + + var schema = JsonSchema.FromText(schemaJson); + SchemaRegistry.Global.Register(schema); + var result = schema.Evaluate(content); + return result.IsValid; + } +} diff --git a/src/portalbackend/PortalBackend.DBAccess/Models/BringYourOwnWalletClientFields.cs b/src/portalbackend/PortalBackend.DBAccess/Models/BringYourOwnWalletClientFields.cs new file mode 100644 index 0000000000..eac8bddf86 --- /dev/null +++ b/src/portalbackend/PortalBackend.DBAccess/Models/BringYourOwnWalletClientFields.cs @@ -0,0 +1,8 @@ +namespace Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; + +public static class BringYourOwnWalletClientFields +{ + public const string Identification = "BYOWCLIENT_ID"; + public const string NotUsed = "NOT_USED"; + +} diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs index 2b0f53485c..6f0b185d82 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs @@ -74,11 +74,11 @@ public void CreateCustomerWallet(Guid companyId, string did) companyId, did, JsonDocument.Parse("{}"), - "BYOWCLIENT_ID", + BringYourOwnWalletClientFields.Identification, new byte[1], new byte[1], default, - "NOT_SET"); + BringYourOwnWalletClientFields.NotUsed); context.CompanyWalletDatas.Add(wallet); } @@ -91,7 +91,7 @@ public Task IsBringYourOwnWallet(Guid applicationId) app => app.CompanyId, wallet => wallet.CompanyId, (app, wallet) => wallet) - .AnyAsync(wallet => wallet.ClientId == "BYOWCLIENT_ID"); + .AnyAsync(wallet => wallet.ClientId == BringYourOwnWalletClientFields.Identification); } public void AttachAndModifyAddress(Guid addressId, Action
? initialize, Action
modify) { diff --git a/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs b/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs new file mode 100644 index 0000000000..e4033bb829 --- /dev/null +++ b/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs @@ -0,0 +1,47 @@ +/******************************************************************************** + * Copyright (c) 2025 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 Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library; +using System.Net; + +namespace Org.Eclipse.TractusX.Portal.Backend.Registration.Service.BusinessLogic +{ + public class BringYourOwnWalletBusinessLogic : IBringYourOwnWalletBusinessLogic + { + private readonly IUniversalDidResolverService _universalResolverService; + + public BringYourOwnWalletBusinessLogic(IUniversalDidResolverService universalResolverService) + { + _universalResolverService = universalResolverService; + } + public async Task ValidateDid(string did, CancellationToken cancellationToken) + { + var validationResult = await _universalResolverService.ValidateDid(did, cancellationToken); + var isSchemaValid = await _universalResolverService.ValidateSchema( + validationResult.DidDocument, + cancellationToken + ).ConfigureAwait(false); + + if (!isSchemaValid) + { + throw new UnsupportedMediaTypeException("DID validation failed. DID Document is not valid."); + } + } + } +} diff --git a/src/registration/Registration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs b/src/registration/Registration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs new file mode 100644 index 0000000000..cba51186ed --- /dev/null +++ b/src/registration/Registration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs @@ -0,0 +1,26 @@ +/******************************************************************************** + * Copyright (c) 2025 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 + ********************************************************************************/ + +namespace Org.Eclipse.TractusX.Portal.Backend.Registration.Service.BusinessLogic +{ + public interface IBringYourOwnWalletBusinessLogic + { + Task ValidateDid(string did, CancellationToken cancellationToken); + } +} diff --git a/src/registration/Registration.Service/Controllers/BringYourOwnWalletController.cs b/src/registration/Registration.Service/Controllers/BringYourOwnWalletController.cs new file mode 100644 index 0000000000..2db33b39be --- /dev/null +++ b/src/registration/Registration.Service/Controllers/BringYourOwnWalletController.cs @@ -0,0 +1,45 @@ +/******************************************************************************** + * Copyright (c) 2025 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 Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling.Web; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Web; +using Org.Eclipse.TractusX.Portal.Backend.Registration.Service.BusinessLogic; + +namespace Org.Eclipse.TractusX.Portal.Backend.Registration.Service.Controllers +{ + [ApiController] + [EnvironmentRoute("MVC_ROUTING_BASEPATH", "bringYourOwnWallet")] + public class BringYourOwnWalletController(IBringYourOwnWalletBusinessLogic logic) : ControllerBase + { + [HttpPost] + [Authorize(Roles = "submit_registration")] + [Route("{did}/validateDid")] + [ProducesResponseType(typeof(NoContentResult), StatusCodes.Status204NoContent)] + [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status415UnsupportedMediaType)] + [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status404NotFound)] + public async Task validateDid([FromRoute] string did, CancellationToken cancellationToken) + { + + await logic.ValidateDid(did, cancellationToken); + + return NoContent(); + } + } +} diff --git a/src/registration/Registration.Service/Program.cs b/src/registration/Registration.Service/Program.cs index db4fbd2abd..0b47d2254e 100644 --- a/src/registration/Registration.Service/Program.cs +++ b/src/registration/Registration.Service/Program.cs @@ -19,17 +19,19 @@ ********************************************************************************/ using Org.Eclipse.TractusX.Portal.Backend.Bpdm.Library.DependencyInjection; +using Org.Eclipse.TractusX.Portal.Backend.Dim.Library.DependencyInjection; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling.Service; using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Extensions; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Token; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess; using Org.Eclipse.TractusX.Portal.Backend.Processes.ApplicationChecklist.Config; using Org.Eclipse.TractusX.Portal.Backend.Processes.Mailing.Library.DependencyInjection; using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library; using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library.Service; using Org.Eclipse.TractusX.Portal.Backend.Registration.Common.ErrorHandling; -using Org.Eclipse.TractusX.Portal.Backend.Registration.Service; using Org.Eclipse.TractusX.Portal.Backend.Registration.Service.BusinessLogic; using Org.Eclipse.TractusX.Portal.Backend.Registration.Service.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library.DependencyInjection; using Org.Eclipse.TractusX.Portal.Backend.Web.Initialization; using Org.Eclipse.TractusX.Portal.Backend.Web.PublicInfos.DependencyInjection; @@ -51,13 +53,14 @@ await WebAppHelper .AddTransient() .ConfigureRegistrationSettings(builder.Configuration.GetSection("Registration")) .AddTransient(); - builder.Services.AddApplicationChecklistCreation(builder.Configuration.GetSection("ApplicationCreation")); builder.Services .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton(); + builder.Services.AddTransient() + .AddUniversalDidResolverService(builder.Configuration.GetSection("UniversalDidResolver")); builder.Services.AddBpnAccess(builder.Configuration.GetSection("BpnAccess")); builder.Services.AddMailingProcessCreation(builder.Configuration.GetSection("MailingProcessCreation")); diff --git a/src/registration/Registration.Service/Registration.Service.csproj b/src/registration/Registration.Service/Registration.Service.csproj index aedcec3a55..6a880fb5f9 100644 --- a/src/registration/Registration.Service/Registration.Service.csproj +++ b/src/registration/Registration.Service/Registration.Service.csproj @@ -53,6 +53,7 @@ + diff --git a/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolver.Library.Tests.csproj b/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolver.Library.Tests.csproj new file mode 100644 index 0000000000..47fb71553d --- /dev/null +++ b/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolver.Library.Tests.csproj @@ -0,0 +1,41 @@ + + + + net9.0 + enable + enable + false + Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library.Tests + Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library.Tests + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + \ No newline at end of file diff --git a/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolverServiceTest.cs b/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolverServiceTest.cs new file mode 100644 index 0000000000..8496286f0e --- /dev/null +++ b/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolverServiceTest.cs @@ -0,0 +1,193 @@ +/******************************************************************************** + * Copyright (c) 2025 Cofinity-X GmbH + * Copyright (c) 2025 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 AutoFixture; +using AutoFixture.AutoFakeItEasy; +using FakeItEasy; +using FluentAssertions; +using Microsoft.Extensions.Options; +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.Tests.Shared.Extensions; +using Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library.DependencyInjection; +using Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library.Models; +using System.Net; +using System.Text.Json; +using Xunit; + +namespace Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library.Tests; + +public class UniversalDidResolverServiceTests +{ + private readonly IFixture _fixture; + private readonly UniversalDidResolverSettings _settings; + private readonly IUniversalDidResolverService _sut; + public UniversalDidResolverServiceTests() + { + _fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true }); + _fixture.ConfigureFixture(); + + _settings = new UniversalDidResolverSettings + { + UniversalResolverAddress = "https://dev.uniresolver.io/" + }; + _fixture.Inject(Options.Create(_settings)); + _sut = new UniversalDidResolverService(_fixture.Freeze()); + } + + [Fact] + public async Task ValidateDid_ReturnsTrue_WhenDidIsValid() + { + // Arrange + const string did = "did:web:123"; + HttpRequestMessage? request = null; + var didDocumentJson = "{\"id\":\"did:web:123\"}"; + using var doc = JsonDocument.Parse(didDocumentJson); + var didValidationResult = new DidValidationResult(new DidResolutionMetadata(null), doc.RootElement.Clone()); + using var responseMessage = new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent(JsonSerializer.Serialize(didValidationResult)) + }; + _fixture.ConfigureHttpClientFactoryFixture("universalResolver", responseMessage, requestMessage => request = requestMessage, _settings.UniversalResolverAddress); + + // Mock ValidateSchema to return true + var universalDidResolverService = _fixture.Freeze(); + A.CallTo(() => universalDidResolverService.ValidateSchema(A._, A._)).Returns(true); + + // Act + var result = await _sut.ValidateDid(did, CancellationToken.None); + + // Assert + request.Should().NotBeNull(); + request!.RequestUri.Should().NotBeNull(); + request.RequestUri!.AbsoluteUri.Should().Be($"https://dev.uniresolver.io/1.0/identifiers/{Uri.EscapeDataString(did)}"); + } + + [Fact] + public async Task ValidateDid_ReturnsFalse_WhenDidHasError() + { + // Arrange + const string did = "did:web:123"; + HttpRequestMessage? request = null; + var didDocumentJson = "{\"id\":\"did:web:123\"}"; + using var doc = JsonDocument.Parse(didDocumentJson); + var didValidationResult = new DidValidationResult( + new DidResolutionMetadata("notFound"), + doc.RootElement.Clone() + ); + using var responseMessage = new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent(JsonSerializer.Serialize(didValidationResult)) + }; + _fixture.ConfigureHttpClientFactoryFixture("universalResolver", responseMessage, requestMessage => request = requestMessage, _settings.UniversalResolverAddress); + + // Act + async Task Act() => await _sut.ValidateDid(did, CancellationToken.None); + + // Assert + var ex = await Assert.ThrowsAsync(Act); + ex.Message.Should().Contain("DID validation failed during validation"); + } + + [Fact] + public async Task ValidateDid_ThrowNotFoundException_WhenDidNotResolved() + { + // Arrange + const string did = "did:web:123"; + HttpRequestMessage? request = null; + using var emptyDoc = JsonDocument.Parse("{}"); + var didValidationResult = new DidValidationResult(new DidResolutionMetadata(null), emptyDoc.RootElement.Clone()); + using var responseMessage = new HttpResponseMessage + { + StatusCode = HttpStatusCode.BadRequest, + Content = new StringContent(JsonSerializer.Serialize(didValidationResult)) + }; + _fixture.ConfigureHttpClientFactoryFixture( + "universalResolver", + responseMessage, + requestMessage => request = requestMessage, + _settings.UniversalResolverAddress + ); + + // Act + async Task Act() => await _sut.ValidateDid(did, CancellationToken.None); + + // Assert + var ex = await Assert.ThrowsAsync(Act); + ex.Message.Should().Be("DID URL could not be reached by the exetenal resolver, 404 error"); + request.Should().NotBeNull(); + request!.RequestUri.Should().NotBeNull(); + request.RequestUri!.AbsoluteUri.Should().Be($"https://dev.uniresolver.io/1.0/identifiers/{Uri.EscapeDataString(did)}"); + } + + [Fact] + public async Task ValidateDid_ReturnsFalse_WhenDeserializationReturnsNull() + { + // Arrange + const string did = "did:web:123"; + HttpRequestMessage? request = null; + using var responseMessage = new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + Content = null // Simulating an empty response + }; + _fixture.ConfigureHttpClientFactoryFixture("universalResolver", responseMessage, requestMessage => request = requestMessage, _settings.UniversalResolverAddress); + + // Act + async Task Act() => await _sut.ValidateDid(did, CancellationToken.None); + + // Assert + var ex = await Assert.ThrowsAsync(Act); + ex.Message.Should().Contain("The input does not contain any JSON tokens"); + request.Should().NotBeNull(); + request!.RequestUri.Should().NotBeNull(); + request.RequestUri!.AbsoluteUri.Should().Be($"https://dev.uniresolver.io/1.0/identifiers/{Uri.EscapeDataString(did)}"); + } + + [Fact] + public async Task ValidateDid_ThrowsException_WhenValidationResultIsNull() + { + // Arrange + const string did = "did:web:123"; + HttpRequestMessage? request = null; + using var responseMessage = new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent("null", System.Text.Encoding.UTF8, "application/json") + }; + _fixture.ConfigureHttpClientFactoryFixture( + "universalResolver", + responseMessage, + requestMessage => request = requestMessage, + _settings.UniversalResolverAddress + ); + + // Act + async Task Act() => await _sut.ValidateDid(did, CancellationToken.None); + + // Assert + var ex = await Assert.ThrowsAsync(Act); + ex.Message.Should().Contain("DID validation failed: No result returned."); + request.Should().NotBeNull(); + request!.RequestUri.Should().NotBeNull(); + request.RequestUri!.AbsoluteUri.Should().Be($"https://dev.uniresolver.io/1.0/identifiers/{Uri.EscapeDataString(did)}"); + } +} diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRepositoryTests.cs b/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRepositoryTests.cs index eda817deff..db915d16ba 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRepositoryTests.cs +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRepositoryTests.cs @@ -988,9 +988,10 @@ public async Task GetAllMemberCompaniesBPN_withNull_ReturnsExpected() var result = await sut.GetAllMemberCompaniesBPNAsync(null).ToListAsync(); // Assert - result.Should().NotBeNull().And.HaveCount(5).And.Satisfy( + result.Should().NotBeNull().And.HaveCount(6).And.Satisfy( x => x == "BPNL07800HZ01643", x => x == "BPNL00000003AYRE", + x => x == "BPNL00000000BYOW", x => x == "BPNL00000003CRHK", x => x == "BPNL00000003CRHL", x => x == "BPNL00000001TEST"); @@ -1104,4 +1105,48 @@ public async Task IsExistingCompany_WithNotExistingCompany_ReturnsFalse() } #endregion + + #region BringYourOwnWallet + + [Fact] + public async Task IsBringYourOwnWallet_ReturnsTrue() + { + // Arrange + var (sut, _) = await CreateSut(); + + // Act + var result = await sut.IsBringYourOwnWallet(Guid.Parse("c20ba2f7-ac9d-48d4-9498-a46be50c9271")).ConfigureAwait(ConfigureAwaitOptions.None); + + // Assert + result.Should().BeTrue(); + } + + [Fact] + public async Task IsBringYourOwnWallet_NoMatchingClientId_ReturnsFalse() + { + // Arrange + var (sut, _) = await CreateSut(); + + // Act + var result = await sut.IsBringYourOwnWallet(Guid.NewGuid()).ConfigureAwait(ConfigureAwaitOptions.None); + + // Assert + result.Should().BeFalse(); + } + + [Fact] + public async Task CreateCompanyWallet_ReturnsExpectedResult() + { + // Arrange + var (sut, context) = await CreateSut(); + + // Act + sut.CreateCustomerWallet(Guid.NewGuid(), "did:web:example.com"); + + // Assert + context.CompanyWalletDatas.Should().NotBeEmpty(); + context.CompanyWalletDatas.Should().HaveCount(2); + } + + #endregion } diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/companies.unittest.json b/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/companies.unittest.json index 79959e6335..e8847e211f 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/companies.unittest.json +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/companies.unittest.json @@ -89,5 +89,15 @@ "company_status_id": 1, "address_id": "27418031-d6b5-442b-8802-3f653c6679f7", "self_description_document_id": null + }, + { + "id": "554f23d5-669e-48ed-b06b-9a84dfa017c5", + "date_created": "2022-03-24 18:01:33.306000 +00:00", + "business_partner_number": "BPNL00000000BYOW", + "name": "Bring Your Own Wallet Company", + "shortname": "CX-Operator", + "company_status_id": 2, + "address_id": "b4db3945-19a7-4a50-97d6-e66e8dfd04fb", + "self_description_document_id": "00000000-0000-0000-0000-000000000009" } ] diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/company_applications.unittest.json b/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/company_applications.unittest.json index 919554ba9d..60e8bd4bb5 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/company_applications.unittest.json +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/company_applications.unittest.json @@ -48,5 +48,15 @@ "checklist_process_id": "0187aba6-a66e-4fe5-9519-c4254a4a63d8", "company_application_type_id": 1, "last_editor_id": null + }, + { + "id": "c20ba2f7-ac9d-48d4-9498-a46be50c9271", + "date_created": "2023-11-06 10:01:33.439000 +00:00", + "date_last_changed": "2023-11-06 10:01:33.439000 +00:00", + "application_status_id": 1, + "company_id": "554f23d5-669e-48ed-b06b-9a84dfa017c5", + "checklist_process_id": null, + "company_application_type_id": 1, + "last_editor_id": null } ] diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/company_wallet_data.unittest.json b/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/company_wallet_data.unittest.json index 9d8a55daa8..7dc848ab3b 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/company_wallet_data.unittest.json +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/company_wallet_data.unittest.json @@ -9,5 +9,17 @@ "initialization_vector": null, "encryption_mode": 0, "authentication_service_url": "https://example.org/auth" + }, + { + "id": "84571142-db6b-4b86-b7af-1039dc0ffb5d", + "company_id": "554f23d5-669e-48ed-b06b-9a84dfa017c5", + "did": "did:web:test", + "did_document": {}, + "client_id": "BYOWCLIENT_ID", + "client_secret": "", + "initialization_vector": null, + "encryption_mode": 0, + "authentication_service_url": "https://example.org/auth" } + ] \ No newline at end of file diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/process_steps.unittest.json b/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/process_steps.unittest.json index 76f2d6de1b..1976553b74 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/process_steps.unittest.json +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/process_steps.unittest.json @@ -270,5 +270,21 @@ "process_id": "d91ac968-ffe8-466f-b34a-ae2517b7c6ab", "date_created": "2023-10-11 08:15:20.479000 +00:00", "date_last_changed": null + }, + { + "id": "dada25f9-dcef-4c18-9eaf-479d9a855e17", + "process_step_type_id": 500, + "process_step_status_id": 2, + "process_id": "d6f8e638-c8f2-4803-b910-6d379241905a", + "date_created": "2023-10-11 08:15:20.479000 +00:00", + "date_last_changed": null + }, + { + "id": "3dbf0030-cec1-4471-9647-8dbb06350ebb", + "process_step_type_id": 502, + "process_step_status_id": 1, + "process_id": "d6f8e638-c8f2-4803-b910-6d379241905a", + "date_created": "2023-10-11 08:15:20.479000 +00:00", + "date_last_changed": null } ] diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/processes.unittest.json b/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/processes.unittest.json index ca72277ac5..56930654af 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/processes.unittest.json +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/Seeder/Data/processes.unittest.json @@ -106,5 +106,11 @@ "process_type_id" : 3, "lock_expiry_date" : "2023-03-01 00:00:00.000000 +00:00", "version" : "deadbeef-dead-beef-dead-beefdeadbeef" + }, + { + "id": "d6f8e638-c8f2-4803-b910-6d379241905a", + "process_type_id" : 3, + "lock_expiry_date" : "2023-03-01 00:00:00.000000 +00:00", + "version" : "deadbeef-dead-beef-dead-beefdeadbeef" } ] diff --git a/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs b/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs new file mode 100644 index 0000000000..e8883904a4 --- /dev/null +++ b/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs @@ -0,0 +1,117 @@ +/******************************************************************************** + * Copyright (c) 2025 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 FakeItEasy; +using FluentAssertions; +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.Registration.Service.BusinessLogic; +using Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library; +using Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library.Models; +using System.Text.Json; +using Xunit; + +namespace Org.Eclipse.TractusX.Portal.Backend.Registration.Service.Tests.BusinessLogic; + +public class BringYourOwnWalletBuisinessLogicTests +{ + private readonly IUniversalDidResolverService _universalDidResolverService; + private readonly IBringYourOwnWalletBusinessLogic _sut; + + public BringYourOwnWalletBuisinessLogicTests() + { + _universalDidResolverService = A.Fake(); + _sut = new BringYourOwnWalletBusinessLogic(_universalDidResolverService); + } + + [Fact] + public async Task ValidateDid_Completes_WhenDidIsValid() + { + // Arrange + const string did = "did:web:123"; + var didDocument = JsonDocument.Parse("{\"id\":\"did:web:123\"}").RootElement; + var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); + A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) + .ReturnsLazily(() => Task.FromResult(validationResult)); + A.CallTo(() => _universalDidResolverService.ValidateSchema(didDocument, A._)) + .ReturnsLazily(() => true); + + // Act & Assert + await _sut.Invoking(s => s.ValidateDid(did, CancellationToken.None)) + .Should().NotThrowAsync(); + } + + [Fact] + public async Task ValidateDid_ThrowsSericeException_WhenDidHasError() + { + // Arrange + const string did = "did:web:123"; + var didDocument = JsonDocument.Parse("{\"id\":\"did:web:123\"}").RootElement; + var validationResult = new DidValidationResult(new DidResolutionMetadata("notFound"), didDocument); + A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) + .ReturnsLazily(() => Task.FromResult(validationResult)); + + // Act + var act = () => _sut.ValidateDid(did, CancellationToken.None); + + // Assert + await act.Should().ThrowAsync() + .WithMessage("DID validation failed. DID Document is not valid."); + } + + [Fact] + public async Task ValidateDid_ThrowsServiceException_WhenSchemaIsInvalid() + { + // Arrange + const string did = "did:web:123"; + var didDocument = JsonDocument.Parse("{\"id\":\"did:web:123\"}").RootElement; + var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); + A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) + .ReturnsLazily(() => Task.FromResult(validationResult)); + A.CallTo(() => _universalDidResolverService.ValidateSchema(didDocument, A._)) + .ReturnsLazily(() => false); + + // Act + var act = () => _sut.ValidateDid(did, CancellationToken.None); + + // Assert + await act.Should().ThrowAsync() + .WithMessage("DID validation failed. DID Document is not valid."); + } + + [Fact] + public async Task ValidateDid_ThrowsServiceException_WhenValidationResultIsNull() + { + // Arrange + const string did = "did:web:empty"; + var didDocument = JsonDocument.Parse("{}").RootElement; + var validationResult = new DidValidationResult( + new DidResolutionMetadata(Error: null), + didDocument + ); + A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) + .ReturnsLazily(_ => Task.FromResult(validationResult)); + + // Act + var act = () => _sut.ValidateDid(did, CancellationToken.None); + + // Assert + await act.Should().ThrowAsync() + .WithMessage("DID validation failed. DID Document is not valid."); + } +} diff --git a/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs b/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs index b5328863d5..2d205cd003 100644 --- a/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs +++ b/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs @@ -1058,7 +1058,6 @@ public async Task SetCompanyWithAddressAsync_WithoutInitialCompanyAddress_Create A.CallTo(() => _companyRepository.CreateCustomerWallet(A._, A._)) .MustNotHaveHappened(); - company.Should().NotBeNull(); address.Should().NotBeNull(); diff --git a/tests/registration/Registration.Service.Tests/Controller/BringYourOwnWalletControllerTests.cs b/tests/registration/Registration.Service.Tests/Controller/BringYourOwnWalletControllerTests.cs new file mode 100644 index 0000000000..3408cdf2a8 --- /dev/null +++ b/tests/registration/Registration.Service.Tests/Controller/BringYourOwnWalletControllerTests.cs @@ -0,0 +1,78 @@ +/******************************************************************************** + * Copyright (c) 2025 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 FakeItEasy; +using FluentAssertions; +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Identity; +using Org.Eclipse.TractusX.Portal.Backend.Registration.Service.BusinessLogic; +using Org.Eclipse.TractusX.Portal.Backend.Registration.Service.Controllers; +using Org.Eclipse.TractusX.Portal.Backend.Tests.Shared.Extensions; +using System.Net; +using Xunit; + +namespace Org.Eclipse.TractusX.Portal.Backend.Registration.Service.Tests; + +public class BringYourOwnWalletControllerTests +{ + private readonly IIdentityData _identity; + private readonly BringYourOwnWalletController _controller; + private readonly IBringYourOwnWalletBusinessLogic _bringYourOwnWalletBusinessLogicFake; + + public BringYourOwnWalletControllerTests() + { + _identity = A.Fake(); + A.CallTo(() => _identity.IdentityId).Returns(Guid.NewGuid()); + A.CallTo(() => _identity.IdentityTypeId).Returns(IdentityTypeId.COMPANY_USER); + A.CallTo(() => _identity.CompanyId).Returns(Guid.NewGuid()); + _bringYourOwnWalletBusinessLogicFake = A.Fake(); + _controller = new BringYourOwnWalletController(_bringYourOwnWalletBusinessLogicFake); + _controller.AddControllerContextWithClaimAndBearer("ac-token", _identity); + } + + [Fact] + public async Task ValidateDid_ShouldBeValid() + { + // Arrange + var did = "did:web:example.com"; + A.CallTo(() => _bringYourOwnWalletBusinessLogicFake.ValidateDid(did, A._)).DoesNothing(); + + // Act + await _controller.validateDid(did, CancellationToken.None); + + // Assert + A.CallTo(() => _bringYourOwnWalletBusinessLogicFake.ValidateDid(did, CancellationToken.None)).MustHaveHappenedOnceExactly(); + } + + [Fact] + public async Task ValidateDid_WhenDidIsInvalid_ShouldThrowServiceException() + { + // Arrange + var did = "did:web:invalid"; + var exception = new ServiceException("DID validation failed", HttpStatusCode.BadRequest); + A.CallTo(() => _bringYourOwnWalletBusinessLogicFake.ValidateDid(did, A._)).Throws(exception); + + // Act + Func act = async () => await _controller.validateDid(did, CancellationToken.None); + + // Assert + await act.Should().ThrowAsync() + .WithMessage("DID validation failed"); + } +} From 8ca45df7a8149709292b05b61ddd1706117d02a9 Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Tue, 8 Jul 2025 15:20:10 +0200 Subject: [PATCH 04/38] Added step to trasmit the did to other systems --- docs/api/registration-service.yaml | 12 - .../RegistrationBusinessLogic.cs | 2 +- .../IssuerComponentBusinessLogic.cs | 24 +- .../IUniversalDidResolverService.cs | 2 +- .../Models/DidValidationResult.cs | 2 +- .../UniversalDidResolverService.cs | 2 +- .../Repositories/CompanyRepository.cs | 4 +- .../Repositories/ICompanyRepository.cs | 2 +- .../BringYourOwnWalletBusinessLogic.cs | 6 +- .../IBringYourOwnWalletBusinessLogic.cs | 5 +- .../RegistrationBusinessLogic.cs | 16 +- .../RegistrationBusinessLogicTest.cs | 4 +- .../IssuerComponentBusinessLogicTests.cs | 20 +- .../UniversalDidResolverServiceTest.cs | 10 +- .../CompanyRepositoryTests.cs | 4 +- .../BringYourOwnWalletBuisinessLogicTests.cs | 9 +- .../RegistrationBusinessLogicTest.cs | 217 +++++++++++------- .../BringYourOwnWalletControllerTests.cs | 4 +- 18 files changed, 196 insertions(+), 149 deletions(-) diff --git a/docs/api/registration-service.yaml b/docs/api/registration-service.yaml index 208eeed66a..b557c5c29b 100644 --- a/docs/api/registration-service.yaml +++ b/docs/api/registration-service.yaml @@ -51,18 +51,6 @@ paths: text/json: schema: $ref: '#/components/schemas/ErrorResponse' - '502': - description: Bad Gateway - content: - text/plain: - schema: - $ref: '#/components/schemas/ErrorResponse' - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - text/json: - schema: - $ref: '#/components/schemas/ErrorResponse' '500': description: Internal Server Error '401': diff --git a/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs index ee5e4171ff..da3d37985f 100644 --- a/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs @@ -301,7 +301,7 @@ private async Task UpdateCompanyBpnInternal(Guid applicationId, string bpn) private async Task CreateWalletOrBpnCredentialStepAsync(Guid applicationId) { var isWalletCustomerProvider = await portalRepositories.GetInstance().IsBringYourOwnWallet(applicationId); - return isWalletCustomerProvider ? ProcessStepTypeId.REQUEST_BPN_CREDENTIAL : CreateWalletStep(); + return isWalletCustomerProvider ? ProcessStepTypeId.TRANSMIT_BPN_DID : CreateWalletStep(); } /// public async Task ProcessClearinghouseResponseAsync(ClearinghouseResponseData data, string bpn, CancellationToken cancellationToken) diff --git a/src/externalsystems/IssuerComponent.Library/BusinessLogic/IssuerComponentBusinessLogic.cs b/src/externalsystems/IssuerComponent.Library/BusinessLogic/IssuerComponentBusinessLogic.cs index 9162d9cec4..4597dfd02d 100644 --- a/src/externalsystems/IssuerComponent.Library/BusinessLogic/IssuerComponentBusinessLogic.cs +++ b/src/externalsystems/IssuerComponent.Library/BusinessLogic/IssuerComponentBusinessLogic.cs @@ -69,11 +69,11 @@ public class IssuerComponentBusinessLogic( var secret = CryptoHelper.Decrypt(walletInformation.ClientSecret, walletInformation.InitializationVector, Convert.FromHexString(cryptoConfig.EncryptionKey), cryptoConfig.CipherMode, cryptoConfig.PaddingMode); var callbackUrl = $"{_settings.CallbackBaseUrl}/api/administration/registration/issuer/bpncredential"; - var data = new CreateBpnCredentialRequest(holder, businessPartnerNumber, - isBringYourOwnWallet - ? null + var data = new CreateBpnCredentialRequest(holder, businessPartnerNumber, + isBringYourOwnWallet + ? null : new TechnicalUserDetails(walletInformation.WalletUrl, walletInformation.ClientId, secret), callbackUrl); - + await service.CreateBpnlCredential(data, cancellationToken).ConfigureAwait(false); return new IApplicationChecklistService.WorkerChecklistProcessStepExecutionResult( ProcessStepStatusId.DONE, @@ -142,12 +142,12 @@ public async Task StoreBpnlCredentialResponse(Guid applicationId, IssuerResponse var secret = CryptoHelper.Decrypt(walletInformation.ClientSecret, walletInformation.InitializationVector, Convert.FromHexString(cryptoConfig.EncryptionKey), cryptoConfig.CipherMode, cryptoConfig.PaddingMode); var callbackUrl = $"{_settings.CallbackBaseUrl}/api/administration/registration/issuer/membershipcredential"; - - var data = new CreateMembershipCredentialRequest(holder, businessPartnerNumber, "catena-x", - isBringYourOwnWallet - ? null + + var data = new CreateMembershipCredentialRequest(holder, businessPartnerNumber, "catena-x", + isBringYourOwnWallet + ? null : new TechnicalUserDetails(walletInformation.WalletUrl, walletInformation.ClientId, secret), callbackUrl); - + await service.CreateMembershipCredential(data, cancellationToken).ConfigureAwait(false); return new IApplicationChecklistService.WorkerChecklistProcessStepExecutionResult( ProcessStepStatusId.DONE, @@ -208,9 +208,9 @@ public async Task CreateFrameworkCredentialData(Guid useCaseFrameworkVersi var cryptoConfig = _settings.EncryptionConfigs.SingleOrDefault(x => x.Index == walletInformation.EncryptionMode) ?? throw new ConfigurationException($"EncryptionModeIndex {walletInformation.EncryptionMode} is not configured"); var secret = CryptoHelper.Decrypt(walletInformation.ClientSecret, walletInformation.InitializationVector, Convert.FromHexString(cryptoConfig.EncryptionKey), cryptoConfig.CipherMode, cryptoConfig.PaddingMode); - - var data = new CreateFrameworkCredentialRequest(holder, businessPartnerNumber, frameworkId, useCaseFrameworkVersionId, - isBringYourOwnWallet + + var data = new CreateFrameworkCredentialRequest(holder, businessPartnerNumber, frameworkId, useCaseFrameworkVersionId, + isBringYourOwnWallet ? null : new TechnicalUserDetails(walletInformation.WalletUrl, walletInformation.ClientId, secret), null); return await service.CreateFrameworkCredential(data, token, cancellationToken).ConfigureAwait(false); diff --git a/src/externalsystems/UniversalDidResolver.Library/IUniversalDidResolverService.cs b/src/externalsystems/UniversalDidResolver.Library/IUniversalDidResolverService.cs index ed4e7a1c65..dd57ad8224 100644 --- a/src/externalsystems/UniversalDidResolver.Library/IUniversalDidResolverService.cs +++ b/src/externalsystems/UniversalDidResolver.Library/IUniversalDidResolverService.cs @@ -33,5 +33,5 @@ public interface IUniversalDidResolverService /// true if the did is valid, otherwise false Task ValidateDid(string did, CancellationToken cancellationToken); - Task ValidateSchema(JsonElement content, CancellationToken cancellationToken); + Task ValidateSchema(JsonDocument content, CancellationToken cancellationToken); } diff --git a/src/externalsystems/UniversalDidResolver.Library/Models/DidValidationResult.cs b/src/externalsystems/UniversalDidResolver.Library/Models/DidValidationResult.cs index 228cbae03e..9e586ac625 100644 --- a/src/externalsystems/UniversalDidResolver.Library/Models/DidValidationResult.cs +++ b/src/externalsystems/UniversalDidResolver.Library/Models/DidValidationResult.cs @@ -25,7 +25,7 @@ namespace Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library.Model public record DidValidationResult( [property: JsonPropertyName("didResolutionMetadata")] DidResolutionMetadata DidResolutionMetadata, - [property: JsonPropertyName("didDocument")] JsonElement DidDocument + [property: JsonPropertyName("didDocument")] JsonDocument DidDocument ); diff --git a/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolverService.cs b/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolverService.cs index af17714789..80133e3272 100644 --- a/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolverService.cs +++ b/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolverService.cs @@ -68,7 +68,7 @@ public async Task ValidateDid(string did, CancellationToken return validationResult; } - public async Task ValidateSchema(JsonElement content, CancellationToken cancellationToken) + public async Task ValidateSchema(JsonDocument content, CancellationToken cancellationToken) { var location = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? throw new UnexpectedConditionException("Assembly location must be set"); diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs index 6f0b185d82..72118e9536 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs @@ -67,13 +67,13 @@ Address ICompanyRepository.CreateAddress(string city, string streetname, string setOptionalParameters?.Invoke(address); return context.Addresses.Add(address).Entity; } - public void CreateCustomerWallet(Guid companyId, string did) + public void CreateCustomerWallet(Guid companyId, string did, JsonDocument didDocument) { var wallet = new CompanyWalletData( Guid.NewGuid(), companyId, did, - JsonDocument.Parse("{}"), + didDocument, BringYourOwnWalletClientFields.Identification, new byte[1], new byte[1], diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs index b2d6bf5053..a464fe678d 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs @@ -42,7 +42,7 @@ public interface ICompanyRepository void AttachAndModifyCompany(Guid companyId, Action? initialize, Action modify); - void CreateCustomerWallet(Guid companyId, string did); + void CreateCustomerWallet(Guid companyId, string did, JsonDocument didDocument); Task IsBringYourOwnWallet(Guid applicationId); diff --git a/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs b/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs index e4033bb829..74fc9f79de 100644 --- a/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs +++ b/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs @@ -1,4 +1,5 @@ /******************************************************************************** + * Copyright (c) 2025 Cofinity-X GmbH * Copyright (c) 2025 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional @@ -18,7 +19,7 @@ ********************************************************************************/ using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; using Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library; -using System.Net; +using System.Text.Json; namespace Org.Eclipse.TractusX.Portal.Backend.Registration.Service.BusinessLogic { @@ -30,7 +31,7 @@ public BringYourOwnWalletBusinessLogic(IUniversalDidResolverService universalRes { _universalResolverService = universalResolverService; } - public async Task ValidateDid(string did, CancellationToken cancellationToken) + public async Task ValidateDid(string did, CancellationToken cancellationToken) { var validationResult = await _universalResolverService.ValidateDid(did, cancellationToken); var isSchemaValid = await _universalResolverService.ValidateSchema( @@ -42,6 +43,7 @@ public async Task ValidateDid(string did, CancellationToken cancellationToken) { throw new UnsupportedMediaTypeException("DID validation failed. DID Document is not valid."); } + return validationResult.DidDocument; } } } diff --git a/src/registration/Registration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs b/src/registration/Registration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs index cba51186ed..613f06c9df 100644 --- a/src/registration/Registration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs +++ b/src/registration/Registration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs @@ -1,4 +1,5 @@ /******************************************************************************** + * Copyright (c) 2025 Cofinity-X GmbH * Copyright (c) 2025 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional @@ -17,10 +18,12 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +using System.Text.Json; + namespace Org.Eclipse.TractusX.Portal.Backend.Registration.Service.BusinessLogic { public interface IBringYourOwnWalletBusinessLogic { - Task ValidateDid(string did, CancellationToken cancellationToken); + Task ValidateDid(string did, CancellationToken cancellationToken); } } diff --git a/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs b/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs index e92f105c62..e05e7affc7 100644 --- a/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs +++ b/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs @@ -41,6 +41,7 @@ using Org.Eclipse.TractusX.Portal.Backend.Registration.Common; using Org.Eclipse.TractusX.Portal.Backend.Registration.Service.ErrorHandling; using Org.Eclipse.TractusX.Portal.Backend.Registration.Service.Model; +using Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library; using System.Collections.Immutable; using System.Text.RegularExpressions; using System.Web; @@ -57,11 +58,12 @@ public class RegistrationBusinessLogic( IApplicationChecklistCreationService checklistService, IIdentityService identityService, IDateTimeProvider dateTimeProvider, - IMailingProcessCreation mailingProcessCreation) : IRegistrationBusinessLogic + IMailingProcessCreation mailingProcessCreation, + IBringYourOwnWalletBusinessLogic bringYourOwnWalletBusinessLogic) : IRegistrationBusinessLogic { private readonly IIdentityData _identityData = identityService.IdentityData; private readonly RegistrationSettings _settings = settings.Value; - + private readonly IBringYourOwnWalletBusinessLogic _bringYourOwnWalletBusinessLogic = bringYourOwnWalletBusinessLogic; private static readonly Regex bpnRegex = new(@"(\w|\d){16}", RegexOptions.None, TimeSpan.FromSeconds(1)); public IAsyncEnumerable GetClientRolesCompositeAsync() => @@ -272,14 +274,16 @@ await companyDetails.ValidateDatabaseData( } if (!string.IsNullOrEmpty(companyDetails.HolderDid)) { - CreateCustomerWallet(companyDetails.CompanyId, companyDetails.HolderDid, companyRepository); + await CreateCustomerWalletAsync(companyDetails.CompanyId, companyDetails.HolderDid, companyRepository); } await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); } - private static void CreateCustomerWallet(Guid companyId, string did, ICompanyRepository companyRepository) => companyRepository.CreateCustomerWallet( - companyId, - did); + private async Task CreateCustomerWalletAsync(Guid companyId, string did, ICompanyRepository companyRepository) + { + var didDocument = await _bringYourOwnWalletBusinessLogic.ValidateDid(did, CancellationToken.None).ConfigureAwait(ConfigureAwaitOptions.None); + companyRepository.CreateCustomerWallet(companyId, did, didDocument); + } private async Task GetAndValidateApplicationData(Guid applicationId, CompanyDetailData companyDetails, IApplicationRepository applicationRepository) { var companyApplicationData = await applicationRepository diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs index 09b39d2bcc..8ee5f8766d 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs @@ -466,8 +466,8 @@ public async Task SetRegistrationVerification_WithApproval_CallsExpected(bool us } [Theory] - [InlineData(true, ProcessStepTypeId.REQUEST_BPN_CREDENTIAL)] - [InlineData(false, ProcessStepTypeId.REQUEST_BPN_CREDENTIAL)] + [InlineData(true, ProcessStepTypeId.TRANSMIT_BPN_DID)] + [InlineData(false, ProcessStepTypeId.TRANSMIT_BPN_DID)] public async Task SetRegistrationVerification_WithApproval_BYOW_CallsExpected(bool useDimWallet, ProcessStepTypeId expectedTypeId) { // Arrange diff --git a/tests/externalsystems/IssuerComponent.Library.Tests/IssuerComponentBusinessLogicTests.cs b/tests/externalsystems/IssuerComponent.Library.Tests/IssuerComponentBusinessLogicTests.cs index 25aee05213..343a0427e3 100644 --- a/tests/externalsystems/IssuerComponent.Library.Tests/IssuerComponentBusinessLogicTests.cs +++ b/tests/externalsystems/IssuerComponent.Library.Tests/IssuerComponentBusinessLogicTests.cs @@ -135,11 +135,11 @@ public async Task CreateBpnlCredential_WithValid_CallsExpected() x.CallbackUrl == "https://example.org/callback/api/administration/registration/issuer/bpncredential"), A._)) .MustHaveHappenedOnceExactly(); - + //bring your own wallet true A.CallTo(() => _companyRepository.IsBringYourOwnWallet(A._)).Returns(true); result = await _sut.CreateBpnlCredential(context, CancellationToken.None); - + A.CallTo(() => _issuerComponentService .CreateBpnlCredential( A.That.Matches(x => @@ -149,7 +149,7 @@ public async Task CreateBpnlCredential_WithValid_CallsExpected() ), A._)) .MustHaveHappenedOnceExactly(); - + //call should be 2 times now A.CallTo(() => _applicationRepository.GetBpnlCredentialIformationByApplicationId(IdWithBpn)) .MustHaveHappenedTwiceExactly(); @@ -355,7 +355,7 @@ public async Task CreateMembershipCredential_WithValid_CallsExpected() //bring your own wallet false A.CallTo(() => _companyRepository.IsBringYourOwnWallet(A._)).Returns(false); - + // Act var result = await _sut.CreateMembershipCredential(context, CancellationToken.None); @@ -373,10 +373,10 @@ public async Task CreateMembershipCredential_WithValid_CallsExpected() ), A._)) .MustHaveHappenedOnceExactly(); - + //bring your own wallet false A.CallTo(() => _companyRepository.IsBringYourOwnWallet(A._)).Returns(true); - + // Act result = await _sut.CreateMembershipCredential(context, CancellationToken.None); A.CallTo(() => _issuerComponentService @@ -588,7 +588,7 @@ public async Task CreateFrameworkCredential_WithValid_CallsExpected() //bring your own wallet false A.CallTo(() => _companyRepository.IsBringYourOwnWallet(A._)).Returns(false); - + // Act var result = await _sut.CreateFrameworkCredentialData(useCaseFrameworkVersionId, "TRACEABILITY_FRAMEWORK", identityId, Token, CancellationToken.None); @@ -607,10 +607,10 @@ public async Task CreateFrameworkCredential_WithValid_CallsExpected() Token, A._)) .MustHaveHappenedOnceExactly(); - + //bring your own wallet true A.CallTo(() => _companyRepository.IsBringYourOwnWallet(A._)).Returns(true); - + // Act result = await _sut.CreateFrameworkCredentialData(useCaseFrameworkVersionId, "TRACEABILITY_FRAMEWORK", identityId, Token, CancellationToken.None); @@ -626,7 +626,7 @@ public async Task CreateFrameworkCredential_WithValid_CallsExpected() Token, A._)) .MustHaveHappenedOnceExactly(); - + //call is 2 times A.CallTo(() => _companyRepository.GetWalletData(identityId)) .MustHaveHappenedTwiceExactly(); diff --git a/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolverServiceTest.cs b/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolverServiceTest.cs index 8496286f0e..bef0a5655d 100644 --- a/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolverServiceTest.cs +++ b/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolverServiceTest.cs @@ -59,7 +59,7 @@ public async Task ValidateDid_ReturnsTrue_WhenDidIsValid() HttpRequestMessage? request = null; var didDocumentJson = "{\"id\":\"did:web:123\"}"; using var doc = JsonDocument.Parse(didDocumentJson); - var didValidationResult = new DidValidationResult(new DidResolutionMetadata(null), doc.RootElement.Clone()); + var didValidationResult = new DidValidationResult(new DidResolutionMetadata(null), doc); using var responseMessage = new HttpResponseMessage { StatusCode = HttpStatusCode.OK, @@ -69,7 +69,7 @@ public async Task ValidateDid_ReturnsTrue_WhenDidIsValid() // Mock ValidateSchema to return true var universalDidResolverService = _fixture.Freeze(); - A.CallTo(() => universalDidResolverService.ValidateSchema(A._, A._)).Returns(true); + A.CallTo(() => universalDidResolverService.ValidateSchema(A._, A._)).Returns(true); // Act var result = await _sut.ValidateDid(did, CancellationToken.None); @@ -90,7 +90,7 @@ public async Task ValidateDid_ReturnsFalse_WhenDidHasError() using var doc = JsonDocument.Parse(didDocumentJson); var didValidationResult = new DidValidationResult( new DidResolutionMetadata("notFound"), - doc.RootElement.Clone() + doc ); using var responseMessage = new HttpResponseMessage { @@ -114,7 +114,7 @@ public async Task ValidateDid_ThrowNotFoundException_WhenDidNotResolved() const string did = "did:web:123"; HttpRequestMessage? request = null; using var emptyDoc = JsonDocument.Parse("{}"); - var didValidationResult = new DidValidationResult(new DidResolutionMetadata(null), emptyDoc.RootElement.Clone()); + var didValidationResult = new DidValidationResult(new DidResolutionMetadata(null), emptyDoc); using var responseMessage = new HttpResponseMessage { StatusCode = HttpStatusCode.BadRequest, @@ -132,7 +132,7 @@ public async Task ValidateDid_ThrowNotFoundException_WhenDidNotResolved() // Assert var ex = await Assert.ThrowsAsync(Act); - ex.Message.Should().Be("DID URL could not be reached by the exetenal resolver, 404 error"); + ex.Message.Should().Be("DID URL could not be reached by the external resolver, 404 error"); request.Should().NotBeNull(); request!.RequestUri.Should().NotBeNull(); request.RequestUri!.AbsoluteUri.Should().Be($"https://dev.uniresolver.io/1.0/identifiers/{Uri.EscapeDataString(did)}"); diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRepositoryTests.cs b/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRepositoryTests.cs index db915d16ba..6382f9f560 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRepositoryTests.cs +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRepositoryTests.cs @@ -27,6 +27,7 @@ using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Entities; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; using System.Collections.Immutable; +using System.Text.Json; using Xunit.Extensions.AssemblyFixture; namespace Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Tests; @@ -1139,9 +1140,10 @@ public async Task CreateCompanyWallet_ReturnsExpectedResult() { // Arrange var (sut, context) = await CreateSut(); + var didDocument = JsonDocument.Parse("{}"); // Provide a valid JSON string or object as needed // Act - sut.CreateCustomerWallet(Guid.NewGuid(), "did:web:example.com"); + sut.CreateCustomerWallet(Guid.NewGuid(), "did:web:example.com", didDocument); // Assert context.CompanyWalletDatas.Should().NotBeEmpty(); diff --git a/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs b/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs index e8883904a4..d24a21b1b4 100644 --- a/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs +++ b/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs @@ -1,4 +1,5 @@ /******************************************************************************** + * Copyright (c) 2025 Cofinity-X GmbH * Copyright (c) 2025 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional @@ -44,7 +45,7 @@ public async Task ValidateDid_Completes_WhenDidIsValid() { // Arrange const string did = "did:web:123"; - var didDocument = JsonDocument.Parse("{\"id\":\"did:web:123\"}").RootElement; + var didDocument = JsonDocument.Parse("{\"id\":\"did:web:123\"}"); var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) .ReturnsLazily(() => Task.FromResult(validationResult)); @@ -61,7 +62,7 @@ public async Task ValidateDid_ThrowsSericeException_WhenDidHasError() { // Arrange const string did = "did:web:123"; - var didDocument = JsonDocument.Parse("{\"id\":\"did:web:123\"}").RootElement; + var didDocument = JsonDocument.Parse("{\"id\":\"did:web:123\"}"); var validationResult = new DidValidationResult(new DidResolutionMetadata("notFound"), didDocument); A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) .ReturnsLazily(() => Task.FromResult(validationResult)); @@ -79,7 +80,7 @@ public async Task ValidateDid_ThrowsServiceException_WhenSchemaIsInvalid() { // Arrange const string did = "did:web:123"; - var didDocument = JsonDocument.Parse("{\"id\":\"did:web:123\"}").RootElement; + var didDocument = JsonDocument.Parse("{\"id\":\"did:web:123\"}"); var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) .ReturnsLazily(() => Task.FromResult(validationResult)); @@ -99,7 +100,7 @@ public async Task ValidateDid_ThrowsServiceException_WhenValidationResultIsNull( { // Arrange const string did = "did:web:empty"; - var didDocument = JsonDocument.Parse("{}").RootElement; + var didDocument = JsonDocument.Parse("{}"); var validationResult = new DidValidationResult( new DidResolutionMetadata(Error: null), didDocument diff --git a/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs b/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs index 2d205cd003..7721d8782e 100644 --- a/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs +++ b/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs @@ -49,6 +49,7 @@ using Org.Eclipse.TractusX.Portal.Backend.Tests.Shared; using Org.Eclipse.TractusX.Portal.Backend.Tests.Shared.Extensions; using System.Collections.Immutable; +using System.Text.Json; using Xunit; using Address = Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Entities.Address; using RegistrationData = Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models.RegistrationData; @@ -74,6 +75,7 @@ public class RegistrationBusinessLogicTest private readonly IPortalProcessStepRepository _processStepRepository; private readonly IIdentityProviderRepository _identityProviderRepository; private readonly IApplicationChecklistCreationService _checklistService; + private readonly IBringYourOwnWalletBusinessLogic _bringYourOwnWalletBusinessLogic; private readonly IIdentityData _identity; private readonly Guid _existingApplicationId; private readonly string _displayName; @@ -109,6 +111,7 @@ public RegistrationBusinessLogicTest() _countryRepository = A.Fake(); _consentRepository = A.Fake(); _mailingProcessCreation = A.Fake(); + _bringYourOwnWalletBusinessLogic = A.Fake(); _checklistService = A.Fake(); _staticDataRepository = A.Fake(); @@ -173,7 +176,8 @@ public async Task GetClientRolesCompositeAsync_GetsAllRoles() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); // Act var result = await sut.GetClientRolesCompositeAsync().ToListAsync(); @@ -252,7 +256,8 @@ public async Task GetCompanyBpdmDetailDataByBusinessPartnerNumber_WithValidBpn_R null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); // Act var result = await sut @@ -297,7 +302,8 @@ public async Task GetCompanyBpdmDetailDataByBusinessPartnerNumber_WithValidBpn_T null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.GetCompanyBpdmDetailDataByBusinessPartnerNumber("NotLongEnough", "justatoken", CancellationToken.None); @@ -332,7 +338,8 @@ public async Task GetAllApplicationsForUserWithStatus_WithValidUser_GetsAllRoles null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); var resultList = new[] { @@ -401,7 +408,8 @@ public async Task GetCompanyWithAddressAsync_WithValidApplication_GetsData() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); A.CallTo(() => _applicationRepository.GetCompanyApplicationDetailDataAsync(A._, A._, A._)) .Returns(data); @@ -431,7 +439,8 @@ public async Task GetCompanyWithAddressAsync_WithInvalidApplication_ThrowsNotFou null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); A.CallTo(() => _applicationRepository.GetCompanyApplicationDetailDataAsync(A._, A._, A._)) .Returns(null); @@ -461,7 +470,8 @@ public async Task GetCompanyWithAddressAsync_WithInvalidUser_ThrowsForbiddenExce null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); A.CallTo(() => _applicationRepository.GetCompanyApplicationDetailDataAsync(A._, A._, A._)) .Returns(_fixture.Build().With(x => x.IsUserOfCompany, false).Create()); @@ -512,7 +522,8 @@ public async Task SetCompanyWithAddressAsync_WithMissingData_ThrowsArgumentExcep null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); var uniqueIdData = uniqueIdentifierIds.Zip(values, (id, value) => new CompanyUniqueIdData(id, value)); var companyData = new CompanyDetailData(Guid.NewGuid(), name!, city!, streetName!, countryCode!, null, null, @@ -542,7 +553,8 @@ public async Task SetCompanyWithAddressAsync_WithInvalidApplicationId_ThrowsNotF null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); var companyData = new CompanyDetailData(companyId, "name", "munich", "main street", "de", null, null, _region, null, null, null, Enumerable.Empty()); @@ -582,7 +594,8 @@ public async Task SetCompanyWithAddressAsync_WithoutCompanyUserId_ThrowsForbidde null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); A.CallTo(() => _applicationRepository.GetCompanyApplicationDetailDataAsync(applicationId, A._, companyId)) .Returns(_fixture.Build().With(x => x.IsUserOfCompany, false).Create()); @@ -623,7 +636,8 @@ public async Task SetCompanyWithAddressAsync__WithInvalidBpn_ThrowsControllerArg null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SetCompanyDetailDataAsync(applicationId, companyData); @@ -664,7 +678,8 @@ public async Task SetCompanyWithAddressAsync__WithInvalidRegion_ThrowsController null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SetCompanyDetailDataAsync(applicationId, companyData); @@ -706,7 +721,8 @@ public async Task SetCompanyWithAddressAsync__WithExistingBpn_ModifiesCompany() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); var existingData = _fixture.Build() .With(x => x.IsUserOfCompany, true) @@ -794,7 +810,8 @@ public async Task SetCompanyWithAddressAsync__WithCompanyNameChange_ModifiesComp null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); var existingData = _fixture.Build() .With(x => x.IsUserOfCompany, true) @@ -860,7 +877,8 @@ public async Task SetCompanyWithAddressAsync__WithoutCompanyNameChange_ModifiesC null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); var existingData = _fixture.Build() .With(x => x.IsUserOfCompany, true) @@ -938,7 +956,8 @@ public async Task SetCompanyWithAddressAsync_ModifyCompany(string? bpn, string r null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); A.CallTo(() => _applicationRepository.GetCompanyApplicationDetailDataAsync(applicationId, A._, companyId)) .Returns(existingData); @@ -1018,7 +1037,8 @@ public async Task SetCompanyWithAddressAsync_WithoutInitialCompanyAddress_Create null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); A.CallTo(() => _applicationRepository.GetCompanyApplicationDetailDataAsync(applicationId, A._, companyId)) .Returns(existingData); @@ -1055,7 +1075,7 @@ public async Task SetCompanyWithAddressAsync_WithoutInitialCompanyAddress_Create A.CallTo(() => _companyRepository.AttachAndModifyAddress(A._, A>._, A>._)) .MustNotHaveHappened(); A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappenedOnceExactly(); - A.CallTo(() => _companyRepository.CreateCustomerWallet(A._, A._)) + A.CallTo(() => _companyRepository.CreateCustomerWallet(A._, A._, A._)) .MustNotHaveHappened(); company.Should().NotBeNull(); @@ -1112,7 +1132,8 @@ public async Task SetCompanyWithAddressAsync_WithInitialCompanyAddress_ModifyAdd null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); A.CallTo(() => _applicationRepository.GetCompanyApplicationDetailDataAsync(applicationId, A._, companyId)) .Returns(existingData); @@ -1214,7 +1235,8 @@ public async Task SetCompanyWithAddressAsync_WithoutInitialCompanyAddress_Create null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); A.CallTo(() => _applicationRepository.GetCompanyApplicationDetailDataAsync(applicationId, A._, companyId)) .Returns(existingData); @@ -1251,7 +1273,9 @@ public async Task SetCompanyWithAddressAsync_WithoutInitialCompanyAddress_Create A.CallTo(() => _companyRepository.AttachAndModifyAddress(A._, A>._, A>._)) .MustNotHaveHappened(); A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappenedOnceExactly(); - A.CallTo(() => _companyRepository.CreateCustomerWallet(A._, A._)) + A.CallTo(() => _companyRepository.CreateCustomerWallet(A._, A._, A._)) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _bringYourOwnWalletBusinessLogic.ValidateDid(A._, CancellationToken.None)) .MustHaveHappenedOnceExactly(); company.Should().NotBeNull(); @@ -1333,7 +1357,8 @@ public async Task SetCompanyWithAddressAsync_WithUniqueIdentifiers_CreateModifyD null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); A.CallTo(() => _dateTimeProvider.OffsetNow).Returns(now); A.CallTo(() => _applicationRepository.GetCompanyApplicationDetailDataAsync(applicationId, A._, companyId)) @@ -1405,7 +1430,8 @@ public async Task SetCompanyWithAddressAsync_WithInvalidCountryCode_Throws() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); A.CallTo(() => _countryRepository.GetCountryAssignedIdentifiers(A._, A>._)) @@ -1452,7 +1478,8 @@ public async Task SetCompanyWithAddressAsync_WithInvalidUniqueIdentifiers_Throws null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); A.CallTo(() => _countryRepository.GetCountryAssignedIdentifiers(_alpha2code, A>._)) @@ -1491,7 +1518,8 @@ public async Task SetOwnCompanyApplicationStatusAsync_WithInvalidStatus_ThrowsCo null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SetOwnCompanyApplicationStatusAsync(applicationId, 0); @@ -1521,7 +1549,8 @@ public async Task SetOwnCompanyApplicationStatusAsync_WithInvalidApplication_Thr null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserDataAsync(A._, A._)) .Returns<(bool, CompanyApplicationStatusId)>(default); @@ -1554,7 +1583,8 @@ public async Task SetOwnCompanyApplicationStatusAsync_WithInvalidStatus_ThrowsAr null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); var existingStatus = CompanyApplicationStatusId.CREATED; A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserDataAsync(A._, A._)) @@ -1611,7 +1641,8 @@ public async Task SetOwnCompanyApplicationStatusAsync_WithValidData_SavesChanges null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserDataAsync(A._, A._)) .Returns((true, currentStatus)); @@ -1656,7 +1687,8 @@ public async Task GetCompanyRolesAsync_() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); // Act var result = await sut.GetCompanyRoles().ToListAsync(); @@ -1745,7 +1777,8 @@ public async Task UploadDocumentAsync_WithValidData_CreatesDocument() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); // Act await sut.UploadDocumentAsync(_existingApplicationId, file, DocumentTypeId.CX_FRAME_CONTRACT, CancellationToken.None); @@ -1809,7 +1842,8 @@ public async Task UploadDocumentAsync_WithNotExistingApplicationId_ThrowsExcepti null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); var notExistingId = Guid.NewGuid(); // Act @@ -1845,7 +1879,8 @@ public async Task UploadDocumentAsync_WithNotExistingIamUser_ThrowsException() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.UploadDocumentAsync(_existingApplicationId, file, DocumentTypeId.CX_FRAME_CONTRACT, CancellationToken.None); @@ -1879,7 +1914,8 @@ public async Task UploadDocumentAsync_WithInvalidDocumentTypeId_ThrowsException( null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.UploadDocumentAsync(_existingApplicationId, file, DocumentTypeId.ADDITIONAL_DETAILS, CancellationToken.None); @@ -1920,7 +1956,8 @@ public async Task TestInviteNewUserAsyncSuccess() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); await sut.InviteNewUserAsync(_existingApplicationId, userCreationInfo); @@ -1954,7 +1991,8 @@ public async Task TestInviteNewUserEmptyEmailThrows() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); Task Act() => sut.InviteNewUserAsync(_existingApplicationId, userCreationInfo); @@ -1985,7 +2023,8 @@ public async Task TestInviteNewUserUserAlreadyExistsThrows() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); Task Act() => sut.InviteNewUserAsync(_existingApplicationId, userCreationInfo); @@ -2021,7 +2060,8 @@ public async Task TestInviteNewUserAsyncCreationErrorThrows() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); Task Act() => sut.InviteNewUserAsync(_existingApplicationId, userCreationInfo); @@ -2061,7 +2101,8 @@ public async Task GetUploadedDocumentsAsync_ReturnsExpectedOutput() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); // Act var result = await sut.GetUploadedDocumentsAsync(applicationId, DocumentTypeId.APP_CONTRACT); @@ -2092,7 +2133,8 @@ public async Task GetUploadedDocumentsAsync_InvalidApplication_ThrowsNotFound() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); Task Act() => sut.GetUploadedDocumentsAsync(applicationId, DocumentTypeId.APP_CONTRACT); @@ -2124,7 +2166,8 @@ public async Task GetUploadedDocumentsAsync_InvalidUser_ThrowsForbidden() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); Task Act() => sut.GetUploadedDocumentsAsync(applicationId, DocumentTypeId.APP_CONTRACT); @@ -2146,7 +2189,7 @@ public async Task SubmitRoleConsentsAsync_WithNotExistingApplication_ThrowsNotFo var notExistingId = _fixture.Create(); A.CallTo(() => _companyRolesRepository.GetCompanyRoleAgreementConsentDataAsync(notExistingId)) .Returns(null); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SubmitRoleConsentAsync(notExistingId, _fixture.Create()); @@ -2165,7 +2208,7 @@ public async Task SubmitRoleConsentsAsync_WithWrongCompanyUser_ThrowsForbiddenEx var data = new CompanyRoleAgreementConsentData(Guid.NewGuid(), applicationStatusId, _fixture.CreateMany(2), _fixture.CreateMany(5)); A.CallTo(() => _companyRolesRepository.GetCompanyRoleAgreementConsentDataAsync(applicationId)) .Returns(data); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SubmitRoleConsentAsync(applicationId, _fixture.Create()); @@ -2194,7 +2237,7 @@ public async Task SubmitRoleConsentsAsync_WithInvalidRoles_ThrowsControllerArgum .Returns(data); A.CallTo(() => _companyRolesRepository.GetAgreementAssignedCompanyRolesUntrackedAsync(roleIds)) .Returns(companyRoleAssignedAgreements.ToAsyncEnumerable()); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SubmitRoleConsentAsync(applicationId, _fixture.Create()); @@ -2230,7 +2273,7 @@ public async Task SubmitRoleConsentsAsync_WithoutAllRolesConsentGiven_ThrowsCont A.CallTo(() => _companyRolesRepository.GetAgreementAssignedCompanyRolesUntrackedAsync(A>._)) .Returns(companyRoleAssignedAgreements.ToAsyncEnumerable()); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SubmitRoleConsentAsync(applicationId, consents); @@ -2312,7 +2355,7 @@ public async Task SubmitRoleConsentsAsync_WithValidData_CallsExpected() removedCompanyRoleIds = companyRoleIds; }); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act await sut.SubmitRoleConsentAsync(applicationId, consents); @@ -2365,7 +2408,7 @@ public async Task SubmitRoleConsentsAsync_WithextraAgreement_ThrowsControllerArg A.CallTo(() => _companyRolesRepository.GetAgreementAssignedCompanyRolesUntrackedAsync(A>._)) .Returns(companyRoleAssignedAgreements.ToAsyncEnumerable()); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SubmitRoleConsentAsync(applicationId, consents); @@ -2392,7 +2435,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingApplication_ThrowsNotFo }; A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(A._, A._, A>._)) .Returns(null); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SubmitRegistrationAsync(notExistingId); @@ -2482,7 +2525,7 @@ public async Task SubmitRegistrationAsync_WithDocumentId_Success() DocumentTypeId.COMMERCIAL_REGISTER_EXTRACT ] }; - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, _checklistService, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, _checklistService, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act await sut.SubmitRegistrationAsync(applicationId); @@ -2573,7 +2616,7 @@ public async Task SubmitRegistrationAsync_InvalidStatus_ThrowsForbiddenException var companyData = new CompanyData("Test Company", Guid.NewGuid(), "Strabe Street", "Munich", "Germany", uniqueIds, companyRoleIds); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(A._, A._, A>._)) .Returns(new CompanyApplicationUserEmailData(statusId, true, _fixture.Create(), documents, companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2618,7 +2661,7 @@ public async Task SubmitRegistrationAsync_AlreadyClosed_ThrowsForbiddenException }; A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(A._, A._, A>._)) .Returns(new CompanyApplicationUserEmailData(statusId, true, _fixture.Create(), documents, companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2656,7 +2699,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingCompanyUser_ThrowsForbi }; A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(A._, A._, A>._)) .Returns(new CompanyApplicationUserEmailData(CompanyApplicationStatusId.VERIFY, false, null, null!, companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2694,7 +2737,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingStreetName_ThrowsConfli }; A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(applicationId, userId, A>._)) .Returns(new CompanyApplicationUserEmailData(CompanyApplicationStatusId.VERIFY, true, _fixture.Create(), Enumerable.Empty(), companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2724,7 +2767,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingAddressId_ThrowsConflic var companyData = new CompanyData("Test Company", null, "Strabe Street", "Munich", "Germany", uniqueIds, companyRoleIds); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(applicationId, userId, A>._)) .Returns(new CompanyApplicationUserEmailData(CompanyApplicationStatusId.VERIFY, true, _fixture.Create(), Enumerable.Empty(), companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2754,7 +2797,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingCompanyName_ThrowsConfl var companyData = new CompanyData(string.Empty, Guid.NewGuid(), "Strabe Street", "Munich", "Germany", uniqueIds, companyRoleIds); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(applicationId, userId, A>._)) .Returns(new CompanyApplicationUserEmailData(CompanyApplicationStatusId.VERIFY, true, _fixture.Create(), Enumerable.Empty(), companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2784,7 +2827,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingUniqueId_ThrowsConflict var companyData = new CompanyData("Test Company", Guid.NewGuid(), "Strabe Street", "Munich", "Germany", uniqueIdentifierData, companyRoleIds); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(applicationId, userId, A>._)) .Returns(new CompanyApplicationUserEmailData(CompanyApplicationStatusId.VERIFY, true, _fixture.Create(), Enumerable.Empty(), companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2814,7 +2857,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingCompanyRoleId_ThrowsCon var companyData = new CompanyData("Test Company", Guid.NewGuid(), "Strabe Street", "Munich", "Germany", uniqueIds, companyRoleIdData); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(applicationId, userId, A>._)) .Returns(new CompanyApplicationUserEmailData(CompanyApplicationStatusId.VERIFY, true, _fixture.Create(), Enumerable.Empty(), companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2841,7 +2884,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingAgreementandConsent_Thr var companyData = new CompanyData("Test Company", Guid.NewGuid(), "Strabe Street", "Munich", "Germany", uniqueIds, companyRoleIds); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(applicationId, userId, A>._)) .Returns(new CompanyApplicationUserEmailData(CompanyApplicationStatusId.VERIFY, true, _fixture.Create(), Enumerable.Empty(), companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2871,7 +2914,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingCity_ThrowsConflictExce var companyData = new CompanyData("Test Company", Guid.NewGuid(), "Strabe Street", string.Empty, "Germany", uniqueIds, companyRoleIds); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(applicationId, userId, A>._)) .Returns(new CompanyApplicationUserEmailData(CompanyApplicationStatusId.VERIFY, true, _fixture.Create(), Enumerable.Empty(), companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2902,7 +2945,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingCountry_ThrowsConflictE var companyData = new CompanyData("Test Company", Guid.NewGuid(), "Strabe Street", "Munich", string.Empty, uniqueIds, companyRoleIds); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(applicationId, userId, A>._)) .Returns(new CompanyApplicationUserEmailData(CompanyApplicationStatusId.VERIFY, true, _fixture.Create(), Enumerable.Empty(), companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2946,7 +2989,7 @@ public async Task SubmitRegistrationAsync_WithUserEmail_SendsMail() { setOptionalFields.Invoke(application); }); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, _checklistService, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, _checklistService, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act var result = await sut.SubmitRegistrationAsync(applicationId); @@ -2996,7 +3039,7 @@ public async Task SubmitRegistrationAsync_WithoutUserEmail_DoesntSendMail() { setOptionalFields.Invoke(application); }); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, A.Fake>(), _portalRepositories, _checklistService, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, A.Fake>(), _portalRepositories, _checklistService, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act var result = await sut.SubmitRegistrationAsync(applicationId); @@ -3035,7 +3078,8 @@ public async Task GetCompanyIdentifiers_ReturnsExpectedOutput() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); // Act var result = await sut.GetCompanyIdentifiers(_fixture.Create()); @@ -3067,7 +3111,8 @@ public async Task GetCompanyIdentifiers_InvalidCountry_Throws() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation); + _mailingProcessCreation, + _bringYourOwnWalletBusinessLogic); var countryCode = _fixture.Create(); @@ -3091,7 +3136,7 @@ public async Task GetRegistrationDataAsync_ReturnsExpected() A.CallTo(() => _applicationRepository.GetRegistrationDataUntrackedAsync(_existingApplicationId, _identity.CompanyId, A>._)) .Returns((true, true, data)); - var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act var result = await sut.GetRegistrationDataAsync(_existingApplicationId); @@ -3136,7 +3181,7 @@ public async Task GetRegistrationDataAsync_WithInvalidApplicationId_Throws() A.CallTo(() => _applicationRepository.GetRegistrationDataUntrackedAsync(A._, _identity.CompanyId, A>._)) .Returns((false, false, data)); - var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.GetRegistrationDataAsync(applicationId); @@ -3155,7 +3200,7 @@ public async Task GetRegistrationDataAsync_WithInvalidUser_Throws() A.CallTo(() => _applicationRepository.GetRegistrationDataUntrackedAsync(A._, _identity.CompanyId, A>._)) .Returns((true, false, data)); - var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.GetRegistrationDataAsync(applicationId); @@ -3174,7 +3219,7 @@ public async Task GetRegistrationDataAsync_WithNullData_Throws() A.CallTo(() => _applicationRepository.GetRegistrationDataUntrackedAsync(A._, _identity.CompanyId, A>._)) .Returns((true, true, null)); - var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.GetRegistrationDataAsync(applicationId); @@ -3194,7 +3239,7 @@ public async Task GetRegistrationDocumentAsync_ReturnsExpectedResult() var content = new byte[7]; A.CallTo(() => _documentRepository.GetDocumentAsync(documentId, A>._)) .Returns((content, "test.json", true, MediaTypeId.JSON)); - var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); //Act var result = await sut.GetRegistrationDocumentAsync(documentId); @@ -3213,7 +3258,7 @@ public async Task GetRegistrationDocumentAsync_WithInvalidDocumentTypeId_ThrowsN var content = new byte[7]; A.CallTo(() => _documentRepository.GetDocumentAsync(documentId, A>._)) .Returns((content, "test.json", false, MediaTypeId.JSON)); - var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); //Act Task Act() => sut.GetRegistrationDocumentAsync(documentId); @@ -3230,7 +3275,7 @@ public async Task GetRegistrationDocumentAsync_WithInvalidDocumentId_ThrowsNotFo var documentId = Guid.NewGuid(); A.CallTo(() => _documentRepository.GetDocumentAsync(documentId, A>._)) .Returns<(byte[], string, bool, MediaTypeId)>(default); - var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); //Act Task Act() => sut.GetRegistrationDocumentAsync(documentId); @@ -3252,7 +3297,7 @@ public async Task GetDocumentAsync_WithValidData_ReturnsExpected() .Returns((documentId, true, true, false)); A.CallTo(() => _documentRepository.GetDocumentByIdAsync(A._, A>._)) .Returns(new Document(documentId, content, content, "test.pdf", MediaTypeId.PDF, DateTimeOffset.UtcNow, DocumentStatusId.LOCKED, DocumentTypeId.APP_CONTRACT, content.Length)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act var result = await sut.GetDocumentContentAsync(documentId); @@ -3271,7 +3316,7 @@ public async Task GetDocumentAsync_WithoutDocument_ThrowsNotFoundException() var documentId = Guid.NewGuid(); A.CallTo(() => _documentRepository.GetDocumentIdWithCompanyUserCheckAsync(documentId, _identity.IdentityId)) .Returns((Guid.Empty, false, false, false)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.GetDocumentContentAsync(documentId); @@ -3288,7 +3333,7 @@ public async Task GetDocumentAsync_WithWrongUser_ThrowsForbiddenException() var documentId = Guid.NewGuid(); A.CallTo(() => _documentRepository.GetDocumentIdWithCompanyUserCheckAsync(documentId, _identity.IdentityId)) .Returns((documentId, false, false, false)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.GetDocumentContentAsync(documentId); @@ -3305,7 +3350,7 @@ public async Task GetDocumentAsync_WithConfirmedApplicationStatus_ThrowsForbidde var documentId = Guid.NewGuid(); A.CallTo(() => _documentRepository.GetDocumentIdWithCompanyUserCheckAsync(documentId, _identity.IdentityId)) .Returns((documentId, true, true, true)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.GetDocumentContentAsync(documentId); @@ -3342,7 +3387,7 @@ public async Task SetInvitationStatusAsync_ReturnsExpected() }); A.CallTo(() => _portalRepositories.SaveAsync()).Returns(retval); - var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act var result = await sut.SetInvitationStatusAsync(); @@ -3361,7 +3406,7 @@ public async Task SetInvitationStatusAsync_Throws_ForbiddenException() // Arrange A.CallTo(() => _invitationRepository.GetInvitationStatusAsync(A._)) .Returns(null); - var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); //Act Task Act() => sut.SetInvitationStatusAsync(); @@ -3415,7 +3460,7 @@ public async Task DeleteRegistrationDocumentAsync_ReturnsExpected() A.CallTo(() => _documentRepository.GetDocumentDetailsForApplicationUntrackedAsync(documentId, _identity.CompanyId, settings.ApplicationStatusIds)) .Returns((documentId, DocumentStatusId.PENDING, true, DocumentTypeId.CX_FRAME_CONTRACT, false, applicationIds)); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act var result = await sut.DeleteRegistrationDocumentAsync(documentId); @@ -3453,7 +3498,7 @@ public async Task DeleteRegistrationDocumentAsync_DocumentTypeId_ConflictExcepti A.CallTo(() => _documentRepository.GetDocumentDetailsForApplicationUntrackedAsync(documentId, _identity.CompanyId, settings.ApplicationStatusIds)) .Returns((documentId, DocumentStatusId.PENDING, true, DocumentTypeId.CONFORMITY_APPROVAL_BUSINESS_APPS, false, applicationId)); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.DeleteRegistrationDocumentAsync(documentId); @@ -3470,7 +3515,7 @@ public async Task DeleteRegistrationDocumentAsync_Throws_NotFoundException() A.CallTo(() => _documentRepository.GetDocumentDetailsForApplicationUntrackedAsync(A._, _identity.CompanyId, A>._)) .Returns<(Guid, DocumentStatusId, bool, DocumentTypeId, bool, IEnumerable)>(default); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.DeleteRegistrationDocumentAsync(_fixture.Create()); @@ -3501,7 +3546,7 @@ public async Task DeleteRegistrationDocumentAsync_Throws_ConflictException() A.CallTo(() => _documentRepository.GetDocumentDetailsForApplicationUntrackedAsync(A._, _identity.CompanyId, A>._)) .Returns((documentId, DocumentStatusId.PENDING, true, DocumentTypeId.CX_FRAME_CONTRACT, true, applicationId)); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.DeleteRegistrationDocumentAsync(documentId); @@ -3532,7 +3577,7 @@ public async Task DeleteRegistrationDocumentAsync_Throws_ForbiddenException() A.CallTo(() => _documentRepository.GetDocumentDetailsForApplicationUntrackedAsync(A._, _identity.CompanyId, A>._)) .Returns((documentId, DocumentStatusId.PENDING, false, DocumentTypeId.CX_FRAME_CONTRACT, false, applicationId)); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.DeleteRegistrationDocumentAsync(documentId); @@ -3563,7 +3608,7 @@ public async Task DeleteRegistrationDocumentAsync_DocumentStatusId_Throws_Confli A.CallTo(() => _documentRepository.GetDocumentDetailsForApplicationUntrackedAsync(A._, _identity.CompanyId, A>._)) .Returns((documentId, DocumentStatusId.LOCKED, true, DocumentTypeId.CX_FRAME_CONTRACT, false, applicationId)); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.DeleteRegistrationDocumentAsync(documentId); @@ -3577,7 +3622,7 @@ public async Task DeleteRegistrationDocumentAsync_DocumentStatusId_Throws_Confli public async Task DeleteRegistrationDocumentAsync_Throws_ControllerArgumentException() { // Arrange; - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.DeleteRegistrationDocumentAsync(default); @@ -3700,7 +3745,7 @@ public async Task GetApplicationsDeclineData_CallsExpected() ApplicationDeclineStatusIds = [CompanyApplicationStatusId.CREATED] }); - var sut = new RegistrationBusinessLogic(options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, null!, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, null!, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act var result = await sut.GetApplicationsDeclineData(); @@ -3833,7 +3878,7 @@ public async Task DeclineApplicationRegistrationAsync_CallsExpected() } }); - var sut = new RegistrationBusinessLogic(options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, null!, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, null!, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act await sut.DeclineApplicationRegistrationAsync(applicationId); @@ -4007,7 +4052,7 @@ public async Task DeclineApplicationRegistrationAsync_ThrowsNotFoundException_Re A.CallTo(() => _applicationRepository.GetDeclineApplicationDataForApplicationId(A._, A._, A>._)) .Returns<(bool, bool, ApplicationDeclineData?)>(default); - var sut = new RegistrationBusinessLogic(options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, null!, _mailingProcessCreation); + var sut = new RegistrationBusinessLogic(options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, null!, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); // Act Task Act() => sut.DeclineApplicationRegistrationAsync(applicationId); diff --git a/tests/registration/Registration.Service.Tests/Controller/BringYourOwnWalletControllerTests.cs b/tests/registration/Registration.Service.Tests/Controller/BringYourOwnWalletControllerTests.cs index 3408cdf2a8..471973806f 100644 --- a/tests/registration/Registration.Service.Tests/Controller/BringYourOwnWalletControllerTests.cs +++ b/tests/registration/Registration.Service.Tests/Controller/BringYourOwnWalletControllerTests.cs @@ -1,4 +1,5 @@ /******************************************************************************** + * Copyright (c) 2025 Cofinity-X GmbH * Copyright (c) 2025 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional @@ -51,7 +52,8 @@ public async Task ValidateDid_ShouldBeValid() { // Arrange var did = "did:web:example.com"; - A.CallTo(() => _bringYourOwnWalletBusinessLogicFake.ValidateDid(did, A._)).DoesNothing(); + A.CallTo(() => _bringYourOwnWalletBusinessLogicFake.ValidateDid(did, A._)) + .Returns(Task.FromResult(System.Text.Json.JsonDocument.Parse("{}"))); // Act await _controller.validateDid(did, CancellationToken.None); From ba0b20f86ab6ef05351b81b538d1a3cb4d129798 Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Wed, 9 Jul 2025 18:42:33 +0200 Subject: [PATCH 05/38] Added the correct step during on board process for byow --- .../RegistrationBusinessLogic.cs | 6 ++-- .../BpnDidResolverBusinessLogic.cs | 5 +++- .../IssuerComponentBusinessLogic.cs | 20 +++++++++---- .../ApplicationChecklistCreationService.cs | 7 +++-- .../BpnDidResolverBusinessLogicTests.cs | 28 +++++++++++++++++++ 5 files changed, 53 insertions(+), 13 deletions(-) diff --git a/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs index da3d37985f..d20cf0af60 100644 --- a/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs @@ -433,7 +433,7 @@ public async Task ProcessClearinghouseSelfDescription(SelfDescriptionResponseDat /// public async Task ApproveRegistrationVerification(Guid applicationId) { - var createWalletOrBpnCredentalStep = await CreateWalletOrBpnCredentialStepAsync(applicationId); + var createWalletOrTransmitCustomerDidStep = await CreateWalletOrBpnCredentialStepAsync(applicationId); var context = await checklistService .VerifyChecklistEntryAndProcessSteps( applicationId, @@ -441,7 +441,7 @@ public async Task ApproveRegistrationVerification(Guid applicationId) [ApplicationChecklistEntryStatusId.TO_DO], ProcessStepTypeId.MANUAL_VERIFY_REGISTRATION, [ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER], - [createWalletOrBpnCredentalStep]) + [createWalletOrTransmitCustomerDidStep]) .ConfigureAwait(ConfigureAwaitOptions.None); var businessPartnerSuccess = context.Checklist[ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER] == new ValueTuple(ApplicationChecklistEntryStatusId.DONE, null); @@ -454,7 +454,7 @@ public async Task ApproveRegistrationVerification(Guid applicationId) entry.ApplicationChecklistEntryStatusId = ApplicationChecklistEntryStatusId.DONE; }, businessPartnerSuccess - ? new[] { createWalletOrBpnCredentalStep } + ? new[] { createWalletOrTransmitCustomerDidStep } : null); await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); diff --git a/src/externalsystems/BpnDidResolver.Library/BusinessLogic/BpnDidResolverBusinessLogic.cs b/src/externalsystems/BpnDidResolver.Library/BusinessLogic/BpnDidResolverBusinessLogic.cs index 98c17a58e4..27c3f98d3b 100644 --- a/src/externalsystems/BpnDidResolver.Library/BusinessLogic/BpnDidResolverBusinessLogic.cs +++ b/src/externalsystems/BpnDidResolver.Library/BusinessLogic/BpnDidResolverBusinessLogic.cs @@ -39,7 +39,7 @@ public BpnDidResolverBusinessLogic(IPortalRepositories portalRepositories, IBpnD public async Task TransmitDidAndBpn(IApplicationChecklistService.WorkerChecklistProcessStepData context, CancellationToken cancellationToken) { - if (context.Checklist[ApplicationChecklistEntryTypeId.IDENTITY_WALLET] != ApplicationChecklistEntryStatusId.IN_PROGRESS) + if (context.Checklist[ApplicationChecklistEntryTypeId.IDENTITY_WALLET] != ApplicationChecklistEntryStatusId.IN_PROGRESS && !await IsBringYourOwnWallet(context.ApplicationId)) { return new IApplicationChecklistService.WorkerChecklistProcessStepExecutionResult( ProcessStepStatusId.FAILED, @@ -64,6 +64,9 @@ public BpnDidResolverBusinessLogic(IPortalRepositories portalRepositories, IBpnD null); } + private async Task IsBringYourOwnWallet(Guid applicationId) => + await _portalRepositories.GetInstance().IsBringYourOwnWallet(applicationId).ConfigureAwait(ConfigureAwaitOptions.None); + private async Task PostDidAndBpn(Guid applicationId, CancellationToken cancellationToken) { var (exists, did, bpn) = await _portalRepositories.GetInstance().GetDidAndBpnForApplicationId(applicationId).ConfigureAwait(ConfigureAwaitOptions.None); diff --git a/src/externalsystems/IssuerComponent.Library/BusinessLogic/IssuerComponentBusinessLogic.cs b/src/externalsystems/IssuerComponent.Library/BusinessLogic/IssuerComponentBusinessLogic.cs index 4597dfd02d..aac9abdab2 100644 --- a/src/externalsystems/IssuerComponent.Library/BusinessLogic/IssuerComponentBusinessLogic.cs +++ b/src/externalsystems/IssuerComponent.Library/BusinessLogic/IssuerComponentBusinessLogic.cs @@ -65,9 +65,7 @@ public class IssuerComponentBusinessLogic( throw new ConflictException("The wallet information must be set"); } - var cryptoConfig = _settings.EncryptionConfigs.SingleOrDefault(x => x.Index == walletInformation.EncryptionMode) ?? throw new ConfigurationException($"EncryptionModeIndex {walletInformation.EncryptionMode} is not configured"); - var secret = CryptoHelper.Decrypt(walletInformation.ClientSecret, walletInformation.InitializationVector, Convert.FromHexString(cryptoConfig.EncryptionKey), cryptoConfig.CipherMode, cryptoConfig.PaddingMode); - + var secret = GetDecriptedSecret(walletInformation, isBringYourOwnWallet); var callbackUrl = $"{_settings.CallbackBaseUrl}/api/administration/registration/issuer/bpncredential"; var data = new CreateBpnCredentialRequest(holder, businessPartnerNumber, isBringYourOwnWallet @@ -87,6 +85,18 @@ public class IssuerComponentBusinessLogic( null); } + private string GetDecriptedSecret(PortalBackend.DBAccess.Models.WalletInformation walletInformation, bool isBringYourOwnWallet) + { + if (isBringYourOwnWallet) + { + return string.Empty; + } + + var cryptoConfig = _settings.EncryptionConfigs.SingleOrDefault(x => x.Index == walletInformation.EncryptionMode) ?? throw new ConfigurationException($"EncryptionModeIndex {walletInformation.EncryptionMode} is not configured"); + return CryptoHelper.Decrypt(walletInformation.ClientSecret, walletInformation.InitializationVector, Convert.FromHexString(cryptoConfig.EncryptionKey), cryptoConfig.CipherMode, cryptoConfig.PaddingMode); + + } + public async Task StoreBpnlCredentialResponse(Guid applicationId, IssuerResponseData data) { var context = await checklistService @@ -138,9 +148,7 @@ public async Task StoreBpnlCredentialResponse(Guid applicationId, IssuerResponse throw new ConflictException("The wallet information must be set"); } - var cryptoConfig = _settings.EncryptionConfigs.SingleOrDefault(x => x.Index == walletInformation.EncryptionMode) ?? throw new ConfigurationException($"EncryptionModeIndex {walletInformation.EncryptionMode} is not configured"); - var secret = CryptoHelper.Decrypt(walletInformation.ClientSecret, walletInformation.InitializationVector, Convert.FromHexString(cryptoConfig.EncryptionKey), cryptoConfig.CipherMode, cryptoConfig.PaddingMode); - + var secret = GetDecriptedSecret(walletInformation, isBringYourOwnWallet); var callbackUrl = $"{_settings.CallbackBaseUrl}/api/administration/registration/issuer/membershipcredential"; var data = new CreateMembershipCredentialRequest(holder, businessPartnerNumber, "catena-x", diff --git a/src/processes/ApplicationChecklist.Library/ApplicationChecklistCreationService.cs b/src/processes/ApplicationChecklist.Library/ApplicationChecklistCreationService.cs index 75049f8855..97845724be 100644 --- a/src/processes/ApplicationChecklist.Library/ApplicationChecklistCreationService.cs +++ b/src/processes/ApplicationChecklist.Library/ApplicationChecklistCreationService.cs @@ -51,7 +51,7 @@ public ApplicationChecklistCreationService(IPortalRepositories portalRepositorie private IEnumerable<(ApplicationChecklistEntryTypeId, ApplicationChecklistEntryStatusId)> CreateEntries(Guid applicationId, IEnumerable existingChecklistEntryTypeIds, string? bpn) { - var missingEntries = GetApplicationChecklistTypes() + var missingEntries = GetApplicationChecklistTypes(applicationId) .Except(existingChecklistEntryTypeIds); if (missingEntries.Any()) { @@ -64,9 +64,10 @@ public ApplicationChecklistCreationService(IPortalRepositories portalRepositorie return Enumerable.Empty<(ApplicationChecklistEntryTypeId, ApplicationChecklistEntryStatusId)>(); } - private IEnumerable GetApplicationChecklistTypes() + private IEnumerable GetApplicationChecklistTypes(Guid applicationId) { - if (_settings.UseDimWallet) + var isBringYourOwnWallet = _portalRepositories.GetInstance().IsBringYourOwnWallet(applicationId).GetAwaiter().GetResult(); + if (_settings.UseDimWallet || isBringYourOwnWallet) return Enum.GetValues(); return Enum.GetValues().Except(new[] diff --git a/tests/externalsystems/BpnDidResolver.Library.Tests/BpnDidResolverBusinessLogicTests.cs b/tests/externalsystems/BpnDidResolver.Library.Tests/BpnDidResolverBusinessLogicTests.cs index 11b319664c..bbf4ae2891 100644 --- a/tests/externalsystems/BpnDidResolver.Library.Tests/BpnDidResolverBusinessLogicTests.cs +++ b/tests/externalsystems/BpnDidResolver.Library.Tests/BpnDidResolverBusinessLogicTests.cs @@ -38,6 +38,7 @@ public class BpnDidResolverBusinessLogicTests private readonly IFixture _fixture; private readonly IApplicationRepository _applicationRepository; + private readonly ICompanyRepository _companyRepository; private readonly IBpnDidResolverService _bpnDidResolverService; private readonly IBpnDidResolverBusinessLogic _logic; @@ -48,8 +49,10 @@ public BpnDidResolverBusinessLogicTests() var portalRepository = A.Fake(); _applicationRepository = A.Fake(); + _companyRepository = A.Fake(); _bpnDidResolverService = A.Fake(); A.CallTo(() => portalRepository.GetInstance()).Returns(_applicationRepository); + A.CallTo(() => portalRepository.GetInstance()).Returns(_companyRepository); _logic = new BpnDidResolverBusinessLogic(portalRepository, _bpnDidResolverService); } @@ -237,5 +240,30 @@ public async Task TransmitDidAndBpn_WithValid_CallsExpected() result.ScheduleStepTypeIds.Should().ContainSingle(x => x == ProcessStepTypeId.REQUEST_BPN_CREDENTIAL); } + [Fact] + public async Task TransmitDidAndBpn_WithBpnProcessInTodo_BringYourWalletTrue() + { + // Arrange + var checklist = new Dictionary + { + {ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.DONE}, + {ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER, ApplicationChecklistEntryStatusId.TO_DO}, + {ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.TO_DO}, + } + .ToImmutableDictionary(); + var context = new IApplicationChecklistService.WorkerChecklistProcessStepData(ApplicationId, default, checklist, Enumerable.Empty()); + var did = "did:web:test:1234"; + A.CallTo(() => _applicationRepository.GetDidAndBpnForApplicationId(ApplicationId)) + .Returns(new ValueTuple(true, did, BPN)); + A.CallTo(() => _companyRepository.IsBringYourOwnWallet(ApplicationId)).Returns(true); + + // Act + var result = await _logic.TransmitDidAndBpn(context, CancellationToken.None); + + // Assert + A.CallTo(() => _bpnDidResolverService.TransmitDidAndBpn(did, BPN, A._)) + .MustHaveHappenedOnceExactly(); + } + #endregion } From 9f72fef09e2c30b92df539f94d5f857fcd282cc5 Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Fri, 11 Jul 2025 21:36:20 +0200 Subject: [PATCH 06/38] Updated the updated bpn to also check BYOW --- .../BusinessLogic/RegistrationBusinessLogic.cs | 5 +++-- .../BusinessLogic/RegistrationBusinessLogic.cs | 10 ++++++++++ .../BusinessLogic/RegistrationBusinessLogicTest.cs | 10 +++++++--- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs index d20cf0af60..5816b29e25 100644 --- a/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs @@ -249,6 +249,7 @@ private async Task UpdateCompanyBpnInternal(Guid applicationId, string bpn) throw ConflictException.Create(AdministrationRegistrationErrors.REGISTRATION_CONFLICT_BPN_OF_COMPANY_SET, new ErrorParameter[] { new("companyId", applicationCompanyData.CompanyId.ToString()) }); } + var createWalletOrTransmitCustomerDidStep = await CreateWalletOrBpnCredentialStepAsync(applicationId); var context = await checklistService .VerifyChecklistEntryAndProcessSteps( applicationId, @@ -267,7 +268,7 @@ private async Task UpdateCompanyBpnInternal(Guid applicationId, string bpn) ProcessStepTypeId.CREATE_BUSINESS_PARTNER_NUMBER_PULL, ProcessStepTypeId.RETRIGGER_BUSINESS_PARTNER_NUMBER_PULL, ProcessStepTypeId.RETRIGGER_BUSINESS_PARTNER_NUMBER_PUSH, - ProcessStepTypeId.CREATE_IDENTITY_WALLET + createWalletOrTransmitCustomerDidStep ]) .ConfigureAwait(ConfigureAwaitOptions.None); @@ -291,7 +292,7 @@ private async Task UpdateCompanyBpnInternal(Guid applicationId, string bpn) entry => entry.ApplicationChecklistEntryStatusId = ApplicationChecklistEntryStatusId.DONE, registrationValidationFailed ? null - : new[] { CreateWalletStep() }); + : new[] { createWalletOrTransmitCustomerDidStep }); await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); } diff --git a/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs b/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs index e05e7affc7..f6b6a16c93 100644 --- a/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs +++ b/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs @@ -272,16 +272,19 @@ await companyDetails.ValidateDatabaseData( { await identityProviderProvisioningService.UpdateCompanyNameInSharedIdentityProvider(_identityData.CompanyId, companyDetails.Name).ConfigureAwait(ConfigureAwaitOptions.None); } + if (!string.IsNullOrEmpty(companyDetails.HolderDid)) { await CreateCustomerWalletAsync(companyDetails.CompanyId, companyDetails.HolderDid, companyRepository); } + await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); } private async Task CreateCustomerWalletAsync(Guid companyId, string did, ICompanyRepository companyRepository) { var didDocument = await _bringYourOwnWalletBusinessLogic.ValidateDid(did, CancellationToken.None).ConfigureAwait(ConfigureAwaitOptions.None); + UpdateDidLocation(companyId, did, companyRepository); companyRepository.CreateCustomerWallet(companyId, did, didDocument); } private async Task GetAndValidateApplicationData(Guid applicationId, CompanyDetailData companyDetails, IApplicationRepository applicationRepository) @@ -367,6 +370,13 @@ private static void ModifyCompany(Guid addressId, CompanyApplicationDetailData i } ); + private static void UpdateDidLocation(Guid companyId, string didLocation, ICompanyRepository companyRepository) => + companyRepository.AttachAndModifyCompany( + companyId, + _ => { }, + c => { c.DidDocumentLocation = didLocation; } + ); + public Task InviteNewUserAsync(Guid applicationId, UserCreationInfoWithMessage userCreationInfo) { if (string.IsNullOrEmpty(userCreationInfo.eMail)) diff --git a/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs b/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs index 7721d8782e..18a4e39539 100644 --- a/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs +++ b/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs @@ -708,6 +708,7 @@ public async Task SetCompanyWithAddressAsync__WithExistingBpn_ModifiesCompany() .With(x => x.CountryAlpha2Code, _alpha2code) .With(x => x.Region, _region) .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, _vatId)]) + .With(x => x.HolderDid, (string?)null) .Create(); A.CallTo(() => _companyRepository.CheckBpnExists("BPNL00000001TEST")).Returns(true); @@ -797,6 +798,7 @@ public async Task SetCompanyWithAddressAsync__WithCompanyNameChange_ModifiesComp .With(x => x.CompanyId, companyId) .With(x => x.CountryAlpha2Code, countryCode) .With(x => x.Region, _region) + .With(x => x.HolderDid, (string?)null) .With(x => x.UniqueIds, [new CompanyUniqueIdData(uniqueIdentifierId, identifierValue)]) .Create(); @@ -865,6 +867,7 @@ public async Task SetCompanyWithAddressAsync__WithoutCompanyNameChange_ModifiesC .With(x => x.CountryAlpha2Code, _alpha2code) .With(x => x.Region, _region) .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, _vatId)]) + .With(x => x.HolderDid, (string?)null) .Create(); var sut = new RegistrationBusinessLogic( @@ -938,6 +941,7 @@ public async Task SetCompanyWithAddressAsync_ModifyCompany(string? bpn, string r .With(x => x.CountryAlpha2Code, _alpha2code) .With(x => x.Region, region) .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, _vatId)]) + .With(x => x.HolderDid, (string?)null) .Create(); var existingData = _fixture.Build() @@ -1112,6 +1116,7 @@ public async Task SetCompanyWithAddressAsync_WithInitialCompanyAddress_ModifyAdd .With(x => x.CountryAlpha2Code, _alpha2code) .With(x => x.Region, _region) .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, _vatId)]) + .With(x => x.HolderDid, (string?)null) .Create(); var existingData = _fixture.Build() @@ -1222,7 +1227,7 @@ public async Task SetCompanyWithAddressAsync_WithoutInitialCompanyAddress_Create .With(x => x.IsUserOfCompany, true) .Create(); - Company? company = null; + var company = new Company(companyId, null!, default, default); Address? address = null; var sut = new RegistrationBusinessLogic( @@ -1244,7 +1249,6 @@ public async Task SetCompanyWithAddressAsync_WithoutInitialCompanyAddress_Create A.CallTo(() => _companyRepository.AttachAndModifyCompany(A._, A>._, A>._)) .Invokes((Guid companyId, Action? initialize, Action modify) => { - company = new Company(companyId, null!, default, default); initialize?.Invoke(company); modify(company); }); @@ -1267,7 +1271,7 @@ public async Task SetCompanyWithAddressAsync_WithoutInitialCompanyAddress_Create companyData.CountryAlpha2Code, A>._)) .MustHaveHappened(); A.CallTo(() => _companyRepository.AttachAndModifyCompany(A._, A>._, A>._)) - .MustHaveHappenedOnceExactly(); + .MustHaveHappenedTwiceExactly(); A.CallTo(() => _companyRepository.AttachAndModifyCompany(companyId, A>._, A>._)) .MustHaveHappened(); A.CallTo(() => _companyRepository.AttachAndModifyAddress(A._, A>._, A>._)) From d9776096c968894be32ae187fa3d911c95935156 Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Mon, 14 Jul 2025 18:52:52 +0200 Subject: [PATCH 07/38] Created an endpoint to save the did number and refoctor the registration business logic --- docs/api/registration-service.yaml | 116 +++++++ .../IssuerComponentBusinessLogic.cs | 3 +- .../Repositories/CompanyRepository.cs | 8 + .../Repositories/ICompanyRepository.cs | 3 +- .../BringYourOwnWalletBusinessLogic.cs | 39 ++- .../IBringYourOwnWalletBusinessLogic.cs | 2 + .../RegistrationBusinessLogic.cs | 24 +- .../BringYourOwnWalletController.cs | 24 ++ .../BringYourOwnWalletBuisinessLogicTests.cs | 116 ++++++- .../RegistrationBusinessLogicTest.cs | 322 +++++------------- .../BringYourOwnWalletControllerTests.cs | 67 ++++ 11 files changed, 456 insertions(+), 268 deletions(-) diff --git a/docs/api/registration-service.yaml b/docs/api/registration-service.yaml index b557c5c29b..1d2b8564a5 100644 --- a/docs/api/registration-service.yaml +++ b/docs/api/registration-service.yaml @@ -55,6 +55,122 @@ paths: description: Internal Server Error '401': description: The User is unauthorized + '/api/registration/bringYourOwnWallet/{companyId}/saveHolderDid/{did}': + post: + tags: + - BringYourOwnWallet + summary: ' (Authorization required - Roles: submit_registration)' + parameters: + - name: companyId + in: path + required: true + schema: + type: string + format: uuid + - name: did + in: path + required: true + schema: + type: string + responses: + '204': + description: No Content + content: + text/plain: + schema: + $ref: '#/components/schemas/NoContentResult' + application/json: + schema: + $ref: '#/components/schemas/NoContentResult' + text/json: + schema: + $ref: '#/components/schemas/NoContentResult' + '415': + description: Unsupported Media Type + content: + text/plain: + schema: + $ref: '#/components/schemas/ErrorResponse' + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + text/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Not Found + content: + text/plain: + schema: + $ref: '#/components/schemas/ErrorResponse' + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + text/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal Server Error + '401': + description: The User is unauthorized + '/api/registration/bringYourOwnWallet/{did}/getHolderDid': + get: + tags: + - BringYourOwnWallet + summary: ' (Authorization required - Roles: submit_registration)' + parameters: + - name: companyId + in: path + required: true + schema: + type: string + format: uuid + - name: did + in: path + required: true + schema: + type: string + responses: + '204': + description: No Content + content: + text/plain: + schema: + $ref: '#/components/schemas/NoContentResult' + application/json: + schema: + $ref: '#/components/schemas/NoContentResult' + text/json: + schema: + $ref: '#/components/schemas/NoContentResult' + '415': + description: Unsupported Media Type + content: + text/plain: + schema: + $ref: '#/components/schemas/ErrorResponse' + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + text/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Not Found + content: + text/plain: + schema: + $ref: '#/components/schemas/ErrorResponse' + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + text/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal Server Error + '401': + description: The User is unauthorized /api/registration/errormessage: get: tags: diff --git a/src/externalsystems/IssuerComponent.Library/BusinessLogic/IssuerComponentBusinessLogic.cs b/src/externalsystems/IssuerComponent.Library/BusinessLogic/IssuerComponentBusinessLogic.cs index aac9abdab2..d73907eb0b 100644 --- a/src/externalsystems/IssuerComponent.Library/BusinessLogic/IssuerComponentBusinessLogic.cs +++ b/src/externalsystems/IssuerComponent.Library/BusinessLogic/IssuerComponentBusinessLogic.cs @@ -214,8 +214,7 @@ public async Task CreateFrameworkCredentialData(Guid useCaseFrameworkVersi throw new ConflictException("The wallet information must be set"); } - var cryptoConfig = _settings.EncryptionConfigs.SingleOrDefault(x => x.Index == walletInformation.EncryptionMode) ?? throw new ConfigurationException($"EncryptionModeIndex {walletInformation.EncryptionMode} is not configured"); - var secret = CryptoHelper.Decrypt(walletInformation.ClientSecret, walletInformation.InitializationVector, Convert.FromHexString(cryptoConfig.EncryptionKey), cryptoConfig.CipherMode, cryptoConfig.PaddingMode); + var secret = GetDecriptedSecret(walletInformation, isBringYourOwnWallet); var data = new CreateFrameworkCredentialRequest(holder, businessPartnerNumber, frameworkId, useCaseFrameworkVersionId, isBringYourOwnWallet diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs index 72118e9536..d4d1d32e1a 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs @@ -93,6 +93,14 @@ public Task IsBringYourOwnWallet(Guid applicationId) (app, wallet) => wallet) .AnyAsync(wallet => wallet.ClientId == BringYourOwnWalletClientFields.Identification); } + + public Task GetCompanyHolderDidAsync(Guid companyId) => + context.Companies + .AsNoTracking() + .Where(company => company.Id == companyId) + .Select(company => company.DidDocumentLocation) + .SingleOrDefaultAsync(); + public void AttachAndModifyAddress(Guid addressId, Action
? initialize, Action
modify) { var address = new Address(addressId, null!, null!, null!, null!, default); diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs index a464fe678d..688abd6959 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs @@ -17,7 +17,6 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; using Org.Eclipse.TractusX.Portal.Backend.Framework.Models; using Org.Eclipse.TractusX.Portal.Backend.Framework.Processes.Library.Models; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; @@ -46,6 +45,8 @@ public interface ICompanyRepository Task IsBringYourOwnWallet(Guid applicationId); + Task GetCompanyHolderDidAsync(Guid companyId); + Address CreateAddress(string city, string streetname, string region, string countryAlpha2Code, Action
? setOptionalParameters = null); void AttachAndModifyAddress(Guid addressId, Action
? initialize, Action
modify); diff --git a/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs b/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs index 74fc9f79de..6d1ceec188 100644 --- a/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs +++ b/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs @@ -18,6 +18,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; using Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library; using System.Text.Json; @@ -26,10 +28,13 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Registration.Service.BusinessLogic public class BringYourOwnWalletBusinessLogic : IBringYourOwnWalletBusinessLogic { private readonly IUniversalDidResolverService _universalResolverService; + private readonly IPortalRepositories _portalRepositories; - public BringYourOwnWalletBusinessLogic(IUniversalDidResolverService universalResolverService) + public BringYourOwnWalletBusinessLogic(IUniversalDidResolverService universalResolverService, IPortalRepositories portalRepositories) { _universalResolverService = universalResolverService; + _portalRepositories = portalRepositories; + } public async Task ValidateDid(string did, CancellationToken cancellationToken) { @@ -45,5 +50,37 @@ public async Task ValidateDid(string did, CancellationToken cancel } return validationResult.DidDocument; } + + public async Task SaveCustomerWalletAsync(Guid companyId, string did) + { + if (string.IsNullOrEmpty(did)) + { + throw new ForbiddenException("Invalid DID. DID cannot be empty or NULL."); + } + + var companyRepository = _portalRepositories.GetInstance(); + var didDocument = await ValidateDid(did, CancellationToken.None).ConfigureAwait(ConfigureAwaitOptions.None); + UpdateDidLocation(companyId, did, companyRepository); + companyRepository.CreateCustomerWallet(companyId, did, didDocument); + + await _portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); + } + + public async Task getCompanyWalletDidAsync(Guid companyId) + { + var companyRepository = _portalRepositories.GetInstance(); + var did = await companyRepository.GetCompanyHolderDidAsync(companyId).ConfigureAwait(ConfigureAwaitOptions.None); + if (string.IsNullOrEmpty(did)) + { + throw new NotFoundException("Company wallet DID not found for the given company ID."); + } + return did; + } + private static void UpdateDidLocation(Guid companyId, string didLocation, ICompanyRepository companyRepository) => + companyRepository.AttachAndModifyCompany( + companyId, + _ => { }, + c => { c.DidDocumentLocation = didLocation; } + ); } } diff --git a/src/registration/Registration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs b/src/registration/Registration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs index 613f06c9df..fd90d6ab24 100644 --- a/src/registration/Registration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs +++ b/src/registration/Registration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs @@ -25,5 +25,7 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Registration.Service.BusinessLogic public interface IBringYourOwnWalletBusinessLogic { Task ValidateDid(string did, CancellationToken cancellationToken); + Task SaveCustomerWalletAsync(Guid companyId, string did); + Task getCompanyWalletDidAsync(Guid companyId); } } diff --git a/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs b/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs index f6b6a16c93..78f4561b3c 100644 --- a/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs +++ b/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs @@ -41,10 +41,8 @@ using Org.Eclipse.TractusX.Portal.Backend.Registration.Common; using Org.Eclipse.TractusX.Portal.Backend.Registration.Service.ErrorHandling; using Org.Eclipse.TractusX.Portal.Backend.Registration.Service.Model; -using Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library; using System.Collections.Immutable; using System.Text.RegularExpressions; -using System.Web; namespace Org.Eclipse.TractusX.Portal.Backend.Registration.Service.BusinessLogic; @@ -58,12 +56,10 @@ public class RegistrationBusinessLogic( IApplicationChecklistCreationService checklistService, IIdentityService identityService, IDateTimeProvider dateTimeProvider, - IMailingProcessCreation mailingProcessCreation, - IBringYourOwnWalletBusinessLogic bringYourOwnWalletBusinessLogic) : IRegistrationBusinessLogic + IMailingProcessCreation mailingProcessCreation) : IRegistrationBusinessLogic { private readonly IIdentityData _identityData = identityService.IdentityData; private readonly RegistrationSettings _settings = settings.Value; - private readonly IBringYourOwnWalletBusinessLogic _bringYourOwnWalletBusinessLogic = bringYourOwnWalletBusinessLogic; private static readonly Regex bpnRegex = new(@"(\w|\d){16}", RegexOptions.None, TimeSpan.FromSeconds(1)); public IAsyncEnumerable GetClientRolesCompositeAsync() => @@ -273,20 +269,9 @@ await companyDetails.ValidateDatabaseData( await identityProviderProvisioningService.UpdateCompanyNameInSharedIdentityProvider(_identityData.CompanyId, companyDetails.Name).ConfigureAwait(ConfigureAwaitOptions.None); } - if (!string.IsNullOrEmpty(companyDetails.HolderDid)) - { - await CreateCustomerWalletAsync(companyDetails.CompanyId, companyDetails.HolderDid, companyRepository); - } - await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); } - private async Task CreateCustomerWalletAsync(Guid companyId, string did, ICompanyRepository companyRepository) - { - var didDocument = await _bringYourOwnWalletBusinessLogic.ValidateDid(did, CancellationToken.None).ConfigureAwait(ConfigureAwaitOptions.None); - UpdateDidLocation(companyId, did, companyRepository); - companyRepository.CreateCustomerWallet(companyId, did, didDocument); - } private async Task GetAndValidateApplicationData(Guid applicationId, CompanyDetailData companyDetails, IApplicationRepository applicationRepository) { var companyApplicationData = await applicationRepository @@ -370,13 +355,6 @@ private static void ModifyCompany(Guid addressId, CompanyApplicationDetailData i } ); - private static void UpdateDidLocation(Guid companyId, string didLocation, ICompanyRepository companyRepository) => - companyRepository.AttachAndModifyCompany( - companyId, - _ => { }, - c => { c.DidDocumentLocation = didLocation; } - ); - public Task InviteNewUserAsync(Guid applicationId, UserCreationInfoWithMessage userCreationInfo) { if (string.IsNullOrEmpty(userCreationInfo.eMail)) diff --git a/src/registration/Registration.Service/Controllers/BringYourOwnWalletController.cs b/src/registration/Registration.Service/Controllers/BringYourOwnWalletController.cs index 2db33b39be..ffcefed86b 100644 --- a/src/registration/Registration.Service/Controllers/BringYourOwnWalletController.cs +++ b/src/registration/Registration.Service/Controllers/BringYourOwnWalletController.cs @@ -41,5 +41,29 @@ public async Task validateDid([FromRoute] string did, Cancellat return NoContent(); } + + [HttpPost] + [Authorize(Roles = "submit_registration")] + [Route("{companyId}/saveHolderDid/{did}")] + [ProducesResponseType(typeof(NoContentResult), StatusCodes.Status204NoContent)] + [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status415UnsupportedMediaType)] + [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status404NotFound)] + public async Task SaveHolderDid([FromRoute] Guid companyId, [FromRoute] string did) + { + await logic.SaveCustomerWalletAsync(companyId, did); + + return NoContent(); + } + + [HttpGet] + [Authorize(Roles = "submit_registration")] + [Route("{did}/getHolderDid")] + [ProducesResponseType(typeof(NoContentResult), StatusCodes.Status204NoContent)] + [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status415UnsupportedMediaType)] + [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status404NotFound)] + public async Task GetHolderDid([FromRoute] Guid companyId) + { + return await logic.getCompanyWalletDidAsync(companyId); + } } } diff --git a/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs b/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs index d24a21b1b4..c9e97324e2 100644 --- a/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs +++ b/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs @@ -21,6 +21,8 @@ using FakeItEasy; using FluentAssertions; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; using Org.Eclipse.TractusX.Portal.Backend.Registration.Service.BusinessLogic; using Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library; using Org.Eclipse.TractusX.Portal.Backend.UniversalDidResolver.Library.Models; @@ -33,11 +35,12 @@ public class BringYourOwnWalletBuisinessLogicTests { private readonly IUniversalDidResolverService _universalDidResolverService; private readonly IBringYourOwnWalletBusinessLogic _sut; - + private readonly IPortalRepositories _portalRepositories = A.Fake(); public BringYourOwnWalletBuisinessLogicTests() { _universalDidResolverService = A.Fake(); - _sut = new BringYourOwnWalletBusinessLogic(_universalDidResolverService); + _portalRepositories = A.Fake(); + _sut = new BringYourOwnWalletBusinessLogic(_universalDidResolverService, _portalRepositories); } [Fact] @@ -82,6 +85,7 @@ public async Task ValidateDid_ThrowsServiceException_WhenSchemaIsInvalid() const string did = "did:web:123"; var didDocument = JsonDocument.Parse("{\"id\":\"did:web:123\"}"); var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); + A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) .ReturnsLazily(() => Task.FromResult(validationResult)); A.CallTo(() => _universalDidResolverService.ValidateSchema(didDocument, A._)) @@ -115,4 +119,112 @@ public async Task ValidateDid_ThrowsServiceException_WhenValidationResultIsNull( await act.Should().ThrowAsync() .WithMessage("DID validation failed. DID Document is not valid."); } + + [Fact] + public async Task SaveCustomerWalletAsync_SavesWallet_WhenDidIsValid() + { + // Arrange + var companyId = Guid.NewGuid(); + var did = "did:web:example.com"; + var didDocument = JsonDocument.Parse("{\"id\":\"did:web:example.com\"}"); + var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); + + A.CallTo(() => _portalRepositories.GetInstance().CreateCustomerWallet(companyId, did, didDocument)) + .DoesNothing(); + A.CallTo(() => _portalRepositories.GetInstance()) + .Returns(A.Fake()); + A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) + .ReturnsLazily(() => Task.FromResult(validationResult)); + A.CallTo(() => _universalDidResolverService.ValidateSchema(didDocument, A._)) + .ReturnsLazily(() => true); + // Act + await _sut.SaveCustomerWalletAsync(companyId, did); + + // Assert + A.CallTo(() => _portalRepositories.GetInstance().CreateCustomerWallet(companyId, did, didDocument)) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappenedOnceExactly(); + } + + [Fact] + public async Task SaveCustomerWalletAsync_ThrowsException_WhenDidIsEmpty() + { + // Arrange + var companyId = Guid.NewGuid(); + var did = ""; + var didDocument = JsonDocument.Parse("{\"id\":\"did:web:example.com\"}"); + var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); + + A.CallTo(() => _portalRepositories.GetInstance()) + .Returns(A.Fake()); + A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) + .ReturnsLazily(() => Task.FromResult(validationResult)); + A.CallTo(() => _universalDidResolverService.ValidateSchema(didDocument, A._)) + .ReturnsLazily(() => true); + // Act + Func act = async () => await _sut.SaveCustomerWalletAsync(companyId, did); + + // Assert + await act.Should().ThrowAsync() + .WithMessage("Invalid DID. DID cannot be empty or NULL."); + } + + [Fact] + public async Task SaveCustomerWalletAsync_ThrowsException_WhenDidIsInvalid() + { + // Arrange + var companyId = Guid.NewGuid(); + var did = "did:web:INVALID"; + var didDocument = JsonDocument.Parse("{\"id\":\"did:web:example.com\"}"); + var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); + + A.CallTo(() => _portalRepositories.GetInstance()) + .Returns(A.Fake()); + A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)).Throws(); + A.CallTo(() => _universalDidResolverService.ValidateSchema(didDocument, A._)) + .ReturnsLazily(() => true); + // Act + Func act = async () => await _sut.SaveCustomerWalletAsync(companyId, did); + + // Assert + await act.Should().ThrowAsync(); + } + + [Fact] + public async Task GetCompanyWalletDidAsync_ReturnsDid_WhenWalletExists() + { + // Arrange + var companyId = Guid.NewGuid(); + var expectedDid = "did:web:example.com"; + var companyRepository = A.Fake(); + A.CallTo(() => _portalRepositories.GetInstance()) + .Returns(companyRepository); + A.CallTo(() => companyRepository.GetCompanyHolderDidAsync(companyId)) + .Returns(Task.FromResult(expectedDid)); + + // Act + var result = await _sut.getCompanyWalletDidAsync(companyId); + + // Assert + result.Should().Be(expectedDid); + } + + [Fact] + public async Task GetCompanyWalletDidAsync_ReturnsNull_WhenWalletDoesNotExist() + { + // Arrange + var companyId = Guid.NewGuid(); + var companyRepository = A.Fake(); + A.CallTo(() => _portalRepositories.GetInstance()) + .Returns(companyRepository); + A.CallTo(() => companyRepository.GetCompanyHolderDidAsync(companyId)) + .Returns(Task.FromResult(null)); + + // Act + Func act = async () => await _sut.getCompanyWalletDidAsync(companyId); + + // Assert + await act.Should().ThrowAsync() + .WithMessage("Company wallet DID not found for the given company ID."); + } } diff --git a/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs b/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs index 18a4e39539..1a6fdea939 100644 --- a/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs +++ b/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs @@ -49,7 +49,6 @@ using Org.Eclipse.TractusX.Portal.Backend.Tests.Shared; using Org.Eclipse.TractusX.Portal.Backend.Tests.Shared.Extensions; using System.Collections.Immutable; -using System.Text.Json; using Xunit; using Address = Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Entities.Address; using RegistrationData = Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models.RegistrationData; @@ -75,7 +74,6 @@ public class RegistrationBusinessLogicTest private readonly IPortalProcessStepRepository _processStepRepository; private readonly IIdentityProviderRepository _identityProviderRepository; private readonly IApplicationChecklistCreationService _checklistService; - private readonly IBringYourOwnWalletBusinessLogic _bringYourOwnWalletBusinessLogic; private readonly IIdentityData _identity; private readonly Guid _existingApplicationId; private readonly string _displayName; @@ -111,7 +109,6 @@ public RegistrationBusinessLogicTest() _countryRepository = A.Fake(); _consentRepository = A.Fake(); _mailingProcessCreation = A.Fake(); - _bringYourOwnWalletBusinessLogic = A.Fake(); _checklistService = A.Fake(); _staticDataRepository = A.Fake(); @@ -176,8 +173,7 @@ public async Task GetClientRolesCompositeAsync_GetsAllRoles() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); // Act var result = await sut.GetClientRolesCompositeAsync().ToListAsync(); @@ -256,8 +252,7 @@ public async Task GetCompanyBpdmDetailDataByBusinessPartnerNumber_WithValidBpn_R null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); // Act var result = await sut @@ -302,8 +297,7 @@ public async Task GetCompanyBpdmDetailDataByBusinessPartnerNumber_WithValidBpn_T null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); // Act Task Act() => sut.GetCompanyBpdmDetailDataByBusinessPartnerNumber("NotLongEnough", "justatoken", CancellationToken.None); @@ -338,8 +332,7 @@ public async Task GetAllApplicationsForUserWithStatus_WithValidUser_GetsAllRoles null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); var resultList = new[] { @@ -408,8 +401,7 @@ public async Task GetCompanyWithAddressAsync_WithValidApplication_GetsData() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); A.CallTo(() => _applicationRepository.GetCompanyApplicationDetailDataAsync(A._, A._, A._)) .Returns(data); @@ -439,8 +431,7 @@ public async Task GetCompanyWithAddressAsync_WithInvalidApplication_ThrowsNotFou null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); A.CallTo(() => _applicationRepository.GetCompanyApplicationDetailDataAsync(A._, A._, A._)) .Returns(null); @@ -470,8 +461,7 @@ public async Task GetCompanyWithAddressAsync_WithInvalidUser_ThrowsForbiddenExce null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); A.CallTo(() => _applicationRepository.GetCompanyApplicationDetailDataAsync(A._, A._, A._)) .Returns(_fixture.Build().With(x => x.IsUserOfCompany, false).Create()); @@ -522,8 +512,7 @@ public async Task SetCompanyWithAddressAsync_WithMissingData_ThrowsArgumentExcep null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); var uniqueIdData = uniqueIdentifierIds.Zip(values, (id, value) => new CompanyUniqueIdData(id, value)); var companyData = new CompanyDetailData(Guid.NewGuid(), name!, city!, streetName!, countryCode!, null, null, @@ -553,8 +542,7 @@ public async Task SetCompanyWithAddressAsync_WithInvalidApplicationId_ThrowsNotF null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); var companyData = new CompanyDetailData(companyId, "name", "munich", "main street", "de", null, null, _region, null, null, null, Enumerable.Empty()); @@ -594,8 +582,7 @@ public async Task SetCompanyWithAddressAsync_WithoutCompanyUserId_ThrowsForbidde null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); A.CallTo(() => _applicationRepository.GetCompanyApplicationDetailDataAsync(applicationId, A._, companyId)) .Returns(_fixture.Build().With(x => x.IsUserOfCompany, false).Create()); @@ -636,8 +623,7 @@ public async Task SetCompanyWithAddressAsync__WithInvalidBpn_ThrowsControllerArg null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); // Act Task Act() => sut.SetCompanyDetailDataAsync(applicationId, companyData); @@ -678,8 +664,7 @@ public async Task SetCompanyWithAddressAsync__WithInvalidRegion_ThrowsController null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); // Act Task Act() => sut.SetCompanyDetailDataAsync(applicationId, companyData); @@ -708,7 +693,6 @@ public async Task SetCompanyWithAddressAsync__WithExistingBpn_ModifiesCompany() .With(x => x.CountryAlpha2Code, _alpha2code) .With(x => x.Region, _region) .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, _vatId)]) - .With(x => x.HolderDid, (string?)null) .Create(); A.CallTo(() => _companyRepository.CheckBpnExists("BPNL00000001TEST")).Returns(true); @@ -722,8 +706,7 @@ public async Task SetCompanyWithAddressAsync__WithExistingBpn_ModifiesCompany() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); var existingData = _fixture.Build() .With(x => x.IsUserOfCompany, true) @@ -798,7 +781,6 @@ public async Task SetCompanyWithAddressAsync__WithCompanyNameChange_ModifiesComp .With(x => x.CompanyId, companyId) .With(x => x.CountryAlpha2Code, countryCode) .With(x => x.Region, _region) - .With(x => x.HolderDid, (string?)null) .With(x => x.UniqueIds, [new CompanyUniqueIdData(uniqueIdentifierId, identifierValue)]) .Create(); @@ -812,8 +794,7 @@ public async Task SetCompanyWithAddressAsync__WithCompanyNameChange_ModifiesComp null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); var existingData = _fixture.Build() .With(x => x.IsUserOfCompany, true) @@ -867,7 +848,6 @@ public async Task SetCompanyWithAddressAsync__WithoutCompanyNameChange_ModifiesC .With(x => x.CountryAlpha2Code, _alpha2code) .With(x => x.Region, _region) .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, _vatId)]) - .With(x => x.HolderDid, (string?)null) .Create(); var sut = new RegistrationBusinessLogic( @@ -880,8 +860,7 @@ public async Task SetCompanyWithAddressAsync__WithoutCompanyNameChange_ModifiesC null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); var existingData = _fixture.Build() .With(x => x.IsUserOfCompany, true) @@ -941,7 +920,6 @@ public async Task SetCompanyWithAddressAsync_ModifyCompany(string? bpn, string r .With(x => x.CountryAlpha2Code, _alpha2code) .With(x => x.Region, region) .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, _vatId)]) - .With(x => x.HolderDid, (string?)null) .Create(); var existingData = _fixture.Build() @@ -960,8 +938,7 @@ public async Task SetCompanyWithAddressAsync_ModifyCompany(string? bpn, string r null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); A.CallTo(() => _applicationRepository.GetCompanyApplicationDetailDataAsync(applicationId, A._, companyId)) .Returns(existingData); @@ -1012,7 +989,6 @@ public async Task SetCompanyWithAddressAsync_WithoutInitialCompanyAddress_Create .With(x => x.CountryAlpha2Code, _alpha2code) .With(x => x.Region, _region) .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, _vatId)]) - .With(x => x.HolderDid, () => (string?)null) .Create(); var existingData = _fixture.Build() @@ -1041,8 +1017,7 @@ public async Task SetCompanyWithAddressAsync_WithoutInitialCompanyAddress_Create null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); A.CallTo(() => _applicationRepository.GetCompanyApplicationDetailDataAsync(applicationId, A._, companyId)) .Returns(existingData); @@ -1079,8 +1054,6 @@ public async Task SetCompanyWithAddressAsync_WithoutInitialCompanyAddress_Create A.CallTo(() => _companyRepository.AttachAndModifyAddress(A._, A>._, A>._)) .MustNotHaveHappened(); A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappenedOnceExactly(); - A.CallTo(() => _companyRepository.CreateCustomerWallet(A._, A._, A._)) - .MustNotHaveHappened(); company.Should().NotBeNull(); address.Should().NotBeNull(); @@ -1116,7 +1089,6 @@ public async Task SetCompanyWithAddressAsync_WithInitialCompanyAddress_ModifyAdd .With(x => x.CountryAlpha2Code, _alpha2code) .With(x => x.Region, _region) .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, _vatId)]) - .With(x => x.HolderDid, (string?)null) .Create(); var existingData = _fixture.Build() @@ -1137,8 +1109,7 @@ public async Task SetCompanyWithAddressAsync_WithInitialCompanyAddress_ModifyAdd null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); A.CallTo(() => _applicationRepository.GetCompanyApplicationDetailDataAsync(applicationId, A._, companyId)) .Returns(existingData); @@ -1192,112 +1163,6 @@ public async Task SetCompanyWithAddressAsync_WithInitialCompanyAddress_ModifyAdd a.Streetnumber == companyData.StreetNumber && a.Zipcode == companyData.ZipCode); } - [Fact] - public async Task SetCompanyWithAddressAsync_WithoutInitialCompanyAddress_CreatesAddressAndAddCustomerWallet() - { - //Arrange - var applicationId = Guid.NewGuid(); - var companyId = Guid.NewGuid(); - var addressId = Guid.NewGuid(); - var identityData = A.Fake(); - A.CallTo(() => identityData.IdentityId).Returns(Guid.NewGuid()); - A.CallTo(() => identityData.IdentityTypeId).Returns(IdentityTypeId.COMPANY_USER); - A.CallTo(() => identityData.CompanyId).Returns(companyId); - - A.CallTo(() => _identityService.IdentityData).Returns(identityData); - var companyData = _fixture.Build() - .With(x => x.BusinessPartnerNumber, default(string?)) - .With(x => x.CompanyId, companyId) - .With(x => x.CountryAlpha2Code, _alpha2code) - .With(x => x.Region, _region) - .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, _vatId)]) - .With(x => x.HolderDid, "https://did.example.com/holder") - .Create(); - - var existingData = _fixture.Build() - .With(x => x.CompanyId, companyId) - .With(x => x.AddressId, default(Guid?)) - .With(x => x.City, default(string?)) - .With(x => x.CountryAlpha2Code, default(string?)) - .With(x => x.Region, default(string?)) - .With(x => x.Streetadditional, default(string?)) - .With(x => x.Streetname, default(string?)) - .With(x => x.Streetnumber, default(string?)) - .With(x => x.Zipcode, default(string?)) - .With(x => x.IsUserOfCompany, true) - .Create(); - - var company = new Company(companyId, null!, default, default); - Address? address = null; - - var sut = new RegistrationBusinessLogic( - _options, - null!, - _userProvisioningService, - _identityProviderProvisioningService, - null!, - _portalRepositories, - null!, - _identityService, - _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); - - A.CallTo(() => _applicationRepository.GetCompanyApplicationDetailDataAsync(applicationId, A._, companyId)) - .Returns(existingData); - - A.CallTo(() => _companyRepository.AttachAndModifyCompany(A._, A>._, A>._)) - .Invokes((Guid companyId, Action? initialize, Action modify) => - { - initialize?.Invoke(company); - modify(company); - }); - - A.CallTo(() => _companyRepository.CreateAddress(A._, A._, A._, A._, A>._)) - .ReturnsLazily((string city, string streetName, string region, string alpha2Code, Action
? setParameters) => - { - address = new Address(addressId, city, streetName, region, alpha2Code, default); - setParameters?.Invoke(address); - return address; - }); - - // Act - await sut.SetCompanyDetailDataAsync(applicationId, companyData); - - // Assert - A.CallTo(() => _companyRepository.CreateAddress(A._, A._, A._, A._, A>._)) - .MustHaveHappenedOnceExactly(); - A.CallTo(() => _companyRepository.CreateAddress(companyData.City, companyData.StreetName, companyData.Region, - companyData.CountryAlpha2Code, A>._)) - .MustHaveHappened(); - A.CallTo(() => _companyRepository.AttachAndModifyCompany(A._, A>._, A>._)) - .MustHaveHappenedTwiceExactly(); - A.CallTo(() => _companyRepository.AttachAndModifyCompany(companyId, A>._, A>._)) - .MustHaveHappened(); - A.CallTo(() => _companyRepository.AttachAndModifyAddress(A._, A>._, A>._)) - .MustNotHaveHappened(); - A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappenedOnceExactly(); - A.CallTo(() => _companyRepository.CreateCustomerWallet(A._, A._, A._)) - .MustHaveHappenedOnceExactly(); - A.CallTo(() => _bringYourOwnWalletBusinessLogic.ValidateDid(A._, CancellationToken.None)) - .MustHaveHappenedOnceExactly(); - - company.Should().NotBeNull(); - address.Should().NotBeNull(); - - company!.Id.Should().Be(companyId); - company.AddressId.Should().Be(addressId); - - address.Should().Match
(a => - a.Id == addressId && - a.City == companyData.City && - a.CountryAlpha2Code == companyData.CountryAlpha2Code && - a.Region == companyData.Region && - a.Streetadditional == companyData.StreetAdditional && - a.Streetname == companyData.StreetName && - a.Streetnumber == companyData.StreetNumber && - a.Zipcode == companyData.ZipCode); - } [Fact] public async Task SetCompanyWithAddressAsync_WithUniqueIdentifiers_CreateModifyDeleteExpected() @@ -1361,8 +1226,7 @@ public async Task SetCompanyWithAddressAsync_WithUniqueIdentifiers_CreateModifyD null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); A.CallTo(() => _dateTimeProvider.OffsetNow).Returns(now); A.CallTo(() => _applicationRepository.GetCompanyApplicationDetailDataAsync(applicationId, A._, companyId)) @@ -1434,8 +1298,7 @@ public async Task SetCompanyWithAddressAsync_WithInvalidCountryCode_Throws() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); A.CallTo(() => _countryRepository.GetCountryAssignedIdentifiers(A._, A>._)) @@ -1482,8 +1345,7 @@ public async Task SetCompanyWithAddressAsync_WithInvalidUniqueIdentifiers_Throws null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); A.CallTo(() => _countryRepository.GetCountryAssignedIdentifiers(_alpha2code, A>._)) @@ -1522,8 +1384,7 @@ public async Task SetOwnCompanyApplicationStatusAsync_WithInvalidStatus_ThrowsCo null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); // Act Task Act() => sut.SetOwnCompanyApplicationStatusAsync(applicationId, 0); @@ -1553,8 +1414,7 @@ public async Task SetOwnCompanyApplicationStatusAsync_WithInvalidApplication_Thr null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserDataAsync(A._, A._)) .Returns<(bool, CompanyApplicationStatusId)>(default); @@ -1587,8 +1447,7 @@ public async Task SetOwnCompanyApplicationStatusAsync_WithInvalidStatus_ThrowsAr null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); var existingStatus = CompanyApplicationStatusId.CREATED; A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserDataAsync(A._, A._)) @@ -1645,8 +1504,7 @@ public async Task SetOwnCompanyApplicationStatusAsync_WithValidData_SavesChanges null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserDataAsync(A._, A._)) .Returns((true, currentStatus)); @@ -1691,8 +1549,7 @@ public async Task GetCompanyRolesAsync_() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); // Act var result = await sut.GetCompanyRoles().ToListAsync(); @@ -1781,8 +1638,7 @@ public async Task UploadDocumentAsync_WithValidData_CreatesDocument() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); // Act await sut.UploadDocumentAsync(_existingApplicationId, file, DocumentTypeId.CX_FRAME_CONTRACT, CancellationToken.None); @@ -1846,8 +1702,7 @@ public async Task UploadDocumentAsync_WithNotExistingApplicationId_ThrowsExcepti null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); var notExistingId = Guid.NewGuid(); // Act @@ -1883,8 +1738,7 @@ public async Task UploadDocumentAsync_WithNotExistingIamUser_ThrowsException() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); // Act Task Act() => sut.UploadDocumentAsync(_existingApplicationId, file, DocumentTypeId.CX_FRAME_CONTRACT, CancellationToken.None); @@ -1918,8 +1772,7 @@ public async Task UploadDocumentAsync_WithInvalidDocumentTypeId_ThrowsException( null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); // Act Task Act() => sut.UploadDocumentAsync(_existingApplicationId, file, DocumentTypeId.ADDITIONAL_DETAILS, CancellationToken.None); @@ -1960,8 +1813,7 @@ public async Task TestInviteNewUserAsyncSuccess() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); await sut.InviteNewUserAsync(_existingApplicationId, userCreationInfo); @@ -1995,8 +1847,7 @@ public async Task TestInviteNewUserEmptyEmailThrows() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); Task Act() => sut.InviteNewUserAsync(_existingApplicationId, userCreationInfo); @@ -2027,8 +1878,7 @@ public async Task TestInviteNewUserUserAlreadyExistsThrows() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); Task Act() => sut.InviteNewUserAsync(_existingApplicationId, userCreationInfo); @@ -2064,8 +1914,7 @@ public async Task TestInviteNewUserAsyncCreationErrorThrows() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); Task Act() => sut.InviteNewUserAsync(_existingApplicationId, userCreationInfo); @@ -2105,8 +1954,7 @@ public async Task GetUploadedDocumentsAsync_ReturnsExpectedOutput() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); // Act var result = await sut.GetUploadedDocumentsAsync(applicationId, DocumentTypeId.APP_CONTRACT); @@ -2137,8 +1985,7 @@ public async Task GetUploadedDocumentsAsync_InvalidApplication_ThrowsNotFound() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); Task Act() => sut.GetUploadedDocumentsAsync(applicationId, DocumentTypeId.APP_CONTRACT); @@ -2170,8 +2017,7 @@ public async Task GetUploadedDocumentsAsync_InvalidUser_ThrowsForbidden() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); Task Act() => sut.GetUploadedDocumentsAsync(applicationId, DocumentTypeId.APP_CONTRACT); @@ -2193,7 +2039,7 @@ public async Task SubmitRoleConsentsAsync_WithNotExistingApplication_ThrowsNotFo var notExistingId = _fixture.Create(); A.CallTo(() => _companyRolesRepository.GetCompanyRoleAgreementConsentDataAsync(notExistingId)) .Returns(null); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.SubmitRoleConsentAsync(notExistingId, _fixture.Create()); @@ -2212,7 +2058,7 @@ public async Task SubmitRoleConsentsAsync_WithWrongCompanyUser_ThrowsForbiddenEx var data = new CompanyRoleAgreementConsentData(Guid.NewGuid(), applicationStatusId, _fixture.CreateMany(2), _fixture.CreateMany(5)); A.CallTo(() => _companyRolesRepository.GetCompanyRoleAgreementConsentDataAsync(applicationId)) .Returns(data); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.SubmitRoleConsentAsync(applicationId, _fixture.Create()); @@ -2241,7 +2087,7 @@ public async Task SubmitRoleConsentsAsync_WithInvalidRoles_ThrowsControllerArgum .Returns(data); A.CallTo(() => _companyRolesRepository.GetAgreementAssignedCompanyRolesUntrackedAsync(roleIds)) .Returns(companyRoleAssignedAgreements.ToAsyncEnumerable()); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.SubmitRoleConsentAsync(applicationId, _fixture.Create()); @@ -2277,7 +2123,7 @@ public async Task SubmitRoleConsentsAsync_WithoutAllRolesConsentGiven_ThrowsCont A.CallTo(() => _companyRolesRepository.GetAgreementAssignedCompanyRolesUntrackedAsync(A>._)) .Returns(companyRoleAssignedAgreements.ToAsyncEnumerable()); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.SubmitRoleConsentAsync(applicationId, consents); @@ -2359,7 +2205,7 @@ public async Task SubmitRoleConsentsAsync_WithValidData_CallsExpected() removedCompanyRoleIds = companyRoleIds; }); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act await sut.SubmitRoleConsentAsync(applicationId, consents); @@ -2412,7 +2258,7 @@ public async Task SubmitRoleConsentsAsync_WithextraAgreement_ThrowsControllerArg A.CallTo(() => _companyRolesRepository.GetAgreementAssignedCompanyRolesUntrackedAsync(A>._)) .Returns(companyRoleAssignedAgreements.ToAsyncEnumerable()); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.SubmitRoleConsentAsync(applicationId, consents); @@ -2439,7 +2285,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingApplication_ThrowsNotFo }; A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(A._, A._, A>._)) .Returns(null); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.SubmitRegistrationAsync(notExistingId); @@ -2529,7 +2375,7 @@ public async Task SubmitRegistrationAsync_WithDocumentId_Success() DocumentTypeId.COMMERCIAL_REGISTER_EXTRACT ] }; - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, _checklistService, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, _checklistService, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act await sut.SubmitRegistrationAsync(applicationId); @@ -2620,7 +2466,7 @@ public async Task SubmitRegistrationAsync_InvalidStatus_ThrowsForbiddenException var companyData = new CompanyData("Test Company", Guid.NewGuid(), "Strabe Street", "Munich", "Germany", uniqueIds, companyRoleIds); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(A._, A._, A>._)) .Returns(new CompanyApplicationUserEmailData(statusId, true, _fixture.Create(), documents, companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2665,7 +2511,7 @@ public async Task SubmitRegistrationAsync_AlreadyClosed_ThrowsForbiddenException }; A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(A._, A._, A>._)) .Returns(new CompanyApplicationUserEmailData(statusId, true, _fixture.Create(), documents, companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2703,7 +2549,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingCompanyUser_ThrowsForbi }; A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(A._, A._, A>._)) .Returns(new CompanyApplicationUserEmailData(CompanyApplicationStatusId.VERIFY, false, null, null!, companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2741,7 +2587,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingStreetName_ThrowsConfli }; A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(applicationId, userId, A>._)) .Returns(new CompanyApplicationUserEmailData(CompanyApplicationStatusId.VERIFY, true, _fixture.Create(), Enumerable.Empty(), companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2771,7 +2617,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingAddressId_ThrowsConflic var companyData = new CompanyData("Test Company", null, "Strabe Street", "Munich", "Germany", uniqueIds, companyRoleIds); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(applicationId, userId, A>._)) .Returns(new CompanyApplicationUserEmailData(CompanyApplicationStatusId.VERIFY, true, _fixture.Create(), Enumerable.Empty(), companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2801,7 +2647,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingCompanyName_ThrowsConfl var companyData = new CompanyData(string.Empty, Guid.NewGuid(), "Strabe Street", "Munich", "Germany", uniqueIds, companyRoleIds); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(applicationId, userId, A>._)) .Returns(new CompanyApplicationUserEmailData(CompanyApplicationStatusId.VERIFY, true, _fixture.Create(), Enumerable.Empty(), companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2831,7 +2677,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingUniqueId_ThrowsConflict var companyData = new CompanyData("Test Company", Guid.NewGuid(), "Strabe Street", "Munich", "Germany", uniqueIdentifierData, companyRoleIds); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(applicationId, userId, A>._)) .Returns(new CompanyApplicationUserEmailData(CompanyApplicationStatusId.VERIFY, true, _fixture.Create(), Enumerable.Empty(), companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2861,7 +2707,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingCompanyRoleId_ThrowsCon var companyData = new CompanyData("Test Company", Guid.NewGuid(), "Strabe Street", "Munich", "Germany", uniqueIds, companyRoleIdData); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(applicationId, userId, A>._)) .Returns(new CompanyApplicationUserEmailData(CompanyApplicationStatusId.VERIFY, true, _fixture.Create(), Enumerable.Empty(), companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2888,7 +2734,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingAgreementandConsent_Thr var companyData = new CompanyData("Test Company", Guid.NewGuid(), "Strabe Street", "Munich", "Germany", uniqueIds, companyRoleIds); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(applicationId, userId, A>._)) .Returns(new CompanyApplicationUserEmailData(CompanyApplicationStatusId.VERIFY, true, _fixture.Create(), Enumerable.Empty(), companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2918,7 +2764,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingCity_ThrowsConflictExce var companyData = new CompanyData("Test Company", Guid.NewGuid(), "Strabe Street", string.Empty, "Germany", uniqueIds, companyRoleIds); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(applicationId, userId, A>._)) .Returns(new CompanyApplicationUserEmailData(CompanyApplicationStatusId.VERIFY, true, _fixture.Create(), Enumerable.Empty(), companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2949,7 +2795,7 @@ public async Task SubmitRegistrationAsync_WithNotExistingCountry_ThrowsConflictE var companyData = new CompanyData("Test Company", Guid.NewGuid(), "Strabe Street", "Munich", string.Empty, uniqueIds, companyRoleIds); A.CallTo(() => _applicationRepository.GetOwnCompanyApplicationUserEmailDataAsync(applicationId, userId, A>._)) .Returns(new CompanyApplicationUserEmailData(CompanyApplicationStatusId.VERIFY, true, _fixture.Create(), Enumerable.Empty(), companyData, agreementConsents)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.SubmitRegistrationAsync(applicationId); @@ -2993,7 +2839,7 @@ public async Task SubmitRegistrationAsync_WithUserEmail_SendsMail() { setOptionalFields.Invoke(application); }); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, _checklistService, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, _checklistService, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act var result = await sut.SubmitRegistrationAsync(applicationId); @@ -3043,7 +2889,7 @@ public async Task SubmitRegistrationAsync_WithoutUserEmail_DoesntSendMail() { setOptionalFields.Invoke(application); }); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, A.Fake>(), _portalRepositories, _checklistService, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, A.Fake>(), _portalRepositories, _checklistService, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act var result = await sut.SubmitRegistrationAsync(applicationId); @@ -3082,8 +2928,7 @@ public async Task GetCompanyIdentifiers_ReturnsExpectedOutput() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); // Act var result = await sut.GetCompanyIdentifiers(_fixture.Create()); @@ -3115,8 +2960,7 @@ public async Task GetCompanyIdentifiers_InvalidCountry_Throws() null!, _identityService, _dateTimeProvider, - _mailingProcessCreation, - _bringYourOwnWalletBusinessLogic); + _mailingProcessCreation); var countryCode = _fixture.Create(); @@ -3140,7 +2984,7 @@ public async Task GetRegistrationDataAsync_ReturnsExpected() A.CallTo(() => _applicationRepository.GetRegistrationDataUntrackedAsync(_existingApplicationId, _identity.CompanyId, A>._)) .Returns((true, true, data)); - var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act var result = await sut.GetRegistrationDataAsync(_existingApplicationId); @@ -3185,7 +3029,7 @@ public async Task GetRegistrationDataAsync_WithInvalidApplicationId_Throws() A.CallTo(() => _applicationRepository.GetRegistrationDataUntrackedAsync(A._, _identity.CompanyId, A>._)) .Returns((false, false, data)); - var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.GetRegistrationDataAsync(applicationId); @@ -3204,7 +3048,7 @@ public async Task GetRegistrationDataAsync_WithInvalidUser_Throws() A.CallTo(() => _applicationRepository.GetRegistrationDataUntrackedAsync(A._, _identity.CompanyId, A>._)) .Returns((true, false, data)); - var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.GetRegistrationDataAsync(applicationId); @@ -3223,7 +3067,7 @@ public async Task GetRegistrationDataAsync_WithNullData_Throws() A.CallTo(() => _applicationRepository.GetRegistrationDataUntrackedAsync(A._, _identity.CompanyId, A>._)) .Returns((true, true, null)); - var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.GetRegistrationDataAsync(applicationId); @@ -3243,7 +3087,7 @@ public async Task GetRegistrationDocumentAsync_ReturnsExpectedResult() var content = new byte[7]; A.CallTo(() => _documentRepository.GetDocumentAsync(documentId, A>._)) .Returns((content, "test.json", true, MediaTypeId.JSON)); - var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); //Act var result = await sut.GetRegistrationDocumentAsync(documentId); @@ -3262,7 +3106,7 @@ public async Task GetRegistrationDocumentAsync_WithInvalidDocumentTypeId_ThrowsN var content = new byte[7]; A.CallTo(() => _documentRepository.GetDocumentAsync(documentId, A>._)) .Returns((content, "test.json", false, MediaTypeId.JSON)); - var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); //Act Task Act() => sut.GetRegistrationDocumentAsync(documentId); @@ -3279,7 +3123,7 @@ public async Task GetRegistrationDocumentAsync_WithInvalidDocumentId_ThrowsNotFo var documentId = Guid.NewGuid(); A.CallTo(() => _documentRepository.GetDocumentAsync(documentId, A>._)) .Returns<(byte[], string, bool, MediaTypeId)>(default); - var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); //Act Task Act() => sut.GetRegistrationDocumentAsync(documentId); @@ -3301,7 +3145,7 @@ public async Task GetDocumentAsync_WithValidData_ReturnsExpected() .Returns((documentId, true, true, false)); A.CallTo(() => _documentRepository.GetDocumentByIdAsync(A._, A>._)) .Returns(new Document(documentId, content, content, "test.pdf", MediaTypeId.PDF, DateTimeOffset.UtcNow, DocumentStatusId.LOCKED, DocumentTypeId.APP_CONTRACT, content.Length)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act var result = await sut.GetDocumentContentAsync(documentId); @@ -3320,7 +3164,7 @@ public async Task GetDocumentAsync_WithoutDocument_ThrowsNotFoundException() var documentId = Guid.NewGuid(); A.CallTo(() => _documentRepository.GetDocumentIdWithCompanyUserCheckAsync(documentId, _identity.IdentityId)) .Returns((Guid.Empty, false, false, false)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.GetDocumentContentAsync(documentId); @@ -3337,7 +3181,7 @@ public async Task GetDocumentAsync_WithWrongUser_ThrowsForbiddenException() var documentId = Guid.NewGuid(); A.CallTo(() => _documentRepository.GetDocumentIdWithCompanyUserCheckAsync(documentId, _identity.IdentityId)) .Returns((documentId, false, false, false)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.GetDocumentContentAsync(documentId); @@ -3354,7 +3198,7 @@ public async Task GetDocumentAsync_WithConfirmedApplicationStatus_ThrowsForbidde var documentId = Guid.NewGuid(); A.CallTo(() => _documentRepository.GetDocumentIdWithCompanyUserCheckAsync(documentId, _identity.IdentityId)) .Returns((documentId, true, true, true)); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.GetDocumentContentAsync(documentId); @@ -3391,7 +3235,7 @@ public async Task SetInvitationStatusAsync_ReturnsExpected() }); A.CallTo(() => _portalRepositories.SaveAsync()).Returns(retval); - var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act var result = await sut.SetInvitationStatusAsync(); @@ -3410,7 +3254,7 @@ public async Task SetInvitationStatusAsync_Throws_ForbiddenException() // Arrange A.CallTo(() => _invitationRepository.GetInvitationStatusAsync(A._)) .Returns(null); - var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(_options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); //Act Task Act() => sut.SetInvitationStatusAsync(); @@ -3464,7 +3308,7 @@ public async Task DeleteRegistrationDocumentAsync_ReturnsExpected() A.CallTo(() => _documentRepository.GetDocumentDetailsForApplicationUntrackedAsync(documentId, _identity.CompanyId, settings.ApplicationStatusIds)) .Returns((documentId, DocumentStatusId.PENDING, true, DocumentTypeId.CX_FRAME_CONTRACT, false, applicationIds)); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act var result = await sut.DeleteRegistrationDocumentAsync(documentId); @@ -3502,7 +3346,7 @@ public async Task DeleteRegistrationDocumentAsync_DocumentTypeId_ConflictExcepti A.CallTo(() => _documentRepository.GetDocumentDetailsForApplicationUntrackedAsync(documentId, _identity.CompanyId, settings.ApplicationStatusIds)) .Returns((documentId, DocumentStatusId.PENDING, true, DocumentTypeId.CONFORMITY_APPROVAL_BUSINESS_APPS, false, applicationId)); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.DeleteRegistrationDocumentAsync(documentId); @@ -3519,7 +3363,7 @@ public async Task DeleteRegistrationDocumentAsync_Throws_NotFoundException() A.CallTo(() => _documentRepository.GetDocumentDetailsForApplicationUntrackedAsync(A._, _identity.CompanyId, A>._)) .Returns<(Guid, DocumentStatusId, bool, DocumentTypeId, bool, IEnumerable)>(default); - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.DeleteRegistrationDocumentAsync(_fixture.Create()); @@ -3550,7 +3394,7 @@ public async Task DeleteRegistrationDocumentAsync_Throws_ConflictException() A.CallTo(() => _documentRepository.GetDocumentDetailsForApplicationUntrackedAsync(A._, _identity.CompanyId, A>._)) .Returns((documentId, DocumentStatusId.PENDING, true, DocumentTypeId.CX_FRAME_CONTRACT, true, applicationId)); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.DeleteRegistrationDocumentAsync(documentId); @@ -3581,7 +3425,7 @@ public async Task DeleteRegistrationDocumentAsync_Throws_ForbiddenException() A.CallTo(() => _documentRepository.GetDocumentDetailsForApplicationUntrackedAsync(A._, _identity.CompanyId, A>._)) .Returns((documentId, DocumentStatusId.PENDING, false, DocumentTypeId.CX_FRAME_CONTRACT, false, applicationId)); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.DeleteRegistrationDocumentAsync(documentId); @@ -3612,7 +3456,7 @@ public async Task DeleteRegistrationDocumentAsync_DocumentStatusId_Throws_Confli A.CallTo(() => _documentRepository.GetDocumentDetailsForApplicationUntrackedAsync(A._, _identity.CompanyId, A>._)) .Returns((documentId, DocumentStatusId.LOCKED, true, DocumentTypeId.CX_FRAME_CONTRACT, false, applicationId)); - var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(settings), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.DeleteRegistrationDocumentAsync(documentId); @@ -3626,7 +3470,7 @@ public async Task DeleteRegistrationDocumentAsync_DocumentStatusId_Throws_Confli public async Task DeleteRegistrationDocumentAsync_Throws_ControllerArgumentException() { // Arrange; - var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(Options.Create(new RegistrationSettings()), null!, null!, null!, null!, _portalRepositories, null!, _identityService, _dateTimeProvider, _mailingProcessCreation); // Act Task Act() => sut.DeleteRegistrationDocumentAsync(default); @@ -3749,7 +3593,7 @@ public async Task GetApplicationsDeclineData_CallsExpected() ApplicationDeclineStatusIds = [CompanyApplicationStatusId.CREATED] }); - var sut = new RegistrationBusinessLogic(options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, null!, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, null!, _mailingProcessCreation); // Act var result = await sut.GetApplicationsDeclineData(); @@ -3882,7 +3726,7 @@ public async Task DeclineApplicationRegistrationAsync_CallsExpected() } }); - var sut = new RegistrationBusinessLogic(options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, null!, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, null!, _mailingProcessCreation); // Act await sut.DeclineApplicationRegistrationAsync(applicationId); @@ -4056,7 +3900,7 @@ public async Task DeclineApplicationRegistrationAsync_ThrowsNotFoundException_Re A.CallTo(() => _applicationRepository.GetDeclineApplicationDataForApplicationId(A._, A._, A>._)) .Returns<(bool, bool, ApplicationDeclineData?)>(default); - var sut = new RegistrationBusinessLogic(options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, null!, _mailingProcessCreation, _bringYourOwnWalletBusinessLogic); + var sut = new RegistrationBusinessLogic(options, null!, null!, null!, null!, _portalRepositories, null!, _identityService, null!, _mailingProcessCreation); // Act Task Act() => sut.DeclineApplicationRegistrationAsync(applicationId); diff --git a/tests/registration/Registration.Service.Tests/Controller/BringYourOwnWalletControllerTests.cs b/tests/registration/Registration.Service.Tests/Controller/BringYourOwnWalletControllerTests.cs index 471973806f..099316ca24 100644 --- a/tests/registration/Registration.Service.Tests/Controller/BringYourOwnWalletControllerTests.cs +++ b/tests/registration/Registration.Service.Tests/Controller/BringYourOwnWalletControllerTests.cs @@ -77,4 +77,71 @@ public async Task ValidateDid_WhenDidIsInvalid_ShouldThrowServiceException() await act.Should().ThrowAsync() .WithMessage("DID validation failed"); } + + [Fact] + public async Task SaveHolderDid_WhenDidIsInvalid_ShouldThrowServiceException() + { + // Arrange + var companyId = Guid.NewGuid(); + var did = "did:web:example.com"; + var exception = new ServiceException("DID validation failed", HttpStatusCode.BadRequest); + + A.CallTo(() => _bringYourOwnWalletBusinessLogicFake.SaveCustomerWalletAsync(companyId, did)) + .Throws(exception); + + // Act + Func act = async () => await _controller.SaveHolderDid(companyId, did); + + // Assert + await act.Should().ThrowAsync() + .WithMessage("DID validation failed"); + } + + [Fact] + public async Task SaveHolderDid_ShouldBeValid() + { + // Arrange + var companyId = Guid.NewGuid(); + var did = "did:web:example.com"; + A.CallTo(() => _bringYourOwnWalletBusinessLogicFake.ValidateDid(did, A._)) + .Returns(Task.FromResult(System.Text.Json.JsonDocument.Parse("{}"))); + + // Act + await _controller.SaveHolderDid(companyId, did); + + // Assert + A.CallTo(() => _bringYourOwnWalletBusinessLogicFake.SaveCustomerWalletAsync(companyId, did)).MustHaveHappenedOnceExactly(); + } + [Fact] + public async Task GetHolderDid_ShouldReturnDid_WhenDidExists() + { + // Arrange + var companyId = Guid.NewGuid(); + var expectedDid = "did:web:example.com"; + A.CallTo(() => _bringYourOwnWalletBusinessLogicFake.getCompanyWalletDidAsync(companyId)) + .Returns(Task.FromResult(expectedDid)); + + // Act + var result = await _controller.GetHolderDid(companyId); + + // Assert + result.Should().Be(expectedDid); + A.CallTo(() => _bringYourOwnWalletBusinessLogicFake.getCompanyWalletDidAsync(companyId)).MustHaveHappenedOnceExactly(); + } + + [Fact] + public async Task GetHolderDid_ShouldReturnNull_WhenDidDoesNotExist() + { + // Arrange + var companyId = Guid.NewGuid(); + A.CallTo(() => _bringYourOwnWalletBusinessLogicFake.getCompanyWalletDidAsync(companyId)) + .Throws(new NotFoundException("Company wallet DID not found for the given company ID.")); + + // Act + Func act = async () => await _controller.GetHolderDid(companyId); + + // Assert + await act.Should().ThrowAsync() + .WithMessage("Company wallet DID not found for the given company ID."); + } } From 547173109e42cab11fbe5c633b431acd528d7117 Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Tue, 15 Jul 2025 09:42:30 +0200 Subject: [PATCH 08/38] removed uncessary parameter on the endpoint byow --- docs/api/registration-service.yaml | 7 +------ .../Controllers/BringYourOwnWalletController.cs | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/docs/api/registration-service.yaml b/docs/api/registration-service.yaml index 1d2b8564a5..2300f0a434 100644 --- a/docs/api/registration-service.yaml +++ b/docs/api/registration-service.yaml @@ -113,7 +113,7 @@ paths: description: Internal Server Error '401': description: The User is unauthorized - '/api/registration/bringYourOwnWallet/{did}/getHolderDid': + '/api/registration/bringYourOwnWallet/{companyId}/getHolderDid': get: tags: - BringYourOwnWallet @@ -125,11 +125,6 @@ paths: schema: type: string format: uuid - - name: did - in: path - required: true - schema: - type: string responses: '204': description: No Content diff --git a/src/registration/Registration.Service/Controllers/BringYourOwnWalletController.cs b/src/registration/Registration.Service/Controllers/BringYourOwnWalletController.cs index ffcefed86b..402006847f 100644 --- a/src/registration/Registration.Service/Controllers/BringYourOwnWalletController.cs +++ b/src/registration/Registration.Service/Controllers/BringYourOwnWalletController.cs @@ -57,7 +57,7 @@ public async Task SaveHolderDid([FromRoute] Guid companyId, [Fr [HttpGet] [Authorize(Roles = "submit_registration")] - [Route("{did}/getHolderDid")] + [Route("{companyId}/getHolderDid")] [ProducesResponseType(typeof(NoContentResult), StatusCodes.Status204NoContent)] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status415UnsupportedMediaType)] [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status404NotFound)] From 181c9662ddbae0653e2caf941b259ee4ec4188dd Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Tue, 15 Jul 2025 09:51:12 +0200 Subject: [PATCH 09/38] Added necessary parameter for resolver did --- src/registration/Registration.Service/appsettings.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/registration/Registration.Service/appsettings.json b/src/registration/Registration.Service/appsettings.json index 0c89fb26e4..f8d4c57526 100644 --- a/src/registration/Registration.Service/appsettings.json +++ b/src/registration/Registration.Service/appsettings.json @@ -36,6 +36,9 @@ "SubmitDocumentTypeIds": [], "PasswordResendAddress": "" }, + "UniversalDidResolver": { + "UniversalResolverAddress": "" + }, "BpnAccess": { "BaseAddress": "" }, From 72db6bf530ba0b610bf3c3c0ca79b74081d0419e Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Wed, 16 Jul 2025 22:09:43 +0200 Subject: [PATCH 10/38] Extracting correct did http address and enhanced tests --- .../Repositories/CompanyRepository.cs | 18 ++- .../Repositories/ICompanyRepository.cs | 2 +- .../BringYourOwnWalletBusinessLogic.cs | 48 +++++++- .../CompanyRepositoryTests.cs | 16 ++- .../BringYourOwnWalletBuisinessLogicTests.cs | 108 ++++++++++++++++++ 5 files changed, 183 insertions(+), 9 deletions(-) diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs index d4d1d32e1a..a108e80140 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs @@ -67,10 +67,15 @@ Address ICompanyRepository.CreateAddress(string city, string streetname, string setOptionalParameters?.Invoke(address); return context.Addresses.Add(address).Entity; } - public void CreateCustomerWallet(Guid companyId, string did, JsonDocument didDocument) + public async Task CreateCustomerWallet(Guid companyId, string did, JsonDocument didDocument) { + var walletId = await context.CompanyWalletDatas + .Where(wallet => wallet.CompanyId == companyId) + .Select(wallet => wallet.Id) + .SingleOrDefaultAsync(); + var wallet = new CompanyWalletData( - Guid.NewGuid(), + walletId == Guid.Empty ? Guid.NewGuid() : walletId, companyId, did, didDocument, @@ -80,7 +85,14 @@ public void CreateCustomerWallet(Guid companyId, string did, JsonDocument didDoc default, BringYourOwnWalletClientFields.NotUsed); - context.CompanyWalletDatas.Add(wallet); + if (walletId != Guid.Empty) + { + context.CompanyWalletDatas.Entry(wallet).State = EntityState.Modified; + } + else + { + context.CompanyWalletDatas.Add(wallet); + } } public Task IsBringYourOwnWallet(Guid applicationId) diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs index 688abd6959..cf4f0502b3 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs @@ -41,7 +41,7 @@ public interface ICompanyRepository void AttachAndModifyCompany(Guid companyId, Action? initialize, Action modify); - void CreateCustomerWallet(Guid companyId, string did, JsonDocument didDocument); + Task CreateCustomerWallet(Guid companyId, string did, JsonDocument didDocument); Task IsBringYourOwnWallet(Guid applicationId); diff --git a/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs b/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs index 6d1ceec188..3114e317a8 100644 --- a/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs +++ b/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs @@ -60,8 +60,10 @@ public async Task SaveCustomerWalletAsync(Guid companyId, string did) var companyRepository = _portalRepositories.GetInstance(); var didDocument = await ValidateDid(did, CancellationToken.None).ConfigureAwait(ConfigureAwaitOptions.None); - UpdateDidLocation(companyId, did, companyRepository); - companyRepository.CreateCustomerWallet(companyId, did, didDocument); + var didLocation = CreateDidLocation(didDocument); + + UpdateCompanyDidLocation(companyId, didLocation, companyRepository); + await companyRepository.CreateCustomerWallet(companyId, did, didDocument).ConfigureAwait(ConfigureAwaitOptions.None); await _portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); } @@ -76,11 +78,49 @@ public async Task getCompanyWalletDidAsync(Guid companyId) } return did; } - private static void UpdateDidLocation(Guid companyId, string didLocation, ICompanyRepository companyRepository) => + private static void UpdateCompanyDidLocation(Guid companyId, string didLocation, ICompanyRepository companyRepository) => companyRepository.AttachAndModifyCompany( companyId, _ => { }, - c => { c.DidDocumentLocation = didLocation; } + c => + { + c.DidDocumentLocation = didLocation; + } ); + + private static string CreateDidLocation(JsonDocument didDocument) + { + if (!didDocument.RootElement.TryGetProperty("id", out var idProperty)) + { + throw new UnsupportedMediaTypeException("DID validation failed: missing 'id' property."); + } + + var did = idProperty.GetString(); + if (string.IsNullOrWhiteSpace(did) || !did.StartsWith("did:", StringComparison.OrdinalIgnoreCase)) + { + throw new UnsupportedMediaTypeException("Invalid DID format: must start with 'did:'."); + } + + var didParts = did.Split(':', 3); + if (didParts.Length != 3) + { + throw new UnsupportedMediaTypeException("Invalid DID format: must be in the form 'did::'."); + } + + var method = didParts[1]; + var identifier = didParts[2]; + + if (!method.Equals("web", StringComparison.OrdinalIgnoreCase)) + { + throw new UnsupportedMediaTypeException($"Unsupported DID method: '{method}'. Only 'did:web' is supported."); + } + + var hostAndPath = identifier.Replace(":", "/"); + + var isBareDomain = !hostAndPath.Contains("/"); + var urlPath = isBareDomain ? "/.well-known/did.json" : "/did.json"; + + return $"https://{hostAndPath}{urlPath}"; + } } } diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRepositoryTests.cs b/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRepositoryTests.cs index 6382f9f560..ade9186fc0 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRepositoryTests.cs +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRepositoryTests.cs @@ -1143,12 +1143,26 @@ public async Task CreateCompanyWallet_ReturnsExpectedResult() var didDocument = JsonDocument.Parse("{}"); // Provide a valid JSON string or object as needed // Act - sut.CreateCustomerWallet(Guid.NewGuid(), "did:web:example.com", didDocument); + await sut.CreateCustomerWallet(Guid.NewGuid(), "did:web:example.com", didDocument); // Assert context.CompanyWalletDatas.Should().NotBeEmpty(); context.CompanyWalletDatas.Should().HaveCount(2); } + [Fact] + public async Task CreateCompanyWallet_ExistingCustomerWallet_ReturnsExpectedResult() + { + // Arrange + var (sut, context) = await CreateSut(); + var didDocument = JsonDocument.Parse("{}"); // Provide a valid JSON string or object as needed + + // Act + await sut.CreateCustomerWallet(Guid.Parse("554f23d5-669e-48ed-b06b-9a84dfa017c5"), "did:web:example.com", didDocument); + + // Assert + context.CompanyWalletDatas.Should().NotBeEmpty(); + context.CompanyWalletDatas.Where(wallet => wallet.ClientId.Equals(BringYourOwnWalletClientFields.Identification)).Should().HaveCount(1); + } #endregion } diff --git a/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs b/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs index c9e97324e2..19177a15fb 100644 --- a/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs +++ b/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs @@ -227,4 +227,112 @@ public async Task GetCompanyWalletDidAsync_ReturnsNull_WhenWalletDoesNotExist() await act.Should().ThrowAsync() .WithMessage("Company wallet DID not found for the given company ID."); } + + [Fact] + public async Task SaveCustomerWallet_InvalidDocumentId_ThrowException() + { + // Arrange + const string did = "did:web:123"; + var companyId = Guid.NewGuid(); + var didDocument = JsonDocument.Parse("{}"); + var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); + A.CallTo(() => _portalRepositories.GetInstance().CreateCustomerWallet(companyId, did, didDocument)) + .DoesNothing(); + A.CallTo(() => _portalRepositories.GetInstance()) + .Returns(A.Fake()); + A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) + .ReturnsLazily(() => Task.FromResult(validationResult)); + A.CallTo(() => _universalDidResolverService.ValidateSchema(didDocument, A._)) + .ReturnsLazily(() => true); + + // Act + + Func act = async () => + await _sut.SaveCustomerWalletAsync(companyId, did); + + // Assert + await act.Should().ThrowAsync() + .WithMessage("DID validation failed: missing 'id' property."); + } + + [Fact] + public async Task SaveCustomerWallet_InvalidDidFormat_ThrowException() + { + // Arrange + const string did = "did:web:123"; + var companyId = Guid.NewGuid(); + var didDocument = JsonDocument.Parse("{\"id\":\":web:123\"}"); + var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); + A.CallTo(() => _portalRepositories.GetInstance().CreateCustomerWallet(companyId, did, didDocument)) + .DoesNothing(); + A.CallTo(() => _portalRepositories.GetInstance()) + .Returns(A.Fake()); + A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) + .ReturnsLazily(() => Task.FromResult(validationResult)); + A.CallTo(() => _universalDidResolverService.ValidateSchema(didDocument, A._)) + .ReturnsLazily(() => true); + + // Act + + Func act = async () => + await _sut.SaveCustomerWalletAsync(companyId, did); + + // Assert + await act.Should().ThrowAsync() + .WithMessage("Invalid DID format: must start with 'did:'."); + } + + [Fact] + public async Task SaveCustomerWallet_InvalidDidForm_ThrowException() + { + // Arrange + const string did = "did:web:123"; + var companyId = Guid.NewGuid(); + var didDocument = JsonDocument.Parse("{\"id\":\"did:web\"}"); + var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); + A.CallTo(() => _portalRepositories.GetInstance().CreateCustomerWallet(companyId, did, didDocument)) + .DoesNothing(); + A.CallTo(() => _portalRepositories.GetInstance()) + .Returns(A.Fake()); + A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) + .ReturnsLazily(() => Task.FromResult(validationResult)); + A.CallTo(() => _universalDidResolverService.ValidateSchema(didDocument, A._)) + .ReturnsLazily(() => true); + + // Act + + Func act = async () => + await _sut.SaveCustomerWalletAsync(companyId, did); + + // Assert + await act.Should().ThrowAsync() + .WithMessage("Invalid DID format: must be in the form 'did::'."); + } + + [Fact] + public async Task SaveCustomerWallet_InvalidDidMethod_ThrowException() + { + // Arrange + const string did = "did:web:123"; + var companyId = Guid.NewGuid(); + var didDocument = JsonDocument.Parse("{\"id\":\"did:error:123\"}"); + var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); + A.CallTo(() => _portalRepositories.GetInstance().CreateCustomerWallet(companyId, did, didDocument)) + .DoesNothing(); + A.CallTo(() => _portalRepositories.GetInstance()) + .Returns(A.Fake()); + A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) + .ReturnsLazily(() => Task.FromResult(validationResult)); + A.CallTo(() => _universalDidResolverService.ValidateSchema(didDocument, A._)) + .ReturnsLazily(() => true); + + // Act + + Func act = async () => + await _sut.SaveCustomerWalletAsync(companyId, did); + + // Assert + await act.Should().ThrowAsync() + .WithMessage("Unsupported DID method: 'error'. Only 'did:web' is supported."); + } } From da15c32a877dc4efbb269e14b549a6b557042a9c Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Thu, 17 Jul 2025 17:33:40 +0200 Subject: [PATCH 11/38] fix: added check for missing configuration --- .../UniversalDidResolverServiceCollectionExtension.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs b/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs index 97e3698f71..ba68509d86 100644 --- a/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs +++ b/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs @@ -29,7 +29,10 @@ public static class UniversalDidResolverServiceCollectionExtension public static IServiceCollection AddUniversalDidResolverService(this IServiceCollection services, IConfigurationSection section) { services.AddOptions() - .Bind(section); + .Bind(section) + .Validate(settings => + !string.IsNullOrWhiteSpace(settings.UniversalResolverAddress), + "UniversalResolverAddress is required."); services.AddTransient>(); From cf36b5b47df75077c213fabf9e81b206617eb6e7 Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Fri, 18 Jul 2025 13:09:45 +0200 Subject: [PATCH 12/38] fix: added implementation to avoid duplicate dids --- .../Repositories/CompanyRepository.cs | 6 +++++ .../Repositories/ICompanyRepository.cs | 2 ++ .../BringYourOwnWalletBusinessLogic.cs | 8 +++++++ .../BringYourOwnWalletBuisinessLogicTests.cs | 24 +++++++++++++++++++ 4 files changed, 40 insertions(+) diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs index a108e80140..7835c04f48 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs @@ -106,6 +106,12 @@ public Task IsBringYourOwnWallet(Guid applicationId) .AnyAsync(wallet => wallet.ClientId == BringYourOwnWalletClientFields.Identification); } + public Task IsDidInUse(string did) => + context.CompanyWalletDatas + .AsNoTracking() + .Where(wallet => wallet.Did == did) + .AnyAsync(); + public Task GetCompanyHolderDidAsync(Guid companyId) => context.Companies .AsNoTracking() diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs index cf4f0502b3..ee9918ff84 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs @@ -45,6 +45,8 @@ public interface ICompanyRepository Task IsBringYourOwnWallet(Guid applicationId); + Task IsDidInUse(string did); + Task GetCompanyHolderDidAsync(Guid companyId); Address CreateAddress(string city, string streetname, string region, string countryAlpha2Code, Action
? setOptionalParameters = null); diff --git a/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs b/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs index 3114e317a8..684ea7703e 100644 --- a/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs +++ b/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs @@ -48,6 +48,14 @@ public async Task ValidateDid(string did, CancellationToken cancel { throw new UnsupportedMediaTypeException("DID validation failed. DID Document is not valid."); } + + var companyRepository = _portalRepositories.GetInstance(); + var didExists = await companyRepository.IsDidInUse(did).ConfigureAwait(ConfigureAwaitOptions.None); + if (didExists) + { + throw new ConflictException("DID is already in use."); + } + return validationResult.DidDocument; } diff --git a/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs b/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs index 19177a15fb..081d33c54b 100644 --- a/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs +++ b/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs @@ -78,6 +78,30 @@ await act.Should().ThrowAsync() .WithMessage("DID validation failed. DID Document is not valid."); } + [Fact] + public async Task ValidateDid_ThrowsConflictException_WhenDidExists() + { + // Arrange + const string did = "did:web:123"; + var didDocument = JsonDocument.Parse("{\"id\":\"did:web:123\"}"); + var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); + var companyRepository = _portalRepositories.GetInstance(); + + A.CallTo(() => _portalRepositories.GetInstance()).Returns(companyRepository); + A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) + .ReturnsLazily(() => Task.FromResult(validationResult)); + A.CallTo(() => _universalDidResolverService.ValidateSchema(didDocument, A._)) + .ReturnsLazily(() => true); + A.CallTo(() => companyRepository.IsDidInUse(did)).ReturnsLazily(() => true); + + // Act + var act = () => _sut.ValidateDid(did, CancellationToken.None); + + // Assert + await act.Should().ThrowAsync() + .WithMessage("DID is already in use."); + } + [Fact] public async Task ValidateDid_ThrowsServiceException_WhenSchemaIsInvalid() { From 4ebf168c17208267fd77c62374e2a4ad94a21adb Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Sun, 20 Jul 2025 18:04:12 +0200 Subject: [PATCH 13/38] Feat: Added restrictions user roles for BYOW and Tests --- .../BringYourOwnWalletBusinessLogic.cs | 54 ++++++++ .../BringYourOwnWalletSettings.cs | 27 ++++ .../IBringYourOwnWalletBusinessLogic.cs | 31 +++++ .../TechnicalUserBusinessLogic.cs | 51 +++++-- ...tionServiceAccountErrorMessageContainer.cs | 4 +- .../Administration.Service/Program.cs | 5 + .../Administration.Service/appsettings.json | 5 + .../Repositories/CompanyRepository.cs | 7 + .../Repositories/ICompanyRepository.cs | 2 + .../BringYourOwnWalletBusinessLogic.cs | 16 ++- .../BringYourOwnWalletBusinessLogicTests.cs | 96 +++++++++++++ .../TechnicalUserBusinessLogicTests.cs | 129 +++++++++++++----- .../BringYourOwnWalletBuisinessLogicTests.cs | 75 ++++++++-- 13 files changed, 445 insertions(+), 57 deletions(-) create mode 100644 src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs create mode 100644 src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletSettings.cs create mode 100644 src/administration/Administration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs create mode 100644 tests/administration/Administration.Service.Tests/BusinessLogic/BringYourOwnWalletBusinessLogicTests.cs diff --git a/src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs new file mode 100644 index 0000000000..f73753316a --- /dev/null +++ b/src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs @@ -0,0 +1,54 @@ +/******************************************************************************** + * Copyright (c) 2025 Cofinity-X GmbH + * Copyright (c) 2025 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 Microsoft.Extensions.Options; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; + +namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLogic; + +public class BringYourOwnWalletBusinessLogic( + IPortalRepositories portalRepositories, + IOptions options) : IBringYourOwnWalletBusinessLogic +{ + private readonly IEnumerable _excludedRoles = options.Value.NonApplicableUserRoles; + + public async Task IsUserRoleAuthorizedForBYOW(Guid companyId, IEnumerable userRoleId) + { + var companyRepository = portalRepositories.GetInstance(); + if (!await IsBringYourOwnWallet(companyId)) + { + return true; + } + return !userRoleId.Any(id => _excludedRoles.Contains(id)); + } + + public IEnumerable GetExcludedUserRoles() + { + return _excludedRoles; + } + + public async Task IsBringYourOwnWallet(Guid companyId) + { + var companyRepository = portalRepositories.GetInstance(); + var applicationId = await companyRepository.GetApplicationIdByCompanyId(companyId); + return await companyRepository.IsBringYourOwnWallet(applicationId); + } +} diff --git a/src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletSettings.cs b/src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletSettings.cs new file mode 100644 index 0000000000..791081d8fd --- /dev/null +++ b/src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletSettings.cs @@ -0,0 +1,27 @@ + +/******************************************************************************** + * Copyright (c) 2025 Cofinity-X GmbH + * Copyright (c) 2025 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 + ********************************************************************************/ + +namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLogic; + +public class BringYourOwnWalletSettings +{ + public IEnumerable NonApplicableUserRoles { get; set; } = Enumerable.Empty(); +} diff --git a/src/administration/Administration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs new file mode 100644 index 0000000000..32e3148f05 --- /dev/null +++ b/src/administration/Administration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs @@ -0,0 +1,31 @@ +/******************************************************************************** + * Copyright (c) 2025 Cofinity-X GmbH + * Copyright (c) 2025 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 + ********************************************************************************/ + +namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLogic; + +public interface IBringYourOwnWalletBusinessLogic +{ + Task IsUserRoleAuthorizedForBYOW(Guid companyId, IEnumerable userRoleId); + + IEnumerable GetExcludedUserRoles(); + + Task IsBringYourOwnWallet(Guid companyId); + +} diff --git a/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs index a7568d21a8..f2c2974dbc 100644 --- a/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs @@ -46,7 +46,8 @@ public class TechnicalUserBusinessLogic( IOptions options, ITechnicalUserCreation technicalUserCreation, IIdentityService identityService, - IServiceAccountManagement serviceAccountManagement) + IServiceAccountManagement serviceAccountManagement, + IBringYourOwnWalletBusinessLogic bringYourOwnWalletBusinessLogic) : ITechnicalUserBusinessLogic { private readonly IIdentityData _identityData = identityService.IdentityData; @@ -78,6 +79,11 @@ public async Task> CreateOwnCompanyServiceAcc throw ConflictException.Create(AdministrationServiceAccountErrors.SERVICE_BPN_NOT_SET_CONFLICT, [new(CompanyId, companyId.ToString())]); } + if (!await bringYourOwnWalletBusinessLogic.IsUserRoleAuthorizedForBYOW(companyId, technicalUserCreationInfos.UserRoleIds)) + { + throw ControllerArgumentException.Create(AdministrationServiceAccountErrors.SERVICE_ACCOUNT_USER_ROLES_NOT_ALLOWED, parameters: [new("userRoleIds", string.Join(",", bringYourOwnWalletBusinessLogic.GetExcludedUserRoles()))]); + } + technicalUserCreationInfos.UserRoleIds.Except(result.TechnicalUserRoleIds) .IfAny(unassignable => throw ControllerArgumentException.Create(AdministrationServiceAccountErrors.SERVICE_ROLES_NOT_ASSIGN_ARGUMENT, parameters: [new("unassignable", string.Join(",", unassignable)), new("userRoleIds", string.Join(",", result.TechnicalUserRoleIds))])); @@ -335,19 +341,38 @@ public IAsyncEnumerable GetServiceAccountRolesAsync(str { var externalRoleNames = _settings.DimUserRoles.Where(role => role.ClientId == _settings.ClientId).SelectMany(role => role.UserRoleNames).ToImmutableHashSet(); var providerOnlyRoleNames = _settings.UserRolesAccessibleByProviderOnly.Where(role => role.ClientId == _settings.ClientId).SelectMany(role => role.UserRoleNames).ToImmutableHashSet(); + var companyId = _identityData.CompanyId; + + var userRoles = portalRepositories.GetInstance() + .GetServiceAccountRolesAsync( + companyId, + _settings.ClientId, + languageShortName ?? Constants.DefaultLanguage) + .Select(x => new UserRoleWithDescription( + x.UserRoleId, + x.UserRoleText, + x.RoleDescription, + externalRoleNames.Contains(x.UserRoleText) ? UserRoleType.External : UserRoleType.Internal, + providerOnlyRoleNames.Contains(x.UserRoleText) + )); + + var isBringYourOwnWallet = bringYourOwnWalletBusinessLogic.IsBringYourOwnWallet(companyId).GetAwaiter().GetResult(); + if (isBringYourOwnWallet) + { + var excludedRoles = bringYourOwnWalletBusinessLogic.GetExcludedUserRoles(); + if (excludedRoles.Any()) + { + return userRoles.Where(role => !excludedRoles.Contains(role.UserRoleId)) + .Select(role => new UserRoleWithDescription( + role.UserRoleId, + role.UserRoleText, + role.RoleDescription, + role.RoleType, + role.ProviderOnly)); + } + } - return portalRepositories.GetInstance() - .GetServiceAccountRolesAsync( - _identityData.CompanyId, - _settings.ClientId, - languageShortName ?? Constants.DefaultLanguage) - .Select(x => new UserRoleWithDescription( - x.UserRoleId, - x.UserRoleText, - x.RoleDescription, - externalRoleNames.Contains(x.UserRoleText) ? UserRoleType.External : UserRoleType.Internal, - providerOnlyRoleNames.Contains(x.UserRoleText) - )); + return userRoles; } public async Task HandleServiceAccountCreationCallback(Guid processId, AuthenticationDetail callbackData) diff --git a/src/administration/Administration.Service/ErrorHandling/AdministrationServiceAccountErrorMessageContainer.cs b/src/administration/Administration.Service/ErrorHandling/AdministrationServiceAccountErrorMessageContainer.cs index dbd6887cda..e5bb4ab50b 100644 --- a/src/administration/Administration.Service/ErrorHandling/AdministrationServiceAccountErrorMessageContainer.cs +++ b/src/administration/Administration.Service/ErrorHandling/AdministrationServiceAccountErrorMessageContainer.cs @@ -41,6 +41,7 @@ public class AdministrationServiceAccountErrorMessageContainer : IErrorMessageCo new((int)AdministrationServiceAccountErrors.SERVICE_ACCOUNT_PENDING_PROCESS_STEPS, "Service Account {serviceAccountId} has pending process steps {processStepTypeIds}"), new((int)AdministrationServiceAccountErrors.SERVICE_ACCOUNT_NOT_ACTIVE, "Service Account {serviceAccountId} is not status active"), new((int)AdministrationServiceAccountErrors.SERVICE_ACCOUNT_NO_PROVIDER_OR_OWNER, "Only provider or owner of the service account are allowed to delete it"), + new((int)AdministrationServiceAccountErrors.SERVICE_ACCOUNT_USER_ROLES_NOT_ALLOWED, "The roles {userRoleIds} are not allowed, clientId is BringYourOwnWallet"), new((int)AdministrationServiceAccountErrors.TECHNICAL_USER_CREATION_IN_PROGRESS, "Technical user can't be deleted because the creation progress is still running") ]); @@ -66,5 +67,6 @@ public enum AdministrationServiceAccountErrors SERVICE_ACCOUNT_PENDING_PROCESS_STEPS, TECHNICAL_USER_CREATION_IN_PROGRESS, SERVICE_ACCOUNT_NOT_ACTIVE, - SERVICE_ACCOUNT_NO_PROVIDER_OR_OWNER + SERVICE_ACCOUNT_NO_PROVIDER_OR_OWNER, + SERVICE_ACCOUNT_USER_ROLES_NOT_ALLOWED, } diff --git a/src/administration/Administration.Service/Program.cs b/src/administration/Administration.Service/Program.cs index 2e116630f3..c69a919265 100644 --- a/src/administration/Administration.Service/Program.cs +++ b/src/administration/Administration.Service/Program.cs @@ -64,6 +64,11 @@ await WebAppHelper builder.Services.AddTransient() .ConfigureRegistrationSettings(builder.Configuration.GetSection("Registration")); + builder.Services + .AddTransient() + .Configure( + builder.Configuration.GetSection("BringYourOwnWallet")); + builder.Services.AddTransient() .ConfigureServiceAccountSettings(builder.Configuration.GetSection("ServiceAccount")); diff --git a/src/administration/Administration.Service/appsettings.json b/src/administration/Administration.Service/appsettings.json index fa2bf198b1..15e07155dd 100644 --- a/src/administration/Administration.Service/appsettings.json +++ b/src/administration/Administration.Service/appsettings.json @@ -64,6 +64,11 @@ "ClockSkew": "00:05:00" } }, + "BringYourOwnWallet": { + "NonApplicableUserRoles": [ + "607818be-4978-41f4-bf63-fa8d2de51158" + ] + }, "Provisioning": { "CentralRealm": "", "CentralRealmId": "", diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs index 7835c04f48..fe5b085dec 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyRepository.cs @@ -106,6 +106,13 @@ public Task IsBringYourOwnWallet(Guid applicationId) .AnyAsync(wallet => wallet.ClientId == BringYourOwnWalletClientFields.Identification); } + public Task GetApplicationIdByCompanyId(Guid companyId) => + context.CompanyApplications + .AsNoTracking() + .Where(app => app.CompanyId == companyId) + .Select(app => app.Id) + .SingleOrDefaultAsync(); + public Task IsDidInUse(string did) => context.CompanyWalletDatas .AsNoTracking() diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs index ee9918ff84..ed95eaf1d0 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyRepository.cs @@ -45,6 +45,8 @@ public interface ICompanyRepository Task IsBringYourOwnWallet(Guid applicationId); + Task GetApplicationIdByCompanyId(Guid companyId); + Task IsDidInUse(string did); Task GetCompanyHolderDidAsync(Guid companyId); diff --git a/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs b/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs index 684ea7703e..9605bdc3e9 100644 --- a/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs +++ b/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs @@ -61,12 +61,18 @@ public async Task ValidateDid(string did, CancellationToken cancel public async Task SaveCustomerWalletAsync(Guid companyId, string did) { + var companyRepository = _portalRepositories.GetInstance(); + var companyExists = await companyRepository.IsExistingCompany(companyId).ConfigureAwait(ConfigureAwaitOptions.None); + if (!companyExists) + { + throw new NotFoundException("Company ID not found or not valid."); + } + if (string.IsNullOrEmpty(did)) { throw new ForbiddenException("Invalid DID. DID cannot be empty or NULL."); } - var companyRepository = _portalRepositories.GetInstance(); var didDocument = await ValidateDid(did, CancellationToken.None).ConfigureAwait(ConfigureAwaitOptions.None); var didLocation = CreateDidLocation(didDocument); @@ -79,10 +85,16 @@ public async Task SaveCustomerWalletAsync(Guid companyId, string did) public async Task getCompanyWalletDidAsync(Guid companyId) { var companyRepository = _portalRepositories.GetInstance(); + var companyExists = await companyRepository.IsExistingCompany(companyId).ConfigureAwait(ConfigureAwaitOptions.None); + if (!companyExists) + { + throw new NotFoundException("Company ID not found or not valid."); + } + var did = await companyRepository.GetCompanyHolderDidAsync(companyId).ConfigureAwait(ConfigureAwaitOptions.None); if (string.IsNullOrEmpty(did)) { - throw new NotFoundException("Company wallet DID not found for the given company ID."); + throw new NotFoundException("Company wallet DID not found."); } return did; } diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/BringYourOwnWalletBusinessLogicTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/BringYourOwnWalletBusinessLogicTests.cs new file mode 100644 index 0000000000..98f4bdab20 --- /dev/null +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/BringYourOwnWalletBusinessLogicTests.cs @@ -0,0 +1,96 @@ +using Microsoft.Extensions.Options; +using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLogic; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; + +namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Tests.BusinessLogic; + +public class BringYourOwnWalletBusinessLogicTests +{ + private readonly IPortalRepositories _portalRepositories; + private readonly ICompanyRepository _companyRepository; + private readonly IOptions _options; + private readonly BringYourOwnWalletBusinessLogic _sut; + private readonly Guid _companyId = Guid.NewGuid(); + private readonly Guid _applicationId = Guid.NewGuid(); + private readonly Guid _excludedRoleId = Guid.NewGuid(); + + public BringYourOwnWalletBusinessLogicTests() + { + _portalRepositories = A.Fake(); + _companyRepository = A.Fake(); + _options = Options.Create(new BringYourOwnWalletSettings + { + NonApplicableUserRoles = new List { _excludedRoleId } + }); + A.CallTo(() => _portalRepositories.GetInstance()).Returns(_companyRepository); + _sut = new BringYourOwnWalletBusinessLogic(_portalRepositories, _options); + } + + [Fact] + public async Task IsUserRoleAuthorizedForBYOW_ReturnsTrue_WhenNotBYOW() + { + // Arrange + A.CallTo(() => _companyRepository.GetApplicationIdByCompanyId(_companyId)).Returns(_applicationId); + A.CallTo(() => _companyRepository.IsBringYourOwnWallet(_applicationId)).Returns(false); + + // Act + var result = await _sut.IsUserRoleAuthorizedForBYOW(_companyId, new[] { _excludedRoleId }); + + // Assert + result.Should().BeTrue(); + } + + [Fact] + public async Task IsUserRoleAuthorizedForBYOW_ReturnsFalse_WhenBYOWAndRoleIsExcluded() + { + // Arrange + A.CallTo(() => _companyRepository.GetApplicationIdByCompanyId(_companyId)).Returns(_applicationId); + A.CallTo(() => _companyRepository.IsBringYourOwnWallet(_applicationId)).Returns(true); + + // Act + var result = await _sut.IsUserRoleAuthorizedForBYOW(_companyId, new[] { _excludedRoleId }); + + // Assert + result.Should().BeFalse(); + } + + [Fact] + public async Task IsUserRoleAuthorizedForBYOW_ReturnsTrue_WhenBYOWAndRoleIsNotExcluded() + { + // Arrange + var otherRoleId = Guid.NewGuid(); + A.CallTo(() => _companyRepository.GetApplicationIdByCompanyId(_companyId)).Returns(_applicationId); + A.CallTo(() => _companyRepository.IsBringYourOwnWallet(_applicationId)).Returns(true); + + // Act + var result = await _sut.IsUserRoleAuthorizedForBYOW(_companyId, new[] { otherRoleId }); + + // Assert + result.Should().BeTrue(); + } + + [Fact] + public void GetExcludedUserRoles_ReturnsConfiguredRoles() + { + // Act + var result = _sut.GetExcludedUserRoles(); + + // Assert + result.Should().ContainSingle().Which.Should().Be(_excludedRoleId); + } + + [Fact] + public async Task IsBringYourOwnWallet_ReturnsExpectedValue() + { + // Arrange + A.CallTo(() => _companyRepository.GetApplicationIdByCompanyId(_companyId)).Returns(_applicationId); + A.CallTo(() => _companyRepository.IsBringYourOwnWallet(_applicationId)).Returns(true); + + // Act + var result = await _sut.IsBringYourOwnWallet(_companyId); + + // Assert + result.Should().BeTrue(); + } +} diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/TechnicalUserBusinessLogicTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/TechnicalUserBusinessLogicTests.cs index 973b9d5386..66312906d9 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/TechnicalUserBusinessLogicTests.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/TechnicalUserBusinessLogicTests.cs @@ -73,6 +73,7 @@ public class TechnicalUserBusinessLogicTests private readonly IFixture _fixture; private readonly IOptions _options; private readonly IIdentityService _identityService; + private readonly IBringYourOwnWalletBusinessLogic _bringYourOwnWalletBusinessLogic; public TechnicalUserBusinessLogicTests() { @@ -92,6 +93,8 @@ public TechnicalUserBusinessLogicTests() _processStepRepository = A.Fake(); _provisioningManager = A.Fake(); _portalRepositories = A.Fake(); + _bringYourOwnWalletBusinessLogic = A.Fake(); + A.CallTo(() => _portalRepositories.GetInstance()).Returns(_processStepRepository); A.CallTo(() => _portalRepositories.GetInstance()).Returns(_userRepository); A.CallTo(() => _portalRepositories.GetInstance()).Returns(_technicalUserRepository); @@ -104,6 +107,8 @@ public TechnicalUserBusinessLogicTests() A.CallTo(() => _identity.IdentityTypeId).Returns(IdentityTypeId.COMPANY_USER); A.CallTo(() => _identity.CompanyId).Returns(ValidCompanyId); A.CallTo(() => _identityService.IdentityData).Returns(_identity); + A.CallTo(() => _bringYourOwnWalletBusinessLogic.IsUserRoleAuthorizedForBYOW(A._, A>._)) + .Returns(true); var encryptionKey = _fixture.CreateMany(32).ToArray(); @@ -124,7 +129,7 @@ public async Task CreateOwnCompanyServiceAccountAsync_WithValidInput_ReturnsCrea // Arrange SetupCreateOwnCompanyServiceAccount(); var serviceAccountCreationInfos = new TechnicalUserCreationInfo("TheName", "Just a short description", IamClientAuthMethod.SECRET, Enumerable.Repeat(UserRoleId1, 1)); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act var result = await sut.CreateOwnCompanyServiceAccountAsync(serviceAccountCreationInfos); @@ -134,6 +139,24 @@ public async Task CreateOwnCompanyServiceAccountAsync_WithValidInput_ReturnsCrea x => x.IamClientAuthMethod == IamClientAuthMethod.SECRET); } + [Fact] + public async Task CreateOwnCompanyServiceAccountAsync_InvalieUserRole_ReturnsControllerException() + { + // Arrange + SetupCreateOwnCompanyServiceAccount(); + var serviceAccountCreationInfos = new TechnicalUserCreationInfo("TheName", "Just a short description", IamClientAuthMethod.SECRET, Enumerable.Repeat(UserRoleId1, 1)); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); + A.CallTo(() => _bringYourOwnWalletBusinessLogic.IsUserRoleAuthorizedForBYOW(A._, A>._)) + .Returns(false); + + // Act + async Task Act() => await sut.CreateOwnCompanyServiceAccountAsync(serviceAccountCreationInfos); + + // Assert + var exception = await Assert.ThrowsAsync(Act); + exception.Message.Should().Be(AdministrationServiceAccountErrors.SERVICE_ACCOUNT_USER_ROLES_NOT_ALLOWED.ToString()); + } + [Fact] public async Task CreateOwnCompanyServiceAccountAsync_WithInvalidUser_NotFoundException() { @@ -142,7 +165,7 @@ public async Task CreateOwnCompanyServiceAccountAsync_WithInvalidUser_NotFoundEx A.CallTo(() => _identityService.IdentityData).Returns(identity); SetupCreateOwnCompanyServiceAccount(); var serviceAccountCreationInfos = new TechnicalUserCreationInfo("TheName", "Just a short description", IamClientAuthMethod.SECRET, Enumerable.Repeat(UserRoleId1, 1)); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act async Task Act() => await sut.CreateOwnCompanyServiceAccountAsync(serviceAccountCreationInfos); @@ -158,7 +181,7 @@ public async Task CreateOwnCompanyServiceAccountAsync_WithEmptyName_ThrowsContro // Arrange SetupCreateOwnCompanyServiceAccount(); var serviceAccountCreationInfos = new TechnicalUserCreationInfo(string.Empty, "Just a short description", IamClientAuthMethod.SECRET, Enumerable.Repeat(UserRoleId1, 1)); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act async Task Act() => await sut.CreateOwnCompanyServiceAccountAsync(serviceAccountCreationInfos); @@ -177,7 +200,7 @@ public async Task CreateOwnCompanyServiceAccountAsync_WithInvalidIamClientAuthMe // Arrange SetupCreateOwnCompanyServiceAccount(); var serviceAccountCreationInfos = new TechnicalUserCreationInfo("TheName", "Just a short description", IamClientAuthMethod.JWT, Enumerable.Repeat(UserRoleId1, 1)); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act async Task Act() => await sut.CreateOwnCompanyServiceAccountAsync(serviceAccountCreationInfos); @@ -197,7 +220,7 @@ public async Task CreateOwnCompanyServiceAccountAsync_WithInvalidUserRoleId_Thro var wrongUserRoleId = Guid.NewGuid(); SetupCreateOwnCompanyServiceAccount(); var serviceAccountCreationInfos = new TechnicalUserCreationInfo("TheName", "Just a short description", IamClientAuthMethod.SECRET, [UserRoleId1, wrongUserRoleId]); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act async Task Act() => await sut.CreateOwnCompanyServiceAccountAsync(serviceAccountCreationInfos); @@ -220,7 +243,7 @@ public async Task GetOwnCompanyServiceAccountDetailsAsync_WithValidInput_GetsAll { // Arrange SetupGetOwnCompanyServiceAccountDetails(); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act var result = await sut.GetOwnCompanyServiceAccountDetailsAsync(ValidServiceAccountId); @@ -240,7 +263,7 @@ public async Task GetOwnCompanyServiceAccountDetailsAsync_WithValidInputAndDimCo { // Arrange SetupGetOwnCompanyServiceAccountDetails(); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act var result = await sut.GetOwnCompanyServiceAccountDetailsAsync(ValidServiceAccountWithDimDataId); @@ -260,7 +283,7 @@ public async Task GetOwnCompanyServiceAccountDetailsAsync_WithValidInputAndDimCo { // Arrange SetupGetOwnCompanyServiceAccountDetails(); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act var result = await sut.GetOwnCompanyServiceAccountDetailsAsync(ValidServiceAccountWithDimDataIdWithPendingUserStatus); @@ -283,7 +306,7 @@ public async Task GetOwnCompanyServiceAccountDetailsAsync_WithInvalidCompany_Not SetupGetOwnCompanyServiceAccountDetails(); var invalidCompanyId = Guid.NewGuid(); A.CallTo(() => _identity.CompanyId).Returns(invalidCompanyId); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act async Task Act() => await sut.GetOwnCompanyServiceAccountDetailsAsync(ValidServiceAccountId); @@ -299,7 +322,7 @@ public async Task GetOwnCompanyServiceAccountDetailsAsync_WithInvalidServiceAcco // Arrange SetupGetOwnCompanyServiceAccountDetails(); var invalidServiceAccountId = Guid.NewGuid(); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act async Task Act() => await sut.GetOwnCompanyServiceAccountDetailsAsync(invalidServiceAccountId); @@ -318,7 +341,7 @@ public async Task ResetOwnCompanyServiceAccountSecretAsync_WithValidInput_GetsAl { // Arrange SetupResetOwnCompanyServiceAccountSecret(); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act var result = await sut.ResetOwnCompanyServiceAccountSecretAsync(ValidServiceAccountId); @@ -335,7 +358,7 @@ public async Task ResetOwnCompanyServiceAccountSecretAsync_WithInvalidUser_NotFo SetupResetOwnCompanyServiceAccountSecret(); var invalidUser = _fixture.Create(); A.CallTo(() => _identityService.IdentityData).Returns(invalidUser); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act async Task Act() => await sut.ResetOwnCompanyServiceAccountSecretAsync(ValidServiceAccountId); @@ -351,7 +374,7 @@ public async Task ResetOwnCompanyServiceAccountSecretAsync_WithInvalidServiceAcc // Arrange SetupResetOwnCompanyServiceAccountSecret(); var invalidServiceAccountId = Guid.NewGuid(); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act async Task Act() => await sut.ResetOwnCompanyServiceAccountSecretAsync(invalidServiceAccountId); @@ -371,7 +394,7 @@ public async Task UpdateOwnCompanyServiceAccountDetailsAsync_WithValidData_Retur // Arrange SetupUpdateOwnCompanyServiceAccountDetails(); var data = new ServiceAccountEditableDetails(ValidServiceAccountId, "new name", "changed description", IamClientAuthMethod.SECRET); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); TechnicalUser? initial = null; TechnicalUser? modified = null; @@ -406,7 +429,7 @@ public async Task UpdateOwnCompanyServiceAccountDetailsAsync_WithInvalidAuthMeth // Arrange SetupUpdateOwnCompanyServiceAccountDetails(); var data = new ServiceAccountEditableDetails(ValidServiceAccountId, "new name", "changed description", IamClientAuthMethod.JWT); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act async Task Act() => await sut.UpdateOwnCompanyServiceAccountDetailsAsync(ValidServiceAccountId, data); @@ -424,7 +447,7 @@ public async Task UpdateOwnCompanyServiceAccountDetailsAsync_WithDifferentServic // Arrange SetupUpdateOwnCompanyServiceAccountDetails(); var data = new ServiceAccountEditableDetails(ValidServiceAccountId, "new name", "changed description", IamClientAuthMethod.SECRET); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act async Task Act() => await sut.UpdateOwnCompanyServiceAccountDetailsAsync(Guid.NewGuid(), data); @@ -446,7 +469,7 @@ public async Task UpdateOwnCompanyServiceAccountDetailsAsync_WithNotExistingServ A.CallTo(() => _technicalUserRepository.GetTechnicalUserWithRoleDataClientIdAsync(invalidServiceAccountId, ValidCompanyId)) .Returns(null); var data = new ServiceAccountEditableDetails(invalidServiceAccountId, "new name", "changed description", IamClientAuthMethod.SECRET); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act async Task Act() => await sut.UpdateOwnCompanyServiceAccountDetailsAsync(invalidServiceAccountId, data); @@ -467,7 +490,7 @@ public async Task UpdateOwnCompanyServiceAccountDetailsAsync_WithInactiveService A.CallTo(() => _technicalUserRepository.GetTechnicalUserWithRoleDataClientIdAsync(InactiveServiceAccount, ValidCompanyId)) .Returns(inactive); var data = new ServiceAccountEditableDetails(InactiveServiceAccount, "new name", "changed description", IamClientAuthMethod.SECRET); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act async Task Act() => await sut.UpdateOwnCompanyServiceAccountDetailsAsync(InactiveServiceAccount, data); @@ -491,7 +514,7 @@ public async Task UpdateOwnCompanyServiceAccountDetailsAsync_WithExternalService A.CallTo(() => _technicalUserRepository.GetTechnicalUserWithRoleDataClientIdAsync(ExternalServiceAccount, ValidCompanyId)) .Returns(external); var data = new ServiceAccountEditableDetails(ExternalServiceAccount, "new name", "changed description", IamClientAuthMethod.SECRET); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act var result = await sut.UpdateOwnCompanyServiceAccountDetailsAsync(ExternalServiceAccount, data); @@ -536,7 +559,7 @@ public async Task GetOwnCompanyServiceAccountsDataAsync_GetsExpectedData(IEnumer .Returns((int skip, int take) => Task.FromResult?>(new(data.Count(), data.Skip(skip).Take(take)))); A.CallTo(() => _portalRepositories.GetInstance()).Returns(_technicalUserRepository); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act var result = await sut.GetOwnCompanyServiceAccountsDataAsync(1, 10, null, null, isUserInactive, userStatusIds); @@ -567,7 +590,7 @@ public async Task DeleteOwnCompanyServiceAccountAsync_WithNotExistingServiceAcco var serviceAccountId = Guid.NewGuid(); SetupDeleteOwnCompanyServiceAccount(); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act async Task Act() => await sut.DeleteOwnCompanyServiceAccountAsync(serviceAccountId); @@ -587,7 +610,7 @@ public async Task DeleteOwnCompanyServiceAccountAsync_WithExistingOfferSubscript A.CallTo(() => _technicalUserRepository.GetOwnTechnicalUserWithIamUserRolesAsync(A.That.Not.Matches(x => x == ValidServiceAccountId), A._, A>._)) .Returns(null); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act async Task Act() => await sut.DeleteOwnCompanyServiceAccountAsync(ValidServiceAccountId); @@ -607,7 +630,7 @@ public async Task DeleteOwnCompanyServiceAccountAsync_WithInvalidConnectorStatus A.CallTo(() => _technicalUserRepository.GetOwnTechnicalUserWithIamUserRolesAsync(A.That.Not.Matches(x => x == ValidServiceAccountId), A._, A>._)) .Returns(null); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act async Task Act() => await sut.DeleteOwnCompanyServiceAccountAsync(ValidServiceAccountId); @@ -628,7 +651,7 @@ public async Task DeleteOwnCompanyServiceAccountAsync_WithServiceAccountNotActiv A.CallTo(() => _technicalUserRepository.GetOwnTechnicalUserWithIamUserRolesAsync(ValidServiceAccountId, ValidCompanyId, A>._)) .Returns(new OwnTechnicalUserData(_userRoleIds, ValidServiceAccountId, userStatusId, true, Guid.NewGuid(), null, null, ConnectorStatusId.ACTIVE, OfferSubscriptionStatusId.PENDING, false, false, null)); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act async Task Act() => await sut.DeleteOwnCompanyServiceAccountAsync(ValidServiceAccountId); @@ -645,7 +668,7 @@ public async Task DeleteOwnCompanyServiceAccountAsync_WithUserNotOwnerOrProvider A.CallTo(() => _technicalUserRepository.GetOwnTechnicalUserWithIamUserRolesAsync(ValidServiceAccountId, ValidCompanyId, A>._)) .Returns(new OwnTechnicalUserData(_userRoleIds, ValidServiceAccountId, UserStatusId.ACTIVE, false, Guid.NewGuid(), null, null, ConnectorStatusId.ACTIVE, OfferSubscriptionStatusId.PENDING, false, false, null)); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act async Task Act() => await sut.DeleteOwnCompanyServiceAccountAsync(ValidServiceAccountId); @@ -668,7 +691,7 @@ public async Task DeleteOwnCompanyServiceAccountAsync_WithoutClient_CallsExpecte .Create(); var processId = Guid.NewGuid(); SetupDeleteOwnCompanyServiceAccount(connector, identity, processId); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, null!, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act await sut.DeleteOwnCompanyServiceAccountAsync(ValidServiceAccountId); @@ -705,7 +728,7 @@ public async Task GetServiceAccountRolesAsync_GetsExpectedData() A.CallTo(() => _portalRepositories.GetInstance()).Returns(_userRolesRepository); - var sut = new TechnicalUserBusinessLogic(null!, _portalRepositories, options, null!, _identityService, null!); + var sut = new TechnicalUserBusinessLogic(null!, _portalRepositories, options, null!, _identityService, null!, _bringYourOwnWalletBusinessLogic); // Act var result = await sut.GetServiceAccountRolesAsync(null).ToListAsync(); @@ -720,6 +743,48 @@ public async Task GetServiceAccountRolesAsync_GetsExpectedData() .And.AllSatisfy(x => x.ProviderOnly.Should().Be(x.UserRoleText == providerOnlyRole)); } + [Fact] + public async Task GetServiceAccountRolesAsync_BringYourOwnWallet_GetsExpectedData() + { + // Arrange + + var data = _fixture.CreateMany(15).ToImmutableArray(); + + var externalRole = data[0].UserRoleText; + var providerOnlyRole = data[1].UserRoleText; + var ignoreData = data[4].UserRoleId; + + var options = Options.Create(new ServiceAccountSettings + { + ClientId = ClientId, + DimUserRoles = [new(ClientId, [externalRole]), new("OtherClientId", [data[2].UserRoleText])], + UserRolesAccessibleByProviderOnly = [new(ClientId, [providerOnlyRole]), new("OtherClientId", [data[3].UserRoleText])] + }); + + A.CallTo(() => _userRolesRepository.GetServiceAccountRolesAsync(A._, A._, A._)) + .Returns(data.ToAsyncEnumerable()); + + A.CallTo(() => _portalRepositories.GetInstance()).Returns(_userRolesRepository); + + A.CallTo(() => _bringYourOwnWalletBusinessLogic.IsBringYourOwnWallet(A._)).Returns(true); + + A.CallTo(() => _bringYourOwnWalletBusinessLogic.GetExcludedUserRoles()).Returns([ignoreData]); + + var sut = new TechnicalUserBusinessLogic(null!, _portalRepositories, options, null!, _identityService, null!, _bringYourOwnWalletBusinessLogic); + + // Act + var result = await sut.GetServiceAccountRolesAsync(null).ToListAsync(); + + // Assert + A.CallTo(() => _userRolesRepository.GetServiceAccountRolesAsync(_identity.CompanyId, ClientId, Constants.DefaultLanguage)).MustHaveHappenedOnceExactly(); + + result.Should() + .HaveCount(14) + .And.AllSatisfy(x => data.Should().Contain(new UserRoleWithDescriptionTransferData(x.UserRoleId, x.UserRoleText, x.RoleDescription))) + .And.AllSatisfy(x => x.RoleType.Should().Be(x.UserRoleText == externalRole ? UserRoleType.External : UserRoleType.Internal)) + .And.AllSatisfy(x => x.ProviderOnly.Should().Be(x.UserRoleText == providerOnlyRole)); + } + #endregion #region HandleServiceAccountCreationCallback @@ -736,7 +801,7 @@ public async Task HandleServiceAccountCreationCallback_WithValidOfferSubscriptio A.CallTo(() => _technicalUserRepository.GetProcessDataForTechnicalUserCallback(A._, A>._)) .Returns((ProcessTypeId.OFFER_SUBSCRIPTION, context, technicalUserId, technicalUserVersionId)); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act await sut.HandleServiceAccountCreationCallback(process.Id, _fixture.Create()); @@ -767,7 +832,7 @@ public async Task HandleServiceAccountCreationCallback_WithLinkedUser_ExecutesEx A.CallTo(() => _offerSubscriptionsRepository.GetProcessDataForTechnicalUserCallback(A._, A>._)) .Returns(Enumerable.Repeat(new ValueTuple, Guid?, Guid?>(ProcessTypeId.OFFER_SUBSCRIPTION, context, technicalUserId, technicalUserVersionId), 1).ToAsyncEnumerable()); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); // Act await sut.HandleServiceAccountCreationCallback(process.Id, _fixture.Create()); @@ -797,7 +862,7 @@ public async Task HandleServiceAccountCreationCallback_WithMoreThanOneTechnicalU A.CallTo(() => _offerSubscriptionsRepository.GetProcessDataForTechnicalUserCallback(A._, A>._)) .Returns(Enumerable.Repeat(new ValueTuple, Guid?, Guid?>(ProcessTypeId.OFFER_SUBSCRIPTION, _fixture.Create>(), Guid.NewGuid(), Guid.NewGuid()), 2).ToAsyncEnumerable()); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); async Task Act() => await sut.HandleServiceAccountCreationCallback(process.Id, _fixture.Create()); @@ -825,7 +890,7 @@ public async Task HandleServiceAccountCreationCallback_WithNotExistingProcess_Th A.CallTo(() => _offerSubscriptionsRepository.GetProcessDataForTechnicalUserCallback(A._, A>._)) .Returns(Enumerable.Empty<(ProcessTypeId, VerifyProcessData, Guid?, Guid?)>().ToAsyncEnumerable()); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); async Task Act() => await sut.HandleServiceAccountCreationCallback(process.Id, _fixture.Create()); @@ -848,7 +913,7 @@ public async Task HandleServiceAccountCreationCallback_WithOfferSubscriptionIdNo A.CallTo(() => _technicalUserRepository.GetProcessDataForTechnicalUserCallback(A._, A>._)) .Returns((ProcessTypeId.OFFER_SUBSCRIPTION, context, null, null)); - var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement); + var sut = new TechnicalUserBusinessLogic(_provisioningManager, _portalRepositories, _options, _technicalUserCreation, _identityService, _serviceAccountManagement, _bringYourOwnWalletBusinessLogic); async Task Act() => await sut.HandleServiceAccountCreationCallback(process.Id, _fixture.Create()); diff --git a/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs b/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs index 081d33c54b..eab6b18199 100644 --- a/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs +++ b/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs @@ -152,15 +152,16 @@ public async Task SaveCustomerWalletAsync_SavesWallet_WhenDidIsValid() var did = "did:web:example.com"; var didDocument = JsonDocument.Parse("{\"id\":\"did:web:example.com\"}"); var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); - + var companyRepository = A.Fake(); A.CallTo(() => _portalRepositories.GetInstance().CreateCustomerWallet(companyId, did, didDocument)) .DoesNothing(); A.CallTo(() => _portalRepositories.GetInstance()) - .Returns(A.Fake()); + .Returns(companyRepository); A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) .ReturnsLazily(() => Task.FromResult(validationResult)); A.CallTo(() => _universalDidResolverService.ValidateSchema(didDocument, A._)) .ReturnsLazily(() => true); + A.CallTo(() => companyRepository.IsExistingCompany(companyId)).ReturnsLazily(() => true); // Act await _sut.SaveCustomerWalletAsync(companyId, did); @@ -178,9 +179,11 @@ public async Task SaveCustomerWalletAsync_ThrowsException_WhenDidIsEmpty() var did = ""; var didDocument = JsonDocument.Parse("{\"id\":\"did:web:example.com\"}"); var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); + var companyRepository = A.Fake(); A.CallTo(() => _portalRepositories.GetInstance()) - .Returns(A.Fake()); + .Returns(companyRepository); + A.CallTo(() => companyRepository.IsExistingCompany(companyId)).ReturnsLazily(() => true); A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) .ReturnsLazily(() => Task.FromResult(validationResult)); A.CallTo(() => _universalDidResolverService.ValidateSchema(didDocument, A._)) @@ -214,6 +217,30 @@ public async Task SaveCustomerWalletAsync_ThrowsException_WhenDidIsInvalid() await act.Should().ThrowAsync(); } + [Fact] + public async Task SaveCustomerWalletAsync_ThrowsException_WhenCompanyIdInvalid() + { + // Arrange + var companyId = Guid.NewGuid(); + var did = "did:web:example.com"; + var didDocument = JsonDocument.Parse("{\"id\":\"did:web:example.com\"}"); + var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); + var companyRepository = A.Fake(); + + A.CallTo(() => _portalRepositories.GetInstance()) + .Returns(companyRepository); + A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)).Throws(); + A.CallTo(() => _universalDidResolverService.ValidateSchema(didDocument, A._)) + .ReturnsLazily(() => true); + A.CallTo(() => companyRepository.IsExistingCompany(companyId)).ReturnsLazily(() => false); + // Act + Func act = async () => await _sut.SaveCustomerWalletAsync(companyId, did); + + // Assert + await act.Should().ThrowAsync() + .WithMessage("Company ID not found or not valid."); + } + [Fact] public async Task GetCompanyWalletDidAsync_ReturnsDid_WhenWalletExists() { @@ -225,6 +252,7 @@ public async Task GetCompanyWalletDidAsync_ReturnsDid_WhenWalletExists() .Returns(companyRepository); A.CallTo(() => companyRepository.GetCompanyHolderDidAsync(companyId)) .Returns(Task.FromResult(expectedDid)); + A.CallTo(() => companyRepository.IsExistingCompany(companyId)).ReturnsLazily(() => true); // Act var result = await _sut.getCompanyWalletDidAsync(companyId); @@ -234,22 +262,41 @@ public async Task GetCompanyWalletDidAsync_ReturnsDid_WhenWalletExists() } [Fact] - public async Task GetCompanyWalletDidAsync_ReturnsNull_WhenWalletDoesNotExist() + public async Task GetCompanyWalletDidAsync_ThrowException_WhenWalletDoesNotExist() { // Arrange var companyId = Guid.NewGuid(); var companyRepository = A.Fake(); A.CallTo(() => _portalRepositories.GetInstance()) .Returns(companyRepository); + A.CallTo(() => companyRepository.IsExistingCompany(companyId)).ReturnsLazily(() => true); A.CallTo(() => companyRepository.GetCompanyHolderDidAsync(companyId)) .Returns(Task.FromResult(null)); + // Act + Func act = async () => await _sut.getCompanyWalletDidAsync(companyId); + // Assert + await act.Should().ThrowAsync() + .WithMessage("Company wallet DID not found."); + } + + [Fact] + public async Task GetCompanyWalletDidAsync_ThrowException_WhenCompanyIdDoesNotExist() + { + // Arrange + var companyId = Guid.NewGuid(); + var companyRepository = A.Fake(); + A.CallTo(() => _portalRepositories.GetInstance()) + .Returns(companyRepository); + A.CallTo(() => companyRepository.IsExistingCompany(companyId)).ReturnsLazily(() => false); + A.CallTo(() => companyRepository.GetCompanyHolderDidAsync(companyId)) + .Returns(Task.FromResult(null)); // Act Func act = async () => await _sut.getCompanyWalletDidAsync(companyId); // Assert await act.Should().ThrowAsync() - .WithMessage("Company wallet DID not found for the given company ID."); + .WithMessage("Company ID not found or not valid."); } [Fact] @@ -260,14 +307,17 @@ public async Task SaveCustomerWallet_InvalidDocumentId_ThrowException() var companyId = Guid.NewGuid(); var didDocument = JsonDocument.Parse("{}"); var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); + var companyRepository = A.Fake(); + A.CallTo(() => _portalRepositories.GetInstance().CreateCustomerWallet(companyId, did, didDocument)) .DoesNothing(); A.CallTo(() => _portalRepositories.GetInstance()) - .Returns(A.Fake()); + .Returns(companyRepository); A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) .ReturnsLazily(() => Task.FromResult(validationResult)); A.CallTo(() => _universalDidResolverService.ValidateSchema(didDocument, A._)) .ReturnsLazily(() => true); + A.CallTo(() => companyRepository.IsExistingCompany(companyId)).ReturnsLazily(() => true); // Act @@ -287,10 +337,12 @@ public async Task SaveCustomerWallet_InvalidDidFormat_ThrowException() var companyId = Guid.NewGuid(); var didDocument = JsonDocument.Parse("{\"id\":\":web:123\"}"); var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); + var companyRepository = A.Fake(); A.CallTo(() => _portalRepositories.GetInstance().CreateCustomerWallet(companyId, did, didDocument)) .DoesNothing(); A.CallTo(() => _portalRepositories.GetInstance()) - .Returns(A.Fake()); + .Returns(companyRepository); + A.CallTo(() => companyRepository.IsExistingCompany(companyId)).ReturnsLazily(() => true); A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) .ReturnsLazily(() => Task.FromResult(validationResult)); A.CallTo(() => _universalDidResolverService.ValidateSchema(didDocument, A._)) @@ -314,10 +366,13 @@ public async Task SaveCustomerWallet_InvalidDidForm_ThrowException() var companyId = Guid.NewGuid(); var didDocument = JsonDocument.Parse("{\"id\":\"did:web\"}"); var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); + var companyRepository = A.Fake(); + A.CallTo(() => _portalRepositories.GetInstance().CreateCustomerWallet(companyId, did, didDocument)) .DoesNothing(); A.CallTo(() => _portalRepositories.GetInstance()) - .Returns(A.Fake()); + .Returns(companyRepository); + A.CallTo(() => companyRepository.IsExistingCompany(companyId)).ReturnsLazily(() => true); A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) .ReturnsLazily(() => Task.FromResult(validationResult)); A.CallTo(() => _universalDidResolverService.ValidateSchema(didDocument, A._)) @@ -341,10 +396,12 @@ public async Task SaveCustomerWallet_InvalidDidMethod_ThrowException() var companyId = Guid.NewGuid(); var didDocument = JsonDocument.Parse("{\"id\":\"did:error:123\"}"); var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); + var companyRepository = A.Fake(); A.CallTo(() => _portalRepositories.GetInstance().CreateCustomerWallet(companyId, did, didDocument)) .DoesNothing(); A.CallTo(() => _portalRepositories.GetInstance()) - .Returns(A.Fake()); + .Returns(companyRepository); + A.CallTo(() => companyRepository.IsExistingCompany(companyId)).ReturnsLazily(() => true); A.CallTo(() => _universalDidResolverService.ValidateDid(did, A._)) .ReturnsLazily(() => Task.FromResult(validationResult)); A.CallTo(() => _universalDidResolverService.ValidateSchema(didDocument, A._)) From f30503f29325c2b01acd793d4f88da4c52884a26 Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Mon, 21 Jul 2025 17:55:07 +0200 Subject: [PATCH 14/38] fix: reverted changes for technical user request and changes exception types for universal resolver --- .../TechnicalUserBusinessLogic.cs | 43 ++++++------------- .../UniversalDidResolverService.cs | 10 ++--- .../BringYourOwnWalletBusinessLogic.cs | 10 ++--- .../TechnicalUserBusinessLogicTests.cs | 42 ------------------ .../UniversalDidResolverServiceTest.cs | 6 +-- .../BringYourOwnWalletBuisinessLogicTests.cs | 14 +++--- 6 files changed, 32 insertions(+), 93 deletions(-) diff --git a/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs index f2c2974dbc..19f9e01931 100644 --- a/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs @@ -341,38 +341,19 @@ public IAsyncEnumerable GetServiceAccountRolesAsync(str { var externalRoleNames = _settings.DimUserRoles.Where(role => role.ClientId == _settings.ClientId).SelectMany(role => role.UserRoleNames).ToImmutableHashSet(); var providerOnlyRoleNames = _settings.UserRolesAccessibleByProviderOnly.Where(role => role.ClientId == _settings.ClientId).SelectMany(role => role.UserRoleNames).ToImmutableHashSet(); - var companyId = _identityData.CompanyId; - - var userRoles = portalRepositories.GetInstance() - .GetServiceAccountRolesAsync( - companyId, - _settings.ClientId, - languageShortName ?? Constants.DefaultLanguage) - .Select(x => new UserRoleWithDescription( - x.UserRoleId, - x.UserRoleText, - x.RoleDescription, - externalRoleNames.Contains(x.UserRoleText) ? UserRoleType.External : UserRoleType.Internal, - providerOnlyRoleNames.Contains(x.UserRoleText) - )); - - var isBringYourOwnWallet = bringYourOwnWalletBusinessLogic.IsBringYourOwnWallet(companyId).GetAwaiter().GetResult(); - if (isBringYourOwnWallet) - { - var excludedRoles = bringYourOwnWalletBusinessLogic.GetExcludedUserRoles(); - if (excludedRoles.Any()) - { - return userRoles.Where(role => !excludedRoles.Contains(role.UserRoleId)) - .Select(role => new UserRoleWithDescription( - role.UserRoleId, - role.UserRoleText, - role.RoleDescription, - role.RoleType, - role.ProviderOnly)); - } - } - return userRoles; + return portalRepositories.GetInstance() + .GetServiceAccountRolesAsync( + _identityData.CompanyId, + _settings.ClientId, + languageShortName ?? Constants.DefaultLanguage) + .Select(x => new UserRoleWithDescription( + x.UserRoleId, + x.UserRoleText, + x.RoleDescription, + externalRoleNames.Contains(x.UserRoleText) ? UserRoleType.External : UserRoleType.Internal, + providerOnlyRoleNames.Contains(x.UserRoleText) + )); } public async Task HandleServiceAccountCreationCallback(Guid processId, AuthenticationDetail callbackData) diff --git a/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolverService.cs b/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolverService.cs index 80133e3272..9d9a1d512d 100644 --- a/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolverService.cs +++ b/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolverService.cs @@ -44,25 +44,25 @@ public async Task ValidateDid(string did, CancellationToken { if (ex.InnerException is SocketException) { - throw new NotFoundException("Universal resolver is not reachable"); + throw new ControllerArgumentException("Universal resolver is not reachable"); } - throw new NotFoundException("DID URL could not be reached by the external resolver, 404 error", ex); + throw new ControllerArgumentException("DID URL could not be reached by the external resolver, 404 error"); } if (!result.IsSuccessStatusCode) { - throw new NotFoundException($"Did validation failed with status code {result.StatusCode} and reason {result.ReasonPhrase}. Did: {did}"); + throw new ControllerArgumentException($"Did validation failed with status code {result.StatusCode} and reason {result.ReasonPhrase}. Did: {did}"); } var validationResult = await result.Content.ReadFromJsonAsync(Options, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); if (validationResult == null) { - throw new NotFoundException("DID validation failed: No result returned."); + throw new ControllerArgumentException("DID validation failed: No result returned."); } if (!string.IsNullOrWhiteSpace(validationResult.DidResolutionMetadata.Error)) { - throw new UnsupportedMediaTypeException("DID validation failed during validation"); + throw new ControllerArgumentException("DID validation failed during validation"); } return validationResult; diff --git a/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs b/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs index 9605bdc3e9..7e4f3a5d6e 100644 --- a/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs +++ b/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs @@ -46,7 +46,7 @@ public async Task ValidateDid(string did, CancellationToken cancel if (!isSchemaValid) { - throw new UnsupportedMediaTypeException("DID validation failed. DID Document is not valid."); + throw new ControllerArgumentException("DID validation failed. DID Document is not valid."); } var companyRepository = _portalRepositories.GetInstance(); @@ -112,19 +112,19 @@ private static string CreateDidLocation(JsonDocument didDocument) { if (!didDocument.RootElement.TryGetProperty("id", out var idProperty)) { - throw new UnsupportedMediaTypeException("DID validation failed: missing 'id' property."); + throw new ControllerArgumentException("DID validation failed: missing 'id' property."); } var did = idProperty.GetString(); if (string.IsNullOrWhiteSpace(did) || !did.StartsWith("did:", StringComparison.OrdinalIgnoreCase)) { - throw new UnsupportedMediaTypeException("Invalid DID format: must start with 'did:'."); + throw new ControllerArgumentException("Invalid DID format: must start with 'did:'."); } var didParts = did.Split(':', 3); if (didParts.Length != 3) { - throw new UnsupportedMediaTypeException("Invalid DID format: must be in the form 'did::'."); + throw new ControllerArgumentException("Invalid DID format: must be in the form 'did::'."); } var method = didParts[1]; @@ -132,7 +132,7 @@ private static string CreateDidLocation(JsonDocument didDocument) if (!method.Equals("web", StringComparison.OrdinalIgnoreCase)) { - throw new UnsupportedMediaTypeException($"Unsupported DID method: '{method}'. Only 'did:web' is supported."); + throw new ControllerArgumentException($"Unsupported DID method: '{method}'. Only 'did:web' is supported."); } var hostAndPath = identifier.Replace(":", "/"); diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/TechnicalUserBusinessLogicTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/TechnicalUserBusinessLogicTests.cs index 66312906d9..000b1e396d 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/TechnicalUserBusinessLogicTests.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/TechnicalUserBusinessLogicTests.cs @@ -743,48 +743,6 @@ public async Task GetServiceAccountRolesAsync_GetsExpectedData() .And.AllSatisfy(x => x.ProviderOnly.Should().Be(x.UserRoleText == providerOnlyRole)); } - [Fact] - public async Task GetServiceAccountRolesAsync_BringYourOwnWallet_GetsExpectedData() - { - // Arrange - - var data = _fixture.CreateMany(15).ToImmutableArray(); - - var externalRole = data[0].UserRoleText; - var providerOnlyRole = data[1].UserRoleText; - var ignoreData = data[4].UserRoleId; - - var options = Options.Create(new ServiceAccountSettings - { - ClientId = ClientId, - DimUserRoles = [new(ClientId, [externalRole]), new("OtherClientId", [data[2].UserRoleText])], - UserRolesAccessibleByProviderOnly = [new(ClientId, [providerOnlyRole]), new("OtherClientId", [data[3].UserRoleText])] - }); - - A.CallTo(() => _userRolesRepository.GetServiceAccountRolesAsync(A._, A._, A._)) - .Returns(data.ToAsyncEnumerable()); - - A.CallTo(() => _portalRepositories.GetInstance()).Returns(_userRolesRepository); - - A.CallTo(() => _bringYourOwnWalletBusinessLogic.IsBringYourOwnWallet(A._)).Returns(true); - - A.CallTo(() => _bringYourOwnWalletBusinessLogic.GetExcludedUserRoles()).Returns([ignoreData]); - - var sut = new TechnicalUserBusinessLogic(null!, _portalRepositories, options, null!, _identityService, null!, _bringYourOwnWalletBusinessLogic); - - // Act - var result = await sut.GetServiceAccountRolesAsync(null).ToListAsync(); - - // Assert - A.CallTo(() => _userRolesRepository.GetServiceAccountRolesAsync(_identity.CompanyId, ClientId, Constants.DefaultLanguage)).MustHaveHappenedOnceExactly(); - - result.Should() - .HaveCount(14) - .And.AllSatisfy(x => data.Should().Contain(new UserRoleWithDescriptionTransferData(x.UserRoleId, x.UserRoleText, x.RoleDescription))) - .And.AllSatisfy(x => x.RoleType.Should().Be(x.UserRoleText == externalRole ? UserRoleType.External : UserRoleType.Internal)) - .And.AllSatisfy(x => x.ProviderOnly.Should().Be(x.UserRoleText == providerOnlyRole)); - } - #endregion #region HandleServiceAccountCreationCallback diff --git a/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolverServiceTest.cs b/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolverServiceTest.cs index bef0a5655d..2a6ac6a7a8 100644 --- a/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolverServiceTest.cs +++ b/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolverServiceTest.cs @@ -103,7 +103,7 @@ public async Task ValidateDid_ReturnsFalse_WhenDidHasError() async Task Act() => await _sut.ValidateDid(did, CancellationToken.None); // Assert - var ex = await Assert.ThrowsAsync(Act); + var ex = await Assert.ThrowsAsync(Act); ex.Message.Should().Contain("DID validation failed during validation"); } @@ -131,7 +131,7 @@ public async Task ValidateDid_ThrowNotFoundException_WhenDidNotResolved() async Task Act() => await _sut.ValidateDid(did, CancellationToken.None); // Assert - var ex = await Assert.ThrowsAsync(Act); + var ex = await Assert.ThrowsAsync(Act); ex.Message.Should().Be("DID URL could not be reached by the external resolver, 404 error"); request.Should().NotBeNull(); request!.RequestUri.Should().NotBeNull(); @@ -184,7 +184,7 @@ public async Task ValidateDid_ThrowsException_WhenValidationResultIsNull() async Task Act() => await _sut.ValidateDid(did, CancellationToken.None); // Assert - var ex = await Assert.ThrowsAsync(Act); + var ex = await Assert.ThrowsAsync(Act); ex.Message.Should().Contain("DID validation failed: No result returned."); request.Should().NotBeNull(); request!.RequestUri.Should().NotBeNull(); diff --git a/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs b/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs index eab6b18199..6979fac2a5 100644 --- a/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs +++ b/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs @@ -74,7 +74,7 @@ public async Task ValidateDid_ThrowsSericeException_WhenDidHasError() var act = () => _sut.ValidateDid(did, CancellationToken.None); // Assert - await act.Should().ThrowAsync() + await act.Should().ThrowAsync() .WithMessage("DID validation failed. DID Document is not valid."); } @@ -119,7 +119,7 @@ public async Task ValidateDid_ThrowsServiceException_WhenSchemaIsInvalid() var act = () => _sut.ValidateDid(did, CancellationToken.None); // Assert - await act.Should().ThrowAsync() + await act.Should().ThrowAsync() .WithMessage("DID validation failed. DID Document is not valid."); } @@ -140,7 +140,7 @@ public async Task ValidateDid_ThrowsServiceException_WhenValidationResultIsNull( var act = () => _sut.ValidateDid(did, CancellationToken.None); // Assert - await act.Should().ThrowAsync() + await act.Should().ThrowAsync() .WithMessage("DID validation failed. DID Document is not valid."); } @@ -325,7 +325,7 @@ public async Task SaveCustomerWallet_InvalidDocumentId_ThrowException() await _sut.SaveCustomerWalletAsync(companyId, did); // Assert - await act.Should().ThrowAsync() + await act.Should().ThrowAsync() .WithMessage("DID validation failed: missing 'id' property."); } @@ -354,7 +354,7 @@ public async Task SaveCustomerWallet_InvalidDidFormat_ThrowException() await _sut.SaveCustomerWalletAsync(companyId, did); // Assert - await act.Should().ThrowAsync() + await act.Should().ThrowAsync() .WithMessage("Invalid DID format: must start with 'did:'."); } @@ -384,7 +384,7 @@ public async Task SaveCustomerWallet_InvalidDidForm_ThrowException() await _sut.SaveCustomerWalletAsync(companyId, did); // Assert - await act.Should().ThrowAsync() + await act.Should().ThrowAsync() .WithMessage("Invalid DID format: must be in the form 'did::'."); } @@ -413,7 +413,7 @@ public async Task SaveCustomerWallet_InvalidDidMethod_ThrowException() await _sut.SaveCustomerWalletAsync(companyId, did); // Assert - await act.Should().ThrowAsync() + await act.Should().ThrowAsync() .WithMessage("Unsupported DID method: 'error'. Only 'did:web' is supported."); } } From 725f76674ad0745440f3b205c512fe108d65120f Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Wed, 23 Jul 2025 14:37:18 +0200 Subject: [PATCH 15/38] feat: added example of configuratio for universal resolver --- src/registration/Registration.Service/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registration/Registration.Service/appsettings.json b/src/registration/Registration.Service/appsettings.json index f8d4c57526..2d7cadc091 100644 --- a/src/registration/Registration.Service/appsettings.json +++ b/src/registration/Registration.Service/appsettings.json @@ -37,7 +37,7 @@ "PasswordResendAddress": "" }, "UniversalDidResolver": { - "UniversalResolverAddress": "" + "UniversalResolverAddress": "https://resolver.example.io/1.0/identifiers/" }, "BpnAccess": { "BaseAddress": "" From c3c247d006daf2dba72e18fdc7aec5e6bb458bf7 Mon Sep 17 00:00:00 2001 From: leandro-cavalcante <162341380+leandro-cavalcante@users.noreply.github.com> Date: Wed, 23 Jul 2025 16:43:26 +0200 Subject: [PATCH 16/38] Update src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs Co-authored-by: Karsten Thiems --- .../BusinessLogic/BringYourOwnWalletBusinessLogic.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs b/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs index 7e4f3a5d6e..ad1ca4348c 100644 --- a/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs +++ b/src/registration/Registration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs @@ -17,6 +17,7 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ + using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; From 60e616e1cdfd87bf1731e9d09757481fed74986c Mon Sep 17 00:00:00 2001 From: leandro-cavalcante <162341380+leandro-cavalcante@users.noreply.github.com> Date: Wed, 23 Jul 2025 16:43:56 +0200 Subject: [PATCH 17/38] Update src/registration/Registration.Service/Controllers/BringYourOwnWalletController.cs Co-authored-by: Karsten Thiems --- .../Controllers/BringYourOwnWalletController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/registration/Registration.Service/Controllers/BringYourOwnWalletController.cs b/src/registration/Registration.Service/Controllers/BringYourOwnWalletController.cs index 402006847f..c67a6b494d 100644 --- a/src/registration/Registration.Service/Controllers/BringYourOwnWalletController.cs +++ b/src/registration/Registration.Service/Controllers/BringYourOwnWalletController.cs @@ -16,6 +16,7 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ + using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling.Web; From 31a0324bb3289d7c2f985c55b17c153d8ae1a707 Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Wed, 23 Jul 2025 17:42:00 +0200 Subject: [PATCH 18/38] fix: resolved codeql findings --- .../BringYourOwnWalletBusinessLogic.cs | 1 - .../BusinessLogic/DimBusinessLogic.cs | 2 +- .../BringYourOwnWalletBusinessLogicTests.cs | 20 +++++++++++++++++++ .../UniversalDidResolver.Library.Tests.csproj | 4 +++- .../UniversalDidResolverServiceTest.cs | 2 +- .../BringYourOwnWalletBuisinessLogicTests.cs | 2 -- 6 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs index f73753316a..de4dd8f559 100644 --- a/src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs @@ -32,7 +32,6 @@ public class BringYourOwnWalletBusinessLogic( public async Task IsUserRoleAuthorizedForBYOW(Guid companyId, IEnumerable userRoleId) { - var companyRepository = portalRepositories.GetInstance(); if (!await IsBringYourOwnWallet(companyId)) { return true; diff --git a/src/externalsystems/Dim.Library/BusinessLogic/DimBusinessLogic.cs b/src/externalsystems/Dim.Library/BusinessLogic/DimBusinessLogic.cs index 7ae3026cf1..56026b8002 100644 --- a/src/externalsystems/Dim.Library/BusinessLogic/DimBusinessLogic.cs +++ b/src/externalsystems/Dim.Library/BusinessLogic/DimBusinessLogic.cs @@ -247,7 +247,7 @@ private static async Task ValidateSchema(JsonDocument content, Cancellatio { var location = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? throw new UnexpectedConditionException("Assembly location must be set"); - var path = Path.Combine(location, "Schemas", "DidDocument.schema.json"); + var path = Path.Join(location, "Schemas", "DidDocument.schema.json"); var schemaJson = await File.ReadAllTextAsync(path, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); var schema = JsonSchema.FromText(schemaJson); diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/BringYourOwnWalletBusinessLogicTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/BringYourOwnWalletBusinessLogicTests.cs index 98f4bdab20..5a2513d6b3 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/BringYourOwnWalletBusinessLogicTests.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/BringYourOwnWalletBusinessLogicTests.cs @@ -1,3 +1,23 @@ +/******************************************************************************** + * Copyright (c) 2025 Cofinity-X GmbH + * Copyright (c) 2025 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 Microsoft.Extensions.Options; using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLogic; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess; diff --git a/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolver.Library.Tests.csproj b/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolver.Library.Tests.csproj index 47fb71553d..6f80265cd5 100644 --- a/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolver.Library.Tests.csproj +++ b/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolver.Library.Tests.csproj @@ -17,7 +17,9 @@ - - SPDX-License-Identifier: Apache-2.0 --> + + net9.0 enable @@ -38,4 +40,4 @@ - \ No newline at end of file + diff --git a/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolverServiceTest.cs b/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolverServiceTest.cs index 2a6ac6a7a8..cd713470d4 100644 --- a/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolverServiceTest.cs +++ b/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolverServiceTest.cs @@ -72,7 +72,7 @@ public async Task ValidateDid_ReturnsTrue_WhenDidIsValid() A.CallTo(() => universalDidResolverService.ValidateSchema(A._, A._)).Returns(true); // Act - var result = await _sut.ValidateDid(did, CancellationToken.None); + await _sut.ValidateDid(did, CancellationToken.None); // Assert request.Should().NotBeNull(); diff --git a/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs b/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs index 6979fac2a5..6a943ff3ee 100644 --- a/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs +++ b/tests/registration/Registration.Service.Tests/BusinessLogic/BringYourOwnWalletBuisinessLogicTests.cs @@ -203,7 +203,6 @@ public async Task SaveCustomerWalletAsync_ThrowsException_WhenDidIsInvalid() var companyId = Guid.NewGuid(); var did = "did:web:INVALID"; var didDocument = JsonDocument.Parse("{\"id\":\"did:web:example.com\"}"); - var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); A.CallTo(() => _portalRepositories.GetInstance()) .Returns(A.Fake()); @@ -224,7 +223,6 @@ public async Task SaveCustomerWalletAsync_ThrowsException_WhenCompanyIdInvalid() var companyId = Guid.NewGuid(); var did = "did:web:example.com"; var didDocument = JsonDocument.Parse("{\"id\":\"did:web:example.com\"}"); - var validationResult = new DidValidationResult(new DidResolutionMetadata(null), didDocument); var companyRepository = A.Fake(); A.CallTo(() => _portalRepositories.GetInstance()) From b69f2d393767625d2fc418d91c8b1d620c6c7dc0 Mon Sep 17 00:00:00 2001 From: leandro-cavalcante <162341380+leandro-cavalcante@users.noreply.github.com> Date: Wed, 23 Jul 2025 17:43:02 +0200 Subject: [PATCH 19/38] Update src/Portal.Backend.sln Co-authored-by: Karsten Thiems --- src/Portal.Backend.sln | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Portal.Backend.sln b/src/Portal.Backend.sln index 525c6414ec..9cd03ae85c 100644 --- a/src/Portal.Backend.sln +++ b/src/Portal.Backend.sln @@ -1,4 +1,3 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32407.343 From 25f452b6ef0b3f057c5df150dcf22f70ed9a026a Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Wed, 23 Jul 2025 17:48:49 +0200 Subject: [PATCH 20/38] fix: resolved codeql findings --- .../UniversalDidResolver.Library/UniversalDidResolverService.cs | 2 +- .../BpnDidResolverBusinessLogicTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolverService.cs b/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolverService.cs index 9d9a1d512d..cb8462a4db 100644 --- a/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolverService.cs +++ b/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolverService.cs @@ -72,7 +72,7 @@ public async Task ValidateSchema(JsonDocument content, CancellationToken c { var location = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? throw new UnexpectedConditionException("Assembly location must be set"); - var path = Path.Combine(location, "Schemas", "DidDocument.schema.json"); + var path = Path.Join(location, "Schemas", "DidDocument.schema.json"); var schemaJson = await File.ReadAllTextAsync(path, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); var schema = JsonSchema.FromText(schemaJson); diff --git a/tests/externalsystems/BpnDidResolver.Library.Tests/BpnDidResolverBusinessLogicTests.cs b/tests/externalsystems/BpnDidResolver.Library.Tests/BpnDidResolverBusinessLogicTests.cs index bbf4ae2891..9984a32bb7 100644 --- a/tests/externalsystems/BpnDidResolver.Library.Tests/BpnDidResolverBusinessLogicTests.cs +++ b/tests/externalsystems/BpnDidResolver.Library.Tests/BpnDidResolverBusinessLogicTests.cs @@ -258,7 +258,7 @@ public async Task TransmitDidAndBpn_WithBpnProcessInTodo_BringYourWalletTrue() A.CallTo(() => _companyRepository.IsBringYourOwnWallet(ApplicationId)).Returns(true); // Act - var result = await _logic.TransmitDidAndBpn(context, CancellationToken.None); + await _logic.TransmitDidAndBpn(context, CancellationToken.None); // Assert A.CallTo(() => _bpnDidResolverService.TransmitDidAndBpn(did, BPN, A._)) From 349c7179b94fe14de3fcfdc5ca73192f0fbf7717 Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Wed, 23 Jul 2025 17:57:43 +0200 Subject: [PATCH 21/38] fix: resolved pr requests --- .../UniversalDidResolver.Library.Tests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolver.Library.Tests.csproj b/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolver.Library.Tests.csproj index 6f80265cd5..f7f1c499ef 100644 --- a/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolver.Library.Tests.csproj +++ b/tests/externalsystems/UniversalDidResolver.Library.Tests/UniversalDidResolver.Library.Tests.csproj @@ -40,4 +40,5 @@ + From 56a214c8a78cca8a7f5bdb6f4a33debfacc23e3a Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Wed, 23 Jul 2025 18:02:49 +0200 Subject: [PATCH 22/38] fix: removed codeql warning for user-controlled data --- .../BusinessLogic/TechnicalUserBusinessLogic.cs | 6 +++--- .../UniversalDidResolverServiceCollectionExtension.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs index 19f9e01931..cb243da63a 100644 --- a/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs @@ -79,14 +79,14 @@ public async Task> CreateOwnCompanyServiceAcc throw ConflictException.Create(AdministrationServiceAccountErrors.SERVICE_BPN_NOT_SET_CONFLICT, [new(CompanyId, companyId.ToString())]); } + technicalUserCreationInfos.UserRoleIds.Except(result.TechnicalUserRoleIds) + .IfAny(unassignable => throw ControllerArgumentException.Create(AdministrationServiceAccountErrors.SERVICE_ROLES_NOT_ASSIGN_ARGUMENT, parameters: [new("unassignable", string.Join(",", unassignable)), new("userRoleIds", string.Join(",", result.TechnicalUserRoleIds))])); + if (!await bringYourOwnWalletBusinessLogic.IsUserRoleAuthorizedForBYOW(companyId, technicalUserCreationInfos.UserRoleIds)) { throw ControllerArgumentException.Create(AdministrationServiceAccountErrors.SERVICE_ACCOUNT_USER_ROLES_NOT_ALLOWED, parameters: [new("userRoleIds", string.Join(",", bringYourOwnWalletBusinessLogic.GetExcludedUserRoles()))]); } - technicalUserCreationInfos.UserRoleIds.Except(result.TechnicalUserRoleIds) - .IfAny(unassignable => throw ControllerArgumentException.Create(AdministrationServiceAccountErrors.SERVICE_ROLES_NOT_ASSIGN_ARGUMENT, parameters: [new("unassignable", string.Join(",", unassignable)), new("userRoleIds", string.Join(",", result.TechnicalUserRoleIds))])); - const TechnicalUserTypeId TechnicalUserTypeId = TechnicalUserTypeId.OWN; var (_, _, serviceAccounts) = await technicalUserCreation.CreateTechnicalUsersAsync(technicalUserCreationInfos, companyId, [result.Bpn], TechnicalUserTypeId, false, true, new ServiceAccountCreationProcessData(ProcessTypeId.DIM_TECHNICAL_USER, null)).ConfigureAwait(ConfigureAwaitOptions.None); diff --git a/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs b/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs index ba68509d86..14740da1da 100644 --- a/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs +++ b/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs @@ -48,7 +48,7 @@ public static IServiceCollection AddUniversalDidResolverService(this IServiceCol private static void RegisterUniversalResolver(UniversalDidResolverSettings settings, IServiceCollection services) { - var baseAddress = settings.UniversalResolverAddress.EndsWith("/") + var baseAddress = settings.UniversalResolverAddress.EndsWith('/') ? settings.UniversalResolverAddress : $"{settings.UniversalResolverAddress}/"; services.AddHttpClient("universalResolver", c => From de3fa2cc9eb2d2f934c8ddaa434314536cb096f4 Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Thu, 24 Jul 2025 15:06:56 +0200 Subject: [PATCH 23/38] Revert "fix: removed codeql warning for user-controlled data" This reverts commit bb0a4ad95887f62ffbf1556f05cc3bff454ff72b. --- .../BusinessLogic/TechnicalUserBusinessLogic.cs | 6 +++--- .../UniversalDidResolverServiceCollectionExtension.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs index cb243da63a..19f9e01931 100644 --- a/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs @@ -79,14 +79,14 @@ public async Task> CreateOwnCompanyServiceAcc throw ConflictException.Create(AdministrationServiceAccountErrors.SERVICE_BPN_NOT_SET_CONFLICT, [new(CompanyId, companyId.ToString())]); } - technicalUserCreationInfos.UserRoleIds.Except(result.TechnicalUserRoleIds) - .IfAny(unassignable => throw ControllerArgumentException.Create(AdministrationServiceAccountErrors.SERVICE_ROLES_NOT_ASSIGN_ARGUMENT, parameters: [new("unassignable", string.Join(",", unassignable)), new("userRoleIds", string.Join(",", result.TechnicalUserRoleIds))])); - if (!await bringYourOwnWalletBusinessLogic.IsUserRoleAuthorizedForBYOW(companyId, technicalUserCreationInfos.UserRoleIds)) { throw ControllerArgumentException.Create(AdministrationServiceAccountErrors.SERVICE_ACCOUNT_USER_ROLES_NOT_ALLOWED, parameters: [new("userRoleIds", string.Join(",", bringYourOwnWalletBusinessLogic.GetExcludedUserRoles()))]); } + technicalUserCreationInfos.UserRoleIds.Except(result.TechnicalUserRoleIds) + .IfAny(unassignable => throw ControllerArgumentException.Create(AdministrationServiceAccountErrors.SERVICE_ROLES_NOT_ASSIGN_ARGUMENT, parameters: [new("unassignable", string.Join(",", unassignable)), new("userRoleIds", string.Join(",", result.TechnicalUserRoleIds))])); + const TechnicalUserTypeId TechnicalUserTypeId = TechnicalUserTypeId.OWN; var (_, _, serviceAccounts) = await technicalUserCreation.CreateTechnicalUsersAsync(technicalUserCreationInfos, companyId, [result.Bpn], TechnicalUserTypeId, false, true, new ServiceAccountCreationProcessData(ProcessTypeId.DIM_TECHNICAL_USER, null)).ConfigureAwait(ConfigureAwaitOptions.None); diff --git a/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs b/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs index 14740da1da..ba68509d86 100644 --- a/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs +++ b/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs @@ -48,7 +48,7 @@ public static IServiceCollection AddUniversalDidResolverService(this IServiceCol private static void RegisterUniversalResolver(UniversalDidResolverSettings settings, IServiceCollection services) { - var baseAddress = settings.UniversalResolverAddress.EndsWith('/') + var baseAddress = settings.UniversalResolverAddress.EndsWith("/") ? settings.UniversalResolverAddress : $"{settings.UniversalResolverAddress}/"; services.AddHttpClient("universalResolver", c => From 08f5c49c7c1381c7e26d4b5a37522e8b4f89106c Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Thu, 24 Jul 2025 15:11:30 +0200 Subject: [PATCH 24/38] fix: removed codeql warning for user-controlled data --- .../BusinessLogic/TechnicalUserBusinessLogic.cs | 6 +++--- .../UniversalDidResolverServiceCollectionExtension.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs index 19f9e01931..cb243da63a 100644 --- a/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs @@ -79,14 +79,14 @@ public async Task> CreateOwnCompanyServiceAcc throw ConflictException.Create(AdministrationServiceAccountErrors.SERVICE_BPN_NOT_SET_CONFLICT, [new(CompanyId, companyId.ToString())]); } + technicalUserCreationInfos.UserRoleIds.Except(result.TechnicalUserRoleIds) + .IfAny(unassignable => throw ControllerArgumentException.Create(AdministrationServiceAccountErrors.SERVICE_ROLES_NOT_ASSIGN_ARGUMENT, parameters: [new("unassignable", string.Join(",", unassignable)), new("userRoleIds", string.Join(",", result.TechnicalUserRoleIds))])); + if (!await bringYourOwnWalletBusinessLogic.IsUserRoleAuthorizedForBYOW(companyId, technicalUserCreationInfos.UserRoleIds)) { throw ControllerArgumentException.Create(AdministrationServiceAccountErrors.SERVICE_ACCOUNT_USER_ROLES_NOT_ALLOWED, parameters: [new("userRoleIds", string.Join(",", bringYourOwnWalletBusinessLogic.GetExcludedUserRoles()))]); } - technicalUserCreationInfos.UserRoleIds.Except(result.TechnicalUserRoleIds) - .IfAny(unassignable => throw ControllerArgumentException.Create(AdministrationServiceAccountErrors.SERVICE_ROLES_NOT_ASSIGN_ARGUMENT, parameters: [new("unassignable", string.Join(",", unassignable)), new("userRoleIds", string.Join(",", result.TechnicalUserRoleIds))])); - const TechnicalUserTypeId TechnicalUserTypeId = TechnicalUserTypeId.OWN; var (_, _, serviceAccounts) = await technicalUserCreation.CreateTechnicalUsersAsync(technicalUserCreationInfos, companyId, [result.Bpn], TechnicalUserTypeId, false, true, new ServiceAccountCreationProcessData(ProcessTypeId.DIM_TECHNICAL_USER, null)).ConfigureAwait(ConfigureAwaitOptions.None); diff --git a/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs b/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs index ba68509d86..14740da1da 100644 --- a/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs +++ b/src/externalsystems/UniversalDidResolver.Library/DependencyInjection/UniversalDidResolverServiceCollectionExtension.cs @@ -48,7 +48,7 @@ public static IServiceCollection AddUniversalDidResolverService(this IServiceCol private static void RegisterUniversalResolver(UniversalDidResolverSettings settings, IServiceCollection services) { - var baseAddress = settings.UniversalResolverAddress.EndsWith("/") + var baseAddress = settings.UniversalResolverAddress.EndsWith('/') ? settings.UniversalResolverAddress : $"{settings.UniversalResolverAddress}/"; services.AddHttpClient("universalResolver", c => From 3b1613440637b9d2ffd99b76d81d9458d423f36f Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Thu, 24 Jul 2025 15:50:26 +0200 Subject: [PATCH 25/38] fix: removed user-controlled bypass sensitive method --- .../BusinessLogic/TechnicalUserBusinessLogic.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs index cb243da63a..faab381bff 100644 --- a/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs @@ -82,7 +82,10 @@ public async Task> CreateOwnCompanyServiceAcc technicalUserCreationInfos.UserRoleIds.Except(result.TechnicalUserRoleIds) .IfAny(unassignable => throw ControllerArgumentException.Create(AdministrationServiceAccountErrors.SERVICE_ROLES_NOT_ASSIGN_ARGUMENT, parameters: [new("unassignable", string.Join(",", unassignable)), new("userRoleIds", string.Join(",", result.TechnicalUserRoleIds))])); - if (!await bringYourOwnWalletBusinessLogic.IsUserRoleAuthorizedForBYOW(companyId, technicalUserCreationInfos.UserRoleIds)) + var filteredTechnicalUserRoles = technicalUserCreationInfos.UserRoleIds + .Where(roleId => result.TechnicalUserRoleIds.Contains(roleId)) + .ToImmutableArray(); + if (!await bringYourOwnWalletBusinessLogic.IsUserRoleAuthorizedForBYOW(companyId, filteredTechnicalUserRoles)) { throw ControllerArgumentException.Create(AdministrationServiceAccountErrors.SERVICE_ACCOUNT_USER_ROLES_NOT_ALLOWED, parameters: [new("userRoleIds", string.Join(",", bringYourOwnWalletBusinessLogic.GetExcludedUserRoles()))]); } From 163cbee1f8be762d25393299844139a27320e874 Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Thu, 24 Jul 2025 16:21:22 +0200 Subject: [PATCH 26/38] Revert "fix: removed user-controlled bypass sensitive method" This reverts commit 1ef3e666efa8cc90f40edfddb4661ef3e113e33c. --- .../BusinessLogic/TechnicalUserBusinessLogic.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs index faab381bff..cb243da63a 100644 --- a/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/TechnicalUserBusinessLogic.cs @@ -82,10 +82,7 @@ public async Task> CreateOwnCompanyServiceAcc technicalUserCreationInfos.UserRoleIds.Except(result.TechnicalUserRoleIds) .IfAny(unassignable => throw ControllerArgumentException.Create(AdministrationServiceAccountErrors.SERVICE_ROLES_NOT_ASSIGN_ARGUMENT, parameters: [new("unassignable", string.Join(",", unassignable)), new("userRoleIds", string.Join(",", result.TechnicalUserRoleIds))])); - var filteredTechnicalUserRoles = technicalUserCreationInfos.UserRoleIds - .Where(roleId => result.TechnicalUserRoleIds.Contains(roleId)) - .ToImmutableArray(); - if (!await bringYourOwnWalletBusinessLogic.IsUserRoleAuthorizedForBYOW(companyId, filteredTechnicalUserRoles)) + if (!await bringYourOwnWalletBusinessLogic.IsUserRoleAuthorizedForBYOW(companyId, technicalUserCreationInfos.UserRoleIds)) { throw ControllerArgumentException.Create(AdministrationServiceAccountErrors.SERVICE_ACCOUNT_USER_ROLES_NOT_ALLOWED, parameters: [new("userRoleIds", string.Join(",", bringYourOwnWalletBusinessLogic.GetExcludedUserRoles()))]); } From 67ec43eeccdd6c50862c222da6abf90232674156 Mon Sep 17 00:00:00 2001 From: leandro-cavalcante Date: Fri, 25 Jul 2025 18:18:09 +0200 Subject: [PATCH 27/38] fix: enhanced the use of exceptions and updated tests --- .../BringYourOwnWalletBusinessLogic.cs | 1 - .../BringYourOwnWalletSettings.cs | 1 - .../IBringYourOwnWalletBusinessLogic.cs | 1 - .../IUniversalDidResolverService.cs | 1 - .../Models/DidValidationResult.cs | 1 - .../UniversalDidResolver.Library.csproj | 1 - .../UniversalDidResolverService.cs | 1 - .../BringYourOwnWalletBusinessLogic.cs | 23 ++--- .../IBringYourOwnWalletBusinessLogic.cs | 1 - .../RegistrationErrorMessageContainer.cs | 15 ++- .../BringYourOwnWalletBusinessLogicTests.cs | 1 - .../IssuerComponentBusinessLogicTests.cs | 97 ++++++++++++++----- .../UniversalDidResolver.Library.Tests.csproj | 1 - .../UniversalDidResolverServiceTest.cs | 1 - .../BringYourOwnWalletBuisinessLogicTests.cs | 26 ++--- .../BringYourOwnWalletControllerTests.cs | 1 - 16 files changed, 112 insertions(+), 61 deletions(-) diff --git a/src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs index de4dd8f559..072b948f92 100644 --- a/src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletBusinessLogic.cs @@ -1,5 +1,4 @@ /******************************************************************************** - * Copyright (c) 2025 Cofinity-X GmbH * Copyright (c) 2025 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional diff --git a/src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletSettings.cs b/src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletSettings.cs index 791081d8fd..799b2e1ecf 100644 --- a/src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletSettings.cs +++ b/src/administration/Administration.Service/BusinessLogic/BringYourOwnWalletSettings.cs @@ -1,6 +1,5 @@ /******************************************************************************** - * Copyright (c) 2025 Cofinity-X GmbH * Copyright (c) 2025 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional diff --git a/src/administration/Administration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs index 32e3148f05..1b4274a444 100644 --- a/src/administration/Administration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/IBringYourOwnWalletBusinessLogic.cs @@ -1,5 +1,4 @@ /******************************************************************************** - * Copyright (c) 2025 Cofinity-X GmbH * Copyright (c) 2025 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional diff --git a/src/externalsystems/UniversalDidResolver.Library/IUniversalDidResolverService.cs b/src/externalsystems/UniversalDidResolver.Library/IUniversalDidResolverService.cs index dd57ad8224..cac4b2552d 100644 --- a/src/externalsystems/UniversalDidResolver.Library/IUniversalDidResolverService.cs +++ b/src/externalsystems/UniversalDidResolver.Library/IUniversalDidResolverService.cs @@ -1,5 +1,4 @@ /******************************************************************************** - * Copyright (c) 2025 Cofinity-X GmbH * Copyright (c) 2025 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional diff --git a/src/externalsystems/UniversalDidResolver.Library/Models/DidValidationResult.cs b/src/externalsystems/UniversalDidResolver.Library/Models/DidValidationResult.cs index 9e586ac625..a292f463d0 100644 --- a/src/externalsystems/UniversalDidResolver.Library/Models/DidValidationResult.cs +++ b/src/externalsystems/UniversalDidResolver.Library/Models/DidValidationResult.cs @@ -1,5 +1,4 @@ /******************************************************************************** - * Copyright (c) 2025 Cofinity-X GmbH * Copyright (c) 2025 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional diff --git a/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolver.Library.csproj b/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolver.Library.csproj index 137b9cddfb..d95863ce17 100644 --- a/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolver.Library.csproj +++ b/src/externalsystems/UniversalDidResolver.Library/UniversalDidResolver.Library.csproj @@ -1,5 +1,4 @@