< Summary

Information
Class: ProjectTemplate.Web.Authentication.Claims.ApplicationClaimsTransformation
Assembly: ProjectTemplate.Web
File(s): /home/runner/work/NetCoreApplicationTemplate/NetCoreApplicationTemplate/src/ProjectTemplate.Web/Authentication/Claims/ApplicationClaimsTransformation.cs
Line coverage
97%
Covered lines: 48
Uncovered lines: 1
Coverable lines: 49
Total lines: 116
Line coverage: 97.9%
Branch coverage
88%
Covered branches: 23
Total branches: 26
Branch coverage: 88.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)50%22100%
TransformAsync(...)100%44100%
ResolveMappings(...)75%44100%
NormalizeClaim(...)92.85%141495.83%
HasClaim(...)100%22100%

File(s)

/home/runner/work/NetCoreApplicationTemplate/NetCoreApplicationTemplate/src/ProjectTemplate.Web/Authentication/Claims/ApplicationClaimsTransformation.cs

#LineLine coverage
 1using System.Security.Claims;
 2using Microsoft.AspNetCore.Authentication;
 3using Microsoft.Extensions.Options;
 4using ProjectTemplate.Web.Authentication.Options;
 5
 6namespace ProjectTemplate.Web.Authentication.Claims;
 7
 8/// <summary>
 9/// Normalizes provider-specific claims into application-owned claim names.
 10/// </summary>
 11/// <param name="authenticationOptionsAccessor">The application authentication options accessor.</param>
 10812public sealed class ApplicationClaimsTransformation(
 10813    IOptions<ApplicationAuthenticationOptions> authenticationOptionsAccessor) : IClaimsTransformation
 14{
 10815    private readonly IOptions<ApplicationAuthenticationOptions> _authenticationOptionsAccessor =
 10816        authenticationOptionsAccessor ?? throw new ArgumentNullException(nameof(authenticationOptionsAccessor));
 17
 18    /// <inheritdoc />
 19    public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
 20    {
 2821        ArgumentNullException.ThrowIfNull(principal);
 22
 2823        ApplicationClaimsTransformationOptions options =
 2824            _authenticationOptionsAccessor.Value.ClaimsTransformation;
 25
 2826        if (!options.Enabled)
 27        {
 228            return Task.FromResult(principal);
 29        }
 30
 10431        foreach (ClaimsIdentity identity in principal.Identities.OfType<ClaimsIdentity>())
 32        {
 2633            ApplicationClaimMappingOptions mappings = ResolveMappings(options, identity.AuthenticationType);
 34
 2635            NormalizeClaim(identity, ApplicationClaimTypes.Subject, mappings.Subject, options.RemoveOriginalClaims);
 2636            NormalizeClaim(identity, ApplicationClaimTypes.Name, mappings.Name, options.RemoveOriginalClaims);
 2637            NormalizeClaim(identity, ApplicationClaimTypes.Email, mappings.Email, options.RemoveOriginalClaims);
 2638            NormalizeClaim(identity, ApplicationClaimTypes.Role, mappings.Role, options.RemoveOriginalClaims);
 2639            NormalizeClaim(identity, ApplicationClaimTypes.Group, mappings.Group, options.RemoveOriginalClaims);
 2640            NormalizeClaim(identity, ApplicationClaimTypes.Permission, mappings.Permission, options.RemoveOriginalClaims
 41        }
 42
 2643        return Task.FromResult(principal);
 44    }
 45
 46    private static ApplicationClaimMappingOptions ResolveMappings(
 47        ApplicationClaimsTransformationOptions options,
 48        string? authenticationType)
 49    {
 2650        return !string.IsNullOrWhiteSpace(authenticationType)
 2651            && options.ProviderMappings.TryGetValue(authenticationType, out ApplicationClaimMappingOptions? providerMapp
 2652            ? providerMappings
 2653            : options.DefaultMappings;
 54    }
 55
 56    private static void NormalizeClaim(
 57        ClaimsIdentity identity,
 58        string normalizedClaimType,
 59        IEnumerable<string> sourceClaimTypes,
 60        bool removeOriginalClaims)
 61    {
 15662        var sourceTypes = sourceClaimTypes
 49263            .Where(claimType => !string.IsNullOrWhiteSpace(claimType))
 15664            .ToHashSet(StringComparer.OrdinalIgnoreCase);
 65
 15666        if (sourceTypes.Count == 0)
 67        {
 068            return;
 69        }
 70
 15671        var sourceClaims = identity.Claims
 68672            .Where(claim => sourceTypes.Contains(claim.Type))
 15673            .ToList();
 74
 15675        if (sourceClaims.Count == 0)
 76        {
 8877            return;
 78        }
 79
 27280        foreach (Claim sourceClaim in sourceClaims)
 81        {
 6882            if (!HasClaim(identity, normalizedClaimType, sourceClaim.Value))
 83            {
 6684                identity.AddClaim(new Claim(
 6685                    normalizedClaimType,
 6686                    sourceClaim.Value,
 6687                    sourceClaim.ValueType,
 6688                    sourceClaim.Issuer,
 6689                    sourceClaim.OriginalIssuer));
 90            }
 91        }
 92
 6893        if (!removeOriginalClaims)
 94        {
 6695            return;
 96        }
 97
 898        foreach (Claim sourceClaim in sourceClaims)
 99        {
 2100            if (!string.Equals(sourceClaim.Type, normalizedClaimType, StringComparison.OrdinalIgnoreCase))
 101            {
 2102                identity.RemoveClaim(sourceClaim);
 103            }
 104        }
 2105    }
 106
 107    private static bool HasClaim(
 108        ClaimsIdentity identity,
 109        string claimType,
 110        string claimValue)
 111    {
 68112        return identity.Claims.Any(claim =>
 460113            string.Equals(claim.Type, claimType, StringComparison.OrdinalIgnoreCase)
 460114            && string.Equals(claim.Value, claimValue, StringComparison.Ordinal));
 115    }
 116}