From e63ec91f53f20d61c10b8013af88d4016f5c7e19 Mon Sep 17 00:00:00 2001 From: Dhirender Singh Date: Wed, 24 Jul 2024 11:39:46 +0200 Subject: [PATCH] optimized the company regex --- .../BusinessLogic/InvitationBusinessLogic.cs | 4 +- .../BusinessLogic/NetworkBusinessLogic.cs | 4 +- .../RegistrationBusinessLogic.cs | 9 +-- .../Framework.Models/ValidationExpressions.cs | 3 + .../ValidationExpressionsValidator.cs | 30 +++++++++ .../BusinessLogic/AppReleaseBusinessLogic.cs | 6 +- .../BusinessLogic/AppsBusinessLogic.cs | 4 +- .../BusinessLogic/ServiceBusinessLogic.cs | 4 +- .../InvitationBusinessLogicTests.cs | 44 ++++++++++++ .../ValidationExpressionsTests.cs | 67 +++++++++++++++++++ 10 files changed, 153 insertions(+), 22 deletions(-) create mode 100644 src/framework/Framework.Models/ValidationExpressionsValidator.cs create mode 100644 tests/framework/Framework.Models.Tests/ValidationExpressionsTests.cs diff --git a/src/administration/Administration.Service/BusinessLogic/InvitationBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/InvitationBusinessLogic.cs index 80f7c8ad70..d53bb1004d 100644 --- a/src/administration/Administration.Service/BusinessLogic/InvitationBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/InvitationBusinessLogic.cs @@ -24,13 +24,11 @@ using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; using Org.Eclipse.TractusX.Portal.Backend.Processes.Library; -using System.Text.RegularExpressions; namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLogic; public class InvitationBusinessLogic : IInvitationBusinessLogic { - private static readonly Regex Company = new(ValidationExpressions.Company, RegexOptions.Compiled, TimeSpan.FromSeconds(1)); private readonly IPortalRepositories _portalRepositories; /// @@ -55,7 +53,7 @@ public Task ExecuteInvitation(CompanyInvitationData invitationData) throw new ControllerArgumentException("organisationName must not be empty", "organisationName"); } - if (!string.IsNullOrEmpty(invitationData.OrganisationName) && !Company.IsMatch(invitationData.OrganisationName)) + if (!string.IsNullOrEmpty(invitationData.OrganisationName) && !ValidationExpressionsValidator.IsValidCompanyName(invitationData.OrganisationName)) { throw new ControllerArgumentException($"OrganisationName: {ValidationExpressionErrorMessages.CompanyError}", "organisationName"); } diff --git a/src/administration/Administration.Service/BusinessLogic/NetworkBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/NetworkBusinessLogic.cs index 16061c9d8d..ee1f0488c9 100644 --- a/src/administration/Administration.Service/BusinessLogic/NetworkBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/NetworkBusinessLogic.cs @@ -41,8 +41,6 @@ public class NetworkBusinessLogic : INetworkBusinessLogic { private static readonly Regex Name = new(ValidationExpressions.Name, RegexOptions.Compiled, TimeSpan.FromSeconds(1)); private static readonly Regex ExternalID = new("^[A-Za-z0-9\\-+_/,.]{6,36}$", RegexOptions.Compiled, TimeSpan.FromSeconds(1)); - private static readonly Regex Company = new(ValidationExpressions.Company, RegexOptions.Compiled, TimeSpan.FromSeconds(1)); - private readonly IPortalRepositories _portalRepositories; private readonly IIdentityData _identityData; private readonly IUserProvisioningService _userProvisioningService; @@ -60,7 +58,7 @@ public NetworkBusinessLogic(IPortalRepositories portalRepositories, IIdentitySer public async Task HandlePartnerRegistration(PartnerRegistrationData data) { - if (!string.IsNullOrEmpty(data.Name) && !Company.IsMatch(data.Name)) + if (!ValidationExpressionsValidator.IsValidCompanyName(data.Name)) { throw new ControllerArgumentException($"OrganisationName: {ValidationExpressionErrorMessages.CompanyError}", "organisationName"); } diff --git a/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs index a52b76e1cf..ff5c3cc14c 100644 --- a/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs @@ -50,9 +50,6 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLog public sealed class RegistrationBusinessLogic : IRegistrationBusinessLogic { private static readonly Regex BpnRegex = new(ValidationExpressions.Bpn, RegexOptions.Compiled, TimeSpan.FromSeconds(1)); - - private static readonly Regex Company = new(ValidationExpressions.Company, RegexOptions.Compiled, TimeSpan.FromSeconds(1)); - private readonly IPortalRepositories _portalRepositories; private readonly RegistrationSettings _settings; private readonly IApplicationChecklistService _checklistService; @@ -105,7 +102,7 @@ private async Task GetCompanyWithAddressAsyncInternal(Gu { throw NotFoundException.Create(AdministrationRegistrationErrors.APPLICATION_NOT_FOUND, [new("applicationId", applicationId.ToString())]); } - if (!string.IsNullOrEmpty(companyWithAddress.Name) && !Company.IsMatch(companyWithAddress.Name)) + if (!ValidationExpressionsValidator.IsValidCompanyName(companyWithAddress.Name)) { throw new ControllerArgumentException($"OrganisationName: {ValidationExpressionErrorMessages.CompanyError}", "organisationName"); } @@ -144,7 +141,7 @@ private async Task GetCompanyWithAddressAsyncInternal(Gu public Task> GetCompanyApplicationDetailsAsync(int page, int size, CompanyApplicationStatusFilter? companyApplicationStatusFilter = null, string? companyName = null) { - if (!string.IsNullOrEmpty(companyName) && !Company.IsMatch(companyName)) + if (!string.IsNullOrEmpty(companyName) && !ValidationExpressionsValidator.IsValidCompanyName(companyName)) { throw new ControllerArgumentException($"CompanyName: {ValidationExpressionErrorMessages.CompanyError}", nameof(companyName)); } @@ -183,7 +180,7 @@ private async Task GetCompanyWithAddressAsyncInternal(Gu public Task> GetAllCompanyApplicationsDetailsAsync(int page, int size, string? companyName = null) { - if (!string.IsNullOrEmpty(companyName) && !Company.IsMatch(companyName)) + if (!ValidationExpressionsValidator.IsValidCompanyName(companyName)) { throw new ControllerArgumentException($"CompanyName: {ValidationExpressionErrorMessages.CompanyError}", nameof(companyName)); } diff --git a/src/framework/Framework.Models/ValidationExpressions.cs b/src/framework/Framework.Models/ValidationExpressions.cs index be30311273..8612fb411a 100644 --- a/src/framework/Framework.Models/ValidationExpressions.cs +++ b/src/framework/Framework.Models/ValidationExpressions.cs @@ -17,6 +17,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +using System.Text.RegularExpressions; + namespace Org.Eclipse.TractusX.Portal.Backend.Framework.Models; public static class ValidationExpressions @@ -24,4 +26,5 @@ public static class ValidationExpressions public const string Name = @"^.+$"; public const string Bpn = @"^(BPNL|bpnl)[\w|\d]{12}$"; public const string Company = @"^(?!.*\s$)([\wÀ-ÿ£$€¥¢@%*+\-/\\,.:;=<>!?&^#'\x22()[\]]\s?){1,160}$"; + public static readonly Regex CompanyRegex = new(ValidationExpressions.Company, RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture, TimeSpan.FromSeconds(1)); } diff --git a/src/framework/Framework.Models/ValidationExpressionsValidator.cs b/src/framework/Framework.Models/ValidationExpressionsValidator.cs new file mode 100644 index 0000000000..c0f79ed0bd --- /dev/null +++ b/src/framework/Framework.Models/ValidationExpressionsValidator.cs @@ -0,0 +1,30 @@ +/******************************************************************************** + * Copyright (c) 2022 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.RegularExpressions; + +namespace Org.Eclipse.TractusX.Portal.Backend.Framework.Models; + +public static class ValidationExpressionsValidator +{ + public static bool IsValidCompanyName(string companyName) + { + return ValidationExpressions.CompanyRegex.IsMatch(companyName); + } +} diff --git a/src/marketplace/Apps.Service/BusinessLogic/AppReleaseBusinessLogic.cs b/src/marketplace/Apps.Service/BusinessLogic/AppReleaseBusinessLogic.cs index cb00807b4e..6bdb2d02b7 100644 --- a/src/marketplace/Apps.Service/BusinessLogic/AppReleaseBusinessLogic.cs +++ b/src/marketplace/Apps.Service/BusinessLogic/AppReleaseBusinessLogic.cs @@ -31,7 +31,6 @@ using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Identities; -using System.Text.RegularExpressions; namespace Org.Eclipse.TractusX.Portal.Backend.Apps.Service.BusinessLogic; @@ -40,7 +39,6 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Apps.Service.BusinessLogic; /// public class AppReleaseBusinessLogic : IAppReleaseBusinessLogic { - private static readonly Regex Company = new(ValidationExpressions.Company, RegexOptions.Compiled, TimeSpan.FromSeconds(1)); private readonly IPortalRepositories _portalRepositories; private readonly AppsSettings _settings; private readonly IOfferService _offerService; @@ -189,7 +187,7 @@ public Task AddAppAsync(AppRequestModel appRequestModel) throw new ControllerArgumentException("Use Case Ids must not be null or empty", nameof(appRequestModel.UseCaseIds)); } - if (!string.IsNullOrEmpty(appRequestModel.Provider) && !Company.IsMatch(appRequestModel.Provider)) + if (!string.IsNullOrEmpty(appRequestModel.Provider) && !ValidationExpressionsValidator.IsValidCompanyName(appRequestModel.Provider)) { throw new ControllerArgumentException($"Provider: {ValidationExpressionErrorMessages.CompanyError}", nameof(appRequestModel.Provider)); } @@ -269,7 +267,7 @@ public async Task UpdateAppReleaseAsync(Guid appId, AppRequestModel appRequestMo throw new ForbiddenException($"Company {companyId} is not the app provider."); } - if (!string.IsNullOrEmpty(appRequestModel.Provider) && !Company.IsMatch(appRequestModel.Provider)) + if (!string.IsNullOrEmpty(appRequestModel.Provider) && !ValidationExpressionsValidator.IsValidCompanyName(appRequestModel.Provider)) { throw new ControllerArgumentException($"Provider: {ValidationExpressionErrorMessages.CompanyError}", nameof(appRequestModel.Provider)); } diff --git a/src/marketplace/Apps.Service/BusinessLogic/AppsBusinessLogic.cs b/src/marketplace/Apps.Service/BusinessLogic/AppsBusinessLogic.cs index 4c36b0a64e..36e8d4b341 100644 --- a/src/marketplace/Apps.Service/BusinessLogic/AppsBusinessLogic.cs +++ b/src/marketplace/Apps.Service/BusinessLogic/AppsBusinessLogic.cs @@ -30,7 +30,6 @@ using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Entities; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Identities; -using System.Text.RegularExpressions; namespace Org.Eclipse.TractusX.Portal.Backend.Apps.Service.BusinessLogic; @@ -39,7 +38,6 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Apps.Service.BusinessLogic; /// public class AppsBusinessLogic : IAppsBusinessLogic { - private static readonly Regex Company = new(ValidationExpressions.Company, RegexOptions.Compiled, TimeSpan.FromSeconds(1)); private readonly IPortalRepositories _portalRepositories; private readonly IOfferSubscriptionService _offerSubscriptionService; private readonly AppsSettings _settings; @@ -162,7 +160,7 @@ public async Task AddFavouriteAppForUserAsync(Guid appId) /// public async Task> GetCompanyProvidedAppSubscriptionStatusesForUserAsync(int page, int size, SubscriptionStatusSorting? sorting, OfferSubscriptionStatusId? statusId, Guid? offerId, string? companyName = null) { - if (!string.IsNullOrWhiteSpace(companyName) && !Company.IsMatch(companyName)) + if (!string.IsNullOrEmpty(companyName) && !ValidationExpressionsValidator.IsValidCompanyName(companyName)) { throw new ControllerArgumentException($"CompanyName: {ValidationExpressionErrorMessages.CompanyError}"); } diff --git a/src/marketplace/Services.Service/BusinessLogic/ServiceBusinessLogic.cs b/src/marketplace/Services.Service/BusinessLogic/ServiceBusinessLogic.cs index 07e9e52343..0eba542632 100644 --- a/src/marketplace/Services.Service/BusinessLogic/ServiceBusinessLogic.cs +++ b/src/marketplace/Services.Service/BusinessLogic/ServiceBusinessLogic.cs @@ -29,7 +29,6 @@ using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Identities; using Org.Eclipse.TractusX.Portal.Backend.Services.Service.ViewModels; -using System.Text.RegularExpressions; namespace Org.Eclipse.TractusX.Portal.Backend.Services.Service.BusinessLogic; @@ -38,7 +37,6 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Services.Service.BusinessLogic; /// public class ServiceBusinessLogic : IServiceBusinessLogic { - private static readonly Regex Company = new(ValidationExpressions.Company, RegexOptions.Compiled, TimeSpan.FromSeconds(1)); private readonly IPortalRepositories _portalRepositories; private readonly IOfferService _offerService; private readonly IOfferSubscriptionService _offerSubscriptionService; @@ -139,7 +137,7 @@ public Task AutoSetupServiceAsync(OfferAutoSetupData /// public async Task> GetCompanyProvidedServiceSubscriptionStatusesForUserAsync(int page, int size, SubscriptionStatusSorting? sorting, OfferSubscriptionStatusId? statusId, Guid? offerId, string? companyName = null) { - if (!string.IsNullOrEmpty(companyName) && !Company.IsMatch(companyName)) + if (!string.IsNullOrEmpty(companyName) && !ValidationExpressionsValidator.IsValidCompanyName(companyName)) { throw new ControllerArgumentException($"CompanyName: {ValidationExpressionErrorMessages.CompanyError}"); } diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/InvitationBusinessLogicTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/InvitationBusinessLogicTests.cs index d36048f7d1..27d0214e5e 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/InvitationBusinessLogicTests.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/InvitationBusinessLogicTests.cs @@ -20,6 +20,7 @@ using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLogic; using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Models; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Models; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; @@ -112,6 +113,49 @@ public async Task ExecuteInvitation_WithValidData_CreatesExpected() invitations.Should().ContainSingle().And.Satisfy(x => x.ProcessId == processes.Single().Id && x.UserName == "testUserName"); } + [Theory] + [InlineData("ValidOrganisationName123")] + [InlineData("Organisation Name")] + [InlineData("Organisation$Name")] + [InlineData("Organisation\\Name")] + [InlineData("Organisation/Name")] + [InlineData("Organisation")] + [InlineData("Organisation Name!")] + [InlineData("7-ELEVEN INTERNATIONAL LLC")] + [InlineData("C")] + [InlineData("+SEN Inc.")] + [InlineData("Double \"Quote\" Company S.A.")] + [InlineData("Special Characters ^&%#@*/_-\\")] + [InlineData("German: ÄÖÜß")] + [InlineData("Icelandic: ÆÐÞ")] + [InlineData("C")] + public async Task ExecuteInvitation_WithValidOrganisationName_DoesNotThrowException(string validName) + { + var invitationData = _fixture.Build() + .With(x => x.OrganisationName, validName) + .Create(); + + Func Act = async () => await _sut.ExecuteInvitation(invitationData); + + await Act.Should().NotThrowAsync(); + } + + [Theory] + [InlineData("Organisation Name ")] // Ends with whitespace + [InlineData("1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWX")] // Exceeds 160 characters + public async Task ExecuteInvitation_WithInvalidOrganisationName_ThrowsControllerArgumentException(string invalidName) + { + var invitationData = _fixture.Build() + .With(x => x.OrganisationName, invalidName) + .Create(); + + async Task Act() => await _sut.ExecuteInvitation(invitationData); + + var ex = await Assert.ThrowsAsync(Act); + ex.Message.Should().Be($"OrganisationName: {ValidationExpressionErrorMessages.CompanyError} (Parameter 'organisationName')"); + ex.ParamName.Should().Be("organisationName"); + } + #endregion #region RetriggerCreateCentralIdp diff --git a/tests/framework/Framework.Models.Tests/ValidationExpressionsTests.cs b/tests/framework/Framework.Models.Tests/ValidationExpressionsTests.cs new file mode 100644 index 0000000000..8c77d4986d --- /dev/null +++ b/tests/framework/Framework.Models.Tests/ValidationExpressionsTests.cs @@ -0,0 +1,67 @@ + +/******************************************************************************** + * Copyright (c) 2023 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.RegularExpressions; + +namespace Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Tests; + +public class ValidationExpressionsTests +{ + private static readonly Regex regex = new Regex(ValidationExpressions.Company); + + [Theory] + [InlineData("ValidCompanyName123", true)] // Valid company name + [InlineData("Company Name", true)] // Valid with space + [InlineData("Company$Name", true)] // Valid with special character + [InlineData("Company\\Name", true)] // Valid with backslash + [InlineData("Company/Name", true)] // Valid with forward slash + [InlineData("Company", true)] // Valid with angle brackets + [InlineData("Company Name!", true)] // Valid with exclamation mark + [InlineData("Company@Name", true)] // Valid with @ symbol + [InlineData("C", true)] // Minimum valid length + [InlineData("7-ELEVEN INTERNATIONAL LLC", true)] + [InlineData("Recht 24/7 Schröder Rechtsanwaltsgesellschaft mbH", true)] + [InlineData("Currency £$€¥¢", true)] + [InlineData("Brackets []()", true)] + [InlineData("Punctuation !?,.;:", true)] + [InlineData("Double \"Quote\" Company S.A.", true)] + [InlineData("Single 'Quote' Company LLC", true)] + [InlineData("Special Characters ^&%#@*/_-\\", true)] + [InlineData("German: ÄÖÜß", true)] + [InlineData("+SEN Inc.", true)] // leading special character + [InlineData("Danish: ÆØÅ", true)] + [InlineData("Bayerische Motoren Werke Aktiengesellschaft ", false)] // Ends with whitespace + [InlineData(" Bayerische Motoren Werke Aktiengesellschaft", false)] // starts with whitespace + [InlineData("Bayerische Motoren Werke Aktiengesellschaft", false)] // double whitespace + [InlineData(@"123456789012345678901234567890 + 123456789012345678901234567890 + 123456789012345678901234567890 + 123456789012345678901234567890 + 123456789012345678901234567890 + 12345678901234567890", false)] // Exceeds 160 characters + [InlineData("", false)] // Empty string + [InlineData(" ", false)] // Single space + [InlineData(" ", false)] // Multiple spaces + public void TestCompanyNameRegex(string companyName, bool expectedResult) + { + var result = ValidationExpressionsValidator.IsValidCompanyName(companyName); + Assert.Equal(expectedResult, result); + } +} \ No newline at end of file