From 63c19e1df949dd8fc15defc1942e2405c5426958 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Wed, 24 Aug 2016 11:55:57 +0100 Subject: Move 3PU/3PL lookup APIs into /thirdparty containing entity --- synapse/appservice/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'synapse/appservice/api.py') diff --git a/synapse/appservice/api.py b/synapse/appservice/api.py index 066127b666..07ec33b9a1 100644 --- a/synapse/appservice/api.py +++ b/synapse/appservice/api.py @@ -97,10 +97,10 @@ class ApplicationServiceApi(SimpleHttpClient): @defer.inlineCallbacks def query_3pe(self, service, kind, protocol, fields): if kind == ThirdPartyEntityKind.USER: - uri = "%s/3pu/%s" % (service.url, urllib.quote(protocol)) + uri = "%s/thirdparty/user/%s" % (service.url, urllib.quote(protocol)) required_field = "userid" elif kind == ThirdPartyEntityKind.LOCATION: - uri = "%s/3pl/%s" % (service.url, urllib.quote(protocol)) + uri = "%s/thirdparty/location/%s" % (service.url, urllib.quote(protocol)) required_field = "alias" else: raise ValueError( -- cgit 1.4.1 From 5474824975b5372665b0921960a0c3887a49efdb Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Thu, 25 Aug 2016 15:10:06 +0100 Subject: Actually query over AS API for 3PE lookup metadata --- synapse/appservice/api.py | 20 ++++++++++++++++++++ synapse/handlers/appservice.py | 14 +------------- 2 files changed, 21 insertions(+), 13 deletions(-) (limited to 'synapse/appservice/api.py') diff --git a/synapse/appservice/api.py b/synapse/appservice/api.py index 07ec33b9a1..1de0e7240f 100644 --- a/synapse/appservice/api.py +++ b/synapse/appservice/api.py @@ -52,6 +52,13 @@ class ApplicationServiceApi(SimpleHttpClient): pushing. """ + PROTOCOL_META = { + # TODO(paul): Declare kinds of metadata in here + "gitter": { + "user_fields": ["username"], + } + } + def __init__(self, hs): super(ApplicationServiceApi, self).__init__(hs) self.clock = hs.get_clock() @@ -131,6 +138,19 @@ class ApplicationServiceApi(SimpleHttpClient): logger.warning("query_3pe to %s threw exception %s", uri, ex) defer.returnValue([]) + @defer.inlineCallbacks + def get_3pe_protocol(self, service, protocol): + # TODO: cache + uri = "%s/thirdparty/protocol/%s" % (service.url, urllib.quote(protocol)) + try: + response = yield self.get_json(uri, {}) + defer.returnValue(response) + except Exception as ex: + logger.warning("query_3pe_protocol to %s threw exception %s", + uri, ex + ) + defer.returnValue({}) + @defer.inlineCallbacks def push_bulk(self, service, events, txn_id=None): events = self._serialize(events) diff --git a/synapse/handlers/appservice.py b/synapse/handlers/appservice.py index 1c5c5ec302..efb9da798d 100644 --- a/synapse/handlers/appservice.py +++ b/synapse/handlers/appservice.py @@ -37,13 +37,6 @@ def log_failure(failure): class ApplicationServicesHandler(object): - PROTOCOL_META = { - # TODO(paul): Declare kinds of metadata in here - "gitter": { - "user_fields": ["username"], - } - } - def __init__(self, hs): self.store = hs.get_datastore() self.is_mine_id = hs.is_mine_id @@ -195,12 +188,7 @@ class ApplicationServicesHandler(object): protocols = {} for s in services: for p in s.protocols: - if p in self.PROTOCOL_META: - protocols[p] = self.PROTOCOL_META[p] - else: - # We don't know any metadata for it, but we'd best at least - # still declare that we know it exists - protocols[p] = {} + protocols[p] = yield self.appservice_api.get_3pe_protocol(s, p) self.supported_protocols = protocols defer.returnValue(protocols) -- cgit 1.4.1 From d0b8d49f714dca232f8a970e31042686ea82b982 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Thu, 25 Aug 2016 15:45:28 +0100 Subject: Kill PROTOCOL_META since I'm not using it any more --- synapse/appservice/api.py | 7 ------- 1 file changed, 7 deletions(-) (limited to 'synapse/appservice/api.py') diff --git a/synapse/appservice/api.py b/synapse/appservice/api.py index 1de0e7240f..acef09609c 100644 --- a/synapse/appservice/api.py +++ b/synapse/appservice/api.py @@ -52,13 +52,6 @@ class ApplicationServiceApi(SimpleHttpClient): pushing. """ - PROTOCOL_META = { - # TODO(paul): Declare kinds of metadata in here - "gitter": { - "user_fields": ["username"], - } - } - def __init__(self, hs): super(ApplicationServiceApi, self).__init__(hs) self.clock = hs.get_clock() -- cgit 1.4.1 From db7283cc6bf16a5640da3f650cc30996af29d291 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Thu, 25 Aug 2016 15:56:27 +0100 Subject: Implement a ResponseCache around 3PE lookup metadata lookups --- synapse/appservice/api.py | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) (limited to 'synapse/appservice/api.py') diff --git a/synapse/appservice/api.py b/synapse/appservice/api.py index acef09609c..9de5c6e47a 100644 --- a/synapse/appservice/api.py +++ b/synapse/appservice/api.py @@ -17,6 +17,7 @@ from twisted.internet import defer from synapse.api.errors import CodeMessageException from synapse.http.client import SimpleHttpClient from synapse.events.utils import serialize_event +from synapse.util.caches.response_cache import ResponseCache from synapse.types import ThirdPartyEntityKind import logging @@ -25,6 +26,9 @@ import urllib logger = logging.getLogger(__name__) +HOUR_IN_MS = 60 * 60 * 1000 + + def _is_valid_3pe_result(r, field): if not isinstance(r, dict): return False @@ -56,6 +60,8 @@ class ApplicationServiceApi(SimpleHttpClient): super(ApplicationServiceApi, self).__init__(hs) self.clock = hs.get_clock() + self.protocol_meta_cache = ResponseCache(hs, timeout_ms=1*HOUR_IN_MS) + @defer.inlineCallbacks def query_user(self, service, user_id): uri = service.url + ("/users/%s" % urllib.quote(user_id)) @@ -131,18 +137,22 @@ class ApplicationServiceApi(SimpleHttpClient): logger.warning("query_3pe to %s threw exception %s", uri, ex) defer.returnValue([]) - @defer.inlineCallbacks def get_3pe_protocol(self, service, protocol): - # TODO: cache - uri = "%s/thirdparty/protocol/%s" % (service.url, urllib.quote(protocol)) - try: - response = yield self.get_json(uri, {}) - defer.returnValue(response) - except Exception as ex: - logger.warning("query_3pe_protocol to %s threw exception %s", - uri, ex - ) - defer.returnValue({}) + @defer.inlineCallbacks + def _get(): + uri = "%s/thirdparty/protocol/%s" % (service.url, urllib.quote(protocol)) + try: + defer.returnValue((yield self.get_json(uri, {}))) + except Exception as ex: + logger.warning("query_3pe_protocol to %s threw exception %s", + uri, ex + ) + defer.returnValue({}) + + key = (service.id, protocol) + return self.protocol_meta_cache.get(key) or ( + self.protocol_meta_cache.set(key, _get()) + ) @defer.inlineCallbacks def push_bulk(self, service, events, txn_id=None): -- cgit 1.4.1 From adf53f04cea7eb9629896769aa8ca642a0a4646e Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Thu, 25 Aug 2016 16:00:31 +0100 Subject: appease pep8 --- synapse/appservice/api.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'synapse/appservice/api.py') diff --git a/synapse/appservice/api.py b/synapse/appservice/api.py index 9de5c6e47a..d28a9dff0a 100644 --- a/synapse/appservice/api.py +++ b/synapse/appservice/api.py @@ -60,7 +60,7 @@ class ApplicationServiceApi(SimpleHttpClient): super(ApplicationServiceApi, self).__init__(hs) self.clock = hs.get_clock() - self.protocol_meta_cache = ResponseCache(hs, timeout_ms=1*HOUR_IN_MS) + self.protocol_meta_cache = ResponseCache(hs, timeout_ms=HOUR_IN_MS) @defer.inlineCallbacks def query_user(self, service, user_id): @@ -145,8 +145,7 @@ class ApplicationServiceApi(SimpleHttpClient): defer.returnValue((yield self.get_json(uri, {}))) except Exception as ex: logger.warning("query_3pe_protocol to %s threw exception %s", - uri, ex - ) + uri, ex) defer.returnValue({}) key = (service.id, protocol) -- cgit 1.4.1 From e7af8be5ae1d68ef45bfa455b989ced57e07df85 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Thu, 25 Aug 2016 18:06:29 +0100 Subject: Root the 3PE lookup API within /_matrix/app/unstable instead of at toplevel --- synapse/appservice/api.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'synapse/appservice/api.py') diff --git a/synapse/appservice/api.py b/synapse/appservice/api.py index d28a9dff0a..632dc1a4f8 100644 --- a/synapse/appservice/api.py +++ b/synapse/appservice/api.py @@ -29,6 +29,9 @@ logger = logging.getLogger(__name__) HOUR_IN_MS = 60 * 60 * 1000 +APP_SERVICE_PREFIX = "/_matrix/app/unstable" + + def _is_valid_3pe_result(r, field): if not isinstance(r, dict): return False @@ -103,16 +106,22 @@ class ApplicationServiceApi(SimpleHttpClient): @defer.inlineCallbacks def query_3pe(self, service, kind, protocol, fields): if kind == ThirdPartyEntityKind.USER: - uri = "%s/thirdparty/user/%s" % (service.url, urllib.quote(protocol)) + fragment = "user" required_field = "userid" elif kind == ThirdPartyEntityKind.LOCATION: - uri = "%s/thirdparty/location/%s" % (service.url, urllib.quote(protocol)) + fragment = "location" required_field = "alias" else: raise ValueError( "Unrecognised 'kind' argument %r to query_3pe()", kind ) + uri = "%s%s/thirdparty/%s/%s" % ( + service.url, + APP_SERVICE_PREFIX, + fragment, + urllib.quote(protocol) + ) try: response = yield self.get_json(uri, fields) if not isinstance(response, list): @@ -140,7 +149,11 @@ class ApplicationServiceApi(SimpleHttpClient): def get_3pe_protocol(self, service, protocol): @defer.inlineCallbacks def _get(): - uri = "%s/thirdparty/protocol/%s" % (service.url, urllib.quote(protocol)) + uri = "%s%s/thirdparty/protocol/%s" % ( + service.url, + APP_SERVICE_PREFIX, + urllib.quote(protocol) + ) try: defer.returnValue((yield self.get_json(uri, {}))) except Exception as ex: -- cgit 1.4.1 From 1294d4a3299faba9b5f09ec6f452dfb2ab9f5e35 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Thu, 25 Aug 2016 18:34:47 +0100 Subject: Move ThirdPartyEntityKind into api.constants so the expectation becomes that the value is significant --- synapse/api/constants.py | 5 +++++ synapse/appservice/api.py | 2 +- synapse/rest/client/v2_alpha/thirdparty.py | 2 +- synapse/types.py | 7 ------- 4 files changed, 7 insertions(+), 9 deletions(-) (limited to 'synapse/appservice/api.py') diff --git a/synapse/api/constants.py b/synapse/api/constants.py index 8cf4d6169c..a8123cddcb 100644 --- a/synapse/api/constants.py +++ b/synapse/api/constants.py @@ -85,3 +85,8 @@ class RoomCreationPreset(object): PRIVATE_CHAT = "private_chat" PUBLIC_CHAT = "public_chat" TRUSTED_PRIVATE_CHAT = "trusted_private_chat" + + +class ThirdPartyEntityKind(object): + USER = "user" + LOCATION = "location" diff --git a/synapse/appservice/api.py b/synapse/appservice/api.py index 632dc1a4f8..24253e7785 100644 --- a/synapse/appservice/api.py +++ b/synapse/appservice/api.py @@ -14,11 +14,11 @@ # limitations under the License. from twisted.internet import defer +from synapse.api.constants import ThirdPartyEntityKind from synapse.api.errors import CodeMessageException from synapse.http.client import SimpleHttpClient from synapse.events.utils import serialize_event from synapse.util.caches.response_cache import ResponseCache -from synapse.types import ThirdPartyEntityKind import logging import urllib diff --git a/synapse/rest/client/v2_alpha/thirdparty.py b/synapse/rest/client/v2_alpha/thirdparty.py index bbc3e9b962..4f6f1a7e17 100644 --- a/synapse/rest/client/v2_alpha/thirdparty.py +++ b/synapse/rest/client/v2_alpha/thirdparty.py @@ -18,8 +18,8 @@ import logging from twisted.internet import defer +from synapse.api.constants import ThirdPartyEntityKind from synapse.http.servlet import RestServlet -from synapse.types import ThirdPartyEntityKind from ._base import client_v2_patterns logger = logging.getLogger(__name__) diff --git a/synapse/types.py b/synapse/types.py index fd17ecbbe0..5349b0c450 100644 --- a/synapse/types.py +++ b/synapse/types.py @@ -269,10 +269,3 @@ class RoomStreamToken(namedtuple("_StreamToken", "topological stream")): return "t%d-%d" % (self.topological, self.stream) else: return "s%d" % (self.stream,) - - -# Some arbitrary constants used for internal API enumerations. Don't rely on -# exact values; always pass or compare symbolically -class ThirdPartyEntityKind(object): - USER = 'user' - LOCATION = 'location' -- cgit 1.4.1 From 9459137f1ed18ebd0e2a1ff728868dafcd9c9c1a Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Thu, 25 Aug 2016 18:35:38 +0100 Subject: Just sprintf the 'kind' argument into uri directly --- synapse/appservice/api.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'synapse/appservice/api.py') diff --git a/synapse/appservice/api.py b/synapse/appservice/api.py index 24253e7785..775417eb21 100644 --- a/synapse/appservice/api.py +++ b/synapse/appservice/api.py @@ -106,10 +106,8 @@ class ApplicationServiceApi(SimpleHttpClient): @defer.inlineCallbacks def query_3pe(self, service, kind, protocol, fields): if kind == ThirdPartyEntityKind.USER: - fragment = "user" required_field = "userid" elif kind == ThirdPartyEntityKind.LOCATION: - fragment = "location" required_field = "alias" else: raise ValueError( @@ -119,7 +117,7 @@ class ApplicationServiceApi(SimpleHttpClient): uri = "%s%s/thirdparty/%s/%s" % ( service.url, APP_SERVICE_PREFIX, - fragment, + kind, urllib.quote(protocol) ) try: -- cgit 1.4.1 From e82247f990d8b47fc1d151d6151d82f9d802bebb Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 30 Aug 2016 16:21:16 +0100 Subject: Allow application services to have an optional 'url' If 'url' is not specified, they will not be pushed for events or queries. This is useful for bots who simply wish to reserve large chunks of user/alias namespace, and don't care about being pushed for events. --- synapse/appservice/api.py | 11 +++++++++++ synapse/config/appservice.py | 6 ++++++ 2 files changed, 17 insertions(+) (limited to 'synapse/appservice/api.py') diff --git a/synapse/appservice/api.py b/synapse/appservice/api.py index 775417eb21..e596cb7376 100644 --- a/synapse/appservice/api.py +++ b/synapse/appservice/api.py @@ -67,6 +67,8 @@ class ApplicationServiceApi(SimpleHttpClient): @defer.inlineCallbacks def query_user(self, service, user_id): + if service.url == "": + defer.returnValue(False) uri = service.url + ("/users/%s" % urllib.quote(user_id)) response = None try: @@ -86,6 +88,8 @@ class ApplicationServiceApi(SimpleHttpClient): @defer.inlineCallbacks def query_alias(self, service, alias): + if service.url == "": + defer.returnValue(False) uri = service.url + ("/rooms/%s" % urllib.quote(alias)) response = None try: @@ -113,6 +117,8 @@ class ApplicationServiceApi(SimpleHttpClient): raise ValueError( "Unrecognised 'kind' argument %r to query_3pe()", kind ) + if service.url == "": + defer.returnValue([]) uri = "%s%s/thirdparty/%s/%s" % ( service.url, @@ -145,6 +151,8 @@ class ApplicationServiceApi(SimpleHttpClient): defer.returnValue([]) def get_3pe_protocol(self, service, protocol): + if service.url == "": + defer.returnValue({}) @defer.inlineCallbacks def _get(): uri = "%s%s/thirdparty/protocol/%s" % ( @@ -166,6 +174,9 @@ class ApplicationServiceApi(SimpleHttpClient): @defer.inlineCallbacks def push_bulk(self, service, events, txn_id=None): + if service.url == "": + defer.returnValue(True) + events = self._serialize(events) if txn_id is None: diff --git a/synapse/config/appservice.py b/synapse/config/appservice.py index dfe43b0b4c..3488a28ff2 100644 --- a/synapse/config/appservice.py +++ b/synapse/config/appservice.py @@ -132,6 +132,12 @@ def _load_appservice(hostname, as_info, config_filename): for p in protocols: if not isinstance(p, str): raise KeyError("Bad value for 'protocols' item") + + if as_info["url"] == "": + logger.info( + "(%s) Explicitly empty 'url' provided. This application service will not receive events or queries.", + config_filename, + ) return ApplicationService( token=as_info["as_token"], url=as_info["url"], -- cgit 1.4.1 From 16b652f0a375a880175b54d5d439bde6a0604dd2 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 30 Aug 2016 16:30:12 +0100 Subject: Flake8 --- synapse/appservice/api.py | 1 + synapse/config/appservice.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'synapse/appservice/api.py') diff --git a/synapse/appservice/api.py b/synapse/appservice/api.py index e596cb7376..ee63f06359 100644 --- a/synapse/appservice/api.py +++ b/synapse/appservice/api.py @@ -153,6 +153,7 @@ class ApplicationServiceApi(SimpleHttpClient): def get_3pe_protocol(self, service, protocol): if service.url == "": defer.returnValue({}) + @defer.inlineCallbacks def _get(): uri = "%s%s/thirdparty/protocol/%s" % ( diff --git a/synapse/config/appservice.py b/synapse/config/appservice.py index 3488a28ff2..5569c43b35 100644 --- a/synapse/config/appservice.py +++ b/synapse/config/appservice.py @@ -135,7 +135,8 @@ def _load_appservice(hostname, as_info, config_filename): if as_info["url"] == "": logger.info( - "(%s) Explicitly empty 'url' provided. This application service will not receive events or queries.", + "(%s) Explicitly empty 'url' provided. This application service " + + "will not receive events or queries.", config_filename, ) return ApplicationService( -- cgit 1.4.1 From 572acde4832869c8f7f4daf555296bd1164364d0 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 30 Aug 2016 17:16:00 +0100 Subject: Use None instead of the empty string Change how we validate the 'url' field as a result. --- synapse/appservice/api.py | 10 +++++----- synapse/config/appservice.py | 15 +++++++++++---- 2 files changed, 16 insertions(+), 9 deletions(-) (limited to 'synapse/appservice/api.py') diff --git a/synapse/appservice/api.py b/synapse/appservice/api.py index ee63f06359..cc4af23962 100644 --- a/synapse/appservice/api.py +++ b/synapse/appservice/api.py @@ -67,7 +67,7 @@ class ApplicationServiceApi(SimpleHttpClient): @defer.inlineCallbacks def query_user(self, service, user_id): - if service.url == "": + if service.url is None: defer.returnValue(False) uri = service.url + ("/users/%s" % urllib.quote(user_id)) response = None @@ -88,7 +88,7 @@ class ApplicationServiceApi(SimpleHttpClient): @defer.inlineCallbacks def query_alias(self, service, alias): - if service.url == "": + if service.url is None: defer.returnValue(False) uri = service.url + ("/rooms/%s" % urllib.quote(alias)) response = None @@ -117,7 +117,7 @@ class ApplicationServiceApi(SimpleHttpClient): raise ValueError( "Unrecognised 'kind' argument %r to query_3pe()", kind ) - if service.url == "": + if service.url is None: defer.returnValue([]) uri = "%s%s/thirdparty/%s/%s" % ( @@ -151,7 +151,7 @@ class ApplicationServiceApi(SimpleHttpClient): defer.returnValue([]) def get_3pe_protocol(self, service, protocol): - if service.url == "": + if service.url is None: defer.returnValue({}) @defer.inlineCallbacks @@ -175,7 +175,7 @@ class ApplicationServiceApi(SimpleHttpClient): @defer.inlineCallbacks def push_bulk(self, service, events, txn_id=None): - if service.url == "": + if service.url is None: defer.returnValue(True) events = self._serialize(events) diff --git a/synapse/config/appservice.py b/synapse/config/appservice.py index 5569c43b35..759209a85b 100644 --- a/synapse/config/appservice.py +++ b/synapse/config/appservice.py @@ -86,7 +86,7 @@ def load_appservices(hostname, config_files): def _load_appservice(hostname, as_info, config_filename): required_string_fields = [ - "id", "url", "as_token", "hs_token", "sender_localpart" + "id", "as_token", "hs_token", "sender_localpart" ] for field in required_string_fields: if not isinstance(as_info.get(field), basestring): @@ -94,6 +94,13 @@ def _load_appservice(hostname, as_info, config_filename): field, config_filename, )) + # 'url' must either be a string or explicitly null, not missing + # to avoid accidentally turning off push for ASes. + if not isinstance(as_info.get("url"), basestring) and as_info.get("url", "") is not None: + raise KeyError( + "Required string field or explicit null: 'url' (%s)" % (config_filename,) + ) + localpart = as_info["sender_localpart"] if urllib.quote(localpart) != localpart: raise ValueError( @@ -133,10 +140,10 @@ def _load_appservice(hostname, as_info, config_filename): if not isinstance(p, str): raise KeyError("Bad value for 'protocols' item") - if as_info["url"] == "": + if as_info["url"] == None: logger.info( - "(%s) Explicitly empty 'url' provided. This application service " + - "will not receive events or queries.", + "(%s) Explicitly empty 'url' provided. This application service" + " will not receive events or queries.", config_filename, ) return ApplicationService( -- cgit 1.4.1