Merge branch 'develop' of github.com:matrix-org/synapse into neilj/create_support_user
This commit is contained in:
@@ -1 +1 @@
|
||||
Add `m.login.terms` to the registration flow when consent tracking is enabled. **This makes the template arguments conditionally optional on a new `public_version` variable - update your privacy templates to support this.**
|
||||
Include flags to optionally add `m.login.terms` to the registration flow when consent tracking is enabled.
|
||||
|
||||
1
changelog.d/4113.bugfix
Normal file
1
changelog.d/4113.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
Fix e2e key backup with more than 9 backup versions
|
||||
1
changelog.d/4123.bugfix
Normal file
1
changelog.d/4123.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
fix return code of empty key backups
|
||||
@@ -1 +1 @@
|
||||
Add `m.login.terms` to the registration flow when consent tracking is enabled. **This makes the template arguments conditionally optional on a new `public_version` variable - update your privacy templates to support this.**
|
||||
Include flags to optionally add `m.login.terms` to the registration flow when consent tracking is enabled.
|
||||
|
||||
1
changelog.d/4142.feature
Normal file
1
changelog.d/4142.feature
Normal file
@@ -0,0 +1 @@
|
||||
Include flags to optionally add `m.login.terms` to the registration flow when consent tracking is enabled.
|
||||
1
changelog.d/4155.misc
Normal file
1
changelog.d/4155.misc
Normal file
@@ -0,0 +1 @@
|
||||
add purge_history.sh and purge_remote_media.sh scripts to contrib/
|
||||
1
changelog.d/4156.misc
Normal file
1
changelog.d/4156.misc
Normal file
@@ -0,0 +1 @@
|
||||
HTTP tests have been refactored to contain less boilerplate.
|
||||
1
changelog.d/4157.bugfix
Normal file
1
changelog.d/4157.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
Loading URL previews from the DB cache on Postgres will no longer cause Unicode type errors when responding to the request, and URL previews will no longer fail if the remote server returns a Content-Type header with the chartype in quotes.
|
||||
1
changelog.d/4161.bugfix
Normal file
1
changelog.d/4161.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
The hash_password script now works on Python 3.
|
||||
1
changelog.d/4163.bugfix
Normal file
1
changelog.d/4163.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
Generating the user consent URI no longer fails on Python 3.
|
||||
1
changelog.d/4164.bugfix
Normal file
1
changelog.d/4164.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
Fix noop checks when updating device keys, reducing spurious device list update notifications.
|
||||
1
changelog.d/4165.misc
Normal file
1
changelog.d/4165.misc
Normal file
@@ -0,0 +1 @@
|
||||
Drop incoming events from federation for unknown rooms
|
||||
1
changelog.d/4184.feature
Normal file
1
changelog.d/4184.feature
Normal file
@@ -0,0 +1 @@
|
||||
Include flags to optionally add `m.login.terms` to the registration flow when consent tracking is enabled.
|
||||
16
contrib/purge_api/README.md
Normal file
16
contrib/purge_api/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
Purge history API examples
|
||||
==========================
|
||||
|
||||
# `purge_history.sh`
|
||||
|
||||
A bash file, that uses the [purge history API](/docs/admin_api/README.rst) to
|
||||
purge all messages in a list of rooms up to a certain event. You can select a
|
||||
timeframe or a number of messages that you want to keep in the room.
|
||||
|
||||
Just configure the variables DOMAIN, ADMIN, ROOMS_ARRAY and TIME at the top of
|
||||
the script.
|
||||
|
||||
# `purge_remote_media.sh`
|
||||
|
||||
A bash file, that uses the [purge history API](/docs/admin_api/README.rst) to
|
||||
purge all old cached remote media.
|
||||
141
contrib/purge_api/purge_history.sh
Normal file
141
contrib/purge_api/purge_history.sh
Normal file
@@ -0,0 +1,141 @@
|
||||
#!/bin/bash
|
||||
|
||||
# this script will use the api:
|
||||
# https://github.com/matrix-org/synapse/blob/master/docs/admin_api/purge_history_api.rst
|
||||
#
|
||||
# It will purge all messages in a list of rooms up to a cetrain event
|
||||
|
||||
###################################################################################################
|
||||
# define your domain and admin user
|
||||
###################################################################################################
|
||||
# add this user as admin in your home server:
|
||||
DOMAIN=yourserver.tld
|
||||
# add this user as admin in your home server:
|
||||
ADMIN="@you_admin_username:$DOMAIN"
|
||||
|
||||
API_URL="$DOMAIN:8008/_matrix/client/r0"
|
||||
|
||||
###################################################################################################
|
||||
#choose the rooms to prune old messages from (add a free comment at the end)
|
||||
###################################################################################################
|
||||
# the room_id's you can get e.g. from your Riot clients "View Source" button on each message
|
||||
ROOMS_ARRAY=(
|
||||
'!DgvjtOljKujDBrxyHk:matrix.org#riot:matrix.org'
|
||||
'!QtykxKocfZaZOUrTwp:matrix.org#Matrix HQ'
|
||||
)
|
||||
|
||||
# ALTERNATIVELY:
|
||||
# you can select all the rooms that are not encrypted and loop over the result:
|
||||
# SELECT room_id FROM rooms WHERE room_id NOT IN (SELECT DISTINCT room_id FROM events WHERE type ='m.room.encrypted')
|
||||
# or
|
||||
# select all rooms with at least 100 members:
|
||||
# SELECT q.room_id FROM (select count(*) as numberofusers, room_id FROM current_state_events WHERE type ='m.room.member'
|
||||
# GROUP BY room_id) AS q LEFT JOIN room_aliases a ON q.room_id=a.room_id WHERE q.numberofusers > 100 ORDER BY numberofusers desc
|
||||
|
||||
###################################################################################################
|
||||
# evaluate the EVENT_ID before which should be pruned
|
||||
###################################################################################################
|
||||
# choose a time before which the messages should be pruned:
|
||||
TIME='12 months ago'
|
||||
# ALTERNATIVELY:
|
||||
# a certain time:
|
||||
# TIME='2016-08-31 23:59:59'
|
||||
|
||||
# creates a timestamp from the given time string:
|
||||
UNIX_TIMESTAMP=$(date +%s%3N --date='TZ="UTC+2" '"$TIME")
|
||||
|
||||
# ALTERNATIVELY:
|
||||
# prune all messages that are older than 1000 messages ago:
|
||||
# LAST_MESSAGES=1000
|
||||
# SQL_GET_EVENT="SELECT event_id from events WHERE type='m.room.message' AND room_id ='$ROOM' ORDER BY received_ts DESC LIMIT 1 offset $(($LAST_MESSAGES - 1))"
|
||||
|
||||
# ALTERNATIVELY:
|
||||
# select the EVENT_ID manually:
|
||||
#EVENT_ID='$1471814088343495zpPNI:matrix.org' # an example event from 21st of Aug 2016 by Matthew
|
||||
|
||||
###################################################################################################
|
||||
# make the admin user a server admin in the database with
|
||||
###################################################################################################
|
||||
# psql -A -t --dbname=synapse -c "UPDATE users SET admin=1 WHERE name LIKE '$ADMIN'"
|
||||
|
||||
###################################################################################################
|
||||
# database function
|
||||
###################################################################################################
|
||||
sql (){
|
||||
# for sqlite3:
|
||||
#sqlite3 homeserver.db "pragma busy_timeout=20000;$1" | awk '{print $2}'
|
||||
# for postgres:
|
||||
psql -A -t --dbname=synapse -c "$1" | grep -v 'Pager'
|
||||
}
|
||||
|
||||
###################################################################################################
|
||||
# get an access token
|
||||
###################################################################################################
|
||||
# for example externally by watching Riot in your browser's network inspector
|
||||
# or internally on the server locally, use this:
|
||||
TOKEN=$(sql "SELECT token FROM access_tokens WHERE user_id='$ADMIN' ORDER BY id DESC LIMIT 1")
|
||||
AUTH="Authorization: Bearer $TOKEN"
|
||||
|
||||
###################################################################################################
|
||||
# check, if your TOKEN works. For example this works:
|
||||
###################################################################################################
|
||||
# $ curl --header "$AUTH" "$API_URL/rooms/$ROOM/state/m.room.power_levels"
|
||||
|
||||
###################################################################################################
|
||||
# finally start pruning the room:
|
||||
###################################################################################################
|
||||
POSTDATA='{"delete_local_events":"true"}' # this will really delete local events, so the messages in the room really disappear unless they are restored by remote federation
|
||||
|
||||
for ROOM in "${ROOMS_ARRAY[@]}"; do
|
||||
echo "########################################### $(date) ################# "
|
||||
echo "pruning room: $ROOM ..."
|
||||
ROOM=${ROOM%#*}
|
||||
#set -x
|
||||
echo "check for alias in db..."
|
||||
# for postgres:
|
||||
sql "SELECT * FROM room_aliases WHERE room_id='$ROOM'"
|
||||
echo "get event..."
|
||||
# for postgres:
|
||||
EVENT_ID=$(sql "SELECT event_id FROM events WHERE type='m.room.message' AND received_ts<'$UNIX_TIMESTAMP' AND room_id='$ROOM' ORDER BY received_ts DESC LIMIT 1;")
|
||||
if [ "$EVENT_ID" == "" ]; then
|
||||
echo "no event $TIME"
|
||||
else
|
||||
echo "event: $EVENT_ID"
|
||||
SLEEP=2
|
||||
set -x
|
||||
# call purge
|
||||
OUT=$(curl --header "$AUTH" -s -d $POSTDATA POST "$API_URL/admin/purge_history/$ROOM/$EVENT_ID")
|
||||
PURGE_ID=$(echo "$OUT" |grep purge_id|cut -d'"' -f4 )
|
||||
if [ "$PURGE_ID" == "" ]; then
|
||||
# probably the history purge is already in progress for $ROOM
|
||||
: "continuing with next room"
|
||||
else
|
||||
while : ; do
|
||||
# get status of purge and sleep longer each time if still active
|
||||
sleep $SLEEP
|
||||
STATUS=$(curl --header "$AUTH" -s GET "$API_URL/admin/purge_history_status/$PURGE_ID" |grep status|cut -d'"' -f4)
|
||||
: "$ROOM --> Status: $STATUS"
|
||||
[[ "$STATUS" == "active" ]] || break
|
||||
SLEEP=$((SLEEP + 1))
|
||||
done
|
||||
fi
|
||||
set +x
|
||||
sleep 1
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
###################################################################################################
|
||||
# additionally
|
||||
###################################################################################################
|
||||
# to benefit from pruning large amounts of data, you need to call VACUUM to free the unused space.
|
||||
# This can take a very long time (hours) and the client have to be stopped while you do so:
|
||||
# $ synctl stop
|
||||
# $ sqlite3 -line homeserver.db "vacuum;"
|
||||
# $ synctl start
|
||||
|
||||
# This could be set, so you don't need to prune every time after deleting some rows:
|
||||
# $ sqlite3 homeserver.db "PRAGMA auto_vacuum = FULL;"
|
||||
# be cautious, it could make the database somewhat slow if there are a lot of deletions
|
||||
|
||||
exit
|
||||
54
contrib/purge_api/purge_remote_media.sh
Normal file
54
contrib/purge_api/purge_remote_media.sh
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
|
||||
DOMAIN=yourserver.tld
|
||||
# add this user as admin in your home server:
|
||||
ADMIN="@you_admin_username:$DOMAIN"
|
||||
|
||||
API_URL="$DOMAIN:8008/_matrix/client/r0"
|
||||
|
||||
# choose a time before which the messages should be pruned:
|
||||
# TIME='2016-08-31 23:59:59'
|
||||
TIME='12 months ago'
|
||||
|
||||
# creates a timestamp from the given time string:
|
||||
UNIX_TIMESTAMP=$(date +%s%3N --date='TZ="UTC+2" '"$TIME")
|
||||
|
||||
|
||||
###################################################################################################
|
||||
# database function
|
||||
###################################################################################################
|
||||
sql (){
|
||||
# for sqlite3:
|
||||
#sqlite3 homeserver.db "pragma busy_timeout=20000;$1" | awk '{print $2}'
|
||||
# for postgres:
|
||||
psql -A -t --dbname=synapse -c "$1" | grep -v 'Pager'
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# make the admin user a server admin in the database with
|
||||
###############################################################################
|
||||
# sql "UPDATE users SET admin=1 WHERE name LIKE '$ADMIN'"
|
||||
|
||||
###############################################################################
|
||||
# get an access token
|
||||
###############################################################################
|
||||
# for example externally by watching Riot in your browser's network inspector
|
||||
# or internally on the server locally, use this:
|
||||
TOKEN=$(sql "SELECT token FROM access_tokens WHERE user_id='$ADMIN' ORDER BY id DESC LIMIT 1")
|
||||
|
||||
###############################################################################
|
||||
# check, if your TOKEN works. For example this works:
|
||||
###############################################################################
|
||||
# curl --header "Authorization: Bearer $TOKEN" "$API_URL/rooms/$ROOM/state/m.room.power_levels"
|
||||
|
||||
###############################################################################
|
||||
# optional check size before
|
||||
###############################################################################
|
||||
# echo calculate used storage before ...
|
||||
# du -shc ../.synapse/media_store/*
|
||||
|
||||
###############################################################################
|
||||
# finally start pruning media:
|
||||
###############################################################################
|
||||
set -x # for debugging the generated string
|
||||
curl --header "Authorization: Bearer $TOKEN" -v POST "$API_URL/admin/purge_media_cache/?before_ts=$UNIX_TIMESTAMP"
|
||||
@@ -81,9 +81,40 @@ should be a matter of `pip install Jinja2`. On debian, try `apt-get install
|
||||
python-jinja2`.
|
||||
|
||||
Once this is complete, and the server has been restarted, try visiting
|
||||
`https://<server>/_matrix/consent`. If correctly configured, you should see a
|
||||
default policy document. It is now possible to manually construct URIs where
|
||||
users can give their consent.
|
||||
`https://<server>/_matrix/consent`. If correctly configured, this should give
|
||||
an error "Missing string query parameter 'u'". It is now possible to manually
|
||||
construct URIs where users can give their consent.
|
||||
|
||||
### Enabling consent tracking at registration
|
||||
|
||||
1. Add the following to your configuration:
|
||||
|
||||
```yaml
|
||||
user_consent:
|
||||
require_at_registration: true
|
||||
policy_name: "Privacy Policy" # or whatever you'd like to call the policy
|
||||
```
|
||||
|
||||
2. In your consent templates, make use of the `public_version` variable to
|
||||
see if an unauthenticated user is viewing the page. This is typically
|
||||
wrapped around the form that would be used to actually agree to the document:
|
||||
|
||||
```
|
||||
{% if not public_version %}
|
||||
<!-- The variables used here are only provided when the 'u' param is given to the homeserver -->
|
||||
<form method="post" action="consent">
|
||||
<input type="hidden" name="v" value="{{version}}"/>
|
||||
<input type="hidden" name="u" value="{{user}}"/>
|
||||
<input type="hidden" name="h" value="{{userhmac}}"/>
|
||||
<input type="submit" value="Sure thing!"/>
|
||||
</form>
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
3. Restart Synapse to apply the changes.
|
||||
|
||||
Visiting `https://<server>/_matrix/consent` should now give you a view of the privacy
|
||||
document. This is what users will be able to see when registering for accounts.
|
||||
|
||||
### Constructing the consent URI
|
||||
|
||||
@@ -108,7 +139,8 @@ query parameters:
|
||||
|
||||
Note that not providing a `u` parameter will be interpreted as wanting to view
|
||||
the document from an unauthenticated perspective, such as prior to registration.
|
||||
Therefore, the `h` parameter is not required in this scenario.
|
||||
Therefore, the `h` parameter is not required in this scenario. To enable this
|
||||
behaviour, set `require_at_registration` to `true` in your `user_consent` config.
|
||||
|
||||
|
||||
Sending users a server notice asking them to agree to the policy
|
||||
|
||||
@@ -154,10 +154,15 @@ def request_json(method, origin_name, origin_key, destination, path, content):
|
||||
s = requests.Session()
|
||||
s.mount("matrix://", MatrixConnectionAdapter())
|
||||
|
||||
headers = {"Host": destination, "Authorization": authorization_headers[0]}
|
||||
|
||||
if method == "POST":
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
result = s.request(
|
||||
method=method,
|
||||
url=dest,
|
||||
headers={"Host": destination, "Authorization": authorization_headers[0]},
|
||||
headers=headers,
|
||||
verify=False,
|
||||
data=content,
|
||||
)
|
||||
@@ -203,7 +208,7 @@ def main():
|
||||
parser.add_argument(
|
||||
"-X",
|
||||
"--method",
|
||||
help="HTTP method to use for the request. Defaults to GET if --data is"
|
||||
help="HTTP method to use for the request. Defaults to GET if --body is"
|
||||
"unspecified, POST if it is.",
|
||||
)
|
||||
|
||||
|
||||
@@ -3,13 +3,15 @@
|
||||
import argparse
|
||||
import getpass
|
||||
import sys
|
||||
import unicodedata
|
||||
|
||||
import bcrypt
|
||||
import yaml
|
||||
|
||||
bcrypt_rounds=12
|
||||
bcrypt_rounds = 12
|
||||
password_pepper = ""
|
||||
|
||||
|
||||
def prompt_for_pass():
|
||||
password = getpass.getpass("Password: ")
|
||||
|
||||
@@ -23,19 +25,27 @@ def prompt_for_pass():
|
||||
|
||||
return password
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Calculate the hash of a new password, so that passwords"
|
||||
" can be reset")
|
||||
description=(
|
||||
"Calculate the hash of a new password, so that passwords can be reset"
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p", "--password",
|
||||
"-p",
|
||||
"--password",
|
||||
default=None,
|
||||
help="New password for user. Will prompt if omitted.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c", "--config",
|
||||
"-c",
|
||||
"--config",
|
||||
type=argparse.FileType('r'),
|
||||
help="Path to server config file. Used to read in bcrypt_rounds and password_pepper.",
|
||||
help=(
|
||||
"Path to server config file. "
|
||||
"Used to read in bcrypt_rounds and password_pepper."
|
||||
),
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
@@ -49,4 +59,21 @@ if __name__ == "__main__":
|
||||
if not password:
|
||||
password = prompt_for_pass()
|
||||
|
||||
print bcrypt.hashpw(password + password_pepper, bcrypt.gensalt(bcrypt_rounds))
|
||||
# On Python 2, make sure we decode it to Unicode before we normalise it
|
||||
if isinstance(password, bytes):
|
||||
try:
|
||||
password = password.decode(sys.stdin.encoding)
|
||||
except UnicodeDecodeError:
|
||||
print(
|
||||
"ERROR! Your password is not decodable using your terminal encoding (%s)."
|
||||
% (sys.stdin.encoding,)
|
||||
)
|
||||
|
||||
pw = unicodedata.normalize("NFKC", password)
|
||||
|
||||
hashed = bcrypt.hashpw(
|
||||
pw.encode('utf8') + password_pepper.encode("utf8"),
|
||||
bcrypt.gensalt(bcrypt_rounds),
|
||||
).decode('ascii')
|
||||
|
||||
print(hashed)
|
||||
|
||||
@@ -42,6 +42,14 @@ DEFAULT_CONFIG = """\
|
||||
# until the user consents to the privacy policy. The value of the setting is
|
||||
# used as the text of the error.
|
||||
#
|
||||
# 'require_at_registration', if enabled, will add a step to the registration
|
||||
# process, similar to how captcha works. Users will be required to accept the
|
||||
# policy before their account is created.
|
||||
#
|
||||
# 'policy_name' is the display name of the policy users will see when registering
|
||||
# for an account. Has no effect unless `require_at_registration` is enabled.
|
||||
# Defaults to "Privacy Policy".
|
||||
#
|
||||
# user_consent:
|
||||
# template_dir: res/templates/privacy
|
||||
# version: 1.0
|
||||
@@ -54,6 +62,8 @@ DEFAULT_CONFIG = """\
|
||||
# block_events_error: >-
|
||||
# To continue using this homeserver you must review and agree to the
|
||||
# terms and conditions at %(consent_uri)s
|
||||
# require_at_registration: False
|
||||
# policy_name: Privacy Policy
|
||||
#
|
||||
"""
|
||||
|
||||
@@ -67,6 +77,8 @@ class ConsentConfig(Config):
|
||||
self.user_consent_server_notice_content = None
|
||||
self.user_consent_server_notice_to_guests = False
|
||||
self.block_events_without_consent_error = None
|
||||
self.user_consent_at_registration = False
|
||||
self.user_consent_policy_name = "Privacy Policy"
|
||||
|
||||
def read_config(self, config):
|
||||
consent_config = config.get("user_consent")
|
||||
@@ -83,6 +95,12 @@ class ConsentConfig(Config):
|
||||
self.user_consent_server_notice_to_guests = bool(consent_config.get(
|
||||
"send_server_notice_to_guests", False,
|
||||
))
|
||||
self.user_consent_at_registration = bool(consent_config.get(
|
||||
"require_at_registration", False,
|
||||
))
|
||||
self.user_consent_policy_name = consent_config.get(
|
||||
"policy_name", "Privacy Policy",
|
||||
)
|
||||
|
||||
def default_config(self, **kwargs):
|
||||
return DEFAULT_CONFIG
|
||||
|
||||
@@ -162,8 +162,30 @@ class FederationServer(FederationBase):
|
||||
p["age_ts"] = request_time - int(p["age"])
|
||||
del p["age"]
|
||||
|
||||
# We try and pull out an event ID so that if later checks fail we
|
||||
# can log something sensible. We don't mandate an event ID here in
|
||||
# case future event formats get rid of the key.
|
||||
possible_event_id = p.get("event_id", "<Unknown>")
|
||||
|
||||
# Now we get the room ID so that we can check that we know the
|
||||
# version of the room.
|
||||
room_id = p.get("room_id")
|
||||
if not room_id:
|
||||
logger.info(
|
||||
"Ignoring PDU as does not have a room_id. Event ID: %s",
|
||||
possible_event_id,
|
||||
)
|
||||
continue
|
||||
|
||||
try:
|
||||
# In future we will actually use the room version to parse the
|
||||
# PDU into an event.
|
||||
yield self.store.get_room_version(room_id)
|
||||
except NotFoundError:
|
||||
logger.info("Ignoring PDU for unknown room_id: %s", room_id)
|
||||
continue
|
||||
|
||||
event = event_from_pdu_json(p)
|
||||
room_id = event.room_id
|
||||
pdus_by_room.setdefault(room_id, []).append(event)
|
||||
|
||||
pdu_results = {}
|
||||
|
||||
@@ -472,7 +472,7 @@ class AuthHandler(BaseHandler):
|
||||
"privacy_policy": {
|
||||
"version": self.hs.config.user_consent_version,
|
||||
"en": {
|
||||
"name": "Privacy Policy",
|
||||
"name": self.hs.config.user_consent_policy_name,
|
||||
"url": "%s/_matrix/consent?v=%s" % (
|
||||
self.hs.config.public_baseurl,
|
||||
self.hs.config.user_consent_version,
|
||||
|
||||
@@ -19,7 +19,7 @@ from six import iteritems
|
||||
|
||||
from twisted.internet import defer
|
||||
|
||||
from synapse.api.errors import RoomKeysVersionError, StoreError, SynapseError
|
||||
from synapse.api.errors import NotFoundError, RoomKeysVersionError, StoreError
|
||||
from synapse.util.async_helpers import Linearizer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -55,6 +55,8 @@ class E2eRoomKeysHandler(object):
|
||||
room_id(string): room ID to get keys for, for None to get keys for all rooms
|
||||
session_id(string): session ID to get keys for, for None to get keys for all
|
||||
sessions
|
||||
Raises:
|
||||
NotFoundError: if the backup version does not exist
|
||||
Returns:
|
||||
A deferred list of dicts giving the session_data and message metadata for
|
||||
these room keys.
|
||||
@@ -63,13 +65,19 @@ class E2eRoomKeysHandler(object):
|
||||
# we deliberately take the lock to get keys so that changing the version
|
||||
# works atomically
|
||||
with (yield self._upload_linearizer.queue(user_id)):
|
||||
# make sure the backup version exists
|
||||
try:
|
||||
yield self.store.get_e2e_room_keys_version_info(user_id, version)
|
||||
except StoreError as e:
|
||||
if e.code == 404:
|
||||
raise NotFoundError("Unknown backup version")
|
||||
else:
|
||||
raise
|
||||
|
||||
results = yield self.store.get_e2e_room_keys(
|
||||
user_id, version, room_id, session_id
|
||||
)
|
||||
|
||||
if results['rooms'] == {}:
|
||||
raise SynapseError(404, "No room_keys found")
|
||||
|
||||
defer.returnValue(results)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
@@ -120,7 +128,7 @@ class E2eRoomKeysHandler(object):
|
||||
}
|
||||
|
||||
Raises:
|
||||
SynapseError: with code 404 if there are no versions defined
|
||||
NotFoundError: if there are no versions defined
|
||||
RoomKeysVersionError: if the uploaded version is not the current version
|
||||
"""
|
||||
|
||||
@@ -134,7 +142,7 @@ class E2eRoomKeysHandler(object):
|
||||
version_info = yield self.store.get_e2e_room_keys_version_info(user_id)
|
||||
except StoreError as e:
|
||||
if e.code == 404:
|
||||
raise SynapseError(404, "Version '%s' not found" % (version,))
|
||||
raise NotFoundError("Version '%s' not found" % (version,))
|
||||
else:
|
||||
raise
|
||||
|
||||
@@ -148,7 +156,7 @@ class E2eRoomKeysHandler(object):
|
||||
raise RoomKeysVersionError(current_version=version_info['version'])
|
||||
except StoreError as e:
|
||||
if e.code == 404:
|
||||
raise SynapseError(404, "Version '%s' not found" % (version,))
|
||||
raise NotFoundError("Version '%s' not found" % (version,))
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
@@ -202,27 +202,22 @@ class FederationHandler(BaseHandler):
|
||||
self.room_queues[room_id].append((pdu, origin))
|
||||
return
|
||||
|
||||
# If we're no longer in the room just ditch the event entirely. This
|
||||
# is probably an old server that has come back and thinks we're still
|
||||
# in the room (or we've been rejoined to the room by a state reset).
|
||||
# If we're not in the room just ditch the event entirely. This is
|
||||
# probably an old server that has come back and thinks we're still in
|
||||
# the room (or we've been rejoined to the room by a state reset).
|
||||
#
|
||||
# If we were never in the room then maybe our database got vaped and
|
||||
# we should check if we *are* in fact in the room. If we are then we
|
||||
# can magically rejoin the room.
|
||||
# Note that if we were never in the room then we would have already
|
||||
# dropped the event, since we wouldn't know the room version.
|
||||
is_in_room = yield self.auth.check_host_in_room(
|
||||
room_id,
|
||||
self.server_name
|
||||
)
|
||||
if not is_in_room:
|
||||
was_in_room = yield self.store.was_host_joined(
|
||||
pdu.room_id, self.server_name,
|
||||
logger.info(
|
||||
"[%s %s] Ignoring PDU from %s as we're not in the room",
|
||||
room_id, event_id, origin,
|
||||
)
|
||||
if was_in_room:
|
||||
logger.info(
|
||||
"[%s %s] Ignoring PDU from %s as we've left the room",
|
||||
room_id, event_id, origin,
|
||||
)
|
||||
defer.returnValue(None)
|
||||
defer.returnValue(None)
|
||||
|
||||
state = None
|
||||
auth_chain = []
|
||||
@@ -557,86 +552,54 @@ class FederationHandler(BaseHandler):
|
||||
room_id, event_id, event,
|
||||
)
|
||||
|
||||
# FIXME (erikj): Awful hack to make the case where we are not currently
|
||||
# in the room work
|
||||
# If state and auth_chain are None, then we don't need to do this check
|
||||
# as we already know we have enough state in the DB to handle this
|
||||
# event.
|
||||
if state and auth_chain and not event.internal_metadata.is_outlier():
|
||||
is_in_room = yield self.auth.check_host_in_room(
|
||||
room_id,
|
||||
self.server_name
|
||||
)
|
||||
else:
|
||||
is_in_room = True
|
||||
event_ids = set()
|
||||
if state:
|
||||
event_ids |= {e.event_id for e in state}
|
||||
if auth_chain:
|
||||
event_ids |= {e.event_id for e in auth_chain}
|
||||
|
||||
seen_ids = yield self.store.have_seen_events(event_ids)
|
||||
|
||||
if state and auth_chain is not None:
|
||||
# If we have any state or auth_chain given to us by the replication
|
||||
# layer, then we should handle them (if we haven't before.)
|
||||
|
||||
event_infos = []
|
||||
|
||||
for e in itertools.chain(auth_chain, state):
|
||||
if e.event_id in seen_ids:
|
||||
continue
|
||||
e.internal_metadata.outlier = True
|
||||
auth_ids = e.auth_event_ids()
|
||||
auth = {
|
||||
(e.type, e.state_key): e for e in auth_chain
|
||||
if e.event_id in auth_ids or e.type == EventTypes.Create
|
||||
}
|
||||
event_infos.append({
|
||||
"event": e,
|
||||
"auth_events": auth,
|
||||
})
|
||||
seen_ids.add(e.event_id)
|
||||
|
||||
if not is_in_room:
|
||||
logger.info(
|
||||
"[%s %s] Got event for room we're not in",
|
||||
room_id, event_id,
|
||||
"[%s %s] persisting newly-received auth/state events %s",
|
||||
room_id, event_id, [e["event"].event_id for e in event_infos]
|
||||
)
|
||||
yield self._handle_new_events(origin, event_infos)
|
||||
|
||||
try:
|
||||
yield self._persist_auth_tree(
|
||||
origin, auth_chain, state, event
|
||||
)
|
||||
except AuthError as e:
|
||||
raise FederationError(
|
||||
"ERROR",
|
||||
e.code,
|
||||
e.msg,
|
||||
affected=event_id,
|
||||
)
|
||||
|
||||
else:
|
||||
event_ids = set()
|
||||
if state:
|
||||
event_ids |= {e.event_id for e in state}
|
||||
if auth_chain:
|
||||
event_ids |= {e.event_id for e in auth_chain}
|
||||
|
||||
seen_ids = yield self.store.have_seen_events(event_ids)
|
||||
|
||||
if state and auth_chain is not None:
|
||||
# If we have any state or auth_chain given to us by the replication
|
||||
# layer, then we should handle them (if we haven't before.)
|
||||
|
||||
event_infos = []
|
||||
|
||||
for e in itertools.chain(auth_chain, state):
|
||||
if e.event_id in seen_ids:
|
||||
continue
|
||||
e.internal_metadata.outlier = True
|
||||
auth_ids = e.auth_event_ids()
|
||||
auth = {
|
||||
(e.type, e.state_key): e for e in auth_chain
|
||||
if e.event_id in auth_ids or e.type == EventTypes.Create
|
||||
}
|
||||
event_infos.append({
|
||||
"event": e,
|
||||
"auth_events": auth,
|
||||
})
|
||||
seen_ids.add(e.event_id)
|
||||
|
||||
logger.info(
|
||||
"[%s %s] persisting newly-received auth/state events %s",
|
||||
room_id, event_id, [e["event"].event_id for e in event_infos]
|
||||
)
|
||||
yield self._handle_new_events(origin, event_infos)
|
||||
|
||||
try:
|
||||
context = yield self._handle_new_event(
|
||||
origin,
|
||||
event,
|
||||
state=state,
|
||||
)
|
||||
except AuthError as e:
|
||||
raise FederationError(
|
||||
"ERROR",
|
||||
e.code,
|
||||
e.msg,
|
||||
affected=event.event_id,
|
||||
)
|
||||
try:
|
||||
context = yield self._handle_new_event(
|
||||
origin,
|
||||
event,
|
||||
state=state,
|
||||
)
|
||||
except AuthError as e:
|
||||
raise FederationError(
|
||||
"ERROR",
|
||||
e.code,
|
||||
e.msg,
|
||||
affected=event.event_id,
|
||||
)
|
||||
|
||||
room = yield self.store.get_room(room_id)
|
||||
|
||||
|
||||
@@ -468,13 +468,13 @@ def set_cors_headers(request):
|
||||
Args:
|
||||
request (twisted.web.http.Request): The http request to add CORs to.
|
||||
"""
|
||||
request.setHeader("Access-Control-Allow-Origin", "*")
|
||||
request.setHeader(b"Access-Control-Allow-Origin", b"*")
|
||||
request.setHeader(
|
||||
"Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"
|
||||
b"Access-Control-Allow-Methods", b"GET, POST, PUT, DELETE, OPTIONS"
|
||||
)
|
||||
request.setHeader(
|
||||
"Access-Control-Allow-Headers",
|
||||
"Origin, X-Requested-With, Content-Type, Accept, Authorization"
|
||||
b"Access-Control-Allow-Headers",
|
||||
b"Origin, X-Requested-With, Content-Type, Accept, Authorization"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -121,16 +121,15 @@ def parse_string(request, name, default=None, required=False,
|
||||
|
||||
Args:
|
||||
request: the twisted HTTP request.
|
||||
name (bytes/unicode): the name of the query parameter.
|
||||
default (bytes/unicode|None): value to use if the parameter is absent,
|
||||
name (bytes|unicode): the name of the query parameter.
|
||||
default (bytes|unicode|None): value to use if the parameter is absent,
|
||||
defaults to None. Must be bytes if encoding is None.
|
||||
required (bool): whether to raise a 400 SynapseError if the
|
||||
parameter is absent, defaults to False.
|
||||
allowed_values (list[bytes/unicode]): List of allowed values for the
|
||||
allowed_values (list[bytes|unicode]): List of allowed values for the
|
||||
string, or None if any value is allowed, defaults to None. Must be
|
||||
the same type as name, if given.
|
||||
encoding: The encoding to decode the name to, and decode the string
|
||||
content with.
|
||||
encoding (str|None): The encoding to decode the string content with.
|
||||
|
||||
Returns:
|
||||
bytes/unicode|None: A string value or the default. Unicode if encoding
|
||||
|
||||
@@ -360,7 +360,7 @@ class RegisterRestServlet(RestServlet):
|
||||
])
|
||||
|
||||
# Append m.login.terms to all flows if we're requiring consent
|
||||
if self.hs.config.block_events_without_consent_error is not None:
|
||||
if self.hs.config.user_consent_at_registration:
|
||||
new_flows = []
|
||||
for flow in flows:
|
||||
flow.append(LoginType.TERMS)
|
||||
|
||||
@@ -17,7 +17,7 @@ import logging
|
||||
|
||||
from twisted.internet import defer
|
||||
|
||||
from synapse.api.errors import Codes, SynapseError
|
||||
from synapse.api.errors import Codes, NotFoundError, SynapseError
|
||||
from synapse.http.servlet import (
|
||||
RestServlet,
|
||||
parse_json_object_from_request,
|
||||
@@ -208,10 +208,25 @@ class RoomKeysServlet(RestServlet):
|
||||
user_id, version, room_id, session_id
|
||||
)
|
||||
|
||||
# Convert room_keys to the right format to return.
|
||||
if session_id:
|
||||
room_keys = room_keys['rooms'][room_id]['sessions'][session_id]
|
||||
# If the client requests a specific session, but that session was
|
||||
# not backed up, then return an M_NOT_FOUND.
|
||||
if room_keys['rooms'] == {}:
|
||||
raise NotFoundError("No room_keys found")
|
||||
else:
|
||||
room_keys = room_keys['rooms'][room_id]['sessions'][session_id]
|
||||
elif room_id:
|
||||
room_keys = room_keys['rooms'][room_id]
|
||||
# If the client requests all sessions from a room, but no sessions
|
||||
# are found, then return an empty result rather than an error, so
|
||||
# that clients don't have to handle an error condition, and an
|
||||
# empty result is valid. (Similarly if the client requests all
|
||||
# sessions from the backup, but in that case, room_keys is already
|
||||
# in the right format, so we don't need to do anything about it.)
|
||||
if room_keys['rooms'] == {}:
|
||||
room_keys = {'sessions': {}}
|
||||
else:
|
||||
room_keys = room_keys['rooms'][room_id]
|
||||
|
||||
defer.returnValue((200, room_keys))
|
||||
|
||||
|
||||
@@ -143,9 +143,9 @@ class ConsentResource(Resource):
|
||||
has_consented = False
|
||||
public_version = username == ""
|
||||
if not public_version:
|
||||
userhmac = parse_string(request, "h", required=True, encoding=None)
|
||||
userhmac_bytes = parse_string(request, "h", required=True, encoding=None)
|
||||
|
||||
self._check_hash(username, userhmac)
|
||||
self._check_hash(username, userhmac_bytes)
|
||||
|
||||
if username.startswith('@'):
|
||||
qualified_user_id = username
|
||||
@@ -155,13 +155,18 @@ class ConsentResource(Resource):
|
||||
u = yield self.store.get_user_by_id(qualified_user_id)
|
||||
if u is None:
|
||||
raise NotFoundError("Unknown user")
|
||||
|
||||
has_consented = u["consent_version"] == version
|
||||
userhmac = userhmac_bytes.decode("ascii")
|
||||
|
||||
try:
|
||||
self._render_template(
|
||||
request, "%s.html" % (version,),
|
||||
user=username, userhmac=userhmac, version=version,
|
||||
has_consented=has_consented, public_version=public_version,
|
||||
user=username,
|
||||
userhmac=userhmac,
|
||||
version=version,
|
||||
has_consented=has_consented,
|
||||
public_version=public_version,
|
||||
)
|
||||
except TemplateNotFound:
|
||||
raise NotFoundError("Unknown policy version")
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
# 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.
|
||||
|
||||
import cgi
|
||||
import datetime
|
||||
import errno
|
||||
@@ -24,6 +25,7 @@ import shutil
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import six
|
||||
from six import string_types
|
||||
from six.moves import urllib_parse as urlparse
|
||||
|
||||
@@ -98,7 +100,7 @@ class PreviewUrlResource(Resource):
|
||||
# XXX: if get_user_by_req fails, what should we do in an async render?
|
||||
requester = yield self.auth.get_user_by_req(request)
|
||||
url = parse_string(request, "url")
|
||||
if "ts" in request.args:
|
||||
if b"ts" in request.args:
|
||||
ts = parse_integer(request, "ts")
|
||||
else:
|
||||
ts = self.clock.time_msec()
|
||||
@@ -180,7 +182,12 @@ class PreviewUrlResource(Resource):
|
||||
cache_result["expires_ts"] > ts and
|
||||
cache_result["response_code"] / 100 == 2
|
||||
):
|
||||
defer.returnValue(cache_result["og"])
|
||||
# It may be stored as text in the database, not as bytes (such as
|
||||
# PostgreSQL). If so, encode it back before handing it on.
|
||||
og = cache_result["og"]
|
||||
if isinstance(og, six.text_type):
|
||||
og = og.encode('utf8')
|
||||
defer.returnValue(og)
|
||||
return
|
||||
|
||||
media_info = yield self._download_url(url, user)
|
||||
@@ -213,14 +220,17 @@ class PreviewUrlResource(Resource):
|
||||
elif _is_html(media_info['media_type']):
|
||||
# TODO: somehow stop a big HTML tree from exploding synapse's RAM
|
||||
|
||||
file = open(media_info['filename'])
|
||||
body = file.read()
|
||||
file.close()
|
||||
with open(media_info['filename'], 'rb') as file:
|
||||
body = file.read()
|
||||
|
||||
# clobber the encoding from the content-type, or default to utf-8
|
||||
# XXX: this overrides any <meta/> or XML charset headers in the body
|
||||
# which may pose problems, but so far seems to work okay.
|
||||
match = re.match(r'.*; *charset=(.*?)(;|$)', media_info['media_type'], re.I)
|
||||
match = re.match(
|
||||
r'.*; *charset="?(.*?)"?(;|$)',
|
||||
media_info['media_type'],
|
||||
re.I
|
||||
)
|
||||
encoding = match.group(1) if match else "utf-8"
|
||||
|
||||
og = decode_and_calc_og(body, media_info['uri'], encoding)
|
||||
|
||||
@@ -118,6 +118,11 @@ class EndToEndRoomKeyStore(SQLBaseStore):
|
||||
these room keys.
|
||||
"""
|
||||
|
||||
try:
|
||||
version = int(version)
|
||||
except ValueError:
|
||||
defer.returnValue({'rooms': {}})
|
||||
|
||||
keyvalues = {
|
||||
"user_id": user_id,
|
||||
"version": version,
|
||||
@@ -212,14 +217,23 @@ class EndToEndRoomKeyStore(SQLBaseStore):
|
||||
Raises:
|
||||
StoreError: with code 404 if there are no e2e_room_keys_versions present
|
||||
Returns:
|
||||
A deferred dict giving the info metadata for this backup version
|
||||
A deferred dict giving the info metadata for this backup version, with
|
||||
fields including:
|
||||
version(str)
|
||||
algorithm(str)
|
||||
auth_data(object): opaque dict supplied by the client
|
||||
"""
|
||||
|
||||
def _get_e2e_room_keys_version_info_txn(txn):
|
||||
if version is None:
|
||||
this_version = self._get_current_version(txn, user_id)
|
||||
else:
|
||||
this_version = version
|
||||
try:
|
||||
this_version = int(version)
|
||||
except ValueError:
|
||||
# Our versions are all ints so if we can't convert it to an integer,
|
||||
# it isn't there.
|
||||
raise StoreError(404, "No row found")
|
||||
|
||||
result = self._simple_select_one_txn(
|
||||
txn,
|
||||
@@ -236,6 +250,7 @@ class EndToEndRoomKeyStore(SQLBaseStore):
|
||||
),
|
||||
)
|
||||
result["auth_data"] = json.loads(result["auth_data"])
|
||||
result["version"] = str(result["version"])
|
||||
return result
|
||||
|
||||
return self.runInteraction(
|
||||
|
||||
@@ -40,7 +40,10 @@ class EndToEndKeyStore(SQLBaseStore):
|
||||
allow_none=True,
|
||||
)
|
||||
|
||||
new_key_json = encode_canonical_json(device_keys)
|
||||
# In py3 we need old_key_json to match new_key_json type. The DB
|
||||
# returns unicode while encode_canonical_json returns bytes.
|
||||
new_key_json = encode_canonical_json(device_keys).decode("utf-8")
|
||||
|
||||
if old_key_json == new_key_json:
|
||||
return False
|
||||
|
||||
|
||||
53
synapse/storage/schema/delta/52/e2e_room_keys.sql
Normal file
53
synapse/storage/schema/delta/52/e2e_room_keys.sql
Normal file
@@ -0,0 +1,53 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
/* Change version column to an integer so we can do MAX() sensibly
|
||||
*/
|
||||
CREATE TABLE e2e_room_keys_versions_new (
|
||||
user_id TEXT NOT NULL,
|
||||
version BIGINT NOT NULL,
|
||||
algorithm TEXT NOT NULL,
|
||||
auth_data TEXT NOT NULL,
|
||||
deleted SMALLINT DEFAULT 0 NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO e2e_room_keys_versions_new
|
||||
SELECT user_id, CAST(version as BIGINT), algorithm, auth_data, deleted FROM e2e_room_keys_versions;
|
||||
|
||||
DROP TABLE e2e_room_keys_versions;
|
||||
ALTER TABLE e2e_room_keys_versions_new RENAME TO e2e_room_keys_versions;
|
||||
|
||||
CREATE UNIQUE INDEX e2e_room_keys_versions_idx ON e2e_room_keys_versions(user_id, version);
|
||||
|
||||
/* Change e2e_rooms_keys to match
|
||||
*/
|
||||
CREATE TABLE e2e_room_keys_new (
|
||||
user_id TEXT NOT NULL,
|
||||
room_id TEXT NOT NULL,
|
||||
session_id TEXT NOT NULL,
|
||||
version BIGINT NOT NULL,
|
||||
first_message_index INT,
|
||||
forwarded_count INT,
|
||||
is_verified BOOLEAN,
|
||||
session_data TEXT NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO e2e_room_keys_new
|
||||
SELECT user_id, room_id, session_id, CAST(version as BIGINT), first_message_index, forwarded_count, is_verified, session_data FROM e2e_room_keys;
|
||||
|
||||
DROP TABLE e2e_room_keys;
|
||||
ALTER TABLE e2e_room_keys_new RENAME TO e2e_room_keys;
|
||||
|
||||
CREATE UNIQUE INDEX e2e_room_keys_idx ON e2e_room_keys(user_id, room_id, session_id);
|
||||
@@ -169,8 +169,8 @@ class E2eRoomKeysHandlerTestCase(unittest.TestCase):
|
||||
self.assertEqual(res, 404)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_get_missing_room_keys(self):
|
||||
"""Check that we get a 404 on querying missing room_keys
|
||||
def test_get_missing_backup(self):
|
||||
"""Check that we get a 404 on querying missing backup
|
||||
"""
|
||||
res = None
|
||||
try:
|
||||
@@ -179,19 +179,20 @@ class E2eRoomKeysHandlerTestCase(unittest.TestCase):
|
||||
res = e.code
|
||||
self.assertEqual(res, 404)
|
||||
|
||||
# check we also get a 404 even if the version is valid
|
||||
@defer.inlineCallbacks
|
||||
def test_get_missing_room_keys(self):
|
||||
"""Check we get an empty response from an empty backup
|
||||
"""
|
||||
version = yield self.handler.create_version(self.local_user, {
|
||||
"algorithm": "m.megolm_backup.v1",
|
||||
"auth_data": "first_version_auth_data",
|
||||
})
|
||||
self.assertEqual(version, "1")
|
||||
|
||||
res = None
|
||||
try:
|
||||
yield self.handler.get_room_keys(self.local_user, version)
|
||||
except errors.SynapseError as e:
|
||||
res = e.code
|
||||
self.assertEqual(res, 404)
|
||||
res = yield self.handler.get_room_keys(self.local_user, version)
|
||||
self.assertDictEqual(res, {
|
||||
"rooms": {}
|
||||
})
|
||||
|
||||
# TODO: test the locking semantics when uploading room_keys,
|
||||
# although this is probably best done in sytest
|
||||
@@ -345,17 +346,15 @@ class E2eRoomKeysHandlerTestCase(unittest.TestCase):
|
||||
# check for bulk-delete
|
||||
yield self.handler.upload_room_keys(self.local_user, version, room_keys)
|
||||
yield self.handler.delete_room_keys(self.local_user, version)
|
||||
res = None
|
||||
try:
|
||||
yield self.handler.get_room_keys(
|
||||
self.local_user,
|
||||
version,
|
||||
room_id="!abc:matrix.org",
|
||||
session_id="c0ff33",
|
||||
)
|
||||
except errors.SynapseError as e:
|
||||
res = e.code
|
||||
self.assertEqual(res, 404)
|
||||
res = yield self.handler.get_room_keys(
|
||||
self.local_user,
|
||||
version,
|
||||
room_id="!abc:matrix.org",
|
||||
session_id="c0ff33",
|
||||
)
|
||||
self.assertDictEqual(res, {
|
||||
"rooms": {}
|
||||
})
|
||||
|
||||
# check for bulk-delete per room
|
||||
yield self.handler.upload_room_keys(self.local_user, version, room_keys)
|
||||
@@ -364,17 +363,15 @@ class E2eRoomKeysHandlerTestCase(unittest.TestCase):
|
||||
version,
|
||||
room_id="!abc:matrix.org",
|
||||
)
|
||||
res = None
|
||||
try:
|
||||
yield self.handler.get_room_keys(
|
||||
self.local_user,
|
||||
version,
|
||||
room_id="!abc:matrix.org",
|
||||
session_id="c0ff33",
|
||||
)
|
||||
except errors.SynapseError as e:
|
||||
res = e.code
|
||||
self.assertEqual(res, 404)
|
||||
res = yield self.handler.get_room_keys(
|
||||
self.local_user,
|
||||
version,
|
||||
room_id="!abc:matrix.org",
|
||||
session_id="c0ff33",
|
||||
)
|
||||
self.assertDictEqual(res, {
|
||||
"rooms": {}
|
||||
})
|
||||
|
||||
# check for bulk-delete per session
|
||||
yield self.handler.upload_room_keys(self.local_user, version, room_keys)
|
||||
@@ -384,14 +381,12 @@ class E2eRoomKeysHandlerTestCase(unittest.TestCase):
|
||||
room_id="!abc:matrix.org",
|
||||
session_id="c0ff33",
|
||||
)
|
||||
res = None
|
||||
try:
|
||||
yield self.handler.get_room_keys(
|
||||
self.local_user,
|
||||
version,
|
||||
room_id="!abc:matrix.org",
|
||||
session_id="c0ff33",
|
||||
)
|
||||
except errors.SynapseError as e:
|
||||
res = e.code
|
||||
self.assertEqual(res, 404)
|
||||
res = yield self.handler.get_room_keys(
|
||||
self.local_user,
|
||||
version,
|
||||
room_id="!abc:matrix.org",
|
||||
session_id="c0ff33",
|
||||
)
|
||||
self.assertDictEqual(res, {
|
||||
"rooms": {}
|
||||
})
|
||||
|
||||
@@ -60,6 +60,13 @@ class ConsentResourceTestCase(unittest.HomeserverTestCase):
|
||||
hs = self.setup_test_homeserver(config=config)
|
||||
return hs
|
||||
|
||||
def test_render_public_consent(self):
|
||||
"""You can observe the terms form without specifying a user"""
|
||||
resource = consent_resource.ConsentResource(self.hs)
|
||||
request, channel = self.make_request("GET", "/consent?v=1", shorthand=False)
|
||||
render(request, resource, self.reactor)
|
||||
self.assertEqual(channel.code, 200)
|
||||
|
||||
def test_accept_consent(self):
|
||||
"""
|
||||
A user can use the consent form to accept the terms.
|
||||
|
||||
@@ -19,24 +19,17 @@ import json
|
||||
|
||||
from mock import Mock
|
||||
|
||||
from synapse.http.server import JsonResource
|
||||
from synapse.rest.client.v1.admin import register_servlets
|
||||
from synapse.util import Clock
|
||||
|
||||
from tests import unittest
|
||||
from tests.server import (
|
||||
ThreadedMemoryReactorClock,
|
||||
make_request,
|
||||
render,
|
||||
setup_test_homeserver,
|
||||
)
|
||||
|
||||
|
||||
class UserRegisterTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
class UserRegisterTestCase(unittest.HomeserverTestCase):
|
||||
|
||||
servlets = [register_servlets]
|
||||
|
||||
def make_homeserver(self, reactor, clock):
|
||||
|
||||
self.clock = ThreadedMemoryReactorClock()
|
||||
self.hs_clock = Clock(self.clock)
|
||||
self.url = "/_matrix/client/r0/admin/register"
|
||||
|
||||
self.registration_handler = Mock()
|
||||
@@ -50,17 +43,14 @@ class UserRegisterTestCase(unittest.TestCase):
|
||||
|
||||
self.secrets = Mock()
|
||||
|
||||
self.hs = setup_test_homeserver(
|
||||
self.addCleanup, http_client=None, clock=self.hs_clock, reactor=self.clock
|
||||
)
|
||||
self.hs = self.setup_test_homeserver()
|
||||
|
||||
self.hs.config.registration_shared_secret = u"shared"
|
||||
|
||||
self.hs.get_media_repository = Mock()
|
||||
self.hs.get_deactivate_account_handler = Mock()
|
||||
|
||||
self.resource = JsonResource(self.hs)
|
||||
register_servlets(self.hs, self.resource)
|
||||
return self.hs
|
||||
|
||||
def test_disabled(self):
|
||||
"""
|
||||
@@ -69,8 +59,8 @@ class UserRegisterTestCase(unittest.TestCase):
|
||||
"""
|
||||
self.hs.config.registration_shared_secret = None
|
||||
|
||||
request, channel = make_request("POST", self.url, b'{}')
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("POST", self.url, b'{}')
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual(
|
||||
@@ -87,8 +77,8 @@ class UserRegisterTestCase(unittest.TestCase):
|
||||
|
||||
self.hs.get_secrets = Mock(return_value=secrets)
|
||||
|
||||
request, channel = make_request("GET", self.url)
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("GET", self.url)
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(channel.json_body, {"nonce": "abcd"})
|
||||
|
||||
@@ -97,25 +87,25 @@ class UserRegisterTestCase(unittest.TestCase):
|
||||
Calling GET on the endpoint will return a randomised nonce, which will
|
||||
only last for SALT_TIMEOUT (60s).
|
||||
"""
|
||||
request, channel = make_request("GET", self.url)
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("GET", self.url)
|
||||
self.render(request)
|
||||
nonce = channel.json_body["nonce"]
|
||||
|
||||
# 59 seconds
|
||||
self.clock.advance(59)
|
||||
self.reactor.advance(59)
|
||||
|
||||
body = json.dumps({"nonce": nonce})
|
||||
request, channel = make_request("POST", self.url, body.encode('utf8'))
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("POST", self.url, body.encode('utf8'))
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual('username must be specified', channel.json_body["error"])
|
||||
|
||||
# 61 seconds
|
||||
self.clock.advance(2)
|
||||
self.reactor.advance(2)
|
||||
|
||||
request, channel = make_request("POST", self.url, body.encode('utf8'))
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("POST", self.url, body.encode('utf8'))
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual('unrecognised nonce', channel.json_body["error"])
|
||||
@@ -124,8 +114,8 @@ class UserRegisterTestCase(unittest.TestCase):
|
||||
"""
|
||||
Only the provided nonce can be used, as it's checked in the MAC.
|
||||
"""
|
||||
request, channel = make_request("GET", self.url)
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("GET", self.url)
|
||||
self.render(request)
|
||||
nonce = channel.json_body["nonce"]
|
||||
|
||||
want_mac = hmac.new(key=b"shared", digestmod=hashlib.sha1)
|
||||
@@ -141,8 +131,8 @@ class UserRegisterTestCase(unittest.TestCase):
|
||||
"mac": want_mac,
|
||||
}
|
||||
)
|
||||
request, channel = make_request("POST", self.url, body.encode('utf8'))
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("POST", self.url, body.encode('utf8'))
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(403, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual("HMAC incorrect", channel.json_body["error"])
|
||||
@@ -152,8 +142,8 @@ class UserRegisterTestCase(unittest.TestCase):
|
||||
When the correct nonce is provided, and the right key is provided, the
|
||||
user is registered.
|
||||
"""
|
||||
request, channel = make_request("GET", self.url)
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("GET", self.url)
|
||||
self.render(request)
|
||||
nonce = channel.json_body["nonce"]
|
||||
|
||||
want_mac = hmac.new(key=b"shared", digestmod=hashlib.sha1)
|
||||
@@ -169,8 +159,8 @@ class UserRegisterTestCase(unittest.TestCase):
|
||||
"mac": want_mac,
|
||||
}
|
||||
)
|
||||
request, channel = make_request("POST", self.url, body.encode('utf8'))
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("POST", self.url, body.encode('utf8'))
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual("@bob:test", channel.json_body["user_id"])
|
||||
@@ -179,8 +169,8 @@ class UserRegisterTestCase(unittest.TestCase):
|
||||
"""
|
||||
A valid unrecognised nonce.
|
||||
"""
|
||||
request, channel = make_request("GET", self.url)
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("GET", self.url)
|
||||
self.render(request)
|
||||
nonce = channel.json_body["nonce"]
|
||||
|
||||
want_mac = hmac.new(key=b"shared", digestmod=hashlib.sha1)
|
||||
@@ -196,15 +186,15 @@ class UserRegisterTestCase(unittest.TestCase):
|
||||
"mac": want_mac,
|
||||
}
|
||||
)
|
||||
request, channel = make_request("POST", self.url, body.encode('utf8'))
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("POST", self.url, body.encode('utf8'))
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual("@bob:test", channel.json_body["user_id"])
|
||||
|
||||
# Now, try and reuse it
|
||||
request, channel = make_request("POST", self.url, body.encode('utf8'))
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("POST", self.url, body.encode('utf8'))
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual('unrecognised nonce', channel.json_body["error"])
|
||||
@@ -217,8 +207,8 @@ class UserRegisterTestCase(unittest.TestCase):
|
||||
"""
|
||||
|
||||
def nonce():
|
||||
request, channel = make_request("GET", self.url)
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("GET", self.url)
|
||||
self.render(request)
|
||||
return channel.json_body["nonce"]
|
||||
|
||||
#
|
||||
@@ -227,8 +217,8 @@ class UserRegisterTestCase(unittest.TestCase):
|
||||
|
||||
# Must be present
|
||||
body = json.dumps({})
|
||||
request, channel = make_request("POST", self.url, body.encode('utf8'))
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("POST", self.url, body.encode('utf8'))
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual('nonce must be specified', channel.json_body["error"])
|
||||
@@ -239,32 +229,32 @@ class UserRegisterTestCase(unittest.TestCase):
|
||||
|
||||
# Must be present
|
||||
body = json.dumps({"nonce": nonce()})
|
||||
request, channel = make_request("POST", self.url, body.encode('utf8'))
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("POST", self.url, body.encode('utf8'))
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual('username must be specified', channel.json_body["error"])
|
||||
|
||||
# Must be a string
|
||||
body = json.dumps({"nonce": nonce(), "username": 1234})
|
||||
request, channel = make_request("POST", self.url, body.encode('utf8'))
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("POST", self.url, body.encode('utf8'))
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual('Invalid username', channel.json_body["error"])
|
||||
|
||||
# Must not have null bytes
|
||||
body = json.dumps({"nonce": nonce(), "username": u"abcd\u0000"})
|
||||
request, channel = make_request("POST", self.url, body.encode('utf8'))
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("POST", self.url, body.encode('utf8'))
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual('Invalid username', channel.json_body["error"])
|
||||
|
||||
# Must not have null bytes
|
||||
body = json.dumps({"nonce": nonce(), "username": "a" * 1000})
|
||||
request, channel = make_request("POST", self.url, body.encode('utf8'))
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("POST", self.url, body.encode('utf8'))
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual('Invalid username', channel.json_body["error"])
|
||||
@@ -275,16 +265,16 @@ class UserRegisterTestCase(unittest.TestCase):
|
||||
|
||||
# Must be present
|
||||
body = json.dumps({"nonce": nonce(), "username": "a"})
|
||||
request, channel = make_request("POST", self.url, body.encode('utf8'))
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("POST", self.url, body.encode('utf8'))
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual('password must be specified', channel.json_body["error"])
|
||||
|
||||
# Must be a string
|
||||
body = json.dumps({"nonce": nonce(), "username": "a", "password": 1234})
|
||||
request, channel = make_request("POST", self.url, body.encode('utf8'))
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("POST", self.url, body.encode('utf8'))
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual('Invalid password', channel.json_body["error"])
|
||||
@@ -293,16 +283,16 @@ class UserRegisterTestCase(unittest.TestCase):
|
||||
body = json.dumps(
|
||||
{"nonce": nonce(), "username": "a", "password": u"abcd\u0000"}
|
||||
)
|
||||
request, channel = make_request("POST", self.url, body.encode('utf8'))
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("POST", self.url, body.encode('utf8'))
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual('Invalid password', channel.json_body["error"])
|
||||
|
||||
# Super long
|
||||
body = json.dumps({"nonce": nonce(), "username": "a", "password": "A" * 1000})
|
||||
request, channel = make_request("POST", self.url, body.encode('utf8'))
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request("POST", self.url, body.encode('utf8'))
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
|
||||
self.assertEqual('Invalid password', channel.json_body["error"])
|
||||
|
||||
@@ -45,11 +45,11 @@ class CreateUserServletTestCase(unittest.TestCase):
|
||||
)
|
||||
|
||||
handlers = Mock(registration_handler=self.registration_handler)
|
||||
self.clock = MemoryReactorClock()
|
||||
self.hs_clock = Clock(self.clock)
|
||||
self.reactor = MemoryReactorClock()
|
||||
self.hs_clock = Clock(self.reactor)
|
||||
|
||||
self.hs = self.hs = setup_test_homeserver(
|
||||
self.addCleanup, http_client=None, clock=self.hs_clock, reactor=self.clock
|
||||
self.addCleanup, http_client=None, clock=self.hs_clock, reactor=self.reactor
|
||||
)
|
||||
self.hs.get_datastore = Mock(return_value=self.datastore)
|
||||
self.hs.get_handlers = Mock(return_value=handlers)
|
||||
@@ -76,8 +76,8 @@ class CreateUserServletTestCase(unittest.TestCase):
|
||||
return_value=(user_id, token)
|
||||
)
|
||||
|
||||
request, channel = make_request(b"POST", url, request_data)
|
||||
render(request, res, self.clock)
|
||||
request, channel = make_request(self.reactor, b"POST", url, request_data)
|
||||
render(request, res, self.reactor)
|
||||
|
||||
self.assertEquals(channel.result["code"], b"200")
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ class RestHelper(object):
|
||||
path = path + "?access_token=%s" % tok
|
||||
|
||||
request, channel = make_request(
|
||||
"POST", path, json.dumps(content).encode('utf8')
|
||||
self.hs.get_reactor(), "POST", path, json.dumps(content).encode('utf8')
|
||||
)
|
||||
render(request, self.resource, self.hs.get_reactor())
|
||||
|
||||
@@ -217,7 +217,9 @@ class RestHelper(object):
|
||||
|
||||
data = {"membership": membership}
|
||||
|
||||
request, channel = make_request("PUT", path, json.dumps(data).encode('utf8'))
|
||||
request, channel = make_request(
|
||||
self.hs.get_reactor(), "PUT", path, json.dumps(data).encode('utf8')
|
||||
)
|
||||
|
||||
render(request, self.resource, self.hs.get_reactor())
|
||||
|
||||
@@ -228,18 +230,6 @@ class RestHelper(object):
|
||||
|
||||
self.auth_user_id = temp_id
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def register(self, user_id):
|
||||
(code, response) = yield self.mock_resource.trigger(
|
||||
"POST",
|
||||
"/_matrix/client/r0/register",
|
||||
json.dumps(
|
||||
{"user": user_id, "password": "test", "type": "m.login.password"}
|
||||
),
|
||||
)
|
||||
self.assertEquals(200, code)
|
||||
defer.returnValue(response)
|
||||
|
||||
def send(self, room_id, body=None, txn_id=None, tok=None, expect_code=200):
|
||||
if txn_id is None:
|
||||
txn_id = "m%s" % (str(time.time()))
|
||||
@@ -251,7 +241,9 @@ class RestHelper(object):
|
||||
if tok:
|
||||
path = path + "?access_token=%s" % tok
|
||||
|
||||
request, channel = make_request("PUT", path, json.dumps(content).encode('utf8'))
|
||||
request, channel = make_request(
|
||||
self.hs.get_reactor(), "PUT", path, json.dumps(content).encode('utf8')
|
||||
)
|
||||
render(request, self.resource, self.hs.get_reactor())
|
||||
|
||||
assert int(channel.result["code"]) == expect_code, (
|
||||
|
||||
@@ -13,84 +13,47 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import synapse.types
|
||||
from synapse.api.errors import Codes
|
||||
from synapse.http.server import JsonResource
|
||||
from synapse.rest.client.v2_alpha import filter
|
||||
from synapse.types import UserID
|
||||
from synapse.util import Clock
|
||||
|
||||
from tests import unittest
|
||||
from tests.server import (
|
||||
ThreadedMemoryReactorClock as MemoryReactorClock,
|
||||
make_request,
|
||||
render,
|
||||
setup_test_homeserver,
|
||||
)
|
||||
|
||||
PATH_PREFIX = "/_matrix/client/v2_alpha"
|
||||
|
||||
|
||||
class FilterTestCase(unittest.TestCase):
|
||||
class FilterTestCase(unittest.HomeserverTestCase):
|
||||
|
||||
USER_ID = "@apple:test"
|
||||
user_id = "@apple:test"
|
||||
hijack_auth = True
|
||||
EXAMPLE_FILTER = {"room": {"timeline": {"types": ["m.room.message"]}}}
|
||||
EXAMPLE_FILTER_JSON = b'{"room": {"timeline": {"types": ["m.room.message"]}}}'
|
||||
TO_REGISTER = [filter]
|
||||
servlets = [filter.register_servlets]
|
||||
|
||||
def setUp(self):
|
||||
self.clock = MemoryReactorClock()
|
||||
self.hs_clock = Clock(self.clock)
|
||||
|
||||
self.hs = setup_test_homeserver(
|
||||
self.addCleanup, http_client=None, clock=self.hs_clock, reactor=self.clock
|
||||
)
|
||||
|
||||
self.auth = self.hs.get_auth()
|
||||
|
||||
def get_user_by_access_token(token=None, allow_guest=False):
|
||||
return {
|
||||
"user": UserID.from_string(self.USER_ID),
|
||||
"token_id": 1,
|
||||
"is_guest": False,
|
||||
}
|
||||
|
||||
def get_user_by_req(request, allow_guest=False, rights="access"):
|
||||
return synapse.types.create_requester(
|
||||
UserID.from_string(self.USER_ID), 1, False, None
|
||||
)
|
||||
|
||||
self.auth.get_user_by_access_token = get_user_by_access_token
|
||||
self.auth.get_user_by_req = get_user_by_req
|
||||
|
||||
self.store = self.hs.get_datastore()
|
||||
self.filtering = self.hs.get_filtering()
|
||||
self.resource = JsonResource(self.hs)
|
||||
|
||||
for r in self.TO_REGISTER:
|
||||
r.register_servlets(self.hs, self.resource)
|
||||
def prepare(self, reactor, clock, hs):
|
||||
self.filtering = hs.get_filtering()
|
||||
self.store = hs.get_datastore()
|
||||
|
||||
def test_add_filter(self):
|
||||
request, channel = make_request(
|
||||
request, channel = self.make_request(
|
||||
"POST",
|
||||
"/_matrix/client/r0/user/%s/filter" % (self.USER_ID),
|
||||
"/_matrix/client/r0/user/%s/filter" % (self.user_id),
|
||||
self.EXAMPLE_FILTER_JSON,
|
||||
)
|
||||
render(request, self.resource, self.clock)
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(channel.result["code"], b"200")
|
||||
self.assertEqual(channel.json_body, {"filter_id": "0"})
|
||||
filter = self.store.get_user_filter(user_localpart="apple", filter_id=0)
|
||||
self.clock.advance(0)
|
||||
self.pump()
|
||||
self.assertEquals(filter.result, self.EXAMPLE_FILTER)
|
||||
|
||||
def test_add_filter_for_other_user(self):
|
||||
request, channel = make_request(
|
||||
request, channel = self.make_request(
|
||||
"POST",
|
||||
"/_matrix/client/r0/user/%s/filter" % ("@watermelon:test"),
|
||||
self.EXAMPLE_FILTER_JSON,
|
||||
)
|
||||
render(request, self.resource, self.clock)
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(channel.result["code"], b"403")
|
||||
self.assertEquals(channel.json_body["errcode"], Codes.FORBIDDEN)
|
||||
@@ -98,12 +61,12 @@ class FilterTestCase(unittest.TestCase):
|
||||
def test_add_filter_non_local_user(self):
|
||||
_is_mine = self.hs.is_mine
|
||||
self.hs.is_mine = lambda target_user: False
|
||||
request, channel = make_request(
|
||||
request, channel = self.make_request(
|
||||
"POST",
|
||||
"/_matrix/client/r0/user/%s/filter" % (self.USER_ID),
|
||||
"/_matrix/client/r0/user/%s/filter" % (self.user_id),
|
||||
self.EXAMPLE_FILTER_JSON,
|
||||
)
|
||||
render(request, self.resource, self.clock)
|
||||
self.render(request)
|
||||
|
||||
self.hs.is_mine = _is_mine
|
||||
self.assertEqual(channel.result["code"], b"403")
|
||||
@@ -113,21 +76,21 @@ class FilterTestCase(unittest.TestCase):
|
||||
filter_id = self.filtering.add_user_filter(
|
||||
user_localpart="apple", user_filter=self.EXAMPLE_FILTER
|
||||
)
|
||||
self.clock.advance(1)
|
||||
self.reactor.advance(1)
|
||||
filter_id = filter_id.result
|
||||
request, channel = make_request(
|
||||
"GET", "/_matrix/client/r0/user/%s/filter/%s" % (self.USER_ID, filter_id)
|
||||
request, channel = self.make_request(
|
||||
"GET", "/_matrix/client/r0/user/%s/filter/%s" % (self.user_id, filter_id)
|
||||
)
|
||||
render(request, self.resource, self.clock)
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(channel.result["code"], b"200")
|
||||
self.assertEquals(channel.json_body, self.EXAMPLE_FILTER)
|
||||
|
||||
def test_get_filter_non_existant(self):
|
||||
request, channel = make_request(
|
||||
"GET", "/_matrix/client/r0/user/%s/filter/12382148321" % (self.USER_ID)
|
||||
request, channel = self.make_request(
|
||||
"GET", "/_matrix/client/r0/user/%s/filter/12382148321" % (self.user_id)
|
||||
)
|
||||
render(request, self.resource, self.clock)
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(channel.result["code"], b"400")
|
||||
self.assertEquals(channel.json_body["errcode"], Codes.NOT_FOUND)
|
||||
@@ -135,18 +98,18 @@ class FilterTestCase(unittest.TestCase):
|
||||
# Currently invalid params do not have an appropriate errcode
|
||||
# in errors.py
|
||||
def test_get_filter_invalid_id(self):
|
||||
request, channel = make_request(
|
||||
"GET", "/_matrix/client/r0/user/%s/filter/foobar" % (self.USER_ID)
|
||||
request, channel = self.make_request(
|
||||
"GET", "/_matrix/client/r0/user/%s/filter/foobar" % (self.user_id)
|
||||
)
|
||||
render(request, self.resource, self.clock)
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(channel.result["code"], b"400")
|
||||
|
||||
# No ID also returns an invalid_id error
|
||||
def test_get_filter_no_id(self):
|
||||
request, channel = make_request(
|
||||
"GET", "/_matrix/client/r0/user/%s/filter/" % (self.USER_ID)
|
||||
request, channel = self.make_request(
|
||||
"GET", "/_matrix/client/r0/user/%s/filter/" % (self.user_id)
|
||||
)
|
||||
render(request, self.resource, self.clock)
|
||||
self.render(request)
|
||||
|
||||
self.assertEqual(channel.result["code"], b"400")
|
||||
|
||||
@@ -3,22 +3,19 @@ import json
|
||||
from mock import Mock
|
||||
|
||||
from twisted.python import failure
|
||||
from twisted.test.proto_helpers import MemoryReactorClock
|
||||
|
||||
from synapse.api.errors import InteractiveAuthIncompleteError
|
||||
from synapse.http.server import JsonResource
|
||||
from synapse.rest.client.v2_alpha.register import register_servlets
|
||||
from synapse.util import Clock
|
||||
|
||||
from tests import unittest
|
||||
from tests.server import make_request, render, setup_test_homeserver
|
||||
|
||||
|
||||
class RegisterRestServletTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
class RegisterRestServletTestCase(unittest.HomeserverTestCase):
|
||||
|
||||
servlets = [register_servlets]
|
||||
|
||||
def make_homeserver(self, reactor, clock):
|
||||
|
||||
self.clock = MemoryReactorClock()
|
||||
self.hs_clock = Clock(self.clock)
|
||||
self.url = b"/_matrix/client/r0/register"
|
||||
|
||||
self.appservice = None
|
||||
@@ -46,9 +43,7 @@ class RegisterRestServletTestCase(unittest.TestCase):
|
||||
identity_handler=self.identity_handler,
|
||||
login_handler=self.login_handler,
|
||||
)
|
||||
self.hs = setup_test_homeserver(
|
||||
self.addCleanup, http_client=None, clock=self.hs_clock, reactor=self.clock
|
||||
)
|
||||
self.hs = self.setup_test_homeserver()
|
||||
self.hs.get_auth = Mock(return_value=self.auth)
|
||||
self.hs.get_handlers = Mock(return_value=self.handlers)
|
||||
self.hs.get_auth_handler = Mock(return_value=self.auth_handler)
|
||||
@@ -58,8 +53,7 @@ class RegisterRestServletTestCase(unittest.TestCase):
|
||||
self.hs.config.registrations_require_3pid = []
|
||||
self.hs.config.auto_join_rooms = []
|
||||
|
||||
self.resource = JsonResource(self.hs)
|
||||
register_servlets(self.hs, self.resource)
|
||||
return self.hs
|
||||
|
||||
def test_POST_appservice_registration_valid(self):
|
||||
user_id = "@kermit:muppet"
|
||||
@@ -69,10 +63,10 @@ class RegisterRestServletTestCase(unittest.TestCase):
|
||||
self.auth_handler.get_access_token_for_user_id = Mock(return_value=token)
|
||||
request_data = json.dumps({"username": "kermit"})
|
||||
|
||||
request, channel = make_request(
|
||||
request, channel = self.make_request(
|
||||
b"POST", self.url + b"?access_token=i_am_an_app_service", request_data
|
||||
)
|
||||
render(request, self.resource, self.clock)
|
||||
self.render(request)
|
||||
|
||||
self.assertEquals(channel.result["code"], b"200", channel.result)
|
||||
det_data = {
|
||||
@@ -85,25 +79,25 @@ class RegisterRestServletTestCase(unittest.TestCase):
|
||||
def test_POST_appservice_registration_invalid(self):
|
||||
self.appservice = None # no application service exists
|
||||
request_data = json.dumps({"username": "kermit"})
|
||||
request, channel = make_request(
|
||||
request, channel = self.make_request(
|
||||
b"POST", self.url + b"?access_token=i_am_an_app_service", request_data
|
||||
)
|
||||
render(request, self.resource, self.clock)
|
||||
self.render(request)
|
||||
|
||||
self.assertEquals(channel.result["code"], b"401", channel.result)
|
||||
|
||||
def test_POST_bad_password(self):
|
||||
request_data = json.dumps({"username": "kermit", "password": 666})
|
||||
request, channel = make_request(b"POST", self.url, request_data)
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request(b"POST", self.url, request_data)
|
||||
self.render(request)
|
||||
|
||||
self.assertEquals(channel.result["code"], b"400", channel.result)
|
||||
self.assertEquals(channel.json_body["error"], "Invalid password")
|
||||
|
||||
def test_POST_bad_username(self):
|
||||
request_data = json.dumps({"username": 777, "password": "monkey"})
|
||||
request, channel = make_request(b"POST", self.url, request_data)
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request(b"POST", self.url, request_data)
|
||||
self.render(request)
|
||||
|
||||
self.assertEquals(channel.result["code"], b"400", channel.result)
|
||||
self.assertEquals(channel.json_body["error"], "Invalid username")
|
||||
@@ -121,8 +115,8 @@ class RegisterRestServletTestCase(unittest.TestCase):
|
||||
self.auth_handler.get_access_token_for_user_id = Mock(return_value=token)
|
||||
self.device_handler.check_device_registered = Mock(return_value=device_id)
|
||||
|
||||
request, channel = make_request(b"POST", self.url, request_data)
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request(b"POST", self.url, request_data)
|
||||
self.render(request)
|
||||
|
||||
det_data = {
|
||||
"user_id": user_id,
|
||||
@@ -143,8 +137,8 @@ class RegisterRestServletTestCase(unittest.TestCase):
|
||||
self.auth_result = (None, {"username": "kermit", "password": "monkey"}, None)
|
||||
self.registration_handler.register = Mock(return_value=("@user:id", "t"))
|
||||
|
||||
request, channel = make_request(b"POST", self.url, request_data)
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request(b"POST", self.url, request_data)
|
||||
self.render(request)
|
||||
|
||||
self.assertEquals(channel.result["code"], b"403", channel.result)
|
||||
self.assertEquals(channel.json_body["error"], "Registration has been disabled")
|
||||
@@ -155,8 +149,8 @@ class RegisterRestServletTestCase(unittest.TestCase):
|
||||
self.hs.config.allow_guest_access = True
|
||||
self.registration_handler.register = Mock(return_value=(user_id, None))
|
||||
|
||||
request, channel = make_request(b"POST", self.url + b"?kind=guest", b"{}")
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request(b"POST", self.url + b"?kind=guest", b"{}")
|
||||
self.render(request)
|
||||
|
||||
det_data = {
|
||||
"user_id": user_id,
|
||||
@@ -169,8 +163,8 @@ class RegisterRestServletTestCase(unittest.TestCase):
|
||||
def test_POST_disabled_guest_registration(self):
|
||||
self.hs.config.allow_guest_access = False
|
||||
|
||||
request, channel = make_request(b"POST", self.url + b"?kind=guest", b"{}")
|
||||
render(request, self.resource, self.clock)
|
||||
request, channel = self.make_request(b"POST", self.url + b"?kind=guest", b"{}")
|
||||
self.render(request)
|
||||
|
||||
self.assertEquals(channel.result["code"], b"403", channel.result)
|
||||
self.assertEquals(channel.json_body["error"], "Guest access is disabled")
|
||||
|
||||
164
tests/rest/media/v1/test_url_preview.py
Normal file
164
tests/rest/media/v1/test_url_preview.py
Normal file
@@ -0,0 +1,164 @@
|
||||
# -*- 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.
|
||||
|
||||
import os
|
||||
|
||||
from mock import Mock
|
||||
|
||||
from twisted.internet.defer import Deferred
|
||||
|
||||
from synapse.config.repository import MediaStorageProviderConfig
|
||||
from synapse.util.module_loader import load_module
|
||||
|
||||
from tests import unittest
|
||||
|
||||
|
||||
class URLPreviewTests(unittest.HomeserverTestCase):
|
||||
|
||||
hijack_auth = True
|
||||
user_id = "@test:user"
|
||||
|
||||
def make_homeserver(self, reactor, clock):
|
||||
|
||||
self.storage_path = self.mktemp()
|
||||
os.mkdir(self.storage_path)
|
||||
|
||||
config = self.default_config()
|
||||
config.url_preview_enabled = True
|
||||
config.max_spider_size = 9999999
|
||||
config.url_preview_url_blacklist = []
|
||||
config.media_store_path = self.storage_path
|
||||
|
||||
provider_config = {
|
||||
"module": "synapse.rest.media.v1.storage_provider.FileStorageProviderBackend",
|
||||
"store_local": True,
|
||||
"store_synchronous": False,
|
||||
"store_remote": True,
|
||||
"config": {"directory": self.storage_path},
|
||||
}
|
||||
|
||||
loaded = list(load_module(provider_config)) + [
|
||||
MediaStorageProviderConfig(False, False, False)
|
||||
]
|
||||
|
||||
config.media_storage_providers = [loaded]
|
||||
|
||||
hs = self.setup_test_homeserver(config=config)
|
||||
|
||||
return hs
|
||||
|
||||
def prepare(self, reactor, clock, hs):
|
||||
|
||||
self.fetches = []
|
||||
|
||||
def get_file(url, output_stream, max_size):
|
||||
"""
|
||||
Returns tuple[int,dict,str,int] of file length, response headers,
|
||||
absolute URI, and response code.
|
||||
"""
|
||||
|
||||
def write_to(r):
|
||||
data, response = r
|
||||
output_stream.write(data)
|
||||
return response
|
||||
|
||||
d = Deferred()
|
||||
d.addCallback(write_to)
|
||||
self.fetches.append((d, url))
|
||||
return d
|
||||
|
||||
client = Mock()
|
||||
client.get_file = get_file
|
||||
|
||||
self.media_repo = hs.get_media_repository_resource()
|
||||
preview_url = self.media_repo.children[b'preview_url']
|
||||
preview_url.client = client
|
||||
self.preview_url = preview_url
|
||||
|
||||
def test_cache_returns_correct_type(self):
|
||||
|
||||
request, channel = self.make_request(
|
||||
"GET", "url_preview?url=matrix.org", shorthand=False
|
||||
)
|
||||
request.render(self.preview_url)
|
||||
self.pump()
|
||||
|
||||
# We've made one fetch
|
||||
self.assertEqual(len(self.fetches), 1)
|
||||
|
||||
end_content = (
|
||||
b'<html><head>'
|
||||
b'<meta property="og:title" content="~matrix~" />'
|
||||
b'<meta property="og:description" content="hi" />'
|
||||
b'</head></html>'
|
||||
)
|
||||
|
||||
self.fetches[0][0].callback(
|
||||
(
|
||||
end_content,
|
||||
(
|
||||
len(end_content),
|
||||
{
|
||||
b"Content-Length": [b"%d" % (len(end_content))],
|
||||
b"Content-Type": [b'text/html; charset="utf8"'],
|
||||
},
|
||||
"https://example.com",
|
||||
200,
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
self.pump()
|
||||
self.assertEqual(channel.code, 200)
|
||||
self.assertEqual(
|
||||
channel.json_body, {"og:title": "~matrix~", "og:description": "hi"}
|
||||
)
|
||||
|
||||
# Check the cache returns the correct response
|
||||
request, channel = self.make_request(
|
||||
"GET", "url_preview?url=matrix.org", shorthand=False
|
||||
)
|
||||
request.render(self.preview_url)
|
||||
self.pump()
|
||||
|
||||
# Only one fetch, still, since we'll lean on the cache
|
||||
self.assertEqual(len(self.fetches), 1)
|
||||
|
||||
# Check the cache response has the same content
|
||||
self.assertEqual(channel.code, 200)
|
||||
self.assertEqual(
|
||||
channel.json_body, {"og:title": "~matrix~", "og:description": "hi"}
|
||||
)
|
||||
|
||||
# Clear the in-memory cache
|
||||
self.assertIn("matrix.org", self.preview_url._cache)
|
||||
self.preview_url._cache.pop("matrix.org")
|
||||
self.assertNotIn("matrix.org", self.preview_url._cache)
|
||||
|
||||
# Check the database cache returns the correct response
|
||||
request, channel = self.make_request(
|
||||
"GET", "url_preview?url=matrix.org", shorthand=False
|
||||
)
|
||||
request.render(self.preview_url)
|
||||
self.pump()
|
||||
|
||||
# Only one fetch, still, since we'll lean on the cache
|
||||
self.assertEqual(len(self.fetches), 1)
|
||||
|
||||
# Check the cache response has the same content
|
||||
self.assertEqual(channel.code, 200)
|
||||
self.assertEqual(
|
||||
channel.json_body, {"og:title": "~matrix~", "og:description": "hi"}
|
||||
)
|
||||
@@ -34,6 +34,7 @@ class FakeChannel(object):
|
||||
wire).
|
||||
"""
|
||||
|
||||
_reactor = attr.ib()
|
||||
result = attr.ib(default=attr.Factory(dict))
|
||||
_producer = None
|
||||
|
||||
@@ -56,6 +57,8 @@ class FakeChannel(object):
|
||||
self.result["headers"] = headers
|
||||
|
||||
def write(self, content):
|
||||
assert isinstance(content, bytes), "Should be bytes! " + repr(content)
|
||||
|
||||
if "body" not in self.result:
|
||||
self.result["body"] = b""
|
||||
|
||||
@@ -63,6 +66,15 @@ class FakeChannel(object):
|
||||
|
||||
def registerProducer(self, producer, streaming):
|
||||
self._producer = producer
|
||||
self.producerStreaming = streaming
|
||||
|
||||
def _produce():
|
||||
if self._producer:
|
||||
self._producer.resumeProducing()
|
||||
self._reactor.callLater(0.1, _produce)
|
||||
|
||||
if not streaming:
|
||||
self._reactor.callLater(0.0, _produce)
|
||||
|
||||
def unregisterProducer(self):
|
||||
if self._producer is None:
|
||||
@@ -105,7 +117,13 @@ class FakeSite:
|
||||
|
||||
|
||||
def make_request(
|
||||
method, path, content=b"", access_token=None, request=SynapseRequest, shorthand=True
|
||||
reactor,
|
||||
method,
|
||||
path,
|
||||
content=b"",
|
||||
access_token=None,
|
||||
request=SynapseRequest,
|
||||
shorthand=True,
|
||||
):
|
||||
"""
|
||||
Make a web request using the given method and path, feed it the
|
||||
@@ -138,7 +156,7 @@ def make_request(
|
||||
content = content.encode('utf8')
|
||||
|
||||
site = FakeSite()
|
||||
channel = FakeChannel()
|
||||
channel = FakeChannel(reactor)
|
||||
|
||||
req = request(site, channel)
|
||||
req.process = lambda: b""
|
||||
|
||||
@@ -44,6 +44,21 @@ class EndToEndKeyStoreTestCase(tests.unittest.TestCase):
|
||||
dev = res["user"]["device"]
|
||||
self.assertDictContainsSubset({"keys": json, "device_display_name": None}, dev)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_reupload_key(self):
|
||||
now = 1470174257070
|
||||
json = {"key": "value"}
|
||||
|
||||
yield self.store.store_device("user", "device", None)
|
||||
|
||||
changed = yield self.store.set_e2e_device_keys("user", "device", now, json)
|
||||
self.assertTrue(changed)
|
||||
|
||||
# If we try to upload the same key then we should be told nothing
|
||||
# changed
|
||||
changed = yield self.store.set_e2e_device_keys("user", "device", now, json)
|
||||
self.assertFalse(changed)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_get_key_with_device_name(self):
|
||||
now = 1470174257070
|
||||
|
||||
@@ -21,30 +21,20 @@ from mock import Mock, NonCallableMock
|
||||
|
||||
from synapse.api.constants import LoginType
|
||||
from synapse.api.errors import Codes, HttpResponseException, SynapseError
|
||||
from synapse.http.server import JsonResource
|
||||
from synapse.rest.client.v2_alpha import register, sync
|
||||
from synapse.util import Clock
|
||||
|
||||
from tests import unittest
|
||||
from tests.server import (
|
||||
ThreadedMemoryReactorClock,
|
||||
make_request,
|
||||
render,
|
||||
setup_test_homeserver,
|
||||
)
|
||||
|
||||
|
||||
class TestMauLimit(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.reactor = ThreadedMemoryReactorClock()
|
||||
self.clock = Clock(self.reactor)
|
||||
class TestMauLimit(unittest.HomeserverTestCase):
|
||||
|
||||
self.hs = setup_test_homeserver(
|
||||
self.addCleanup,
|
||||
servlets = [register.register_servlets, sync.register_servlets]
|
||||
|
||||
def make_homeserver(self, reactor, clock):
|
||||
|
||||
self.hs = self.setup_test_homeserver(
|
||||
"red",
|
||||
http_client=None,
|
||||
clock=self.clock,
|
||||
reactor=self.reactor,
|
||||
federation_client=Mock(),
|
||||
ratelimiter=NonCallableMock(spec_set=["send_message"]),
|
||||
)
|
||||
@@ -63,10 +53,7 @@ class TestMauLimit(unittest.TestCase):
|
||||
self.hs.config.server_notices_mxid_display_name = None
|
||||
self.hs.config.server_notices_mxid_avatar_url = None
|
||||
self.hs.config.server_notices_room_name = "Test Server Notice Room"
|
||||
|
||||
self.resource = JsonResource(self.hs)
|
||||
register.register_servlets(self.hs, self.resource)
|
||||
sync.register_servlets(self.hs, self.resource)
|
||||
return self.hs
|
||||
|
||||
def test_simple_deny_mau(self):
|
||||
# Create and sync so that the MAU counts get updated
|
||||
@@ -193,8 +180,8 @@ class TestMauLimit(unittest.TestCase):
|
||||
}
|
||||
)
|
||||
|
||||
request, channel = make_request("POST", "/register", request_data)
|
||||
render(request, self.resource, self.reactor)
|
||||
request, channel = self.make_request("POST", "/register", request_data)
|
||||
self.render(request)
|
||||
|
||||
if channel.code != 200:
|
||||
raise HttpResponseException(
|
||||
@@ -206,10 +193,10 @@ class TestMauLimit(unittest.TestCase):
|
||||
return access_token
|
||||
|
||||
def do_sync_for_user(self, token):
|
||||
request, channel = make_request(
|
||||
request, channel = self.make_request(
|
||||
"GET", "/sync", access_token=token
|
||||
)
|
||||
render(request, self.resource, self.reactor)
|
||||
self.render(request)
|
||||
|
||||
if channel.code != 200:
|
||||
raise HttpResponseException(
|
||||
|
||||
@@ -57,7 +57,9 @@ class JsonResourceTests(unittest.TestCase):
|
||||
"GET", [re.compile("^/_matrix/foo/(?P<room_id>[^/]*)$")], _callback
|
||||
)
|
||||
|
||||
request, channel = make_request(b"GET", b"/_matrix/foo/%E2%98%83?a=%E2%98%83")
|
||||
request, channel = make_request(
|
||||
self.reactor, b"GET", b"/_matrix/foo/%E2%98%83?a=%E2%98%83"
|
||||
)
|
||||
render(request, res, self.reactor)
|
||||
|
||||
self.assertEqual(request.args, {b'a': [u"\N{SNOWMAN}".encode('utf8')]})
|
||||
@@ -75,7 +77,7 @@ class JsonResourceTests(unittest.TestCase):
|
||||
res = JsonResource(self.homeserver)
|
||||
res.register_paths("GET", [re.compile("^/_matrix/foo$")], _callback)
|
||||
|
||||
request, channel = make_request(b"GET", b"/_matrix/foo")
|
||||
request, channel = make_request(self.reactor, b"GET", b"/_matrix/foo")
|
||||
render(request, res, self.reactor)
|
||||
|
||||
self.assertEqual(channel.result["code"], b'500')
|
||||
@@ -98,7 +100,7 @@ class JsonResourceTests(unittest.TestCase):
|
||||
res = JsonResource(self.homeserver)
|
||||
res.register_paths("GET", [re.compile("^/_matrix/foo$")], _callback)
|
||||
|
||||
request, channel = make_request(b"GET", b"/_matrix/foo")
|
||||
request, channel = make_request(self.reactor, b"GET", b"/_matrix/foo")
|
||||
render(request, res, self.reactor)
|
||||
|
||||
self.assertEqual(channel.result["code"], b'500')
|
||||
@@ -115,7 +117,7 @@ class JsonResourceTests(unittest.TestCase):
|
||||
res = JsonResource(self.homeserver)
|
||||
res.register_paths("GET", [re.compile("^/_matrix/foo$")], _callback)
|
||||
|
||||
request, channel = make_request(b"GET", b"/_matrix/foo")
|
||||
request, channel = make_request(self.reactor, b"GET", b"/_matrix/foo")
|
||||
render(request, res, self.reactor)
|
||||
|
||||
self.assertEqual(channel.result["code"], b'403')
|
||||
@@ -136,7 +138,7 @@ class JsonResourceTests(unittest.TestCase):
|
||||
res = JsonResource(self.homeserver)
|
||||
res.register_paths("GET", [re.compile("^/_matrix/foo$")], _callback)
|
||||
|
||||
request, channel = make_request(b"GET", b"/_matrix/foobar")
|
||||
request, channel = make_request(self.reactor, b"GET", b"/_matrix/foobar")
|
||||
render(request, res, self.reactor)
|
||||
|
||||
self.assertEqual(channel.result["code"], b'400')
|
||||
|
||||
@@ -23,7 +23,6 @@ from synapse.rest.client.v2_alpha.register import register_servlets
|
||||
from synapse.util import Clock
|
||||
|
||||
from tests import unittest
|
||||
from tests.server import make_request
|
||||
|
||||
|
||||
class TermsTestCase(unittest.HomeserverTestCase):
|
||||
@@ -42,7 +41,8 @@ class TermsTestCase(unittest.HomeserverTestCase):
|
||||
hs.config.enable_registration_captcha = False
|
||||
|
||||
def test_ui_auth(self):
|
||||
self.hs.config.block_events_without_consent_error = True
|
||||
self.hs.config.user_consent_at_registration = True
|
||||
self.hs.config.user_consent_policy_name = "My Cool Privacy Policy"
|
||||
self.hs.config.public_baseurl = "https://example.org"
|
||||
self.hs.config.user_consent_version = "1.0"
|
||||
|
||||
@@ -66,7 +66,7 @@ class TermsTestCase(unittest.HomeserverTestCase):
|
||||
"policies": {
|
||||
"privacy_policy": {
|
||||
"en": {
|
||||
"name": "Privacy Policy",
|
||||
"name": "My Cool Privacy Policy",
|
||||
"url": "https://example.org/_matrix/consent?v=1.0",
|
||||
},
|
||||
"version": "1.0"
|
||||
@@ -91,7 +91,7 @@ class TermsTestCase(unittest.HomeserverTestCase):
|
||||
|
||||
self.registration_handler.check_username = Mock(return_value=True)
|
||||
|
||||
request, channel = make_request(b"POST", self.url, request_data)
|
||||
request, channel = self.make_request(b"POST", self.url, request_data)
|
||||
self.render(request)
|
||||
|
||||
# We don't bother checking that the response is correct - we'll leave that to
|
||||
@@ -109,7 +109,7 @@ class TermsTestCase(unittest.HomeserverTestCase):
|
||||
},
|
||||
}
|
||||
)
|
||||
request, channel = make_request(b"POST", self.url, request_data)
|
||||
request, channel = self.make_request(b"POST", self.url, request_data)
|
||||
self.render(request)
|
||||
|
||||
# We're interested in getting a response that looks like a successful
|
||||
|
||||
@@ -189,11 +189,11 @@ class HomeserverTestCase(TestCase):
|
||||
for servlet in self.servlets:
|
||||
servlet(self.hs, self.resource)
|
||||
|
||||
from tests.rest.client.v1.utils import RestHelper
|
||||
|
||||
self.helper = RestHelper(self.hs, self.resource, getattr(self, "user_id", None))
|
||||
|
||||
if hasattr(self, "user_id"):
|
||||
from tests.rest.client.v1.utils import RestHelper
|
||||
|
||||
self.helper = RestHelper(self.hs, self.resource, self.user_id)
|
||||
|
||||
if self.hijack_auth:
|
||||
|
||||
def get_user_by_access_token(token=None, allow_guest=False):
|
||||
@@ -285,7 +285,9 @@ class HomeserverTestCase(TestCase):
|
||||
if isinstance(content, dict):
|
||||
content = json.dumps(content).encode('utf8')
|
||||
|
||||
return make_request(method, path, content, access_token, request, shorthand)
|
||||
return make_request(
|
||||
self.reactor, method, path, content, access_token, request, shorthand
|
||||
)
|
||||
|
||||
def render(self, request):
|
||||
"""
|
||||
|
||||
@@ -123,6 +123,8 @@ def default_config(name):
|
||||
config.user_directory_search_all_users = False
|
||||
config.user_consent_server_notice_content = None
|
||||
config.block_events_without_consent_error = None
|
||||
config.user_consent_at_registration = False
|
||||
config.user_consent_policy_name = "Privacy Policy"
|
||||
config.media_storage_providers = []
|
||||
config.autocreate_auto_join_rooms = True
|
||||
config.auto_join_rooms = []
|
||||
|
||||
2
tox.ini
2
tox.ini
@@ -122,7 +122,7 @@ skip_install = True
|
||||
basepython = python3.6
|
||||
deps =
|
||||
flake8
|
||||
commands = /bin/sh -c "flake8 synapse tests scripts scripts-dev scripts/register_new_matrix_user scripts/synapse_port_db synctl {env:PEP8SUFFIX:}"
|
||||
commands = /bin/sh -c "flake8 synapse tests scripts scripts-dev scripts/hash_password scripts/register_new_matrix_user scripts/synapse_port_db synctl {env:PEP8SUFFIX:}"
|
||||
|
||||
[testenv:check_isort]
|
||||
skip_install = True
|
||||
|
||||
Reference in New Issue
Block a user