kopia lustrzana https://github.com/snarfed/bridgy-fed
rodzic
e3f1431018
commit
46f039af63
45
common.py
45
common.py
|
@ -14,7 +14,7 @@ from oauth_dropins.webutil.flask_util import error
|
|||
import requests
|
||||
from werkzeug.exceptions import BadGateway
|
||||
|
||||
from models import Activity
|
||||
from models import Activity, Domain
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -242,22 +242,23 @@ def send_webmentions(activity_wrapped, proxy=None, **activity_props):
|
|||
error(msg, status=int(errors[0][0] or 502))
|
||||
|
||||
|
||||
def postprocess_as2(activity, target=None, domain=None):
|
||||
def postprocess_as2(activity, domain=None, target=None):
|
||||
"""Prepare an AS2 object to be served or sent via ActivityPub.
|
||||
|
||||
Args:
|
||||
activity: dict, AS2 object or activity
|
||||
domain: :class:`Domain`, required. populated into actor.id and
|
||||
publicKey fields if needed.
|
||||
target: dict, AS2 object, optional. The target of activity's inReplyTo or
|
||||
Like/Announce/etc object, if any.
|
||||
domain: :class:`models.Domain`, optional. populated into publicKey field
|
||||
if provided.
|
||||
"""
|
||||
assert domain
|
||||
type = activity.get('type')
|
||||
|
||||
# actor objects
|
||||
if type == 'Person':
|
||||
postprocess_as2_actor(activity)
|
||||
if not activity.get('publicKey') and domain:
|
||||
postprocess_as2_actor(activity, domain)
|
||||
if not activity.get('publicKey'):
|
||||
# underspecified, inferred from this issue and Mastodon's implementation:
|
||||
# https://github.com/w3c/activitypub/issues/203#issuecomment-297553229
|
||||
# https://github.com/tootsuite/mastodon/blob/bc2c263504e584e154384ecc2d804aeb1afb1ba3/app/services/activitypub/process_account_service.rb#L77
|
||||
|
@ -275,7 +276,7 @@ def postprocess_as2(activity, target=None, domain=None):
|
|||
|
||||
for actor in (util.get_list(activity, 'attributedTo') +
|
||||
util.get_list(activity, 'actor')):
|
||||
postprocess_as2_actor(actor)
|
||||
postprocess_as2_actor(actor, domain)
|
||||
|
||||
# inReplyTo: singly valued, prefer id over url
|
||||
target_id = target.get('id') if target else None
|
||||
|
@ -344,7 +345,9 @@ def postprocess_as2(activity, target=None, domain=None):
|
|||
# to public, since Mastodon interprets to public as public, cc public as unlisted:
|
||||
# https://socialhub.activitypub.rocks/t/visibility-to-cc-mapping/284
|
||||
# https://wordsmith.social/falkreon/securing-activitypub
|
||||
activity.setdefault('to', []).append(AS2_PUBLIC_AUDIENCE)
|
||||
to = activity.setdefault('to', [])
|
||||
if AS2_PUBLIC_AUDIENCE not in to:
|
||||
to.append(AS2_PUBLIC_AUDIENCE)
|
||||
|
||||
# wrap articles and notes in a Create activity
|
||||
if type in ('Article', 'Note'):
|
||||
|
@ -352,29 +355,37 @@ def postprocess_as2(activity, target=None, domain=None):
|
|||
'@context': as2.CONTEXT,
|
||||
'type': 'Create',
|
||||
'id': f'{activity["id"]}#bridgy-fed-create',
|
||||
'actor': postprocess_as2_actor({}, domain),
|
||||
'object': activity,
|
||||
}
|
||||
|
||||
return util.trim_nulls(activity)
|
||||
|
||||
|
||||
def postprocess_as2_actor(actor):
|
||||
def postprocess_as2_actor(actor, domain=None):
|
||||
"""Prepare an AS2 actor object to be served or sent via ActivityPub.
|
||||
|
||||
Modifies actor in place.
|
||||
|
||||
Args:
|
||||
actor: dict, AS2 actor object
|
||||
domain: :class:`Domain`
|
||||
|
||||
Returns:
|
||||
actor dict
|
||||
"""
|
||||
url = actor.get('url')
|
||||
if url:
|
||||
domain = urllib.parse.urlparse(url).netloc
|
||||
actor.update({
|
||||
'id': request.host_url + domain,
|
||||
'url': redirect_wrap(url),
|
||||
'preferredUsername': domain,
|
||||
})
|
||||
url = actor.get('url') or f'https://{domain.key.id()}/'
|
||||
domain_str = urllib.parse.urlparse(url).netloc
|
||||
|
||||
actor.setdefault('id', request.host_url + domain_str)
|
||||
actor.update({
|
||||
'url': redirect_wrap(url),
|
||||
'preferredUsername': domain_str,
|
||||
})
|
||||
|
||||
# required by pixelfed. https://github.com/snarfed/bridgy-fed/issues/39
|
||||
actor.setdefault('summary', '')
|
||||
return actor
|
||||
|
||||
|
||||
def redirect_wrap(url):
|
||||
|
|
19
redirect.py
19
redirect.py
|
@ -51,9 +51,11 @@ def redir(to):
|
|||
util.domain_from_link(to, minimize=False),
|
||||
urllib.parse.urlparse(to).hostname))
|
||||
for domain in domains:
|
||||
if domain and Domain.get_by_id(domain):
|
||||
logger.info(f'Found Domain for domain {domain}')
|
||||
break
|
||||
if domain:
|
||||
entity = Domain.get_by_id(domain)
|
||||
if entity:
|
||||
logger.info(f'Found Domain for domain {domain}')
|
||||
break
|
||||
else:
|
||||
logger.info(f'No user found for any of {domains}; returning 404')
|
||||
abort(404)
|
||||
|
@ -62,24 +64,29 @@ def redir(to):
|
|||
# priorities.
|
||||
if request.headers.get('Accept') in (common.CONTENT_TYPE_AS2,
|
||||
common.CONTENT_TYPE_AS2_LD):
|
||||
return convert_to_as2(to)
|
||||
return convert_to_as2(to, entity)
|
||||
|
||||
# redirect
|
||||
logger.info(f'redirecting to {to}')
|
||||
return redirect(to, code=301)
|
||||
|
||||
|
||||
def convert_to_as2(url):
|
||||
def convert_to_as2(url, domain):
|
||||
"""Fetch a URL as HTML, convert it to AS2, and return it.
|
||||
|
||||
Currently mainly for Pixelfed.
|
||||
https://github.com/snarfed/bridgy-fed/issues/39
|
||||
|
||||
Args:
|
||||
url: str
|
||||
domain: :class:`Domain`
|
||||
"""
|
||||
mf2 = util.fetch_mf2(url)
|
||||
entry = mf2util.find_first_entry(mf2, ['h-entry'])
|
||||
logger.info(f"Parsed mf2 for {mf2['url']}: {json_dumps(entry, indent=2)}")
|
||||
|
||||
obj = common.postprocess_as2(as2.from_as1(microformats2.json_to_object(entry)))
|
||||
obj = common.postprocess_as2(as2.from_as1(microformats2.json_to_object(entry)),
|
||||
domain)
|
||||
logger.info(f'Returning: {json_dumps(obj, indent=2)}')
|
||||
|
||||
return obj, {
|
||||
|
|
|
@ -9,6 +9,7 @@ from werkzeug.exceptions import BadGateway
|
|||
|
||||
from app import app
|
||||
import common
|
||||
from models import Domain
|
||||
from . import testutil
|
||||
|
||||
HTML = requests_response('<html></html>', headers={
|
||||
|
@ -67,11 +68,56 @@ class CommonTest(testutil.TestCase):
|
|||
|
||||
def test_postprocess_as2_multiple_in_reply_tos(self):
|
||||
with app.test_request_context('/'):
|
||||
self.assertEqual({
|
||||
self.assert_equals({
|
||||
'id': 'http://localhost/r/xyz',
|
||||
'inReplyTo': 'foo',
|
||||
'to': [common.AS2_PUBLIC_AUDIENCE],
|
||||
}, common.postprocess_as2({
|
||||
'id': 'xyz',
|
||||
'inReplyTo': ['foo', 'bar'],
|
||||
}))
|
||||
}, domain=Domain(id='foo.com')))
|
||||
|
||||
def test_postprocess_as2_actor_attributedTo(self):
|
||||
with app.test_request_context('/'):
|
||||
self.assert_equals({
|
||||
'actor': {
|
||||
'id': 'baj',
|
||||
'preferredUsername': 'foo.com',
|
||||
'url': 'http://localhost/r/https://foo.com/',
|
||||
},
|
||||
'attributedTo': [{
|
||||
'id': 'bar',
|
||||
'preferredUsername': 'foo.com',
|
||||
'url': 'http://localhost/r/https://foo.com/',
|
||||
}, {
|
||||
'id': 'baz',
|
||||
'preferredUsername': 'foo.com',
|
||||
'url': 'http://localhost/r/https://foo.com/',
|
||||
}],
|
||||
'to': [common.AS2_PUBLIC_AUDIENCE],
|
||||
}, common.postprocess_as2({
|
||||
'attributedTo': [{'id': 'bar'}, {'id': 'baz'}],
|
||||
'actor': {'id': 'baj'},
|
||||
}, domain=Domain(id='foo.com')))
|
||||
|
||||
def test_postprocess_as2_note(self):
|
||||
with app.test_request_context('/'):
|
||||
self.assert_equals({
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'id': 'http://localhost/r/xyz#bridgy-fed-create',
|
||||
'type': 'Create',
|
||||
'actor': {
|
||||
'id': 'http://localhost/foo.com',
|
||||
'url': 'http://localhost/r/https://foo.com/',
|
||||
'preferredUsername': 'foo.com'
|
||||
},
|
||||
'object': {
|
||||
'id': 'http://localhost/r/xyz',
|
||||
'type': 'Note',
|
||||
'to': [common.AS2_PUBLIC_AUDIENCE],
|
||||
},
|
||||
}, common.postprocess_as2({
|
||||
'id': 'xyz',
|
||||
'type': 'Note',
|
||||
}, domain=Domain(id='foo.com')))
|
||||
|
||||
|
|
|
@ -158,6 +158,11 @@ class WebmentionTest(testutil.TestCase):
|
|||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'type': 'Create',
|
||||
'id': 'http://localhost/r/http://a/reply#bridgy-fed-create',
|
||||
'actor': {
|
||||
'id': 'http://localhost/a',
|
||||
'url': 'http://localhost/r/https://a/',
|
||||
'preferredUsername': 'a',
|
||||
},
|
||||
'object': {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'type': 'Note',
|
||||
|
@ -240,6 +245,11 @@ class WebmentionTest(testutil.TestCase):
|
|||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'type': 'Create',
|
||||
'id': 'http://localhost/r/http://orig/post#bridgy-fed-create',
|
||||
'actor': {
|
||||
'id': 'http://localhost/orig',
|
||||
'url': 'http://localhost/r/https://orig/',
|
||||
'preferredUsername': 'orig',
|
||||
},
|
||||
'object': {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'type': 'Note',
|
||||
|
|
Ładowanie…
Reference in New Issue