Browse Source

Update the ClaimsIdentity/ClaimsPrincipal.GetClaim() extension to throw an exception when multiple claims of the same type exist

pull/1958/head
Kévin Chalet 2 years ago
parent
commit
05ab8b7428
  1. 3
      src/OpenIddict.Abstractions/OpenIddictResources.resx
  2. 70
      src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs
  3. 34
      test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs

3
src/OpenIddict.Abstractions/OpenIddictResources.resx

@ -1575,6 +1575,9 @@ To apply post-logout redirection responses, create a class implementing 'IOpenId
<data name="ID0422" xml:space="preserve">
<value>A configuration manager must be attached to the client registration to be able to resolve the server configuration.</value>
</data>
<data name="ID0423" xml:space="preserve">
<value>Multiple claims of the same type are present in the identity or principal.</value>
</data>
<data name="ID2000" xml:space="preserve">
<value>The security token is missing.</value>
</data>

70
src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs

@ -1462,10 +1462,13 @@ public static class OpenIddictExtensions
}
/// <summary>
/// Gets the claim value corresponding to the given type.
/// Gets the unique claim value corresponding to the given type.
/// </summary>
/// <param name="identity">The identity.</param>
/// <param name="type">The type associated with the claim.</param>
/// <exception cref="InvalidOperationException">
/// Multiple claims using the same <paramref name="type"/> are present in the identity.
/// </exception>
/// <returns>The claim value.</returns>
public static string? GetClaim(this ClaimsIdentity identity, string type)
{
@ -1479,14 +1482,43 @@ public static class OpenIddictExtensions
throw new ArgumentException(SR.GetResourceString(SR.ID0184), nameof(type));
}
return identity.FindFirst(type)?.Value;
var claims = identity.FindAll(type);
if (claims is IList<Claim> list)
{
return list switch
{
[Claim claim] => claim.Value,
[ ] => null,
[ .. ] => throw new InvalidOperationException(SR.GetResourceString(SR.ID0423))
};
}
else
{
using var enumerator = claims.GetEnumerator();
while (enumerator.MoveNext())
{
var claim = enumerator.Current;
if (!enumerator.MoveNext())
{
return claim.Value;
}
throw new InvalidOperationException(SR.GetResourceString(SR.ID0423));
}
return null;
}
}
/// <summary>
/// Gets the claim value corresponding to the given type.
/// Gets the unique claim value corresponding to the given type.
/// </summary>
/// <param name="principal">The principal.</param>
/// <param name="type">The type associated with the claim.</param>
/// <exception cref="InvalidOperationException">
/// Multiple claims using the same <paramref name="type"/> are present in the principal.
/// </exception>
/// <returns>The claim value.</returns>
public static string? GetClaim(this ClaimsPrincipal principal, string type)
{
@ -1500,11 +1532,37 @@ public static class OpenIddictExtensions
throw new ArgumentException(SR.GetResourceString(SR.ID0184), nameof(type));
}
return principal.FindFirst(type)?.Value;
var claims = principal.FindAll(type);
if (claims is IList<Claim> list)
{
return list switch
{
[Claim claim] => claim.Value,
[ ] => null,
[ .. ] => throw new InvalidOperationException(SR.GetResourceString(SR.ID0423))
};
}
else
{
using var enumerator = claims.GetEnumerator();
while (enumerator.MoveNext())
{
var claim = enumerator.Current;
if (!enumerator.MoveNext())
{
return claim.Value;
}
throw new InvalidOperationException(SR.GetResourceString(SR.ID0423));
}
return null;
}
}
/// <summary>
/// Gets the claim values corresponding to the given type.
/// Gets all the claim values corresponding to the given type.
/// </summary>
/// <param name="identity">The claims identity.</param>
/// <param name="type">The type associated with the claims.</param>
@ -1525,7 +1583,7 @@ public static class OpenIddictExtensions
}
/// <summary>
/// Gets the claim values corresponding to the given type.
/// Gets all the claim values corresponding to the given type.
/// </summary>
/// <param name="principal">The claims principal.</param>
/// <param name="type">The type associated with the claims.</param>

34
test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs

@ -2412,6 +2412,40 @@ public class OpenIddictExtensionsTests
Assert.Equal("principal", exception.ParamName);
}
[Fact]
public void ClaimsIdentity_GetClaim_ThrowsAnExceptionForDuplicateClaimType()
{
// Arrange
var identity = new ClaimsIdentity();
identity.AddClaim(Claims.Name, "Bob le Bricoleur");
identity.AddClaim(Claims.Name, "Bob le Bricoleur");
// Act and assert
var exception = Assert.Throws<InvalidOperationException>(() =>
{
identity.GetClaim(Claims.Name);
});
Assert.Equal(SR.GetResourceString(SR.ID0423), exception.Message);
}
[Fact]
public void ClaimsPrincipal_GetClaim_ThrowsAnExceptionForDuplicateClaimType()
{
// Arrange
var principal = new ClaimsPrincipal(new ClaimsIdentity());
principal.AddClaim(Claims.Name, "Bob le Bricoleur");
principal.AddClaim(Claims.Name, "Bob le Bricoleur");
// Act and assert
var exception = Assert.Throws<InvalidOperationException>(() =>
{
principal.GetClaim(Claims.Name);
});
Assert.Equal(SR.GetResourceString(SR.ID0423), exception.Message);
}
[Fact]
public void ClaimsIdentity_GetClaim_ReturnsNullForMissingClaims()
{

Loading…
Cancel
Save