Skip to content

Commit

Permalink
Add support for IList<string>, ICollection<string>
Browse files Browse the repository at this point in the history
  • Loading branch information
Brent Schmaltz authored and brentschmaltz committed Sep 7, 2023
1 parent f602540 commit 76aa676
Show file tree
Hide file tree
Showing 3 changed files with 335 additions and 2 deletions.
34 changes: 34 additions & 0 deletions src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonClaimSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -241,20 +241,54 @@ internal T GetValue<T>(string key, bool throwEx, out bool found)
if (objType == typeof(string))
return (T)(object)new string[] { (string)obj };

if (objType == typeof(DateTime))
return (T)(object)new string[] { ((DateTime)obj).ToString("o", CultureInfo.InvariantCulture) };

return (T)(object)new string[] { obj.ToString() };
}
else if (typeof(T) == typeof(List<string>))
{
if (objType == typeof(string))
return (T)(object)new List<string> { (string)obj };

if (objType == typeof(DateTime))
return (T)(object)new List<string> { ((DateTime)obj).ToString("o", CultureInfo.InvariantCulture) };

return (T)(object)new List<string> { obj.ToString() };
}
else if (typeof(T) == typeof(Collection<string>))
{
if (objType == typeof(string))
return (T)(object)new Collection<string> { (string)obj };

if (objType == typeof(DateTime))
return (T)(object)new Collection<string> { ((DateTime)obj).ToString("o", CultureInfo.InvariantCulture) };

return (T)(object)new Collection<string> { obj.ToString() };
}
// we could have added an OR condition to List<string>
// but we have set an order of preference for the return types: Collection<string> is preferred over IList<string>
else if (typeof(T) == typeof(IList<string>))
{
if (objType == typeof(string))
return (T)(object)new List<string> { (string)obj };

if (objType == typeof(DateTime))
return (T)(object)new List<string> { ((DateTime)obj).ToString("o", CultureInfo.InvariantCulture) };

return (T)(object)new List<string> { obj.ToString() };
}
// we could have added an OR condition to Collection<string>
// but we have set an order of preference for the return types:
// string[], List<string>, Collection<string>, IList<string>, ICollection<string>
else if (typeof(T) == typeof(ICollection<string>))
{
if (objType == typeof(string))
return (T)(object)new Collection<string> { (string)obj };

if (objType == typeof(DateTime))
return (T)(object)new Collection<string> { ((DateTime)obj).ToString("o", CultureInfo.InvariantCulture) };

return (T)(object)new Collection<string> { obj.ToString() };
}
else if (typeof(T) == typeof(object[]))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,35 @@ public static bool TryCreateTypeFromJsonElement<T>(JsonElement jsonElement, out
t = (T)(object)items;
return true;
}
// we could have added an OR condition to List<string>
// but we have set an order of preference for the return types: Collection<string> is preferred over IList<string>
else if (typeof(T) == typeof(IList<string>))
{
List<string> items = new();
foreach (JsonElement j in jsonElement.EnumerateArray())
if (j.ValueKind == JsonValueKind.String)
items.Add(j.GetString());
else
items.Add(j.GetRawText());

t = (T)(object)items;
return true;
}
// we could have added an OR condition to Collection<string>
// but we have set an order of preference for the return types:
// string[], List<string>, Collection<string>, IList<string>, ICollection<string>
else if (typeof(T) == typeof(ICollection<string>))
{
Collection<string> items = new();
foreach (JsonElement j in jsonElement.EnumerateArray())
if (j.ValueKind == JsonValueKind.String)
items.Add(j.GetString());
else
items.Add(j.GetRawText());

t = (T)(object)items;
return true;
}
else if (typeof(T) == typeof(object[]))
{
int numItems = 0;
Expand Down
274 changes: 272 additions & 2 deletions test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ public void CompareJwtSecurityTokenWithJsonSecurityTokenMultipleAudiences()

// This test ensures that TryGetPayloadValue does not throw
// No need to check for equal as GetPayloadValue does that
[Theory, MemberData(nameof(GetPayloadValueTheoryData))]
[Theory, MemberData(nameof(GetPayloadValueTheoryData), DisableDiscoveryEnumeration = true)]
public void TryGetPayloadValue(GetPayloadValueTheoryData theoryData)
{
CompareContext context = TestUtilities.WriteHeader($"{this}.TryGetPayloadValue", theoryData);
Expand All @@ -264,7 +264,7 @@ public void TryGetPayloadValue(GetPayloadValueTheoryData theoryData)
}

// This test ensures that our roundtripping works as expected.
[Theory, MemberData(nameof(GetPayloadValueTheoryData))]
[Theory, MemberData(nameof(GetPayloadValueTheoryData), DisableDiscoveryEnumeration = true)]
public void GetPayloadValue(GetPayloadValueTheoryData theoryData)
{
CompareContext context = TestUtilities.WriteHeader($"{this}.GetPayloadValue", theoryData);
Expand Down Expand Up @@ -484,6 +484,260 @@ public static TheoryData<GetPayloadValueTheoryData> GetPayloadValueTheoryData
});
#endregion

#region collection of strings form simple types

#region string[]
theoryData.Add(new GetPayloadValueTheoryData("string[]dateTime")
{
PropertyName = "dateTime",
PropertyType = typeof(string[]),
PropertyValue = new string[] {dateTime.ToString("o", CultureInfo.InvariantCulture)},
Json = JsonUtilities.CreateUnsignedToken("dateTime", dateTime)
});

theoryData.Add(new GetPayloadValueTheoryData("string[]true")
{
PropertyName = "true",
PropertyType = typeof(string[]),
PropertyValue = new string[] { "True" },
Json = JsonUtilities.CreateUnsignedToken("true", true)
});

theoryData.Add(new GetPayloadValueTheoryData("string[]double")
{
PropertyName = "double",
PropertyType = typeof(string[]),
PropertyValue = new string[] { "422.101" },
Json = JsonUtilities.CreateUnsignedToken("double", 422.101)
});

theoryData.Add(new GetPayloadValueTheoryData("string[]integer")
{
PropertyName = "integer",
PropertyType = typeof(string[]),
PropertyValue = new string[] { "42" },
Json = JsonUtilities.CreateUnsignedToken("integer", 42)
});

theoryData.Add(new GetPayloadValueTheoryData("string[]ulong")
{
PropertyName = "ulong",
PropertyType = typeof(string[]),
PropertyValue = new string[] { "42" },
Json = JsonUtilities.CreateUnsignedToken("ulong", 42)
});

theoryData.Add(new GetPayloadValueTheoryData("string[]string")
{
PropertyName = "string",
PropertyType = typeof(string[]),
PropertyValue = new string[] { "property" },
Json = JsonUtilities.CreateUnsignedToken("string", "property")
});
#endregion

#region List:string
theoryData.Add(new GetPayloadValueTheoryData("List<string>dateTime")
{
PropertyName = "dateTime",
PropertyType = typeof(List<string>),
PropertyValue = new List<string> { dateTime.ToString("o", CultureInfo.InvariantCulture) },
Json = JsonUtilities.CreateUnsignedToken("dateTime", dateTime)
});

theoryData.Add(new GetPayloadValueTheoryData("List<string>true")
{
PropertyName = "true",
PropertyType = typeof(List<string>),
PropertyValue = new List<string> { "True" },
Json = JsonUtilities.CreateUnsignedToken("true", true)
});

theoryData.Add(new GetPayloadValueTheoryData("List<string>double")
{
PropertyName = "double",
PropertyType = typeof(List<string>),
PropertyValue = new List<string> { "422.101" },
Json = JsonUtilities.CreateUnsignedToken("double", 422.101)
});

theoryData.Add(new GetPayloadValueTheoryData("List<string>integer")
{
PropertyName = "integer",
PropertyType = typeof(List<string>),
PropertyValue = new List<string> { "42" },
Json = JsonUtilities.CreateUnsignedToken("integer", 42)
});

theoryData.Add(new GetPayloadValueTheoryData("List<string>ulong")
{
PropertyName = "ulong",
PropertyType = typeof(List<string>),
PropertyValue = new List<string> { "42" },
Json = JsonUtilities.CreateUnsignedToken("ulong", 42)
});

theoryData.Add(new GetPayloadValueTheoryData("List<string>string")
{
PropertyName = "string",
PropertyType = typeof(List<string>),
PropertyValue = new List<string> { "property" },
Json = JsonUtilities.CreateUnsignedToken("string", "property")
});
#endregion

#region Collection:string
theoryData.Add(new GetPayloadValueTheoryData("Collection<string>dateTime")
{
PropertyName = "dateTime",
PropertyType = typeof(Collection<string>),
PropertyValue = new Collection<string> { dateTime.ToString("o", CultureInfo.InvariantCulture) },
Json = JsonUtilities.CreateUnsignedToken("dateTime", dateTime)
});

theoryData.Add(new GetPayloadValueTheoryData("Collection<string>true")
{
PropertyName = "true",
PropertyType = typeof(Collection<string>),
PropertyValue = new Collection<string> { "True" },
Json = JsonUtilities.CreateUnsignedToken("true", true)
});

theoryData.Add(new GetPayloadValueTheoryData("Collection<string>double")
{
PropertyName = "double",
PropertyType = typeof(Collection<string>),
PropertyValue = new Collection<string> { "422.101" },
Json = JsonUtilities.CreateUnsignedToken("double", 422.101)
});

theoryData.Add(new GetPayloadValueTheoryData("Collection<string>integer")
{
PropertyName = "integer",
PropertyType = typeof(Collection<string>),
PropertyValue = new Collection<string> { "42" },
Json = JsonUtilities.CreateUnsignedToken("integer", 42)
});

theoryData.Add(new GetPayloadValueTheoryData("Collection<string>ulong")
{
PropertyName = "ulong",
PropertyType = typeof(Collection<string>),
PropertyValue = new Collection<string> { "42" },
Json = JsonUtilities.CreateUnsignedToken("ulong", 42)
});

theoryData.Add(new GetPayloadValueTheoryData("Collection<string>string")
{
PropertyName = "string",
PropertyType = typeof(Collection<string>),
PropertyValue = new Collection<string> { "property" },
Json = JsonUtilities.CreateUnsignedToken("string", "property")
});
#endregion

#region IList:string
theoryData.Add(new GetPayloadValueTheoryData("IList<string>dateTime")
{
PropertyName = "dateTime",
PropertyType = typeof(IList<string>),
PropertyValue = new List<string> { dateTime.ToString("o", CultureInfo.InvariantCulture) },
Json = JsonUtilities.CreateUnsignedToken("dateTime", dateTime)
});

theoryData.Add(new GetPayloadValueTheoryData("IList<string>true")
{
PropertyName = "true",
PropertyType = typeof(IList<string>),
PropertyValue = new List<string> { "True" },
Json = JsonUtilities.CreateUnsignedToken("true", true)
});

theoryData.Add(new GetPayloadValueTheoryData("IList<string>double")
{
PropertyName = "double",
PropertyType = typeof(IList<string>),
PropertyValue = new List<string> { "422.101" },
Json = JsonUtilities.CreateUnsignedToken("double", 422.101)
});

theoryData.Add(new GetPayloadValueTheoryData("IList<string>integer")
{
PropertyName = "integer",
PropertyType = typeof(IList<string>),
PropertyValue = new List<string> { "42" },
Json = JsonUtilities.CreateUnsignedToken("integer", 42)
});

theoryData.Add(new GetPayloadValueTheoryData("IList<string>ulong")
{
PropertyName = "ulong",
PropertyType = typeof(IList<string>),
PropertyValue = new List<string> { "42" },
Json = JsonUtilities.CreateUnsignedToken("ulong", 42)
});

theoryData.Add(new GetPayloadValueTheoryData("IList<string>string")
{
PropertyName = "string",
PropertyType = typeof(IList<string>),
PropertyValue = new List<string> { "property" },
Json = JsonUtilities.CreateUnsignedToken("string", "property")
});
#endregion

#region ICollection:string
theoryData.Add(new GetPayloadValueTheoryData("ICollection<string>dateTime")
{
PropertyName = "dateTime",
PropertyType = typeof(ICollection<string>),
PropertyValue = new Collection<string> { dateTime.ToString("o", CultureInfo.InvariantCulture) },
Json = JsonUtilities.CreateUnsignedToken("dateTime", dateTime)
});

theoryData.Add(new GetPayloadValueTheoryData("ICollection<string>true")
{
PropertyName = "true",
PropertyType = typeof(ICollection<string>),
PropertyValue = new Collection<string> { "True" },
Json = JsonUtilities.CreateUnsignedToken("true", true)
});

theoryData.Add(new GetPayloadValueTheoryData("ICollection<string>double")
{
PropertyName = "double",
PropertyType = typeof(ICollection<string>),
PropertyValue = new Collection<string> { "422.101" },
Json = JsonUtilities.CreateUnsignedToken("double", 422.101)
});

theoryData.Add(new GetPayloadValueTheoryData("ICollection<string>integer")
{
PropertyName = "integer",
PropertyType = typeof(ICollection<string>),
PropertyValue = new Collection<string> { "42" },
Json = JsonUtilities.CreateUnsignedToken("integer", 42)
});

theoryData.Add(new GetPayloadValueTheoryData("ICollection<string>ulong")
{
PropertyName = "ulong",
PropertyType = typeof(ICollection<string>),
PropertyValue = new Collection<string> { "42" },
Json = JsonUtilities.CreateUnsignedToken("ulong", 42)
});

theoryData.Add(new GetPayloadValueTheoryData("ICollection<string>string")
{
PropertyName = "string",
PropertyType = typeof(ICollection<string>),
PropertyValue = new Collection<string> { "property" },
Json = JsonUtilities.CreateUnsignedToken("string", "property")
});
#endregion

#endregion

#region complex types, dictionary, list, array, collection
List<string> listStrings = new List<string> { "listValue1", "listValue2" };
List<object> listObjects = new List<object> { "listValue1", "listValue2" };
Expand Down Expand Up @@ -559,6 +813,14 @@ public static TheoryData<GetPayloadValueTheoryData> GetPayloadValueTheoryData
Json = JsonUtilities.CreateUnsignedToken("c", listStrings)
});

theoryData.Add(new GetPayloadValueTheoryData("IListOfStrings")
{
PropertyName = "c",
PropertyType = typeof(IList<string>),
PropertyValue = listStrings,
Json = JsonUtilities.CreateUnsignedToken("c", listStrings)
});

theoryData.Add(new GetPayloadValueTheoryData("ListOfObjects")
{
PropertyName = "c",
Expand All @@ -575,6 +837,14 @@ public static TheoryData<GetPayloadValueTheoryData> GetPayloadValueTheoryData
Json = JsonUtilities.CreateUnsignedToken("c", collectionStrings)
});

theoryData.Add(new GetPayloadValueTheoryData("ICollectionOfStrings")
{
PropertyName = "c",
PropertyType = typeof(ICollection<string>),
PropertyValue = collectionStrings,
Json = JsonUtilities.CreateUnsignedToken("c", collectionStrings)
});

theoryData.Add(new GetPayloadValueTheoryData("CollectionOfObjects")
{
PropertyName = "c",
Expand Down

0 comments on commit 76aa676

Please sign in to comment.