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
|
|
|
|
2021-07-08 04:02:13 +00:00
|
|
|
from flask import redirect, request
|
2019-01-04 15:17:45 +00:00
|
|
|
from granary import as2, microformats2
|
|
|
|
import mf2util
|
2021-07-18 04:22:13 +00:00
|
|
|
from oauth_dropins.webutil import flask_util, util
|
2021-08-06 17:29:25 +00:00
|
|
|
from oauth_dropins.webutil.flask_util import error
|
2019-12-25 07:26:58 +00:00
|
|
|
from oauth_dropins.webutil.util import json_dumps
|
2021-07-08 04:02:13 +00:00
|
|
|
from werkzeug.exceptions import abort
|
2018-10-14 14:42:28 +00:00
|
|
|
|
2021-07-08 04:02:13 +00:00
|
|
|
from app import app, cache
|
2018-10-14 14:42:28 +00:00
|
|
|
import common
|
2021-03-07 15:36:34 +00:00
|
|
|
from models import MagicKey
|
2018-10-14 14:42:28 +00:00
|
|
|
|
2021-07-08 14:25:44 +00:00
|
|
|
CACHE_TIME = datetime.timedelta(seconds=15)
|
|
|
|
|
2019-04-16 15:02:19 +00:00
|
|
|
|
2021-07-11 23:30:14 +00:00
|
|
|
@app.get(r'/r/<path:to>')
|
2021-08-28 14:18:46 +00:00
|
|
|
@flask_util.cached(cache, CACHE_TIME)
|
2021-08-06 17:28:56 +00:00
|
|
|
def redir(to):
|
2021-07-08 04:02:13 +00:00
|
|
|
"""301 redirect to the embedded fully qualified URL.
|
2018-10-14 14:42:28 +00:00
|
|
|
|
|
|
|
e.g. redirects /r/https://foo.com/bar?baz to https://foo.com/bar?baz
|
|
|
|
"""
|
2021-07-08 04:02:13 +00:00
|
|
|
if request.args:
|
|
|
|
to += '?' + urllib.parse.urlencode(request.args)
|
|
|
|
# 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)
|
|
|
|
|
|
|
|
if not to.startswith('http://') and not to.startswith('https://'):
|
2021-08-06 17:29:25 +00:00
|
|
|
error(f'Expected fully qualified URL; got {to}')
|
2021-07-08 04:02:13 +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:
|
2021-09-02 02:48:37 +00:00
|
|
|
if domain and MagicKey.get_by_id(domain):
|
2021-07-08 04:02:13 +00:00
|
|
|
logging.info(f'Found MagicKey for domain {domain}')
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
logging.info(f'No user found for any of {domains}; returning 404')
|
|
|
|
abort(404)
|
|
|
|
|
|
|
|
# poor man's conneg, only handle single Accept values, not multiple with
|
|
|
|
# priorities.
|
|
|
|
if request.headers.get('Accept') in (common.CONTENT_TYPE_AS2,
|
|
|
|
common.CONTENT_TYPE_AS2_LD):
|
|
|
|
return convert_to_as2(to)
|
|
|
|
|
|
|
|
# redirect
|
|
|
|
logging.info('redirecting to %s', to)
|
|
|
|
return redirect(to, code=301)
|
|
|
|
|
|
|
|
|
|
|
|
def convert_to_as2(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
|
|
|
|
"""
|
|
|
|
mf2 = util.fetch_mf2(url)
|
|
|
|
entry = mf2util.find_first_entry(mf2, ['h-entry'])
|
|
|
|
logging.info('Parsed mf2 for %s: %s', mf2['url'], json_dumps(entry, indent=2))
|
|
|
|
|
|
|
|
obj = common.postprocess_as2(as2.from_as1(microformats2.json_to_object(entry)))
|
|
|
|
logging.info('Returning: %s', json_dumps(obj, indent=2))
|
|
|
|
|
|
|
|
return obj, {
|
|
|
|
'Content-Type': common.CONTENT_TYPE_AS2,
|
|
|
|
'Access-Control-Allow-Origin': '*',
|
|
|
|
}
|