2018-10-14 14:42:28 +00:00
|
|
|
"""Simple endpoint that redirects to the embedded fully qualified URL.
|
|
|
|
|
2019-01-04 15:17:45 +00:00
|
|
|
May also instead fetch and convert to AS2, depending on conneg.
|
|
|
|
|
2018-10-14 14:42:28 +00:00
|
|
|
Used to wrap ActivityPub ids with the fed.brid.gy domain so that Mastodon
|
|
|
|
accepts them. Background:
|
|
|
|
|
|
|
|
https://github.com/snarfed/bridgy-fed/issues/16#issuecomment-424799599
|
|
|
|
https://github.com/tootsuite/mastodon/pull/6219#issuecomment-429142747
|
|
|
|
"""
|
2019-04-16 15:02:19 +00:00
|
|
|
import datetime
|
|
|
|
import logging
|
2021-06-29 05:52:04 +00:00
|
|
|
import re
|
2021-03-07 15:36:34 +00:00
|
|
|
import urllib.parse
|
2018-10-14 14:42:28 +00:00
|
|
|
|
2019-01-04 15:17:45 +00:00
|
|
|
from granary import as2, microformats2
|
|
|
|
import mf2util
|
2019-10-04 04:08:26 +00:00
|
|
|
from oauth_dropins.webutil import util
|
2019-11-13 05:28:44 +00:00
|
|
|
from oauth_dropins.webutil.handlers import cache_response
|
2019-12-25 07:26:58 +00:00
|
|
|
from oauth_dropins.webutil.util import json_dumps
|
2019-10-08 14:34:36 +00:00
|
|
|
import ujson as json
|
2018-10-14 14:42:28 +00:00
|
|
|
import webapp2
|
|
|
|
|
|
|
|
import common
|
2021-03-07 15:36:34 +00:00
|
|
|
from models import MagicKey
|
2018-10-14 14:42:28 +00:00
|
|
|
|
2019-04-16 15:02:19 +00:00
|
|
|
CACHE_TIME = datetime.timedelta(seconds=15)
|
|
|
|
|
2018-10-14 14:42:28 +00:00
|
|
|
|
2020-01-31 15:38:58 +00:00
|
|
|
class RedirectHandler(common.Handler):
|
2018-10-14 14:42:28 +00:00
|
|
|
"""301 redirects to the embedded fully qualified URL.
|
|
|
|
|
|
|
|
e.g. redirects /r/https://foo.com/bar?baz to https://foo.com/bar?baz
|
|
|
|
"""
|
2019-11-13 05:28:44 +00:00
|
|
|
@cache_response(CACHE_TIME)
|
2018-10-14 14:42:28 +00:00
|
|
|
def get(self):
|
|
|
|
assert self.request.path_qs.startswith('/r/')
|
|
|
|
to = self.request.path_qs[3:]
|
2021-06-29 05:52:04 +00:00
|
|
|
|
|
|
|
# some browsers collapse repeated /s in the path down to a single slash.
|
|
|
|
# if that happened to this URL, expand it back to two /s.
|
|
|
|
to = re.sub(r'^(https?:/)([^/])', r'\1/\2', to)
|
|
|
|
|
2018-10-14 14:42:28 +00:00
|
|
|
if not to.startswith('http://') and not to.startswith('https://'):
|
2020-01-31 15:38:58 +00:00
|
|
|
self.error('Expected fully qualified URL; got %s' % to)
|
2019-01-04 15:17:45 +00:00
|
|
|
|
2021-03-07 15:36:34 +00:00
|
|
|
# check that we've seen this domain before so we're not an open redirect
|
|
|
|
domains = set((util.domain_from_link(to),
|
|
|
|
urllib.parse.urlparse(to).hostname))
|
|
|
|
for domain in domains:
|
|
|
|
if MagicKey.get_by_id(domain):
|
|
|
|
logging.info(f'Found MagicKey for domain {domain}')
|
|
|
|
break
|
|
|
|
else:
|
2021-03-12 04:31:48 +00:00
|
|
|
logging.info(f'No user found for any of {domains}; returning 404')
|
|
|
|
self.abort(404)
|
2021-03-07 15:36:34 +00:00
|
|
|
|
2019-01-04 15:17:45 +00:00
|
|
|
# poor man's conneg, only handle single Accept values, not multiple with
|
|
|
|
# priorities.
|
|
|
|
if self.request.headers.get('Accept') in (common.CONTENT_TYPE_AS2,
|
|
|
|
common.CONTENT_TYPE_AS2_LD):
|
|
|
|
return self.convert_to_as2(to)
|
|
|
|
|
|
|
|
# redirect
|
2018-10-14 14:42:28 +00:00
|
|
|
logging.info('redirecting to %s', to)
|
2021-03-07 05:47:08 +00:00
|
|
|
self.redirect(to, permanent=True)
|
2018-10-14 14:42:28 +00:00
|
|
|
|
2019-01-04 15:17:45 +00:00
|
|
|
def convert_to_as2(self, url):
|
|
|
|
"""Fetch a URL as HTML, convert it to AS2, and return it.
|
|
|
|
|
|
|
|
Currently mainly for Pixelfed.
|
|
|
|
https://github.com/snarfed/bridgy-fed/issues/39
|
|
|
|
"""
|
2019-10-04 04:08:26 +00:00
|
|
|
mf2 = util.fetch_mf2(url)
|
2019-01-04 15:17:45 +00:00
|
|
|
entry = mf2util.find_first_entry(mf2, ['h-entry'])
|
2019-12-25 07:26:58 +00:00
|
|
|
logging.info('Parsed mf2 for %s: %s', mf2['url'], json_dumps(entry, indent=2))
|
2019-01-04 15:17:45 +00:00
|
|
|
|
2020-01-31 15:38:58 +00:00
|
|
|
obj = self.postprocess_as2(as2.from_as1(microformats2.json_to_object(entry)))
|
2019-12-25 07:26:58 +00:00
|
|
|
logging.info('Returning: %s', json_dumps(obj, indent=2))
|
2019-01-04 15:17:45 +00:00
|
|
|
|
|
|
|
self.response.headers.update({
|
|
|
|
'Content-Type': common.CONTENT_TYPE_AS2,
|
|
|
|
'Access-Control-Allow-Origin': '*',
|
|
|
|
})
|
2019-12-25 07:26:58 +00:00
|
|
|
self.response.write(json_dumps(obj, indent=2))
|
2019-01-04 15:17:45 +00:00
|
|
|
|
2018-10-14 14:42:28 +00:00
|
|
|
|
2019-12-26 06:20:57 +00:00
|
|
|
ROUTES = [
|
2018-10-14 14:42:28 +00:00
|
|
|
(r'/r/.+', RedirectHandler),
|
2019-12-26 06:20:57 +00:00
|
|
|
]
|