kopia lustrzana https://github.com/snarfed/bridgy-fed
rodzic
8176cd1f56
commit
060ad96752
|
@ -15,7 +15,7 @@ from oauth_dropins.webutil.util import json_dumps, json_loads
|
|||
from app import app, cache
|
||||
import common
|
||||
from common import CACHE_TIME, redirect_unwrap, redirect_wrap
|
||||
from models import Activity, Follower, User
|
||||
from models import Follower, Object, User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -119,10 +119,11 @@ def inbox(domain=None):
|
|||
return accept_follow(activity, activity_unwrapped, user)
|
||||
|
||||
# send webmentions to each target
|
||||
as1 = as2.to_as1(activity)
|
||||
source_as2 = json_dumps(activity_unwrapped)
|
||||
sent = common.send_webmentions(as1, proxy=True, protocol='activitypub',
|
||||
source_as2=source_as2)
|
||||
activity_as2 = json_dumps(activity_unwrapped)
|
||||
activity_as1 = json_dumps(as2.to_as1(activity_unwrapped))
|
||||
sent = common.send_webmentions(as2.to_as1(activity), proxy=True,
|
||||
source_protocol='activitypub',
|
||||
as2=activity_as2, as1=activity_as1)
|
||||
|
||||
if not sent and type in ('Create', 'Announce'):
|
||||
# check that this activity is public. only do this check for Creates,
|
||||
|
@ -142,10 +143,10 @@ def inbox(domain=None):
|
|||
domains = [f.src for f in
|
||||
Follower.query(Follower.dest == actor_id,
|
||||
projection=[Follower.src]).fetch()]
|
||||
key = Activity(source=source, target='Public', direction='in',
|
||||
protocol='activitypub', domain=domains, status='complete',
|
||||
source_as2=source_as2).put()
|
||||
logging.info(f'Wrote Activity {key} with {len(domains)} follower domains')
|
||||
|
||||
key = Object(id=source, source_protocol='activitypub', domains=domains,
|
||||
status='complete', as2=activity_as2, as1=activity_as1).put()
|
||||
logging.info(f'Wrote Object {key} with {len(domains)} follower domains')
|
||||
|
||||
return ''
|
||||
|
||||
|
@ -207,8 +208,9 @@ def accept_follow(follow, follow_unwrapped, user):
|
|||
resp = common.signed_post(inbox, data=accept, user=user)
|
||||
|
||||
# send webmention
|
||||
common.send_webmentions(as2.to_as1(follow), proxy=True, protocol='activitypub',
|
||||
source_as2=json_dumps(follow_unwrapped))
|
||||
common.send_webmentions(as2.to_as1(follow), proxy=True, source_protocol='activitypub',
|
||||
as2=json_dumps(follow_unwrapped),
|
||||
as1=json_dumps(as2.to_as1(follow_unwrapped)))
|
||||
|
||||
return resp.text, resp.status_code
|
||||
|
||||
|
|
21
common.py
21
common.py
|
@ -22,7 +22,7 @@ from oauth_dropins.webutil.util import json_dumps, json_loads
|
|||
import requests
|
||||
from werkzeug.exceptions import BadGateway
|
||||
|
||||
from models import Activity, Follower, User
|
||||
from models import Follower, Object, User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -243,11 +243,11 @@ def remove_blocklisted(urls):
|
|||
util.domain_from_link(u), DOMAIN_BLOCKLIST)]
|
||||
|
||||
|
||||
def send_webmentions(activity_wrapped, proxy=None, **activity_props):
|
||||
def send_webmentions(activity_wrapped, proxy=None, **object_props):
|
||||
"""Sends webmentions for an incoming ActivityPub inbox delivery.
|
||||
Args:
|
||||
activity_wrapped: dict, AS1 activity
|
||||
activity_props: passed through to the newly created Activity entities
|
||||
object_props: passed through to the newly created Object entities
|
||||
|
||||
Returns: boolean, True if any webmentions were sent, False otherwise
|
||||
"""
|
||||
|
@ -292,7 +292,7 @@ def send_webmentions(activity_wrapped, proxy=None, **activity_props):
|
|||
|
||||
logger.info(f'targets: {targets}')
|
||||
|
||||
# send webmentions and store Activitys
|
||||
# send webmentions and store Objects
|
||||
errors = [] # stores (code, body) tuples
|
||||
for target in targets:
|
||||
domain = util.domain_from_link(target, minimize=False)
|
||||
|
@ -300,10 +300,9 @@ def send_webmentions(activity_wrapped, proxy=None, **activity_props):
|
|||
logger.info(f'Skipping same-domain webmention from {source} to {target}')
|
||||
continue
|
||||
|
||||
activity = Activity(source=source, target=target, direction='in',
|
||||
domain=[domain], **activity_props)
|
||||
activity.put()
|
||||
wm_source = (activity.proxy_url()
|
||||
obj = Object(id=source, domains=[domain], **object_props)
|
||||
obj.put()
|
||||
wm_source = (obj.proxy_url()
|
||||
if verb in ('follow', 'like', 'share') or proxy
|
||||
else source)
|
||||
logger.info(f'Sending webmention from {wm_source} to {target}')
|
||||
|
@ -312,14 +311,14 @@ def send_webmentions(activity_wrapped, proxy=None, **activity_props):
|
|||
endpoint = webmention.discover(target).endpoint
|
||||
if endpoint:
|
||||
webmention.send(endpoint, wm_source, target)
|
||||
activity.status = 'complete'
|
||||
obj.status = 'complete'
|
||||
logger.info('Success!')
|
||||
else:
|
||||
activity.status = 'ignored'
|
||||
obj.status = 'ignored'
|
||||
logger.info('Ignoring.')
|
||||
except BaseException as e:
|
||||
errors.append(util.interpret_http_exception(e))
|
||||
activity.put()
|
||||
obj.put()
|
||||
|
||||
if errors:
|
||||
msg = 'Errors: ' + ', '.join(f'{code} {body}' for code, body in errors)
|
||||
|
|
|
@ -16,7 +16,7 @@ from urllib3.exceptions import ReadTimeoutError
|
|||
|
||||
import activitypub
|
||||
import common
|
||||
from models import Follower, User, Activity
|
||||
from models import Follower, Object, User
|
||||
from . import testutil
|
||||
|
||||
REPLY_OBJECT = {
|
||||
|
@ -298,13 +298,13 @@ class ActivityPubTest(testutil.TestCase):
|
|||
def test_inbox_reply_create_activity(self, *mocks):
|
||||
self._test_inbox_reply(REPLY, REPLY, *mocks)
|
||||
|
||||
def _test_inbox_reply(self, as2, expected_as2, mock_head, mock_get, mock_post):
|
||||
def _test_inbox_reply(self, reply, expected_as2, mock_head, mock_get, mock_post):
|
||||
mock_head.return_value = requests_response(url='http://or.ig/post')
|
||||
mock_get.return_value = requests_response(
|
||||
'<html><head><link rel="webmention" href="/webmention"></html>')
|
||||
mock_post.return_value = requests_response()
|
||||
|
||||
got = self.client.post('/foo.com/inbox', json=as2)
|
||||
got = self.client.post('/foo.com/inbox', json=reply)
|
||||
self.assertEqual(200, got.status_code, got.get_data(as_text=True))
|
||||
self.assert_req(mock_get, 'http://or.ig/post')
|
||||
self.assert_req(
|
||||
|
@ -313,17 +313,17 @@ class ActivityPubTest(testutil.TestCase):
|
|||
headers={'Accept': '*/*'},
|
||||
allow_redirects=False,
|
||||
data={
|
||||
'source': 'http://localhost/render?source=http%3A%2F%2Fth.is%2Freply&target=http%3A%2F%2For.ig%2Fpost',
|
||||
'source': 'http://localhost/render?id=http%3A%2F%2Fth.is%2Freply',
|
||||
'target': 'http://or.ig/post',
|
||||
},
|
||||
)
|
||||
|
||||
activity = Activity.get_by_id('http://th.is/reply http://or.ig/post')
|
||||
self.assertEqual(['or.ig'], activity.domain)
|
||||
self.assertEqual('in', activity.direction)
|
||||
self.assertEqual('activitypub', activity.protocol)
|
||||
self.assertEqual('complete', activity.status)
|
||||
self.assertEqual(expected_as2, json_loads(activity.source_as2))
|
||||
obj = Object.get_by_id('http://th.is/reply')
|
||||
self.assertEqual(['or.ig'], obj.domains)
|
||||
self.assertEqual('activitypub', obj.source_protocol)
|
||||
self.assertEqual('complete', obj.status)
|
||||
self.assertEqual(expected_as2, json_loads(obj.as2))
|
||||
self.assertEqual(as2.to_as1(expected_as2), json_loads(obj.as1))
|
||||
|
||||
def test_inbox_reply_to_self_domain(self, mock_head, mock_get, mock_post):
|
||||
self._test_inbox_ignore_reply_to('http://localhost/th.is',
|
||||
|
@ -344,9 +344,9 @@ class ActivityPubTest(testutil.TestCase):
|
|||
|
||||
mock_get.assert_not_called()
|
||||
mock_post.assert_not_called()
|
||||
self.assertEqual(0, Activity.query().count())
|
||||
self.assertEqual(0, Object.query().count())
|
||||
|
||||
def test_inbox_create_activity(self, mock_head, mock_get, mock_post):
|
||||
def test_inbox_create_obj(self, mock_head, mock_get, mock_post):
|
||||
Follower.get_or_create(ACTOR['id'], 'foo.com')
|
||||
Follower.get_or_create('http://other/actor', 'bar.com')
|
||||
Follower.get_or_create(ACTOR['id'], 'baz.com')
|
||||
|
@ -359,15 +359,16 @@ class ActivityPubTest(testutil.TestCase):
|
|||
got = self.client.post('/foo.com/inbox', json=NOTE)
|
||||
self.assertEqual(200, got.status_code, got.get_data(as_text=True))
|
||||
|
||||
activity = Activity.get_by_id('http://th.is/note/as2 Public')
|
||||
self.assertEqual('in', activity.direction)
|
||||
self.assertEqual('activitypub', activity.protocol)
|
||||
self.assertEqual('complete', activity.status)
|
||||
expected_as2 = copy.deepcopy(NOTE)
|
||||
expected_as2['actor'] = ACTOR
|
||||
self.assertEqual(common.redirect_unwrap(expected_as2),
|
||||
json_loads(activity.source_as2))
|
||||
self.assert_equals(['foo.com', 'baz.com'], activity.domain)
|
||||
obj = Object.get_by_id('http://th.is/note/as2')
|
||||
self.assertEqual('activitypub', obj.source_protocol)
|
||||
self.assertEqual('complete', obj.status)
|
||||
expected_as2 = common.redirect_unwrap({
|
||||
**NOTE,
|
||||
'actor': ACTOR,
|
||||
})
|
||||
self.assertEqual(expected_as2, json_loads(obj.as2))
|
||||
self.assertEqual(as2.to_as1(expected_as2), json_loads(obj.as1))
|
||||
self.assert_equals(['foo.com', 'baz.com'], obj.domains)
|
||||
|
||||
def test_inbox_not_public(self, mock_head, mock_get, mock_post):
|
||||
Follower.get_or_create(ACTOR['id'], 'foo.com')
|
||||
|
@ -381,7 +382,7 @@ class ActivityPubTest(testutil.TestCase):
|
|||
with self.client:
|
||||
got = self.client.post('/foo.com/inbox', json=not_public)
|
||||
self.assertEqual(200, got.status_code, got.get_data(as_text=True))
|
||||
self.assertEqual(0, Activity.query().count())
|
||||
self.assertEqual(0, Object.query().count())
|
||||
|
||||
def test_inbox_mention_object(self, *mocks):
|
||||
self._test_inbox_mention(MENTION_OBJECT, *mocks)
|
||||
|
@ -389,14 +390,14 @@ class ActivityPubTest(testutil.TestCase):
|
|||
def test_inbox_mention_create_activity(self, *mocks):
|
||||
self._test_inbox_mention(MENTION, *mocks)
|
||||
|
||||
def _test_inbox_mention(self, as2, mock_head, mock_get, mock_post):
|
||||
def _test_inbox_mention(self, mention, mock_head, mock_get, mock_post):
|
||||
mock_head.return_value = requests_response(url='http://tar.get')
|
||||
mock_get.return_value = requests_response(
|
||||
'<html><head><link rel="webmention" href="/webmention"></html>')
|
||||
mock_post.return_value = requests_response()
|
||||
|
||||
with self.client:
|
||||
got = self.client.post('/foo.com/inbox', json=as2)
|
||||
got = self.client.post('/foo.com/inbox', json=mention)
|
||||
self.assertEqual(200, got.status_code, got.get_data(as_text=True))
|
||||
self.assert_req(mock_get, 'http://tar.get/')
|
||||
self.assert_req(
|
||||
|
@ -405,17 +406,19 @@ class ActivityPubTest(testutil.TestCase):
|
|||
headers={'Accept': '*/*'},
|
||||
allow_redirects=False,
|
||||
data={
|
||||
'source': 'http://localhost/render?source=http%3A%2F%2Fth.is%2Fmention&target=http%3A%2F%2Ftar.get%2F',
|
||||
'source': 'http://localhost/render?id=http%3A%2F%2Fth.is%2Fmention',
|
||||
'target': 'http://tar.get/',
|
||||
},
|
||||
)
|
||||
|
||||
activity = Activity.get_by_id('http://th.is/mention http://tar.get/')
|
||||
self.assertEqual(['tar.get'], activity.domain)
|
||||
self.assertEqual('in', activity.direction)
|
||||
self.assertEqual('activitypub', activity.protocol)
|
||||
self.assertEqual('complete', activity.status)
|
||||
self.assertEqual(common.redirect_unwrap(as2), json_loads(activity.source_as2))
|
||||
obj = Object.get_by_id('http://th.is/mention')
|
||||
self.assertEqual(['tar.get'], obj.domains)
|
||||
self.assertEqual('activitypub', obj.source_protocol)
|
||||
self.assertEqual('complete', obj.status)
|
||||
|
||||
expected_as2 = common.redirect_unwrap(mention)
|
||||
self.assertEqual(expected_as2, json_loads(obj.as2))
|
||||
self.assertEqual(as2.to_as1(expected_as2), json_loads(obj.as1))
|
||||
|
||||
def test_inbox_like(self, mock_head, mock_get, mock_post):
|
||||
mock_head.return_value = requests_response(url='http://or.ig/post')
|
||||
|
@ -440,26 +443,27 @@ class ActivityPubTest(testutil.TestCase):
|
|||
self.assertEqual(('http://or.ig/webmention',), args)
|
||||
self.assertEqual({
|
||||
# TODO
|
||||
'source': 'http://localhost/render?source=http%3A%2F%2Fth.is%2Flike__ok&target=http%3A%2F%2For.ig%2Fpost',
|
||||
'source': 'http://localhost/render?id=http%3A%2F%2Fth.is%2Flike%23ok',
|
||||
'target': 'http://or.ig/post',
|
||||
}, kwargs['data'])
|
||||
|
||||
activity = Activity.get_by_id('http://th.is/like__ok http://or.ig/post')
|
||||
self.assertEqual(['or.ig'], activity.domain)
|
||||
self.assertEqual('in', activity.direction)
|
||||
self.assertEqual('activitypub', activity.protocol)
|
||||
self.assertEqual('complete', activity.status)
|
||||
self.assertEqual(LIKE_WITH_ACTOR, json_loads(activity.source_as2))
|
||||
obj = Object.get_by_id('http://th.is/like#ok')
|
||||
self.assertEqual(['or.ig'], obj.domains)
|
||||
self.assertEqual('activitypub', obj.source_protocol)
|
||||
self.assertEqual('complete', obj.status)
|
||||
self.assertEqual(LIKE_WITH_ACTOR, json_loads(obj.as2))
|
||||
self.assertEqual(as2.to_as1(LIKE_WITH_ACTOR), json_loads(obj.as1))
|
||||
|
||||
def test_inbox_follow_accept_with_id(self, mock_head, mock_get, mock_post):
|
||||
self._test_inbox_follow_accept(FOLLOW_WRAPPED, ACCEPT,
|
||||
mock_head, mock_get, mock_post)
|
||||
|
||||
activity = Activity.query().get()
|
||||
obj = Object.query().get()
|
||||
follow = copy.deepcopy(FOLLOW_WITH_ACTOR)
|
||||
follow['url'] = 'https://mastodon.social/users/swentel#followed-https://www.realize.be/'
|
||||
|
||||
self.assertEqual(follow, json_loads(activity.source_as2))
|
||||
self.assertEqual(follow, json_loads(obj.as2))
|
||||
self.assertEqual(as2.to_as1(follow), json_loads(obj.as1))
|
||||
|
||||
follower = Follower.query().get()
|
||||
self.assertEqual(FOLLOW_WRAPPED_WITH_ACTOR, json_loads(follower.last_follow))
|
||||
|
@ -489,13 +493,14 @@ class ActivityPubTest(testutil.TestCase):
|
|||
})
|
||||
self.assertEqual(follow, json_loads(follower.last_follow))
|
||||
|
||||
activity = Activity.query().get()
|
||||
obj = Object.query().get()
|
||||
follow.update({
|
||||
'actor': FOLLOW_WITH_ACTOR['actor'],
|
||||
'object': unwrapped_user,
|
||||
'url': 'https://mastodon.social/users/swentel#followed-https://www.realize.be/',
|
||||
})
|
||||
self.assertEqual(follow, json_loads(activity.source_as2))
|
||||
self.assertEqual(follow, json_loads(obj.as2))
|
||||
self.assertEqual(as2.to_as1(follow), json_loads(obj.as1))
|
||||
|
||||
def _test_inbox_follow_accept(self, follow_as2, accept_as2,
|
||||
mock_head, mock_get, mock_post):
|
||||
|
@ -526,15 +531,14 @@ class ActivityPubTest(testutil.TestCase):
|
|||
args, kwargs = mock_post.call_args_list[1]
|
||||
self.assertEqual(('https://www.realize.be/webmention',), args)
|
||||
self.assertEqual({
|
||||
'source': 'http://localhost/render?source=https%3A%2F%2Fmastodon.social%2F6d1a&target=https%3A%2F%2Fwww.realize.be%2F',
|
||||
'source': 'http://localhost/render?id=https%3A%2F%2Fmastodon.social%2F6d1a',
|
||||
'target': 'https://www.realize.be/',
|
||||
}, kwargs['data'])
|
||||
|
||||
activity = Activity.get_by_id('https://mastodon.social/6d1a https://www.realize.be/')
|
||||
self.assertEqual(['www.realize.be'], activity.domain)
|
||||
self.assertEqual('in', activity.direction)
|
||||
self.assertEqual('activitypub', activity.protocol)
|
||||
self.assertEqual('complete', activity.status)
|
||||
obj = Object.get_by_id('https://mastodon.social/6d1a')
|
||||
self.assertEqual(['www.realize.be'], obj.domains)
|
||||
self.assertEqual('activitypub', obj.source_protocol)
|
||||
self.assertEqual('complete', obj.status)
|
||||
|
||||
# check that we stored a Follower object
|
||||
follower = Follower.get_by_id(f'www.realize.be {FOLLOW["actor"]}')
|
||||
|
@ -642,7 +646,7 @@ class ActivityPubTest(testutil.TestCase):
|
|||
# bad object, should ignore activity
|
||||
self.assertEqual(200, got.status_code)
|
||||
mock_post.assert_not_called()
|
||||
self.assertEqual(0, Activity.query().count())
|
||||
self.assertEqual(0, Object.query().count())
|
||||
|
||||
def test_individual_inbox_delete_actor_noop(self, mock_head, mock_get, mock_post):
|
||||
"""Deletes sent to individual users' inboxes do nothing."""
|
||||
|
@ -705,11 +709,10 @@ class ActivityPubTest(testutil.TestCase):
|
|||
got = self.client.post('/foo.com/inbox', json=LIKE)
|
||||
self.assertEqual(200, got.status_code)
|
||||
|
||||
activity = Activity.get_by_id('http://th.is/like__ok http://or.ig/post')
|
||||
self.assertEqual(['or.ig'], activity.domain)
|
||||
self.assertEqual('in', activity.direction)
|
||||
self.assertEqual('activitypub', activity.protocol)
|
||||
self.assertEqual('ignored', activity.status)
|
||||
obj = Object.get_by_id('http://th.is/like#ok')
|
||||
self.assertEqual(['or.ig'], obj.domains)
|
||||
self.assertEqual('activitypub', obj.source_protocol)
|
||||
self.assertEqual('ignored', obj.status)
|
||||
|
||||
def test_followers_collection_unknown_user(self, *args):
|
||||
resp = self.client.get('/foo.com/followers')
|
||||
|
|
Ładowanie…
Reference in New Issue