mirror of
https://mau.dev/maunium/synapse.git
synced 2024-10-01 01:36:05 -04:00
Fix userconsent on Python 3 (#3938)
This commit is contained in:
parent
8f5c23d0cd
commit
6e05fd032c
1
changelog.d/3938.bugfix
Normal file
1
changelog.d/3938.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
Sending server notices regarding user consent now works on Python 3.
|
@ -64,7 +64,7 @@ class ConsentURIBuilder(object):
|
|||||||
"""
|
"""
|
||||||
mac = hmac.new(
|
mac = hmac.new(
|
||||||
key=self._hmac_secret,
|
key=self._hmac_secret,
|
||||||
msg=user_id,
|
msg=user_id.encode('ascii'),
|
||||||
digestmod=sha256,
|
digestmod=sha256,
|
||||||
).hexdigest()
|
).hexdigest()
|
||||||
consent_uri = "%s_matrix/consent?%s" % (
|
consent_uri = "%s_matrix/consent?%s" % (
|
||||||
|
100
tests/server_notices/test_consent.py
Normal file
100
tests/server_notices/test_consent.py
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2018 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 synapse.rest.client.v1 import admin, login, room
|
||||||
|
from synapse.rest.client.v2_alpha import sync
|
||||||
|
|
||||||
|
from tests import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class ConsentNoticesTests(unittest.HomeserverTestCase):
|
||||||
|
|
||||||
|
servlets = [
|
||||||
|
sync.register_servlets,
|
||||||
|
admin.register_servlets,
|
||||||
|
login.register_servlets,
|
||||||
|
room.register_servlets,
|
||||||
|
]
|
||||||
|
|
||||||
|
def make_homeserver(self, reactor, clock):
|
||||||
|
|
||||||
|
self.consent_notice_message = "consent %(consent_uri)s"
|
||||||
|
config = self.default_config()
|
||||||
|
config.user_consent_version = "1"
|
||||||
|
config.user_consent_server_notice_content = {
|
||||||
|
"msgtype": "m.text",
|
||||||
|
"body": self.consent_notice_message,
|
||||||
|
}
|
||||||
|
config.public_baseurl = "https://example.com/"
|
||||||
|
config.form_secret = "123abc"
|
||||||
|
|
||||||
|
config.server_notices_mxid = "@notices:test"
|
||||||
|
config.server_notices_mxid_display_name = "test display name"
|
||||||
|
config.server_notices_mxid_avatar_url = None
|
||||||
|
config.server_notices_room_name = "Server Notices"
|
||||||
|
|
||||||
|
hs = self.setup_test_homeserver(config=config)
|
||||||
|
|
||||||
|
return hs
|
||||||
|
|
||||||
|
def prepare(self, reactor, clock, hs):
|
||||||
|
self.user_id = self.register_user("bob", "abc123")
|
||||||
|
self.access_token = self.login("bob", "abc123")
|
||||||
|
|
||||||
|
def test_get_sync_message(self):
|
||||||
|
"""
|
||||||
|
When user consent server notices are enabled, a sync will cause a notice
|
||||||
|
to fire (in a room which the user is invited to). The notice contains
|
||||||
|
the notice URL + an authentication code.
|
||||||
|
"""
|
||||||
|
# Initial sync, to get the user consent room invite
|
||||||
|
request, channel = self.make_request(
|
||||||
|
"GET", "/_matrix/client/r0/sync", access_token=self.access_token
|
||||||
|
)
|
||||||
|
self.render(request)
|
||||||
|
self.assertEqual(channel.code, 200)
|
||||||
|
|
||||||
|
# Get the Room ID to join
|
||||||
|
room_id = list(channel.json_body["rooms"]["invite"].keys())[0]
|
||||||
|
|
||||||
|
# Join the room
|
||||||
|
request, channel = self.make_request(
|
||||||
|
"POST",
|
||||||
|
"/_matrix/client/r0/rooms/" + room_id + "/join",
|
||||||
|
access_token=self.access_token,
|
||||||
|
)
|
||||||
|
self.render(request)
|
||||||
|
self.assertEqual(channel.code, 200)
|
||||||
|
|
||||||
|
# Sync again, to get the message in the room
|
||||||
|
request, channel = self.make_request(
|
||||||
|
"GET", "/_matrix/client/r0/sync", access_token=self.access_token
|
||||||
|
)
|
||||||
|
self.render(request)
|
||||||
|
self.assertEqual(channel.code, 200)
|
||||||
|
|
||||||
|
# Get the message
|
||||||
|
room = channel.json_body["rooms"]["join"][room_id]
|
||||||
|
messages = [
|
||||||
|
x for x in room["timeline"]["events"] if x["type"] == "m.room.message"
|
||||||
|
]
|
||||||
|
|
||||||
|
# One message, with the consent URL
|
||||||
|
self.assertEqual(len(messages), 1)
|
||||||
|
self.assertTrue(
|
||||||
|
messages[0]["content"]["body"].startswith(
|
||||||
|
"consent https://example.com/_matrix/consent"
|
||||||
|
)
|
||||||
|
)
|
@ -14,10 +14,6 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import hashlib
|
|
||||||
import hmac
|
|
||||||
import json
|
|
||||||
|
|
||||||
from mock import Mock
|
from mock import Mock
|
||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
@ -145,34 +141,8 @@ class ClientIpAuthTestCase(unittest.HomeserverTestCase):
|
|||||||
return hs
|
return hs
|
||||||
|
|
||||||
def prepare(self, hs, reactor, clock):
|
def prepare(self, hs, reactor, clock):
|
||||||
self.hs.config.registration_shared_secret = u"shared"
|
|
||||||
self.store = self.hs.get_datastore()
|
self.store = self.hs.get_datastore()
|
||||||
|
self.user_id = self.register_user("bob", "abc123", True)
|
||||||
# Create the user
|
|
||||||
request, channel = self.make_request("GET", "/_matrix/client/r0/admin/register")
|
|
||||||
self.render(request)
|
|
||||||
nonce = channel.json_body["nonce"]
|
|
||||||
|
|
||||||
want_mac = hmac.new(key=b"shared", digestmod=hashlib.sha1)
|
|
||||||
want_mac.update(nonce.encode('ascii') + b"\x00bob\x00abc123\x00admin")
|
|
||||||
want_mac = want_mac.hexdigest()
|
|
||||||
|
|
||||||
body = json.dumps(
|
|
||||||
{
|
|
||||||
"nonce": nonce,
|
|
||||||
"username": "bob",
|
|
||||||
"password": "abc123",
|
|
||||||
"admin": True,
|
|
||||||
"mac": want_mac,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
request, channel = self.make_request(
|
|
||||||
"POST", "/_matrix/client/r0/admin/register", body.encode('utf8')
|
|
||||||
)
|
|
||||||
self.render(request)
|
|
||||||
|
|
||||||
self.assertEqual(channel.code, 200)
|
|
||||||
self.user_id = channel.json_body["user_id"]
|
|
||||||
|
|
||||||
def test_request_with_xforwarded(self):
|
def test_request_with_xforwarded(self):
|
||||||
"""
|
"""
|
||||||
@ -194,20 +164,7 @@ class ClientIpAuthTestCase(unittest.HomeserverTestCase):
|
|||||||
def _runtest(self, headers, expected_ip, make_request_args):
|
def _runtest(self, headers, expected_ip, make_request_args):
|
||||||
device_id = "bleb"
|
device_id = "bleb"
|
||||||
|
|
||||||
body = json.dumps(
|
access_token = self.login("bob", "abc123", device_id=device_id)
|
||||||
{
|
|
||||||
"type": "m.login.password",
|
|
||||||
"user": "bob",
|
|
||||||
"password": "abc123",
|
|
||||||
"device_id": device_id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
request, channel = self.make_request(
|
|
||||||
"POST", "/_matrix/client/r0/login", body.encode('utf8'), **make_request_args
|
|
||||||
)
|
|
||||||
self.render(request)
|
|
||||||
self.assertEqual(channel.code, 200)
|
|
||||||
access_token = channel.json_body["access_token"].encode('ascii')
|
|
||||||
|
|
||||||
# Advance to a known time
|
# Advance to a known time
|
||||||
self.reactor.advance(123456 - self.reactor.seconds())
|
self.reactor.advance(123456 - self.reactor.seconds())
|
||||||
@ -215,7 +172,6 @@ class ClientIpAuthTestCase(unittest.HomeserverTestCase):
|
|||||||
request, channel = self.make_request(
|
request, channel = self.make_request(
|
||||||
"GET",
|
"GET",
|
||||||
"/_matrix/client/r0/admin/users/" + self.user_id,
|
"/_matrix/client/r0/admin/users/" + self.user_id,
|
||||||
body.encode('utf8'),
|
|
||||||
access_token=access_token,
|
access_token=access_token,
|
||||||
**make_request_args
|
**make_request_args
|
||||||
)
|
)
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import hmac
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from mock import Mock
|
from mock import Mock
|
||||||
@ -32,6 +34,7 @@ from synapse.types import UserID, create_requester
|
|||||||
from synapse.util.logcontext import LoggingContextFilter
|
from synapse.util.logcontext import LoggingContextFilter
|
||||||
|
|
||||||
from tests.server import get_clock, make_request, render, setup_test_homeserver
|
from tests.server import get_clock, make_request, render, setup_test_homeserver
|
||||||
|
from tests.utils import default_config
|
||||||
|
|
||||||
# Set up putting Synapse's logs into Trial's.
|
# Set up putting Synapse's logs into Trial's.
|
||||||
rootLogger = logging.getLogger()
|
rootLogger = logging.getLogger()
|
||||||
@ -121,7 +124,7 @@ class TestCase(unittest.TestCase):
|
|||||||
try:
|
try:
|
||||||
self.assertEquals(attrs[key], getattr(obj, key))
|
self.assertEquals(attrs[key], getattr(obj, key))
|
||||||
except AssertionError as e:
|
except AssertionError as e:
|
||||||
raise (type(e))(str(e) + " for '.%s'" % key)
|
raise (type(e))(e.message + " for '.%s'" % key)
|
||||||
|
|
||||||
def assert_dict(self, required, actual):
|
def assert_dict(self, required, actual):
|
||||||
"""Does a partial assert of a dict.
|
"""Does a partial assert of a dict.
|
||||||
@ -223,6 +226,15 @@ class HomeserverTestCase(TestCase):
|
|||||||
hs = self.setup_test_homeserver()
|
hs = self.setup_test_homeserver()
|
||||||
return hs
|
return hs
|
||||||
|
|
||||||
|
def default_config(self, name="test"):
|
||||||
|
"""
|
||||||
|
Get a default HomeServer config object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The homeserver name/domain.
|
||||||
|
"""
|
||||||
|
return default_config(name)
|
||||||
|
|
||||||
def prepare(self, reactor, clock, homeserver):
|
def prepare(self, reactor, clock, homeserver):
|
||||||
"""
|
"""
|
||||||
Prepare for the test. This involves things like mocking out parts of
|
Prepare for the test. This involves things like mocking out parts of
|
||||||
@ -297,3 +309,69 @@ class HomeserverTestCase(TestCase):
|
|||||||
return d
|
return d
|
||||||
self.pump()
|
self.pump()
|
||||||
return self.successResultOf(d)
|
return self.successResultOf(d)
|
||||||
|
|
||||||
|
def register_user(self, username, password, admin=False):
|
||||||
|
"""
|
||||||
|
Register a user. Requires the Admin API be registered.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
username (bytes/unicode): The user part of the new user.
|
||||||
|
password (bytes/unicode): The password of the new user.
|
||||||
|
admin (bool): Whether the user should be created as an admin
|
||||||
|
or not.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The MXID of the new user (unicode).
|
||||||
|
"""
|
||||||
|
self.hs.config.registration_shared_secret = u"shared"
|
||||||
|
|
||||||
|
# Create the user
|
||||||
|
request, channel = self.make_request("GET", "/_matrix/client/r0/admin/register")
|
||||||
|
self.render(request)
|
||||||
|
nonce = channel.json_body["nonce"]
|
||||||
|
|
||||||
|
want_mac = hmac.new(key=b"shared", digestmod=hashlib.sha1)
|
||||||
|
nonce_str = b"\x00".join([username.encode('utf8'), password.encode('utf8')])
|
||||||
|
if admin:
|
||||||
|
nonce_str += b"\x00admin"
|
||||||
|
else:
|
||||||
|
nonce_str += b"\x00notadmin"
|
||||||
|
want_mac.update(nonce.encode('ascii') + b"\x00" + nonce_str)
|
||||||
|
want_mac = want_mac.hexdigest()
|
||||||
|
|
||||||
|
body = json.dumps(
|
||||||
|
{
|
||||||
|
"nonce": nonce,
|
||||||
|
"username": username,
|
||||||
|
"password": password,
|
||||||
|
"admin": admin,
|
||||||
|
"mac": want_mac,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
request, channel = self.make_request(
|
||||||
|
"POST", "/_matrix/client/r0/admin/register", body.encode('utf8')
|
||||||
|
)
|
||||||
|
self.render(request)
|
||||||
|
self.assertEqual(channel.code, 200)
|
||||||
|
|
||||||
|
user_id = channel.json_body["user_id"]
|
||||||
|
return user_id
|
||||||
|
|
||||||
|
def login(self, username, password, device_id=None):
|
||||||
|
"""
|
||||||
|
Log in a user, and get an access token. Requires the Login API be
|
||||||
|
registered.
|
||||||
|
|
||||||
|
"""
|
||||||
|
body = {"type": "m.login.password", "user": username, "password": password}
|
||||||
|
if device_id:
|
||||||
|
body["device_id"] = device_id
|
||||||
|
|
||||||
|
request, channel = self.make_request(
|
||||||
|
"POST", "/_matrix/client/r0/login", json.dumps(body).encode('utf8')
|
||||||
|
)
|
||||||
|
self.render(request)
|
||||||
|
self.assertEqual(channel.code, 200)
|
||||||
|
|
||||||
|
access_token = channel.json_body["access_token"].encode('ascii')
|
||||||
|
return access_token
|
||||||
|
105
tests/utils.py
105
tests/utils.py
@ -96,6 +96,62 @@ def setupdb():
|
|||||||
atexit.register(_cleanup)
|
atexit.register(_cleanup)
|
||||||
|
|
||||||
|
|
||||||
|
def default_config(name):
|
||||||
|
"""
|
||||||
|
Create a reasonable test config.
|
||||||
|
"""
|
||||||
|
config = Mock()
|
||||||
|
config.signing_key = [MockKey()]
|
||||||
|
config.event_cache_size = 1
|
||||||
|
config.enable_registration = True
|
||||||
|
config.macaroon_secret_key = "not even a little secret"
|
||||||
|
config.expire_access_token = False
|
||||||
|
config.server_name = name
|
||||||
|
config.trusted_third_party_id_servers = []
|
||||||
|
config.room_invite_state_types = []
|
||||||
|
config.password_providers = []
|
||||||
|
config.worker_replication_url = ""
|
||||||
|
config.worker_app = None
|
||||||
|
config.email_enable_notifs = False
|
||||||
|
config.block_non_admin_invites = False
|
||||||
|
config.federation_domain_whitelist = None
|
||||||
|
config.federation_rc_reject_limit = 10
|
||||||
|
config.federation_rc_sleep_limit = 10
|
||||||
|
config.federation_rc_sleep_delay = 100
|
||||||
|
config.federation_rc_concurrent = 10
|
||||||
|
config.filter_timeline_limit = 5000
|
||||||
|
config.user_directory_search_all_users = False
|
||||||
|
config.user_consent_server_notice_content = None
|
||||||
|
config.block_events_without_consent_error = None
|
||||||
|
config.media_storage_providers = []
|
||||||
|
config.auto_join_rooms = []
|
||||||
|
config.limit_usage_by_mau = False
|
||||||
|
config.hs_disabled = False
|
||||||
|
config.hs_disabled_message = ""
|
||||||
|
config.hs_disabled_limit_type = ""
|
||||||
|
config.max_mau_value = 50
|
||||||
|
config.mau_trial_days = 0
|
||||||
|
config.mau_limits_reserved_threepids = []
|
||||||
|
config.admin_contact = None
|
||||||
|
config.rc_messages_per_second = 10000
|
||||||
|
config.rc_message_burst_count = 10000
|
||||||
|
|
||||||
|
# we need a sane default_room_version, otherwise attempts to create rooms will
|
||||||
|
# fail.
|
||||||
|
config.default_room_version = "1"
|
||||||
|
|
||||||
|
# disable user directory updates, because they get done in the
|
||||||
|
# background, which upsets the test runner.
|
||||||
|
config.update_user_directory = False
|
||||||
|
|
||||||
|
def is_threepid_reserved(threepid):
|
||||||
|
return ServerConfig.is_threepid_reserved(config, threepid)
|
||||||
|
|
||||||
|
config.is_threepid_reserved.side_effect = is_threepid_reserved
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
class TestHomeServer(HomeServer):
|
class TestHomeServer(HomeServer):
|
||||||
DATASTORE_CLASS = DataStore
|
DATASTORE_CLASS = DataStore
|
||||||
|
|
||||||
@ -124,54 +180,7 @@ def setup_test_homeserver(
|
|||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
|
|
||||||
if config is None:
|
if config is None:
|
||||||
config = Mock()
|
config = default_config(name)
|
||||||
config.signing_key = [MockKey()]
|
|
||||||
config.event_cache_size = 1
|
|
||||||
config.enable_registration = True
|
|
||||||
config.macaroon_secret_key = "not even a little secret"
|
|
||||||
config.expire_access_token = False
|
|
||||||
config.server_name = name
|
|
||||||
config.trusted_third_party_id_servers = []
|
|
||||||
config.room_invite_state_types = []
|
|
||||||
config.password_providers = []
|
|
||||||
config.worker_replication_url = ""
|
|
||||||
config.worker_app = None
|
|
||||||
config.email_enable_notifs = False
|
|
||||||
config.block_non_admin_invites = False
|
|
||||||
config.federation_domain_whitelist = None
|
|
||||||
config.federation_rc_reject_limit = 10
|
|
||||||
config.federation_rc_sleep_limit = 10
|
|
||||||
config.federation_rc_sleep_delay = 100
|
|
||||||
config.federation_rc_concurrent = 10
|
|
||||||
config.filter_timeline_limit = 5000
|
|
||||||
config.user_directory_search_all_users = False
|
|
||||||
config.user_consent_server_notice_content = None
|
|
||||||
config.block_events_without_consent_error = None
|
|
||||||
config.media_storage_providers = []
|
|
||||||
config.auto_join_rooms = []
|
|
||||||
config.limit_usage_by_mau = False
|
|
||||||
config.hs_disabled = False
|
|
||||||
config.hs_disabled_message = ""
|
|
||||||
config.hs_disabled_limit_type = ""
|
|
||||||
config.max_mau_value = 50
|
|
||||||
config.mau_trial_days = 0
|
|
||||||
config.mau_limits_reserved_threepids = []
|
|
||||||
config.admin_contact = None
|
|
||||||
config.rc_messages_per_second = 10000
|
|
||||||
config.rc_message_burst_count = 10000
|
|
||||||
|
|
||||||
# we need a sane default_room_version, otherwise attempts to create rooms will
|
|
||||||
# fail.
|
|
||||||
config.default_room_version = "1"
|
|
||||||
|
|
||||||
# disable user directory updates, because they get done in the
|
|
||||||
# background, which upsets the test runner.
|
|
||||||
config.update_user_directory = False
|
|
||||||
|
|
||||||
def is_threepid_reserved(threepid):
|
|
||||||
return ServerConfig.is_threepid_reserved(config, threepid)
|
|
||||||
|
|
||||||
config.is_threepid_reserved.side_effect = is_threepid_reserved
|
|
||||||
|
|
||||||
config.use_frozen_dicts = True
|
config.use_frozen_dicts = True
|
||||||
config.ldap_enabled = False
|
config.ldap_enabled = False
|
||||||
|
Loading…
Reference in New Issue
Block a user