Skip to content

Commit

Permalink
Fix userconsent on Python 3 (matrix-org#3938)
Browse files Browse the repository at this point in the history
  • Loading branch information
hawkowl authored Oct 1, 2018
1 parent 8f5c23d commit 6e05fd0
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 96 deletions.
1 change: 1 addition & 0 deletions changelog.d/3938.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Sending server notices regarding user consent now works on Python 3.
2 changes: 1 addition & 1 deletion synapse/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def build_user_consent_uri(self, user_id):
"""
mac = hmac.new(
key=self._hmac_secret,
msg=user_id,
msg=user_id.encode('ascii'),
digestmod=sha256,
).hexdigest()
consent_uri = "%s_matrix/consent?%s" % (
Expand Down
100 changes: 100 additions & 0 deletions tests/server_notices/test_consent.py
Original file line number Diff line number Diff line change
@@ -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"
)
)
48 changes: 2 additions & 46 deletions tests/storage/test_client_ips.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import hashlib
import hmac
import json

from mock import Mock

from twisted.internet import defer
Expand Down Expand Up @@ -145,34 +141,8 @@ def make_homeserver(self, reactor, clock):
return hs

def prepare(self, hs, reactor, clock):
self.hs.config.registration_shared_secret = u"shared"
self.store = self.hs.get_datastore()

# 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"]
self.user_id = self.register_user("bob", "abc123", True)

def test_request_with_xforwarded(self):
"""
Expand All @@ -194,28 +164,14 @@ def test_request_from_getPeer(self):
def _runtest(self, headers, expected_ip, make_request_args):
device_id = "bleb"

body = json.dumps(
{
"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')
access_token = self.login("bob", "abc123", device_id=device_id)

# Advance to a known time
self.reactor.advance(123456 - self.reactor.seconds())

request, channel = self.make_request(
"GET",
"/_matrix/client/r0/admin/users/" + self.user_id,
body.encode('utf8'),
access_token=access_token,
**make_request_args
)
Expand Down
80 changes: 79 additions & 1 deletion tests/unittest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import hashlib
import hmac
import logging

from mock import Mock
Expand All @@ -32,6 +34,7 @@
from synapse.util.logcontext import LoggingContextFilter

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.
rootLogger = logging.getLogger()
Expand Down Expand Up @@ -121,7 +124,7 @@ def assertObjectHasAttributes(self, attrs, obj):
try:
self.assertEquals(attrs[key], getattr(obj, key))
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):
"""Does a partial assert of a dict.
Expand Down Expand Up @@ -223,6 +226,15 @@ def make_homeserver(self, reactor, clock):
hs = self.setup_test_homeserver()
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):
"""
Prepare for the test. This involves things like mocking out parts of
Expand Down Expand Up @@ -297,3 +309,69 @@ def get_success(self, d):
return d
self.pump()
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
Loading

0 comments on commit 6e05fd0

Please sign in to comment.