2017-08-20 14:28:40 +00:00
|
|
|
"""Handles requests for Salmon endpoints: actors, inbox, etc.
|
2017-08-20 18:35:14 +00:00
|
|
|
|
|
|
|
https://github.com/salmon-protocol/salmon-protocol/blob/master/draft-panzer-salmon-00.html
|
|
|
|
https://github.com/salmon-protocol/salmon-protocol/blob/master/draft-panzer-magicsig-01.html
|
2017-08-20 14:28:40 +00:00
|
|
|
"""
|
|
|
|
import logging
|
2021-07-09 05:50:33 +00:00
|
|
|
import re
|
2017-10-17 05:02:37 +00:00
|
|
|
from xml.etree.ElementTree import ParseError
|
2017-08-20 14:28:40 +00:00
|
|
|
|
2017-08-20 18:33:00 +00:00
|
|
|
from django_salmon import magicsigs, utils
|
2021-07-09 05:50:33 +00:00
|
|
|
from flask import request
|
2017-10-15 23:44:29 +00:00
|
|
|
from granary import atom
|
|
|
|
from oauth_dropins.webutil import util
|
2021-08-06 17:29:25 +00:00
|
|
|
from oauth_dropins.webutil.flask_util import error
|
2017-08-20 14:28:40 +00:00
|
|
|
|
2021-07-09 05:50:33 +00:00
|
|
|
from app import app
|
2017-08-20 14:28:40 +00:00
|
|
|
import common
|
|
|
|
|
2022-02-12 06:38:56 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2017-08-20 18:33:00 +00:00
|
|
|
# from django_salmon.feeds
|
|
|
|
ATOM_NS = 'http://www.w3.org/2005/Atom'
|
|
|
|
ATOM_THREADING_NS = 'http://purl.org/syndication/thread/1.0'
|
|
|
|
|
2017-10-17 05:21:13 +00:00
|
|
|
SUPPORTED_VERBS = (
|
|
|
|
'checkin',
|
|
|
|
'create',
|
2017-10-24 04:23:33 +00:00
|
|
|
'favorite',
|
2017-10-17 05:21:13 +00:00
|
|
|
'like',
|
|
|
|
'share',
|
|
|
|
'tag',
|
|
|
|
'update',
|
|
|
|
)
|
|
|
|
|
2017-08-20 14:28:40 +00:00
|
|
|
|
2021-07-12 21:19:56 +00:00
|
|
|
@app.post(f'/<regex("{common.ACCT_RE}|{common.DOMAIN_RE}"):acct>/salmon')
|
2021-07-09 05:50:33 +00:00
|
|
|
def slap(acct):
|
2017-09-03 19:54:10 +00:00
|
|
|
"""Accepts POSTs to /[ACCT]/salmon and converts to outbound webmentions."""
|
2021-07-09 05:50:33 +00:00
|
|
|
body = request.get_data(as_text=True)
|
2022-02-12 06:38:56 +00:00
|
|
|
logger.info(f'Got: {body}')
|
2017-08-20 14:28:40 +00:00
|
|
|
|
2021-07-09 05:50:33 +00:00
|
|
|
try:
|
|
|
|
parsed = utils.parse_magic_envelope(body)
|
|
|
|
except ParseError as e:
|
2021-08-06 17:29:25 +00:00
|
|
|
error('Could not parse POST body as XML', exc_info=True)
|
2021-07-09 05:50:33 +00:00
|
|
|
data = parsed['data']
|
2022-02-12 06:38:56 +00:00
|
|
|
logger.info(f'Decoded: {data}')
|
2017-10-17 05:21:13 +00:00
|
|
|
|
2021-07-09 05:50:33 +00:00
|
|
|
# check that we support this activity type
|
|
|
|
try:
|
2017-10-16 14:13:43 +00:00
|
|
|
activity = atom.atom_to_activity(data)
|
2021-07-09 05:50:33 +00:00
|
|
|
except ParseError as e:
|
2021-08-06 17:29:25 +00:00
|
|
|
error('Could not parse envelope data as XML', exc_info=True)
|
2021-07-09 05:50:33 +00:00
|
|
|
|
|
|
|
verb = activity.get('verb')
|
|
|
|
if verb and verb not in SUPPORTED_VERBS:
|
2021-08-06 17:29:25 +00:00
|
|
|
error(f'Sorry, {verb} activities are not supported yet.', status=501)
|
2021-07-09 05:50:33 +00:00
|
|
|
|
|
|
|
# verify author and signature
|
|
|
|
author = util.get_url(activity.get('actor'))
|
|
|
|
if ':' not in author:
|
|
|
|
author = f'acct:{author}'
|
|
|
|
elif not author.startswith('acct:'):
|
2021-08-06 17:29:25 +00:00
|
|
|
error(f'Author URI {author} has unsupported scheme; expected acct:')
|
2021-07-09 05:50:33 +00:00
|
|
|
|
2022-02-12 06:38:56 +00:00
|
|
|
logger.info(f'Fetching Salmon key for {author}')
|
2021-07-09 05:50:33 +00:00
|
|
|
if not magicsigs.verify(data, parsed['sig'], author_uri=author):
|
2021-08-06 17:29:25 +00:00
|
|
|
error('Could not verify magic signature.')
|
2022-02-12 06:38:56 +00:00
|
|
|
logger.info('Verified magic signature.')
|
2021-07-09 05:50:33 +00:00
|
|
|
|
|
|
|
# Verify that the timestamp is recent. Required by spec.
|
|
|
|
# I get that this helps prevent spam, but in practice it's a bit silly,
|
|
|
|
# and other major implementations don't (e.g. Mastodon), so forget it.
|
|
|
|
#
|
|
|
|
# updated = utils.parse_updated_from_atom(data)
|
|
|
|
# if not utils.verify_timestamp(updated):
|
2021-08-06 17:29:25 +00:00
|
|
|
# error('Timestamp is more than 1h old.')
|
2021-07-09 05:50:33 +00:00
|
|
|
|
|
|
|
# send webmentions to each target
|
|
|
|
activity = atom.atom_to_activity(data)
|
|
|
|
common.send_webmentions(activity, protocol='ostatus', source_atom=data)
|
|
|
|
return ''
|