| | | 1 | | using Elsa.Common; |
| | | 2 | | using Elsa.ExternalAuthentication.Contracts; |
| | | 3 | | using Elsa.ExternalAuthentication.Models; |
| | | 4 | | |
| | | 5 | | namespace Elsa.ExternalAuthentication.Stores.InMemory; |
| | | 6 | | |
| | 33 | 7 | | public sealed class InMemoryExternalAuthenticationStateStore(ISystemClock clock) : IExternalAuthenticationStateStore |
| | | 8 | | { |
| | 33 | 9 | | private readonly object _syncRoot = new(); |
| | 33 | 10 | | private readonly Dictionary<(string Purpose, string HandleHash), StateEntry> _entries = new(); |
| | | 11 | | |
| | | 12 | | public ValueTask PutAsync<T>(string purpose, string handleHash, T value, DateTimeOffset expiresAt, CancellationToken |
| | | 13 | | { |
| | 20 | 14 | | cancellationToken.ThrowIfCancellationRequested(); |
| | 20 | 15 | | var key = (purpose, handleHash); |
| | | 16 | | |
| | 20 | 17 | | lock (_syncRoot) |
| | | 18 | | { |
| | 20 | 19 | | if (_entries.ContainsKey(key)) |
| | 1 | 20 | | throw new InvalidOperationException("A state entry already exists for the supplied purpose and handle.") |
| | | 21 | | |
| | 19 | 22 | | _entries[key] = new StateEntry(value, expiresAt); |
| | 19 | 23 | | } |
| | | 24 | | |
| | 19 | 25 | | return ValueTask.CompletedTask; |
| | | 26 | | } |
| | | 27 | | |
| | | 28 | | public ValueTask<TakeResult<T>> TryTakeAsync<T>(string purpose, string handleHash, CancellationToken cancellationTok |
| | | 29 | | { |
| | 33 | 30 | | cancellationToken.ThrowIfCancellationRequested(); |
| | | 31 | | |
| | 33 | 32 | | lock (_syncRoot) |
| | | 33 | | { |
| | 33 | 34 | | if (!_entries.TryGetValue((purpose, handleHash), out var entry) || entry.Value is not T value) |
| | 0 | 35 | | return ValueTask.FromResult<TakeResult<T>>(new TakeResult<T>.NotFound()); |
| | | 36 | | |
| | 33 | 37 | | if (entry.ExpiresAt <= clock.UtcNow) |
| | 1 | 38 | | return ValueTask.FromResult<TakeResult<T>>(new TakeResult<T>.Expired()); |
| | | 39 | | |
| | 32 | 40 | | if (entry.IsConsumed) |
| | 17 | 41 | | return ValueTask.FromResult<TakeResult<T>>(new TakeResult<T>.AlreadyConsumed()); |
| | | 42 | | |
| | 15 | 43 | | entry.IsConsumed = true; |
| | 15 | 44 | | return ValueTask.FromResult<TakeResult<T>>(new TakeResult<T>.Taken(value)); |
| | | 45 | | } |
| | 33 | 46 | | } |
| | | 47 | | |
| | 19 | 48 | | private sealed class StateEntry(object? value, DateTimeOffset expiresAt) |
| | | 49 | | { |
| | 52 | 50 | | public object? Value { get; } = value; |
| | 52 | 51 | | public DateTimeOffset ExpiresAt { get; } = expiresAt; |
| | 47 | 52 | | public bool IsConsumed { get; set; } |
| | | 53 | | } |
| | | 54 | | } |