diff --git a/src/administration/Administration.Service/BusinessLogic/CompanyDataBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/CompanyDataBusinessLogic.cs index fc8a9b769f..0cf42b585b 100644 --- a/src/administration/Administration.Service/BusinessLogic/CompanyDataBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/CompanyDataBusinessLogic.cs @@ -45,7 +45,6 @@ public class CompanyDataBusinessLogic( { private static readonly Regex BpnsRegex = new(ValidationExpressions.Bpns, RegexOptions.Compiled, TimeSpan.FromSeconds(1)); private static readonly Regex EcmRegex = new(ValidationExpressions.ExternalCertificateNumber, RegexOptions.Compiled, TimeSpan.FromSeconds(1)); - private static readonly Regex Company = new(ValidationExpressions.Company, RegexOptions.Compiled, TimeSpan.FromSeconds(1)); private readonly IIdentityData _identityData = identityService.IdentityData; private readonly CompanyDataSettings _settings = options.Value; @@ -267,9 +266,9 @@ public async Task CreateCompanyCertificate(CompanyCertificateCreationData data, throw new ControllerArgumentException("ValidTill date should be greater than current date"); } - if (data.Issuer != null && !Company.IsMatch(data.Issuer!)) + if (data.Issuer != null && !data.Issuer.IsValidCompanyName()) { - throw new ControllerArgumentException("Issuer length must be 3-40 characters and *+=#%\\s not used as one of the first three characters in the company name"); + throw ControllerArgumentException.Create(ValidationExpressionErrors.INCORRECT_COMPANY_NAME, [new("name", nameof(data.Issuer))]); } var documentContentType = data.Document.ContentType.ParseMediaTypeId(); diff --git a/src/administration/Administration.Service/BusinessLogic/InvitationBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/InvitationBusinessLogic.cs index 289b5ef6a6..49bb0bca7d 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; /// @@ -50,14 +48,9 @@ public Task ExecuteInvitation(CompanyInvitationData invitationData) throw new ControllerArgumentException("email must not be empty", "email"); } - if (string.IsNullOrWhiteSpace(invitationData.OrganisationName)) + if (invitationData.OrganisationName == null || !invitationData.OrganisationName.IsValidCompanyName()) { - throw new ControllerArgumentException("organisationName must not be empty", "organisationName"); - } - - if (!string.IsNullOrEmpty(invitationData.OrganisationName) && !Company.IsMatch(invitationData.OrganisationName)) - { - throw new ControllerArgumentException("OrganisationName length must be 3-40 characters and *+=#%\\s not used as one of the first three characters in the Organisation name", "organisationName"); + throw ControllerArgumentException.Create(ValidationExpressionErrors.INCORRECT_COMPANY_NAME, [new("name", nameof(invitationData.OrganisationName))]); } return ExecuteInvitationInternalAsync(invitationData); diff --git a/src/administration/Administration.Service/BusinessLogic/NetworkBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/NetworkBusinessLogic.cs index 91cbe4a288..7550d8803d 100644 --- a/src/administration/Administration.Service/BusinessLogic/NetworkBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/NetworkBusinessLogic.cs @@ -47,16 +47,14 @@ public class NetworkBusinessLogic( { 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 IIdentityData _identityData = identityService.IdentityData; private readonly PartnerRegistrationSettings _settings = options.Value; public async Task HandlePartnerRegistration(PartnerRegistrationData data) { - if (!string.IsNullOrEmpty(data.Name) && !Company.IsMatch(data.Name)) + if (!data.Name.IsValidCompanyName()) { - throw new ControllerArgumentException("OrganisationName length must be 3-40 characters and *+=#%\\s not used as one of the first three characters in the Organisation name", "organisationName"); + throw ControllerArgumentException.Create(ValidationExpressionErrors.INCORRECT_COMPANY_NAME, [new("name", "OrganisationName")]); } var ownerCompanyId = _identityData.CompanyId; diff --git a/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs index df02c3c6b0..9b1b59e811 100644 --- a/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs @@ -63,7 +63,6 @@ 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 IIdentityData _identityData = identityService.IdentityData; private readonly RegistrationSettings _settings = configuration.Value; @@ -85,9 +84,9 @@ 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 (!companyWithAddress.Name.IsValidCompanyName()) { - throw new ControllerArgumentException("OrganisationName length must be 3-40 characters and *+=#%\\s not used as one of the first three characters in the Organisation name", "organisationName"); + throw ControllerArgumentException.Create(ValidationExpressionErrors.INCORRECT_COMPANY_NAME, [new("name", "OrganisationName")]); } return new CompanyWithAddressData( @@ -124,9 +123,9 @@ private async Task GetCompanyWithAddressAsyncInternal(Gu public Task> GetCompanyApplicationDetailsAsync(int page, int size, CompanyApplicationStatusFilter? companyApplicationStatusFilter, string? companyName) { - if (!string.IsNullOrEmpty(companyName) && !Company.IsMatch(companyName)) + if (companyName != null && !companyName.IsValidCompanyName()) { - throw new ControllerArgumentException("CompanyName length must be 3-40 characters and *+=#%\\s not used as one of the first three characters in the company name", nameof(companyName)); + throw ControllerArgumentException.Create(ValidationExpressionErrors.INCORRECT_COMPANY_NAME, [new("name", "CompanyName")]); } var applications = portalRepositories.GetInstance() .GetCompanyApplicationsFilteredQuery( @@ -164,9 +163,9 @@ private async Task GetCompanyWithAddressAsyncInternal(Gu public Task> GetOspCompanyDetailsAsync(int page, int size, CompanyApplicationStatusFilter? companyApplicationStatusFilter, string? companyName) { - if (!string.IsNullOrEmpty(companyName) && !Company.IsMatch(companyName)) + if (companyName != null && !companyName.IsValidCompanyName()) { - throw new ControllerArgumentException("CompanyName length must be 3-40 characters and *+=#%\\s not used as one of the first three characters in the company name", nameof(companyName)); + throw ControllerArgumentException.Create(ValidationExpressionErrors.INCORRECT_COMPANY_NAME, [new("name", "CompanyName")]); } var applications = portalRepositories.GetInstance() .GetExternalCompanyApplicationsFilteredQuery(_identityData.CompanyId, @@ -199,9 +198,9 @@ private async Task GetCompanyWithAddressAsyncInternal(Gu } public Task> GetAllCompanyApplicationsDetailsAsync(int page, int size, string? companyName) { - if (!string.IsNullOrEmpty(companyName) && !Company.IsMatch(companyName)) + if (companyName != null && !companyName.IsValidCompanyName()) { - throw new ControllerArgumentException("CompanyName length must be 3-40 characters and *+=#%\\s not used as one of the first three characters in the company name", nameof(companyName)); + throw ControllerArgumentException.Create(ValidationExpressionErrors.INCORRECT_COMPANY_NAME, [new("name", "CompanyName")]); } var applications = portalRepositories.GetInstance().GetAllCompanyApplicationsDetailsQuery(companyName); diff --git a/src/administration/Administration.Service/Program.cs b/src/administration/Administration.Service/Program.cs index a6868d2408..3f366431c3 100644 --- a/src/administration/Administration.Service/Program.cs +++ b/src/administration/Administration.Service/Program.cs @@ -21,6 +21,7 @@ using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.DependencyInjection; using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.ErrorHandling; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling.Service; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Models; using Org.Eclipse.TractusX.Portal.Backend.IssuerComponent.Library.DependencyInjection; using Org.Eclipse.TractusX.Portal.Backend.Notifications.Library; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess; @@ -94,7 +95,8 @@ await WebAppHelper .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton(); + .AddSingleton() + .AddSingleton(); builder.Services.AddProvisioningDBAccess(builder.Configuration); }).ConfigureAwait(ConfigureAwaitOptions.None); diff --git a/src/framework/Framework.Async/Directory.Build.props b/src/framework/Framework.Async/Directory.Build.props index f53f08f151..416bf1cdf9 100644 --- a/src/framework/Framework.Async/Directory.Build.props +++ b/src/framework/Framework.Async/Directory.Build.props @@ -19,7 +19,7 @@ - 2.5.0 + 2.6.0 diff --git a/src/framework/Framework.Cors/Directory.Build.props b/src/framework/Framework.Cors/Directory.Build.props index f53f08f151..416bf1cdf9 100644 --- a/src/framework/Framework.Cors/Directory.Build.props +++ b/src/framework/Framework.Cors/Directory.Build.props @@ -19,7 +19,7 @@ - 2.5.0 + 2.6.0 diff --git a/src/framework/Framework.DBAccess/Directory.Build.props b/src/framework/Framework.DBAccess/Directory.Build.props index f53f08f151..416bf1cdf9 100644 --- a/src/framework/Framework.DBAccess/Directory.Build.props +++ b/src/framework/Framework.DBAccess/Directory.Build.props @@ -19,7 +19,7 @@ - 2.5.0 + 2.6.0 diff --git a/src/framework/Framework.DateTimeProvider/Directory.Build.props b/src/framework/Framework.DateTimeProvider/Directory.Build.props index f53f08f151..416bf1cdf9 100644 --- a/src/framework/Framework.DateTimeProvider/Directory.Build.props +++ b/src/framework/Framework.DateTimeProvider/Directory.Build.props @@ -19,7 +19,7 @@ - 2.5.0 + 2.6.0 diff --git a/src/framework/Framework.DependencyInjection/Directory.Build.props b/src/framework/Framework.DependencyInjection/Directory.Build.props index f53f08f151..416bf1cdf9 100644 --- a/src/framework/Framework.DependencyInjection/Directory.Build.props +++ b/src/framework/Framework.DependencyInjection/Directory.Build.props @@ -19,7 +19,7 @@ - 2.5.0 + 2.6.0 diff --git a/src/framework/Framework.ErrorHandling.Controller/Directory.Build.props b/src/framework/Framework.ErrorHandling.Controller/Directory.Build.props index f53f08f151..416bf1cdf9 100644 --- a/src/framework/Framework.ErrorHandling.Controller/Directory.Build.props +++ b/src/framework/Framework.ErrorHandling.Controller/Directory.Build.props @@ -19,7 +19,7 @@ - 2.5.0 + 2.6.0 diff --git a/src/framework/Framework.ErrorHandling.Web/Directory.Build.props b/src/framework/Framework.ErrorHandling.Web/Directory.Build.props index f53f08f151..416bf1cdf9 100644 --- a/src/framework/Framework.ErrorHandling.Web/Directory.Build.props +++ b/src/framework/Framework.ErrorHandling.Web/Directory.Build.props @@ -19,7 +19,7 @@ - 2.5.0 + 2.6.0 diff --git a/src/framework/Framework.ErrorHandling/Directory.Build.props b/src/framework/Framework.ErrorHandling/Directory.Build.props index f53f08f151..416bf1cdf9 100644 --- a/src/framework/Framework.ErrorHandling/Directory.Build.props +++ b/src/framework/Framework.ErrorHandling/Directory.Build.props @@ -19,7 +19,7 @@ - 2.5.0 + 2.6.0 diff --git a/src/framework/Framework.HttpClientExtensions/Directory.Build.props b/src/framework/Framework.HttpClientExtensions/Directory.Build.props index f53f08f151..416bf1cdf9 100644 --- a/src/framework/Framework.HttpClientExtensions/Directory.Build.props +++ b/src/framework/Framework.HttpClientExtensions/Directory.Build.props @@ -19,7 +19,7 @@ - 2.5.0 + 2.6.0 diff --git a/src/framework/Framework.IO/Directory.Build.props b/src/framework/Framework.IO/Directory.Build.props index f53f08f151..416bf1cdf9 100644 --- a/src/framework/Framework.IO/Directory.Build.props +++ b/src/framework/Framework.IO/Directory.Build.props @@ -19,7 +19,7 @@ - 2.5.0 + 2.6.0 diff --git a/src/framework/Framework.Linq/Directory.Build.props b/src/framework/Framework.Linq/Directory.Build.props index f53f08f151..416bf1cdf9 100644 --- a/src/framework/Framework.Linq/Directory.Build.props +++ b/src/framework/Framework.Linq/Directory.Build.props @@ -19,7 +19,7 @@ - 2.5.0 + 2.6.0 diff --git a/src/framework/Framework.Logging/Directory.Build.props b/src/framework/Framework.Logging/Directory.Build.props index f53f08f151..416bf1cdf9 100644 --- a/src/framework/Framework.Logging/Directory.Build.props +++ b/src/framework/Framework.Logging/Directory.Build.props @@ -19,7 +19,7 @@ - 2.5.0 + 2.6.0 diff --git a/src/framework/Framework.Models/Directory.Build.props b/src/framework/Framework.Models/Directory.Build.props index f53f08f151..416bf1cdf9 100644 --- a/src/framework/Framework.Models/Directory.Build.props +++ b/src/framework/Framework.Models/Directory.Build.props @@ -19,7 +19,7 @@ - 2.5.0 + 2.6.0 diff --git a/src/framework/Framework.Models/ValidationExpressionErrorMessages.cs b/src/framework/Framework.Models/ValidationExpressionErrorMessages.cs new file mode 100644 index 0000000000..e06fe4fb98 --- /dev/null +++ b/src/framework/Framework.Models/ValidationExpressionErrorMessages.cs @@ -0,0 +1,40 @@ +/******************************************************************************** + * Copyright (c) 2024 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 Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling.Service; +using System.Collections.Immutable; + +namespace Org.Eclipse.TractusX.Portal.Backend.Framework.Models; + +// This class contains error messages for validation expressions +public class ValidationExpressionErrorMessageContainer : IErrorMessageContainer +{ + private static readonly IReadOnlyDictionary _messageContainer = new Dictionary { + { ValidationExpressionErrors.INCORRECT_COMPANY_NAME, "{name}: length must be between 1 and 160 characters and not start or end with a white space" }, + + }.ToImmutableDictionary(x => (int)x.Key, x => x.Value); + + public Type Type { get => typeof(ValidationExpressionErrors); } + public IReadOnlyDictionary MessageContainer { get => _messageContainer; } +} + +public enum ValidationExpressionErrors +{ + INCORRECT_COMPANY_NAME +} diff --git a/src/framework/Framework.Models/ValidationExpressions.cs b/src/framework/Framework.Models/ValidationExpressions.cs index 5755887180..c4d265d744 100644 --- a/src/framework/Framework.Models/ValidationExpressions.cs +++ b/src/framework/Framework.Models/ValidationExpressions.cs @@ -24,6 +24,6 @@ public static class ValidationExpressions public const string Name = @"^.+$"; public const string Bpn = @"^(BPNL|bpnl)[\w|\d]{12}$"; public const string Bpns = @"^(BPNS|bpns)[\w|\d]{12}$"; - public const string Company = @"^\d*?[A-Za-zÀ-ÿ]\d?([A-Za-z0-9À-ÿ-_+=.,:;!?'\x22&#@()]\s?){2,40}$"; + public const string Company = @"^(?!.*\s$)([\wÀ-ÿ£$€¥¢@%*+\-/\\,.:;=<>!?&^#'\x22()[\]]\s?){1,160}$"; public const string ExternalCertificateNumber = @"^[a-zA-Z0-9]{0,36}$"; } diff --git a/src/framework/Framework.Models/ValidationExpressionsValidator.cs b/src/framework/Framework.Models/ValidationExpressionsValidator.cs new file mode 100644 index 0000000000..1e62a06aec --- /dev/null +++ b/src/framework/Framework.Models/ValidationExpressionsValidator.cs @@ -0,0 +1,31 @@ +/******************************************************************************** + * Copyright (c) 2024 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 +{ + private static readonly Regex CompanyRegex = new(ValidationExpressions.Company, RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture, TimeSpan.FromSeconds(1)); + public static bool IsValidCompanyName(this string companyName) + { + return CompanyRegex.IsMatch(companyName); + } +} diff --git a/src/framework/Framework.Seeding/Directory.Build.props b/src/framework/Framework.Seeding/Directory.Build.props index f53f08f151..416bf1cdf9 100644 --- a/src/framework/Framework.Seeding/Directory.Build.props +++ b/src/framework/Framework.Seeding/Directory.Build.props @@ -19,7 +19,7 @@ - 2.5.0 + 2.6.0 diff --git a/src/framework/Framework.Swagger/Directory.Build.props b/src/framework/Framework.Swagger/Directory.Build.props index f53f08f151..416bf1cdf9 100644 --- a/src/framework/Framework.Swagger/Directory.Build.props +++ b/src/framework/Framework.Swagger/Directory.Build.props @@ -19,7 +19,7 @@ - 2.5.0 + 2.6.0 diff --git a/src/framework/Framework.Token/Directory.Build.props b/src/framework/Framework.Token/Directory.Build.props index f53f08f151..416bf1cdf9 100644 --- a/src/framework/Framework.Token/Directory.Build.props +++ b/src/framework/Framework.Token/Directory.Build.props @@ -19,7 +19,7 @@ - 2.5.0 + 2.6.0 diff --git a/src/framework/Framework.Web/Directory.Build.props b/src/framework/Framework.Web/Directory.Build.props index f53f08f151..416bf1cdf9 100644 --- a/src/framework/Framework.Web/Directory.Build.props +++ b/src/framework/Framework.Web/Directory.Build.props @@ -19,7 +19,7 @@ - 2.5.0 + 2.6.0 diff --git a/src/marketplace/Apps.Service/BusinessLogic/AppReleaseBusinessLogic.cs b/src/marketplace/Apps.Service/BusinessLogic/AppReleaseBusinessLogic.cs index 2e5914d16f..b61d226008 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,9 +187,9 @@ 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 (appRequestModel.Provider != null && !appRequestModel.Provider.IsValidCompanyName()) { - throw new ControllerArgumentException("Provider length must be 3-40 characters and *+=#%\\s not used as one of the first three characters in the Organisation name", nameof(appRequestModel.Provider)); + throw ControllerArgumentException.Create(ValidationExpressionErrors.INCORRECT_COMPANY_NAME, [new("name", nameof(appRequestModel.Provider))]); } return CreateAppAsync(appRequestModel); @@ -269,9 +267,9 @@ 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 (appRequestModel.Provider != null && !appRequestModel.Provider.IsValidCompanyName()) { - throw new ControllerArgumentException("Provider length must be 3-40 characters and *+=#%\\s not used as one of the first three characters in the Organisation name", nameof(appRequestModel.Provider)); + throw ControllerArgumentException.Create(ValidationExpressionErrors.INCORRECT_COMPANY_NAME, [new("name", nameof(appRequestModel.Provider))]); } if (appRequestModel.SalesManagerId.HasValue) diff --git a/src/marketplace/Apps.Service/BusinessLogic/AppsBusinessLogic.cs b/src/marketplace/Apps.Service/BusinessLogic/AppsBusinessLogic.cs index b33d9ccea0..4bbe253efc 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; @@ -158,9 +156,9 @@ 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 (companyName != null && !companyName.IsValidCompanyName()) { - throw new ControllerArgumentException("CompanyName length must be 3-40 characters and *+=#%\\s not used as one of the first three characters in the company name"); + throw ControllerArgumentException.Create(ValidationExpressionErrors.INCORRECT_COMPANY_NAME, [new("name", "CompanyName")]); } async Task?> GetCompanyProvidedAppSubscriptionStatusData(int skip, int take) diff --git a/src/marketplace/Apps.Service/Program.cs b/src/marketplace/Apps.Service/Program.cs index 1ba4cf95e1..a2be778650 100644 --- a/src/marketplace/Apps.Service/Program.cs +++ b/src/marketplace/Apps.Service/Program.cs @@ -19,6 +19,8 @@ using Org.Eclipse.TractusX.Portal.Backend.Apps.Service.BusinessLogic; using Org.Eclipse.TractusX.Portal.Backend.Dim.Library.DependencyInjection; +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling.Service; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Models; using Org.Eclipse.TractusX.Portal.Backend.Notifications.Library; using Org.Eclipse.TractusX.Portal.Backend.Offers.Library.DependencyInjection; using Org.Eclipse.TractusX.Portal.Backend.Offers.Library.Service; @@ -48,6 +50,8 @@ await WebAppHelper .AddTransient() .AddTransient() .AddTransient() + .AddSingleton() + .AddSingleton() .AddTechnicalUserProfile() .ConfigureAppsSettings(builder.Configuration.GetSection("AppMarketPlace")) .AddOfferDocumentServices(); diff --git a/src/marketplace/Services.Service/BusinessLogic/ServiceBusinessLogic.cs b/src/marketplace/Services.Service/BusinessLogic/ServiceBusinessLogic.cs index c3f87b85e0..366cc59eea 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; @@ -135,9 +133,9 @@ 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 (companyName != null && !companyName.IsValidCompanyName()) { - throw new ControllerArgumentException("CompanyName length must be 3-40 characters and *+=#%\\s not used as one of the first three characters in the company name"); + throw ControllerArgumentException.Create(ValidationExpressionErrors.INCORRECT_COMPANY_NAME, [new("name", "CompanyName")]); } async Task?> GetCompanyProvidedAppSubscriptionStatusData(int skip, int take) { diff --git a/src/marketplace/Services.Service/Program.cs b/src/marketplace/Services.Service/Program.cs index 43ca334e85..3ed8f2ef65 100644 --- a/src/marketplace/Services.Service/Program.cs +++ b/src/marketplace/Services.Service/Program.cs @@ -18,6 +18,8 @@ ********************************************************************************/ using Org.Eclipse.TractusX.Portal.Backend.Dim.Library.DependencyInjection; +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling.Service; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Models; using Org.Eclipse.TractusX.Portal.Backend.Notifications.Library; using Org.Eclipse.TractusX.Portal.Backend.Offers.Library.DependencyInjection; using Org.Eclipse.TractusX.Portal.Backend.Offers.Library.Web.DependencyInjection; @@ -48,6 +50,8 @@ await WebAppHelper .AddServiceBusinessLogic(builder.Configuration) .AddTransient() .AddTransient() + .AddSingleton() + .AddSingleton() .AddTechnicalUserProfile() .AddOfferDocumentServices(); diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/CompanyDataBusinessLogicTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/CompanyDataBusinessLogicTests.cs index f4e7fb44cb..4a88b22a2a 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/CompanyDataBusinessLogicTests.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/CompanyDataBusinessLogicTests.cs @@ -885,7 +885,7 @@ public async Task CheckCompanyCertificateType_WithInvalidCall_ThrowsControllerFo var file = FormFileHelper.GetFormFile("test content", "test.pdf", MediaTypeId.PDF.MapToMediaType()); var externalCertificateNumber = "2345678"; var sites = new[] { "BPNS00000003CRHK" }; - var data = new CompanyCertificateCreationData(CompanyCertificateTypeId.IATF, file, externalCertificateNumber, sites, _now.AddMicroseconds(-1), _now.AddMicroseconds(1), "+ACC"); + var data = new CompanyCertificateCreationData(CompanyCertificateTypeId.IATF, file, externalCertificateNumber, sites, _now.AddMicroseconds(-1), _now.AddMicroseconds(1), " +ACC"); A.CallTo(() => _companyCertificateRepository.CheckCompanyCertificateType(CompanyCertificateTypeId.IATF)) .Returns(false); @@ -895,7 +895,7 @@ public async Task CheckCompanyCertificateType_WithInvalidCall_ThrowsControllerFo // Assert var ex = await Assert.ThrowsAsync(Act); - ex.Message.Should().Be($"Issuer length must be 3-40 characters and *+=#%\\s not used as one of the first three characters in the company name"); + ex.Message.Should().Be(ValidationExpressionErrors.INCORRECT_COMPANY_NAME.ToString()); } #endregion diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/InvitationBusinessLogicTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/InvitationBusinessLogicTests.cs index d36048f7d1..43c3c8d195 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; @@ -71,23 +72,6 @@ public async Task ExecuteInvitation_WithoutEmail_ThrowsControllerArgumentExcepti ex.ParamName.Should().Be("email"); } - [Fact] - public async Task ExecuteInvitation_WithoutOrganisationName_ThrowsControllerArgumentException() - { - var invitationData = _fixture.Build() - .With(x => x.OrganisationName, (string?)null) - .WithNamePattern(x => x.FirstName) - .WithNamePattern(x => x.LastName) - .WithEmailPattern(x => x.Email) - .Create(); - - async Task Act() => await _sut.ExecuteInvitation(invitationData); - - var ex = await Assert.ThrowsAsync(Act); - ex.Message.Should().Be("organisationName must not be empty (Parameter 'organisationName')"); - ex.ParamName.Should().Be("organisationName"); - } - [Fact] public async Task ExecuteInvitation_WithValidData_CreatesExpected() { @@ -112,6 +96,48 @@ 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: ÆÐÞ")] + 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(null)] // null value + [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(ValidationExpressionErrors.INCORRECT_COMPANY_NAME.ToString()); + } + #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..2e8f9f0d50 --- /dev/null +++ b/tests/framework/Framework.Models.Tests/ValidationExpressionsTests.cs @@ -0,0 +1,65 @@ + +/******************************************************************************** + * Copyright (c) 2024 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 +{ + [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 = companyName.IsValidCompanyName(); + Assert.Equal(expectedResult, result); + } +}