| | | 1 | | using ArturRios.Heimdall.Command.Input; |
| | | 2 | | using ArturRios.Heimdall.Command.Output; |
| | | 3 | | using ArturRios.Heimdall.Shared.Retention; |
| | | 4 | | using ArturRios.Mediator.Command; |
| | | 5 | | |
| | | 6 | | namespace ArturRios.Heimdall.WebApi.Retention; |
| | | 7 | | |
| | | 8 | | /// <summary> |
| | | 9 | | /// Runs <see cref="PurgeExpiredTokensCommand" /> on an interval, so the single-use token |
| | | 10 | | /// retention period of NFR-19 is enforced without anyone having to call anything. |
| | | 11 | | /// </summary> |
| | | 12 | | public class TokenRetentionService( |
| | | 13 | | IServiceScopeFactory scopeFactory, |
| | | 14 | | DataRetentionOptions retention, |
| | | 15 | | ILogger<TokenRetentionService> logger) |
| | 899 | 16 | | : ScheduledPassService(scopeFactory, retention.PurgeInterval, logger) |
| | | 17 | | { |
| | | 18 | | protected override string StartupDescription => |
| | 899 | 19 | | $"Token retention purge scheduled every {retention.PurgeInterval}, removing tokens more than " + |
| | 899 | 20 | | $"{retention.SingleUseTokenGrace} past expiry"; |
| | | 21 | | |
| | | 22 | | protected override async Task<string?> RunAsync(IServiceProvider scope) |
| | | 23 | | { |
| | 0 | 24 | | var mediator = scope.GetRequiredService<CommandMediator>(); |
| | | 25 | | |
| | 0 | 26 | | var result = await mediator |
| | 0 | 27 | | .ExecuteCommandAsync<PurgeExpiredTokensCommand, PurgeExpiredTokensCommandOutput>( |
| | 0 | 28 | | new PurgeExpiredTokensCommand()); |
| | | 29 | | |
| | 0 | 30 | | if (!result.Success) |
| | | 31 | | { |
| | 0 | 32 | | return $"Token retention purge failed: {string.Join("; ", result.Errors)}"; |
| | | 33 | | } |
| | | 34 | | |
| | 0 | 35 | | var purged = result.Data!; |
| | | 36 | | |
| | 0 | 37 | | return purged.TotalPurged == 0 |
| | 0 | 38 | | ? null |
| | 0 | 39 | | : $"Token retention purge removed {purged.TotalPurged} rows: " + |
| | 0 | 40 | | $"{purged.PasswordResetTokensPurged} password reset, " + |
| | 0 | 41 | | $"{purged.EmailVerificationTokensPurged} email verification, " + |
| | 0 | 42 | | $"{purged.TwoFactorEmailCodesPurged} two-factor email, " + |
| | 0 | 43 | | $"{purged.PendingRegistrationsPurged} pending registration, " + |
| | 0 | 44 | | $"{purged.ApplicationCredentialsPurged} application credential, " + |
| | 0 | 45 | | $"{purged.AttemptNoticeBudgetsPurged} attempt-notice budget"; |
| | 0 | 46 | | } |
| | | 47 | | } |