| | | 1 | | using ArturRios.Heimdall.Domain.Entities; |
| | | 2 | | using ArturRios.Heimdall.Domain.Enums; |
| | | 3 | | |
| | | 4 | | namespace ArturRios.Heimdall.Command.Services; |
| | | 5 | | |
| | | 6 | | /// <summary>The outcome of <see cref="PasswordRecoveryEligibility.Of" />.</summary> |
| | | 7 | | public enum RecoveryEligibility |
| | | 8 | | { |
| | | 9 | | /// <summary>A reset link would let this person sign in again.</summary> |
| | | 10 | | Eligible, |
| | | 11 | | |
| | | 12 | | /// <summary>Processing of the person is restricted (NFR-24); sending would be processing them.</summary> |
| | | 13 | | Restricted, |
| | | 14 | | |
| | | 15 | | /// <summary>The person is logically deleted, or their record has been anonymised (NFR-20).</summary> |
| | | 16 | | Deleted, |
| | | 17 | | |
| | | 18 | | /// <summary> |
| | | 19 | | /// The account could not sign in whatever its password: a <c>User</c> whose scope is logically |
| | | 20 | | /// deleted or missing (FR-AU-06), or a <c>ScopeAdmin</c> with no live scope (FR-AU-07). |
| | | 21 | | /// </summary> |
| | | 22 | | CannotSignIn |
| | | 23 | | } |
| | | 24 | | |
| | | 25 | | /// <summary> |
| | | 26 | | /// Who a password-reset link is worth sending to: UC-12's rule, shared with UC-52 so the anonymous |
| | | 27 | | /// recovery and the administrator's request cannot disagree (FR-PR-02, FR-PR-05). Run again when |
| | | 28 | | /// a reset or verification link is followed (UC-13, UC-14: FR-PR-08, FR-EV-08), so a link issued |
| | | 29 | | /// before the account changed cannot act on it afterwards. |
| | | 30 | | /// </summary> |
| | | 31 | | /// <remarks> |
| | | 32 | | /// It mirrors the account checks UC-11 applies at login: a link is only worth issuing to someone |
| | | 33 | | /// who could use the resulting password. UC-12 needs only yes or no, since it answers every caller |
| | | 34 | | /// alike; UC-52's caller is authenticated and owed the reason, so the outcome names it. The |
| | | 35 | | /// restriction is checked first, as UC-12 always has, so a person both restricted and deleted is |
| | | 36 | | /// reported as restricted. An anonymised record counts as deleted whatever its flag says: it |
| | | 37 | | /// belongs to nobody, and its address is a placeholder. |
| | | 38 | | /// </remarks> |
| | | 39 | | public static class PasswordRecoveryEligibility |
| | | 40 | | { |
| | | 41 | | /// <summary> |
| | | 42 | | /// Evaluates <paramref name="person" />. <c>ScopeMembership.Scope</c> and every |
| | | 43 | | /// <c>ScopeOwnerships[].Scope</c> must be loaded. |
| | | 44 | | /// </summary> |
| | | 45 | | public static RecoveryEligibility Of(Person person) |
| | | 46 | | { |
| | 155 | 47 | | if (person.ProcessingRestrictedAt is not null) |
| | | 48 | | { |
| | 10 | 49 | | return RecoveryEligibility.Restricted; |
| | | 50 | | } |
| | | 51 | | |
| | 145 | 52 | | if (person.IsDeleted || person.AnonymisedAt is not null) |
| | | 53 | | { |
| | 14 | 54 | | return RecoveryEligibility.Deleted; |
| | | 55 | | } |
| | | 56 | | |
| | 131 | 57 | | var maySignIn = person.RoleId switch |
| | 131 | 58 | | { |
| | 27 | 59 | | (long)Roles.User => person.ScopeMembership is { Scope.IsDeleted: false }, |
| | 33 | 60 | | (long)Roles.ScopeAdmin => person.ScopeOwnerships.Any(ownership => !ownership.Scope.IsDeleted), |
| | 89 | 61 | | _ => true |
| | 131 | 62 | | }; |
| | | 63 | | |
| | 131 | 64 | | return maySignIn ? RecoveryEligibility.Eligible : RecoveryEligibility.CannotSignIn; |
| | | 65 | | } |
| | | 66 | | } |