[backend/federation] Add support for returning xrd+xml WebFinger responses for local users

This commit is contained in:
Laura Hausmann 2024-08-14 01:49:49 +02:00
parent c3eacb34c2
commit b37f02846b
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
4 changed files with 23 additions and 20 deletions

View file

@ -24,7 +24,7 @@ namespace Iceshrimp.Backend.Controllers.Federation;
public class WellKnownController(IOptions<Config.InstanceSection> config, DatabaseContext db) : ControllerBase public class WellKnownController(IOptions<Config.InstanceSection> config, DatabaseContext db) : ControllerBase
{ {
[HttpGet("webfinger")] [HttpGet("webfinger")]
[Produces(MediaTypeNames.Application.Json, "application/jrd+json")] [Produces("application/jrd+json", "application/json", "application/xrd+xml", "application/xml")]
[ProducesResults(HttpStatusCode.OK)] [ProducesResults(HttpStatusCode.OK)]
[ProducesErrors(HttpStatusCode.NotFound)] [ProducesErrors(HttpStatusCode.NotFound)]
public async Task<WebFingerResponse> WebFinger([FromQuery] string resource) public async Task<WebFingerResponse> WebFinger([FromQuery] string resource)
@ -66,6 +66,12 @@ public class WellKnownController(IOptions<Config.InstanceSection> config, Databa
Href = user.GetPublicUri(config.Value) Href = user.GetPublicUri(config.Value)
}, },
new WebFingerLink new WebFingerLink
{
Rel = "self",
Type = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"",
Href = user.GetPublicUri(config.Value)
},
new WebFingerLink
{ {
Rel = "http://webfinger.net/rel/profile-page", Rel = "http://webfinger.net/rel/profile-page",
Type = "text/html", Type = "text/html",

View file

@ -1,6 +1,7 @@
using System.Buffers; using System.Buffers;
using System.Net; using System.Net;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Xml;
using Iceshrimp.Backend.Controllers.Shared.Attributes; using Iceshrimp.Backend.Controllers.Shared.Attributes;
using Iceshrimp.Backend.Core.Middleware; using Iceshrimp.Backend.Core.Middleware;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -35,6 +36,7 @@ public static class MvcBuilderExtensions
opts.InputFormatters.Insert(0, new JsonInputMultiFormatter()); opts.InputFormatters.Insert(0, new JsonInputMultiFormatter());
opts.OutputFormatters.Insert(0, new JsonOutputMultiFormatter()); opts.OutputFormatters.Insert(0, new JsonOutputMultiFormatter());
opts.OutputFormatters.Add(new XmlSerializerOutputFormatter());
}); });
return builder; return builder;

View file

@ -30,24 +30,25 @@ public sealed class WebFingerLink
[SuppressMessage("ReSharper", "CollectionNeverUpdated.Global")] [SuppressMessage("ReSharper", "CollectionNeverUpdated.Global")]
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")] [SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
[XmlRoot("XRD", Namespace = "http://docs.oasis-open.org/ns/xri/xrd-1.0", IsNullable = false)]
public sealed class WebFingerResponse public sealed class WebFingerResponse
{ {
[J("links")] [JR] public List<WebFingerLink> Links { get; set; } = null!; [XmlElement("Link")]
[J("subject")] [JR] public string Subject { get; set; } = null!; [J("links")]
[JR]
public required List<WebFingerLink> Links { get; set; } = null!;
[XmlElement("Subject")]
[J("subject")]
[JR]
public required string Subject { get; set; } = null!;
[XmlElement("Alias")]
[J("aliases")] [J("aliases")]
[JI(Condition = JsonIgnoreCondition.WhenWritingNull)] [JI(Condition = JsonIgnoreCondition.WhenWritingNull)]
public List<string>? Aliases { get; set; } public List<string>? Aliases { get; set; }
} }
[XmlRoot("XRD", Namespace = "http://docs.oasis-open.org/ns/xri/xrd-1.0", IsNullable = false)]
public sealed class WebFingerXmlResponse
{
[XmlElement("Subject")] public required string Subject { get; set; } = null!;
[XmlElement("Alias")] public List<string>? Aliases { get; set; }
[XmlElement("Link")] public required List<WebFingerLink> Links { get; set; }
}
public sealed class NodeInfoIndexResponse public sealed class NodeInfoIndexResponse
{ {
[J("links")] [JR] public List<WebFingerLink> Links { get; set; } = null!; [J("links")] [JR] public List<WebFingerLink> Links { get; set; } = null!;

View file

@ -59,16 +59,10 @@ public class WebFingerService(
if (res.Content.Headers.ContentType?.MediaType is "application/jrd+json" or "application/json") if (res.Content.Headers.ContentType?.MediaType is "application/jrd+json" or "application/json")
return await res.Content.ReadFromJsonAsync<WebFingerResponse>(cts.Token); return await res.Content.ReadFromJsonAsync<WebFingerResponse>(cts.Token);
var deserializer = new XmlSerializer(typeof(WebFingerXmlResponse)); var deserializer = new XmlSerializer(typeof(WebFingerResponse));
if (deserializer.Deserialize(await res.Content.ReadAsStreamAsync(cts.Token)) is not WebFingerXmlResponse xml)
throw new Exception("Failed to deserialize xml payload");
return new WebFingerResponse return deserializer.Deserialize(await res.Content.ReadAsStreamAsync(cts.Token)) as WebFingerResponse ??
{ throw new Exception("Failed to deserialize xml payload");
Subject = xml.Subject,
Links = xml.Links,
Aliases = xml.Aliases
};
} }
public static (string query, string proto, string domain) ParseQuery(string query) public static (string query, string proto, string domain) ParseQuery(string query)