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
4 changed files with 131 additions and 65 deletions

View File

@@ -2366,7 +2366,9 @@ namespace BTCPayServer.Tests
{ {
("{ invalidjson file here}", ("{ invalidjson file here}",
new Dictionary<string, object>() {{String.Empty, "{ 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 => testCases.ForEach(tuple =>

View File

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

View File

@@ -5,7 +5,6 @@ using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices.ComTypes;
using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Configuration; using BTCPayServer.Configuration;
using McMaster.NETCore.Plugins; using McMaster.NETCore.Plugins;
@@ -28,7 +27,7 @@ namespace BTCPayServer.Plugins
public static bool IsExceptionByPlugin(Exception exception) 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, public static IMvcBuilder AddPlugins(this IMvcBuilder mvcBuilder, IServiceCollection serviceCollection,
IConfiguration config, ILoggerFactory loggerFactory) IConfiguration config, ILoggerFactory loggerFactory)

View File

@@ -30,70 +30,136 @@ namespace BTCPayServer.Services.Invoices
seria.ContractResolver = new CamelCasePropertyNamesContractResolver(); seria.ContractResolver = new CamelCasePropertyNamesContractResolver();
MetadataSerializer = seria; MetadataSerializer = seria;
} }
public string OrderId { get; set; }
[JsonProperty(PropertyName = "buyerName")] [JsonIgnore]
public string BuyerName { get; set; } public string OrderId
[JsonProperty(PropertyName = "buyerEmail")] {
public string BuyerEmail { get; set; } get => GetMetadata<string>("orderId");
[JsonProperty(PropertyName = "buyerCountry")] set => SetMetadata("orderId", value);
public string BuyerCountry { get; set; } }
[JsonProperty(PropertyName = "buyerZip")] [JsonIgnore]
public string BuyerZip { get; set; } public string BuyerName{
[JsonProperty(PropertyName = "buyerState")] get => GetMetadata<string>("buyerName");
public string BuyerState { get; set; } set => SetMetadata("buyerName", value);
[JsonProperty(PropertyName = "buyerCity")] }
public string BuyerCity { get; set; } [JsonIgnore]
[JsonProperty(PropertyName = "buyerAddress2")] public string BuyerEmail {
public string BuyerAddress2 { get; set; } get => GetMetadata<string>("buyerEmail");
[JsonProperty(PropertyName = "buyerAddress1")] set => SetMetadata("buyerEmail", value);
public string BuyerAddress1 { get; set; } }
[JsonIgnore]
[JsonProperty(PropertyName = "buyerPhone")] public string BuyerCountry {
public string BuyerPhone { get; set; } get => GetMetadata<string>("buyerCountry");
set => SetMetadata("buyerCountry", value);
[JsonProperty(PropertyName = "itemDesc")] }
public string ItemDesc { get; set; } [JsonIgnore]
[JsonProperty(PropertyName = "itemCode")] public string BuyerZip {
public string ItemCode { get; set; } get => GetMetadata<string>("buyerZip");
[JsonProperty(PropertyName = "physical")] set => SetMetadata("buyerZip", value);
public bool? Physical { get; set; } }
[JsonIgnore]
[JsonProperty(PropertyName = "taxIncluded", DefaultValueHandling = DefaultValueHandling.Ignore)] public string BuyerState{
public decimal? TaxIncluded { get; set; } 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] [JsonIgnore]
public string PosData public string PosData
{ {
get get => GetMetadata<string>("posData");
{ set => SetMetadata("posData", value);
return PosRawData?.ToString();
}
set
{
if (value is null)
{
PosRawData = JValue.CreateNull();
}
else
{
try
{
PosRawData = JToken.Parse(value);
}
catch (Exception )
{
PosRawData = JToken.FromObject(value);
}
}
}
} }
[JsonProperty(PropertyName = "posData")]
public JToken PosRawData { get; set; }
[JsonExtensionData] [JsonExtensionData]
public IDictionary<string, JToken> AdditionalData { get; set; } 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) public static InvoiceMetadata FromJObject(JObject jObject)
{ {
return jObject.ToObject<InvoiceMetadata>(MetadataSerializer); return jObject.ToObject<InvoiceMetadata>(MetadataSerializer);