component AP gateway: HTML redirection:

when a request is done on AP endpoint and `Accept` header is not set to
`application/json`, the request can now be redirected to a configurable URL.
This commit is contained in:
Goffi 2022-11-11 13:51:20 +01:00
commit 020da52ddd
2 changed files with 61 additions and 5 deletions

View file

@ -20,7 +20,6 @@ import base64
import calendar
import hashlib
import json
from os import access
from pathlib import Path
from pprint import pformat
import re
@ -33,7 +32,6 @@ from typing import (
Optional,
Set,
Tuple,
Type,
Union,
overload,
)
@ -266,6 +264,27 @@ class APGateway:
self.host.memory.getConfig(CONF_SECTION, "auto_mentions", C.BOOL_TRUE)
)
html_redirect: Dict[str, Union[str, dict]] = self.host.memory.getConfig(
CONF_SECTION, 'html_redirect_dict', {}
)
self.html_redirect: Dict[str, List[dict]] = {}
for url_type, target in html_redirect.items():
if isinstance(target, str):
target = {"url": target}
elif not isinstance(target, dict):
raise exceptions.ConfigError(
f"html_redirect target must be a URL or a dict, not {target!r}"
)
filters = target.setdefault("filters", {})
if "url" not in target:
log.warning(f"invalid HTML redirection, missing target URL: {target}")
continue
# a slash in the url_type is a syntactic shortcut to have a node filter
if "/" in url_type:
url_type, node_filter = url_type.split("/", 1)
filters["node"] = node_filter
self.html_redirect.setdefault(url_type, []).append(target)
# HTTP server launch
self.server = HTTPServer(self)
if connection_type == 'http':
@ -361,7 +380,7 @@ class APGateway:
if resp.code == 404:
raise exceptions.NotFound(f"Can't find resource at {url}")
else:
msg = f"HTTP error {resp.code}: {text}"
msg = f"HTTP error {resp.code} (url: {url}): {text}"
raise exceptions.ExternalRequestError(msg)
try:
return await treq.json_content(resp)

View file

@ -26,6 +26,7 @@ import unicodedata
from twisted.web import http, resource as web_resource, server
from twisted.web import static
from twisted.web import util as web_util
from twisted.python import failure
from twisted.internet import defer
from twisted.words.protocols.jabber import jid, error
@ -40,8 +41,8 @@ from sat.tools.common import date_utils, uri
from sat.memory.sqla_mapping import SubscriptionState
from .constants import (
NS_AP, CONTENT_TYPE_AP, TYPE_ACTOR, TYPE_INBOX, TYPE_SHARED_INBOX, TYPE_OUTBOX,
TYPE_EVENT, AP_REQUEST_TYPES, PAGE_SIZE, ACTIVITY_TYPES_LOWER,
NS_AP, MEDIA_TYPE_AP, CONTENT_TYPE_AP, TYPE_ACTOR, TYPE_INBOX, TYPE_SHARED_INBOX,
TYPE_OUTBOX, TYPE_EVENT, AP_REQUEST_TYPES, PAGE_SIZE, ACTIVITY_TYPES_LOWER,
ACTIVIY_NO_ACCOUNT_ALLOWED, SIGN_HEADERS, HS2019, SIGN_EXP, TYPE_FOLLOWERS,
TYPE_FOLLOWING, TYPE_ITEM, TYPE_LIKE, TYPE_REACTION, ST_AP_CACHE
)
@ -949,6 +950,42 @@ class HTTPAPGServer(web_resource.Resource):
path
)
request_type, extra_args = self.apg.parseAPURL(ap_url)
if ((request.getHeader("accept") != MEDIA_TYPE_AP
and request_type in self.apg.html_redirect)):
# this is not a AP request, and we have a redirections for it
kw = {}
if extra_args:
kw["jid"], kw["node"] = await self.apg.getJIDAndNode(extra_args[0])
kw["jid_user"] = kw["jid"].user
if kw["node"] is None:
kw["node"] = self.apg._m.namespace
if len(extra_args) > 1:
kw["item"] = extra_args[1]
else:
kw["item"] = ""
else:
kw["jid"], kw["jid_user"], kw["node"], kw["item"] = "", "", "", ""
redirections = self.apg.html_redirect[request_type]
for redirection in redirections:
filters = redirection["filters"]
if not filters:
break
# if we have filter, they must all match
elif all(v in kw[k] for k,v in filters.items()):
break
else:
# no redirection is matching
redirection = None
if redirection is not None:
kw = {k: parse.quote(str(v), safe="") for k,v in kw.items()}
target_url = redirection["url"].format(**kw)
content = web_util.redirectTo(target_url.encode(), request)
request.write(content)
request.finish()
return
if len(extra_args) == 0:
if request_type != "shared_inbox":
raise exceptions.DataError(f"Invalid request type: {request_type!r}")