1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-13 02:32:55 +08:00

Replace usage of TypeNameHandling.All with custom type converter

This commit is contained in:
Dean Herbert 2021-11-11 14:04:17 +09:00
parent 16418ac2ab
commit 98fa253e1e
3 changed files with 81 additions and 4 deletions

View File

@ -4,6 +4,7 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client;
@ -161,9 +162,10 @@ namespace osu.Game.Online
builder.AddNewtonsoftJsonProtocol(options =>
{
options.PayloadSerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
// TODO: This should only be required to be `TypeNameHandling.Auto`.
// See usage in osu-server-spectator for further documentation as to why this is required.
options.PayloadSerializerSettings.TypeNameHandling = TypeNameHandling.All;
options.PayloadSerializerSettings.Converters = new List<JsonConverter>
{
new SignalRDerivedTypeWorkaroundJsonConverter(),
};
});
}

View File

@ -0,0 +1,60 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable enable
using System;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace osu.Game.Online
{
/// <summary>
/// A type of <see cref="JsonConverter"/> that serializes a subset of types used in multiplayer/spectator communication that
/// derive from a known base type. This is a safe alternative to using <see cref="TypeNameHandling.Auto"/> or <see cref="TypeNameHandling.All"/>,
/// which are known to have security issues.
/// </summary>
public class SignalRDerivedTypeWorkaroundJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType) =>
SignalRUnionWorkaroundResolver.BASE_TYPES.Contains(objectType) ||
SignalRUnionWorkaroundResolver.DERIVED_TYPES.Contains(objectType);
public override object? ReadJson(JsonReader reader, Type objectType, object? o, JsonSerializer jsonSerializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
JObject obj = JObject.Load(reader);
string type = (string)obj[@"$dtype"]!;
var resolvedType = SignalRUnionWorkaroundResolver.DERIVED_TYPES.Single(t => t.Name == type);
object? instance = Activator.CreateInstance(resolvedType);
jsonSerializer.Populate(obj["$value"]!.CreateReader(), instance);
return instance;
}
public override void WriteJson(JsonWriter writer, object? o, JsonSerializer serializer)
{
if (o == null)
{
writer.WriteNull();
return;
}
writer.WriteStartObject();
writer.WritePropertyName(@"$dtype");
serializer.Serialize(writer, o.GetType().Name);
writer.WritePropertyName(@"$value");
writer.WriteRawValue(JsonConvert.SerializeObject(o));
writer.WriteEndObject();
}
}
}

View File

@ -20,7 +20,22 @@ namespace osu.Game.Online
public static readonly MessagePackSerializerOptions OPTIONS =
MessagePackSerializerOptions.Standard.WithResolver(new SignalRUnionWorkaroundResolver());
private static readonly Dictionary<Type, IMessagePackFormatter> formatter_map = new Dictionary<Type, IMessagePackFormatter>
public static readonly IReadOnlyList<Type> BASE_TYPES = new[]
{
typeof(MatchServerEvent),
typeof(MatchUserRequest),
typeof(MatchRoomState),
typeof(MatchUserState),
};
public static readonly IReadOnlyList<Type> DERIVED_TYPES = new[]
{
typeof(ChangeTeamRequest),
typeof(TeamVersusRoomState),
typeof(TeamVersusUserState),
};
private static readonly IReadOnlyDictionary<Type, IMessagePackFormatter> formatter_map = new Dictionary<Type, IMessagePackFormatter>
{
{ typeof(TeamVersusUserState), new TypeRedirectingFormatter<TeamVersusUserState, MatchUserState>() },
{ typeof(TeamVersusRoomState), new TypeRedirectingFormatter<TeamVersusRoomState, MatchRoomState>() },