From 38a67b17fecdd5a5b3536956861054855e4ba204 Mon Sep 17 00:00:00 2001 From: Laura Hausmann Date: Fri, 1 Mar 2024 03:20:27 +0100 Subject: [PATCH] [backend/federation] Fix handling of incorrectly namespaced @value type ASCollections (ISH-67) --- .../Contexts/as-extensions.json | 5 +- .../ActivityStreams/Types/ASActor.cs | 2 +- .../ActivityStreams/Types/ASCollection.cs | 40 +++++++++++++++- .../ActivityStreams/Types/ASCollectionPage.cs | 46 ++++++++++++++++++- .../Types/ASOrderedCollection.cs | 44 +++++++++++++++++- .../Types/ASOrderedCollectionPage.cs | 44 +++++++++++++++++- .../ActivityStreams/Types/LDValueObject.cs | 2 +- 7 files changed, 175 insertions(+), 8 deletions(-) diff --git a/Iceshrimp.Backend/Core/Federation/ActivityStreams/Contexts/as-extensions.json b/Iceshrimp.Backend/Core/Federation/ActivityStreams/Contexts/as-extensions.json index 62600f54..ce200028 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityStreams/Contexts/as-extensions.json +++ b/Iceshrimp.Backend/Core/Federation/ActivityStreams/Contexts/as-extensions.json @@ -4,6 +4,9 @@ "@id": "https://www.w3.org/ns/activitystreams#movedTo", "@type": "@id" }, - "featured": "http://joinmastodon.org/ns#featured" + "featured": { + "@id": "http://joinmastodon.org/ns#featured", + "@type": "@id" + } } } \ No newline at end of file diff --git a/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASActor.cs b/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASActor.cs index fd928477..d6766b20 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASActor.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASActor.cs @@ -98,7 +98,7 @@ public class ASActor : ASObject public List? AlsoKnownAs { get; set; } [J("http://joinmastodon.org/ns#featured")] - [JC(typeof(VC))] + [JC(typeof(ASOrderedCollectionConverter))] public ASOrderedCollection? Featured { get; set; } [J("http://joinmastodon.org/ns#featuredTags")] diff --git a/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASCollection.cs b/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASCollection.cs index 5e65ebe8..9d98187b 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASCollection.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASCollection.cs @@ -40,7 +40,45 @@ public class ASCollection : ASObject public const string ObjectType = $"{Constants.ActivityStreamsNs}#Collection"; } -public sealed class ASCollectionConverter : ASSerializer.ListSingleObjectConverter; +public sealed class ASCollectionConverter : JsonConverter +{ + public override bool CanWrite => false; + + public override bool CanConvert(Type objectType) + { + return true; + } + + public override object? ReadJson( + JsonReader reader, Type objectType, object? existingValue, + JsonSerializer serializer + ) + { + if (reader.TokenType == JsonToken.StartArray) + { + var obj = JArray.Load(reader); + var valueList = obj.ToObject>>(); + if (valueList is { Count: > 0 }) + return VC.HandleObject(valueList[0], objectType); + var list = obj.ToObject>(); + return list == null || list.Count == 0 ? null : list[0]; + } + + if (reader.TokenType == JsonToken.StartObject) + { + var obj = JObject.Load(reader); + var valueObj = obj.ToObject>(); + return valueObj != null ? VC.HandleObject(valueObj, objectType) : obj.ToObject(); + } + + return null; + } + + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } +} internal sealed class ASCollectionItemsConverter : JsonConverter { diff --git a/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASCollectionPage.cs b/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASCollectionPage.cs index 36edd933..231960a9 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASCollectionPage.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASCollectionPage.cs @@ -1,5 +1,7 @@ using System.Diagnostics.CodeAnalysis; using Iceshrimp.Backend.Core.Configuration; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using J = Newtonsoft.Json.JsonPropertyAttribute; using JC = Newtonsoft.Json.JsonConverterAttribute; using VC = Iceshrimp.Backend.Core.Federation.ActivityStreams.Types.ValueObjectConverter; @@ -9,7 +11,7 @@ namespace Iceshrimp.Backend.Core.Federation.ActivityStreams.Types; public class ASCollectionPage : ASObject { public ASCollectionPage() => Type = ObjectType; - + [SetsRequiredMembers] public ASCollectionPage(string id) : this() => Id = id; @@ -36,4 +38,44 @@ public class ASCollectionPage : ASObject public const string ObjectType = $"{Constants.ActivityStreamsNs}#CollectionPage"; } -public sealed class ASCollectionPageConverter : ASSerializer.ListSingleObjectConverter; \ No newline at end of file +public sealed class ASCollectionPageConverter : JsonConverter +{ + public override bool CanWrite => false; + + public override bool CanConvert(Type objectType) + { + return true; + } + + public override object? ReadJson( + JsonReader reader, Type objectType, object? existingValue, + JsonSerializer serializer + ) + { + if (reader.TokenType == JsonToken.StartArray) + { + var obj = JArray.Load(reader); + var valueList = obj.ToObject>>(); + if (valueList is { Count: > 0 }) + return VC.HandleObject(valueList[0], objectType); + var list = obj.ToObject>(); + return list == null || list.Count == 0 ? null : list[0]; + } + + if (reader.TokenType == JsonToken.StartObject) + { + var obj = JObject.Load(reader); + var valueObj = obj.ToObject>(); + return valueObj != null + ? VC.HandleObject(valueObj, objectType) + : obj.ToObject(); + } + + return null; + } + + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASOrderedCollection.cs b/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASOrderedCollection.cs index bf2a12aa..05cd103c 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASOrderedCollection.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASOrderedCollection.cs @@ -1,5 +1,7 @@ using System.Diagnostics.CodeAnalysis; using Iceshrimp.Backend.Core.Configuration; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using J = Newtonsoft.Json.JsonPropertyAttribute; using JC = Newtonsoft.Json.JsonConverterAttribute; using JI = Newtonsoft.Json.JsonIgnoreAttribute; @@ -25,4 +27,44 @@ public class ASOrderedCollection : ASCollection public new const string ObjectType = $"{Constants.ActivityStreamsNs}#OrderedCollection"; } -public sealed class ASOrderedCollectionConverter : ASSerializer.ListSingleObjectConverter; \ No newline at end of file +public sealed class ASOrderedCollectionConverter : JsonConverter +{ + public override bool CanWrite => false; + + public override bool CanConvert(Type objectType) + { + return true; + } + + public override object? ReadJson( + JsonReader reader, Type objectType, object? existingValue, + JsonSerializer serializer + ) + { + if (reader.TokenType == JsonToken.StartArray) + { + var obj = JArray.Load(reader); + var valueList = obj.ToObject>>(); + if (valueList?.Any(p => p.Value != null) ?? false) + return ValueObjectConverter.HandleObject(valueList[0], objectType); + var list = obj.ToObject>(); + return list == null || list.Count == 0 ? null : list[0]; + } + + if (reader.TokenType == JsonToken.StartObject) + { + var obj = JObject.Load(reader); + var valueObj = obj.ToObject>(); + return valueObj is { Value: not null } + ? ValueObjectConverter.HandleObject(valueObj, objectType) + : obj.ToObject(); + } + + return null; + } + + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASOrderedCollectionPage.cs b/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASOrderedCollectionPage.cs index 0f76eae2..3df00bd4 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASOrderedCollectionPage.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASOrderedCollectionPage.cs @@ -1,5 +1,7 @@ using System.Diagnostics.CodeAnalysis; using Iceshrimp.Backend.Core.Configuration; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using J = Newtonsoft.Json.JsonPropertyAttribute; using JC = Newtonsoft.Json.JsonConverterAttribute; using VC = Iceshrimp.Backend.Core.Federation.ActivityStreams.Types.ValueObjectConverter; @@ -36,4 +38,44 @@ public class ASOrderedCollectionPage : ASObject public const string ObjectType = $"{Constants.ActivityStreamsNs}#OrderedCollectionPage"; } -public sealed class ASOrderedCollectionPageConverter : ASSerializer.ListSingleObjectConverter; \ No newline at end of file +public sealed class ASOrderedCollectionPageConverter : JsonConverter +{ + public override bool CanWrite => false; + + public override bool CanConvert(Type objectType) + { + return true; + } + + public override object? ReadJson( + JsonReader reader, Type objectType, object? existingValue, + JsonSerializer serializer + ) + { + if (reader.TokenType == JsonToken.StartArray) + { + var obj = JArray.Load(reader); + var valueList = obj.ToObject>>(); + if (valueList is { Count: > 0 }) + return VC.HandleObject(valueList[0], objectType); + var list = obj.ToObject>(); + return list == null || list.Count == 0 ? null : list[0]; + } + + if (reader.TokenType == JsonToken.StartObject) + { + var obj = JObject.Load(reader); + var valueObj = obj.ToObject>(); + return valueObj != null + ? VC.HandleObject(valueObj, objectType) + : obj.ToObject(); + } + + return null; + } + + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/LDValueObject.cs b/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/LDValueObject.cs index 824c63e4..a0f2ff41 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/LDValueObject.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/LDValueObject.cs @@ -45,7 +45,7 @@ public class ValueObjectConverter : JsonConverter return null; } - private static object? HandleObject(LDValueObject? obj, Type objectType) + internal static object? HandleObject(LDValueObject? obj, Type objectType) { if (obj?.Value is string s && (objectType == typeof(DateTime?) || objectType == typeof(DateTime))) {