mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2025-01-10 06:09:32 -05:00
Merge pull request #4420 from matrix-org/jaywink/openid-listener
New listener resource for the federation API "openid/userinfo" endpoint
This commit is contained in:
commit
b201149c7e
1
changelog.d/4420.feature
Normal file
1
changelog.d/4420.feature
Normal file
@ -0,0 +1 @@
|
|||||||
|
Federation OpenID listener resource can now be activated even if federation is disabled
|
@ -86,6 +86,16 @@ class FederationReaderServer(HomeServer):
|
|||||||
resources.update({
|
resources.update({
|
||||||
FEDERATION_PREFIX: TransportLayerServer(self),
|
FEDERATION_PREFIX: TransportLayerServer(self),
|
||||||
})
|
})
|
||||||
|
if name == "openid" and "federation" not in res["names"]:
|
||||||
|
# Only load the openid resource separately if federation resource
|
||||||
|
# is not specified since federation resource includes openid
|
||||||
|
# resource.
|
||||||
|
resources.update({
|
||||||
|
FEDERATION_PREFIX: TransportLayerServer(
|
||||||
|
self,
|
||||||
|
servlet_groups=["openid"],
|
||||||
|
),
|
||||||
|
})
|
||||||
|
|
||||||
root_resource = create_resource_tree(resources, NoResource())
|
root_resource = create_resource_tree(resources, NoResource())
|
||||||
|
|
||||||
@ -98,7 +108,8 @@ class FederationReaderServer(HomeServer):
|
|||||||
listener_config,
|
listener_config,
|
||||||
root_resource,
|
root_resource,
|
||||||
self.version_string,
|
self.version_string,
|
||||||
)
|
),
|
||||||
|
reactor=self.get_reactor()
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info("Synapse federation reader now listening on port %d", port)
|
logger.info("Synapse federation reader now listening on port %d", port)
|
||||||
|
@ -99,6 +99,10 @@ class SynapseHomeServer(HomeServer):
|
|||||||
resources = {}
|
resources = {}
|
||||||
for res in listener_config["resources"]:
|
for res in listener_config["resources"]:
|
||||||
for name in res["names"]:
|
for name in res["names"]:
|
||||||
|
if name == "openid" and "federation" in res["names"]:
|
||||||
|
# Skip loading openid resource if federation is defined
|
||||||
|
# since federation resource will include openid
|
||||||
|
continue
|
||||||
resources.update(self._configure_named_resource(
|
resources.update(self._configure_named_resource(
|
||||||
name, res.get("compress", False),
|
name, res.get("compress", False),
|
||||||
))
|
))
|
||||||
@ -134,6 +138,7 @@ class SynapseHomeServer(HomeServer):
|
|||||||
self.version_string,
|
self.version_string,
|
||||||
),
|
),
|
||||||
self.tls_server_context_factory,
|
self.tls_server_context_factory,
|
||||||
|
reactor=self.get_reactor(),
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -146,7 +151,8 @@ class SynapseHomeServer(HomeServer):
|
|||||||
listener_config,
|
listener_config,
|
||||||
root_resource,
|
root_resource,
|
||||||
self.version_string,
|
self.version_string,
|
||||||
)
|
),
|
||||||
|
reactor=self.get_reactor(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _configure_named_resource(self, name, compress=False):
|
def _configure_named_resource(self, name, compress=False):
|
||||||
@ -193,6 +199,11 @@ class SynapseHomeServer(HomeServer):
|
|||||||
FEDERATION_PREFIX: TransportLayerServer(self),
|
FEDERATION_PREFIX: TransportLayerServer(self),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if name == "openid":
|
||||||
|
resources.update({
|
||||||
|
FEDERATION_PREFIX: TransportLayerServer(self, servlet_groups=["openid"]),
|
||||||
|
})
|
||||||
|
|
||||||
if name in ["static", "client"]:
|
if name in ["static", "client"]:
|
||||||
resources.update({
|
resources.update({
|
||||||
STATIC_PREFIX: File(
|
STATIC_PREFIX: File(
|
||||||
|
@ -335,6 +335,11 @@ class ServerConfig(Config):
|
|||||||
- names: [federation] # Federation APIs
|
- names: [federation] # Federation APIs
|
||||||
compress: false
|
compress: false
|
||||||
|
|
||||||
|
# # If federation is disabled synapse can still expose the open ID endpoint
|
||||||
|
# # to allow integrations to authenticate users
|
||||||
|
# - names: [openid]
|
||||||
|
# compress: false
|
||||||
|
|
||||||
# optional list of additional endpoints which can be loaded via
|
# optional list of additional endpoints which can be loaded via
|
||||||
# dynamic modules
|
# dynamic modules
|
||||||
# additional_resources:
|
# additional_resources:
|
||||||
@ -356,6 +361,10 @@ class ServerConfig(Config):
|
|||||||
compress: true
|
compress: true
|
||||||
- names: [federation]
|
- names: [federation]
|
||||||
compress: false
|
compress: false
|
||||||
|
# # If federation is disabled synapse can still expose the open ID endpoint
|
||||||
|
# # to allow integrations to authenticate users
|
||||||
|
# - names: [openid]
|
||||||
|
# compress: false
|
||||||
|
|
||||||
# Turn on the twisted ssh manhole service on localhost on the given
|
# Turn on the twisted ssh manhole service on localhost on the given
|
||||||
# port.
|
# port.
|
||||||
@ -480,6 +489,7 @@ KNOWN_RESOURCES = (
|
|||||||
'keys',
|
'keys',
|
||||||
'media',
|
'media',
|
||||||
'metrics',
|
'metrics',
|
||||||
|
'openid',
|
||||||
'replication',
|
'replication',
|
||||||
'static',
|
'static',
|
||||||
'webclient',
|
'webclient',
|
||||||
|
@ -43,9 +43,20 @@ logger = logging.getLogger(__name__)
|
|||||||
class TransportLayerServer(JsonResource):
|
class TransportLayerServer(JsonResource):
|
||||||
"""Handles incoming federation HTTP requests"""
|
"""Handles incoming federation HTTP requests"""
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs, servlet_groups=None):
|
||||||
|
"""Initialize the TransportLayerServer
|
||||||
|
|
||||||
|
Will by default register all servlets. For custom behaviour, pass in
|
||||||
|
a list of servlet_groups to register.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
hs (synapse.server.HomeServer): homeserver
|
||||||
|
servlet_groups (list[str], optional): List of servlet groups to register.
|
||||||
|
Defaults to ``DEFAULT_SERVLET_GROUPS``.
|
||||||
|
"""
|
||||||
self.hs = hs
|
self.hs = hs
|
||||||
self.clock = hs.get_clock()
|
self.clock = hs.get_clock()
|
||||||
|
self.servlet_groups = servlet_groups
|
||||||
|
|
||||||
super(TransportLayerServer, self).__init__(hs, canonical_json=False)
|
super(TransportLayerServer, self).__init__(hs, canonical_json=False)
|
||||||
|
|
||||||
@ -67,6 +78,7 @@ class TransportLayerServer(JsonResource):
|
|||||||
resource=self,
|
resource=self,
|
||||||
ratelimiter=self.ratelimiter,
|
ratelimiter=self.ratelimiter,
|
||||||
authenticator=self.authenticator,
|
authenticator=self.authenticator,
|
||||||
|
servlet_groups=self.servlet_groups,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -1308,10 +1320,12 @@ FEDERATION_SERVLET_CLASSES = (
|
|||||||
FederationClientKeysClaimServlet,
|
FederationClientKeysClaimServlet,
|
||||||
FederationThirdPartyInviteExchangeServlet,
|
FederationThirdPartyInviteExchangeServlet,
|
||||||
On3pidBindServlet,
|
On3pidBindServlet,
|
||||||
OpenIdUserInfo,
|
|
||||||
FederationVersionServlet,
|
FederationVersionServlet,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
OPENID_SERVLET_CLASSES = (
|
||||||
|
OpenIdUserInfo,
|
||||||
|
)
|
||||||
|
|
||||||
ROOM_LIST_CLASSES = (
|
ROOM_LIST_CLASSES = (
|
||||||
PublicRoomList,
|
PublicRoomList,
|
||||||
@ -1350,8 +1364,34 @@ GROUP_ATTESTATION_SERVLET_CLASSES = (
|
|||||||
FederationGroupsRenewAttestaionServlet,
|
FederationGroupsRenewAttestaionServlet,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
DEFAULT_SERVLET_GROUPS = (
|
||||||
|
"federation",
|
||||||
|
"room_list",
|
||||||
|
"group_server",
|
||||||
|
"group_local",
|
||||||
|
"group_attestation",
|
||||||
|
"openid",
|
||||||
|
)
|
||||||
|
|
||||||
def register_servlets(hs, resource, authenticator, ratelimiter):
|
|
||||||
|
def register_servlets(hs, resource, authenticator, ratelimiter, servlet_groups=None):
|
||||||
|
"""Initialize and register servlet classes.
|
||||||
|
|
||||||
|
Will by default register all servlets. For custom behaviour, pass in
|
||||||
|
a list of servlet_groups to register.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
hs (synapse.server.HomeServer): homeserver
|
||||||
|
resource (TransportLayerServer): resource class to register to
|
||||||
|
authenticator (Authenticator): authenticator to use
|
||||||
|
ratelimiter (util.ratelimitutils.FederationRateLimiter): ratelimiter to use
|
||||||
|
servlet_groups (list[str], optional): List of servlet groups to register.
|
||||||
|
Defaults to ``DEFAULT_SERVLET_GROUPS``.
|
||||||
|
"""
|
||||||
|
if not servlet_groups:
|
||||||
|
servlet_groups = DEFAULT_SERVLET_GROUPS
|
||||||
|
|
||||||
|
if "federation" in servlet_groups:
|
||||||
for servletclass in FEDERATION_SERVLET_CLASSES:
|
for servletclass in FEDERATION_SERVLET_CLASSES:
|
||||||
servletclass(
|
servletclass(
|
||||||
handler=hs.get_federation_server(),
|
handler=hs.get_federation_server(),
|
||||||
@ -1360,6 +1400,16 @@ def register_servlets(hs, resource, authenticator, ratelimiter):
|
|||||||
server_name=hs.hostname,
|
server_name=hs.hostname,
|
||||||
).register(resource)
|
).register(resource)
|
||||||
|
|
||||||
|
if "openid" in servlet_groups:
|
||||||
|
for servletclass in OPENID_SERVLET_CLASSES:
|
||||||
|
servletclass(
|
||||||
|
handler=hs.get_federation_server(),
|
||||||
|
authenticator=authenticator,
|
||||||
|
ratelimiter=ratelimiter,
|
||||||
|
server_name=hs.hostname,
|
||||||
|
).register(resource)
|
||||||
|
|
||||||
|
if "room_list" in servlet_groups:
|
||||||
for servletclass in ROOM_LIST_CLASSES:
|
for servletclass in ROOM_LIST_CLASSES:
|
||||||
servletclass(
|
servletclass(
|
||||||
handler=hs.get_room_list_handler(),
|
handler=hs.get_room_list_handler(),
|
||||||
@ -1368,6 +1418,7 @@ def register_servlets(hs, resource, authenticator, ratelimiter):
|
|||||||
server_name=hs.hostname,
|
server_name=hs.hostname,
|
||||||
).register(resource)
|
).register(resource)
|
||||||
|
|
||||||
|
if "group_server" in servlet_groups:
|
||||||
for servletclass in GROUP_SERVER_SERVLET_CLASSES:
|
for servletclass in GROUP_SERVER_SERVLET_CLASSES:
|
||||||
servletclass(
|
servletclass(
|
||||||
handler=hs.get_groups_server_handler(),
|
handler=hs.get_groups_server_handler(),
|
||||||
@ -1376,6 +1427,7 @@ def register_servlets(hs, resource, authenticator, ratelimiter):
|
|||||||
server_name=hs.hostname,
|
server_name=hs.hostname,
|
||||||
).register(resource)
|
).register(resource)
|
||||||
|
|
||||||
|
if "group_local" in servlet_groups:
|
||||||
for servletclass in GROUP_LOCAL_SERVLET_CLASSES:
|
for servletclass in GROUP_LOCAL_SERVLET_CLASSES:
|
||||||
servletclass(
|
servletclass(
|
||||||
handler=hs.get_groups_local_handler(),
|
handler=hs.get_groups_local_handler(),
|
||||||
@ -1384,6 +1436,7 @@ def register_servlets(hs, resource, authenticator, ratelimiter):
|
|||||||
server_name=hs.hostname,
|
server_name=hs.hostname,
|
||||||
).register(resource)
|
).register(resource)
|
||||||
|
|
||||||
|
if "group_attestation" in servlet_groups:
|
||||||
for servletclass in GROUP_ATTESTATION_SERVLET_CLASSES:
|
for servletclass in GROUP_ATTESTATION_SERVLET_CLASSES:
|
||||||
servletclass(
|
servletclass(
|
||||||
handler=hs.get_groups_attestation_renewer(),
|
handler=hs.get_groups_attestation_renewer(),
|
||||||
|
@ -85,7 +85,7 @@ CONDITIONAL_REQUIREMENTS = {
|
|||||||
|
|
||||||
"saml2": ["pysaml2>=4.5.0"],
|
"saml2": ["pysaml2>=4.5.0"],
|
||||||
"url_preview": ["lxml>=3.5.0"],
|
"url_preview": ["lxml>=3.5.0"],
|
||||||
"test": ["mock>=2.0"],
|
"test": ["mock>=2.0", "parameterized"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ class FrontendProxyTests(HomeserverTestCase):
|
|||||||
|
|
||||||
def test_listen_http_with_presence_disabled(self):
|
def test_listen_http_with_presence_disabled(self):
|
||||||
"""
|
"""
|
||||||
When presence is on, the stub servlet will register.
|
When presence is off, the stub servlet will register.
|
||||||
"""
|
"""
|
||||||
# Presence is off
|
# Presence is off
|
||||||
self.hs.config.use_presence = False
|
self.hs.config.use_presence = False
|
||||||
|
119
tests/app/test_openid_listener.py
Normal file
119
tests/app/test_openid_listener.py
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2019 New Vector Ltd
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
from mock import Mock, patch
|
||||||
|
|
||||||
|
from parameterized import parameterized
|
||||||
|
|
||||||
|
from synapse.app.federation_reader import FederationReaderServer
|
||||||
|
from synapse.app.homeserver import SynapseHomeServer
|
||||||
|
|
||||||
|
from tests.unittest import HomeserverTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class FederationReaderOpenIDListenerTests(HomeserverTestCase):
|
||||||
|
def make_homeserver(self, reactor, clock):
|
||||||
|
hs = self.setup_test_homeserver(
|
||||||
|
http_client=None, homeserverToUse=FederationReaderServer,
|
||||||
|
)
|
||||||
|
return hs
|
||||||
|
|
||||||
|
@parameterized.expand([
|
||||||
|
(["federation"], "auth_fail"),
|
||||||
|
([], "no_resource"),
|
||||||
|
(["openid", "federation"], "auth_fail"),
|
||||||
|
(["openid"], "auth_fail"),
|
||||||
|
])
|
||||||
|
def test_openid_listener(self, names, expectation):
|
||||||
|
"""
|
||||||
|
Test different openid listener configurations.
|
||||||
|
|
||||||
|
401 is success here since it means we hit the handler and auth failed.
|
||||||
|
"""
|
||||||
|
config = {
|
||||||
|
"port": 8080,
|
||||||
|
"bind_addresses": ["0.0.0.0"],
|
||||||
|
"resources": [{"names": names}],
|
||||||
|
}
|
||||||
|
|
||||||
|
# Listen with the config
|
||||||
|
self.hs._listen_http(config)
|
||||||
|
|
||||||
|
# Grab the resource from the site that was told to listen
|
||||||
|
site = self.reactor.tcpServers[0][1]
|
||||||
|
try:
|
||||||
|
self.resource = (
|
||||||
|
site.resource.children[b"_matrix"].children[b"federation"]
|
||||||
|
)
|
||||||
|
except KeyError:
|
||||||
|
if expectation == "no_resource":
|
||||||
|
return
|
||||||
|
raise
|
||||||
|
|
||||||
|
request, channel = self.make_request(
|
||||||
|
"GET",
|
||||||
|
"/_matrix/federation/v1/openid/userinfo",
|
||||||
|
)
|
||||||
|
self.render(request)
|
||||||
|
|
||||||
|
self.assertEqual(channel.code, 401)
|
||||||
|
|
||||||
|
|
||||||
|
@patch("synapse.app.homeserver.KeyApiV2Resource", new=Mock())
|
||||||
|
class SynapseHomeserverOpenIDListenerTests(HomeserverTestCase):
|
||||||
|
def make_homeserver(self, reactor, clock):
|
||||||
|
hs = self.setup_test_homeserver(
|
||||||
|
http_client=None, homeserverToUse=SynapseHomeServer,
|
||||||
|
)
|
||||||
|
return hs
|
||||||
|
|
||||||
|
@parameterized.expand([
|
||||||
|
(["federation"], "auth_fail"),
|
||||||
|
([], "no_resource"),
|
||||||
|
(["openid", "federation"], "auth_fail"),
|
||||||
|
(["openid"], "auth_fail"),
|
||||||
|
])
|
||||||
|
def test_openid_listener(self, names, expectation):
|
||||||
|
"""
|
||||||
|
Test different openid listener configurations.
|
||||||
|
|
||||||
|
401 is success here since it means we hit the handler and auth failed.
|
||||||
|
"""
|
||||||
|
config = {
|
||||||
|
"port": 8080,
|
||||||
|
"bind_addresses": ["0.0.0.0"],
|
||||||
|
"resources": [{"names": names}],
|
||||||
|
}
|
||||||
|
|
||||||
|
# Listen with the config
|
||||||
|
self.hs._listener_http(config, config)
|
||||||
|
|
||||||
|
# Grab the resource from the site that was told to listen
|
||||||
|
site = self.reactor.tcpServers[0][1]
|
||||||
|
try:
|
||||||
|
self.resource = (
|
||||||
|
site.resource.children[b"_matrix"].children[b"federation"]
|
||||||
|
)
|
||||||
|
except KeyError:
|
||||||
|
if expectation == "no_resource":
|
||||||
|
return
|
||||||
|
raise
|
||||||
|
|
||||||
|
request, channel = self.make_request(
|
||||||
|
"GET",
|
||||||
|
"/_matrix/federation/v1/openid/userinfo",
|
||||||
|
)
|
||||||
|
self.render(request)
|
||||||
|
|
||||||
|
self.assertEqual(channel.code, 401)
|
Loading…
Reference in New Issue
Block a user