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";
}
}
|