< Summary

Information
Class: Elsa.SasTokens.Contracts.DataProtectorTokenService
Assembly: Elsa.SasTokens
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.SasTokens/Contracts/DataProtectorTokenService.cs
Line coverage
93%
Covered lines: 29
Uncovered lines: 2
Coverable lines: 31
Total lines: 102
Line coverage: 93.5%
Branch coverage
100%
Covered branches: 4
Total branches: 4
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
CreateToken(...)100%11100%
CreateToken(...)100%11100%
CreateToken(...)100%11100%
TryDecryptToken(...)100%11100%
DecryptToken(...)100%11100%
Unprotect(...)100%4483.33%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.SasTokens/Contracts/DataProtectorTokenService.cs

#LineLine coverage
 1using System.Security.Cryptography;
 2using System.Text.Json;
 3using Microsoft.AspNetCore.DataProtection;
 4
 5namespace Elsa.SasTokens.Contracts;
 6
 7/// <summary>
 8/// A service that can create and decrypt SAS (Shared Access Signature) tokens using the <see cref="Microsoft.AspNetCore
 9/// </summary>
 10public class DataProtectorTokenService : ITokenService
 11{
 12    private const string TimeLimitedTokenPrefix = "v1.tl.";
 13    private const string NonExpiringTokenPrefix = "v1.ne.";
 14    private readonly IDataProtector _dataProtector;
 15    private readonly ITimeLimitedDataProtector _timeLimitedDataProtector;
 16
 17    /// <summary>
 18    /// Initializes a new instance of the <see cref="DataProtectorTokenService"/> class.
 19    /// </summary>
 920    public DataProtectorTokenService(IDataProtectionProvider dataProtector)
 21    {
 922        _dataProtector = dataProtector.CreateProtector("Elsa Tokens");
 923        _timeLimitedDataProtector = _dataProtector.ToTimeLimitedDataProtector();
 924    }
 25
 26    /// <inheritdoc />
 27    public string CreateToken<T>(T payload, TimeSpan lifetime)
 28    {
 129        var json = JsonSerializer.Serialize(payload);
 130        return TimeLimitedTokenPrefix + _timeLimitedDataProtector.Protect(json, lifetime);
 31    }
 32
 33    /// <inheritdoc />
 34    public string CreateToken<T>(T payload, DateTimeOffset expiresAt)
 35    {
 236        var json = JsonSerializer.Serialize(payload);
 237        return TimeLimitedTokenPrefix + _timeLimitedDataProtector.Protect(json, expiresAt);
 38    }
 39
 40    /// <inheritdoc />
 41    public string CreateToken<T>(T payload)
 42    {
 143        var json = JsonSerializer.Serialize(payload);
 144        return NonExpiringTokenPrefix + _dataProtector.Protect(json);
 45    }
 46
 47    /// <inheritdoc />
 48    public bool TryDecryptToken<T>(string token, out T payload)
 49    {
 550        payload = default!;
 51
 52        try
 53        {
 554            payload = DecryptToken<T>(token);
 355            return true;
 56        }
 257        catch
 58        {
 59            // ignored.
 260        }
 61
 262        return false;
 363    }
 64
 65    /// <inheritdoc />
 66    public T DecryptToken<T>(string token)
 67    {
 668        var json = Unprotect(token);
 469        return JsonSerializer.Deserialize<T>(json)!;
 70    }
 71
 72    private string Unprotect(string token)
 73    {
 674        if (token.StartsWith(TimeLimitedTokenPrefix, StringComparison.Ordinal))
 375            return _timeLimitedDataProtector.Unprotect(token[TimeLimitedTokenPrefix.Length..]);
 76
 377        if (token.StartsWith(NonExpiringTokenPrefix, StringComparison.Ordinal))
 178            return _dataProtector.Unprotect(token[NonExpiringTokenPrefix.Length..]);
 79
 80        try
 81        {
 282            return _timeLimitedDataProtector.Unprotect(token);
 83        }
 184        catch (CryptographicException)
 85        {
 186            var json = _dataProtector.Unprotect(token);
 87
 88            // Expired time-limited tokens can be decrypted by the base protector,
 89            // but the result includes the expiration header.
 90            // Only accept fallback payloads that are standalone JSON produced by CreateToken(payload).
 91            try
 92            {
 193                using var _ = JsonDocument.Parse(json);
 194                return json;
 95            }
 096            catch (JsonException e)
 97            {
 098                throw new CryptographicException("Token payload is not a valid non-expiring token.", e);
 99            }
 100        }
 2101    }
 102}