unfollow via 410, in progress

unfollow
Ryan Barrett 2022-12-21 14:49:22 -08:00
rodzic 7ac1006021
commit b701dfbf57
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
2 zmienionych plików z 74 dodań i 3 usunięć

Wyświetl plik

@ -888,6 +888,38 @@ class WebmentionTest(testutil.TestCase):
self.assert_equals('a', followers[0].src)
self.assert_equals('https://foo.com/about-me', followers[0].dest)
def test_activitypub_unfollow(self, mock_get, mock_post):
Activity.get_or_create('http://a/follow', 'https://foo.com/about-me',
status='complete')
Follower.get_or_create('https://foo.com/about-me', 'a')
self.follow.status_code = 410
mock_get.side_effect = [self.follow, self.actor]
mock_post.return_value = requests_response('abc xyz')
got = self.client.post('/webmention', data={
'source': 'http://a/follow',
'target': 'https://fed.brid.gy/',
})
self.assertEqual(200, got.status_code)
args, kwargs = mock_post.call_args
self.assert_equals(('https://foo.com/inbox',), args)
self.assert_equals({
'@context': 'https://www.w3.org/ns/activitystreams',
'type': 'Undo',
'object': self.follow_as2,
}, json_loads(kwargs['data']))
activity = Activity.get_by_id('http://a/follow#deleted http://followee/')
self.assertEqual(['a'], activity.domain)
self.assertEqual('out', activity.direction)
self.assertEqual('activitypub', activity.protocol)
self.assertEqual('complete', activity.status)
self.assertIsNone(activity.source_mf2)
self.assertEqual(0, Follower.query().count())
def test_activitypub_error_fragment_missing(self, mock_get, mock_post):
mock_get.side_effect = [self.follow_fragment]

Wyświetl plik

@ -50,13 +50,24 @@ class Webmention(View):
# fetch source page
source = flask_util.get_required_param('source')
logger.info(f'webmention from {util.domain_from_link(source, minimize=False)}')
self.source_domain = util.domain_from_link(source, minimize=False)
logger.info(f'webmention from {self.source_domain}')
try:
source_resp = util.requests_get(source, gateway=True)
source_resp = util.requests_get(source)
source_resp.raise_for_status()
except ValueError as e:
error(f'Bad source URL: {source}: {e}')
except BaseException as e:
status, body = util.interpret_http_exception(e)
if status == '410':
if self.try_activitypub_unfollow(source_resp.url or source):
return 'OK'
else:
error(f"410 HTTP response is only supported for unfollow; couldn't find a matching follow post to unfollow")
else:
error(f'Could not fetch source URL {source}: {e}', status=502)
self.source_url = source_resp.url or source
self.source_domain = urllib.parse.urlparse(self.source_url).netloc.split(':')[0]
fragment = urllib.parse.urlparse(self.source_url).fragment
self.source_mf2 = util.parse_mf2(source_resp, id=fragment)
@ -129,6 +140,7 @@ class Webmention(View):
items[0].get('properties')
).get('content')
orig_content = content(json_loads(activity.source_mf2))
new_content = content(self.source_mf2)
if orig_content and new_content and orig_content == new_content:
@ -166,6 +178,33 @@ class Webmention(View):
else:
return str(error)
def try_activitypub_unfollow(self, follow_url):
"""Attempts ActivityPub Undo Follow.
Args:
follow_url: str
Returns: boolean, whether we successfully unfollowed
"""
logging.info(f'Attempting to undo follow {follow_url}')
for activity in Activity.query().filter(
Activity.key > Key('Response', follow_url + ' '),
Activity.key < Key('Response', follow_url + chr(ord(' ') + 1))):
if activity.status == 'complete':
logging.info(f'Found follow activity {activity.key}')
follower = Follower.get_by_id(
Follower._id(activity.target(), self.source_domain))
if follower:
logging.info(f'Deactivating existing Follower {follower.key}')
follower.status = 'inactive'
follower.put()
STATE: construct Activity here, send AP Undo
{
'@context': 'https://www.w3.org/ns/activitystreams',
'type': 'Undo',
})
return True
def _activitypub_targets(self):
"""
Returns: list of (Activity, string inbox URL)