# HG changeset patch # User Goffi # Date 1668171080 -3600 # Node ID 6fa4ca0c047e59012d76fc415e7f2cd345dea694 # Parent db45d49518f6bf97322f0d7e51c1a316e47afe07 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. diff -r db45d49518f6 -r 6fa4ca0c047e sat/plugins/plugin_comp_ap_gateway/__init__.py --- a/sat/plugins/plugin_comp_ap_gateway/__init__.py Thu Nov 10 15:16:43 2022 +0100 +++ b/sat/plugins/plugin_comp_ap_gateway/__init__.py Fri Nov 11 13:51:20 2022 +0100 @@ -20,7 +20,6 @@ import calendar import hashlib import json -from os import access from pathlib import Path from pprint import pformat import re @@ -33,7 +32,6 @@ Optional, Set, Tuple, - Type, Union, overload, ) @@ -266,6 +264,27 @@ 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 @@ 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) diff -r db45d49518f6 -r 6fa4ca0c047e sat/plugins/plugin_comp_ap_gateway/http_server.py --- a/sat/plugins/plugin_comp_ap_gateway/http_server.py Thu Nov 10 15:16:43 2022 +0100 +++ b/sat/plugins/plugin_comp_ap_gateway/http_server.py Fri Nov 11 13:51:20 2022 +0100 @@ -26,6 +26,7 @@ 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.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 @@ 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}")