Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(BpnDidService): Transition from API Key to Technical User Authentication for BDRS Integration (#59) #1129

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 };

/// <summary>
/// Creates a new instance of <see cref="BpnDidResolverService"/>
/// </summary>
/// <param name="tokenService"></param>
/// <param name="options"></param>
public BpnDidResolverService(ITokenService tokenService, IOptions<BpnDidResolverSettings> options)
{
_tokenService = tokenService;
_settings = options.Value;
}

public async Task<bool> TransmitDidAndBpn(string did, string bpn, CancellationToken cancellationToken)
{
using var httpClient = httpClientFactory.CreateClient(nameof(BpnDidResolverService));
using var httpClient = await _tokenService.GetAuthorizedClient<BpnDidResolverService>(_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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,9 @@ public static IServiceCollection AddBpnDidResolver(this IServiceCollection servi

var sp = services.BuildServiceProvider();
var settings = sp.GetRequiredService<IOptions<BpnDidResolverSettings>>();
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<BpnDidResolverService>(baseAddress.EndsWith('/') ? baseAddress : $"{baseAddress}/")
.AddTransient<IBpnDidResolverService, BpnDidResolverService>()
.AddTransient<IBpnDidResolverBusinessLogic, BpnDidResolverBusinessLogic>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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!;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -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."
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
]
10 changes: 8 additions & 2 deletions src/processes/Processes.Worker/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,14 @@
]
},
"BpnDidResolver": {
"BaseAddress": "",
"ApiKey": ""
"Username": "",
"Password": "",
"ClientId": "",
"GrantType": "",
"ClientSecret": "",
"Scope": "",
"TokenAddress": "",
"BaseAddress": ""
}
},
"Processes": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,41 @@
* 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;

public class BpnDidResolveServiceTests
{
private const string BPN = "BPNL0000000000XX";
private readonly IBpnDidResolverService _sut;
private readonly IHttpClientFactory _clientFactory;
private readonly ITokenService _tokenService;
private readonly IOptions<BpnDidResolverSettings> _options;

public BpnDidResolveServiceTests()
{
var fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true });
fixture.ConfigureFixture();

_clientFactory = A.Fake<IHttpClientFactory>();

_sut = new BpnDidResolverService(_clientFactory);
_tokenService = A.Fake<ITokenService>();
_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
Expand All @@ -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<BpnDidResolverService>(_options.Value, A<CancellationToken>._))
.Returns(httpClient);

// Act
var result = await _sut.TransmitDidAndBpn(did, BPN, CancellationToken.None);
Expand All @@ -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<BpnDidResolverService>(_options.Value, A<CancellationToken>._))
.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<ServiceException>(Act);
ex.StatusCode.Should().Be(HttpStatusCode.BadRequest);
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<CompanyServiceAccountData>(y =>
y.TechnicalUserTypeId == TechnicalUserTypeId.OWN &&
Expand Down Expand Up @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
}

Expand Down
Loading