From 460d738e49ce7ddcccd1cf6c3247cf4d5d001b3d Mon Sep 17 00:00:00 2001 From: Tayyab Fayyaz Janjua Date: Thu, 6 Feb 2025 14:25:18 +0100 Subject: [PATCH] fix(osp): applied regex validation on uniqueIds param --- .../Framework.Models/ValidationExpressions.cs | 5 +++ .../RegistrationValidation.cs | 25 +++++++++++++++ .../NetworkBusinessLogicTests.cs | 31 +++++++++++++++++++ .../RegistrationBusinessLogicTest.cs | 26 +++++++++++++--- 4 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/framework/Framework.Models/ValidationExpressions.cs b/src/framework/Framework.Models/ValidationExpressions.cs index b16a630321..4c4549b84a 100644 --- a/src/framework/Framework.Models/ValidationExpressions.cs +++ b/src/framework/Framework.Models/ValidationExpressions.cs @@ -36,4 +36,9 @@ public static class ValidationExpressions /// public const string Company = @"^(?!.*\s$)([\p{L}\u0E00-\u0E7F\d\p{Sc}@%*+_\-/\\,.:;=<>!?&^#'\x22()[\]]\s?){1,160}$"; public const string ExternalCertificateNumber = @"^[a-zA-Z0-9]{0,36}$"; + public const string COMMERCIAL_REG_NUMBER = "^(?!.*\\s$)([A-Za-z0-9](\\.|\\s|-)?){4,21}$"; + public const string VAT_ID = "^(?!.*\\s$)([A-Za-z0-9](\\.|\\s|-|\\/)?){5,18}$"; + public const string LEI_CODE = "^[A-Za-z0-9]{20}$"; + public const string VIES = "^[A-Z]{2}[0-9A-Za-z+*.]{2,12}$"; + public const string EORI = "^[A-Z]{2}[A-Za-z0-9]{1,15}$"; } diff --git a/src/registration/Registration.Common/RegistrationValidation.cs b/src/registration/Registration.Common/RegistrationValidation.cs index 5a4653d517..c9edd2eab5 100644 --- a/src/registration/Registration.Common/RegistrationValidation.cs +++ b/src/registration/Registration.Common/RegistrationValidation.cs @@ -18,6 +18,7 @@ ********************************************************************************/ using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Linq; using Org.Eclipse.TractusX.Portal.Backend.Framework.Models; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; using Org.Eclipse.TractusX.Portal.Backend.Registration.Common.ErrorHandling; @@ -28,6 +29,11 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Registration.Common; public static class RegistrationValidation { private static readonly Regex BpnRegex = new(ValidationExpressions.Bpn, RegexOptions.Compiled, TimeSpan.FromSeconds(1)); + private static readonly Regex CommercialRegNumRegex = new(ValidationExpressions.COMMERCIAL_REG_NUMBER, RegexOptions.Compiled, TimeSpan.FromSeconds(1)); + private static readonly Regex VatIdRegex = new(ValidationExpressions.VAT_ID, RegexOptions.Compiled, TimeSpan.FromSeconds(1)); + private static readonly Regex LeiCodeRegex = new(ValidationExpressions.LEI_CODE, RegexOptions.Compiled, TimeSpan.FromSeconds(1)); + private static readonly Regex ViesRegex = new(ValidationExpressions.VIES, RegexOptions.Compiled, TimeSpan.FromSeconds(1)); + private static readonly Regex EoriRegex = new(ValidationExpressions.EORI, RegexOptions.Compiled, TimeSpan.FromSeconds(1)); public static void ValidateData(this RegistrationData data) { @@ -72,6 +78,14 @@ public static void ValidateData(this RegistrationData data) RegistrationValidationErrors.UNIQUE_IDS_NO_DUPLICATE_VALUES, Enumerable.Repeat(new ErrorParameter("duplicateValues", string.Join(", ", duplicateIds.Select(uniqueId => uniqueId.UniqueIdentifierId))), 1)); } + + data.UniqueIds.Where(uniqueId => IsInvalidValueByUniqueIdentifier(uniqueId.Value, uniqueId.UniqueIdentifierId)) + .IfAny(invalidUniqueIdentifiersValues => + { + throw new ControllerArgumentException( + $"Invalid value of uniqueIds: '{string.Join(", ", invalidUniqueIdentifiersValues.Select(uniqueId => uniqueId.UniqueIdentifierId))}'", + nameof(data.UniqueIds)); + }); } public static async Task ValidateDatabaseData(this RegistrationData data, Func> checkBpn, Func> checkCountryExistByAlpha2Code, Func, Task<(bool IsValidCountry, IEnumerable UniqueIdentifierIds)>> getCountryAssignedIdentifiers, bool checkBpnAlreadyExists) @@ -116,4 +130,15 @@ public static async Task ValidateDatabaseData(this RegistrationData data, Func + uniqueIdentifierId switch + { + UniqueIdentifierId.COMMERCIAL_REG_NUMBER => !CommercialRegNumRegex.IsMatch(value), + UniqueIdentifierId.VAT_ID => !VatIdRegex.IsMatch(value), + UniqueIdentifierId.LEI_CODE => !LeiCodeRegex.IsMatch(value), + UniqueIdentifierId.VIES => !ViesRegex.IsMatch(value), + UniqueIdentifierId.EORI => !EoriRegex.IsMatch(value), + _ => throw new ControllerArgumentException($"Unique identifier: {uniqueIdentifierId} is not available in the system", nameof(uniqueIdentifierId)) + }; } diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/NetworkBusinessLogicTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/NetworkBusinessLogicTests.cs index db47559033..37b2cd2bec 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/NetworkBusinessLogicTests.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/NetworkBusinessLogicTests.cs @@ -47,6 +47,7 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Tests.Busin public class NetworkBusinessLogicTests { private const string Bpn = "BPNL00000001TEST"; + private const string VatId = "DE123456789"; private static readonly string ExistingExternalId = Guid.NewGuid().ToString(); private static readonly Guid CompanyId = new("95c4339e-e087-4cd2-a5b8-44d385e64630"); private static readonly Guid UserRoleId = Guid.NewGuid(); @@ -139,6 +140,25 @@ public async Task HandlePartnerRegistration_WithInvalidBusinessPartnerNumber_Thr ex.Message.Should().Be(RegistrationValidationErrors.BPN_INVALID.ToString()); } + [Fact] + public async Task HandlePartnerRegistration_WithInvalidUniqueId_ThrowsControllerArgumentException() + { + // Arrange + var data = _fixture.Build() + .With(x => x.BusinessPartnerNumber, Bpn) + .With(x => x.CountryAlpha2Code, "DE") + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, "123"), new CompanyUniqueIdData(UniqueIdentifierId.COMMERCIAL_REG_NUMBER, "12")]) + .Create(); + + // Act + async Task Act() => await _sut.HandlePartnerRegistration(data); + + // Assert + var ex = await Assert.ThrowsAsync(Act); + ex.Message.Should().Be("Invalid value of uniqueIds: 'VAT_ID, COMMERCIAL_REG_NUMBER' (Parameter 'UniqueIds')"); + ex.ParamName.Should().Be("UniqueIds"); + } + [Fact] public async Task HandlePartnerRegistration_WithoutExistingBusinessPartnerNumber_ThrowsControllerArgumentException() { @@ -146,6 +166,7 @@ public async Task HandlePartnerRegistration_WithoutExistingBusinessPartnerNumber var data = _fixture.Build() .With(x => x.CountryAlpha2Code, "DE") .With(x => x.BusinessPartnerNumber, "BPNL00000001FAIL") + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, VatId)]) .Create(); // Act @@ -164,6 +185,7 @@ public async Task HandlePartnerRegistration_WithInvalidCompanyUserRole_ThrowsCon .With(x => x.BusinessPartnerNumber, Bpn) .With(x => x.CountryAlpha2Code, "DE") .With(x => x.CompanyRoles, Enumerable.Empty()) + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, VatId)]) .Create(); // Act @@ -185,6 +207,7 @@ public async Task HandlePartnerRegistration_WithInvalidEmail_ThrowsControllerArg .With(x => x.BusinessPartnerNumber, Bpn) .With(x => x.CountryAlpha2Code, "DE") .With(x => x.UserDetails, new[] { new UserDetailData(null, Guid.NewGuid().ToString(), "test", "Test", "test", email) }) + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, VatId)]) .Create(); // Act @@ -204,6 +227,7 @@ public async Task HandlePartnerRegistration_WithInvalidFirstnameEmail_ThrowsCont .With(x => x.BusinessPartnerNumber, Bpn) .With(x => x.CountryAlpha2Code, "DE") .With(x => x.UserDetails, new[] { new UserDetailData(null, Guid.NewGuid().ToString(), "test", firstName, "test", "test@email.com") }) + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, VatId)]) .Create(); // Act @@ -223,6 +247,7 @@ public async Task HandlePartnerRegistration_WithInvalidLastnameEmail_ThrowsContr .With(x => x.BusinessPartnerNumber, Bpn) .With(x => x.CountryAlpha2Code, "DE") .With(x => x.UserDetails, new[] { new UserDetailData(null, Guid.NewGuid().ToString(), "test", "test", lastname, "test@email.com") }) + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, VatId)]) .Create(); // Act @@ -242,6 +267,7 @@ public async Task HandlePartnerRegistration_WithExistingExternalId_ThrowsControl .With(x => x.CountryAlpha2Code, "DE") .With(x => x.UserDetails, new[] { new UserDetailData(null, Guid.NewGuid().ToString(), "test", "test", "test", "test@email.com") }) .With(x => x.ExternalId, ExistingExternalId) + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, VatId)]) .Create(); // Act @@ -261,6 +287,7 @@ public async Task HandlePartnerRegistration_WithInvalidCountryCode_ThrowsControl .With(x => x.BusinessPartnerNumber, Bpn) .With(x => x.UserDetails, new[] { new UserDetailData(null, Guid.NewGuid().ToString(), "test", "test", "test", "test@email.com") }) .With(x => x.CountryAlpha2Code, "XX") + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, VatId)]) .Create(); // Act @@ -280,6 +307,7 @@ public async Task HandlePartnerRegistration_WithNoIdpIdSetAndNoManagedIdps_Throw .With(x => x.BusinessPartnerNumber, Bpn) .With(x => x.CountryAlpha2Code, "DE") .With(x => x.UserDetails, new[] { new UserDetailData(null, "123", "test", "test", "test", "test@email.com") }) + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, VatId)]) .Create(); A.CallTo(() => _identity.CompanyId).Returns(NoIdpCompanyId); @@ -300,6 +328,7 @@ public async Task HandlePartnerRegistration_WithNoIdpIdSetAndMultipleManagedIdps .With(x => x.BusinessPartnerNumber, Bpn) .With(x => x.CountryAlpha2Code, "DE") .With(x => x.UserDetails, new[] { new UserDetailData(null, "123", "test", "test", "test", "test@email.com") }) + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, VatId)]) .Create(); A.CallTo(() => _identity.CompanyId).Returns(MultiIdpCompanyId); @@ -322,6 +351,7 @@ public async Task HandlePartnerRegistration_WithNotExistingIdpIdSet_ThrowsContro .With(x => x.BusinessPartnerNumber, Bpn) .With(x => x.CountryAlpha2Code, "DE") .With(x => x.UserDetails, new[] { new UserDetailData(notExistingIdpId, "123", "test", "test", "test", "test@email.com") }) + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, VatId)]) .Create(); // Act @@ -341,6 +371,7 @@ public async Task HandlePartnerRegistration_WithInvalidInitialRole_ThrowsConfigu .With(x => x.BusinessPartnerNumber, Bpn) .With(x => x.CountryAlpha2Code, "DE") .With(x => x.UserDetails, new[] { new UserDetailData(IdpId, "123", "test", "test", "test", "test@email.com") }) + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, VatId)]) .Create(); A.CallTo(() => _userProvisioningService.GetRoleDatas(A>._)) .Throws(new ControllerArgumentException($"invalid roles: clientId: 'cl1', roles: [Company Admin]")); diff --git a/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs b/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs index 0a9464df4a..5baa922387 100644 --- a/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs +++ b/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs @@ -78,6 +78,7 @@ public class RegistrationBusinessLogicTest private readonly Guid _existingApplicationId; private readonly string _displayName; private readonly string _alpha2code; + private readonly string _vatId; private readonly TestException _error; private readonly IOptions _options; private readonly IStaticDataRepository _staticDataRepository; @@ -136,6 +137,7 @@ public RegistrationBusinessLogicTest() _existingApplicationId = _fixture.Create(); _displayName = _fixture.Create(); _alpha2code = "XY"; + _vatId = "DE123456789"; _error = _fixture.Create(); _processLine = @@ -644,6 +646,7 @@ public async Task SetCompanyWithAddressAsync__WithExistingBpn_ModifiesCompany() .With(x => x.BusinessPartnerNumber, "BPNL00000001TEST") .With(x => x.CompanyId, companyId) .With(x => x.CountryAlpha2Code, _alpha2code) + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, _vatId)]) .Create(); A.CallTo(() => _companyRepository.CheckBpnExists("BPNL00000001TEST")).Returns(true); @@ -707,6 +710,7 @@ public async Task SetCompanyWithAddressAsync__WithCompanyNameChange_ModifiesComp .With(x => x.BusinessPartnerNumber, "BPNL00000001TEST") .With(x => x.CompanyId, companyId) .With(x => x.CountryAlpha2Code, _alpha2code) + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, _vatId)]) .Create(); var sut = new RegistrationBusinessLogic( @@ -771,6 +775,7 @@ public async Task SetCompanyWithAddressAsync__WithoutCompanyNameChange_ModifiesC .With(x => x.BusinessPartnerNumber, "BPNL00000001TEST") .With(x => x.CompanyId, companyId) .With(x => x.CountryAlpha2Code, _alpha2code) + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, _vatId)]) .Create(); var sut = new RegistrationBusinessLogic( @@ -836,6 +841,7 @@ public async Task SetCompanyWithAddressAsync_ModifyCompany(string? bpn) .With(x => x.BusinessPartnerNumber, bpn) .With(x => x.CompanyId, companyId) .With(x => x.CountryAlpha2Code, _alpha2code) + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, _vatId)]) .Create(); var existingData = _fixture.Build() @@ -903,6 +909,7 @@ public async Task SetCompanyWithAddressAsync_WithoutInitialCompanyAddress_Create .With(x => x.BusinessPartnerNumber, default(string?)) .With(x => x.CompanyId, companyId) .With(x => x.CountryAlpha2Code, _alpha2code) + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, _vatId)]) .Create(); var existingData = _fixture.Build() @@ -1001,6 +1008,7 @@ public async Task SetCompanyWithAddressAsync_WithInitialCompanyAddress_ModifyAdd .With(x => x.BusinessPartnerNumber, default(string?)) .With(x => x.CompanyId, companyId) .With(x => x.CountryAlpha2Code, _alpha2code) + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, _vatId)]) .Create(); var existingData = _fixture.Build() @@ -1092,11 +1100,17 @@ public async Task SetCompanyWithAddressAsync_WithUniqueIdentifiers_CreateModifyD var uniqueIdentifiers = _fixture.CreateMany(4); var firstIdData = _fixture.Build() - .With(x => x.UniqueIdentifierId, uniqueIdentifiers.First()).Create(); // shall not modify + .With(x => x.UniqueIdentifierId, uniqueIdentifiers.First()) + .With(x => x.Value, "HRB123456") + .Create(); // shall not modify var secondIdData = _fixture.Build() - .With(x => x.UniqueIdentifierId, uniqueIdentifiers.ElementAt(1)).Create(); // shall modify + .With(x => x.UniqueIdentifierId, uniqueIdentifiers.ElementAt(1)) + .With(x => x.Value, "DE124356789") + .Create(); // shall modify var thirdIdData = _fixture.Build() - .With(x => x.UniqueIdentifierId, uniqueIdentifiers.ElementAt(2)).Create(); // shall create new + .With(x => x.UniqueIdentifierId, uniqueIdentifiers.ElementAt(2)) + .With(x => x.Value, "54930084UKLVMY22DS16") + .Create(); // shall create new var companyData = _fixture.Build() .With(x => x.BusinessPartnerNumber, default(string?)) @@ -1184,6 +1198,7 @@ public async Task SetCompanyWithAddressAsync_WithInvalidCountryCode_Throws() var companyData = _fixture.Build() .With(x => x.BusinessPartnerNumber, default(string?)) .With(x => x.CountryAlpha2Code, _alpha2code) + .With(x => x.UniqueIds, [new CompanyUniqueIdData(UniqueIdentifierId.VAT_ID, _vatId)]) .Create(); var identityData = A.Fake(); A.CallTo(() => identityData.IdentityId).Returns(Guid.NewGuid()); @@ -1231,7 +1246,10 @@ public async Task SetCompanyWithAddressAsync_WithInvalidUniqueIdentifiers_Throws .With(x => x.CountryAlpha2Code, _alpha2code) .With(x => x.UniqueIds, identifiers.Select(id => - _fixture.Build().With(x => x.UniqueIdentifierId, id).Create())) + _fixture.Build() + .With(x => x.UniqueIdentifierId, id) + .With(x => x.Value, _vatId) + .Create())) .Create(); var sut = new RegistrationBusinessLogic(