diff --git a/users/models/identity.py b/users/models/identity.py index ae9b588..764325f 100644 --- a/users/models/identity.py +++ b/users/models/identity.py @@ -248,6 +248,18 @@ class Identity(StatorModel): else: return self.profile_uri + def all_absolute_profile_uris(self) -> list[str]: + """ + Returns alist of profile URIs that are always absolute. For local addresses, + this includes the short and long form URIs. + """ + if not self.local: + return [self.profile_uri] + return [ + f"https://{self.domain.uri_domain}/@{self.username}/", + f"https://{self.domain.uri_domain}/@{self.username}@{self.domain_id}/", + ] + def local_icon_url(self) -> RelativeAbsoluteUrl: """ Returns an icon for use by us, with fallbacks to a placeholder @@ -401,6 +413,36 @@ class Identity(StatorModel): ### ActivityPub (outbound) ### + def to_webfinger(self): + aliases = self.all_absolute_profile_uris() + + actor_links = [] + + if self.restriction != Identity.Restriction.blocked: + # Blocked users don't get a profile page + actor_links.append( + { + "rel": "http://webfinger.net/rel/profile-page", + "type": "text/html", + "href": self.absolute_profile_uri(), + }, + ) + + # TODO: How to handle Restriction.limited and Restriction.blocked? + # Exposing the activity+json will allow migrating off server + actor_links.extend( + [ + {"rel": "self", "type": "application/activity+json", "href": alias_uri} + for alias_uri in aliases + ] + ) + + return { + "subject": f"acct:{self.handle}", + "aliases": aliases, + "links": actor_links, + } + def to_ap(self): response = { "id": self.actor_uri, diff --git a/users/models/system_actor.py b/users/models/system_actor.py index 62b9996..822925c 100644 --- a/users/models/system_actor.py +++ b/users/models/system_actor.py @@ -21,6 +21,7 @@ class SystemActor: self.public_key_id = self.actor_uri + "#main-key" self.profile_uri = f"https://{settings.MAIN_DOMAIN}/about/" self.username = "__system__" + self.handle = f"__system__@{settings.MAIN_DOMAIN}" def absolute_profile_uri(self): return self.profile_uri @@ -58,6 +59,26 @@ class SystemActor: }, } + def to_webfinger(self): + return { + "subject": f"acct:{self.handle}", + "aliases": [ + self.absolute_profile_uri(), + ], + "links": [ + { + "rel": "http://webfinger.net/rel/profile-page", + "type": "text/html", + "href": self.absolute_profile_uri(), + }, + { + "rel": "self", + "type": "application/activity+json", + "href": self.actor_uri, + }, + ], + } + async def signed_request( self, method: Literal["get", "post"], diff --git a/users/views/activitypub.py b/users/views/activitypub.py index e027711..7a9170c 100644 --- a/users/views/activitypub.py +++ b/users/views/activitypub.py @@ -108,28 +108,8 @@ class Webfinger(View): actor = SystemActor() else: actor = by_handle_or_404(request, handle) - handle = actor.handle - return JsonResponse( - { - "subject": f"acct:{handle}", - "aliases": [ - actor.absolute_profile_uri(), - ], - "links": [ - { - "rel": "http://webfinger.net/rel/profile-page", - "type": "text/html", - "href": actor.absolute_profile_uri(), - }, - { - "rel": "self", - "type": "application/activity+json", - "href": actor.actor_uri, - }, - ], - } - ) + return JsonResponse(actor.to_webfinger()) @method_decorator(csrf_exempt, name="dispatch")