< Summary

Information
Class: AsiBackbone.Core.CapabilityGrants.CapabilityGrantValidationOptions
Assembly: AsiBackbone.Core
File(s): /home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Core/CapabilityGrants/CapabilityGrantValidationOptions.cs
Line coverage
98%
Covered lines: 179
Uncovered lines: 3
Coverable lines: 182
Total lines: 391
Line coverage: 98.3%
Branch coverage
91%
Covered branches: 22
Total branches: 24
Branch coverage: 91.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)92.86%141495.52%
Create(...)100%11100%
WithExpectedBindings(...)75%44100%
WithExpectedBindingsCore(...)100%11100%
CreateExecutionBoundary(...)100%11100%
CreateBoundExecutionBoundary(...)100%11100%
CreateMetadataValidation(...)100%11100%
NormalizeScopes(...)100%44100%
NormalizeOptional(...)100%22100%

File(s)

/home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Core/CapabilityGrants/CapabilityGrantValidationOptions.cs

#LineLine coverage
 1using AsiBackbone.Core.Signing;
 2
 3namespace AsiBackbone.Core.CapabilityGrants;
 4
 5public sealed class CapabilityGrantValidationOptions
 6{
 17    private static readonly string[] EmptyScopes = [];
 8
 1419    private CapabilityGrantValidationOptions(
 14110        string? issuer,
 14111        string? audience,
 14112        IReadOnlyList<string> scopes,
 14113        DateTimeOffset? validationUtc,
 14114        TimeSpan allowedClockSkew,
 14115        string? policyVersion,
 14116        string? policyHash,
 14117        string? acknowledgmentId,
 14118        string? handshakeId,
 14119        string? gatewayBinding,
 14120        string? resourceBinding,
 14121        string? expectedSubjectId,
 14122        string? expectedOperationName,
 14123        bool requireSubjectBinding,
 14124        bool requireProof,
 14125        bool requireAcknowledgmentReference,
 14126        bool requireUseCheck,
 14127        int maxUseCount,
 14128        string? expectedProofKeyId,
 14129        string? expectedProofKeyVersion,
 14130        string? expectedProofPolicyVersion,
 14131        string? expectedProofPolicyHash,
 14132        string? requiredProofProvider,
 14133        string? requiredProofHashAlgorithm,
 14134        CanonicalPayloadOptions? proofPayloadOptions)
 35    {
 14136        if (allowedClockSkew < TimeSpan.Zero)
 37        {
 138            throw new ArgumentOutOfRangeException(
 139                nameof(allowedClockSkew),
 140                allowedClockSkew,
 141                "Allowed clock skew must be greater than or equal to zero.");
 42        }
 43
 14044        if (maxUseCount < 1)
 45        {
 146            throw new ArgumentOutOfRangeException(nameof(maxUseCount), maxUseCount, "Maximum use count must be greater t
 47        }
 48
 49        // A grant can be proof-verified and still be the wrong grant for this caller. Without an audience expectation a
 50        // grant issued for one gateway validates at another, so requiring proof or a use check without stating the
 51        // audience is a configuration that cannot deliver what it appears to promise.
 13952        if ((requireProof || requireUseCheck) && string.IsNullOrWhiteSpace(audience))
 53        {
 354            throw new ArgumentException(
 355                "An audience expectation is required when proof or use checking is required, because proof alone does no
 356                nameof(audience));
 57        }
 58
 13659        Issuer = NormalizeOptional(issuer);
 13660        Audience = NormalizeOptional(audience);
 13661        Scopes = scopes;
 13662        ValidationUtc = validationUtc?.ToUniversalTime();
 13663        AllowedClockSkew = allowedClockSkew;
 13664        PolicyVersion = NormalizeOptional(policyVersion);
 13665        PolicyHash = NormalizeOptional(policyHash);
 13666        AcknowledgmentId = NormalizeOptional(acknowledgmentId);
 13667        HandshakeId = NormalizeOptional(handshakeId);
 13668        GatewayBinding = NormalizeOptional(gatewayBinding);
 13669        ResourceBinding = NormalizeOptional(resourceBinding);
 13670        ExpectedSubjectId = NormalizeOptional(expectedSubjectId);
 13671        if (requireSubjectBinding && ExpectedSubjectId is null)
 72        {
 073            throw new ArgumentException(
 074                "A subject expectation is required for this validation profile.",
 075                nameof(expectedSubjectId));
 76        }
 77
 13678        ExpectedOperationName = NormalizeOptional(expectedOperationName);
 13679        RequireSubjectBinding = requireSubjectBinding;
 13680        RequireProof = requireProof;
 13681        RequireAcknowledgmentReference = requireAcknowledgmentReference;
 13682        RequireUseCheck = requireUseCheck;
 13683        MaxUseCount = maxUseCount;
 13684        ExpectedProofKeyId = NormalizeOptional(expectedProofKeyId);
 13685        ExpectedProofKeyVersion = NormalizeOptional(expectedProofKeyVersion);
 13686        ExpectedProofPolicyVersion = NormalizeOptional(expectedProofPolicyVersion);
 13687        ExpectedProofPolicyHash = NormalizeOptional(expectedProofPolicyHash);
 13688        RequiredProofProvider = NormalizeOptional(requiredProofProvider);
 13689        RequiredProofHashAlgorithm = NormalizeOptional(requiredProofHashAlgorithm);
 13690        ProofPayloadOptions = proofPayloadOptions;
 13691    }
 92
 93    public string? Issuer { get; }
 94    public string? Audience { get; }
 95    public IReadOnlyList<string> Scopes { get; }
 96    public DateTimeOffset? ValidationUtc { get; }
 97    public TimeSpan AllowedClockSkew { get; }
 98    public string? PolicyVersion { get; }
 99    public string? PolicyHash { get; }
 100    public string? AcknowledgmentId { get; }
 101    public string? HandshakeId { get; }
 102    public string? GatewayBinding { get; }
 103    public string? ResourceBinding { get; }
 104
 105    /// <summary>
 106    /// Gets the authenticated subject that the grant must identify, when subject binding is configured.
 107    /// </summary>
 108    public string? ExpectedSubjectId { get; }
 109
 110    /// <summary>
 111    /// Gets the requested operation that the grant must identify, when operation binding is configured.
 112    /// </summary>
 113    public string? ExpectedOperationName { get; }
 114    public bool RequireProof { get; }
 115    public bool RequireAcknowledgmentReference { get; }
 116    public bool RequireUseCheck { get; }
 117    public int MaxUseCount { get; }
 118    public string? ExpectedProofKeyId { get; }
 119    public string? ExpectedProofKeyVersion { get; }
 120    public string? ExpectedProofPolicyVersion { get; }
 121    public string? ExpectedProofPolicyHash { get; }
 122    public string? RequiredProofProvider { get; }
 123    public string? RequiredProofHashAlgorithm { get; }
 124
 125    /// <summary>
 126    /// Gets the canonical payload options used to rebuild the grant payload when proof is required.
 127    /// </summary>
 128    /// <remarks>
 129    /// Proof validation recomputes the canonical payload from the grant and compares its hash to the signed hash, so th
 130    /// options must match the options the issuer signed with. The default options bind every grant field except metadat
 131    /// whose allow-list is empty until a host opts a key in.
 132    /// </remarks>
 133    public CanonicalPayloadOptions? ProofPayloadOptions { get; }
 134
 135    public static CapabilityGrantValidationOptions Create(
 136        string? issuer = null,
 137        string? audience = null,
 138        IEnumerable<string>? scopes = null,
 139        DateTimeOffset? validationUtc = null,
 140        string? policyVersion = null,
 141        string? policyHash = null,
 142        string? acknowledgmentId = null,
 143        string? handshakeId = null,
 144        string? gatewayBinding = null,
 145        string? resourceBinding = null,
 146        bool requireProof = false,
 147        bool requireAcknowledgmentReference = false,
 148        bool requireUseCheck = false,
 149        int maxUseCount = 1,
 150        TimeSpan allowedClockSkew = default,
 151        string? expectedProofKeyId = null,
 152        string? expectedProofKeyVersion = null,
 153        string? expectedProofPolicyVersion = null,
 154        string? expectedProofPolicyHash = null,
 155        string? requiredProofProvider = null,
 156        string? requiredProofHashAlgorithm = null,
 157        CanonicalPayloadOptions? proofPayloadOptions = null)
 158    {
 87159        return new CapabilityGrantValidationOptions(
 87160            issuer,
 87161            audience,
 87162            NormalizeScopes(scopes),
 87163            validationUtc,
 87164            allowedClockSkew,
 87165            policyVersion,
 87166            policyHash,
 87167            acknowledgmentId,
 87168            handshakeId,
 87169            gatewayBinding,
 87170            resourceBinding,
 87171            expectedSubjectId: null,
 87172            expectedOperationName: null,
 87173            requireSubjectBinding: false,
 87174            requireProof,
 87175            requireAcknowledgmentReference,
 87176            requireUseCheck,
 87177            maxUseCount,
 87178            expectedProofKeyId,
 87179            expectedProofKeyVersion,
 87180            expectedProofPolicyVersion,
 87181            expectedProofPolicyHash,
 87182            requiredProofProvider,
 87183            requiredProofHashAlgorithm,
 87184            proofPayloadOptions);
 185    }
 186
 187    /// <summary>
 188    /// Creates a copy with optional host expectations for the authenticated subject and requested operation.
 189    /// </summary>
 190    public CapabilityGrantValidationOptions WithExpectedBindings(
 191        string? expectedSubjectId = null,
 192        string? expectedOperationName = null)
 193    {
 49194        string? normalizedSubjectId = NormalizeOptional(expectedSubjectId);
 49195        if (RequireSubjectBinding && normalizedSubjectId is null)
 196        {
 2197            normalizedSubjectId = ExpectedSubjectId;
 198        }
 199
 49200        return WithExpectedBindingsCore(
 49201            normalizedSubjectId,
 49202            expectedOperationName,
 49203            RequireSubjectBinding);
 204    }
 205
 206    private CapabilityGrantValidationOptions WithExpectedBindingsCore(
 207        string? expectedSubjectId,
 208        string? expectedOperationName,
 209        bool requireSubjectBinding)
 210    {
 54211        return new CapabilityGrantValidationOptions(
 54212            Issuer,
 54213            Audience,
 54214            Scopes,
 54215            ValidationUtc,
 54216            AllowedClockSkew,
 54217            PolicyVersion,
 54218            PolicyHash,
 54219            AcknowledgmentId,
 54220            HandshakeId,
 54221            GatewayBinding,
 54222            ResourceBinding,
 54223            expectedSubjectId,
 54224            expectedOperationName,
 54225            requireSubjectBinding,
 54226            RequireProof,
 54227            RequireAcknowledgmentReference,
 54228            RequireUseCheck,
 54229            MaxUseCount,
 54230            ExpectedProofKeyId,
 54231            ExpectedProofKeyVersion,
 54232            ExpectedProofPolicyVersion,
 54233            ExpectedProofPolicyHash,
 54234            RequiredProofProvider,
 54235            RequiredProofHashAlgorithm,
 54236            ProofPayloadOptions);
 237    }
 238
 239    /// <summary>
 240    /// Obsolete. Retains the legacy execution-boundary signature for binary compatibility and always fails closed.
 241    /// </summary>
 242    /// <remarks>
 243    /// This method is retained only for compatibility and always throws <see cref="InvalidOperationException" />.
 244    /// Replace calls with <c>CreateBoundExecutionBoundary(CapabilityGrantBindingExpectations, ...)</c>, whose required
 245    /// first argument supplies the authoritative subject and optional operation binding. The warning remains non-error
 246    /// in the 7.x line; removal is planned for the next permitted major version.
 247    /// </remarks>
 248    [Obsolete(
 249        "CreateExecutionBoundary always fails closed and is retained only for binary compatibility. Use CreateBoundExecu
 250        DiagnosticId = "ASIB901",
 251        UrlFormat = "https://asibackbone.github.io/AsiBackbone/articles/asib901-create-execution-boundary.html")]
 252    public static CapabilityGrantValidationOptions CreateExecutionBoundary(
 253        string? issuer = null,
 254        string? audience = null,
 255        IEnumerable<string>? scopes = null,
 256        DateTimeOffset? validationUtc = null,
 257        string? policyVersion = null,
 258        string? policyHash = null,
 259        string? acknowledgmentId = null,
 260        string? handshakeId = null,
 261        string? gatewayBinding = null,
 262        string? resourceBinding = null,
 263        bool requireAcknowledgmentReference = false,
 264        bool requireUseCheck = true,
 265        int maxUseCount = 1,
 266        TimeSpan allowedClockSkew = default,
 267        string? expectedProofKeyId = null,
 268        string? expectedProofKeyVersion = null,
 269        string? expectedProofPolicyVersion = null,
 270        string? expectedProofPolicyHash = null,
 271        string? requiredProofProvider = null,
 272        string? requiredProofHashAlgorithm = null,
 273        CanonicalPayloadOptions? proofPayloadOptions = null)
 274    {
 2275        throw new InvalidOperationException(
 2276            "Execution-boundary validation requires subject binding expectations. Use CreateBoundExecutionBoundary.");
 277    }
 278
 279    /// <summary>
 280    /// Creates a proof-verifying execution-boundary profile with authoritative host binding expectations.
 281    /// </summary>
 282    public static CapabilityGrantValidationOptions CreateBoundExecutionBoundary(
 283        CapabilityGrantBindingExpectations bindingExpectations,
 284        string? issuer = null,
 285        string? audience = null,
 286        IEnumerable<string>? scopes = null,
 287        DateTimeOffset? validationUtc = null,
 288        string? policyVersion = null,
 289        string? policyHash = null,
 290        string? acknowledgmentId = null,
 291        string? handshakeId = null,
 292        string? gatewayBinding = null,
 293        string? resourceBinding = null,
 294        bool requireAcknowledgmentReference = false,
 295        bool requireUseCheck = true,
 296        int maxUseCount = 1,
 297        TimeSpan allowedClockSkew = default,
 298        string? expectedProofKeyId = null,
 299        string? expectedProofKeyVersion = null,
 300        string? expectedProofPolicyVersion = null,
 301        string? expectedProofPolicyHash = null,
 302        string? requiredProofProvider = null,
 303        string? requiredProofHashAlgorithm = null,
 304        CanonicalPayloadOptions? proofPayloadOptions = null)
 305    {
 6306        ArgumentNullException.ThrowIfNull(bindingExpectations);
 307
 5308        return Create(
 5309            issuer: issuer,
 5310            audience: audience,
 5311            scopes: scopes,
 5312            validationUtc: validationUtc,
 5313            policyVersion: policyVersion,
 5314            policyHash: policyHash,
 5315            acknowledgmentId: acknowledgmentId,
 5316            handshakeId: handshakeId,
 5317            gatewayBinding: gatewayBinding,
 5318            resourceBinding: resourceBinding,
 5319            requireProof: true,
 5320            requireAcknowledgmentReference: requireAcknowledgmentReference,
 5321            requireUseCheck: requireUseCheck,
 5322            maxUseCount: maxUseCount,
 5323            allowedClockSkew: allowedClockSkew,
 5324            expectedProofKeyId: expectedProofKeyId,
 5325            expectedProofKeyVersion: expectedProofKeyVersion,
 5326            expectedProofPolicyVersion: expectedProofPolicyVersion,
 5327            expectedProofPolicyHash: expectedProofPolicyHash,
 5328            requiredProofProvider: requiredProofProvider,
 5329            requiredProofHashAlgorithm: requiredProofHashAlgorithm,
 5330            proofPayloadOptions: proofPayloadOptions)
 5331            .WithExpectedBindingsCore(
 5332                bindingExpectations.SubjectId,
 5333                bindingExpectations.OperationName,
 5334                requireSubjectBinding: true);
 335    }
 336
 337    public static CapabilityGrantValidationOptions CreateMetadataValidation(
 338        string? issuer = null,
 339        string? audience = null,
 340        IEnumerable<string>? scopes = null,
 341        DateTimeOffset? validationUtc = null,
 342        string? policyVersion = null,
 343        string? policyHash = null,
 344        string? acknowledgmentId = null,
 345        string? handshakeId = null,
 346        string? gatewayBinding = null,
 347        string? resourceBinding = null,
 348        bool requireAcknowledgmentReference = false,
 349        TimeSpan allowedClockSkew = default)
 350    {
 3351        return Create(
 3352            issuer: issuer,
 3353            audience: audience,
 3354            scopes: scopes,
 3355            validationUtc: validationUtc,
 3356            policyVersion: policyVersion,
 3357            policyHash: policyHash,
 3358            acknowledgmentId: acknowledgmentId,
 3359            handshakeId: handshakeId,
 3360            gatewayBinding: gatewayBinding,
 3361            resourceBinding: resourceBinding,
 3362            requireProof: false,
 3363            requireAcknowledgmentReference: requireAcknowledgmentReference,
 3364            requireUseCheck: false,
 3365            maxUseCount: 1,
 3366            allowedClockSkew: allowedClockSkew);
 367    }
 368
 369    private static IReadOnlyList<string> NormalizeScopes(IEnumerable<string>? scopes)
 370    {
 87371        if (scopes is null)
 372        {
 23373            return EmptyScopes;
 374        }
 375
 64376        string[] normalized = [.. scopes
 64377            .Where(scope => !string.IsNullOrWhiteSpace(scope))
 64378            .Select(scope => scope.Trim())
 64379            .Distinct(StringComparer.Ordinal)
 64380            .OrderBy(scope => scope, StringComparer.Ordinal)];
 381
 64382        return normalized.Length == 0 ? EmptyScopes : Array.AsReadOnly(normalized);
 383    }
 384
 385    private static string? NormalizeOptional(string? value)
 386    {
 2225387        return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
 388    }
 389
 390    private bool RequireSubjectBinding { get; }
 391}

Methods/Properties

.cctor()
.ctor(System.String,System.String,System.Collections.Generic.IReadOnlyList`1<System.String>,System.Nullable`1<System.DateTimeOffset>,System.TimeSpan,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Int32,System.String,System.String,System.String,System.String,System.String,System.String,AsiBackbone.Core.Signing.CanonicalPayloadOptions)
Create(System.String,System.String,System.Collections.Generic.IEnumerable`1<System.String>,System.Nullable`1<System.DateTimeOffset>,System.String,System.String,System.String,System.String,System.String,System.String,System.Boolean,System.Boolean,System.Boolean,System.Int32,System.TimeSpan,System.String,System.String,System.String,System.String,System.String,System.String,AsiBackbone.Core.Signing.CanonicalPayloadOptions)
WithExpectedBindings(System.String,System.String)
WithExpectedBindingsCore(System.String,System.String,System.Boolean)
CreateExecutionBoundary(System.String,System.String,System.Collections.Generic.IEnumerable`1<System.String>,System.Nullable`1<System.DateTimeOffset>,System.String,System.String,System.String,System.String,System.String,System.String,System.Boolean,System.Boolean,System.Int32,System.TimeSpan,System.String,System.String,System.String,System.String,System.String,System.String,AsiBackbone.Core.Signing.CanonicalPayloadOptions)
CreateBoundExecutionBoundary(AsiBackbone.Core.CapabilityGrants.CapabilityGrantBindingExpectations,System.String,System.String,System.Collections.Generic.IEnumerable`1<System.String>,System.Nullable`1<System.DateTimeOffset>,System.String,System.String,System.String,System.String,System.String,System.String,System.Boolean,System.Boolean,System.Int32,System.TimeSpan,System.String,System.String,System.String,System.String,System.String,System.String,AsiBackbone.Core.Signing.CanonicalPayloadOptions)
CreateMetadataValidation(System.String,System.String,System.Collections.Generic.IEnumerable`1<System.String>,System.Nullable`1<System.DateTimeOffset>,System.String,System.String,System.String,System.String,System.String,System.String,System.Boolean,System.TimeSpan)
NormalizeScopes(System.Collections.Generic.IEnumerable`1<System.String>)
NormalizeOptional(System.String)