kopia lustrzana https://github.com/snarfed/bridgy-fed
AP users: start to replace external with indirect, starting with webfinger
#512circle-datastore-transactions
rodzic
624355d85a
commit
93f621aaf5
16
models.py
16
models.py
|
@ -21,7 +21,7 @@ from oauth_dropins.webutil.util import json_dumps, json_loads
|
|||
import requests
|
||||
|
||||
import common
|
||||
from common import base64_to_long, long_to_base64
|
||||
from common import base64_to_long, long_to_base64, redirect_wrap
|
||||
|
||||
# maps string label to Protocol subclass. populated by ProtocolUserMeta.
|
||||
# seed with old and upcoming protocols that don't have their own classes (yet).
|
||||
|
@ -187,7 +187,7 @@ class User(StringIdModel, metaclass=ProtocolUserMeta):
|
|||
"""
|
||||
domain = self.key.id()
|
||||
|
||||
if self.actor_as2:
|
||||
if self.actor_as2 and self.direct:
|
||||
for url in [u.get('value') if isinstance(u, dict) else u
|
||||
for u in util.get_list(self.actor_as2, 'url')]:
|
||||
if url and url.startswith('acct:'):
|
||||
|
@ -199,13 +199,21 @@ class User(StringIdModel, metaclass=ProtocolUserMeta):
|
|||
logger.info(f'Defaulting username to domain {domain}')
|
||||
return domain
|
||||
|
||||
# TODO(#512): move to activitypub.py?
|
||||
def address(self):
|
||||
"""Returns this user's ActivityPub address, eg '@me@foo.com'."""
|
||||
return f'@{self.username()}@{self.key.id()}'
|
||||
if self.direct:
|
||||
return f'@{self.username()}@{self.key.id()}'
|
||||
else:
|
||||
return f'@{self.key.id()}@{request.host}'
|
||||
|
||||
# TODO(#512): move to activitypub.py?
|
||||
def actor_id(self):
|
||||
"""Returns this user's AS2 actor id, eg 'https://fed.brid.gy/foo.com'."""
|
||||
return common.host_url(self.key.id())
|
||||
if self.direct:
|
||||
return common.host_url(self.key.id())
|
||||
else:
|
||||
return redirect_wrap(self.homepage)
|
||||
|
||||
def is_homepage(self, url):
|
||||
"""Returns True if the given URL points to this user's home page."""
|
||||
|
|
2
pages.py
2
pages.py
|
@ -76,7 +76,7 @@ def check_web_site():
|
|||
@app.get(f'/user/<regex("{DOMAIN_RE}"):domain>')
|
||||
def user(domain):
|
||||
g.user = Web.get_by_id(domain)
|
||||
if not g.user:
|
||||
if not g.user or not g.user.direct:
|
||||
return USER_NOT_FOUND_HTML, 404
|
||||
elif g.user.key.id() != domain:
|
||||
return redirect(f'/user/{g.user.key.id()}', code=301)
|
||||
|
|
|
@ -87,9 +87,15 @@ class UserTest(TestCase):
|
|||
g.user.actor_as2 = {'url': ['http://foo', 'acct:bar@foo', 'acct:baz@y.z']}
|
||||
self.assertEqual('@baz@y.z', g.user.address())
|
||||
|
||||
g.user.direct = False
|
||||
self.assertEqual('@y.z@localhost', g.user.address())
|
||||
|
||||
def test_actor_id(self):
|
||||
self.assertEqual('http://localhost/y.z', g.user.actor_id())
|
||||
|
||||
g.user.direct = False
|
||||
self.assertEqual('http://localhost/r/https://y.z/', g.user.actor_id())
|
||||
|
||||
|
||||
class ObjectTest(TestCase):
|
||||
def setUp(self):
|
||||
|
|
|
@ -48,6 +48,12 @@ class PagesTest(TestCase):
|
|||
got = self.client.get('/user/bar.com')
|
||||
self.assert_equals(404, got.status_code)
|
||||
|
||||
def test_user_not_direct(self):
|
||||
self.user.direct = False
|
||||
self.user.put()
|
||||
got = self.client.get('/user/user.com')
|
||||
self.assert_equals(404, got.status_code)
|
||||
|
||||
def test_user_use_instead(self):
|
||||
bar = self.make_user('bar.com')
|
||||
bar.use_instead = self.user.key
|
||||
|
|
|
@ -9,6 +9,8 @@ from oauth_dropins.webutil.testutil import requests_response
|
|||
|
||||
import common
|
||||
from . import testutil
|
||||
from web import Web
|
||||
|
||||
from .test_web import ACTOR_HTML
|
||||
|
||||
WEBFINGER = {
|
||||
|
@ -203,7 +205,7 @@ class WebfingerTest(testutil.TestCase):
|
|||
self.assertEqual(404, got.status_code)
|
||||
|
||||
@patch('requests.get')
|
||||
def test_webfinger_external_user_fetch_creates_user(self, mock_get):
|
||||
def test_webfinger_external_user_fetch_create_user(self, mock_get):
|
||||
self.user.key.delete()
|
||||
mock_get.return_value = requests_response(ACTOR_HTML)
|
||||
|
||||
|
@ -216,6 +218,9 @@ class WebfingerTest(testutil.TestCase):
|
|||
self.assertEqual(200, got.status_code)
|
||||
self.assertEqual(expected, got.json)
|
||||
|
||||
user = Web.get_by_id('user.com')
|
||||
assert not user.direct
|
||||
|
||||
def test_webfinger_fed_brid_gy(self):
|
||||
got = self.client.get('/.well-known/webfinger?resource=http://localhost/')
|
||||
self.assertEqual(400, got.status_code, got.get_data(as_text=True))
|
||||
|
|
|
@ -131,6 +131,7 @@ class TestCase(unittest.TestCase, testutil.Asserts):
|
|||
def make_user(domain, cls=Web, **kwargs):
|
||||
"""Reuse RSA key across Users because generating it is expensive."""
|
||||
user = cls(id=domain,
|
||||
direct=True,
|
||||
mod=global_user.mod,
|
||||
public_exponent=global_user.public_exponent,
|
||||
private_exponent=global_user.private_exponent,
|
||||
|
|
34
webfinger.py
34
webfinger.py
|
@ -33,33 +33,31 @@ class Actor(flask_util.XrdOrJrd):
|
|||
def template_prefix(self):
|
||||
return 'webfinger_user'
|
||||
|
||||
def template_vars(self, domain=None, external=False):
|
||||
def template_vars(self, domain=None, allow_indirect=False):
|
||||
"""
|
||||
Args:
|
||||
domain: str, user domain
|
||||
external: bool, whether this may be an external user, ie without a
|
||||
stored :class:`User`
|
||||
allow_indirect: bool, whether this may be an indirect user, ie without
|
||||
an existing :class:`User`
|
||||
"""
|
||||
logger.debug(f'Headers: {list(request.headers.items())}')
|
||||
|
||||
if domain.split('.')[-1] in NON_TLDS:
|
||||
error(f"{domain} doesn't look like a domain", status=404)
|
||||
|
||||
g.user = Web.get_by_id(domain)
|
||||
if g.user:
|
||||
actor = g.user.to_as1() or {}
|
||||
homepage = g.user.homepage
|
||||
handle = g.user.address()
|
||||
actor_id = g.user.actor_id()
|
||||
elif external:
|
||||
g.external_user = homepage = f'https://{domain}/'
|
||||
obj = Web.load(g.external_user)
|
||||
actor = obj.as1
|
||||
handle = f'@{domain}@{request.host}'
|
||||
actor_id = common.redirect_wrap(homepage)
|
||||
if allow_indirect:
|
||||
g.user = Web.get_or_create(domain)
|
||||
else:
|
||||
g.user = Web.get_by_id(domain)
|
||||
|
||||
if not g.user:
|
||||
error(f'No user or web site found for {domain}', status=404)
|
||||
|
||||
actor = g.user.to_as1() or {}
|
||||
homepage = g.user.homepage
|
||||
handle = g.user.address()
|
||||
actor_id = g.user.actor_id()
|
||||
|
||||
logger.info(f'Generating WebFinger data for {domain}')
|
||||
logger.info(f'AS1 actor: {actor}')
|
||||
urls = util.dedupe_urls(util.get_list(actor, 'urls') +
|
||||
|
@ -141,16 +139,16 @@ class Webfinger(Actor):
|
|||
if resource in ('', '/', f'acct:{host}', f'acct:@{host}'):
|
||||
error('Expected other domain, not fed.brid.gy')
|
||||
|
||||
external = False
|
||||
allow_indirect = False
|
||||
try:
|
||||
user, domain = util.parse_acct_uri(resource)
|
||||
if domain in common.DOMAINS:
|
||||
domain = user
|
||||
external = True
|
||||
allow_indirect=True
|
||||
except ValueError:
|
||||
domain = urllib.parse.urlparse(resource).netloc or resource
|
||||
|
||||
return super().template_vars(domain=domain, external=external)
|
||||
return super().template_vars(domain=domain, allow_indirect=allow_indirect)
|
||||
|
||||
|
||||
class HostMeta(flask_util.XrdOrJrd):
|
||||
|
|
Ładowanie…
Reference in New Issue