< Summary - Heimdall API

Information
Class: ArturRios.Heimdall.Domain.Entities.ApplicationCredential
Assembly: ArturRios.Heimdall.Domain
File(s): /home/runner/work/heimdall-api/heimdall-api/src/Domain/ArturRios.Heimdall.Domain/Entities/ApplicationCredential.cs
Line coverage
100%
Covered lines: 11
Uncovered lines: 0
Coverable lines: 11
Total lines: 77
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage
100%
Covered methods: 10
Fully covered methods: 10
Total methods: 10
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_PublicId()100%11100%
get_ApplicationId()100%11100%
get_SecretHash()100%11100%
get_CreatedAt()100%11100%
get_ExpiresAt()100%11100%
get_RevokedAt()100%11100%
get_LastUsedAt()100%11100%
get_Application()100%11100%
LiveAt(...)100%11100%
IsLiveAt(...)100%11100%

File(s)

/home/runner/work/heimdall-api/heimdall-api/src/Domain/ArturRios.Heimdall.Domain/Entities/ApplicationCredential.cs

#LineLine coverage
 1using System.ComponentModel.DataAnnotations;
 2using System.Linq.Expressions;
 3using ArturRios.Data.Relational.Core.Entities;
 4
 5namespace ArturRios.Heimdall.Domain.Entities;
 6
 7/// <summary>
 8///     A secret an <see cref="Application" /> exchanges for an application token (UC-53, UC-56,
 9///     FR-AP-10). Only the secret's SHA-256 is stored; the secret itself is shown once, in UC-53's
 10///     response, and never again.
 11/// </summary>
 12/// <remarks>
 13///     <para>
 14///         An application holds at most <see cref="MaxLive" /> live credentials, so a new one can be
 15///         deployed before the old one is revoked (FR-AP-11). Each is revoked on its own (UC-55) and
 16///         may carry an expiry.
 17///     </para>
 18///     <para>
 19///         No personal data: the row names an application, a hash and four instants. Logically
 20///         deleting the application or its scope leaves the rows alone — UC-56 and UC-57 refuse a
 21///         deleted application or scope, so its credentials are inert — and hard-deleting the
 22///         application removes them by cascade. Revoked and expired rows are purged after the
 23///         single-use-token grace by the token retention pass.
 24///     </para>
 25/// </remarks>
 26public class ApplicationCredential : Entity<long>
 27{
 28    /// <summary>The most live credentials one application may hold at once (FR-AP-11).</summary>
 29    public const int MaxLive = 2;
 30
 31    /// <summary>
 32    ///     External identifier — the credential's reference in UC-54's listing, UC-55's route, the
 33    ///     token's <c>credentialId</c> claim and the audit trail. The internal
 34    ///     <see cref="Entity{TKey}.Id" /> is never exposed.
 35    /// </summary>
 25036    public Guid PublicId { get; set; } = Guid.NewGuid();
 37
 38    /// <summary>Foreign key to the owning <see cref="Application" /> (internal Id). Required.</summary>
 14439    public long ApplicationId { get; set; }
 40
 41    /// <summary>
 42    ///     SHA-256 of the secret, as lowercase hex (<c>ApplicationCredentialSecret.HashOf</c>). The
 43    ///     secret itself is never stored: whoever could read this table could otherwise authenticate
 44    ///     as every application (Threat Model TH-33).
 45    /// </summary>
 46    [MaxLength(64)]
 25847    public string SecretHash { get; set; } = string.Empty;
 48
 49    /// <summary>When the credential was issued (UC-53).</summary>
 9850    public DateTime CreatedAt { get; set; }
 51
 52    /// <summary>When the credential stops being accepted, or <c>null</c> for never.</summary>
 15353    public DateTime? ExpiresAt { get; set; }
 54
 55    /// <summary>When the credential was revoked (UC-55), or <c>null</c> while it has not been.</summary>
 13656    public DateTime? RevokedAt { get; set; }
 57
 58    /// <summary>When a token was last issued from this credential (UC-56), or <c>null</c> if never.</summary>
 2159    public DateTime? LastUsedAt { get; set; }
 60
 61    // Navigation properties
 62
 63    /// <summary>The application this credential authenticates.</summary>
 16364    public Application Application { get; set; } = null!;
 65
 66    /// <summary>
 67    ///     The one definition of a live credential — not revoked, and either without expiry or
 68    ///     expiring after <paramref name="now" /> — as an expression, so a query can filter on it in
 69    ///     the database.
 70    /// </summary>
 71    public static Expression<Func<ApplicationCredential, bool>> LiveAt(DateTime now) =>
 9272        credential => credential.RevokedAt == null &&
 9273                      (credential.ExpiresAt == null || credential.ExpiresAt > now);
 74
 75    /// <summary><see cref="LiveAt" />, evaluated for this instance.</summary>
 1476    public bool IsLiveAt(DateTime now) => LiveAt(now).Compile()(this);
 77}