api: refactor user subscription check for monograph embed & links

This commit is contained in:
Abdullah Atta
2025-10-07 16:43:41 +05:00
committed by Abdullah Atta
parent 9860df2379
commit cfe2875a67
4 changed files with 17 additions and 78 deletions

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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<ProUserRequirement>, IAuthorizationRequirement
{
private readonly Dictionary<string, string> 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);
}
}
}

View File

@@ -334,7 +334,7 @@ namespace Notesnook.API.Controllers
{
var json = JsonSerializer.Deserialize<MonographContent>(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);

View File

@@ -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));
}
}

View File

@@ -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);