Files
jobtrackingapp/JobTrackerApi/Services/ProEntitlementAuthorization.cs
T

75 lines
2.7 KiB
C#

using System.Security.Claims;
using System.Text.Json;
using JobTrackerApi.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Policy;
using Microsoft.AspNetCore.Identity;
namespace JobTrackerApi.Services;
public static class ProEntitlement
{
public const string Policy = "Pro";
public const string RequiredCode = "pro_required";
public const string DisabledCode = "ai_disabled";
}
public sealed class ProEntitlementRequirement : IAuthorizationRequirement;
public sealed class ProEntitlementHandler(UserManager<ApplicationUser> users)
: AuthorizationHandler<ProEntitlementRequirement>
{
protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context,
ProEntitlementRequirement requirement)
{
var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier)
?? context.User.FindFirstValue("sub");
var user = string.IsNullOrWhiteSpace(userId) ? null : await users.FindByIdAsync(userId);
if (user is null || !AccountPlans.ForRoles(await users.GetRolesAsync(user)).Ai)
{
context.Fail(new AuthorizationFailureReason(this, ProEntitlement.RequiredCode));
return;
}
if (!user.AiEnabled)
{
context.Fail(new AuthorizationFailureReason(this, ProEntitlement.DisabledCode));
return;
}
context.Succeed(requirement);
}
}
public sealed class ProEntitlementAuthorizationResultHandler : IAuthorizationMiddlewareResultHandler
{
private readonly AuthorizationMiddlewareResultHandler _fallback = new();
public async Task HandleAsync(
RequestDelegate next,
HttpContext context,
AuthorizationPolicy policy,
PolicyAuthorizationResult authorizeResult)
{
if (authorizeResult.Forbidden
&& policy.Requirements.OfType<ProEntitlementRequirement>().Any())
{
var aiDisabled = authorizeResult.AuthorizationFailure?.FailureReasons
.Any(reason => reason.Message == ProEntitlement.DisabledCode) == true;
context.Response.StatusCode = StatusCodes.Status403Forbidden;
context.Response.ContentType = "application/json";
await JsonSerializer.SerializeAsync(context.Response.Body, new
{
code = aiDisabled ? ProEntitlement.DisabledCode : ProEntitlement.RequiredCode,
message = aiDisabled
? "AI is disabled in your privacy settings."
: "This AI feature requires Pro.",
}, cancellationToken: context.RequestAborted);
return;
}
await _fallback.HandleAsync(next, context, policy, authorizeResult);
}
}