A remote handle on its own domain, once that domain's WebFinger agrees (FEP-2c59)
An account on a server whose handles are not its host's (Mastodon's LOCAL_DOMAIN apart from WEB_DOMAIN) showed as user@host. The actor's `webfinger` names the handle; its domain is kept once WebFinger there points back to the actor, and asked again when the name changes. A persona's blocked servers match a handle's domain as well as the actor's host, as the lists Mastodon exports name handles' domains. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
1 parent
e708f1ad2e
commit
02928caac1
8 files changed
+182
-15
No files matched your search
@@ -25,6 +25,7 @@ namespace PrivaPub.Federation.Actors
|
||||
public string Following { get; init; }
|
||||
public string Featured { get; init; }
|
||||
public string Wall { get; init; }
|
||||
public string WebFinger { get; init; }//FEP-2c59: its handle, user@domain, where the domain is not its host's
|
||||
public string SharedInbox { get; init; }
|
||||
public string Icon { get; init; }
|
||||
public bool Discoverable { get; init; } = true;
|
||||
@@ -78,6 +79,7 @@ namespace PrivaPub.Federation.Actors
|
||||
Following = RemoteActorService.Text(root, "following"),
|
||||
Featured = RemoteActorService.Text(root, "featured"),
|
||||
Wall = RemoteActorService.Text(root, "wall") ?? RemoteActorService.Text(root, "sm:wall"),
|
||||
WebFinger = RemoteActorService.Text(root, "webfinger")?.Replace("acct:", "", StringComparison.OrdinalIgnoreCase).TrimStart('@'),
|
||||
SharedInbox = root.TryGetProperty("endpoints", out var endpoints) ? RemoteActorService.Text(endpoints, "sharedInbox") : default,
|
||||
Icon = root.TryGetProperty("icon", out var icon) ? RemoteActorService.Text(icon, "url") : default,
|
||||
Discoverable = !root.TryGetProperty("discoverable", out var discoverable) || discoverable.ValueKind != JsonValueKind.False,
|
||||
|
||||
@@ -190,8 +190,25 @@ namespace PrivaPub.Federation.Actors
|
||||
return true;
|
||||
}
|
||||
|
||||
static async Task<ForeignAvatar> Upsert(ActorDocument actor, ActorKey key, CancellationToken token)
|
||||
// the domain of its handle: its own host, or the one its `webfinger` names (FEP-2c59; a server whose handles are not
|
||||
// its host's, as Mastodon's LOCAL_DOMAIN) once that domain's WebFinger says the handle is this actor. Asked again
|
||||
// only when the handle changes, a rename among them.
|
||||
async Task<string> HandleDomain(ActorDocument actor, CancellationToken token)
|
||||
{
|
||||
var host = new Uri(actor.Id).Authority;
|
||||
var named = actor.WebFinger?.Split('@');
|
||||
if (named is not { Length: 2 } || named[0].Length == 0 || named[1].Length == 0 || named[1].Equals(host, StringComparison.OrdinalIgnoreCase))
|
||||
return host;
|
||||
var domain = named[1].ToLowerInvariant();
|
||||
var stored = await _dbEntities.ForeignAvatars.Match(a => a.ActorURI == actor.Id).ExecuteFirstAsync(token);
|
||||
if (stored?.Domain == domain && stored.UserName == actor.PreferredUsername)
|
||||
return domain;
|
||||
return await ResolveHandle($"{named[0]}@{domain}", token) == actor.Id ? domain : host;
|
||||
}
|
||||
|
||||
async Task<ForeignAvatar> Upsert(ActorDocument actor, ActorKey key, CancellationToken token)
|
||||
{
|
||||
var domain = await HandleDomain(actor, token);
|
||||
var now = DateTime.UtcNow;
|
||||
return await DB.Default.UpdateAndGet<ForeignAvatar>()
|
||||
.Match(a => a.ActorURI == actor.Id)
|
||||
@@ -199,7 +216,7 @@ namespace PrivaPub.Federation.Actors
|
||||
.Modify(a => a.Name, actor.Name)
|
||||
.Modify(a => a.Biography, ContentSanitizer.Html(actor.Summary))
|
||||
.Modify(a => a.Url, actor.Url)
|
||||
.Modify(a => a.Domain, new Uri(actor.Id).Authority)
|
||||
.Modify(a => a.Domain, domain)
|
||||
.Modify(a => a.InboxURL, actor.Inbox)
|
||||
.Modify(a => a.OutboxURL, actor.Outbox)
|
||||
.Modify(a => a.FollowersURL, actor.Followers)
|
||||
|
||||
Reference in new issue
Block a user