diff --git a/src/externalsystems/BpnDidResolver.Library/BpnDidResolverService.cs b/src/externalsystems/BpnDidResolver.Library/BpnDidResolverService.cs index 87b591b7a5..86de301c17 100644 --- a/src/externalsystems/BpnDidResolver.Library/BpnDidResolverService.cs +++ b/src/externalsystems/BpnDidResolver.Library/BpnDidResolverService.cs @@ -17,21 +17,43 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +using Microsoft.Extensions.Options; +using Org.Eclipse.TractusX.Portal.Backend.BpnDidResolver.Library.DependencyInjection; using Org.Eclipse.TractusX.Portal.Backend.Dim.Library.Models; +using Org.Eclipse.TractusX.Portal.Backend.Framework.HttpClientExtensions; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Token; using System.Net.Http.Json; using System.Text.Json; namespace Org.Eclipse.TractusX.Portal.Backend.BpnDidResolver.Library; -public class BpnDidResolverService(IHttpClientFactory httpClientFactory) : IBpnDidResolverService +public class BpnDidResolverService : IBpnDidResolverService { + private readonly ITokenService _tokenService; + private readonly BpnDidResolverSettings _settings; private static readonly JsonSerializerOptions Options = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; + /// + /// Creates a new instance of + /// + /// + /// + public BpnDidResolverService(ITokenService tokenService, IOptions options) + { + _tokenService = tokenService; + _settings = options.Value; + } + public async Task TransmitDidAndBpn(string did, string bpn, CancellationToken cancellationToken) { - using var httpClient = httpClientFactory.CreateClient(nameof(BpnDidResolverService)); + using var httpClient = await _tokenService.GetAuthorizedClient(_settings, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); var data = new BpnMappingData(bpn, did); - var result = await httpClient.PostAsJsonAsync("api/management/bpn-directory", data, Options, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + + async ValueTask<(bool, string?)> CreateErrorMessage(HttpResponseMessage errorResponse) => + (false, (await errorResponse.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None))); + + var result = await httpClient.PostAsJsonAsync("api/management/bpn-directory", data, Options, cancellationToken) + .CatchingIntoServiceExceptionFor("transmit-did-bpn", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE, CreateErrorMessage).ConfigureAwait(false); return result.IsSuccessStatusCode; } } diff --git a/src/externalsystems/BpnDidResolver.Library/DependencyInjection/BpnDidResolverServiceCollectionExtension.cs b/src/externalsystems/BpnDidResolver.Library/DependencyInjection/BpnDidResolverServiceCollectionExtension.cs index c1370f21e2..e540a337f4 100644 --- a/src/externalsystems/BpnDidResolver.Library/DependencyInjection/BpnDidResolverServiceCollectionExtension.cs +++ b/src/externalsystems/BpnDidResolver.Library/DependencyInjection/BpnDidResolverServiceCollectionExtension.cs @@ -37,13 +37,9 @@ public static IServiceCollection AddBpnDidResolver(this IServiceCollection servi var sp = services.BuildServiceProvider(); var settings = sp.GetRequiredService>(); - services.AddHttpClient(nameof(BpnDidResolverService), c => - { - var baseAddress = settings.Value.BaseAddress; - c.BaseAddress = new Uri(baseAddress.EndsWith('/') ? baseAddress : $"{baseAddress}/"); - c.DefaultRequestHeaders.Add("X-Api-Key", settings.Value.ApiKey); - }); + var baseAddress = settings.Value.BaseAddress; services + .AddCustomHttpClientWithAuthentication(baseAddress.EndsWith('/') ? baseAddress : $"{baseAddress}/") .AddTransient() .AddTransient(); diff --git a/src/externalsystems/BpnDidResolver.Library/DependencyInjection/BpnDidResolverSettings.cs b/src/externalsystems/BpnDidResolver.Library/DependencyInjection/BpnDidResolverSettings.cs index cb34bd3b7b..66599c77d1 100644 --- a/src/externalsystems/BpnDidResolver.Library/DependencyInjection/BpnDidResolverSettings.cs +++ b/src/externalsystems/BpnDidResolver.Library/DependencyInjection/BpnDidResolverSettings.cs @@ -17,15 +17,13 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +using Org.Eclipse.TractusX.Portal.Backend.Framework.Token; using System.ComponentModel.DataAnnotations; namespace Org.Eclipse.TractusX.Portal.Backend.BpnDidResolver.Library.DependencyInjection; -public class BpnDidResolverSettings +public class BpnDidResolverSettings : KeyVaultAuthSettings { [Required(AllowEmptyStrings = false)] public string BaseAddress { get; set; } = null!; - - [Required(AllowEmptyStrings = false)] - public string ApiKey { get; set; } = null!; } diff --git a/src/portalbackend/PortalBackend.Migrations/Seeder/Data/identities.json b/src/portalbackend/PortalBackend.Migrations/Seeder/Data/identities.json index aff4a91132..0001c776fe 100644 --- a/src/portalbackend/PortalBackend.Migrations/Seeder/Data/identities.json +++ b/src/portalbackend/PortalBackend.Migrations/Seeder/Data/identities.json @@ -160,5 +160,13 @@ "user_status_id": 1, "user_entity_id": null, "identity_type_id": 2 + }, + { + "id": "b28aaf44-acd6-4b77-879b-398fdec28c8b", + "company_id": "2dc4249f-b5ca-4d42-bef1-7a7a950a4f87", + "date_created": "2024-11-05 18:01:33.439000 +00:00", + "user_status_id": 1, + "user_entity_id": null, + "identity_type_id": 2 } ] diff --git a/src/portalbackend/PortalBackend.Migrations/Seeder/Data/identity_assigned_roles.json b/src/portalbackend/PortalBackend.Migrations/Seeder/Data/identity_assigned_roles.json index 597881f70e..cc553f61e7 100644 --- a/src/portalbackend/PortalBackend.Migrations/Seeder/Data/identity_assigned_roles.json +++ b/src/portalbackend/PortalBackend.Migrations/Seeder/Data/identity_assigned_roles.json @@ -3,5 +3,10 @@ "identity_id": "ac1cf001-7fbc-1f2f-817f-bce058020006", "user_role_id": "58f897ec-0aad-4588-8ffa-5f45d6638632", "last_editor_id": null + }, + { + "identity_id": "b28aaf44-acd6-4b77-879b-398fdec28c8b", + "user_role_id": "c01818be-4978-41f4-bf63-fa6d2de53212", + "last_editor_id": null } ] diff --git a/src/portalbackend/PortalBackend.Migrations/Seeder/Data/technical_users.json b/src/portalbackend/PortalBackend.Migrations/Seeder/Data/technical_users.json index 15197766a5..f4bb8dd5f9 100644 --- a/src/portalbackend/PortalBackend.Migrations/Seeder/Data/technical_users.json +++ b/src/portalbackend/PortalBackend.Migrations/Seeder/Data/technical_users.json @@ -164,5 +164,13 @@ "technical_user_type_id": 2, "technical_user_kind_id": 1, "client_client_id": "sa-cl25-cx-3" + }, + { + "id": "b28aaf44-acd6-4b77-879b-398fdec28c8b", + "name": "sa-cl25-01", + "description": "This client/technical user will be used to communicate between portal and BDRS to store new BPNL/DID connections.", + "technical_user_type_id": 2, + "technical_user_kind_id": 1, + "client_client_id": "sa-cl25-01" } ] diff --git a/src/portalbackend/PortalBackend.Migrations/Seeder/Data/user_role_assigned_collections.json b/src/portalbackend/PortalBackend.Migrations/Seeder/Data/user_role_assigned_collections.json index d47782f948..3590888aa7 100644 --- a/src/portalbackend/PortalBackend.Migrations/Seeder/Data/user_role_assigned_collections.json +++ b/src/portalbackend/PortalBackend.Migrations/Seeder/Data/user_role_assigned_collections.json @@ -246,5 +246,9 @@ { "user_role_collection_id": "a5b8b1de-7759-4620-9c87-6b6d74fb4fbc", "user_role_id": "9956fa8d-e454-49ca-a3b1-45e2c106fe59" + }, + { + "user_role_collection_id": "1a24eca5-901f-4191-84a7-4ef09a894575", + "user_role_id": "c01818be-4978-41f4-bf63-fa6d2de53212" } ] diff --git a/src/portalbackend/PortalBackend.Migrations/Seeder/Data/user_role_descriptions.json b/src/portalbackend/PortalBackend.Migrations/Seeder/Data/user_role_descriptions.json index 1b6de8c287..87501fda6f 100644 --- a/src/portalbackend/PortalBackend.Migrations/Seeder/Data/user_role_descriptions.json +++ b/src/portalbackend/PortalBackend.Migrations/Seeder/Data/user_role_descriptions.json @@ -318,5 +318,15 @@ "user_role_id": "a53a95be-ea7b-4033-926c-26275dc16e0f", "language_short_name": "en", "description": "Reserviere und bearbeitete Golden Record Tasks im Schritt 'Pool'." + }, + { + "user_role_id": "c01818be-4978-41f4-bf63-fa6d2de53212", + "language_short_name": "en", + "description": "Create and manage BPN/DID records inside the service BDRS." + }, + { + "user_role_id": "c01818be-4978-41f4-bf63-fa6d2de53212", + "language_short_name": "de", + "description": "Erstellen und verwalten Sie BPN/DID-Datensätze im BDRS Service." } ] diff --git a/src/portalbackend/PortalBackend.Migrations/Seeder/Data/user_roles.json b/src/portalbackend/PortalBackend.Migrations/Seeder/Data/user_roles.json index 37514c0f26..fca9155839 100644 --- a/src/portalbackend/PortalBackend.Migrations/Seeder/Data/user_roles.json +++ b/src/portalbackend/PortalBackend.Migrations/Seeder/Data/user_roles.json @@ -196,5 +196,11 @@ "user_role": "BPDM Orchestrator Processor PoolSync", "offer_id": "0ffcb416-1101-4ba6-8d4a-a9dfa31745a4", "last_editor_id": null + }, + { + "id": "c01818be-4978-41f4-bf63-fa6d2de53212", + "user_role": "BDRS Management", + "offer_id": "0ffcb416-1101-4ba6-8d4a-a9dfa31745a4", + "last_editor_id": null } ] diff --git a/src/processes/Processes.Worker/appsettings.json b/src/processes/Processes.Worker/appsettings.json index a30cff5fa1..0a0b7f3384 100644 --- a/src/processes/Processes.Worker/appsettings.json +++ b/src/processes/Processes.Worker/appsettings.json @@ -371,8 +371,14 @@ ] }, "BpnDidResolver": { - "BaseAddress": "", - "ApiKey": "" + "Username": "", + "Password": "", + "ClientId": "", + "GrantType": "", + "ClientSecret": "", + "Scope": "", + "TokenAddress": "", + "BaseAddress": "" } }, "Processes": { diff --git a/tests/externalsystems/BpnDidResolver.Library.Tests/BpnDidResolveServiceTests.cs b/tests/externalsystems/BpnDidResolver.Library.Tests/BpnDidResolveServiceTests.cs index 1831142cb2..c6cf9da126 100644 --- a/tests/externalsystems/BpnDidResolver.Library.Tests/BpnDidResolveServiceTests.cs +++ b/tests/externalsystems/BpnDidResolver.Library.Tests/BpnDidResolveServiceTests.cs @@ -17,9 +17,14 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +using Microsoft.Extensions.Options; +using Org.Eclipse.TractusX.Portal.Backend.BpnDidResolver.Library.DependencyInjection; +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; using Org.Eclipse.TractusX.Portal.Backend.Framework.Tests.Shared; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Token; using Org.Eclipse.TractusX.Portal.Backend.Tests.Shared.Extensions; using System.Net; +using System.Text.Json; namespace Org.Eclipse.TractusX.Portal.Backend.BpnDidResolver.Library.Tests; @@ -27,16 +32,26 @@ public class BpnDidResolveServiceTests { private const string BPN = "BPNL0000000000XX"; private readonly IBpnDidResolverService _sut; - private readonly IHttpClientFactory _clientFactory; + private readonly ITokenService _tokenService; + private readonly IOptions _options; public BpnDidResolveServiceTests() { var fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true }); fixture.ConfigureFixture(); - - _clientFactory = A.Fake(); - - _sut = new BpnDidResolverService(_clientFactory); + _tokenService = A.Fake(); + _options = Options.Create(new BpnDidResolverSettings + { + Password = "passWord", + Scope = "test", + Username = "user@name", + BaseAddress = "https://base.address.com", + ClientId = "CatenaX", + ClientSecret = "pass@Secret", + GrantType = "cred", + TokenAddress = "https://key.cloak.com" + }); + _sut = new BpnDidResolverService(_tokenService, _options); } #region ValidateDid @@ -51,7 +66,8 @@ public async Task ValidateDid_WithoutError_ReturnsTrue() { BaseAddress = new Uri("https://base.address.com") }; - A.CallTo(() => _clientFactory.CreateClient(nameof(BpnDidResolverService))).Returns(httpClient); + A.CallTo(() => _tokenService.GetAuthorizedClient(_options.Value, A._)) + .Returns(httpClient); // Act var result = await _sut.TransmitDidAndBpn(did, BPN, CancellationToken.None); @@ -70,13 +86,15 @@ public async Task ValidateDid_WithError_ReturnsFalse() { BaseAddress = new Uri("https://base.address.com") }; - A.CallTo(() => _clientFactory.CreateClient(nameof(BpnDidResolverService))).Returns(httpClient); + A.CallTo(() => _tokenService.GetAuthorizedClient(_options.Value, A._)) + .Returns(httpClient); // Act - var result = await _sut.TransmitDidAndBpn(did, BPN, CancellationToken.None); + async Task Act() => await _sut.TransmitDidAndBpn(did, BPN, CancellationToken.None); // Assert - result.Should().BeFalse(); + var ex = await Assert.ThrowsAsync(Act); + ex.StatusCode.Should().Be(HttpStatusCode.BadRequest); } #endregion diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRepositoryTests.cs b/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRepositoryTests.cs index acdad34cec..6837754b44 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRepositoryTests.cs +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRepositoryTests.cs @@ -688,7 +688,7 @@ public async Task GetCompanyIdAndBpnForIamUserUntrackedAsync_WithValidData_Retur // Assert result.Should().NotBe(default); result.Bpn.Should().Be("BPNL00000003CRHK"); - result.TechnicalUserRoleIds.Should().HaveCount(19).And.OnlyHaveUniqueItems(); + result.TechnicalUserRoleIds.Should().HaveCount(20).And.OnlyHaveUniqueItems(); } #endregion diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRoleCollectionRolesViewTests.cs b/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRoleCollectionRolesViewTests.cs index a55d7a2a54..94a1fc08c6 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRoleCollectionRolesViewTests.cs +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyRoleCollectionRolesViewTests.cs @@ -45,7 +45,7 @@ public async Task CompanyRoleCollectionRolesView_GetAll_ReturnsExpected() // Act var result = await sut.CompanyRoleCollectionRolesView.ToListAsync(); - result.Should().HaveCount(62); + result.Should().HaveCount(63); } [Fact] diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/TechnicalUserRepositoryTests.cs b/tests/portalbackend/PortalBackend.DBAccess.Tests/TechnicalUserRepositoryTests.cs index 9a2ffbaaa2..0da0bba15f 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/TechnicalUserRepositoryTests.cs +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/TechnicalUserRepositoryTests.cs @@ -292,7 +292,7 @@ public async Task GetOwnCompanyServiceAccountsUntracked_WithOwnerTrue_ReturnsExp // Assert result.Should().NotBeNull(); - result!.Count.Should().Be(21); + result!.Count.Should().Be(22); result.Data.Should().HaveCount(10) .And.AllSatisfy(x => x.Should().Match(y => y.TechnicalUserTypeId == TechnicalUserTypeId.OWN && @@ -371,7 +371,7 @@ public async Task GetOwnCompanyServiceAccountsUntracked_WithSearch_ReturnsExpect // Assert result.Should().NotBeNull(); - result!.Count.Should().Be(18); + result!.Count.Should().Be(19); result.Data.Should().HaveCount(10); } diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/UserRolesRepositoryTests.cs b/tests/portalbackend/PortalBackend.DBAccess.Tests/UserRolesRepositoryTests.cs index e0836f3e2b..4949ced6d6 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/UserRolesRepositoryTests.cs +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/UserRolesRepositoryTests.cs @@ -57,7 +57,7 @@ public async Task GetCoreOfferRolesAsync_WithValidData_ReturnsExpected() var data = await sut.GetCoreOfferRolesAsync(_validCompanyId, "en", ClientId).ToListAsync(); // Assert - data.Should().HaveCount(19); + data.Should().HaveCount(20); } #endregion @@ -110,9 +110,9 @@ public async Task GetServiceAccountRolesAsync_WithValidData_ReturnsExpected() var data = await sut.GetServiceAccountRolesAsync(_validCompanyId, ClientId, Enumerable.Repeat(new Guid("607818be-4978-41f4-bf63-fa8d2de51157"), 1), Constants.DefaultLanguage).ToListAsync(); // Assert - data.Should().HaveCount(19); + data.Should().HaveCount(20); data.Should().OnlyHaveUniqueItems(); - data.Where(x => !x.External).Should().HaveCount(18); + data.Where(x => !x.External).Should().HaveCount(19); data.Where(x => x.External).Should().ContainSingle(); }