File: TunnelExtensionsTests.cs

package info (click to toggle)
golang-github-microsoft-dev-tunnels 0.0.25-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,988 kB
  • sloc: cs: 9,969; java: 2,767; javascript: 328; xml: 186; makefile: 5
file content (133 lines) | stat: -rw-r--r-- 4,542 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
using System.Text;
using Microsoft.DevTunnels.Contracts;
using Microsoft.DevTunnels.Management;
using Xunit;
using static System.Formats.Asn1.AsnWriter;

namespace Microsoft.DevTunnels.Test;

public class TunnelExtensionsTests
{
    private static Tunnel Tunnel { get; } = new Tunnel
    {
        AccessTokens = new Dictionary<string, string>
        {
            ["scope1"] = "token1",
            ["scope2 scope3  scope4"] = "token2",
            [" scope5"] = "token3",
            ["scope6 "] = "token4",
            ["scope3"] = "token5",
            ["scope7"] = "",
            ["scope8 scope9"] = null,
        }
    };

    [Fact]
    public void TryGetAccessToken_NullTunnel_Throws() =>
        Assert.Throws<ArgumentNullException>(() => ((Tunnel)null).TryGetAccessToken("scope", out var _));

    [Fact]
    public void TryGetAccessToken_NullScope_Throws() =>
        Assert.Throws<ArgumentNullException>(() => Tunnel.TryGetAccessToken(null, out var _));

    [Fact]
    public void TryGetAccessToken_EmptyScope_Throws() =>
        Assert.Throws<ArgumentException>(() => Tunnel.TryGetAccessToken(string.Empty, out var _));

    [Fact]
    public void TryGetAccessToken_NullAccessTokens() =>
        Assert.False(new Tunnel().TryGetAccessToken("scope", out var _));

    [Fact]
    public void TryGetValidAccessToken_NullTunnel_Throws() =>
        Assert.Throws<ArgumentNullException>(() => ((Tunnel)null).TryGetValidAccessToken("scope", out var _));

    [Fact]
    public void TryGetValidAccessToken_NullScope_Throws() =>
        Assert.Throws<ArgumentNullException>(() => Tunnel.TryGetValidAccessToken(null, out var _));

    [Fact]
    public void TryGetValidAccessToken_EmptyScope_Throws() =>
        Assert.Throws<ArgumentException>(() => Tunnel.TryGetValidAccessToken(string.Empty, out var _));

    [Fact]
    public void TryGetValidAccessToken_NullAccessTokens() =>
        Assert.False(new Tunnel().TryGetValidAccessToken("scope", out var _));

    [Theory]
    [InlineData("scope1", "token1")]
    [InlineData("scope2", "token2")]
    [InlineData("scope3", "token2")]
    [InlineData("scope4", "token2")]
    [InlineData("scope5", "token3")]
    [InlineData("scope6", "token4")]
    public void TryGetAccessToken(string scope, string expectedToken)
    {
        Assert.True(Tunnel.TryGetAccessToken(scope, out var accessToken));
        Assert.Equal(expectedToken, accessToken);

        // All tokens in the tunnel are not valid JWT, so validation for expiration doesn't trip.
        Assert.True(Tunnel.TryGetValidAccessToken(scope, out accessToken));
        Assert.Equal(expectedToken, accessToken);
    }

    [Theory]
    [InlineData("scope2 scope3")]
    [InlineData("token1")]
    [InlineData("scope7")]
    [InlineData("scope8")]
    [InlineData("scope9")]
    public void TryGetAccessTokenMissingScope(string scope)
    {
        Assert.False(Tunnel.TryGetAccessToken(scope, out var accessToken));
        Assert.Null(accessToken);

        // All tokens in the tunnel are not valid JWT, so validation for expiration doesn't trip.
        Assert.False(Tunnel.TryGetValidAccessToken(scope, out accessToken));
        Assert.Null(accessToken);
    }

    [Fact]
    public void TryGetValidAccessTokenNotExipred()
    {
        var token = GetToken(isExpired: false);
        var tunnel = new Tunnel
        {
            AccessTokens = new Dictionary<string, string>
            {
                ["scope"] = token,
            },
        };

        Assert.True(tunnel.TryGetValidAccessToken("scope", out var accessToken));
        Assert.Equal(token, accessToken);
    }

    [Fact]
    public void TryGetValidAccessTokenExipred()
    {
        var token = GetToken(isExpired: true);
        var tunnel = new Tunnel
        {
            AccessTokens = new Dictionary<string, string>
            {
                ["scope"] = token,
            },
        };

        string accessToken = string.Empty;
        Assert.Throws<UnauthorizedAccessException>(() => tunnel.TryGetValidAccessToken("scope", out accessToken));
        Assert.Null(accessToken);
    }

    private static string GetToken(bool isExpired)
    {
        var exp = DateTimeOffset.UtcNow + (isExpired ? -TimeSpan.FromHours(1) : TimeSpan.FromHours(1));
        var claims = $"{{ \"exp\": {exp.ToUnixTimeSeconds():D} }}";
        var payload = Convert.ToBase64String(Encoding.UTF8.GetBytes(claims))
            .TrimEnd('=')
            .Replace('/', '_')
            .Replace('+', '-');
        return $"header.{payload}.signature";
    }
}