Browse Source

Update OpenIddictMessage.AddParameter() to throw an exception when the parameter already exists and introduce new APIs

pull/926/head
Kévin Chalet 6 years ago
parent
commit
ccaf982aeb
  1. 62
      src/OpenIddict.Abstractions/Primitives/OpenIddictConverter.cs
  2. 102
      src/OpenIddict.Abstractions/Primitives/OpenIddictMessage.cs
  3. 111
      src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs
  4. 26
      src/OpenIddict.Abstractions/Primitives/OpenIddictRequest.cs
  5. 26
      src/OpenIddict.Abstractions/Primitives/OpenIddictResponse.cs
  6. 12
      src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs
  7. 76
      test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictConverterTests.cs
  8. 108
      test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictMessageTests.cs
  9. 53
      test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictParameterTests.cs
  10. 2
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs

62
src/OpenIddict.Abstractions/Primitives/OpenIddictConverter.cs

@ -28,7 +28,9 @@ namespace OpenIddict.Abstractions
throw new ArgumentNullException(nameof(type));
}
return typeof(OpenIddictMessage).IsAssignableFrom(type);
return type == typeof(OpenIddictMessage) ||
type == typeof(OpenIddictRequest) ||
type == typeof(OpenIddictResponse);
}
/// <summary>
@ -45,41 +47,12 @@ namespace OpenIddict.Abstractions
throw new ArgumentNullException(nameof(type));
}
// Note: OpenIddict primitives are always represented as JSON objects.
var payload = JsonSerializer.Deserialize<JsonElement>(ref reader, options);
if (payload.ValueKind != JsonValueKind.Object)
{
throw new JsonException("An error occurred while reading the JSON payload.");
}
using var document = JsonDocument.ParseValue(ref reader);
OpenIddictMessage message;
if (type == typeof(OpenIddictMessage))
{
message = new OpenIddictMessage();
}
else if (type == typeof(OpenIddictRequest))
{
message = new OpenIddictRequest();
}
else if (type == typeof(OpenIddictResponse))
{
message = new OpenIddictResponse();
}
else
{
throw new ArgumentException("The specified type is not supported.", nameof(type));
}
foreach (var parameter in payload.EnumerateObject())
{
message.AddParameter(parameter.Name, parameter.Value);
}
return message;
return type == typeof(OpenIddictMessage) ? new OpenIddictMessage(document.RootElement.Clone()) :
type == typeof(OpenIddictRequest) ? (OpenIddictMessage) new OpenIddictRequest(document.RootElement.Clone()) :
type == typeof(OpenIddictResponse) ? new OpenIddictResponse(document.RootElement.Clone()) :
throw new ArgumentException("The specified type is not supported.", nameof(type));
}
/// <summary>
@ -100,24 +73,7 @@ namespace OpenIddict.Abstractions
throw new ArgumentNullException(nameof(value));
}
writer.WriteStartObject();
foreach (var parameter in value.GetParameters())
{
writer.WritePropertyName(parameter.Key);
var token = (JsonElement?) parameter.Value;
if (token == null)
{
writer.WriteNullValue();
continue;
}
token.Value.WriteTo(writer);
}
writer.WriteEndObject();
value.WriteTo(writer);
}
}
}

102
src/OpenIddict.Abstractions/Primitives/OpenIddictMessage.cs

@ -8,6 +8,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
@ -32,27 +33,31 @@ namespace OpenIddict.Abstractions
/// <summary>
/// Initializes a new OpenIddict message.
/// </summary>
public OpenIddictMessage() { }
public OpenIddictMessage()
{
}
/// <summary>
/// Initializes a new OpenIddict message.
/// </summary>
/// <param name="parameters">The message parameters.</param>
public OpenIddictMessage([NotNull] IEnumerable<KeyValuePair<string, JsonElement>> parameters)
public OpenIddictMessage(JsonElement parameters)
{
if (parameters == null)
if (parameters.ValueKind != JsonValueKind.Object)
{
throw new ArgumentNullException(nameof(parameters));
throw new ArgumentException("The specified JSON element is not an object.", nameof(parameters));
}
foreach (var parameter in parameters)
foreach (var parameter in parameters.EnumerateObject())
{
if (string.IsNullOrEmpty(parameter.Key))
// While generally discouraged, JSON objects can contain multiple properties with
// the same name. In this case, the last occurrence replaces the previous ones.
if (HasParameter(parameter.Name))
{
continue;
RemoveParameter(parameter.Name);
}
AddParameter(parameter.Key, parameter.Value);
AddParameter(parameter.Name, parameter.Value);
}
}
@ -69,11 +74,6 @@ namespace OpenIddict.Abstractions
foreach (var parameter in parameters)
{
if (string.IsNullOrEmpty(parameter.Key))
{
continue;
}
AddParameter(parameter.Key, parameter.Value);
}
}
@ -89,14 +89,9 @@ namespace OpenIddict.Abstractions
throw new ArgumentNullException(nameof(parameters));
}
foreach (var parameter in parameters)
foreach (var parameter in parameters.GroupBy(parameter => parameter.Key))
{
if (string.IsNullOrEmpty(parameter.Key))
{
continue;
}
AddParameter(parameter.Key, parameter.Value);
AddParameter(parameter.Key, parameter.Select(parameter => parameter.Value).ToArray());
}
}
@ -113,11 +108,6 @@ namespace OpenIddict.Abstractions
foreach (var parameter in parameters)
{
if (string.IsNullOrEmpty(parameter.Key))
{
continue;
}
// Note: the core OAuth 2.0 specification requires that request parameters
// not be present more than once but derived specifications like the
// token exchange RFC deliberately allow specifying multiple resource
@ -126,8 +116,8 @@ namespace OpenIddict.Abstractions
{
null => default,
0 => default,
1 => new OpenIddictParameter(parameter.Value[0]),
_ => new OpenIddictParameter(parameter.Value)
1 => parameter.Value[0],
_ => parameter.Value
});
}
}
@ -145,11 +135,6 @@ namespace OpenIddict.Abstractions
foreach (var parameter in parameters)
{
if (string.IsNullOrEmpty(parameter.Key))
{
continue;
}
// Note: the core OAuth 2.0 specification requires that request parameters
// not be present more than once but derived specifications like the
// token exchange RFC deliberately allow specifying multiple resource
@ -157,8 +142,8 @@ namespace OpenIddict.Abstractions
AddParameter(parameter.Key, parameter.Value.Count switch
{
0 => default,
1 => new OpenIddictParameter(parameter.Value[0]),
_ => new OpenIddictParameter(parameter.Value.ToArray())
1 => parameter.Value[0],
_ => parameter.Value.ToArray()
});
}
}
@ -186,8 +171,7 @@ namespace OpenIddict.Abstractions
= new Dictionary<string, OpenIddictParameter>(StringComparer.Ordinal);
/// <summary>
/// Adds a parameter. Note: if a parameter with the
/// same name was already added, this method has no effect.
/// Adds a parameter. Note: an exception is thrown if a parameter with the same name was already added.
/// </summary>
/// <param name="name">The parameter name.</param>
/// <param name="value">The parameter value.</param>
@ -199,11 +183,13 @@ namespace OpenIddict.Abstractions
throw new ArgumentException("The parameter name cannot be null or empty.", nameof(name));
}
if (!Parameters.ContainsKey(name))
if (Parameters.ContainsKey(name))
{
Parameters.Add(name, value);
throw new ArgumentException("A parameter with the same name already exists.", nameof(name));
}
Parameters.Add(name, value);
return this;
}
@ -279,8 +265,7 @@ namespace OpenIddict.Abstractions
throw new ArgumentException("The parameter name cannot be null or empty.", nameof(name));
}
// If the parameter value is null or empty,
// remove the corresponding entry from the collection.
// If the parameter value is null or empty, remove the corresponding entry from the collection.
if (value == null || OpenIddictParameter.IsNullOrEmpty(value.GetValueOrDefault()))
{
Parameters.Remove(name);
@ -343,22 +328,11 @@ namespace OpenIddict.Abstractions
case OpenIddictConstants.Parameters.Password:
case OpenIddictConstants.Parameters.RefreshToken:
case OpenIddictConstants.Parameters.Token:
{
writer.WriteStringValue("[removed for security reasons]");
writer.WriteStringValue("[redacted]");
continue;
}
}
var token = (JsonElement?) parameter.Value;
if (token == null)
{
writer.WriteNullValue();
continue;
}
token.Value.WriteTo(writer);
parameter.Value.WriteTo(writer);
}
writer.WriteEndObject();
@ -366,5 +340,27 @@ namespace OpenIddict.Abstractions
return Encoding.UTF8.GetString(stream.ToArray());
}
/// <summary>
/// Writes the message to the specified JSON writer.
/// </summary>
/// <param name="writer">The UTF-8 JSON writer.</param>
public void WriteTo(Utf8JsonWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
writer.WriteStartObject();
foreach (var parameter in Parameters)
{
writer.WritePropertyName(parameter.Key);
parameter.Value.WriteTo(writer);
}
writer.WriteEndObject();
}
}
}

111
src/OpenIddict.Abstractions/Primitives/OpenIddictParameter.cs

@ -22,57 +22,43 @@ namespace OpenIddict.Abstractions
public readonly struct OpenIddictParameter : IEquatable<OpenIddictParameter>
{
/// <summary>
/// Initializes a new OpenID Connect
/// parameter using the specified value.
/// Initializes a new OpenID Connect parameter using the specified value.
/// </summary>
/// <param name="value">The parameter value.</param>
public OpenIddictParameter(bool value) => Value = value;
/// <summary>
/// Initializes a new OpenID Connect
/// parameter using the specified value.
/// Initializes a new OpenID Connect parameter using the specified value.
/// </summary>
/// <param name="value">The parameter value.</param>
public OpenIddictParameter(bool? value) => Value = value;
/// <summary>
/// Initializes a new OpenID Connect
/// parameter using the specified value.
/// Initializes a new OpenID Connect parameter using the specified value.
/// </summary>
/// <param name="value">The parameter value.</param>
public OpenIddictParameter(JsonElement value) => Value = value;
/// <summary>
/// Initializes a new OpenID Connect
/// parameter using the specified value.
/// </summary>
/// <param name="value">The parameter value.</param>
public OpenIddictParameter(JsonElement? value) => Value = value;
/// <summary>
/// Initializes a new OpenID Connect
/// parameter using the specified value.
/// Initializes a new OpenID Connect parameter using the specified value.
/// </summary>
/// <param name="value">The parameter value.</param>
public OpenIddictParameter(long value) => Value = value;
/// <summary>
/// Initializes a new OpenID Connect
/// parameter using the specified value.
/// Initializes a new OpenID Connect parameter using the specified value.
/// </summary>
/// <param name="value">The parameter value.</param>
public OpenIddictParameter(long? value) => Value = value;
/// <summary>
/// Initializes a new OpenID Connect
/// parameter using the specified value.
/// Initializes a new OpenID Connect parameter using the specified value.
/// </summary>
/// <param name="value">The parameter value.</param>
public OpenIddictParameter(string value) => Value = value;
/// <summary>
/// Initializes a new OpenID Connect
/// parameter using the specified value.
/// Initializes a new OpenID Connect parameter using the specified value.
/// </summary>
/// <param name="value">The parameter value.</param>
public OpenIddictParameter(string[] value) => Value = value;
@ -448,6 +434,56 @@ namespace OpenIddict.Abstractions
return false;
}
/// <summary>
/// Writes the parameter value to the specified JSON writer.
/// </summary>
/// <param name="writer">The UTF-8 JSON writer.</param>
public void WriteTo(Utf8JsonWriter writer)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
switch (Value)
{
// Note: undefined JsonElement values are assimilated to null values.
case null:
case JsonElement value when value.ValueKind == JsonValueKind.Undefined:
writer.WriteNullValue();
break;
case bool value:
writer.WriteBooleanValue(value);
break;
case long value:
writer.WriteNumberValue(value);
break;
case string value:
writer.WriteStringValue(value);
break;
case string[] value:
writer.WriteStartArray();
for (var index = 0; index < value.Length; index++)
{
writer.WriteStringValue(value[index]);
}
writer.WriteEndArray();
break;
case JsonElement value:
value.WriteTo(writer);
break;
default: throw new InvalidOperationException("The type of the parameter value is not supported.");
}
}
/// <summary>
/// Determines whether two <see cref="OpenIddictParameter"/> instances are equal.
/// </summary>
@ -510,40 +546,31 @@ namespace OpenIddict.Abstractions
/// <param name="parameter">The parameter to convert.</param>
/// <returns>The converted value.</returns>
public static explicit operator JsonElement(OpenIddictParameter? parameter)
=> ((JsonElement?) parameter).GetValueOrDefault();
/// <summary>
/// Converts an <see cref="OpenIddictParameter"/> instance to a nullale <see cref="JsonElement"/>.
/// </summary>
/// <param name="parameter">The parameter to convert.</param>
/// <returns>The converted value.</returns>
public static explicit operator JsonElement?(OpenIddictParameter? parameter)
{
return parameter?.Value switch
{
// When the parameter is a null value, return null.
null => null,
// When the parameter is a JsonElement representing null, return null.
JsonElement value when value.ValueKind == JsonValueKind.Undefined => null,
JsonElement value when value.ValueKind == JsonValueKind.Null => null,
// When the parameter is a null value, return default.
null => default,
// When the parameter is already a JsonElement, return it as-is.
JsonElement value => value,
// When the parameter is a string, try to derialize it to get a JsonElement instance.
string value => DeserializeElement(value) ?? DeserializeElement(JsonSerializer.Serialize(value, new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = false
})),
// When the parameter is a string starting with '{' or '[' (which would correspond
// to a JSON object or array), try to deserialize it to get a JsonElement instance.
string value when value.Length != 0 && (value[0] == '{' || value[0] == '[') =>
DeserializeElement(value) ??
DeserializeElement(JsonSerializer.Serialize(value, new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = false
})) ?? default,
// Otherwise, serialize it to get a JsonElement instance.
var value => DeserializeElement(JsonSerializer.Serialize(value, new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = false
}))
})) ?? default
};
static JsonElement? DeserializeElement(string value)

26
src/OpenIddict.Abstractions/Primitives/OpenIddictRequest.cs

@ -29,42 +29,54 @@ namespace OpenIddict.Abstractions
/// Initializes a new OpenIddict request.
/// </summary>
public OpenIddictRequest()
: base() { }
: base()
{
}
/// <summary>
/// Initializes a new OpenIddict request.
/// </summary>
/// <param name="parameters">The request parameters.</param>
public OpenIddictRequest([NotNull] IEnumerable<KeyValuePair<string, JsonElement>> parameters)
: base(parameters) { }
public OpenIddictRequest(JsonElement parameters)
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict request.
/// </summary>
/// <param name="parameters">The request parameters.</param>
public OpenIddictRequest([NotNull] IEnumerable<KeyValuePair<string, OpenIddictParameter>> parameters)
: base(parameters) { }
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict request.
/// </summary>
/// <param name="parameters">The request parameters.</param>
public OpenIddictRequest([NotNull] IEnumerable<KeyValuePair<string, string>> parameters)
: base(parameters) { }
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict request.
/// </summary>
/// <param name="parameters">The request parameters.</param>
public OpenIddictRequest([NotNull] IEnumerable<KeyValuePair<string, string[]>> parameters)
: base(parameters) { }
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict request.
/// </summary>
/// <param name="parameters">The request parameters.</param>
public OpenIddictRequest([NotNull] IEnumerable<KeyValuePair<string, StringValues>> parameters)
: base(parameters) { }
: base(parameters)
{
}
/// <summary>
/// Gets or sets the "access_token" parameter.

26
src/OpenIddict.Abstractions/Primitives/OpenIddictResponse.cs

@ -29,42 +29,54 @@ namespace OpenIddict.Abstractions
/// Initializes a new OpenIddict response.
/// </summary>
public OpenIddictResponse()
: base() { }
: base()
{
}
/// <summary>
/// Initializes a new OpenIddict response.
/// </summary>
/// <param name="parameters">The response parameters.</param>
public OpenIddictResponse([NotNull] IEnumerable<KeyValuePair<string, JsonElement>> parameters)
: base(parameters) { }
public OpenIddictResponse(JsonElement parameters)
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict response.
/// </summary>
/// <param name="parameters">The response parameters.</param>
public OpenIddictResponse([NotNull] IEnumerable<KeyValuePair<string, OpenIddictParameter>> parameters)
: base(parameters) { }
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict response.
/// </summary>
/// <param name="parameters">The response parameters.</param>
public OpenIddictResponse([NotNull] IEnumerable<KeyValuePair<string, string>> parameters)
: base(parameters) { }
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict response.
/// </summary>
/// <param name="parameters">The response parameters.</param>
public OpenIddictResponse([NotNull] IEnumerable<KeyValuePair<string, string[]>> parameters)
: base(parameters) { }
: base(parameters)
{
}
/// <summary>
/// Initializes a new OpenIddict response.
/// </summary>
/// <param name="parameters">The response parameters.</param>
public OpenIddictResponse([NotNull] IEnumerable<KeyValuePair<string, StringValues>> parameters)
: base(parameters) { }
: base(parameters)
{
}
/// <summary>
/// Gets or sets the "access_token" parameter.

12
src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs

@ -1168,7 +1168,7 @@ namespace OpenIddict.Server
// When multiple claims share the same type, retrieve the underlying
// JSON values and add everything to a new unique JSON array.
_ => JsonSerializer.Deserialize<JsonElement>(JsonSerializer.Serialize(
_ => DeserializeElement(JsonSerializer.Serialize(
claims.Select(claim => ConvertToParameter(claim).Value), new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
@ -1185,11 +1185,17 @@ namespace OpenIddict.Server
ClaimValueTypes.Integer32 => int.Parse(claim.Value, CultureInfo.InvariantCulture),
ClaimValueTypes.Integer64 => long.Parse(claim.Value, CultureInfo.InvariantCulture),
JsonClaimValueTypes.Json => JsonSerializer.Deserialize<JsonElement>(claim.Value),
JsonClaimValueTypes.JsonArray => JsonSerializer.Deserialize<JsonElement>(claim.Value),
JsonClaimValueTypes.Json => DeserializeElement(claim.Value),
JsonClaimValueTypes.JsonArray => DeserializeElement(claim.Value),
_ => new OpenIddictParameter(claim.Value)
};
static JsonElement DeserializeElement(string value)
{
using var document = JsonDocument.Parse(value);
return document.RootElement.Clone();
}
}
}

76
test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictConverterTests.cs

@ -6,7 +6,6 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using Xunit;
@ -56,31 +55,15 @@ namespace OpenIddict.Abstractions.Tests.Primitives
var converter = new OpenIddictConverter();
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(() =>
var exception = Assert.Throws<ArgumentNullException>(delegate
{
var reader = new Utf8JsonReader();
converter.Read(ref reader, type: null, options: null);
return converter.Read(ref reader, type: null, options: null);
});
Assert.Equal("type", exception.ParamName);
}
[Fact]
public void Read_ThrowsAnExceptionForUnexpectedJsonToken()
{
// Arrange
var converter = new OpenIddictConverter();
// Act and assert
var exception = Assert.Throws<JsonException>(() =>
{
var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes("[0,1,2,3]"));
converter.Read(ref reader, type: typeof(OpenIddictRequest), options: null);
});
Assert.Equal("An error occurred while reading the JSON payload.", exception.Message);
}
[Theory]
[InlineData(typeof(OpenIddictMessage[]))]
[InlineData(typeof(OpenIddictRequest[]))]
@ -97,10 +80,10 @@ namespace OpenIddict.Abstractions.Tests.Primitives
var converter = new OpenIddictConverter();
// Act and assert
var exception = Assert.Throws<ArgumentException>(() =>
var exception = Assert.Throws<ArgumentException>(delegate
{
var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(@"{""name"":""value""}"));
converter.Read(ref reader, type, options: null);
return converter.Read(ref reader, type, options: null);
});
Assert.StartsWith("The specified type is not supported.", exception.Message);
@ -118,11 +101,11 @@ namespace OpenIddict.Abstractions.Tests.Primitives
var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(@"{""name"":""value""}"));
// Act
var result = converter.Read(ref reader, type, options: null);
var message = converter.Read(ref reader, type, options: null);
// Assert
Assert.IsType(type, result);
Assert.Equal("value", (string) result.GetParameter("name"));
Assert.IsType(type, message);
Assert.Equal("value", (string) message.GetParameter("name"));
}
[Fact]
@ -134,20 +117,20 @@ namespace OpenIddict.Abstractions.Tests.Primitives
@"{""string"":null,""bool"":null,""long"":null,""array"":null,""object"":null}"));
// Act
var result = converter.Read(ref reader, typeof(OpenIddictMessage), options: null);
var message = converter.Read(ref reader, typeof(OpenIddictMessage), options: null);
// Assert
Assert.Equal(5, result.GetParameters().Count());
Assert.NotNull(result.GetParameter("string"));
Assert.NotNull(result.GetParameter("bool"));
Assert.NotNull(result.GetParameter("long"));
Assert.NotNull(result.GetParameter("array"));
Assert.NotNull(result.GetParameter("object"));
Assert.Null((string) result.GetParameter("string"));
Assert.Null((bool?) result.GetParameter("bool"));
Assert.Null((long?) result.GetParameter("long"));
Assert.Null((JsonElement?) result.GetParameter("array"));
Assert.Null((JsonElement?) result.GetParameter("object"));
Assert.Equal(5, message.Count);
Assert.NotNull(message.GetParameter("string"));
Assert.NotNull(message.GetParameter("bool"));
Assert.NotNull(message.GetParameter("long"));
Assert.NotNull(message.GetParameter("array"));
Assert.NotNull(message.GetParameter("object"));
Assert.Null((string) message.GetParameter("string"));
Assert.Null((bool?) message.GetParameter("bool"));
Assert.Null((long?) message.GetParameter("long"));
Assert.Equal(JsonValueKind.Null, ((JsonElement) message.GetParameter("array")).ValueKind);
Assert.Equal(JsonValueKind.Null, ((JsonElement) message.GetParameter("object")).ValueKind);
}
[Fact]
@ -158,16 +141,16 @@ namespace OpenIddict.Abstractions.Tests.Primitives
var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(@"{""string"":"""",""array"":[],""object"":{}}"));
// Act
var result = converter.Read(ref reader, typeof(OpenIddictMessage), options: null);
var message = converter.Read(ref reader, typeof(OpenIddictMessage), options: null);
// Assert
Assert.Equal(3, result.GetParameters().Count());
Assert.NotNull(result.GetParameter("string"));
Assert.NotNull(result.GetParameter("array"));
Assert.NotNull(result.GetParameter("object"));
Assert.Empty((string) result.GetParameter("string"));
Assert.NotNull((JsonElement?) result.GetParameter("array"));
Assert.NotNull((JsonElement?) result.GetParameter("object"));
Assert.Equal(3, message.Count);
Assert.NotNull(message.GetParameter("string"));
Assert.NotNull(message.GetParameter("array"));
Assert.NotNull(message.GetParameter("object"));
Assert.Empty((string) message.GetParameter("string"));
Assert.NotNull((JsonElement?) message.GetParameter("array"));
Assert.NotNull((JsonElement?) message.GetParameter("object"));
}
[Fact]
@ -232,8 +215,7 @@ namespace OpenIddict.Abstractions.Tests.Primitives
message.AddParameter("string", new OpenIddictParameter((string) null));
message.AddParameter("bool", new OpenIddictParameter((bool?) null));
message.AddParameter("long", new OpenIddictParameter((long?) null));
message.AddParameter("array", new OpenIddictParameter((JsonElement?) null));
message.AddParameter("object", new OpenIddictParameter((JsonElement?) null));
message.AddParameter("node", new OpenIddictParameter(default(JsonElement)));
// Act
converter.Write(writer, value: message, options: null);
@ -241,7 +223,7 @@ namespace OpenIddict.Abstractions.Tests.Primitives
// Assert
writer.Flush();
stream.Seek(0L, SeekOrigin.Begin);
Assert.Equal(@"{""string"":null,""bool"":null,""long"":null,""array"":null,""object"":null}", reader.ReadToEnd());
Assert.Equal(@"{""string"":null,""bool"":null,""long"":null,""node"":null}", reader.ReadToEnd());
}
[Fact]

108
test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictMessageTests.cs

@ -6,7 +6,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using Xunit;
@ -15,31 +16,65 @@ namespace OpenIddict.Abstractions.Tests.Primitives
{
public class OpenIddictMessageTests
{
[Theory]
[InlineData(null)]
[InlineData("")]
public void Constructor_ThrowsAnExceptionForNullOrEmptyParameterNames(string name)
{
// Arrange, act and assert
var exception = Assert.Throws<ArgumentException>(delegate
{
return new OpenIddictMessage(new[]
{
new KeyValuePair<string, OpenIddictParameter>(name, "Fabrikam")
});
});
Assert.Equal("name", exception.ParamName);
Assert.StartsWith("The parameter name cannot be null or empty.", exception.Message);
}
[Fact]
public void Constructor_ImportsParameters()
public void Constructor_ThrowsAnExceptionForInvalidJsonElement()
{
// Arrange and act
var message = new OpenIddictMessage(new[]
// Arrange, act and assert
var exception = Assert.Throws<ArgumentException>(delegate
{
new KeyValuePair<string, OpenIddictParameter>("parameter", 42)
return new OpenIddictMessage(JsonSerializer.Deserialize<JsonElement>("[0,1,2,3]"));
});
// Assert
Assert.Equal(42, (long) message.GetParameter("parameter"));
Assert.Equal("parameters", exception.ParamName);
Assert.StartsWith("The specified JSON element is not an object.", exception.Message);
}
[Fact]
public void Constructor_ThrowsAnExceptionForDuplicateParameters()
{
// Arrange, act and assert
var exception = Assert.Throws<ArgumentException>(delegate
{
return new OpenIddictMessage(new[]
{
new KeyValuePair<string, OpenIddictParameter>("parameter", "Fabrikam"),
new KeyValuePair<string, OpenIddictParameter>("parameter", "Contoso")
});
});
Assert.Equal("name", exception.ParamName);
Assert.StartsWith("A parameter with the same name already exists.", exception.Message);
}
[Fact]
public void Constructor_IgnoresNamelessParameters()
public void Constructor_ImportsParameters()
{
// Arrange and act
var message = new OpenIddictMessage(new[]
{
new KeyValuePair<string, OpenIddictParameter>(null, new OpenIddictParameter()),
new KeyValuePair<string, OpenIddictParameter>(string.Empty, new OpenIddictParameter())
new KeyValuePair<string, OpenIddictParameter>("parameter", 42)
});
// Assert
Assert.Empty(message.GetParameters());
Assert.Equal(42, (long) message.GetParameter("parameter"));
}
[Fact]
@ -53,22 +88,22 @@ namespace OpenIddict.Abstractions.Tests.Primitives
});
// Assert
Assert.Equal(2, message.GetParameters().Count());
Assert.Equal(2, message.Count);
}
[Fact]
public void Constructor_IgnoresDuplicateParameters()
public void Constructor_AllowsDuplicateParameters()
{
// Arrange and act
var message = new OpenIddictMessage(new[]
{
new KeyValuePair<string, OpenIddictParameter>("parameter", "Fabrikam"),
new KeyValuePair<string, OpenIddictParameter>("parameter", "Contoso")
new KeyValuePair<string, string>("parameter", "Fabrikam"),
new KeyValuePair<string, string>("parameter", "Contoso")
});
// Assert
Assert.Single(message.GetParameters());
Assert.Equal("Fabrikam", message.GetParameter("parameter"));
Assert.Equal(1, message.Count);
Assert.Equal(new[] { "Fabrikam", "Contoso" }, (string[]) message.GetParameter("parameter"));
}
[Fact]
@ -81,7 +116,7 @@ namespace OpenIddict.Abstractions.Tests.Primitives
});
// Assert
Assert.Single(message.GetParameters());
Assert.Equal(1, message.Count);
Assert.Equal(new[] { "Fabrikam", "Contoso" }, (string[]) message.GetParameter("parameter"));
}
@ -95,7 +130,7 @@ namespace OpenIddict.Abstractions.Tests.Primitives
});
// Assert
Assert.Single(message.GetParameters());
Assert.Equal(1, message.Count);
Assert.Equal("Fabrikam", message.GetParameter("parameter")?.Value);
}
@ -444,7 +479,40 @@ namespace OpenIddict.Abstractions.Tests.Primitives
// Act and assert
var element = JsonSerializer.Deserialize<JsonElement>(message.ToString());
Assert.DoesNotContain("secret value", message.ToString());
Assert.Equal("[removed for security reasons]", element.GetProperty(parameter).GetString());
Assert.Equal("[redacted]", element.GetProperty(parameter).GetString());
}
[Fact]
public void WriteTo_ThrowsAnExceptionForNullWriter()
{
// Arrange
var message = new OpenIddictMessage();
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(() => message.WriteTo(writer: null));
Assert.Equal("writer", exception.ParamName);
}
[Fact]
public void WriteTo_WritesUtf8JsonRepresentation()
{
// Arrange
var message = new OpenIddictMessage
{
["redirect_uris"] = new[] { "https://abc.org/callback" },
["client_name"] = "My Example Client"
};
using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream);
// Act
message.WriteTo(writer);
writer.Flush();
// Assert
Assert.Equal(@"{""redirect_uris"":[""https://abc.org/callback""],""client_name"":""My Example Client""}",
Encoding.UTF8.GetString(stream.ToArray()));
}
}
}

53
test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictParameterTests.cs

@ -6,7 +6,10 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using Xunit;
@ -111,13 +114,13 @@ namespace OpenIddict.Abstractions.Tests.Primitives
}
[Fact]
public void Equals_SupportsNullJsonValues()
public void Equals_SupportsUndefinedJsonValues()
{
// Arrange
var parameter = new OpenIddictParameter(42);
// Act and assert
Assert.False(parameter.Equals(new OpenIddictParameter((JsonElement?) null)));
Assert.False(parameter.Equals(new OpenIddictParameter(default(JsonElement))));
}
[Fact]
@ -424,7 +427,13 @@ namespace OpenIddict.Abstractions.Tests.Primitives
Assert.True(OpenIddictParameter.IsNullOrEmpty(new OpenIddictParameter((long?) null)));
Assert.True(OpenIddictParameter.IsNullOrEmpty(new OpenIddictParameter((string) null)));
Assert.True(OpenIddictParameter.IsNullOrEmpty(new OpenIddictParameter((string[]) null)));
Assert.True(OpenIddictParameter.IsNullOrEmpty(new OpenIddictParameter((JsonElement?) null)));
}
[Fact]
public void IsNullOrEmpty_ReturnsTrueForUndefinedValues()
{
// Arrange, act and assert
Assert.True(OpenIddictParameter.IsNullOrEmpty(new OpenIddictParameter(default(JsonElement))));
}
[Fact]
@ -567,6 +576,38 @@ namespace OpenIddict.Abstractions.Tests.Primitives
Assert.Null(value.Value);
}
[Fact]
public void WriteTo_ThrowsAnExceptionForNullWriter()
{
// Arrange
var parameter = new OpenIddictParameter();
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(() => parameter.WriteTo(writer: null));
Assert.Equal("writer", exception.ParamName);
}
[Fact]
public void WriteTo_WritesUtf8JsonRepresentation()
{
// Arrange
var parameter = new OpenIddictParameter(JsonSerializer.Deserialize<JsonElement>(@"{
""redirect_uris"": [""https://abc.org/callback""],
""client_name"":""My Example Client""
}"));
using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream);
// Act
parameter.WriteTo(writer);
writer.Flush();
// Assert
Assert.Equal(@"{""redirect_uris"":[""https://abc.org/callback""],""client_name"":""My Example Client""}",
Encoding.UTF8.GetString(stream.ToArray()));
}
[Fact]
public void BoolConverter_CanCreateParameterFromBooleanValue()
{
@ -655,15 +696,15 @@ namespace OpenIddict.Abstractions.Tests.Primitives
public void JsonElementConverter_ReturnsDefaultValueForNullValues()
{
// Arrange, act and assert
Assert.Null((JsonElement?) new OpenIddictParameter());
Assert.Null((JsonElement?) (OpenIddictParameter?) null);
Assert.Equal(JsonValueKind.Undefined, ((JsonElement) new OpenIddictParameter()).ValueKind);
Assert.Equal(JsonValueKind.Undefined, ((JsonElement) (OpenIddictParameter?) null).ValueKind);
}
[Fact]
public void JsonElementConverter_ReturnsDefaultValueForUnsupportedJsonValues()
{
// Arrange, act and assert
Assert.Null((JsonElement?) new OpenIddictParameter(new JsonElement()));
Assert.Equal(JsonValueKind.Undefined, ((JsonElement) new OpenIddictParameter(new JsonElement())).ValueKind);
}
[Fact]

2
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Introspection.cs

@ -718,7 +718,7 @@ namespace OpenIddict.Server.FunctionalTests
});
// Assert
Assert.Equal(11, response.GetParameters().Count());
Assert.Equal(11, response.Count);
Assert.True((bool) response[Claims.Active]);
Assert.Equal("66B65AED-4033-4E9C-B975-A8CA7FB6FA79", (string) response[Claims.JwtId]);
Assert.Equal(TokenTypes.Bearer, (string) response[Claims.TokenType]);

Loading…
Cancel
Save