add Web.ap_subdomain property, use it in webfinger

Originally, BF served Web users' AP actor ids on fed.brid.gy, eg
https://fed.brid.gy/snarfed.org . When we started adding new protocols, we
switched to per-protocol subdomains, eg https://web.brid.gy/snarfed.org .
However, we need to preserve the old users' actor ids as is. So, this property
tracks which subdomain a given Web user's AP actor uses.
pull/741/head
Ryan Barrett 2023-11-30 15:43:38 -08:00
rodzic c617e9ad7b
commit d33832be1c
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
4 zmienionych plików z 48 dodań i 9 usunięć

Wyświetl plik

@ -1916,6 +1916,8 @@ http://this/404s
self.assertFalse(self.user.is_web_url('https://other.com/'))
def test_handle_as(self, *_):
self.user.ap_subdomain = 'web'
self.assertEqual('@user.com@user.com', self.user.handle_as(ActivityPub))
self.user.obj = Object(id='a', as2={'type': 'Person'})
@ -1937,6 +1939,9 @@ http://this/404s
self.assertEqual('fake:handle:user.com', self.user.handle_as(Fake))
self.assertEqual('user.com.web.brid.gy', self.user.handle_as('atproto'))
self.user.ap_subdomain = 'fed'
self.assertEqual('@user.com@fed.brid.gy', self.user.handle_as(ActivityPub))
def test_ap_actor(self, *_):
self.assertEqual('http://localhost/user.com', self.user.ap_actor())

Wyświetl plik

@ -49,6 +49,7 @@ WEBFINGER = {
'href': 'https://web.brid.gy/ap/sharedInbox',
}, {
'rel': 'http://ostatus.org/schema/1.0/subscribe',
# TODO: genericize
'template': 'https://fed.brid.gy/web/user.com?url={uri}',
}],
}
@ -66,11 +67,11 @@ WEBFINGER_NO_HCARD = {
}, {
'rel': 'self',
'type': 'application/activity+json',
'href': 'http://localhost/user.com',
'href': 'https://web.brid.gy/user.com',
}, {
'rel': 'inbox',
'type': 'application/activity+json',
'href': 'http://localhost/user.com/inbox',
'href': 'https://web.brid.gy/user.com/inbox',
}, {
'rel': 'sharedInbox',
'type': 'application/activity+json',
@ -162,6 +163,20 @@ class WebfingerTest(TestCase):
self.assertEqual('application/jrd+json', got.headers['Content-Type'])
self.assert_equals(WEBFINGER, got.json)
def test_webfinger_web_subdomain_redirects(self):
path = '/.well-known/webfinger?resource=user.com@user.com'
got = self.client.get(path, base_url='https://fed.brid.gy/')
self.assertEqual(302, got.status_code)
self.assertEqual(f'https://web.brid.gy{path}', got.headers['Location'])
self.user.ap_subdomain = 'fed'
self.user.put()
got = self.client.get(path, base_url='https://web.brid.gy/')
self.assertEqual(302, got.status_code)
self.assertEqual(f'https://fed.brid.gy{path}', got.headers['Location'])
def test_user_infer_protocol_from_resource_subdomain(self):
got = self.client.get(
'/.well-known/webfinger?resource=acct:fake:handle:user@fake.brid.gy',
@ -298,8 +313,10 @@ class WebfingerTest(TestCase):
expected = copy.deepcopy(WEBFINGER_NO_HCARD)
expected['subject'] = 'acct:user.com@web.brid.gy'
got = self.client.get('/.well-known/webfinger?resource=acct:user.com@fed.brid.gy',
headers={'Accept': 'application/json'})
got = self.client.get(
'/.well-known/webfinger?resource=acct:user.com@web.brid.gy',
headers={'Accept': 'application/json'},
base_url='https://web.brid.gy/')
self.assertEqual(200, got.status_code)
self.assertEqual(expected, got.json)

9
web.py
Wyświetl plik

@ -76,6 +76,13 @@ class Web(User, Protocol):
redirects_error = ndb.TextProperty()
has_hcard = ndb.BooleanProperty()
# Originally, BF served Web users' AP actor ids on fed.brid.gy, eg
# https://fed.brid.gy/snarfed.org . When we started adding new protocols, we
# switched to per-protocol subdomains, eg https://web.brid.gy/snarfed.org .
# However, we need to preserve the old users' actor ids as is. So, this
# property tracks which subdomain a given Web user's AP actor uses.
ap_subdomain = ndb.StringProperty(choices=['fed', 'web'], default='web')
@classmethod
def _get_kind(cls):
return 'MagicKey'
@ -114,7 +121,7 @@ class Web(User, Protocol):
"""Special case ActivityPub to use custom username."""
if to_proto in ('activitypub', 'ap', PROTOCOLS['ap']):
return (f'@{self.username()}@{self.key.id()}' if self.has_redirects
else f'@{self.key.id()}@{self.ABBREV}{SUPERDOMAIN}')
else f'@{self.key.id()}@{self.ap_subdomain}{SUPERDOMAIN}')
return super().handle_as(to_proto)

Wyświetl plik

@ -4,16 +4,17 @@
* https://tools.ietf.org/html/rfc7033
"""
import logging
import urllib.parse
from urllib.parse import urljoin, urlparse
from flask import g, render_template, request
from flask import g, redirect, render_template, request
from granary import as2
from oauth_dropins.webutil import flask_util, util
from oauth_dropins.webutil.flask_util import error, flash
from oauth_dropins.webutil.flask_util import error, flash, Found
from oauth_dropins.webutil.util import json_dumps, json_loads
import activitypub
import common
from common import LOCAL_DOMAINS, SUPERDOMAIN
from flask_app import app, cache
from protocol import Protocol
@ -55,7 +56,7 @@ class Webfinger(flask_util.XrdOrJrd):
id = user
allow_indirect = True
except ValueError:
id = urllib.parse.urlparse(resource).netloc or resource
id = urlparse(resource).netloc or resource
if not cls:
cls = Protocol.for_request(fed='web')
@ -89,6 +90,15 @@ class Webfinger(flask_util.XrdOrJrd):
if not user:
error(f'No {cls.LABEL} user found for {id}', status=404)
# backward compatibility for initial Web users whose AP actor ids are on
# fed.brid.gy, not web.brid.gy
subdomain = request.host.split('.')[0]
if (user.LABEL == 'web'
and subdomain not in (LOCAL_DOMAINS + (user.ap_subdomain,))):
url = urljoin(f'https://{user.ap_subdomain}{common.SUPERDOMAIN}/',
request.full_path)
raise Found(location=url)
actor = user.obj.as1 if user.obj and user.obj.as1 else {}
logger.info(f'Generating WebFinger data for {user.key}')
logger.info(f'AS1 actor: {actor}')