< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.Command.Services.ReauthenticationBudget
Assembly: ArturRios.Heimdall.Command
File(s): /home/runner/work/heimdall-api/heimdall-api/src/Application/ArturRios.Heimdall.Command/Services/ReauthenticationBudget.cs
Line coverage
100%
Covered lines: 5
Uncovered lines: 0
Coverable lines: 5
Total lines: 52
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage
100%
Covered methods: 3
Fully covered methods: 3
Total methods: 3
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
TryChargeAsync(...)100%11100%
ClearAsync(...)100%11100%

File(s)

/home/runner/work/heimdall-api/heimdall-api/src/Application/ArturRios.Heimdall.Command/Services/ReauthenticationBudget.cs

#LineLine coverage
 1using ArturRios.Heimdall.Domain.Entities;
 2using ArturRios.Heimdall.Domain.Persistence;
 3
 4namespace ArturRios.Heimdall.Command.Services;
 5
 6/// <summary>
 7///     FR-AU-13: the per-account budget for re-authentication — every check of a password or a factor
 8///     made under a bearer token: enabling (UC-36), confirming (UC-37) and disabling (UC-39)
 9///     two-factor authentication, regenerating recovery codes (UC-40), changing one's own email
 10///     (UC-08) and a person requesting erasure (UC-42).
 11/// </summary>
 12/// <remarks>
 13///     <para>
 14///         <b>Why it is its own budget.</b> The caller already holds a token, and the token may be
 15///         stolen. Charging these checks to the login lockout (FR-AU-09) or to the second-factor
 16///         budget (FR-AU-12) would let a token thief lock the account's owner out of signing in, so
 17///         neither is touched here: a thief can only pause these checks, for the owner as for
 18///         themselves.
 19///     </para>
 20///     <para>
 21///         <b>How it is used.</b> Each check is charged before it is compared, and a check made while
 22///         the budget is paused is still compared, its result discarded, so a pause costs and answers
 23///         the same as a wrong credential. A charge that ends in success is cleared, so only failures
 24///         count. The refusal is always <c>AuthMessages.PasswordOrCodeIncorrect</c>.
 25///     </para>
 26/// </remarks>
 27public static class ReauthenticationBudget
 28{
 29    /// <summary>The failures within one <see cref="Window" /> that pause re-authentication.</summary>
 30    public const int MaxFailures = 5;
 31
 32    /// <summary>How long a window lasts, from the first failure counted in it.</summary>
 233    public static readonly TimeSpan Window = TimeSpan.FromMinutes(15);
 34
 35    /// <summary>How long re-authentication stays paused.</summary>
 236    public static readonly TimeSpan Pause = TimeSpan.FromMinutes(15);
 37
 38    /// <summary>
 39    ///     Charges one re-authentication to <paramref name="person" />. <c>false</c> means the budget
 40    ///     is paused: the check may still be made, for its cost, but must not be accepted.
 41    /// </summary>
 42    public static Task<bool> TryChargeAsync(IAtomicWrites atomicWrites, Person person)
 43    {
 23444        var now = DateTime.UtcNow;
 45
 23446        return atomicWrites.TryReserveReauthenticationAsync(person, MaxFailures, now, now - Window, now + Pause);
 47    }
 48
 49    /// <summary>Clears the charge, and any earlier failures, once a re-authentication succeeds.</summary>
 50    public static Task ClearAsync(IAtomicWrites atomicWrites, Person person) =>
 11251        atomicWrites.ClearReauthenticationAttemptsAsync(person);
 52}