From 51ca07033c8dbb5ef8c1abb54315c0b4b4242cda Mon Sep 17 00:00:00 2001 From: Engincan VESKE <43685404+EngincanV@users.noreply.github.com> Date: Tue, 4 Apr 2023 10:52:29 +0300 Subject: [PATCH 1/5] Cms: Don't allow external URLs --- .../CmsKitWebUnifiedModule.cs | 5 ++ .../Pages/Index.cshtml | 2 +- .../CmsKit/Comments/CmsKitCommentOptions.cs | 6 +++ .../CmsKit/Localization/Resources/en.json | 3 +- .../Public/Comments/CreateCommentInput.cs | 2 + ...cs => CreateCommentWithParametersInput.cs} | 4 +- .../Public/Comments/UpdateCommentInput.cs | 2 + .../Comments/CommentPublicAppService.cs | 54 +++++++++++++++++-- .../CmsKitPublicWebAutoMapperProfile.cs | 2 +- .../CmsKitPublicCommentsController.cs | 4 +- .../Commenting/CommentingViewComponent.cs | 6 ++- .../Components/Commenting/Default.cshtml | 2 + .../Shared/Components/Commenting/default.js | 6 ++- 13 files changed, 85 insertions(+), 13 deletions(-) rename modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/{CreateCommentWithParameteresInput.cs => CreateCommentWithParametersInput.cs} (84%) diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs index 763218edc4..429d6b010c 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.IO; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; @@ -168,6 +169,10 @@ public class CmsKitWebUnifiedModule : AbpModule { options.EntityTypes.Add(new CommentEntityTypeDefinition("quote")); options.IsRecaptchaEnabled = true; + options.AllowedExternalUrls = new List + { + "https://abp.io/" + }; }); Configure(options => diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml index 1b3f3ff94c..74e86b7c76 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml @@ -90,7 +90,7 @@ @if (GlobalFeatureManager.Instance.IsEnabled()) { - @await Component.InvokeAsync(typeof(CommentingViewComponent), new {entityType = "quote", entityId = "2"}) + @await Component.InvokeAsync(typeof(CommentingViewComponent), new {entityType = "quote", entityId = "2", allowExternalUrls = false}) } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CmsKitCommentOptions.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CmsKitCommentOptions.cs index 94d3b785ec..c163c8da85 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CmsKitCommentOptions.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CmsKitCommentOptions.cs @@ -13,4 +13,10 @@ public class CmsKitCommentOptions /// Default: false /// public bool IsRecaptchaEnabled { get; set; } + + /// + /// Indicates the allowed external URLs, which can be included in a comment. + /// If it's not specified, all external URLs will be allowed. + /// + public List AllowedExternalUrls { get; set; } = new(); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json index 9bb8dd275c..2f7d8ce13d 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json @@ -216,6 +216,7 @@ "CaptchaCode": "Captcha code", "CommentTextRequired": "Comment is required", "CaptchaCodeErrorMessage" : "The answer you entered for the CAPTCHA was not correct. Please try again", - "CaptchaCodeMissingMessage": "The captcha code is missing!" + "CaptchaCodeMissingMessage": "The captcha code is missing!", + "UnAllowedExternalUrlMessage": "You included an unallowed external URL. Please try again without the external URL." } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentInput.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentInput.cs index c0eae8650e..ef78636ea7 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentInput.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentInput.cs @@ -17,4 +17,6 @@ public class CreateCommentInput public Guid? CaptchaToken { get; set; } public int CaptchaAnswer { get; set; } + + public bool AllowExternalUrls { get; set; } = true; } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentWithParameteresInput.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentWithParametersInput.cs similarity index 84% rename from modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentWithParameteresInput.cs rename to modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentWithParametersInput.cs index 7e156ef375..d8f946d1a0 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentWithParameteresInput.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentWithParametersInput.cs @@ -6,7 +6,7 @@ using Volo.CmsKit.Comments; namespace Volo.CmsKit.Public.Comments; [Serializable] -public class CreateCommentWithParameteresInput +public class CreateCommentWithParametersInput { [Required] [DynamicStringLength(typeof(CommentConsts), nameof(CommentConsts.MaxTextLength))] @@ -18,6 +18,8 @@ public class CreateCommentWithParameteresInput [Required] public string EntityId { get; set; } + public bool AllowExternalUrls { get; set; } = true; + public Guid? RepliedCommentId { get; set; } public Guid? CaptchaToken { get; set; } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/UpdateCommentInput.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/UpdateCommentInput.cs index 7ec1279704..0992622603 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/UpdateCommentInput.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/UpdateCommentInput.cs @@ -14,4 +14,6 @@ public class UpdateCommentInput : IHasConcurrencyStamp public string Text { get; set; } public string ConcurrencyStamp { get; set; } + + public bool AllowExternalUrls { get; set; } = true; } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs index 05857e5db9..3d84a0ecf6 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Security; +using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.Options; +using Volo.Abp; using Volo.Abp.Application.Dtos; using Volo.Abp.Authorization; using Volo.Abp.Data; @@ -23,24 +25,27 @@ namespace Volo.CmsKit.Public.Comments; [RequiresGlobalFeature(typeof(CommentsFeature))] public class CommentPublicAppService : CmsKitPublicAppServiceBase, ICommentPublicAppService { + protected string RegexMarkdownUrlPattern = @"\[[^\]]*\]\((?.*?)\)(?![^\x60]*\x60)"; + protected ICommentRepository CommentRepository { get; } protected ICmsUserLookupService CmsUserLookupService { get; } public IDistributedEventBus DistributedEventBus { get; } protected CommentManager CommentManager { get; } - protected IAuthorizationService AuthorizationService { get; } + + protected CmsKitCommentOptions CmsCommentOptions { get; } public CommentPublicAppService( ICommentRepository commentRepository, ICmsUserLookupService cmsUserLookupService, IDistributedEventBus distributedEventBus, CommentManager commentManager, - IAuthorizationService authorizationService) + IOptionsSnapshot cmsCommentOptions) { CommentRepository = commentRepository; CmsUserLookupService = cmsUserLookupService; DistributedEventBus = distributedEventBus; CommentManager = commentManager; - AuthorizationService = authorizationService; + CmsCommentOptions = cmsCommentOptions.Value; } public virtual async Task> GetListAsync(string entityType, string entityId) @@ -56,6 +61,8 @@ public class CommentPublicAppService : CmsKitPublicAppServiceBase, ICommentPubli [Authorize] public virtual async Task CreateAsync(string entityType, string entityId, CreateCommentInput input) { + CheckExternalUrls(input.AllowExternalUrls, input.Text); + var user = await CmsUserLookupService.GetByIdAsync(CurrentUser.GetId()); if (input.RepliedCommentId.HasValue) @@ -86,6 +93,8 @@ public class CommentPublicAppService : CmsKitPublicAppServiceBase, ICommentPubli [Authorize] public virtual async Task UpdateAsync(Guid id, UpdateCommentInput input) { + CheckExternalUrls(input.AllowExternalUrls, input.Text); + var comment = await CommentRepository.GetAsync(id); if (comment.CreatorId != CurrentUser.GetId()) @@ -148,4 +157,41 @@ public class CommentPublicAppService : CmsKitPublicAppServiceBase, ICommentPubli { return ObjectMapper.Map(comments.Single(c => c.Comment.Id == commentId).Author); } + + private void CheckExternalUrls(bool allowExternalUrls, string text) + { + if (allowExternalUrls || !CmsCommentOptions.AllowedExternalUrls.Any()) + { + return; + } + + var matches = Regex.Matches(text, RegexMarkdownUrlPattern, + RegexOptions.Compiled | RegexOptions.IgnoreCase); + + foreach (Match match in matches) + { + if (!match.Success || match.Groups.Count < 2) + { + continue; + } + + var url = match.Groups[1].Value; + if (!IsExternalUrl(url)) + { + continue; + } + + if (!CmsCommentOptions.AllowedExternalUrls.Contains(url.Replace("www.", "").RemovePostFix("/"), + StringComparer.InvariantCultureIgnoreCase)) + { + throw new UserFriendlyException(L["UnAllowedExternalUrlMessage"]); + } + } + } + + private static bool IsExternalUrl(string url) + { + return url.StartsWith("https", StringComparison.InvariantCultureIgnoreCase) || + url.StartsWith("http", StringComparison.InvariantCultureIgnoreCase); + } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebAutoMapperProfile.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebAutoMapperProfile.cs index 3c44f6fa1c..744d5afbad 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebAutoMapperProfile.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebAutoMapperProfile.cs @@ -8,6 +8,6 @@ public class CmsKitPublicWebAutoMapperProfile : Profile { public CmsKitPublicWebAutoMapperProfile() { - CreateMap(); + CreateMap(); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Controllers/CmsKitPublicCommentsController.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Controllers/CmsKitPublicCommentsController.cs index bb2487587b..e10a606826 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Controllers/CmsKitPublicCommentsController.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Controllers/CmsKitPublicCommentsController.cs @@ -29,14 +29,14 @@ public class CmsKitPublicCommentsController : AbpController } [HttpPost] - public async Task ValidateAsync([FromBody] CreateCommentWithParameteresInput input) + public async Task ValidateAsync([FromBody] CreateCommentWithParametersInput input) { if (CmsKitCommentOptions.IsRecaptchaEnabled && input.CaptchaToken.HasValue) { SimpleMathsCaptchaGenerator.Validate(input.CaptchaToken.Value, input.CaptchaAnswer); } - var dto = ObjectMapper.Map (input); + var dto = ObjectMapper.Map (input); await CommentPublicAppService.CreateAsync(input.EntityType, input.EntityId, dto); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs index 304cfec687..fd7949674b 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs @@ -60,7 +60,8 @@ public class CommentingViewComponent : AbpViewComponent public virtual async Task InvokeAsync( string entityType, string entityId, - IEnumerable referralLinks = null) + IEnumerable referralLinks = null, + bool allowExternalUrls = true) { referralLinks ??= Enumerable.Empty(); var comments = (await CommentPublicAppService @@ -72,6 +73,7 @@ public class CommentingViewComponent : AbpViewComponent { EntityId = entityId, EntityType = entityType, + AllowExternalUrls = allowExternalUrls, ReferralLinks = referralLinks, LoginUrl = loginUrl, Comments = comments.OrderByDescending(i => i.CreationTime).ToList() @@ -121,6 +123,8 @@ public class CommentingViewComponent : AbpViewComponent public string EntityType { get; set; } public string EntityId { get; set; } + + public bool AllowExternalUrls { get; set; } public IEnumerable ReferralLinks { get; set; } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml index 944a818d74..d5b9a064d0 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml @@ -32,6 +32,7 @@ style="@(string.IsNullOrEmpty(repliedCommentId?.ToString() ?? "") ? "" : "display:none")">
+
@@ -120,6 +121,7 @@
+
diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.js b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.js index 20b7119344..1c49e110f3 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.js +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.js @@ -115,7 +115,8 @@ formAsObject.id, { text: formAsObject.commentText, - concurrencyStamp: formAsObject.commentConcurrencyStamp + concurrencyStamp: formAsObject.commentConcurrencyStamp, + allowExternalUrls: formAsObject.allowExternalUrls } ).then(function () { widgetManager.refresh($widget); @@ -151,7 +152,8 @@ repliedCommentId: formAsObject.repliedCommentId, text: formAsObject.commentText, captchaToken: formAsObject.captchaId, - captchaAnswer: formAsObject.input?.captcha + captchaAnswer: formAsObject.input?.captcha, + allowExternalUrls: formAsObject.allowExternalUrls }), success: function () { widgetManager.refresh($widget); From 1d9bafbf5a73af22cacf88320e7abd072fcd7dca Mon Sep 17 00:00:00 2001 From: Engincan VESKE <43685404+EngincanV@users.noreply.github.com> Date: Tue, 4 Apr 2023 11:05:15 +0300 Subject: [PATCH 2/5] Cms: Add tests for comments --- .../CmsKit/Comments/CmsKitCommentOptions.cs | 1 - .../Comments/CommentPublicAppService.cs | 7 +++- .../CmsKitApplicationTestModule.cs | 12 +++++-- .../Comments/CommentPublicAppService_Tests.cs | 33 +++++++++++++++++++ 4 files changed, 49 insertions(+), 4 deletions(-) diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CmsKitCommentOptions.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CmsKitCommentOptions.cs index c163c8da85..ffa1097aab 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CmsKitCommentOptions.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CmsKitCommentOptions.cs @@ -16,7 +16,6 @@ public class CmsKitCommentOptions /// /// Indicates the allowed external URLs, which can be included in a comment. - /// If it's not specified, all external URLs will be allowed. /// public List AllowedExternalUrls { get; set; } = new(); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs index 3d84a0ecf6..8809d8168e 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs @@ -160,11 +160,16 @@ public class CommentPublicAppService : CmsKitPublicAppServiceBase, ICommentPubli private void CheckExternalUrls(bool allowExternalUrls, string text) { - if (allowExternalUrls || !CmsCommentOptions.AllowedExternalUrls.Any()) + if (allowExternalUrls) { return; } + if (!CmsCommentOptions.AllowedExternalUrls.Any()) + { + throw new UserFriendlyException(L["UnAllowedExternalUrlMessage"]); + } + var matches = Regex.Matches(text, RegexMarkdownUrlPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); diff --git a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/CmsKitApplicationTestModule.cs b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/CmsKitApplicationTestModule.cs index e20c222a58..041f77d63c 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/CmsKitApplicationTestModule.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/CmsKitApplicationTestModule.cs @@ -1,4 +1,6 @@ -using Volo.Abp.Modularity; +using System.Collections.Generic; +using Volo.Abp.Modularity; +using Volo.CmsKit.Comments; namespace Volo.CmsKit; @@ -8,5 +10,11 @@ namespace Volo.CmsKit; )] public class CmsKitApplicationTestModule : AbpModule { - + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.AllowedExternalUrls = new List { "https://abp.io" }; + }); + } } diff --git a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Comments/CommentPublicAppService_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Comments/CommentPublicAppService_Tests.cs index d0e698937d..e9e7cc817b 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Comments/CommentPublicAppService_Tests.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Comments/CommentPublicAppService_Tests.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using NSubstitute; using Shouldly; +using Volo.Abp; using Volo.Abp.Users; using Volo.CmsKit.Public.Comments; using Xunit; @@ -62,6 +63,23 @@ public class CommentPublicAppService_Tests : CmsKitApplicationTestBase }); } + [Fact] + public async Task CreateAsync_ShouldThrowUserFriendlyException_If_Url_UnAllowed() + { + _currentUser.Id.Returns(_cmsKitTestData.User2Id); + + await Should.ThrowAsync(async () => + await _commentAppService.CreateAsync( + _cmsKitTestData.EntityType1, + _cmsKitTestData.EntityId1, + new CreateCommentInput { + RepliedCommentId = null, + Text = "[ABP Community](https://community.abp.io/)", //not allowed URL + AllowExternalUrls = false + } + )); + } + [Fact] public async Task UpdateAsync() { @@ -80,6 +98,21 @@ public class CommentPublicAppService_Tests : CmsKitApplicationTestBase comment.Text.ShouldBe("I'm Updated"); }); } + + [Fact] + public async Task UpdateAsync_ShouldThrowUserFriendlyException_If_Url_UnAllowed() + { + _currentUser.Id.Returns(_cmsKitTestData.User2Id); + + await Should.ThrowAsync(async () => + await _commentAppService.UpdateAsync( + _cmsKitTestData.CommentWithChildId, + new UpdateCommentInput { + Text = "[ABP Community - Update](https://community.abp.io/)", //not allowed URL + AllowExternalUrls = false + } + )); + } [Fact] public async Task DeleteAsync() From 364b1270243ad36371bab8e97992cc07f8bb8d1a Mon Sep 17 00:00:00 2001 From: Engincan VESKE <43685404+EngincanV@users.noreply.github.com> Date: Tue, 4 Apr 2023 11:20:22 +0300 Subject: [PATCH 3/5] Update Cms-Kit/Comments.md --- docs/en/Modules/Cms-Kit/Comments.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/en/Modules/Cms-Kit/Comments.md b/docs/en/Modules/Cms-Kit/Comments.md index 26aa7c0398..e317fc7115 100644 --- a/docs/en/Modules/Cms-Kit/Comments.md +++ b/docs/en/Modules/Cms-Kit/Comments.md @@ -19,6 +19,10 @@ Configure(options => { options.EntityTypes.Add(new CommentEntityTypeDefinition("Product")); options.IsRecaptchaEnabled = true; //false by default + options.AllowedExternalUrls = new List + { + "https://abp.io/" + } }); ``` @@ -28,6 +32,7 @@ Configure(options => - `EntityTypes`: List of defined entity types(`CmsKitCommentOptions`) in the comment system. - `IsRecaptchaEnabled`: This flag enables or disables the reCaptcha for the comment system. You can set it as **true** if you want to use reCaptcha in your comment system. +- `AllowedExternalUrls`: Indicates the allowed external URLs, which can be included in a comment. These external URLs are only taken into consideration if the external URLs are not allowed in a comment widget. `CommentEntityTypeDefinition` properties: @@ -42,11 +47,12 @@ The comment system provides a commenting [widget](../../UI/AspNetCore/Widgets.md { entityType = "Product", entityId = "...", - referralLinks = new [] {"nofollow"} + referralLinks = new [] {"nofollow"}, + allowExternalUrls = false //default: "true" }) ``` -`entityType` was explained in the previous section. `entityId` should be the unique id of the product, in this example. If you have a Product entity, you can use its Id here. `referralLinks` is an optional parameter. You can use this parameter to add values (such as "nofollow", "noreferrer", or any other values) to the [rel attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel) of links. +`entityType` was explained in the previous section. `entityId` should be the unique id of the product, in this example. If you have a Product entity, you can use its Id here. `referralLinks` is an optional parameter. You can use this parameter to add values (such as "nofollow", "noreferrer", or any other values) to the [rel attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel) of links. `allowExternalUrls` is also an optional parameter and by default, external URLs are allowed. You can use this parameter to specify the allowed external URLs (and disallow other external URLs) by configuring the `CmsKitCommentOptions` as mentioned in the previous section. ## User Interface From 697bbf9a1366e301986e6dfb84655c13b2fb9b6c Mon Sep 17 00:00:00 2001 From: Engincan VESKE <43685404+EngincanV@users.noreply.github.com> Date: Tue, 4 Apr 2023 15:05:39 +0300 Subject: [PATCH 4/5] Cms: Refactoring --- .../CmsKitWebUnifiedModule.cs | 10 +++++-- .../Pages/Index.cshtml | 2 +- .../CmsKit/Comments/CmsKitCommentOptions.cs | 4 +-- .../Public/Comments/CreateCommentInput.cs | 2 -- .../CreateCommentWithParametersInput.cs | 2 -- .../Public/Comments/UpdateCommentInput.cs | 2 -- .../Comments/CommentPublicAppService.cs | 27 +++++++++---------- .../Commenting/CommentingViewComponent.cs | 6 +---- .../Components/Commenting/Default.cshtml | 2 -- .../Shared/Components/Commenting/default.js | 6 ++--- .../CmsKitApplicationTestModule.cs | 11 +++++++- .../Comments/CommentPublicAppService_Tests.cs | 10 +++---- 12 files changed, 42 insertions(+), 42 deletions(-) diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs index 429d6b010c..f4f746a61d 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs @@ -169,9 +169,15 @@ public class CmsKitWebUnifiedModule : AbpModule { options.EntityTypes.Add(new CommentEntityTypeDefinition("quote")); options.IsRecaptchaEnabled = true; - options.AllowedExternalUrls = new List + options.AllowedExternalUrls = new Dictionary> { - "https://abp.io/" + { + "quote", + new List + { + "https://abp.io/" + } + } }; }); diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml index 74e86b7c76..1b3f3ff94c 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/Pages/Index.cshtml @@ -90,7 +90,7 @@ @if (GlobalFeatureManager.Instance.IsEnabled()) { - @await Component.InvokeAsync(typeof(CommentingViewComponent), new {entityType = "quote", entityId = "2", allowExternalUrls = false}) + @await Component.InvokeAsync(typeof(CommentingViewComponent), new {entityType = "quote", entityId = "2"}) } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CmsKitCommentOptions.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CmsKitCommentOptions.cs index ffa1097aab..1346541e69 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CmsKitCommentOptions.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Comments/CmsKitCommentOptions.cs @@ -15,7 +15,7 @@ public class CmsKitCommentOptions public bool IsRecaptchaEnabled { get; set; } /// - /// Indicates the allowed external URLs, which can be included in a comment. + /// Indicates the allowed external URLs by entity types, which can be included in a comment. /// - public List AllowedExternalUrls { get; set; } = new(); + public Dictionary> AllowedExternalUrls { get; set; } = new(); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentInput.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentInput.cs index ef78636ea7..c0eae8650e 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentInput.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentInput.cs @@ -17,6 +17,4 @@ public class CreateCommentInput public Guid? CaptchaToken { get; set; } public int CaptchaAnswer { get; set; } - - public bool AllowExternalUrls { get; set; } = true; } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentWithParametersInput.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentWithParametersInput.cs index d8f946d1a0..bfdbd0a95d 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentWithParametersInput.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/CreateCommentWithParametersInput.cs @@ -18,8 +18,6 @@ public class CreateCommentWithParametersInput [Required] public string EntityId { get; set; } - public bool AllowExternalUrls { get; set; } = true; - public Guid? RepliedCommentId { get; set; } public Guid? CaptchaToken { get; set; } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/UpdateCommentInput.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/UpdateCommentInput.cs index 0992622603..7ec1279704 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/UpdateCommentInput.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Comments/UpdateCommentInput.cs @@ -14,6 +14,4 @@ public class UpdateCommentInput : IHasConcurrencyStamp public string Text { get; set; } public string ConcurrencyStamp { get; set; } - - public bool AllowExternalUrls { get; set; } = true; } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs index 8809d8168e..4a7bd8be8c 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Comments/CommentPublicAppService.cs @@ -61,7 +61,7 @@ public class CommentPublicAppService : CmsKitPublicAppServiceBase, ICommentPubli [Authorize] public virtual async Task CreateAsync(string entityType, string entityId, CreateCommentInput input) { - CheckExternalUrls(input.AllowExternalUrls, input.Text); + CheckExternalUrls(entityType, input.Text); var user = await CmsUserLookupService.GetByIdAsync(CurrentUser.GetId()); @@ -93,14 +93,13 @@ public class CommentPublicAppService : CmsKitPublicAppServiceBase, ICommentPubli [Authorize] public virtual async Task UpdateAsync(Guid id, UpdateCommentInput input) { - CheckExternalUrls(input.AllowExternalUrls, input.Text); - var comment = await CommentRepository.GetAsync(id); - if (comment.CreatorId != CurrentUser.GetId()) { throw new AbpAuthorizationException(); } + + CheckExternalUrls(comment.EntityType, input.Text); comment.SetText(input.Text); comment.SetConcurrencyStampIfNotNull(input.ConcurrencyStamp); @@ -158,18 +157,13 @@ public class CommentPublicAppService : CmsKitPublicAppServiceBase, ICommentPubli return ObjectMapper.Map(comments.Single(c => c.Comment.Id == commentId).Author); } - private void CheckExternalUrls(bool allowExternalUrls, string text) + private void CheckExternalUrls(string entityType, string text) { - if (allowExternalUrls) + if (!CmsCommentOptions.AllowedExternalUrls.TryGetValue(entityType, out var allowedExternalUrls)) { return; } - if (!CmsCommentOptions.AllowedExternalUrls.Any()) - { - throw new UserFriendlyException(L["UnAllowedExternalUrlMessage"]); - } - var matches = Regex.Matches(text, RegexMarkdownUrlPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); @@ -180,14 +174,14 @@ public class CommentPublicAppService : CmsKitPublicAppServiceBase, ICommentPubli continue; } - var url = match.Groups[1].Value; + var url = NormalizeUrl(match.Groups[1].Value); if (!IsExternalUrl(url)) { continue; } - if (!CmsCommentOptions.AllowedExternalUrls.Contains(url.Replace("www.", "").RemovePostFix("/"), - StringComparer.InvariantCultureIgnoreCase)) + if (!allowedExternalUrls.Any(allowedExternalUrl => + url.Contains(NormalizeUrl(allowedExternalUrl), StringComparison.OrdinalIgnoreCase))) { throw new UserFriendlyException(L["UnAllowedExternalUrlMessage"]); } @@ -199,4 +193,9 @@ public class CommentPublicAppService : CmsKitPublicAppServiceBase, ICommentPubli return url.StartsWith("https", StringComparison.InvariantCultureIgnoreCase) || url.StartsWith("http", StringComparison.InvariantCultureIgnoreCase); } + + private static string NormalizeUrl(string url) + { + return url.Replace("www.", "").RemovePostFix("/"); + } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs index fd7949674b..b1ea42397f 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/CommentingViewComponent.cs @@ -60,8 +60,7 @@ public class CommentingViewComponent : AbpViewComponent public virtual async Task InvokeAsync( string entityType, string entityId, - IEnumerable referralLinks = null, - bool allowExternalUrls = true) + IEnumerable referralLinks = null) { referralLinks ??= Enumerable.Empty(); var comments = (await CommentPublicAppService @@ -73,7 +72,6 @@ public class CommentingViewComponent : AbpViewComponent { EntityId = entityId, EntityType = entityType, - AllowExternalUrls = allowExternalUrls, ReferralLinks = referralLinks, LoginUrl = loginUrl, Comments = comments.OrderByDescending(i => i.CreationTime).ToList() @@ -124,8 +122,6 @@ public class CommentingViewComponent : AbpViewComponent public string EntityId { get; set; } - public bool AllowExternalUrls { get; set; } - public IEnumerable ReferralLinks { get; set; } public string LoginUrl { get; set; } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml index d5b9a064d0..944a818d74 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/Default.cshtml @@ -32,7 +32,6 @@ style="@(string.IsNullOrEmpty(repliedCommentId?.ToString() ?? "") ? "" : "display:none")"> -
@@ -121,7 +120,6 @@
-
diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.js b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.js index 1c49e110f3..20b7119344 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.js +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Commenting/default.js @@ -115,8 +115,7 @@ formAsObject.id, { text: formAsObject.commentText, - concurrencyStamp: formAsObject.commentConcurrencyStamp, - allowExternalUrls: formAsObject.allowExternalUrls + concurrencyStamp: formAsObject.commentConcurrencyStamp } ).then(function () { widgetManager.refresh($widget); @@ -152,8 +151,7 @@ repliedCommentId: formAsObject.repliedCommentId, text: formAsObject.commentText, captchaToken: formAsObject.captchaId, - captchaAnswer: formAsObject.input?.captcha, - allowExternalUrls: formAsObject.allowExternalUrls + captchaAnswer: formAsObject.input?.captcha }), success: function () { widgetManager.refresh($widget); diff --git a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/CmsKitApplicationTestModule.cs b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/CmsKitApplicationTestModule.cs index 041f77d63c..e0dba22140 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/CmsKitApplicationTestModule.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/CmsKitApplicationTestModule.cs @@ -14,7 +14,16 @@ public class CmsKitApplicationTestModule : AbpModule { Configure(options => { - options.AllowedExternalUrls = new List { "https://abp.io" }; + options.AllowedExternalUrls = new Dictionary> + { + { + "EntityName1", + new List + { + "https://abp.io/" + } + } + }; }); } } diff --git a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Comments/CommentPublicAppService_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Comments/CommentPublicAppService_Tests.cs index e9e7cc817b..ddd913a93f 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Comments/CommentPublicAppService_Tests.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Comments/CommentPublicAppService_Tests.cs @@ -72,10 +72,10 @@ public class CommentPublicAppService_Tests : CmsKitApplicationTestBase await _commentAppService.CreateAsync( _cmsKitTestData.EntityType1, _cmsKitTestData.EntityId1, - new CreateCommentInput { + new CreateCommentInput + { RepliedCommentId = null, Text = "[ABP Community](https://community.abp.io/)", //not allowed URL - AllowExternalUrls = false } )); } @@ -102,14 +102,14 @@ public class CommentPublicAppService_Tests : CmsKitApplicationTestBase [Fact] public async Task UpdateAsync_ShouldThrowUserFriendlyException_If_Url_UnAllowed() { - _currentUser.Id.Returns(_cmsKitTestData.User2Id); + _currentUser.Id.Returns(_cmsKitTestData.User1Id); await Should.ThrowAsync(async () => await _commentAppService.UpdateAsync( _cmsKitTestData.CommentWithChildId, - new UpdateCommentInput { + new UpdateCommentInput + { Text = "[ABP Community - Update](https://community.abp.io/)", //not allowed URL - AllowExternalUrls = false } )); } From 7fb0582d830e5b3309ef37bfdc27cb54d5686366 Mon Sep 17 00:00:00 2001 From: Engincan VESKE <43685404+EngincanV@users.noreply.github.com> Date: Tue, 4 Apr 2023 15:14:33 +0300 Subject: [PATCH 5/5] Update Comments.md --- docs/en/Modules/Cms-Kit/Comments.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/en/Modules/Cms-Kit/Comments.md b/docs/en/Modules/Cms-Kit/Comments.md index e317fc7115..af335f1227 100644 --- a/docs/en/Modules/Cms-Kit/Comments.md +++ b/docs/en/Modules/Cms-Kit/Comments.md @@ -19,10 +19,16 @@ Configure(options => { options.EntityTypes.Add(new CommentEntityTypeDefinition("Product")); options.IsRecaptchaEnabled = true; //false by default - options.AllowedExternalUrls = new List + options.AllowedExternalUrls = new Dictionary> { - "https://abp.io/" - } + { + "quote", + new List + { + "https://abp.io/" + } + } + }; }); ``` @@ -32,7 +38,7 @@ Configure(options => - `EntityTypes`: List of defined entity types(`CmsKitCommentOptions`) in the comment system. - `IsRecaptchaEnabled`: This flag enables or disables the reCaptcha for the comment system. You can set it as **true** if you want to use reCaptcha in your comment system. -- `AllowedExternalUrls`: Indicates the allowed external URLs, which can be included in a comment. These external URLs are only taken into consideration if the external URLs are not allowed in a comment widget. +- `AllowedExternalUrls`: Indicates the allowed external URLs by entity types, which can be included in a comment. If it's specified for a certain entity type, then only the specified external URLs are allowed in the comments. `CommentEntityTypeDefinition` properties: @@ -47,12 +53,11 @@ The comment system provides a commenting [widget](../../UI/AspNetCore/Widgets.md { entityType = "Product", entityId = "...", - referralLinks = new [] {"nofollow"}, - allowExternalUrls = false //default: "true" + referralLinks = new [] {"nofollow"} }) ``` -`entityType` was explained in the previous section. `entityId` should be the unique id of the product, in this example. If you have a Product entity, you can use its Id here. `referralLinks` is an optional parameter. You can use this parameter to add values (such as "nofollow", "noreferrer", or any other values) to the [rel attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel) of links. `allowExternalUrls` is also an optional parameter and by default, external URLs are allowed. You can use this parameter to specify the allowed external URLs (and disallow other external URLs) by configuring the `CmsKitCommentOptions` as mentioned in the previous section. +`entityType` was explained in the previous section. `entityId` should be the unique id of the product, in this example. If you have a Product entity, you can use its Id here. `referralLinks` is an optional parameter. You can use this parameter to add values (such as "nofollow", "noreferrer", or any other values) to the [rel attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel) of links. ## User Interface