< Summary

Information
Class: Elsa.ExternalAuthentication.Stores.InMemory.InMemoryExternalAuthenticationSessionStore
Assembly: Elsa.ExternalAuthentication
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.ExternalAuthentication/Stores/InMemory/InMemoryExternalAuthenticationSessionStore.cs
Line coverage
83%
Covered lines: 79
Uncovered lines: 16
Coverable lines: 95
Total lines: 156
Line coverage: 83.1%
Branch coverage
66%
Covered branches: 28
Total branches: 42
Branch coverage: 66.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
FindAsync(...)66.66%1212100%
FindByIdAsync(...)50%22100%
FindByRefreshTokenHashAsync(...)25%6450%
SaveAsync(...)100%11100%
TryRotateRefreshTokenAsync(...)92.85%141495.23%
RevokeAsync(...)100%44100%
RevokeActiveForConnectionAsync(...)0%2040%
Clone(...)50%22100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.ExternalAuthentication/Stores/InMemory/InMemoryExternalAuthenticationSessionStore.cs

#LineLine coverage
 1using Elsa.Common;
 2using Elsa.ExternalAuthentication.Contracts;
 3using Elsa.ExternalAuthentication.Models;
 4using Elsa.ExternalAuthentication.Services;
 5
 6namespace Elsa.ExternalAuthentication.Stores.InMemory;
 7
 378public sealed class InMemoryExternalAuthenticationSessionStore(ISystemClock clock) : IExternalAuthenticationSessionStore
 9{
 3710    private readonly object _syncRoot = new();
 3711    private readonly Dictionary<string, ExternalAuthenticationSession> _sessions = new(StringComparer.Ordinal);
 12
 13    public ValueTask<IReadOnlyCollection<ExternalAuthenticationSession>> FindAsync(ExternalAuthenticationSessionFilter f
 14    {
 315        ArgumentNullException.ThrowIfNull(filter);
 316        cancellationToken.ThrowIfCancellationRequested();
 17
 318        lock (_syncRoot)
 19        {
 320            var sessions = _sessions.Values
 521                .Where(x => string.Equals(x.TenantId, filter.TenantId, StringComparison.Ordinal))
 322                .Where(x => filter.UserId is null || string.Equals(x.UserId, filter.UserId, StringComparison.Ordinal))
 323                .Where(x => filter.ConnectionKey is null || string.Equals(x.ConnectionKey, ConnectionRevisionCalculator.
 324                .Where(x => filter.Status is null ||
 325                    filter.Status.Equals("active", StringComparison.OrdinalIgnoreCase) && x.RevokedAt is null ||
 326                    filter.Status.Equals("revoked", StringComparison.OrdinalIgnoreCase) && x.RevokedAt is not null)
 327                .Select(Clone)
 328                .ToArray();
 329            return ValueTask.FromResult<IReadOnlyCollection<ExternalAuthenticationSession>>(sessions);
 30        }
 331    }
 32
 33    public ValueTask<ExternalAuthenticationSession?> FindByIdAsync(string sessionId, CancellationToken cancellationToken
 34    {
 835        cancellationToken.ThrowIfCancellationRequested();
 36
 837        lock (_syncRoot)
 838            return ValueTask.FromResult(_sessions.TryGetValue(sessionId, out var session) ? Clone(session) : null);
 839    }
 40
 41    public ValueTask<ExternalAuthenticationSession?> FindByRefreshTokenHashAsync(string refreshTokenHash, CancellationTo
 42    {
 143        cancellationToken.ThrowIfCancellationRequested();
 44
 145        if (string.IsNullOrWhiteSpace(refreshTokenHash))
 146            return ValueTask.FromResult<ExternalAuthenticationSession?>(null);
 47
 048        lock (_syncRoot)
 049            return ValueTask.FromResult(_sessions.Values.FirstOrDefault(x => string.Equals(x.CurrentRefreshTokenHash, re
 050    }
 51
 52    public ValueTask SaveAsync(ExternalAuthenticationSession session, CancellationToken cancellationToken = default)
 53    {
 1554        cancellationToken.ThrowIfCancellationRequested();
 55
 1556        lock (_syncRoot)
 1557            _sessions[session.Id] = Clone(session);
 58
 1559        return ValueTask.CompletedTask;
 60    }
 61
 62    public ValueTask<ExternalAuthenticationSessionRotationResult> TryRotateRefreshTokenAsync(string sessionId, string re
 63    {
 2464        cancellationToken.ThrowIfCancellationRequested();
 65
 2466        lock (_syncRoot)
 67        {
 2468            if (!_sessions.TryGetValue(sessionId, out var session))
 069                return ValueTask.FromResult<ExternalAuthenticationSessionRotationResult>(new ExternalAuthenticationSessi
 70
 2471            if (session.RevokedAt != null)
 1572                return ValueTask.FromResult<ExternalAuthenticationSessionRotationResult>(new ExternalAuthenticationSessi
 73
 974            if (session.ExpiresAt <= clock.UtcNow || session.RefreshExpiresAt <= clock.UtcNow)
 75            {
 176                session.RevokedAt = clock.UtcNow;
 177                session.RevocationReason = "expired";
 178                session.ProtectedUpstreamLogoutHint = null;
 179                return ValueTask.FromResult<ExternalAuthenticationSessionRotationResult>(new ExternalAuthenticationSessi
 80            }
 81
 882            if (session.CurrentRefreshTokenHash is null || !string.Equals(session.CurrentRefreshTokenHash, refreshTokenH
 83            {
 384                session.RevokedAt = clock.UtcNow;
 385                session.RevocationReason = "refresh_token_reuse";
 386                session.ProtectedUpstreamLogoutHint = null;
 387                return ValueTask.FromResult<ExternalAuthenticationSessionRotationResult>(new ExternalAuthenticationSessi
 88            }
 89
 590            session.CurrentRefreshTokenHash = nextRefreshTokenHash;
 591            session.RefreshGeneration++;
 592            session.LastRefreshedAt = refreshedAt;
 593            return ValueTask.FromResult<ExternalAuthenticationSessionRotationResult>(new ExternalAuthenticationSessionRo
 94        }
 2495    }
 96
 97    public ValueTask<bool> RevokeAsync(string sessionId, string reason, DateTimeOffset revokedAt, CancellationToken canc
 98    {
 499        cancellationToken.ThrowIfCancellationRequested();
 100
 4101        lock (_syncRoot)
 102        {
 4103            if (!_sessions.TryGetValue(sessionId, out var session) || session.RevokedAt != null)
 1104                return ValueTask.FromResult(false);
 105
 3106            session.RevokedAt = revokedAt;
 3107            session.RevocationReason = reason;
 3108            session.ProtectedUpstreamLogoutHint = null;
 3109            return ValueTask.FromResult(true);
 110        }
 4111    }
 112
 113    public ValueTask<int> RevokeActiveForConnectionAsync(string connectionKey, string reason, DateTimeOffset revokedAt, 
 114    {
 0115        ArgumentException.ThrowIfNullOrWhiteSpace(connectionKey);
 0116        cancellationToken.ThrowIfCancellationRequested();
 0117        var normalizedConnectionKey = ConnectionRevisionCalculator.NormalizeKey(connectionKey);
 118
 0119        lock (_syncRoot)
 120        {
 0121            var count = 0;
 0122            foreach (var session in _sessions.Values.Where(x => x.RevokedAt is null && string.Equals(x.ConnectionKey, no
 123            {
 0124                session.RevokedAt = revokedAt;
 0125                session.RevocationReason = reason;
 0126                session.ProtectedUpstreamLogoutHint = null;
 0127                count++;
 128            }
 129
 0130            return ValueTask.FromResult(count);
 131        }
 0132    }
 133
 30134    private static ExternalAuthenticationSession Clone(ExternalAuthenticationSession session) => new()
 30135    {
 30136        Id = session.Id,
 30137        AuthenticationClientId = session.AuthenticationClientId,
 30138        TenantId = session.TenantId,
 30139        UserId = session.UserId,
 30140        ConnectionKey = session.ConnectionKey,
 30141        ConnectionMaterialRevision = session.ConnectionMaterialRevision,
 30142        SecretGenerationFingerprint = session.SecretGenerationFingerprint,
 30143        Issuer = session.Issuer,
 30144        SubjectHash = session.SubjectHash,
 30145        ExternalGrants = session.ExternalGrants.ToArray(),
 30146        StartedAt = session.StartedAt,
 30147        LastRefreshedAt = session.LastRefreshedAt,
 30148        ExpiresAt = session.ExpiresAt,
 30149        RefreshExpiresAt = session.RefreshExpiresAt,
 30150        CurrentRefreshTokenHash = session.CurrentRefreshTokenHash,
 30151        RefreshGeneration = session.RefreshGeneration,
 30152        RevokedAt = session.RevokedAt,
 30153        RevocationReason = session.RevocationReason
 30154        ,ProtectedUpstreamLogoutHint = session.ProtectedUpstreamLogoutHint?.ToArray()
 30155    };
 156}