From b136ee10dff071baa3fb8895bf00d2b10f443437 Mon Sep 17 00:00:00 2001 From: Joseph Weston Date: Fri, 1 Mar 2019 03:59:25 +0100 Subject: [PATCH 1/4] Import 'admin' module rather than 'register_servlets' directly We will later need also to import 'register_servlets' from the 'login' module, so we un-pollute the namespace now to keep the logical changes separate. --- tests/rest/client/v1/test_admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/rest/client/v1/test_admin.py b/tests/rest/client/v1/test_admin.py index 407bf0ac4c..c926836206 100644 --- a/tests/rest/client/v1/test_admin.py +++ b/tests/rest/client/v1/test_admin.py @@ -20,14 +20,14 @@ import json from mock import Mock from synapse.api.constants import UserTypes -from synapse.rest.client.v1.admin import register_servlets +from synapse.rest.client.v1 import admin from tests import unittest class UserRegisterTestCase(unittest.HomeserverTestCase): - servlets = [register_servlets] + servlets = [admin.register_servlets] def make_homeserver(self, reactor, clock): From 1e8388b311c54d754d6afbe639ed2825c1c1f285 Mon Sep 17 00:00:00 2001 From: Joseph Weston Date: Fri, 1 Mar 2019 04:05:47 +0100 Subject: [PATCH 2/4] Add 'server_version' endpoint to admin API This is required because the 'Server' HTTP header is not always passed through proxies. --- synapse/rest/client/v1/admin.py | 23 +++++++++++++++++++ tests/rest/client/v1/test_admin.py | 36 +++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/synapse/rest/client/v1/admin.py b/synapse/rest/client/v1/admin.py index 82433a2aa9..0201cf1186 100644 --- a/synapse/rest/client/v1/admin.py +++ b/synapse/rest/client/v1/admin.py @@ -17,12 +17,14 @@ import hashlib import hmac import logging +import platform from six import text_type from six.moves import http_client from twisted.internet import defer +import synapse from synapse.api.constants import Membership, UserTypes from synapse.api.errors import AuthError, Codes, NotFoundError, SynapseError from synapse.http.servlet import ( @@ -32,6 +34,7 @@ from synapse.http.servlet import ( parse_string, ) from synapse.types import UserID, create_requester +from synapse.util.versionstring import get_version_string from .base import ClientV1RestServlet, client_path_patterns @@ -66,6 +69,25 @@ class UsersRestServlet(ClientV1RestServlet): defer.returnValue((200, ret)) +class VersionServlet(ClientV1RestServlet): + PATTERNS = client_path_patterns("/admin/server_version") + + @defer.inlineCallbacks + def on_GET(self, request): + requester = yield self.auth.get_user_by_req(request) + is_admin = yield self.auth.is_server_admin(requester.user) + + if not is_admin: + raise AuthError(403, "You are not a server admin") + + ret = { + 'server_version': get_version_string(synapse), + 'python_version': platform.python_version(), + } + + defer.returnValue((200, ret)) + + class UserRegisterServlet(ClientV1RestServlet): """ Attributes: @@ -763,3 +785,4 @@ def register_servlets(hs, http_server): QuarantineMediaInRoom(hs).register(http_server) ListMediaInRoom(hs).register(http_server) UserRegisterServlet(hs).register(http_server) + VersionServlet(hs).register(http_server) diff --git a/tests/rest/client/v1/test_admin.py b/tests/rest/client/v1/test_admin.py index c926836206..ea03b7e523 100644 --- a/tests/rest/client/v1/test_admin.py +++ b/tests/rest/client/v1/test_admin.py @@ -20,11 +20,45 @@ import json from mock import Mock from synapse.api.constants import UserTypes -from synapse.rest.client.v1 import admin +from synapse.rest.client.v1 import admin, login from tests import unittest +class VersionTestCase(unittest.HomeserverTestCase): + + servlets = [ + admin.register_servlets, + login.register_servlets, + ] + + url = '/_matrix/client/r0/admin/server_version' + + def test_version_string(self): + self.register_user("admin", "pass", admin=True) + self.admin_token = self.login("admin", "pass") + + request, channel = self.make_request("GET", self.url, + access_token=self.admin_token) + self.render(request) + + self.assertEqual(200, int(channel.result["code"]), + msg=channel.result["body"]) + self.assertEqual({'server_version', 'python_version'}, + set(channel.json_body.keys())) + + def test_inaccessible_to_non_admins(self): + self.register_user("unprivileged-user", "pass", admin=False) + user_token = self.login("unprivileged-user", "pass") + + request, channel = self.make_request("GET", self.url, + access_token=user_token) + self.render(request) + + self.assertEqual(403, int(channel.result['code']), + msg=channel.result['body']) + + class UserRegisterTestCase(unittest.HomeserverTestCase): servlets = [admin.register_servlets] From d3dcb645012a45cadb1eae2e3a3c09f4f929e1ef Mon Sep 17 00:00:00 2001 From: Joseph Weston Date: Fri, 1 Mar 2019 09:50:28 +0100 Subject: [PATCH 3/4] Add changelog and AUTHORS file entry Signed-off-by: Joseph Weston --- AUTHORS.rst | 3 +++ changelog.d/4772.feature | 1 + 2 files changed, 4 insertions(+) create mode 100644 changelog.d/4772.feature diff --git a/AUTHORS.rst b/AUTHORS.rst index d599aec74c..3ea18eefcb 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -69,3 +69,6 @@ Serban Constantin Jason Robinson * Minor fixes + +Joseph Weston + + Add admin API for querying HS version diff --git a/changelog.d/4772.feature b/changelog.d/4772.feature new file mode 100644 index 0000000000..19bb91f1e8 --- /dev/null +++ b/changelog.d/4772.feature @@ -0,0 +1 @@ +Add an endpoint to the admin API for querying the server version. Contributed by Joseph Weston. From 144cbfd6508eb4dceeb13010fb858831bd4d11af Mon Sep 17 00:00:00 2001 From: Joseph Weston Date: Sat, 2 Mar 2019 03:07:04 +0100 Subject: [PATCH 4/4] add API documentation Signed-off-by: Joseph Weston --- docs/admin_api/version_api.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 docs/admin_api/version_api.rst diff --git a/docs/admin_api/version_api.rst b/docs/admin_api/version_api.rst new file mode 100644 index 0000000000..30a91b5f43 --- /dev/null +++ b/docs/admin_api/version_api.rst @@ -0,0 +1,22 @@ +Version API +=========== + +This API returns the running Synapse version and the Python version +on which Synapse is being run. This is useful when a Synapse instance +is behind a proxy that does not forward the 'Server' header (which also +contains Synapse version information). + +The api is:: + + GET /_matrix/client/r0/admin/server_version + +including an ``access_token`` of a server admin. + +It returns a JSON body like the following: + +.. code:: json + + { + "server_version": "0.99.2rc1 (b=develop, abcdef123)", + "python_version": "3.6.8" + }