Skip to content

Commit

Permalink
feat(bpn): allow lowercase bpn (#400)
Browse files Browse the repository at this point in the history
Refs: CPLP-3440
Co-authored-by: Phil Schneider <[email protected]>
Reviewed-by: Phil Schneider <[email protected]>
  • Loading branch information
AnuragNagpure and Phil91 authored Jan 31, 2024
1 parent e0bf03f commit d5d23bf
Show file tree
Hide file tree
Showing 9 changed files with 34 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ public IAsyncEnumerable<ConnectorEndPointData> GetCompanyConnectorEndPointAsync(
}

return _portalRepositories.GetInstance<IConnectorsRepository>()
.GetConnectorEndPointDataAsync(bpns)
.GetConnectorEndPointDataAsync(bpns.Select(x => x.ToUpper()))
.PreSortedGroupBy(data => data.BusinessPartnerNumber)
.Select(group =>
new ConnectorEndPointData(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLog

public sealed class RegistrationBusinessLogic : IRegistrationBusinessLogic
{
private static readonly Regex BpnRegex = new(@"(\w|\d){16}", RegexOptions.Compiled, TimeSpan.FromSeconds(1));
private static readonly Regex BpnRegex = new(ValidationExpressions.Bpn, RegexOptions.Compiled, TimeSpan.FromSeconds(1));

private readonly IPortalRepositories _portalRepositories;
private readonly RegistrationSettings _settings;
Expand Down Expand Up @@ -215,7 +215,7 @@ public Task UpdateCompanyBpn(Guid applicationId, string bpn)
private async Task UpdateCompanyBpnInternal(Guid applicationId, string bpn)
{
var result = await _portalRepositories.GetInstance<IUserRepository>()
.GetBpnForIamUserUntrackedAsync(applicationId, bpn).ToListAsync().ConfigureAwait(false);
.GetBpnForIamUserUntrackedAsync(applicationId, bpn.ToUpper()).ToListAsync().ConfigureAwait(false);
if (!result.Exists(item => item.IsApplicationCompany))
{
throw new NotFoundException($"application {applicationId} not found");
Expand Down Expand Up @@ -262,7 +262,7 @@ private async Task UpdateCompanyBpnInternal(Guid applicationId, string bpn)
.ConfigureAwait(false);

_portalRepositories.GetInstance<ICompanyRepository>().AttachAndModifyCompany(applicationCompanyData.CompanyId, null,
c => { c.BusinessPartnerNumber = bpn; });
c => { c.BusinessPartnerNumber = bpn.ToUpper(); });

var registrationValidationFailed = context.Checklist[ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION] == new ValueTuple<ApplicationChecklistEntryStatusId, string?>(ApplicationChecklistEntryStatusId.FAILED, null);

Expand Down Expand Up @@ -290,7 +290,7 @@ private async Task UpdateCompanyBpnInternal(Guid applicationId, string bpn)
public async Task ProcessClearinghouseResponseAsync(ClearinghouseResponseData data, CancellationToken cancellationToken)
{
_logger.LogInformation("Process SelfDescription called with the following data {Data}", data);
var result = await _portalRepositories.GetInstance<IApplicationRepository>().GetSubmittedApplicationIdsByBpn(data.BusinessPartnerNumber).ToListAsync(cancellationToken).ConfigureAwait(false);
var result = await _portalRepositories.GetInstance<IApplicationRepository>().GetSubmittedApplicationIdsByBpn(data.BusinessPartnerNumber.ToUpper()).ToListAsync(cancellationToken).ConfigureAwait(false);
if (!result.Any())
{
throw new NotFoundException($"No companyApplication for BPN {data.BusinessPartnerNumber} is not in status SUBMITTED");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library;
using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library.Models;
using Org.Eclipse.TractusX.Portal.Backend.Provisioning.Library.Service;
using System.Text.RegularExpressions;

namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLogic;

Expand All @@ -40,6 +41,7 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLog
/// </summary>
public class UserBusinessLogic : IUserBusinessLogic
{
private static readonly Regex BpnRegex = new(ValidationExpressions.Bpn, RegexOptions.Compiled, TimeSpan.FromSeconds(1));
private readonly IProvisioningManager _provisioningManager;
private readonly IUserProvisioningService _userProvisioningService;
private readonly IProvisioningDBAccess _provisioningDbAccess;
Expand Down Expand Up @@ -315,9 +317,9 @@ await Task.WhenAll(details.IdpUserIds.Select(async x =>

public async Task<int> AddOwnCompanyUsersBusinessPartnerNumbersAsync(Guid userId, IEnumerable<string> businessPartnerNumbers)
{
if (businessPartnerNumbers.Any(businessPartnerNumber => businessPartnerNumber.Length > 20))
if (businessPartnerNumbers.Any(bpn => !BpnRegex.IsMatch(bpn)))
{
throw new ControllerArgumentException("businessPartnerNumbers must not exceed 20 characters", nameof(businessPartnerNumbers));
throw new ControllerArgumentException("BPN must contain exactly 16 characters and must be prefixed with BPNL", nameof(businessPartnerNumbers));
}

var companyId = _identityData.CompanyId;
Expand All @@ -332,7 +334,7 @@ public async Task<int> AddOwnCompanyUsersBusinessPartnerNumbersAsync(Guid userId
await _provisioningManager.AddBpnAttributetoUserAsync(iamUserId, businessPartnerNumbers).ConfigureAwait(false);
foreach (var businessPartnerToAdd in businessPartnerNumbers.Except(assignedBusinessPartnerNumbers))
{
businessPartnerRepository.CreateCompanyUserAssignedBusinessPartner(userId, businessPartnerToAdd);
businessPartnerRepository.CreateCompanyUserAssignedBusinessPartner(userId, businessPartnerToAdd.ToUpper());
}

return await _portalRepositories.SaveAsync();
Expand Down Expand Up @@ -579,7 +581,7 @@ public async Task<int> DeleteOwnUserBusinessPartnerNumbersAsync(Guid userId, str
{
var userBusinessPartnerRepository = _portalRepositories.GetInstance<IUserBusinessPartnerRepository>();

var (isValidUser, isAssignedBusinessPartner, isSameCompany) = await userBusinessPartnerRepository.GetOwnCompanyUserWithAssignedBusinessPartnerNumbersAsync(userId, _identityData.CompanyId, businessPartnerNumber).ConfigureAwait(false);
var (isValidUser, isAssignedBusinessPartner, isSameCompany) = await userBusinessPartnerRepository.GetOwnCompanyUserWithAssignedBusinessPartnerNumbersAsync(userId, _identityData.CompanyId, businessPartnerNumber.ToUpper()).ConfigureAwait(false);

if (!isValidUser)
{
Expand All @@ -598,9 +600,9 @@ public async Task<int> DeleteOwnUserBusinessPartnerNumbersAsync(Guid userId, str

var iamUserId = await _provisioningManager.GetUserByUserName(userId.ToString()).ConfigureAwait(false) ?? throw new ConflictException($"user {userId} is not associated with a user in keycloak");

userBusinessPartnerRepository.DeleteCompanyUserAssignedBusinessPartner(userId, businessPartnerNumber);
userBusinessPartnerRepository.DeleteCompanyUserAssignedBusinessPartner(userId, businessPartnerNumber.ToUpper());

await _provisioningManager.DeleteCentralUserBusinessPartnerNumberAsync(iamUserId, businessPartnerNumber).ConfigureAwait(false);
await _provisioningManager.DeleteCentralUserBusinessPartnerNumberAsync(iamUserId, businessPartnerNumber.ToUpper()).ConfigureAwait(false);

return await _portalRepositories.SaveAsync().ConfigureAwait(false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ public Task<IEnumerable<UserRoleWithId>> ModifyAppUserRolesAsync([FromRoute] Gui
/// <param name="companyUserId" example="ac1cf001-7fbc-1f2f-817f-bce0575a0011">Id of the user to add the business partner numbers to.</param>
/// <param name="businessPartnerNumbers">the business partner numbers that should be added.</param>
/// <returns></returns>
/// <remarks>Example: POST: api/administration/user/owncompany/users/ac1cf001-7fbc-1f2f-817f-bce0575a0011/businessPartnerNumbers</remarks>
/// <remarks>Example: POST: api/administration/user/owncompany/users/{companyUserId}/businessPartnerNumbers</remarks>
/// <response code="200">The business partner numbers have been added successfully.</response>
/// <response code="400">Business Partner Numbers must not exceed 20 characters.</response>
/// <response code="404">User not found.</response>
Expand All @@ -269,7 +269,7 @@ public Task<int> AddOwnCompanyUserBusinessPartnerNumbers(Guid companyUserId, IEn
/// <param name="companyUserId" example="ac1cf001-7fbc-1f2f-817f-bce0575a0011">Id of the user to add the business partner numbers to.</param>
/// <param name="businessPartnerNumber" example="CAXSDUMMYCATENAZZ">the business partner number that should be added.</param>
/// <returns></returns>
/// <remarks>Example: PUT: api/administration/user/owncompany/users/ac1cf001-7fbc-1f2f-817f-bce0575a0011/businessPartnerNumbers/CAXSDUMMYCATENAZZ</remarks>
/// <remarks>Example: PUT: api/administration/user/owncompany/users/{companyUserId}/businessPartnerNumbers/{businessPartnerNumber}</remarks>
/// <response code="200">The business partner number have been added successfully.</response>
/// <response code="400">Business Partner Numbers must not exceed 20 characters.</response>
/// <response code="404">User is not existing.</response>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public void CreateUpdateDeleteIdentifiers(Guid companyId, IEnumerable<(UniqueIde
_context.Companies
.AsNoTracking()
.Where(company => company.CompanyStatusId == CompanyStatusId.ACTIVE &&
(bpnIds == null || bpnIds.Contains(company.BusinessPartnerNumber) &&
(bpnIds == null || bpnIds.Select(x => x.ToUpper()).Contains(company.BusinessPartnerNumber) &&
company.BusinessPartnerNumber != null))
.Select(company => company.BusinessPartnerNumber)
.AsAsyncEnumerable();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public Task<CompanyBpdmDetailData> GetCompanyBpdmDetailDataByBusinessPartnerNumb
{
throw new ControllerArgumentException("BPN must contain exactly 16 digits or letters.", nameof(businessPartnerNumber));
}
return GetCompanyBpdmDetailDataByBusinessPartnerNumberInternal(businessPartnerNumber, token, cancellationToken);
return GetCompanyBpdmDetailDataByBusinessPartnerNumberInternal(businessPartnerNumber.ToUpper(), token, cancellationToken);
}

private async Task<CompanyBpdmDetailData> GetCompanyBpdmDetailDataByBusinessPartnerNumberInternal(string businessPartnerNumber, string token, CancellationToken cancellationToken)
Expand Down Expand Up @@ -376,15 +376,15 @@ private static void ModifyCompany(Guid addressId, CompanyApplicationDetailData i
modifyData.CompanyId,
c =>
{
c.BusinessPartnerNumber = initialData.BusinessPartnerNumber;
c.BusinessPartnerNumber = initialData.BusinessPartnerNumber?.ToUpper();
c.Name = initialData.Name;
c.Shortname = initialData.ShortName;
c.CompanyStatusId = initialData.CompanyStatusId;
c.AddressId = initialData.AddressId;
},
c =>
{
c.BusinessPartnerNumber = modifyData.BusinessPartnerNumber;
c.BusinessPartnerNumber = modifyData.BusinessPartnerNumber?.ToUpper();
c.Name = modifyData.Name;
c.Shortname = modifyData.ShortName;
c.CompanyStatusId = CompanyStatusId.PENDING;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -998,31 +998,31 @@ public async Task GetCompanyConnectorEndPoint_WithValidData_ReturnsExpectedResul
//Arrange
var bpns = new[]
{
"BPNL00000002CRHL",
"bpnL00000002CRHL",
"BPNL00000003CRHL",
"BPNL00000004CRHL",
"BPNL00000003CRHK"
};
A.CallTo(() => _connectorsRepository.GetConnectorEndPointDataAsync(bpns))
A.CallTo(() => _connectorsRepository.GetConnectorEndPointDataAsync(A<IEnumerable<string>>._))
.Returns(new[] {
(BusinessPartnerNumber: "BPNL00000002CRHL", ConnectorEndPoint: "www.googlr5.com"),
(BusinessPartnerNumber: "BPNL00000003CRHL", ConnectorEndPoint: "www.googlr0.com"),
(BusinessPartnerNumber: "BPNL00000003CRHL", ConnectorEndPoint: "www.googlr1.com"),
(BusinessPartnerNumber: "BPNL00000004CRHL", ConnectorEndPoint: "www.googlr2.com"),
(BusinessPartnerNumber: "BPNL00000004CRHL", ConnectorEndPoint: "www.googlr3.com"),
(BusinessPartnerNumber: "BPNL00000004CRHL", ConnectorEndPoint: "www.googlr4.com"),
(BusinessPartnerNumber: "BPNL00000002CRHL", ConnectorEndPoint: "www.googlr5.com")
(BusinessPartnerNumber: "BPNL00000004CRHL", ConnectorEndPoint: "www.googlr4.com")
}.ToAsyncEnumerable());

//Act
var result = await _logic.GetCompanyConnectorEndPointAsync(bpns).ToListAsync().ConfigureAwait(false);

//Assert
A.CallTo(() => _connectorsRepository.GetConnectorEndPointDataAsync(bpns)).MustHaveHappenedOnceExactly();
A.CallTo(() => _connectorsRepository.GetConnectorEndPointDataAsync(A<IEnumerable<string>>.That.IsSameSequenceAs(bpns.Select(x => x.ToUpper())))).MustHaveHappenedOnceExactly();
result.Should().HaveCount(3).And.Satisfy(
x => x.Bpn == "BPNL00000002CRHL" && x.ConnectorEndpoint.Count() == 1 && x.ConnectorEndpoint.Contains("www.googlr5.com"),
x => x.Bpn == "BPNL00000003CRHL" && x.ConnectorEndpoint.Count() == 2 && x.ConnectorEndpoint.Contains("www.googlr0.com") && x.ConnectorEndpoint.Contains("www.googlr1.com"),
x => x.Bpn == "BPNL00000004CRHL" && x.ConnectorEndpoint.Count() == 3 && x.ConnectorEndpoint.Contains("www.googlr2.com") && x.ConnectorEndpoint.Contains("www.googlr3.com") && x.ConnectorEndpoint.Contains("www.googlr4.com")
);
x => x.Bpn == "BPNL00000002CRHL" && x.ConnectorEndpoint.Count() == 1 && x.ConnectorEndpoint.Contains("www.googlr5.com"),
x => x.Bpn == "BPNL00000003CRHL" && x.ConnectorEndpoint.Count() == 2 && x.ConnectorEndpoint.Contains("www.googlr0.com") && x.ConnectorEndpoint.Contains("www.googlr1.com"),
x => x.Bpn == "BPNL00000004CRHL" && x.ConnectorEndpoint.Count() == 3 && x.ConnectorEndpoint.Contains("www.googlr2.com") && x.ConnectorEndpoint.Contains("www.googlr3.com") && x.ConnectorEndpoint.Contains("www.googlr4.com")
);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,21 +267,6 @@ public async Task UpdateCompanyBpnAsync_WithInvalidBpn_ThrowsControllerArgumentE
ex.Message.Should().Be("BPN must contain exactly 16 characters long. (Parameter 'bpn')");
}

[Fact]
public async Task UpdateCompanyBpnAsync_WithInvalidBpnPrefix_ThrowsControllerArgumentException()
{
// Arrange
var bpn = "BPXX123698762345";

// Act
async Task Act() => await _logic.UpdateCompanyBpn(IdWithBpn, bpn).ConfigureAwait(false);

// Assert
var ex = await Assert.ThrowsAsync<ControllerArgumentException>(Act);
ex.ParamName.Should().Be("bpn");
ex.Message.Should().Be("businessPartnerNumbers must prefixed with BPNL (Parameter 'bpn')");
}

[Fact]
public async Task UpdateCompanyBpnAsync_WithNotExistingApplication_ThrowsNotFoundException()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1400,7 +1400,7 @@ public async Task GetOwnCompanyAppUsersAsync_WithUnassignedBusinessPartner_Throw
var businessPartnerNumber = _fixture.Create<string>();
A.CallTo(() => _identity.IdentityId).Returns(_adminUserId);
A.CallTo(() => _identity.CompanyId).Returns(_adminCompanyId);
A.CallTo(() => _userBusinessPartnerRepository.GetOwnCompanyUserWithAssignedBusinessPartnerNumbersAsync(companyUserId, _adminCompanyId, businessPartnerNumber))
A.CallTo(() => _userBusinessPartnerRepository.GetOwnCompanyUserWithAssignedBusinessPartnerNumbersAsync(companyUserId, _adminCompanyId, businessPartnerNumber.ToUpper()))
.Returns((true, false, false));
A.CallTo(() => _portalRepositories.GetInstance<IUserBusinessPartnerRepository>()).Returns(_userBusinessPartnerRepository);
var sut = new UserBusinessLogic(null!, null!, null!, _portalRepositories, _identityService, null!, null!, A.Fake<IOptions<UserSettings>>());
Expand All @@ -1423,7 +1423,7 @@ public async Task GetOwnCompanyAppUsersAsync_WithoutUserForBpn_ThrowsArgumentExc
A.CallTo(() => _identity.CompanyId).Returns(_adminCompanyId);
A.CallTo(() => _provisioningManager.GetUserByUserName(companyUserId.ToString()))
.Returns((string?)null);
A.CallTo(() => _userBusinessPartnerRepository.GetOwnCompanyUserWithAssignedBusinessPartnerNumbersAsync(companyUserId, _adminCompanyId, businessPartnerNumber))
A.CallTo(() => _userBusinessPartnerRepository.GetOwnCompanyUserWithAssignedBusinessPartnerNumbersAsync(companyUserId, _adminCompanyId, businessPartnerNumber.ToUpper()))
.Returns((true, true, true));
A.CallTo(() => _portalRepositories.GetInstance<IUserBusinessPartnerRepository>()).Returns(_userBusinessPartnerRepository);
var sut = new UserBusinessLogic(_provisioningManager, null!, null!, _portalRepositories, _identityService, null!, null!, A.Fake<IOptions<UserSettings>>());
Expand All @@ -1444,7 +1444,7 @@ public async Task GetOwnCompanyAppUsersAsync_WithInvalidUser_ThrowsForbiddenExce
var businessPartnerNumber = _fixture.Create<string>();
A.CallTo(() => _identity.IdentityId).Returns(_adminUserId);
A.CallTo(() => _identity.CompanyId).Returns(_adminCompanyId);
A.CallTo(() => _userBusinessPartnerRepository.GetOwnCompanyUserWithAssignedBusinessPartnerNumbersAsync(companyUserId, _adminCompanyId, businessPartnerNumber))
A.CallTo(() => _userBusinessPartnerRepository.GetOwnCompanyUserWithAssignedBusinessPartnerNumbersAsync(companyUserId, _adminCompanyId, businessPartnerNumber.ToUpper()))
.Returns((true, true, false));
A.CallTo(() => _portalRepositories.GetInstance<IUserBusinessPartnerRepository>()).Returns(_userBusinessPartnerRepository);
var sut = new UserBusinessLogic(null!, null!, null!, _portalRepositories, _identityService, null!, null!, A.Fake<IOptions<UserSettings>>());
Expand All @@ -1466,17 +1466,17 @@ public async Task GetOwnCompanyAppUsersAsync_WithValidData_ThrowsForbiddenExcept
var businessPartnerNumber = _fixture.Create<string>();
A.CallTo(() => _identity.IdentityId).Returns(_adminUserId);
A.CallTo(() => _identity.CompanyId).Returns(_adminCompanyId);
A.CallTo(() => _userBusinessPartnerRepository.GetOwnCompanyUserWithAssignedBusinessPartnerNumbersAsync(companyUserId, _adminCompanyId, businessPartnerNumber))
A.CallTo(() => _userBusinessPartnerRepository.GetOwnCompanyUserWithAssignedBusinessPartnerNumbersAsync(companyUserId, _adminCompanyId, businessPartnerNumber.ToUpper()))
.Returns((true, true, true));
A.CallTo(() => _portalRepositories.GetInstance<IUserBusinessPartnerRepository>()).Returns(_userBusinessPartnerRepository);
A.CallTo(() => _provisioningManager.GetUserByUserName(companyUserId.ToString())).Returns(iamUserId);
var sut = new UserBusinessLogic(_provisioningManager, null!, null!, _portalRepositories, _identityService, null!, null!, A.Fake<IOptions<UserSettings>>());

// Act
await sut.DeleteOwnUserBusinessPartnerNumbersAsync(companyUserId, businessPartnerNumber).ConfigureAwait(false);
await sut.DeleteOwnUserBusinessPartnerNumbersAsync(companyUserId, businessPartnerNumber.ToUpper()).ConfigureAwait(false);

// Assert
A.CallTo(() => _provisioningManager.DeleteCentralUserBusinessPartnerNumberAsync(iamUserId, businessPartnerNumber)).MustHaveHappenedOnceExactly();
A.CallTo(() => _provisioningManager.DeleteCentralUserBusinessPartnerNumberAsync(iamUserId, businessPartnerNumber.ToUpper())).MustHaveHappenedOnceExactly();
A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappenedOnceExactly();
}

Expand Down

0 comments on commit d5d23bf

Please sign in to comment.