< Summary

Information
Class: Api.Auth.Handlers.PermissionCheckAuthorizeAttribute
Assembly: Api
File(s): /home/runner/work/ProjectRead.ing/ProjectRead.ing/Api/Auth/Handlers/PermissionCheckAuthorizationHandler.cs
Line coverage
100%
Covered lines: 4
Uncovered lines: 0
Coverable lines: 4
Total lines: 95
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%

File(s)

/home/runner/work/ProjectRead.ing/ProjectRead.ing/Api/Auth/Handlers/PermissionCheckAuthorizationHandler.cs

#LineLine coverage
 1// SPDX-FileCopyrightText: 2026 Alper Çelik <alper@alper-celik.dev>
 2//
 3// SPDX-License-Identifier: AGPL-3.0-or-later
 4
 5using System.Security.Claims;
 6using Api.Auth.Models;
 7using Microsoft.AspNetCore.Authorization;
 8using Microsoft.Extensions.Options;
 9
 10namespace Api.Auth.Handlers;
 11
 12public class PermissionCheckRequirement(UserPermissionBits permissionBits)
 13    : IAuthorizationRequirement
 14{
 15    public UserPermissionBits PermissionBits { get; } = permissionBits;
 16}
 17
 18public class PermissionPolicyProvider(IOptions<AuthorizationOptions> options)
 19    : DefaultAuthorizationPolicyProvider(options)
 20{
 21    public const string POLICY_PREFIX = "PermissionBits_";
 22    public const string RemoteServicePolicyName = "RemoteService";
 23
 24    public override Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
 25    {
 26        if (
 27            policyName.StartsWith(POLICY_PREFIX)
 28            && long.TryParse(policyName[POLICY_PREFIX.Length..], out long permissionBits)
 29        )
 30        {
 31            return Task.FromResult<AuthorizationPolicy?>(
 32                new AuthorizationPolicyBuilder()
 33                    .AddRequirements(
 34                        new PermissionCheckRequirement((UserPermissionBits)permissionBits)
 35                    )
 36                    .Build()
 37            );
 38        }
 39
 40        if (policyName == RemoteServicePolicyName)
 41        {
 42            return Task.FromResult<AuthorizationPolicy?>(
 43                new AuthorizationPolicyBuilder()
 44                    .RequireClaim(RemoteServiceToken.ServiceIdentifierType)
 45                    .Build()
 46            );
 47        }
 48
 49        return base.GetPolicyAsync(policyName);
 50    }
 51}
 52
 53class PermissionCheckAuthorizationHandler
 54    : AuthorizationHandler<PermissionCheckRequirement>
 55{
 56    protected override Task HandleRequirementAsync(
 57        AuthorizationHandlerContext context,
 58        PermissionCheckRequirement requirement
 59    )
 60    {
 61        var userPermissionBitsString = context.User.FindFirstValue(
 62            UserTokenEF.PermissionBitsType
 63        );
 64
 65        if (userPermissionBitsString is null)
 66            return Task.CompletedTask;
 67
 68        if (!long.TryParse(userPermissionBitsString, out long userPermissionBitsLong))
 69            return Task.CompletedTask;
 70
 71        var userPermissionBits = (UserPermissionBits)userPermissionBitsLong;
 72
 73        if (
 74            (requirement.PermissionBits & userPermissionBits)
 75            == requirement.PermissionBits
 76        )
 77        {
 78            context.Succeed(requirement);
 79        }
 80        return Task.CompletedTask;
 81    }
 82}
 83
 84class PermissionCheckAuthorizeAttribute
 85    : HotChocolate.Authorization.AuthorizeAttribute,
 86        IAuthorizeData
 87{
 188    public PermissionCheckAuthorizeAttribute(UserPermissionBits permissionBits)
 189    {
 190        Policy = $"{PermissionPolicyProvider.POLICY_PREFIX}{(long)permissionBits}";
 191    }
 92
 93    public string? AuthenticationSchemes { get; set; }
 94    string? IAuthorizeData.Roles { get; set; }
 95}