Skip to content
This repository has been archived by the owner on Aug 1, 2024. It is now read-only.

Commit

Permalink
Enforce serializer settings for table parsers and fix support for dou…
Browse files Browse the repository at this point in the history
…ble.NaN, double.PositiveInfinity, double.NegativeInfinity (#656)

* [NET] Fix support for double.Nan, double.PositiveInfinity, double.NegativeInfinity. Expand tests around the various EDM types.

* [NET] Fix case where JSON serialization gets inadvertently overridden

* Adding missing copyright notice

* Fix non-table settings

* Address CR comments, fix bug introduced in 9.0 where doubles were not marked with EdmType when sent to server.

* Remove quotes from normal double values.

* Fix NetCore support of NaN

* Cap language use to C#5

* Further NetCore test fixes

* Remove incorrect project nuget dependency, fix test

* Test fixes

* Revert serializer settings changes for encryption cases
  • Loading branch information
kfarmer-msft authored and erezvani1529 committed Mar 30, 2018
1 parent e49b61b commit e01de1b
Show file tree
Hide file tree
Showing 36 changed files with 658 additions and 150 deletions.
6 changes: 3 additions & 3 deletions Lib/ClassLibraryCommon/Blob/CloudBlob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ public virtual void RotateEncryptionKey(AccessCondition accessCondition = null,
// Update the encryption metadata with the newly wrapped CEK and call SetMetadata.
encryptionData.WrappedContentKey = new WrappedKey(modifiedOptions.EncryptionPolicy.Key.Kid, wrappedKey.encryptedKey, wrappedKey.algorithm);

this.Metadata[Constants.EncryptionConstants.BlobEncryptionData] = Newtonsoft.Json.JsonConvert.SerializeObject(encryptionData, Newtonsoft.Json.Formatting.None);
this.Metadata[Constants.EncryptionConstants.BlobEncryptionData] = Newtonsoft.Json.JsonConvert.SerializeObject(encryptionData, DefaultSerializerSettings.Create());

if (accessCondition == null)
{
Expand Down Expand Up @@ -569,7 +569,7 @@ public virtual async Task RotateEncryptionKeyAsync(AccessCondition accessConditi

encryptionData.WrappedContentKey = new WrappedKey(modifiedOptions.EncryptionPolicy.Key.Kid, wrappedKey.encryptedKey, wrappedKey.algorithm);

this.Metadata[Constants.EncryptionConstants.BlobEncryptionData] = Newtonsoft.Json.JsonConvert.SerializeObject(encryptionData, Newtonsoft.Json.Formatting.None);
this.Metadata[Constants.EncryptionConstants.BlobEncryptionData] = Newtonsoft.Json.JsonConvert.SerializeObject(encryptionData, DefaultSerializerSettings.Create());

if (accessCondition == null)
{
Expand Down Expand Up @@ -4109,7 +4109,7 @@ private async Task<WrappedKeyData> RotateEncryptionHelper(AccessCondition access
this.ValidateKeyRotationArguments(accessCondition, modifiedOptions, encryptionMetadataAvailable);

// Deserialize the old encryption data and validate:
BlobEncryptionData encryptionData = Newtonsoft.Json.JsonConvert.DeserializeObject<BlobEncryptionData>(encryptionDataString);
BlobEncryptionData encryptionData = Newtonsoft.Json.JsonConvert.DeserializeObject<BlobEncryptionData>(encryptionDataString, DefaultSerializerSettings.Create());
if (encryptionData.WrappedContentKey.EncryptedKey == null)
{
throw new InvalidOperationException(SR.KeyRotationNoKeyID);
Expand Down
4 changes: 1 addition & 3 deletions Lib/ClassLibraryCommon/Queue/QueueEncryptionPolicy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ namespace Microsoft.WindowsAzure.Storage.Queue
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using System.Threading;

/// <summary>
Expand Down Expand Up @@ -114,7 +112,7 @@ internal byte[] DecryptMessage(string inputMessage, bool? requireEncryption)

try
{
CloudQueueEncryptedMessage encryptedMessage = JsonConvert.DeserializeObject<CloudQueueEncryptedMessage>(inputMessage);
CloudQueueEncryptedMessage encryptedMessage = JsonConvert.DeserializeObject<CloudQueueEncryptedMessage>(inputMessage, DefaultSerializerSettings.Create());

if (requireEncryption.HasValue && requireEncryption.Value && encryptedMessage.EncryptionData == null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ namespace Microsoft.WindowsAzure.Storage.Table.Protocol
using Microsoft.WindowsAzure.Storage.Shared.Protocol;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
Expand Down Expand Up @@ -174,10 +175,35 @@ private static void WriteEntityContent(TableOperation operation, OperationContex
continue;
}

if (kvp.Value.GetType() == typeof(Double))
{
Double value = ((Double)kvp.Value);

if (Double.IsNaN(value))
{
propertyDictionary[kvp.Key] = "NaN";
}
else if (Double.IsPositiveInfinity(value))
{
propertyDictionary[kvp.Key] = "Infinity";
}
else if (Double.IsNegativeInfinity(value))
{
propertyDictionary[kvp.Key] = "-Infinity";
}
else
{
propertyDictionary[kvp.Key] = kvp.Value;
}

propertyDictionary[kvp.Key + Constants.OdataTypeString] = Constants.EdmDouble;
continue;
}

propertyDictionary[kvp.Key] = kvp.Value;
}

JObject json = JObject.FromObject(propertyDictionary);
JObject json = JObject.FromObject(propertyDictionary, DefaultSerializer.Create());

json.WriteTo(jsonWriter);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ public static async Task<KeyValuePair<string, Dictionary<string, object>>> ReadS

private static Dictionary<string, object> ReadSingleItem(JToken token, out string etag)
{
Dictionary<string, object> properties = token.ToObject<Dictionary<string, object>>();
Dictionary<string, object> properties = token.ToObject<Dictionary<string, object>>(DefaultSerializer.Create());

// Parse the etag, and remove all the "odata.*" properties we don't use.
if (properties.ContainsKey(@"odata.etag"))
Expand Down Expand Up @@ -498,18 +498,30 @@ private static Dictionary<string, object> ReadSingleItem(JToken token, out strin
string propName = typeAnnotation.Key.Split(new char[] { '@' }, StringSplitOptions.RemoveEmptyEntries)[0];
switch ((string)typeAnnotation.Value)
{
case @"Edm.DateTime":
properties[propName] = DateTime.Parse((string)properties[propName], null, DateTimeStyles.AdjustToUniversal);
break;
case @"Edm.Binary":
case Constants.EdmBinary:
properties[propName] = Convert.FromBase64String((string)properties[propName]);
break;
case @"Edm.Guid":
case Constants.EdmBoolean:
properties[propName] = Boolean.Parse((string)properties[propName]);
break;
case Constants.EdmDateTime:
properties[propName] = DateTime.Parse((string)properties[propName], CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
break;
case Constants.EdmDouble:
properties[propName] = Double.Parse((string)properties[propName], CultureInfo.InvariantCulture);
break;
case Constants.EdmGuid:
properties[propName] = Guid.Parse((string)properties[propName]);
break;
case @"Edm.Int64":
case Constants.EdmInt32:
properties[propName] = Int32.Parse((string)properties[propName], CultureInfo.InvariantCulture);
break;
case Constants.EdmInt64:
properties[propName] = Int64.Parse((string)properties[propName], CultureInfo.InvariantCulture);
break;
case Constants.EdmString:
properties[propName] = (string)properties[propName];
break;
default:
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, SR.UnexpectedEDMType, typeAnnotation.Value));
}
Expand Down Expand Up @@ -710,7 +722,7 @@ private static HashSet<string> ParseEncryptedPropertyDetailsSet(bool isJavav1, b
}
else
{
encryptedPropertyDetailsSet = JsonConvert.DeserializeObject<HashSet<string>>(Encoding.UTF8.GetString(binaryVal, 0, binaryVal.Length));
encryptedPropertyDetailsSet = JsonConvert.DeserializeObject<HashSet<string>>(Encoding.UTF8.GetString(binaryVal, 0, binaryVal.Length), DefaultSerializerSettings.Create());
}

return encryptedPropertyDetailsSet;
Expand Down
44 changes: 44 additions & 0 deletions Lib/Common/Core/DefaultSerializationSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// -----------------------------------------------------------------------------------------
// <copyright file="DefaultSerializationSettings.cs" company="Microsoft">
// Copyright 2018 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
// -----------------------------------------------------------------------------------------

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace Microsoft.WindowsAzure.Storage.Core
{
static class DefaultSerializer
{
public static JsonSerializer Create()
{
return new JsonSerializer
{
ContractResolver = new DefaultContractResolver()
};
}
}

static class DefaultSerializerSettings
{
public static JsonSerializerSettings Create()
{
return new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver()
};
}
}
}
8 changes: 6 additions & 2 deletions Lib/Common/Shared/Protocol/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -844,10 +844,14 @@ static class Constants

internal const string OdataTypeString = "@odata.type";

internal const string EdmDateTime = @"Edm.DateTime";
internal const string EdmBinary = @"Edm.Binary";
internal const string EdmInt64 = @"Edm.Int64";
internal const string EdmBoolean = @"Emd.Boolean";
internal const string EdmDateTime = @"Edm.DateTime";
internal const string EdmDouble = @"Edm.Double";
internal const string EdmGuid = @"Edm.Guid";
internal const string EdmInt32 = @"Edm.Int32";
internal const string EdmInt64 = @"Edm.Int64";
internal const string EdmString = @"Edm.String";

internal const string BatchBoundaryMarker = @"multipart/mixed; boundary=batch_";
internal const string ChangesetBoundaryMarker = @"Content-Type: multipart/mixed; boundary=changeset_";
Expand Down
21 changes: 20 additions & 1 deletion Lib/Common/Table/EntityProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,26 @@ public double? DoubleValue
{
if (!this.IsNull)
{
this.EnforceType(EdmType.Double);
// We have to special case these post-9.0, since they may be missing EdmType data at the server, and inferred as String
if (this.PropertyAsObject is string)
{
switch ((string)this.PropertyAsObject)
{
case "NaN":
return double.NaN;
case "Infinity":
return double.PositiveInfinity;
case "-Infinity":
return double.NegativeInfinity;
default:
this.EnforceType(EdmType.Double);
break;
}
}
else
{
this.EnforceType(EdmType.Double);
}
}

return (double?)this.PropertyAsObject;
Expand Down
8 changes: 4 additions & 4 deletions Lib/Common/Table/ODataErrorHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,14 @@ public static async Task<StorageExtendedErrorInformation> ReadAndParseExtendedEr
reader.DateParseHandling = DateParseHandling.None;
JObject dataSet = await JObject.LoadAsync(reader, cancellationToken).ConfigureAwait(false);

Dictionary<string, object> properties = dataSet.ToObject<Dictionary<string, object>>();
Dictionary<string, object> properties = dataSet.ToObject<Dictionary<string, object>>(DefaultSerializer.Create());

StorageExtendedErrorInformation errorInformation = new StorageExtendedErrorInformation();

errorInformation.AdditionalDetails = new Dictionary<string, string>();
if (properties.ContainsKey(@"odata.error"))
{
Dictionary<string, object> errorProperties = ((JObject)properties[@"odata.error"]).ToObject<Dictionary<string, object>>();
Dictionary<string, object> errorProperties = ((JObject)properties[@"odata.error"]).ToObject<Dictionary<string, object>>(DefaultSerializer.Create());
if (errorProperties.ContainsKey(@"code"))
{
#pragma warning disable 618
Expand All @@ -124,15 +124,15 @@ public static async Task<StorageExtendedErrorInformation> ReadAndParseExtendedEr
}
if (errorProperties.ContainsKey(@"message"))
{
Dictionary<string, object> errorMessageProperties = ((JObject)errorProperties[@"message"]).ToObject<Dictionary<string, object>>();
Dictionary<string, object> errorMessageProperties = ((JObject)errorProperties[@"message"]).ToObject<Dictionary<string, object>>(DefaultSerializer.Create());
if (errorMessageProperties.ContainsKey(@"value"))
{
errorInformation.ErrorMessage = (string)errorMessageProperties[@"value"];
}
}
if (errorProperties.ContainsKey(@"innererror"))
{
Dictionary<string, object> innerErrorDictionary = ((JObject)errorProperties[@"innererror"]).ToObject<Dictionary<string, object>>();
Dictionary<string, object> innerErrorDictionary = ((JObject)errorProperties[@"innererror"]).ToObject<Dictionary<string, object>>(DefaultSerializer.Create());
if (innerErrorDictionary.ContainsKey(@"message"))
{
errorInformation.AdditionalDetails[Constants.ErrorExceptionMessage] = (string)innerErrorDictionary[@"message"];
Expand Down
Loading

0 comments on commit e01de1b

Please sign in to comment.