From 0f43b3ee6648c48566a220e0fd239f35191e26e4 Mon Sep 17 00:00:00 2001 From: Abdullah Atta Date: Mon, 15 Sep 2025 09:19:16 +0500 Subject: [PATCH] monograph: remove links, embeds & images for non-pro users --- .../Authorization/ProUserRequirement.cs | 10 ++++++-- .../Controllers/MonographsController.cs | 23 +++++++++++++++++-- Notesnook.API/Notesnook.API.csproj | 1 + 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Notesnook.API/Authorization/ProUserRequirement.cs b/Notesnook.API/Authorization/ProUserRequirement.cs index 33eac97..3380652 100644 --- a/Notesnook.API/Authorization/ProUserRequirement.cs +++ b/Notesnook.API/Authorization/ProUserRequirement.cs @@ -35,11 +35,17 @@ namespace Notesnook.API.Authorization ["/s3"] = "upload attachments", ["/s3/multipart"] = "upload attachments", }; - private readonly string[] allowedClaims = ["trial", "premium", "premium_canceled"]; + private static readonly string[] proClaims = ["premium", "premium_canceled"]; + private static readonly string[] trialClaims = ["trial"]; + public static bool IsUserPro(ClaimsPrincipal user) + => user.Claims.Any((c) => c.Type == "notesnook:status" && proClaims.Contains(c.Value)); + public static bool IsUserTrialing(ClaimsPrincipal user) + => user.Claims.Any((c) => c.Type == "notesnook:status" && trialClaims.Contains(c.Value)); + protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ProUserRequirement requirement) { PathString path = context.Resource is DefaultHttpContext httpContext ? httpContext.Request.Path : null; - var isProOrTrial = context.User.Claims.Any((c) => c.Type == "notesnook:status" && allowedClaims.Contains(c.Value)); + var isProOrTrial = IsUserPro(context.User) || IsUserTrialing(context.User); if (isProOrTrial) context.Succeed(requirement); else { diff --git a/Notesnook.API/Controllers/MonographsController.cs b/Notesnook.API/Controllers/MonographsController.cs index 344e5dc..70be5ab 100644 --- a/Notesnook.API/Controllers/MonographsController.cs +++ b/Notesnook.API/Controllers/MonographsController.cs @@ -23,10 +23,13 @@ using System.Linq; using System.Security.Claims; using System.Text.Json; using System.Threading.Tasks; +using AngleSharp; +using AngleSharp.Dom; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using MongoDB.Bson; using MongoDB.Driver; +using Notesnook.API.Authorization; using Notesnook.API.Models; using Notesnook.API.Services; using Streetwriters.Common; @@ -110,7 +113,7 @@ namespace Notesnook.API.Controllers } if (monograph.EncryptedContent == null) - monograph.CompressedContent = monograph.Content.CompressBrotli(); + monograph.CompressedContent = (await CleanupContentAsync(monograph.Content)).CompressBrotli(); monograph.UserId = userId; monograph.DatePublished = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); @@ -161,7 +164,7 @@ namespace Notesnook.API.Controllers return base.BadRequest("Monograph is too big. Max allowed size is 15mb."); if (monograph.EncryptedContent == null) - monograph.CompressedContent = monograph.Content.CompressBrotli(); + monograph.CompressedContent = (await CleanupContentAsync(monograph.Content)).CompressBrotli(); else monograph.Content = null; @@ -321,5 +324,21 @@ namespace Notesnook.API.Controllers } }); } + + private async Task CleanupContentAsync(string content) + { + if (!Constants.IS_SELF_HOSTED && !ProUserRequirement.IsUserPro(User)) + { + var config = Configuration.Default.WithDefaultLoader(); + var context = BrowsingContext.New(config); + var document = await context.OpenAsync(r => r.Content(content)); + foreach (var element in document.QuerySelectorAll("a,iframe,img,object,svg,button,link")) + { + element.Remove(); + } + return document.ToHtml(); + } + return content; + } } } \ No newline at end of file diff --git a/Notesnook.API/Notesnook.API.csproj b/Notesnook.API/Notesnook.API.csproj index 859cb00..eada3ac 100644 --- a/Notesnook.API/Notesnook.API.csproj +++ b/Notesnook.API/Notesnook.API.csproj @@ -6,6 +6,7 @@ +