do not crash invoice if wellknown metadata keys used with different e… (#2448)

* do not crash invoice if wellknown metadata keys used with different expected types

* fix

* add bits from alt PR
This commit is contained in:
Andrew Camilleri 2021-04-28 09:49:10 +02:00 committed by GitHub
parent 5fe3c1c61f
commit 4e1b18e2bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 131 additions and 65 deletions

View File

@ -2366,7 +2366,9 @@ namespace BTCPayServer.Tests
{
("{ invalidjson file here}",
new Dictionary<string, object>() {{String.Empty, "{ invalidjson file here}"}})
}
},
// Duplicate keys should not crash things
{("{ \"key\": true, \"key\": true}", new Dictionary<string, object>() {{"key", "True"}})}
};
testCases.ForEach(tuple =>

View File

@ -903,21 +903,20 @@ namespace BTCPayServer.Controllers
var jObject = JObject.Parse(posData);
foreach (var item in jObject)
{
switch (item.Value.Type)
{
case JTokenType.Array:
var items = item.Value.AsEnumerable().ToList();
for (var i = 0; i < items.Count; i++)
{
result.Add($"{item.Key}[{i}]", ParsePosData(items[i].ToString()));
result.TryAdd($"{item.Key}[{i}]", ParsePosData(items[i].ToString()));
}
break;
case JTokenType.Object:
result.Add(item.Key, ParsePosData(item.Value.ToString()));
result.TryAdd(item.Key, ParsePosData(item.Value.ToString()));
break;
default:
result.Add(item.Key, item.Value.ToString());
result.TryAdd(item.Key, item.Value.ToString());
break;
}
@ -925,7 +924,7 @@ namespace BTCPayServer.Controllers
}
catch
{
result.Add(string.Empty, posData);
result.TryAdd(string.Empty, posData);
}
return result;
}

View File

@ -5,7 +5,6 @@ using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices.ComTypes;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Configuration;
using McMaster.NETCore.Plugins;
@ -28,7 +27,7 @@ namespace BTCPayServer.Plugins
public static bool IsExceptionByPlugin(Exception exception)
{
return _pluginAssemblies.Any(assembly => assembly.FullName.Contains(exception.Source, StringComparison.OrdinalIgnoreCase));
return _pluginAssemblies.Any(assembly => assembly?.FullName?.Contains(exception.Source!, StringComparison.OrdinalIgnoreCase) is true);
}
public static IMvcBuilder AddPlugins(this IMvcBuilder mvcBuilder, IServiceCollection serviceCollection,
IConfiguration config, ILoggerFactory loggerFactory)

View File

@ -30,70 +30,136 @@ namespace BTCPayServer.Services.Invoices
seria.ContractResolver = new CamelCasePropertyNamesContractResolver();
MetadataSerializer = seria;
}
public string OrderId { get; set; }
[JsonProperty(PropertyName = "buyerName")]
public string BuyerName { get; set; }
[JsonProperty(PropertyName = "buyerEmail")]
public string BuyerEmail { get; set; }
[JsonProperty(PropertyName = "buyerCountry")]
public string BuyerCountry { get; set; }
[JsonProperty(PropertyName = "buyerZip")]
public string BuyerZip { get; set; }
[JsonProperty(PropertyName = "buyerState")]
public string BuyerState { get; set; }
[JsonProperty(PropertyName = "buyerCity")]
public string BuyerCity { get; set; }
[JsonProperty(PropertyName = "buyerAddress2")]
public string BuyerAddress2 { get; set; }
[JsonProperty(PropertyName = "buyerAddress1")]
public string BuyerAddress1 { get; set; }
[JsonProperty(PropertyName = "buyerPhone")]
public string BuyerPhone { get; set; }
[JsonProperty(PropertyName = "itemDesc")]
public string ItemDesc { get; set; }
[JsonProperty(PropertyName = "itemCode")]
public string ItemCode { get; set; }
[JsonProperty(PropertyName = "physical")]
public bool? Physical { get; set; }
[JsonProperty(PropertyName = "taxIncluded", DefaultValueHandling = DefaultValueHandling.Ignore)]
public decimal? TaxIncluded { get; set; }
[JsonIgnore]
public string OrderId
{
get => GetMetadata<string>("orderId");
set => SetMetadata("orderId", value);
}
[JsonIgnore]
public string BuyerName{
get => GetMetadata<string>("buyerName");
set => SetMetadata("buyerName", value);
}
[JsonIgnore]
public string BuyerEmail {
get => GetMetadata<string>("buyerEmail");
set => SetMetadata("buyerEmail", value);
}
[JsonIgnore]
public string BuyerCountry {
get => GetMetadata<string>("buyerCountry");
set => SetMetadata("buyerCountry", value);
}
[JsonIgnore]
public string BuyerZip {
get => GetMetadata<string>("buyerZip");
set => SetMetadata("buyerZip", value);
}
[JsonIgnore]
public string BuyerState{
get => GetMetadata<string>("buyerState");
set => SetMetadata("buyerState", value);
}
[JsonIgnore]
public string BuyerCity {
get => GetMetadata<string>("buyerCity");
set => SetMetadata("buyerCity", value);
}
[JsonIgnore]
public string BuyerAddress2{
get => GetMetadata<string>("buyerAddress2");
set => SetMetadata("buyerAddress2", value);
}
[JsonIgnore]
public string BuyerAddress1 {
get => GetMetadata<string>("buyerAddress1");
set => SetMetadata("buyerAddress1", value);
}
[JsonIgnore]
public string BuyerPhone {
get => GetMetadata<string>("buyerPhone");
set => SetMetadata("buyerPhone", value);
}
[JsonIgnore]
public string ItemDesc {
get => GetMetadata<string>("itemDesc");
set => SetMetadata("itemDesc", value);
}
[JsonIgnore]
public string ItemCode{
get => GetMetadata<string>("itemCode");
set => SetMetadata("itemCode", value);
}
[JsonIgnore]
public bool? Physical {
get => GetMetadata<bool?>("physical");
set => SetMetadata("physical", value);
}
[JsonIgnore]
public decimal? TaxIncluded {
get => GetMetadata<decimal?>("taxIncluded");
set => SetMetadata("taxIncluded", value);
}
[JsonIgnore]
public string PosData
{
get
{
return PosRawData?.ToString();
}
set
{
if (value is null)
{
PosRawData = JValue.CreateNull();
}
else
{
try
{
PosRawData = JToken.Parse(value);
}
catch (Exception )
{
PosRawData = JToken.FromObject(value);
}
}
}
get => GetMetadata<string>("posData");
set => SetMetadata("posData", value);
}
[JsonProperty(PropertyName = "posData")]
public JToken PosRawData { get; set; }
[JsonExtensionData]
public IDictionary<string, JToken> AdditionalData { get; set; }
public T GetMetadata<T>(string propName)
{
if (AdditionalData == null || !(AdditionalData.TryGetValue(propName, out var jt) is true)) return default;
if (jt.Type == JTokenType.Null)
return default;
if (typeof(T) == typeof(string))
{
return (T)(object)jt.ToString();
}
try
{
return jt.Value<T>();
}
catch (Exception)
{
return default;
}
}
public void SetMetadata<T>(string propName, T value)
{
JToken data;
if (value is null)
{
AdditionalData?.Remove(propName);
}
else
{
try
{
if (value is string s)
{
data = JToken.Parse(s);
}
else
{
data = JToken.FromObject(value);
}
}
catch (Exception )
{
data = JToken.FromObject(value);
}
AdditionalData ??= new Dictionary<string, JToken>();
AdditionalData.AddOrReplace(propName, data);
}
}
public static InvoiceMetadata FromJObject(JObject jObject)
{
return jObject.ToObject<InvoiceMetadata>(MetadataSerializer);