< 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: 119
Uncovered lines: 2
Coverable lines: 121
Total lines: 206
Line coverage: 98.3%
Branch coverage
92%
Covered branches: 93
Total branches: 101
Branch coverage: 92%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
ValidateAsync()100%1414100%
ValidateProofAsync()83.33%66100%
ValidateMetadata(...)97.72%4444100%
IsNotYetValid(...)100%44100%
IsExpired(...)100%22100%
ValidateUseAsync()75%1616100%
ContainsRequiredScopes(...)100%44100%
MapVerificationCategory(...)81.81%111185.71%

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    {
 10414        ArgumentNullException.ThrowIfNull(signedGrant);
 10215        cancellationToken.ThrowIfCancellationRequested();
 16
 10017        CapabilityGrantValidationOptions effectiveOptions = options ?? CapabilityGrantValidationOptions.Create();
 10018        CapabilityTokenGrant grant = signedGrant.Artifact;
 10019        DateTimeOffset validationUtc = (effectiveOptions.ValidationUtc ?? DateTimeOffset.UtcNow).ToUniversalTime();
 20
 10021        if (effectiveOptions.RequireProof)
 22        {
 3223            CapabilityGrantValidationResult? proofResult = await ValidateProofAsync(
 3224                signedGrant,
 3225                grant,
 3226                effectiveOptions,
 3227                verificationService,
 3228                cancellationToken).ConfigureAwait(false);
 29
 3230            if (proofResult is not null)
 31            {
 2632                return proofResult;
 33            }
 34        }
 35
 7436        CapabilityGrantValidationResult? metadataResult = ValidateMetadata(grant, effectiveOptions, validationUtc);
 37
 7438        if (metadataResult is not null)
 39        {
 3640            return metadataResult;
 41        }
 42
 3843        if (effectiveOptions.RequireUseCheck)
 44        {
 2445            CapabilityGrantValidationResult? useResult = await ValidateUseAsync(
 2446                grant,
 2447                effectiveOptions,
 2448                useStore,
 2449                validationUtc,
 2450                cancellationToken).ConfigureAwait(false);
 51
 2452            if (useResult is not null)
 53            {
 1854                return useResult;
 55            }
 56        }
 57
 2058        return CapabilityGrantValidationResult.Valid(grant);
 10059    }
 60
 61    private static async ValueTask<CapabilityGrantValidationResult?> ValidateProofAsync(
 62        SignedGovernanceArtifact<CapabilityTokenGrant> signedGrant,
 63        CapabilityTokenGrant grant,
 64        CapabilityGrantValidationOptions options,
 65        IAsiBackboneSignatureVerificationService? verificationService,
 66        CancellationToken cancellationToken)
 67    {
 3268        if (verificationService is null)
 69        {
 470            return CapabilityGrantValidationResult.Failed(
 471                grant,
 472                CapabilityTokenValidationCategory.MissingProof,
 473                VerificationPolicyAction.Deny,
 474                "capability.proof-verifier-missing",
 475                "A proof verifier is required for this validation context.");
 76        }
 77
 2878        var verificationContext = VerificationPolicyContext.Create(
 2879            purpose: CanonicalArtifactTypes.CapabilityTokenGrant,
 2880            expectedKeyId: options.ExpectedProofKeyId,
 2881            expectedKeyVersion: options.ExpectedProofKeyVersion,
 2882            expectedPolicyVersion: options.ExpectedProofPolicyVersion,
 2883            expectedPolicyHash: options.ExpectedProofPolicyHash,
 2884            requiredProvider: options.RequiredProofProvider,
 2885            requiredHashAlgorithm: options.RequiredProofHashAlgorithm);
 86
 2887        VerificationPolicyOutcome verificationOutcome = await GovernanceArtifactVerifier.VerifyAsync(
 2888            signedGrant,
 2889            verificationService,
 2890            context: verificationContext,
 2891            cancellationToken: cancellationToken).ConfigureAwait(false);
 92
 2893        return verificationOutcome.ShouldAllow
 2894            ? null
 2895            : CapabilityGrantValidationResult.Failed(
 2896                grant,
 2897                MapVerificationCategory(verificationOutcome.Category),
 2898                verificationOutcome.Action,
 2899                verificationOutcome.FailureCode ?? "capability.proof-invalid",
 28100                verificationOutcome.FailureMessage);
 32101    }
 102
 103    private static CapabilityGrantValidationResult? ValidateMetadata(
 104        CapabilityTokenGrant grant,
 105        CapabilityGrantValidationOptions options,
 106        DateTimeOffset validationUtc)
 107    {
 74108        return options.Issuer is not null && !string.Equals(options.Issuer, grant.Issuer, StringComparison.Ordinal)
 74109            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.WrongIssuer, VerificationP
 74110            : options.Audience is not null && !string.Equals(options.Audience, grant.Audience, StringComparison.Ordinal)
 74111            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.WrongAudience, Verificatio
 74112            : IsNotYetValid(grant, validationUtc, options.AllowedClockSkew)
 74113            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.NotYetValid, VerificationP
 74114            : IsExpired(grant, validationUtc, options.AllowedClockSkew)
 74115            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.Expired, VerificationPolic
 74116            : options.Scopes.Count > 0 && !ContainsRequiredScopes(grant.Scopes, options.Scopes)
 74117            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.WrongScope, VerificationPo
 74118            : (options.PolicyVersion is not null && !string.Equals(options.PolicyVersion, grant.PolicyVersion, StringCom
 74119            || (options.PolicyHash is not null && !string.Equals(options.PolicyHash, grant.PolicyHash, StringComparison.
 74120            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.PolicyMismatch, Verificati
 74121            : options.RequireAcknowledgmentReference && !grant.HasAcknowledgmentReference
 74122            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.MissingAcknowledgmentRefer
 74123            : options.AcknowledgmentId is not null && !string.Equals(options.AcknowledgmentId, grant.AcknowledgmentId, S
 74124            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.AcknowledgmentMismatch, Ve
 74125            : options.HandshakeId is not null && !string.Equals(options.HandshakeId, grant.HandshakeId, StringComparison
 74126            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.HandshakeMismatch, Verific
 74127            : options.GatewayBinding is not null && !string.Equals(options.GatewayBinding, grant.GatewayBinding, StringC
 74128            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.GatewayMismatch, Verificat
 74129            : options.ResourceBinding is not null && !string.Equals(options.ResourceBinding, grant.ResourceBinding, Stri
 74130            ? CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.ResourceMismatch, Verifica
 74131            : null;
 132    }
 133
 134    private static bool IsNotYetValid(CapabilityTokenGrant grant, DateTimeOffset validationUtc, TimeSpan allowedClockSke
 135    {
 70136        return grant.NotBeforeUtc.HasValue
 70137            && grant.NotBeforeUtc.Value > validationUtc
 70138            && grant.NotBeforeUtc.Value - validationUtc > allowedClockSkew;
 139    }
 140
 141    private static bool IsExpired(CapabilityTokenGrant grant, DateTimeOffset validationUtc, TimeSpan allowedClockSkew)
 142    {
 64143        return validationUtc >= grant.ExpiresUtc
 64144            && validationUtc - grant.ExpiresUtc >= allowedClockSkew;
 145    }
 146
 147    private static async ValueTask<CapabilityGrantValidationResult?> ValidateUseAsync(
 148        CapabilityTokenGrant grant,
 149        CapabilityGrantValidationOptions options,
 150        ICapabilityGrantUseStore? useStore,
 151        DateTimeOffset validationUtc,
 152        CancellationToken cancellationToken)
 153    {
 24154        if (useStore is null)
 155        {
 4156            return CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.ReplayStoreUnavailabl
 157        }
 158
 20159        CapabilityGrantUseResult result = await useStore
 20160            .TryConsumeAsync(grant, options.MaxUseCount, validationUtc, cancellationToken)
 20161            .ConfigureAwait(false);
 162
 20163        return result.State switch
 20164        {
 6165            GrantUseState.Accepted => null,
 4166            GrantUseState.UseLimitExceeded => CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCat
 2167            GrantUseState.Stopped => CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.Rev
 2168            GrantUseState.Cancelled => CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.C
 4169            GrantUseState.Unavailable => CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory
 2170            _ => CapabilityGrantValidationResult.Failed(grant, CapabilityTokenValidationCategory.Failed, VerificationPol
 20171        };
 24172    }
 173
 174    private static bool ContainsRequiredScopes(IReadOnlyList<string> actualScopes, IReadOnlyList<string> requiredScopes)
 175    {
 50176        HashSet<string> actualScopeSet = new(actualScopes, StringComparer.Ordinal);
 177
 200178        foreach (string requiredScope in requiredScopes)
 179        {
 52180            if (!actualScopeSet.Contains(requiredScope))
 181            {
 4182                return false;
 183            }
 184        }
 185
 46186        return true;
 4187    }
 188
 189    private static CapabilityTokenValidationCategory MapVerificationCategory(SignatureVerificationCategory category)
 190    {
 22191        return category switch
 22192        {
 2193            SignatureVerificationCategory.MissingSignature => CapabilityTokenValidationCategory.MissingProof,
 0194            SignatureVerificationCategory.Valid => CapabilityTokenValidationCategory.Valid,
 2195            SignatureVerificationCategory.InvalidSignature => CapabilityTokenValidationCategory.InvalidProof,
 2196            SignatureVerificationCategory.HashMismatch => CapabilityTokenValidationCategory.InvalidProof,
 2197            SignatureVerificationCategory.RevokedKey => CapabilityTokenValidationCategory.Revoked,
 4198            SignatureVerificationCategory.ProviderUnavailable => CapabilityTokenValidationCategory.Failed,
 4199            SignatureVerificationCategory.UnknownKeyVersion => CapabilityTokenValidationCategory.Failed,
 2200            SignatureVerificationCategory.CanonicalizationMismatch => CapabilityTokenValidationCategory.Failed,
 2201            SignatureVerificationCategory.UnsupportedAlgorithm => CapabilityTokenValidationCategory.InvalidProof,
 2202            SignatureVerificationCategory.Failed => CapabilityTokenValidationCategory.Failed,
 0203            _ => CapabilityTokenValidationCategory.Failed
 22204        };
 205    }
 206}