Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Serialize only simple types to session in TempData #2317

Merged
merged 1 commit into from
Apr 17, 2015
Merged

Conversation

ajaybhargavb
Copy link
Contributor

Issue - #2276
@pranavkm

@ghost ghost added the cla-not-required label Apr 2, 2015
@pranavkm
Copy link
Contributor

pranavkm commented Apr 2, 2015

cc @Eilon

@@ -76,6 +76,44 @@ public void Save_NullSession_NonEmptyDictionary_Throws()
});
}

[Theory]
[MemberData(nameof(InvalidTypes))]
public void Save_InvalidType_Throws(string key, object value, Type type)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add test to verify that all the types mentioned in TypeHelper.IsSimpleType can be deserialized...I am specifically interested about struct and also not sure about Uri

https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNet.Mvc.Common/TypeHelper.cs#L68

@pranavkm
Copy link
Contributor

pranavkm commented Apr 2, 2015

We need functional tests that verify these values round trip correctly - if I throw in a IList<string> for instance. There's a bunch of other types that IsSimpleType allows (decimal, DateTime, Guid etc). @Eilon are we ok with that as long as it round trips correctly?

@ajaybhargavb
Copy link
Contributor Author

Updated.

testProvider.EnsureObjectCanBeSerialized(new Dictionary<string, object> { { key, value } });
}

public static TheoryData<string, object, Type> InvalidTypes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting nit: member data needs to preceed the actual test.

@pranavkm
Copy link
Contributor

pranavkm commented Apr 3, 2015

:shipit: it from me. Get sign off from @Eilon before you check this in

private readonly JsonSerializer _jsonSerializer = JsonSerializer.Create(
new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to consult with @blowdart to use this because it is generally not allowed to use any TypeNameHandling value other than None. I don't know if we can guarantee that the data is coming from a trusted source. For example, suppose that the session state provider is a custom one that stores all session data in a cookie - that is no longer trusted data because it can be tampered. Our default session state providers can't be tampered because they are stored in trusted environments (local machine memory, Azure Cache, etc.).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. You can't ship this. Use of Auto is banned because it enables RCE. Unless you can ensure every input is signed, and validated you need to find another way to do this.

@ajaybhargavb
Copy link
Contributor Author

Updated. @Eilon @pranavkm

private readonly JsonSerializer _jsonSerializer = JsonSerializer.Create(
new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@blowdart FYI - be happy 😄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When am I ever HAPPY? I am content.

@Eilon
Copy link
Contributor

Eilon commented Apr 15, 2015

What about dictionaries? Is it easy to support those?

tempDataDictionary = jsonSerializer.Deserialize<Dictionary<string, object>>(writer);
tempDataDictionary = _jsonSerializer.Deserialize<Dictionary<string, object>>(writer);
}
foreach (var item in tempDataDictionary.ToList())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you create a new dictionary rather than writing to this one (preferably one with StringComparer.OrdinalIgnoreCase semantics? Seems cleaner than doing a ToList here. An additional advantage is we could change the dictionary we wrap in TempDataDictionary to be a copy-on-write one.


private static readonly ConcurrentDictionary<Type, Func<JArray, object>> _arrayConverters =
new ConcurrentDictionary<Type, Func<JArray, object>>();
private static readonly ConcurrentDictionary<Type, Func<JObject, object>> _dictConverters =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_dictionaryConverters

@ajaybhargavb
Copy link
Contributor Author

It can only be string because JObject implements IDictionary<string, JToken>

@blowdart
Copy link
Member

Good :)

actualTypes = actualTypes ?? new Type[] { itemType };

// Throw if the key type of the dictionary is not string.
if (actualTypes.Length > 1 && actualTypes[0] != typeof(string))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do this early on as part of detecting the IDictionary<,>. You can avoid having Type[] actualTypes and just have one.

@pranavkm
Copy link
Contributor

@ajaybhargavb
Copy link
Contributor Author

Updated.

<value>Cannot deserialize JToken of type {0}.</value>
</data>
<data name="TempData_CannotSerializeDictionary" xml:space="preserve">
<value>The dictionary with TKey {0} cannot be serialized to Session by '{1}'.</value>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The '{0}' cannot serialize a dictionary with a key of type '{1}' to session state. (Swapped {0} and {1} too.)

@Eilon
Copy link
Contributor

Eilon commented Apr 17, 2015

Just some small string comments, then :shipit: !

}
else if (itemType.GetTypeInfo().IsGenericType)
{
if (itemType.ExtractGenericInterface(typeof(IList<>)) != null ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just split this out:

if (itemType.ExtractGenericInterface(typeof(IList<>)))
{ 
      Debug.Asser(itemType.GetGenericArguments().Length == 1, "IList<T> has one argument");
       itemType = itemType.GetGenericArguments()[0];
}
if (itemType.ExtractGenericInterface(typeof(IDictionary<>)))
{
    var genericTypeArguments = itemType.GetGenericArguments();
   Debug.Assert(genericTypeArguments.Length == 0);
   ...
}

<value>The '{0}' cannot serialize an object of type '{1}' to session state.</value>
</data>
<data name="TempData_CannotDeserializeToken" xml:space="preserve">
<value>Cannot deserialize JToken of type {0}.</value>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make JToken a parameter.

@pranavkm
Copy link
Contributor

:shipit: once the remaining comments are addressed.

@ajaybhargavb ajaybhargavb merged commit a4fd517 into dev Apr 17, 2015
@ajaybhargavb ajaybhargavb deleted the temp-data branch April 23, 2015 22:24
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants