| | | 1 | | using ArturRios.Data.Relational.Core.Interfaces; |
| | | 2 | | using ArturRios.Heimdall.Domain.Entities; |
| | | 3 | | using Microsoft.EntityFrameworkCore; |
| | | 4 | | |
| | | 5 | | namespace ArturRios.Heimdall.Command.Services; |
| | | 6 | | |
| | | 7 | | /// <summary> |
| | | 8 | | /// Retires a person's outstanding password-reset links when their address changes (UC-08, |
| | | 9 | | /// FR-PE-17): a reset link was mailed to the address being replaced, and it would otherwise let |
| | | 10 | | /// whoever reads that mailbox set the password for the rest of its lifetime. The counterpart of |
| | | 11 | | /// <see cref="EmailVerificationTokenRetirement" />, and it leaves the same things alone. |
| | | 12 | | /// </summary> |
| | | 13 | | /// <remarks> |
| | | 14 | | /// Already-expired tokens are left alone — AF-13a refuses them either way, and rewriting them |
| | | 15 | | /// would only make a dead token report a different reason for being dead — and another person's |
| | | 16 | | /// tokens are never touched. |
| | | 17 | | /// </remarks> |
| | | 18 | | public static class PasswordResetTokenRetirement |
| | | 19 | | { |
| | | 20 | | /// <summary> |
| | | 21 | | /// Marks every live (unused, unexpired at <paramref name="now" />) reset token of the person |
| | | 22 | | /// with internal id <paramref name="personId" /> as used. |
| | | 23 | | /// </summary> |
| | | 24 | | /// <returns>The update errors of the first failed write, or <c>null</c> when all succeeded.</returns> |
| | | 25 | | public static async Task<IEnumerable<string>?> RetireLiveAsync( |
| | | 26 | | IAsyncReadOnlyRepository<PasswordResetToken, long> tokenReader, |
| | | 27 | | IAsyncRepository<PasswordResetToken, long> tokenWriter, |
| | | 28 | | long personId, |
| | | 29 | | DateTime now) |
| | | 30 | | { |
| | 68 | 31 | | var live = await tokenReader.Query() |
| | 68 | 32 | | .Where(x => x.PersonId == personId && !x.Used && x.ExpiresAt > now) |
| | 68 | 33 | | .ToListAsync(); |
| | | 34 | | |
| | 146 | 35 | | foreach (var outstanding in live) |
| | | 36 | | { |
| | 5 | 37 | | outstanding.Used = true; |
| | | 38 | | |
| | 5 | 39 | | var update = await tokenWriter.UpdateAsync(outstanding); |
| | | 40 | | |
| | 5 | 41 | | if (!update.Success) |
| | | 42 | | { |
| | 0 | 43 | | return update.Errors; |
| | | 44 | | } |
| | | 45 | | } |
| | | 46 | | |
| | 68 | 47 | | return null; |
| | 68 | 48 | | } |
| | | 49 | | } |