Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flatten role claims #21180

Merged
merged 4 commits into from
Jun 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ public virtual Uri GenerateClientAccessUri(string userId = default, string[] rol
}
if (roles != default && roles.Length > 0)
{
var jsonArray = BinaryData.FromObjectAsJson(roles).ToString();
var role = new Claim("role", jsonArray);
claims.Add(role);
foreach (var role in roles)
{
claims.Add(new Claim("role", role));
}
}

string endpoint = this.endpoint.AbsoluteUri;
Expand All @@ -56,7 +57,7 @@ public virtual Uri GenerateClientAccessUri(string userId = default, string[] rol
string token = WebPubSubAuthenticationPolicy.GenerateAccessToken(audience, claims, _credential, expiresAfter);

var clientEndpoint = new UriBuilder(endpoint);
clientEndpoint.Scheme = "wss";
clientEndpoint.Scheme = this.endpoint.Scheme == "http" ? "ws" : "wss";
var uriString = $"{clientEndpoint}client/hubs/{hub}?access_token={token}";

return new Uri(uriString);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Web;

using NUnit.Framework;
Expand All @@ -12,29 +13,78 @@ namespace Azure.Messaging.WebPubSub.Tests
[TestFixture]
public class WebPubSubParseConnectionStringTests
{
private const string FakeAccessKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGH";
private static readonly JwtSecurityTokenHandler JwtTokenHandler = new JwtSecurityTokenHandler();

[TestCase("Endpoint=https://host;{0};Version=1.0;", "https://host", "abcdefg")]
[TestCase("Endpoint=http://host;{0};Version=1.0;", "http://host", "abcdefg")]
[TestCase("Endpoint=http://host;{0};Version=1.0;Port=8080;", "http://host:8080", "abcdefg")]
[TestCase("{0};Endpoint=http://host;Version=1.0;", "http://host", "abcdefg")]
public void ParseConnectionStringTests(string connectionString, string url, string key)
[TestCase("Endpoint=https://host;AccessKey={0};Version=1.0;", "https://host")]
[TestCase("Endpoint=http://host;AccessKey={0};Version=1.0;", "http://host")]
[TestCase("Endpoint=http://host;AccessKey={0};Version=1.0;Port=8080;", "http://host:8080")]
[TestCase("AccessKey={0};Endpoint=http://host;Version=1.0;", "http://host")]
public void ParseConnectionStringTests(string connectionString, string url)
{
connectionString = string.Format(connectionString, $"AccessKey={key}"); // this is so that credscan is not triggered.
connectionString = string.Format(connectionString, FakeAccessKey);
var (uri, credential) = WebPubSubServiceClient.ParseConnectionString(connectionString);
Assert.AreEqual(new Uri(url), uri);
Assert.AreEqual(key, credential.Key);
Assert.AreEqual(FakeAccessKey, credential.Key);
}

[TestCase("Endpoint=http://localhost;Port=8080;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGH;Version=1.0;")]
public void TestGenerateUriUseSameKidWithSameKey(string connectionString)
[TestCase(null, null)]
[TestCase("ab", new[] { "a" })]
[TestCase("ab", new[] { "a", "a", "a" })]
[TestCase("ab", new[] { "a", "b", "c" })]
[TestCase("ab", new string[0])]
public void TestGenerateUriContainsExpectedPayloads(string userId, string[] roles)
{
var serviceClient = new WebPubSubServiceClient(" Endpoint=http://localhost;Port=8080;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGH;Version=1.0;", "hub");
var serviceClient = new WebPubSubServiceClient(string.Format("Endpoint=http://localhost;Port=8080;AccessKey={0};Version=1.0;", FakeAccessKey), "hub");
var uri = serviceClient.GenerateClientAccessUri(userId, roles, TimeSpan.FromMinutes(5));
var token = HttpUtility.ParseQueryString(uri.Query).Get("access_token");
Assert.NotNull(token);
var jwt = JwtTokenHandler.ReadJwtToken(token);

var audience = jwt.Claims.FirstOrDefault(s => s.Type == "aud");
Assert.NotNull(audience);
Assert.AreEqual("http://localhost:8080/client/hubs/hub", audience.Value);
var iat = jwt.Claims.FirstOrDefault(s => s.Type == "iat")?.Value;
Assert.NotNull(iat);
Assert.IsTrue(long.TryParse(iat, out var issuedAt));
var exp = jwt.Claims.FirstOrDefault(s => s.Type == "exp")?.Value;
Assert.NotNull(exp);
Assert.IsTrue(long.TryParse(exp, out var expireAt));
Assert.AreEqual(TimeSpan.FromMinutes(5).TotalSeconds, expireAt - issuedAt);
var sub = jwt.Claims.Where(s => s.Type == "sub").Select(s => s.Value).ToArray();

if (userId != null)
{
Assert.AreEqual(1, sub.Length);
Assert.AreEqual(userId, sub[0]);
}
else
{
Assert.IsEmpty(sub);
}

var roleClaims = jwt.Claims.Where(s => s.Type == "role").Select(s => s.Value).ToArray();
if (roles?.Length > 0)
{
Assert.AreEqual(roles, roleClaims);
}
else
{
Assert.IsEmpty(roleClaims);
}
}

[TestCase("Endpoint=http://localhost;Port=8080;AccessKey={0};Version=1.0;", "hub", "ws://localhost:8080/client/hubs/hub")]
[TestCase("Endpoint=https://a;AccessKey={0};Version=1.0;", "hub", "wss://a/client/hubs/hub")]
[TestCase("Endpoint=http://a;AccessKey={0};Version=1.0;", "hub", "ws://a/client/hubs/hub")]
public void TestGenerateUriUseSameKidWithSameKey(string connectionString, string hub, string expectedUrl)
{
var serviceClient = new WebPubSubServiceClient(string.Format(connectionString, FakeAccessKey), hub);
var uri1 = serviceClient.GenerateClientAccessUri();
var uri2 = serviceClient.GenerateClientAccessUri();

Assert.AreEqual("localhost:8080", uri1.Authority);
Assert.AreEqual("/client/hubs/hub", uri1.AbsolutePath);
var urlBuilder = new UriBuilder(uri1);
urlBuilder.Query = string.Empty;
Assert.AreEqual(expectedUrl, urlBuilder.Uri.ToString());
var token1 = HttpUtility.ParseQueryString(uri1.Query).Get("access_token");
Assert.NotNull(token1);
var token2 = HttpUtility.ParseQueryString(uri2.Query).Get("access_token");
Expand Down