< Summary

Information
Class: AsiBackbone.Core.CapabilityTokens.CapabilityGrantValidator
Assembly: AsiBackbone.Core
File(s): /home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Core/CapabilityTokens/CapabilityGrantValidator.cs
Line coverage
98%
Covered lines: 107
Uncovered lines: 2
Coverable lines: 109
Total lines: 188
Line coverage: 98.1%
Branch coverage
78%
Covered branches: 78
Total branches: 99
Branch coverage: 78.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
ValidateAsync()85.71%1414100%
ValidateProofAsync()83.33%66100%
ValidateMetadata(...)76.08%4646100%
ValidateUseAsync()75%1616100%
ContainsRequiredScopes(...)100%44100%
MapVerificationCategory(...)81.81%111185.71%
MapVerificationAction(...)50%22100%

File(s)

/home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Core/CapabilityTokens/CapabilityGrantValidator.cs

#LineLine coverage
 1using AsiBackbone.Core.Signing;
 2
 3namespace AsiBackbone.Core.CapabilityTokens;
 4
 5public static class CapabilityGrantValidator
 6{
 7    public static async ValueTask<CapabilityGrantValidationResult> ValidateAsync(
 8        SignedGovernanceArtifact<CapabilityTokenGrant> signedGrant,
 9        CapabilityGrantValidationOptions? options = null,
 10        IAsiBackboneSignatureVerificationService? verificationService = null,
 11        ICapabilityGrantUseStore? useStore = null,
 12        CancellationToken cancellationToken = default)
 13    {
 3514        ArgumentNullException.ThrowIfNull(signedGrant);
 3415        cancellationToken.ThrowIfCancellationRequested();
 16
 3317        CapabilityGrantValidationOptions effectiveOptions = options ?? CapabilityGrantValidationOptions.Create();
 3318        CapabilityTokenGrant grant = signedGrant.Artifact;
 3319        DateTimeOffset validationUtc = (effectiveOptions.ValidationUtc ?? DateTimeOffset.UtcNow).ToUniversalTime();
 20
 3321        if (effectiveOptions.RequireProof)
 22        {
 1123            CapabilityGrantValidationResult? proofResult = await ValidateProofAsync(
 1124                signedGrant,
 1125                grant,
 1126                verificationService,
 1127                cancellationToken).ConfigureAwait(false);
 28
 1129            if (proofResult is not null)
 30            {
 1031                return proofResult;
 32            }
 33        }
 34
 2335        CapabilityGrantValidationResult? metadataResult = ValidateMetadata(grant, effectiveOptions, validationUtc);
 36
 2337        if (metadataResult is not null)
 38        {
 1339            return metadataResult;
 40        }
 41
 1042        if (effectiveOptions.RequireUseCheck)
 43        {
 944            CapabilityGrantValidationResult? useResult = await ValidateUseAsync(
 945                grant,
 946                effectiveOptions,
 947                useStore,
 948                validationUtc,
 949                cancellationToken).ConfigureAwait(false);
 50
 951            if (useResult is not null)
 52            {
 753                return useResult;
 54            }
 55        }
 56
 357        return CapabilityGrantValidationResult.Valid(grant);
 3358    }
 59
 60    private static async ValueTask<CapabilityGrantValidationResult?> ValidateProofAsync(
 61        SignedGovernanceArtifact<CapabilityTokenGrant> signedGrant,
 62        CapabilityTokenGrant grant,
 63        IAsiBackboneSignatureVerificationService? verificationService,
 64        CancellationToken cancellationToken)
 65    {
 1166        if (verificationService is null)
 67        {
 168            return CapabilityGrantValidationResult.Failed(
 169                grant,
 170                CapabilityTokenValidationCategory.MissingProof,
 171                VerificationPolicyAction.Deny,
 172                "capability.proof-verifier-missing",
 173                "A proof verifier is required for this validation context.");
 74        }
 75
 1076        VerificationPolicyOutcome verificationOutcome = await GovernanceArtifactVerifier.VerifyAsync(
 1077            signedGrant,
 1078            verificationService,
 1079            cancellationToken: cancellationToken).ConfigureAwait(false);
 80
 1081        return verificationOutcome.ShouldAllow
 1082            ? null
 1083            : CapabilityGrantValidationResult.Failed(
 1084            grant,
 1085            MapVerificationCategory(verificationOutcome.Category),
 1086            MapVerificationAction(verificationOutcome.Action),
 1087            verificationOutcome.FailureCode ?? "capability.proof-invalid",
 1088            verificationOutcome.FailureMessage);
 1189    }
 90
 91    private static CapabilityGrantValidationResult? ValidateMetadata(
 92        CapabilityTokenGrant grant,
 93        CapabilityGrantValidationOptions options,
 94        DateTimeOffset validationUtc)
 95    {
 2396        return options.Issuer is not null && !string.Equals(options.Issuer, grant.Issuer, StringComparison.Ordinal)
 2397            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.WrongIssuer, VerificationP
 2398            : options.Audience is not null && !string.Equals(options.Audience, grant.Audience, StringComparison.Ordinal)
 2399            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.WrongAudience, Verificatio
 23100            : grant.NotBeforeUtc.HasValue && validationUtc < grant.NotBeforeUtc.Value
 23101            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.NotYetValid, VerificationP
 23102            : validationUtc >= grant.ExpiresUtc
 23103            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.Expired, VerificationPolic
 23104            : options.Scopes.Count > 0 && !ContainsRequiredScopes(grant.Scopes, options.Scopes)
 23105            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.WrongScope, VerificationPo
 23106            : (options.PolicyVersion is not null && !string.Equals(options.PolicyVersion, grant.PolicyVersion, StringCom
 23107            || (options.PolicyHash is not null && !string.Equals(options.PolicyHash, grant.PolicyHash, StringComparison.
 23108            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.PolicyMismatch, Verificati
 23109            : options.RequireAcknowledgmentReference && !grant.HasAcknowledgmentReference
 23110            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.MissingAcknowledgmentRefer
 23111            : options.AcknowledgmentId is not null && !string.Equals(options.AcknowledgmentId, grant.AcknowledgmentId, S
 23112            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.AcknowledgmentMismatch, Ve
 23113            : options.HandshakeId is not null && !string.Equals(options.HandshakeId, grant.HandshakeId, StringComparison
 23114            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.HandshakeMismatch, Verific
 23115            : options.GatewayBinding is not null && !string.Equals(options.GatewayBinding, grant.GatewayBinding, StringC
 23116            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.GatewayMismatch, Verificat
 23117            : options.ResourceBinding is not null && !string.Equals(options.ResourceBinding, grant.ResourceBinding, Stri
 23118            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.ResourceMismatch, Verifica
 23119            : null;
 120    }
 121
 122    private static async ValueTask<CapabilityGrantValidationResult?> ValidateUseAsync(
 123        CapabilityTokenGrant grant,
 124        CapabilityGrantValidationOptions options,
 125        ICapabilityGrantUseStore? useStore,
 126        DateTimeOffset validationUtc,
 127        CancellationToken cancellationToken)
 128    {
 9129        if (useStore is null)
 130        {
 1131            return CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.ReplayStoreUnavailabl
 132        }
 133
 8134        CapabilityGrantUseResult result = await useStore
 8135            .TryConsumeAsync(grant, options.MaxUseCount, validationUtc, cancellationToken)
 8136            .ConfigureAwait(false);
 137
 8138        return result.State switch
 8139        {
 2140            GrantUseState.Accepted => null,
 1141            GrantUseState.UseLimitExceeded => CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCat
 1142            GrantUseState.Stopped => CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.Rev
 1143            GrantUseState.Cancelled => CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.C
 2144            GrantUseState.Unavailable => CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory
 1145            _ => CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.Failed, VerificationPol
 8146        };
 9147    }
 148
 149    private static bool ContainsRequiredScopes(IReadOnlyList<string> actualScopes, IReadOnlyList<string> requiredScopes)
 150    {
 19151        HashSet<string> actualScopeSet = new(actualScopes, StringComparer.Ordinal);
 152
 76153        foreach (string requiredScope in requiredScopes)
 154        {
 20155            if (!actualScopeSet.Contains(requiredScope))
 156            {
 2157                return false;
 158            }
 159        }
 160
 17161        return true;
 2162    }
 163
 164    private static CapabilityTokenValidationCategory MapVerificationCategory(SignatureVerificationCategory category)
 165    {
 9166        return category switch
 9167        {
 1168            SignatureVerificationCategory.MissingSignature => CapabilityTokenValidationCategory.MissingProof,
 0169            SignatureVerificationCategory.Valid => CapabilityTokenValidationCategory.Valid,
 1170            SignatureVerificationCategory.InvalidSignature => CapabilityTokenValidationCategory.InvalidProof,
 1171            SignatureVerificationCategory.HashMismatch => CapabilityTokenValidationCategory.InvalidProof,
 1172            SignatureVerificationCategory.RevokedKey => CapabilityTokenValidationCategory.Revoked,
 1173            SignatureVerificationCategory.ProviderUnavailable => CapabilityTokenValidationCategory.Failed,
 1174            SignatureVerificationCategory.UnknownKeyVersion => CapabilityTokenValidationCategory.Failed,
 1175            SignatureVerificationCategory.CanonicalizationMismatch => CapabilityTokenValidationCategory.Failed,
 1176            SignatureVerificationCategory.UnsupportedAlgorithm => CapabilityTokenValidationCategory.InvalidProof,
 1177            SignatureVerificationCategory.Failed => CapabilityTokenValidationCategory.Failed,
 0178            _ => CapabilityTokenValidationCategory.Failed
 9179        };
 180    }
 181
 182    private static VerificationPolicyAction MapVerificationAction(VerificationPolicyAction action)
 183    {
 9184        return action is VerificationPolicyAction.Allow
 9185            ? VerificationPolicyAction.Allow
 9186            : action;
 187    }
 188}