< Summary - Heimdall API

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

Metrics

File(s)

/home/runner/work/heimdall-api/heimdall-api/src/Application/ArturRios.Heimdall.Command/Output/DataExportCommandOutput.cs

#LineLine coverage
 1using ArturRios.Mediator.Command;
 2
 3namespace ArturRios.Heimdall.Command.Output;
 4
 5/// <summary>
 6///     Everything Heimdall holds about one data subject (UC-41), in one structured document.
 7/// </summary>
 8/// <remarks>
 9///     Art. 15 asks for a copy of the personal data <em>and</em> the context that makes it
 10///     intelligible — the purposes, the recipients, the retention periods, the rights. A dump of
 11///     table rows would satisfy the first half and none of the second, so the sections after
 12///     <see cref="AuditEntries" /> are part of the answer rather than decoration.
 13/// </remarks>
 14public class DataExportCommandOutput : CommandOutput
 15{
 16    /// <summary>When this copy was produced.</summary>
 17    public DateTime ExportedAt { get; set; }
 18
 19    /// <summary>Who it is about, and the values that identify them.</summary>
 20    public required DataExportSubject Subject { get; set; }
 21
 22    /// <summary>The state of the account's credentials, without the credentials.</summary>
 23    public required DataExportSecurity Security { get; set; }
 24
 25    /// <summary>The lawful basis this data is held on, and what the subject was told.</summary>
 26    public required DataExportProcessing Processing { get; set; }
 27
 28    /// <summary>Deletion and erasure state, where any applies.</summary>
 29    public DataExportErasure? Erasure { get; set; }
 30
 31    /// <summary>Scopes the subject belongs to or owns, by public identifier.</summary>
 32    public IEnumerable<Guid> ScopeMemberships { get; set; } = [];
 33
 34    /// <inheritdoc cref="ScopeMemberships" />
 35    public IEnumerable<Guid> ScopeOwnerships { get; set; } = [];
 36
 37    /// <summary>Applications the subject owns.</summary>
 38    public IEnumerable<DataExportApplication> Applications { get; set; } = [];
 39
 40    /// <summary>Every audit entry still attributed to the subject.</summary>
 41    /// <remarks>
 42    ///     Entries whose attribution has been cleared under NFR-21 are absent, and necessarily so:
 43    ///     once cleared, nothing connects them to this person, which is the whole point of clearing
 44    ///     them. <see cref="AuditEntriesTruncated" /> says whether the cap was reached.
 45    /// </remarks>
 46    public IEnumerable<DataExportAuditEntry> AuditEntries { get; set; } = [];
 47
 48    /// <summary>
 49    ///     Whether more audit entries exist than this document carries. Disclosed rather than
 50    ///     silently truncated: a copy that quietly omits part of the data is not the copy Art. 15
 51    ///     asks for.
 52    /// </summary>
 53    public bool AuditEntriesTruncated { get; set; }
 54
 55    /// <summary>
 56    ///     Values held about the subject that are deliberately not reproduced here, each with the
 57    ///     reason.
 58    /// </summary>
 59    /// <remarks>
 60    ///     Art. 15(4) says the right to a copy must not adversely affect others, and reproducing a
 61    ///     password hash or a TOTP secret would hand an attacker holding a stolen token the material
 62    ///     to attack the account offline. They are named rather than silently omitted, so the
 63    ///     subject knows what exists.
 64    /// </remarks>
 65    public IEnumerable<DataExportWithheld> Withheld { get; set; } = [];
 66
 67    /// <summary>Who else receives this data (GDPR Art. 15(1)(c), LGPD Art. 18 VII).</summary>
 68    public IEnumerable<DataExportRecipient> Recipients { get; set; } = [];
 69
 70    /// <summary>
 71    ///     Where data that did not come from the subject came from instead (GDPR Art. 14(2)(f) and
 72    ///     Art. 15(1)(g), LGPD Art. 9).
 73    /// </summary>
 74    /// <remarks>
 75    ///     Separate from <see cref="Recipients" /> because the direction is opposite and conflating
 76    ///     them misleads in both directions. Google is the clearest case: it is where a Google
 77    ///     User's name and address came from, and nothing about them is ever sent to it — the ID
 78    ///     token is validated offline against cached public certificates.
 79    /// </remarks>
 80    public IEnumerable<DataExportSource> Sources { get; set; } = [];
 81
 82    /// <summary>How long each category is kept (GDPR Art. 15(1)(d)).</summary>
 83    public IEnumerable<DataExportRetention> Retention { get; set; } = [];
 84}
 85
 86/// <summary>The identifying values held about the subject.</summary>
 87public class DataExportSubject
 88{
 89    /// <summary>Public identifier. The internal database key is never exposed (NFR-15).</summary>
 90    public Guid Id { get; set; }
 91
 92    /// <summary>Whether the subject authenticates with a password or through Google.</summary>
 93    public string Kind { get; set; } = string.Empty;
 94
 95    public string Name { get; set; } = string.Empty;
 96    public string Email { get; set; } = string.Empty;
 97    public bool EmailVerified { get; set; }
 98
 99    /// <summary>Role value, for a person; <c>null</c> for a Google User, who is always User-equivalent.</summary>
 100    public int? Role { get; set; }
 101
 102    /// <summary>Google's stable subject identifier, for a Google User.</summary>
 103    public string? GoogleId { get; set; }
 104
 105    /// <summary>Profile picture URL, for a Google User.</summary>
 106    public string? ProfilePictureUrl { get; set; }
 107
 108    public bool IsDeleted { get; set; }
 109    public DateTime CreatedAt { get; set; }
 110    public DateTime UpdatedAt { get; set; }
 111}
 112
 113/// <summary>The state of the account's credentials, described rather than disclosed.</summary>
 114public class DataExportSecurity
 115{
 116    /// <summary>Whether a password is set. A Google User has none.</summary>
 49117    public bool PasswordSet { get; set; }
 118
 119    /// <summary>Whether two-factor authentication is active.</summary>
 46120    public bool TwoFactorActive { get; set; }
 121
 122    /// <summary>Whether the authenticator-app method is enabled.</summary>
 45123    public bool TwoFactorAppEnabled { get; set; }
 124
 125    /// <summary>Whether the email method is enabled.</summary>
 44126    public bool TwoFactorEmailEnabled { get; set; }
 127
 128    /// <summary>Recovery codes issued and not yet used.</summary>
 45129    public int UnusedRecoveryCodes { get; set; }
 130
 131    /// <summary>Consecutive failed sign-ins since the last success.</summary>
 44132    public int FailedLoginAttempts { get; set; }
 133
 134    /// <summary>When the account is locked out until, if it is.</summary>
 44135    public DateTime? LockedOutUntil { get; set; }
 136
 137    /// <summary>Failed second factors at sign-in since the last successful one (FR-AU-12).</summary>
 45138    public int FailedSecondFactorAttempts { get; set; }
 139
 140    /// <summary>When second-factor verification is locked until, if it is or was (FR-AU-12).</summary>
 45141    public DateTime? SecondFactorLockedOutUntil { get; set; }
 142
 143    /// <summary>Second-factor locks since the last successful second factor, which set the next one's length.</summary>
 45144    public int SecondFactorLockouts { get; set; }
 145
 146    /// <summary>Failed re-authentications in the current window (FR-AU-13).</summary>
 45147    public int FailedReauthenticationAttempts { get; set; }
 148
 149    /// <summary>When the current re-authentication window began, if one is open (FR-AU-13).</summary>
 45150    public DateTime? ReauthenticationWindowStartedAt { get; set; }
 151
 152    /// <summary>When re-authentication is paused until, if it is or was (FR-AU-13).</summary>
 45153    public DateTime? ReauthenticationPausedUntil { get; set; }
 154
 155    /// <summary>Account emails the person's own requests had sent in the current window (FR-PE-16).</summary>
 45156    public int AccountEmailsSent { get; set; }
 157
 158    /// <summary>When the current account-email window ends, if one was opened (FR-PE-16).</summary>
 45159    public DateTime? AccountEmailWindowEndsAt { get; set; }
 160
 161    /// <summary>When the last of those account emails was sent, which the cooldown counts from (FR-PE-16).</summary>
 45162    public DateTime? AccountEmailLastSentAt { get; set; }
 163}
 164
 165/// <summary>The lawful basis and the notice the subject was shown.</summary>
 166public class DataExportProcessing
 167{
 168    /// <summary>The recorded basis value (see <c>LegalBases</c>).</summary>
 169    public int LegalBasis { get; set; }
 170
 171    /// <summary>The basis in words, so the document is readable without the enum.</summary>
 172    public string LegalBasisName { get; set; } = string.Empty;
 173
 174    /// <summary>The privacy notice version in force when the identity was created.</summary>
 175    public string? PrivacyNoticeVersion { get; set; }
 176
 177    /// <summary>When the basis was recorded.</summary>
 178    public DateTime? RecordedAt { get; set; }
 179}
 180
 181/// <summary>Deletion and erasure state.</summary>
 182public class DataExportErasure
 183{
 184    public bool IsDeleted { get; set; }
 185    public DateTime? DeletedAt { get; set; }
 186    public int? DeletionKind { get; set; }
 187    public DateTime? AnonymisedAt { get; set; }
 188    public DateTime? ErasureRequestedAt { get; set; }
 189    public DateTime? ErasureDueAt { get; set; }
 190    public string? ErasureBlockedReason { get; set; }
 191}
 192
 193/// <summary>An application the subject owns.</summary>
 194public class DataExportApplication
 195{
 196    public Guid Id { get; set; }
 197    public string Name { get; set; } = string.Empty;
 198    public Guid ScopeId { get; set; }
 199    public bool IsDeleted { get; set; }
 200}
 201
 202/// <summary>One audit entry attributed to the subject.</summary>
 203public class DataExportAuditEntry
 204{
 205    public Guid Id { get; set; }
 206    public string Action { get; set; } = string.Empty;
 207    public Guid? TargetId { get; set; }
 208    public bool Succeeded { get; set; }
 209    public string? FailureReason { get; set; }
 210    public DateTime CreatedAt { get; set; }
 211}
 212
 213/// <summary>A value held but not reproduced, and why.</summary>
 214public class DataExportWithheld
 215{
 216    public string Value { get; set; } = string.Empty;
 217    public string Reason { get; set; } = string.Empty;
 218}
 219
 220/// <summary>Somebody else this data reaches.</summary>
 221public class DataExportRecipient
 222{
 223    public string Name { get; set; } = string.Empty;
 224    public string Purpose { get; set; } = string.Empty;
 225    public string Location { get; set; } = string.Empty;
 226}
 227
 228/// <summary>Somewhere data about the subject came from, other than the subject.</summary>
 229public class DataExportSource
 230{
 231    public string Name { get; set; } = string.Empty;
 232
 233    /// <summary>What came from there.</summary>
 234    public string Provides { get; set; } = string.Empty;
 235
 236    /// <summary>Whether anything about the subject is sent back to it.</summary>
 237    public string DataSentThere { get; set; } = string.Empty;
 238}
 239
 240/// <summary>How long a category of data is kept.</summary>
 241public class DataExportRetention
 242{
 243    public string Category { get; set; } = string.Empty;
 244    public string Period { get; set; } = string.Empty;
 245}