From cfe2875a6786927564a6cbc555ab131b5fc75cd5 Mon Sep 17 00:00:00 2001 From: Abdullah Atta Date: Tue, 7 Oct 2025 16:43:41 +0500 Subject: [PATCH] api: refactor user subscription check for monograph embed & links --- .../Authorization/ProUserRequirement.cs | 69 ------------------- .../Controllers/MonographsController.cs | 4 +- .../Extensions/ClaimsPrincipalExtensions.cs | 14 ++++ Notesnook.API/Startup.cs | 8 +-- 4 files changed, 17 insertions(+), 78 deletions(-) delete mode 100644 Notesnook.API/Authorization/ProUserRequirement.cs create mode 100644 Notesnook.API/Extensions/ClaimsPrincipalExtensions.cs diff --git a/Notesnook.API/Authorization/ProUserRequirement.cs b/Notesnook.API/Authorization/ProUserRequirement.cs deleted file mode 100644 index 3380652..0000000 --- a/Notesnook.API/Authorization/ProUserRequirement.cs +++ /dev/null @@ -1,69 +0,0 @@ -/* -This file is part of the Notesnook Sync Server project (https://notesnook.com/) - -Copyright (C) 2023 Streetwriters (Private) Limited - -This program is free software: you can redistribute it and/or modify -it under the terms of the Affero GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -Affero GNU General Public License for more details. - -You should have received a copy of the Affero GNU General Public License -along with this program. If not, see . -*/ - -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Security.Claims; -using System.Text.Json; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; - -namespace Notesnook.API.Authorization -{ - public class ProUserRequirement : AuthorizationHandler, IAuthorizationRequirement - { - private readonly Dictionary pathErrorPhraseMap = new() - { - ["/s3"] = "upload attachments", - ["/s3/multipart"] = "upload attachments", - }; - 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 = IsUserPro(context.User) || IsUserTrialing(context.User); - if (isProOrTrial) context.Succeed(requirement); - else - { - var phrase = "continue"; - foreach (var item in pathErrorPhraseMap) - { - if (path != null && path.StartsWithSegments(item.Key)) - phrase = item.Value; - } - var error = $"Please upgrade to Pro to {phrase}."; - context.Fail(new AuthorizationFailureReason(this, error)); - } - return Task.CompletedTask; - } - - public override Task HandleAsync(AuthorizationHandlerContext context) - { - return this.HandleRequirementAsync(context, this); - } - } -} \ No newline at end of file diff --git a/Notesnook.API/Controllers/MonographsController.cs b/Notesnook.API/Controllers/MonographsController.cs index 6650319..3d0282c 100644 --- a/Notesnook.API/Controllers/MonographsController.cs +++ b/Notesnook.API/Controllers/MonographsController.cs @@ -334,7 +334,7 @@ namespace Notesnook.API.Controllers { var json = JsonSerializer.Deserialize(content); var html = json.Data; - if (!Constants.IS_SELF_HOSTED && !ProUserRequirement.IsUserPro(User)) + if (!Constants.IS_SELF_HOSTED && !User.IsUserSubscribed()) { var config = Configuration.Default.WithDefaultLoader(); var context = BrowsingContext.New(config); @@ -346,7 +346,7 @@ namespace Notesnook.API.Controllers html = document.ToHtml(); } - if (ProUserRequirement.IsUserPro(User)) + if (User.IsUserSubscribed()) { var config = Configuration.Default.WithDefaultLoader(); var context = BrowsingContext.New(config); diff --git a/Notesnook.API/Extensions/ClaimsPrincipalExtensions.cs b/Notesnook.API/Extensions/ClaimsPrincipalExtensions.cs new file mode 100644 index 0000000..c57dd30 --- /dev/null +++ b/Notesnook.API/Extensions/ClaimsPrincipalExtensions.cs @@ -0,0 +1,14 @@ +using System.Threading; +using System; +using System.Threading.Tasks; +using System.Linq; + +namespace System.Security.Claims +{ + public static class ClaimsPrincipalExtensions + { + private readonly static string[] SUBSCRIBED_CLAIMS = ["believer", "education", "essential", "pro", "premium", "premium_canceled"]; + public static bool IsUserSubscribed(this ClaimsPrincipal user) + => user.Claims.Any((c) => c.Type == "notesnook:status" && SUBSCRIBED_CLAIMS.Contains(c.Value)); + } +} \ No newline at end of file diff --git a/Notesnook.API/Startup.cs b/Notesnook.API/Startup.cs index 76cf0de..baeaf5c 100644 --- a/Notesnook.API/Startup.cs +++ b/Notesnook.API/Startup.cs @@ -112,13 +112,7 @@ namespace Notesnook.API policy.RequireAuthenticatedUser(); policy.Requirements.Add(new SyncRequirement()); }); - options.AddPolicy("Pro", policy => - { - policy.AuthenticationSchemes.Add("introspection"); - policy.RequireAuthenticatedUser(); - policy.Requirements.Add(new SyncRequirement()); - policy.Requirements.Add(new ProUserRequirement()); - }); + options.AddPolicy(InboxApiKeyAuthenticationDefaults.AuthenticationScheme, policy => { policy.AuthenticationSchemes.Add(InboxApiKeyAuthenticationDefaults.AuthenticationScheme);