1
0

Compare commits

...

101 Commits

Author SHA1 Message Date
Erik Johnston
228465b0ec Merge branch 'develop' of github.com:matrix-org/synapse into erikj/check_alias 2015-09-14 18:04:36 +01:00
Erik Johnston
91cb3b630d Merge pull request #265 from matrix-org/erikj/check_room_exists
Check room exists when authenticating an event
2015-09-14 17:56:18 +01:00
Erik Johnston
dffc9c4ae0 Drop unused index 2015-09-14 14:41:37 +01:00
David Baker
184a5c81f0 Merge pull request #274 from matrix-org/add_threepid_fix
Fix adding threepids to an existing account
2015-09-10 10:36:58 +01:00
David Baker
30768dcf40 Fix adding threepids to an existing account 2015-09-10 10:33:48 +01:00
Erik Johnston
4ae73d16a9 Merge pull request #270 from matrix-org/markjh/fix_metrics
Fix the size reported by maxrss.
2015-09-10 10:32:10 +01:00
Erik Johnston
a5b41b809f Merge pull request #273 from matrix-org/erikj/key_fetch_fix
Various bug fixes to crypto.keyring
2015-09-10 10:31:23 +01:00
Erik Johnston
dd0867f5ba Various bug fixes to crypto.keyring 2015-09-09 17:02:39 +01:00
Erik Johnston
c0d1f37baf Don't require pdus in check_auth script 2015-09-09 13:47:14 +01:00
Mark Haines
709ba99afd Check that /proc/self/fd exists before listing it 2015-09-07 16:45:55 +01:00
Mark Haines
9e4dacd5e7 The maxrss reported by getrusage is in kilobytes, not pages 2015-09-07 16:45:48 +01:00
Mark Haines
d23bc77e2c Merge branch 'master' into develop 2015-09-07 15:11:36 +01:00
Mark Haines
73e4ad4b8b Merge branch 'master' into develop
Conflicts:
	setup.py
2015-09-07 15:06:46 +01:00
Mark Haines
076e19da28 Merge pull request #269 from matrix-org/markjh/upgrading_setuptools
Add instructions for upgrading setuptools for when people encounter a…
2015-09-07 15:00:02 +01:00
Mark Haines
3ead04ceef Add instructions for upgrading setuptools for when people encounter a message "mock requires setuptools>=17.1" 2015-09-07 14:57:00 +01:00
Erik Johnston
227b77409f DEPENDENCY_LINKS was turned to a list 2015-09-04 08:56:23 +01:00
Erik Johnston
efeeff29f6 Merge branch 'release-v0.10.0' 2015-09-03 09:54:08 +01:00
Erik Johnston
1002bbd732 Change log level to info 2015-09-03 09:51:01 +01:00
Erik Johnston
9ad38c9807 Bump version and changelog 2015-09-03 09:49:54 +01:00
Matthew Hodgson
bdf2e5865a update logger to match new ambiguous script name... 2015-09-03 09:51:42 +03:00
Erik Johnston
fd0a919af3 Lists use 'append' 2015-09-02 17:27:59 +01:00
Daniel Wagner-Hall
77580addc3 Merge pull request #262 from matrix-org/redactyoself
Allow users to redact their own events
2015-09-02 10:02:36 +01:00
Erik Johnston
8e8955bcea Merge pull request #266 from pztrn/develop
Ignore development virtualenv and generated logger configuration as well.
2015-09-02 09:57:55 +01:00
Erik Johnston
530896d9d2 Only allow people to set the alias list for their own domain. 2015-09-01 15:51:43 +01:00
Erik Johnston
24a5a8a118 Merge branch 'erikj/unfederatable' into erikj/check_alias 2015-09-01 15:50:41 +01:00
pztrn
7ab401d4dc Ignore development virtualenv and generated logger configuration as well.
Signed-off-by: Stanislav Nikitin <pztrn@pztrn.name>
2015-09-01 19:48:22 +05:00
Erik Johnston
a88e16152f Add flag which disables federation of the room 2015-09-01 15:47:30 +01:00
Erik Johnston
00149c063b Fix tests 2015-09-01 15:42:03 +01:00
Erik Johnston
ab9e01809d Check room exists when authenticating an event, by asserting they reference a creation event 2015-09-01 15:21:24 +01:00
Mark Haines
236245f7d8 Merge pull request #264 from matrix-org/markjh/syweb_on_pypi
Use the version of "matrix-angular-sdk" hosted on pypi
2015-09-01 14:50:29 +01:00
Mark Haines
57df6fffa7 Use the version of "matrix-angular-sdk" hosted on pypi 2015-09-01 14:47:57 +01:00
Erik Johnston
b62c1395d6 Merge branch 'release-v0.10.0' of github.com:matrix-org/synapse into develop 2015-09-01 13:11:55 +01:00
Daniel Wagner-Hall
b854a375b0 Check domain of events properly
Federated servers still need to delegate authority to owning servers
2015-09-01 11:53:31 +01:00
Matthew Hodgson
d71af2ee12 don't log the whole DB config (including postgres password...) 2015-08-29 22:23:21 +01:00
Daniel Wagner-Hall
b143641b20 Merge pull request #258 from matrix-org/slowtestsmakemesad
Swap out bcrypt for md5 in tests
2015-08-28 15:42:25 +01:00
Daniel Wagner-Hall
4d1ea40008 Merge branch 'develop' into redactyoself
Conflicts:
	synapse/handlers/_base.py
2015-08-28 15:35:39 +01:00
Daniel Wagner-Hall
8256a8ece7 Allow users to redact their own events 2015-08-28 15:31:49 +01:00
Mark Haines
a7122692d9 Merge branch 'release-v0.10.0' into develop
Conflicts:
	synapse/handlers/auth.py
	synapse/python_dependencies.py
	synapse/rest/client/v1/login.py
2015-08-28 11:15:27 +01:00
Daniel Wagner-Hall
86fac9c95e Remove unused import 2015-08-26 16:03:17 +01:00
Daniel Wagner-Hall
3063383547 Swap out bcrypt for md5 in tests
This reduces our ~8 second sequential test time down to ~7 seconds
2015-08-26 15:59:32 +01:00
Mark Haines
4c56928263 Merge pull request #254 from matrix-org/markjh/tox_setuptools
Make 'setup.py test' run tox
2015-08-26 13:50:59 +01:00
Daniel Wagner-Hall
6f0c344ca7 Merge pull request #255 from matrix-org/mergeeriksmadness
Merge erikj/user_dedup to develop
2015-08-26 13:49:38 +01:00
Daniel Wagner-Hall
d3c0e48859 Merge erikj/user_dedup to develop 2015-08-26 13:42:45 +01:00
Mark Haines
06094591c5 Pass an empty list of arguments to tox if no arguments are given 2015-08-26 13:13:01 +01:00
Mark Haines
fd246fde89 Install tox locally if it wasn't already installed when running setup.py test 2015-08-26 12:59:02 +01:00
Mark Haines
4f6fa981ec Make 'setup.py test' run tox 2015-08-26 12:45:29 +01:00
Daniel Wagner-Hall
3cab86a122 Merge pull request #253 from matrix-org/tox
Allow tests to be filter when using tox
2015-08-26 11:48:41 +01:00
Daniel Wagner-Hall
e768d7b3a6 Allow tests to be filter when using tox
`tox` will run all tests
`tox tests.api.test_auth.AuthTestCase` will run just the tests in AuthTestCase
2015-08-26 11:41:42 +01:00
Daniel Wagner-Hall
a2355fae7e Merge pull request #251 from matrix-org/removeadmin
Stop looking up "admin", which we never read
2015-08-25 17:23:05 +01:00
Daniel Wagner-Hall
ee3fa1a99c Merge pull request #248 from matrix-org/deviceid
Remove completely unused concepts from codebase
2015-08-25 17:19:06 +01:00
Daniel Wagner-Hall
460cad7c11 Merge branch 'deviceid' into removeadmin 2015-08-25 16:37:59 +01:00
Daniel Wagner-Hall
825f0875bc Fix up one more reference 2015-08-25 16:37:37 +01:00
Daniel Wagner-Hall
a9d8bd95e7 Stop looking up "admin", which we never read 2015-08-25 16:29:39 +01:00
Daniel Wagner-Hall
57619d6058 Re-wrap line 2015-08-25 16:25:46 +01:00
Daniel Wagner-Hall
a0b181bd17 Remove completely unused concepts from codebase
Removes device_id and ClientInfo

device_id is never actually written, and the matrix.org DB has no
non-null entries for it. Right now, it's just cluttering up code.

This doesn't remove the columns from the database, because that's
fiddly.
2015-08-25 16:23:06 +01:00
Mark Haines
1925a38f95 Merge pull request #247 from matrix-org/markjh/tox
Add a tox.ini config for synapse.
2015-08-25 16:03:55 +01:00
Mark Haines
3a20cdcd27 Add .tox to .gitignore 2015-08-25 15:45:03 +01:00
Mark Haines
d046adf4ec Set PYTHONDONTWRITEBYTECODE in the tox environment so that we don't spew .pyc files everywhere 2015-08-25 15:44:05 +01:00
Mark Haines
c63df2d4e0 Prod jenkins 2015-08-25 15:22:39 +01:00
Mark Haines
43f2e42bfd Prod jenkins 2015-08-25 15:12:38 +01:00
Mark Haines
4bd05573e9 Prod jenkins 2015-08-25 15:03:32 +01:00
Mark Haines
12b1a47ba4 Only include demo/demo.tls.dh. Don't include any other dh file 2015-08-25 14:33:37 +01:00
Mark Haines
2e31dd2ad3 Add tox.ini file for synapse 2015-08-25 14:14:02 +01:00
Mark Haines
1c847af28a Merge pull request #243 from matrix-org/markjh/remove_syutil
Replace syutil dependency with smaller, single-purpose libraries
2015-08-25 10:52:16 +01:00
Mark Haines
cf8c04948f Fix typo in module imports and package dependencies 2015-08-25 10:42:59 +01:00
Mark Haines
aa361f51dc Merge pull request #244 from matrix-org/markjh/refresh_tokens
Remove autoincrement since we incrementing the ID in the storage layer
2015-08-25 09:40:35 +01:00
Mark Haines
037481a033 Remove autoincrement since we incrementing the ID in the storage layer 2015-08-24 17:48:57 +01:00
Mark Haines
01fc3943f1 Fix indent 2015-08-24 17:18:58 +01:00
Mark Haines
f093873d69 Replace syutil references in scripts 2015-08-24 16:30:35 +01:00
Mark Haines
78323ccdb3 Remove syutil dependency in favour of smaller single-purpose libraries 2015-08-24 16:17:38 +01:00
Erik Johnston
745b72660a Merge branch 'release-v0.10.0' of github.com:matrix-org/synapse into develop 2015-08-21 11:39:38 +01:00
David Baker
21b71b6d7c Return fully qualified user_id as per spec 2015-08-20 21:54:53 +01:00
Daniel Wagner-Hall
b1e35eabf2 Merge pull request #240 from matrix-org/refresh
/tokenrefresh POST endpoint
2015-08-20 17:44:46 +01:00
Daniel Wagner-Hall
c7788685b0 Fix bad merge 2015-08-20 17:43:12 +01:00
Daniel Wagner-Hall
8c74bd8960 Fix indentation 2015-08-20 17:26:52 +01:00
Daniel Wagner-Hall
f483340b3e Merge pull request #229 from matrix-org/auth
Issue macaroons as opaque auth tokens
2015-08-20 17:25:42 +01:00
Daniel Wagner-Hall
ea570ffaeb Fix flake8 warnings 2015-08-20 17:22:41 +01:00
Mark Haines
7049e1564f Merge remote-tracking branch 'origin/master' into develop 2015-08-20 17:21:51 +01:00
Daniel Wagner-Hall
d5a825edee Merge branch 'auth' into refresh
Conflicts:
	synapse/handlers/register.py
2015-08-20 17:13:33 +01:00
Daniel Wagner-Hall
225c244aba Remove incorrect whitespace 2015-08-20 17:10:10 +01:00
Daniel Wagner-Hall
4e706ec82c Merge branch 'develop' into auth 2015-08-20 16:59:41 +01:00
Daniel Wagner-Hall
31621c2e06 Merge pull request #239 from matrix-org/pynacl
Correct pynacl version to 0.3.0
2015-08-20 16:51:21 +01:00
Daniel Wagner-Hall
f90ea3dc73 Correct pynacl version to 0.3.0
0.0.3 was a typo
2015-08-20 16:42:17 +01:00
Daniel Wagner-Hall
ce2a7ed6e4 Merge branch 'develop' into auth 2015-08-20 16:28:36 +01:00
Daniel Wagner-Hall
e8cf77fa49 Merge branch 'develop' into refresh
Conflicts:
	synapse/rest/client/v1/login.py
2015-08-20 16:25:40 +01:00
Daniel Wagner-Hall
cecbd636e9 /tokenrefresh POST endpoint
This allows refresh tokens to be exchanged for (access_token,
refresh_token).

It also starts issuing them on login, though no clients currently
interpret them.
2015-08-20 16:21:35 +01:00
Daniel Wagner-Hall
13a6517d89 s/by_token/by_access_token/g
We're about to have two kinds of token, access and refresh
2015-08-20 16:01:29 +01:00
Daniel Wagner-Hall
617501dd2a Move token generation to auth handler
I prefer the auth handler to worry about all auth, and register to call
into it as needed, than to smatter auth logic between the two.
2015-08-20 11:35:56 +01:00
Daniel Wagner-Hall
ade5342752 Merge branch 'auth' into refresh 2015-08-20 11:03:47 +01:00
Daniel Wagner-Hall
f9e7493ac2 Merge branch 'develop' into auth 2015-08-19 15:20:09 +01:00
Daniel Wagner-Hall
ecc59ae66e Merge branch 'master' into auth 2015-08-19 15:19:37 +01:00
Daniel Wagner-Hall
70e265e695 Re-add whitespace around caveat operators 2015-08-19 14:30:31 +01:00
Daniel Wagner-Hall
7f08ebb772 Switch to pymacaroons-pynacl 2015-08-19 13:21:36 +01:00
Daniel Wagner-Hall
ce832c38d4 Remove padding space around caveat operators 2015-08-18 17:39:26 +01:00
Daniel Wagner-Hall
42e858daeb Fix units in test
I made the non-test seconds instead of ms, but not the test
2015-08-18 17:38:37 +01:00
Daniel Wagner-Hall
3e6fdfda00 Fix some formatting to use tuples 2015-08-18 15:18:50 +01:00
Daniel Wagner-Hall
1469141023 Merge branch 'develop' into auth 2015-08-18 14:43:44 +01:00
Daniel Wagner-Hall
cacdb529ab Remove accidentally added file 2015-08-18 14:27:23 +01:00
Daniel Wagner-Hall
2d3462714e Issue macaroons as opaque auth tokens
This just replaces random bytes with macaroons. The macaroons are not
inspected by the client or server.

In particular, they claim to have an expiry time, but nothing verifies
that they have not expired.

Follow-up commits will actually enforce the expiration, and allow for
token refresh.

See https://bit.ly/matrix-auth for more information
2015-08-18 14:22:02 +01:00
Matthew Hodgson
0ac61b1c78 hacky support for video for FS CC DD 2015-08-06 18:18:36 +01:00
Matthew Hodgson
0caf30f94b hacky support for video for FS CC DD 2015-08-06 18:18:16 +01:00
78 changed files with 823 additions and 342 deletions

4
.gitignore vendored
View File

@@ -42,3 +42,7 @@ build/
localhost-800*/
static/client/register/register_config.js
.tox
env/
*.config

View File

@@ -1,3 +1,8 @@
Changes in synapse v0.10.0 (2015-09-03)
=======================================
No change from release candidate.
Changes in synapse v0.10.0-rc6 (2015-09-02)
===========================================

View File

@@ -3,13 +3,20 @@ include LICENSE
include VERSION
include *.rst
include demo/README
include demo/demo.tls.dh
include demo/*.py
include demo/*.sh
recursive-include synapse/storage/schema *.sql
recursive-include synapse/storage/schema *.py
recursive-include demo *.dh
recursive-include demo *.py
recursive-include demo *.sh
recursive-include docs *
recursive-include scripts *
recursive-include scripts-dev *
recursive-include tests *.py
recursive-include static *.css
recursive-include static *.html
recursive-include static *.js
prune demo/etc

View File

@@ -121,6 +121,7 @@ To install the synapse homeserver run::
virtualenv -p python2.7 ~/.synapse
source ~/.synapse/bin/activate
pip install --upgrade setuptools
pip install --process-dependency-links https://github.com/matrix-org/synapse/tarball/master
This installs synapse, along with the libraries it uses, into a virtual
@@ -285,6 +286,11 @@ may need to manually upgrade it::
sudo pip install --upgrade pip
Installing may fail with ``mock requires setuptools>=17.1. Aborting installation``.
You can fix this by upgrading setuptools::
pip install --upgrade setuptools
If pip crashes mid-installation for reason (e.g. lost terminal), pip may
refuse to run until you remove the temporary installation directory it
created. To reset the installation::

View File

@@ -126,12 +126,26 @@ sub on_unknown_event
if (!$bridgestate->{$room_id}->{gathered_candidates}) {
$bridgestate->{$room_id}->{gathered_candidates} = 1;
my $offer = $bridgestate->{$room_id}->{offer};
my $candidate_block = "";
my $candidate_block = {
audio => '',
video => '',
};
foreach (@{$event->{content}->{candidates}}) {
$candidate_block .= "a=" . $_->{candidate} . "\r\n";
if ($_->{sdpMid}) {
$candidate_block->{$_->{sdpMid}} .= "a=" . $_->{candidate} . "\r\n";
}
else {
$candidate_block->{audio} .= "a=" . $_->{candidate} . "\r\n";
$candidate_block->{video} .= "a=" . $_->{candidate} . "\r\n";
}
}
# XXX: collate using the right m= line - for now assume audio call
$offer =~ s/(a=rtcp.*[\r\n]+)/$1$candidate_block/;
# XXX: assumes audio comes first
#$offer =~ s/(a=rtcp-mux[\r\n]+)/$1$candidate_block->{audio}/;
#$offer =~ s/(a=rtcp-mux[\r\n]+)/$1$candidate_block->{video}/;
$offer =~ s/(m=video)/$candidate_block->{audio}$1/;
$offer =~ s/(.$)/$1\n$candidate_block->{video}$1/;
my $f = send_verto_json_request("verto.invite", {
"sdp" => $offer,
@@ -172,22 +186,18 @@ sub on_room_message
warn "[Matrix] in $room_id: $from: " . $content->{body} . "\n";
}
my $verto_connecting = $loop->new_future;
$bot_verto->connect(
%{ $CONFIG{"verto-bot"} },
on_connect_error => sub { die "Cannot connect to verto - $_[-1]" },
on_resolve_error => sub { die "Cannot resolve to verto - $_[-1]" },
)->then( sub {
warn("[Verto] connected to websocket");
$verto_connecting->done($bot_verto) if not $verto_connecting->is_done;
});
Future->needs_all(
$bot_matrix->login( %{ $CONFIG{"matrix-bot"} } )->then( sub {
$bot_matrix->start;
}),
$verto_connecting,
$bot_verto->connect(
%{ $CONFIG{"verto-bot"} },
on_connect_error => sub { die "Cannot connect to verto - $_[-1]" },
on_resolve_error => sub { die "Cannot resolve to verto - $_[-1]" },
)->on_done( sub {
warn("[Verto] connected to websocket");
}),
)->get;
$loop->attach_signal(

View File

@@ -11,7 +11,4 @@ requires 'YAML', 0;
requires 'JSON', 0;
requires 'Getopt::Long', 0;
on 'test' => sub {
requires 'Test::More', '>= 0.98';
};

View File

@@ -56,10 +56,9 @@ if __name__ == '__main__':
js = json.load(args.json)
auth = Auth(Mock())
check_auth(
auth,
[FrozenEvent(d) for d in js["auth_chain"]],
[FrozenEvent(d) for d in js["pdus"]],
[FrozenEvent(d) for d in js.get("pdus", [])],
)

View File

@@ -1,5 +1,5 @@
from synapse.crypto.event_signing import *
from syutil.base64util import encode_base64
from unpaddedbase64 import encode_base64
import argparse
import hashlib

View File

@@ -1,9 +1,7 @@
from syutil.crypto.jsonsign import verify_signed_json
from syutil.crypto.signing_key import (
decode_verify_key_bytes, write_signing_keys
)
from syutil.base64util import decode_base64
from signedjson.sign import verify_signed_json
from signedjson.key import decode_verify_key_bytes, write_signing_keys
from unpaddedbase64 import decode_base64
import urllib2
import json

View File

@@ -4,10 +4,10 @@ import sys
import json
import time
import hashlib
from syutil.base64util import encode_base64
from syutil.crypto.signing_key import read_signing_keys
from syutil.crypto.jsonsign import sign_json
from syutil.jsonutil import encode_canonical_json
from unpaddedbase64 import encode_base64
from signedjson.key import read_signing_keys
from signedjson.sign import sign_json
from canonicaljson import encode_canonical_json
def select_v1_keys(connection):

View File

@@ -6,8 +6,8 @@ from synapse.crypto.event_signing import (
add_event_pdu_content_hash, compute_pdu_event_reference_hash
)
from synapse.api.events.utils import prune_pdu
from syutil.base64util import encode_base64, decode_base64
from syutil.jsonutil import encode_canonical_json
from unpaddedbase64 import encode_base64, decode_base64
from canonicaljson import encode_canonical_json
import sqlite3
import sys

View File

@@ -29,7 +29,7 @@ import traceback
import yaml
logger = logging.getLogger("port_from_sqlite_to_postgres")
logger = logging.getLogger("synapse_port_db")
BOOLEAN_COLUMNS = {

View File

@@ -3,9 +3,6 @@ source-dir = docs/sphinx
build-dir = docs/build
all_files = 1
[aliases]
test = trial
[trial]
test_suite = tests

View File

@@ -16,7 +16,8 @@
import glob
import os
from setuptools import setup, find_packages
from setuptools import setup, find_packages, Command
import sys
here = os.path.abspath(os.path.dirname(__file__))
@@ -37,6 +38,39 @@ def exec_file(path_segments):
exec(code, result)
return result
class Tox(Command):
user_options = [('tox-args=', 'a', "Arguments to pass to tox")]
def initialize_options(self):
self.tox_args = None
def finalize_options(self):
self.test_args = []
self.test_suite = True
def run(self):
#import here, cause outside the eggs aren't loaded
try:
import tox
except ImportError:
try:
self.distribution.fetch_build_eggs("tox")
import tox
except:
raise RuntimeError(
"The tests need 'tox' to run. Please install 'tox'."
)
import shlex
args = self.tox_args
if args:
args = shlex.split(self.tox_args)
else:
args = []
errno = tox.cmdline(args=args)
sys.exit(errno)
version = exec_file(("synapse", "__init__.py"))["__version__"]
dependencies = exec_file(("synapse", "python_dependencies.py"))
long_description = read_file(("README.rst",))
@@ -47,14 +81,10 @@ setup(
packages=find_packages(exclude=["tests", "tests.*"]),
description="Reference Synapse Home Server",
install_requires=dependencies['requirements'](include_conditional=True).keys(),
setup_requires=[
"Twisted>=15.1.0", # Here to override setuptools_trial's dependency on Twisted>=2.4.0
"setuptools_trial",
"mock"
],
dependency_links=dependencies["DEPENDENCY_LINKS"],
dependency_links=dependencies["DEPENDENCY_LINKS"].values(),
include_package_data=True,
zip_safe=False,
long_description=long_description,
scripts=["synctl"] + glob.glob("scripts/*"),
cmdclass={'test': Tox},
)

View File

@@ -16,4 +16,4 @@
""" This is a reference implementation of a Matrix home server.
"""
__version__ = "0.10.0-rc6"
__version__ = "0.10.0"

View File

@@ -20,7 +20,7 @@ from twisted.internet import defer
from synapse.api.constants import EventTypes, Membership, JoinRules
from synapse.api.errors import AuthError, Codes, SynapseError
from synapse.util.logutils import log_function
from synapse.types import UserID, ClientInfo
from synapse.types import EventID, RoomID, UserID
import logging
@@ -65,9 +65,30 @@ class Auth(object):
# FIXME
return True
creation_event = auth_events.get((EventTypes.Create, ""), None)
if not creation_event:
raise SynapseError(
403,
"Room %r does not exist" % (event.room_id,)
)
creating_domain = RoomID.from_string(event.room_id).domain
originating_domain = EventID.from_string(event.event_id).domain
if creating_domain != originating_domain:
if not self.can_federate(event, auth_events):
raise SynapseError(
403,
"This room has been marked as unfederatable."
)
# FIXME: Temp hack
if event.type == EventTypes.Aliases:
return True
alias_domain = UserID.from_string(event.state_key).domain
if alias_domain != originating_domain:
raise AuthError(
403,
"Can only set aliases for own domain"
)
logger.debug(
"Auth events: %s",
@@ -91,7 +112,7 @@ class Auth(object):
self._check_power_levels(event, auth_events)
if event.type == EventTypes.Redaction:
self._check_redaction(event, auth_events)
self.check_redaction(event, auth_events)
logger.debug("Allowing! %s", event)
except AuthError as e:
@@ -153,6 +174,11 @@ class Auth(object):
user_id, room_id, repr(member)
))
def can_federate(self, event, auth_events):
creation_event = auth_events.get((EventTypes.Create, ""))
return creation_event.content.get("m.federate", True) is True
@log_function
def is_membership_change_allowed(self, event, auth_events):
membership = event.content["membership"]
@@ -322,9 +348,9 @@ class Auth(object):
Args:
request - An HTTP request with an access_token query parameter.
Returns:
tuple : of UserID and device string:
User ID object of the user making the request
ClientInfo object of the client instance the user is using
tuple of:
UserID (str)
Access token ID (str)
Raises:
AuthError if no user by that token exists or the token is invalid.
"""
@@ -354,16 +380,13 @@ class Auth(object):
request.authenticated_entity = user_id
defer.returnValue(
(UserID.from_string(user_id), ClientInfo("", ""))
)
defer.returnValue((UserID.from_string(user_id), ""))
return
except KeyError:
pass # normal users won't have the user_id query parameter set.
user_info = yield self.get_user_by_token(access_token)
user_info = yield self.get_user_by_access_token(access_token)
user = user_info["user"]
device_id = user_info["device_id"]
token_id = user_info["token_id"]
ip_addr = self.hs.get_ip_from_request(request)
@@ -375,14 +398,13 @@ class Auth(object):
self.store.insert_client_ip(
user=user,
access_token=access_token,
device_id=user_info["device_id"],
ip=ip_addr,
user_agent=user_agent
)
request.authenticated_entity = user.to_string()
defer.returnValue((user, ClientInfo(device_id, token_id)))
defer.returnValue((user, token_id,))
except KeyError:
raise AuthError(
self.TOKEN_NOT_FOUND_HTTP_STATUS, "Missing access token.",
@@ -390,26 +412,23 @@ class Auth(object):
)
@defer.inlineCallbacks
def get_user_by_token(self, token):
def get_user_by_access_token(self, token):
""" Get a registered user's ID.
Args:
token (str): The access token to get the user by.
Returns:
dict : dict that includes the user, device_id, and whether the
user is a server admin.
dict : dict that includes the user and the ID of their access token.
Raises:
AuthError if no user by that token exists or the token is invalid.
"""
ret = yield self.store.get_user_by_token(token)
ret = yield self.store.get_user_by_access_token(token)
if not ret:
raise AuthError(
self.TOKEN_NOT_FOUND_HTTP_STATUS, "Unrecognised access token.",
errcode=Codes.UNKNOWN_TOKEN
)
user_info = {
"admin": bool(ret.get("admin", False)),
"device_id": ret.get("device_id"),
"user": UserID.from_string(ret.get("name")),
"token_id": ret.get("token_id", None),
}
@@ -548,16 +567,35 @@ class Auth(object):
return True
def _check_redaction(self, event, auth_events):
def check_redaction(self, event, auth_events):
"""Check whether the event sender is allowed to redact the target event.
Returns:
True if the the sender is allowed to redact the target event if the
target event was created by them.
False if the sender is allowed to redact the target event with no
further checks.
Raises:
AuthError if the event sender is definitely not allowed to redact
the target event.
"""
user_level = self._get_user_power_level(event.user_id, auth_events)
redact_level = self._get_named_level(auth_events, "redact", 50)
if user_level < redact_level:
raise AuthError(
403,
"You don't have permission to redact events"
)
if user_level > redact_level:
return False
redacter_domain = EventID.from_string(event.event_id).domain
redactee_domain = EventID.from_string(event.redacts).domain
if redacter_domain == redactee_domain:
return True
raise AuthError(
403,
"You don't have permission to redact events"
)
def _check_power_levels(self, event, auth_events):
user_list = event.content.get("users", {})

View File

@@ -341,7 +341,7 @@ def get_version_string():
)
).encode("ascii")
except Exception as e:
logger.warn("Failed to check for git repository: %s", e)
logger.info("Failed to check for git repository: %s", e)
return ("Synapse/%s" % (synapse.__version__,)).encode("ascii")
@@ -403,7 +403,7 @@ def setup(config_options):
database_engine=database_engine,
)
logger.info("Preparing database: %r...", config.database_config)
logger.info("Preparing database: %s...", config.database_config['name'])
try:
db_conn = database_engine.module.connect(
@@ -425,7 +425,7 @@ def setup(config_options):
)
sys.exit(1)
logger.info("Database prepared in %r.", config.database_config)
logger.info("Database prepared in %s.", config.database_config['name'])
hs.start_listening()

View File

@@ -182,7 +182,7 @@ class Config(object):
) % (entry_path, )
continue
files.add(config_path)
files.append(entry_path)
config_files.extend(sorted(files))
else:

View File

@@ -13,14 +13,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from ._base import Config, ConfigError
import syutil.crypto.signing_key
from syutil.crypto.signing_key import (
is_signing_algorithm_supported, decode_verify_key_bytes
)
from syutil.base64util import decode_base64
from synapse.util.stringutils import random_string
from signedjson.key import (
generate_signing_key, is_signing_algorithm_supported,
decode_signing_key_base64, decode_verify_key_bytes,
read_signing_keys, write_signing_keys, NACL_ED25519
)
from unpaddedbase64 import decode_base64
import os
class KeyConfig(Config):
@@ -83,9 +86,7 @@ class KeyConfig(Config):
def read_signing_key(self, signing_key_path):
signing_keys = self.read_file(signing_key_path, "signing_key")
try:
return syutil.crypto.signing_key.read_signing_keys(
signing_keys.splitlines(True)
)
return read_signing_keys(signing_keys.splitlines(True))
except Exception:
raise ConfigError(
"Error reading signing_key."
@@ -112,22 +113,18 @@ class KeyConfig(Config):
if not os.path.exists(signing_key_path):
with open(signing_key_path, "w") as signing_key_file:
key_id = "a_" + random_string(4)
syutil.crypto.signing_key.write_signing_keys(
signing_key_file,
(syutil.crypto.signing_key.generate_signing_key(key_id),),
write_signing_keys(
signing_key_file, (generate_signing_key(key_id),),
)
else:
signing_keys = self.read_file(signing_key_path, "signing_key")
if len(signing_keys.split("\n")[0].split()) == 1:
# handle keys in the old format.
key_id = "a_" + random_string(4)
key = syutil.crypto.signing_key.decode_signing_key_base64(
syutil.crypto.signing_key.NACL_ED25519,
key_id,
signing_keys.split("\n")[0]
key = decode_signing_key_base64(
NACL_ED25519, key_id, signing_keys.split("\n")[0]
)
with open(signing_key_path, "w") as signing_key_file:
syutil.crypto.signing_key.write_signing_keys(
signing_key_file,
(key,),
write_signing_keys(
signing_key_file, (key,),
)

View File

@@ -32,9 +32,11 @@ class RegistrationConfig(Config):
)
self.registration_shared_secret = config.get("registration_shared_secret")
self.macaroon_secret_key = config.get("macaroon_secret_key")
def default_config(self, config_dir, server_name):
registration_shared_secret = random_string_with_symbols(50)
macaroon_secret_key = random_string_with_symbols(50)
return """\
## Registration ##
@@ -44,6 +46,8 @@ class RegistrationConfig(Config):
# If set, allows registration by anyone who also has the shared
# secret, even if registration is otherwise disabled.
registration_shared_secret: "%(registration_shared_secret)s"
macaroon_secret_key: "%(macaroon_secret_key)s"
""" % locals()
def add_arguments(self, parser):

View File

@@ -15,11 +15,12 @@
# limitations under the License.
from synapse.events.utils import prune_event
from syutil.jsonutil import encode_canonical_json
from syutil.base64util import encode_base64, decode_base64
from syutil.crypto.jsonsign import sign_json
from synapse.api.errors import SynapseError, Codes
from synapse.events.utils import prune_event
from canonicaljson import encode_canonical_json
from unpaddedbase64 import encode_base64, decode_base64
from signedjson.sign import sign_json
import hashlib
import logging

View File

@@ -14,21 +14,21 @@
# limitations under the License.
from synapse.crypto.keyclient import fetch_server_key
from twisted.internet import defer
from syutil.crypto.jsonsign import (
verify_signed_json, signature_ids, sign_json, encode_canonical_json
)
from syutil.crypto.signing_key import (
is_signing_algorithm_supported, decode_verify_key_bytes
)
from syutil.base64util import decode_base64, encode_base64
from synapse.api.errors import SynapseError, Codes
from synapse.util.retryutils import get_retry_limiter
from synapse.util import unwrapFirstError
from synapse.util.async import ObservableDeferred
from twisted.internet import defer
from signedjson.sign import (
verify_signed_json, signature_ids, sign_json, encode_canonical_json
)
from signedjson.key import (
is_signing_algorithm_supported, decode_verify_key_bytes
)
from unpaddedbase64 import decode_base64, encode_base64
from OpenSSL import crypto
from collections import namedtuple
@@ -162,7 +162,9 @@ class Keyring(object):
def remove_deferreds(res, server_name, group_id):
server_to_gids[server_name].discard(group_id)
if not server_to_gids[server_name]:
server_to_deferred.pop(server_name).callback(None)
d = server_to_deferred.pop(server_name, None)
if d:
d.callback(None)
return res
for g_id, deferred in deferreds.items():
@@ -200,8 +202,15 @@ class Keyring(object):
else:
break
for server_name, deferred in server_to_deferred:
self.key_downloads[server_name] = ObservableDeferred(deferred)
for server_name, deferred in server_to_deferred.items():
d = ObservableDeferred(deferred)
self.key_downloads[server_name] = d
def rm(r, server_name):
self.key_downloads.pop(server_name, None)
return r
d.addBoth(rm, server_name)
def get_server_verify_keys(self, group_id_to_group, group_id_to_deferred):
"""Takes a dict of KeyGroups and tries to find at least one key for
@@ -220,9 +229,8 @@ class Keyring(object):
merged_results = {}
missing_keys = {
group.server_name: key_id
group.server_name: set(group.key_ids)
for group in group_id_to_group.values()
for key_id in group.key_ids
}
for fn in key_fetch_fns:
@@ -279,16 +287,15 @@ class Keyring(object):
def get_keys_from_store(self, server_name_and_key_ids):
res = yield defer.gatherResults(
[
self.store.get_server_verify_keys(server_name, key_ids)
self.store.get_server_verify_keys(
server_name, key_ids
).addCallback(lambda ks, server: (server, ks), server_name)
for server_name, key_ids in server_name_and_key_ids
],
consumeErrors=True,
).addErrback(unwrapFirstError)
defer.returnValue(dict(zip(
[server_name for server_name, _ in server_name_and_key_ids],
res
)))
defer.returnValue(dict(res))
@defer.inlineCallbacks
def get_keys_from_perspectives(self, server_name_and_key_ids):

View File

@@ -15,7 +15,7 @@
from twisted.internet import defer
from synapse.api.errors import LimitExceededError, SynapseError
from synapse.api.errors import LimitExceededError, SynapseError, AuthError
from synapse.crypto.event_signing import add_hashes_and_signatures
from synapse.api.constants import Membership, EventTypes
from synapse.types import UserID, RoomAlias
@@ -146,6 +146,21 @@ class BaseHandler(object):
returned_invite.signatures
)
if event.type == EventTypes.Redaction:
if self.auth.check_redaction(event, auth_events=context.current_state):
original_event = yield self.store.get_event(
event.redacts,
check_redacted=False,
get_prev_content=False,
allow_rejected=False,
allow_none=False
)
if event.user_id != original_event.user_id:
raise AuthError(
403,
"You don't have permission to redact events"
)
destinations = set(extra_destinations)
for k, s in context.current_state.items():
try:

View File

@@ -34,6 +34,7 @@ class AdminHandler(BaseHandler):
d = {}
for r in res:
# Note that device_id is always None
device = d.setdefault(r["device_id"], {})
session = device.setdefault(r["access_token"], [])
session.append({

View File

@@ -26,6 +26,7 @@ from twisted.web.client import PartialDownloadError
import logging
import bcrypt
import pymacaroons
import simplejson
import synapse.util.stringutils as stringutils
@@ -279,7 +280,10 @@ class AuthHandler(BaseHandler):
user_id (str): User ID
password (str): Password
Returns:
The access token for the user's session.
A tuple of:
The user's ID.
The access token for the user's session.
The refresh token for the user's session.
Raises:
StoreError if there was a problem storing the token.
LoginError if there was an authentication problem.
@@ -287,11 +291,10 @@ class AuthHandler(BaseHandler):
user_id, password_hash = yield self._find_user_id_and_pwd_hash(user_id)
self._check_password(user_id, password, password_hash)
reg_handler = self.hs.get_handlers().registration_handler
access_token = reg_handler.generate_token(user_id)
logger.info("Logging in user %s", user_id)
yield self.store.add_access_token_to_user(user_id, access_token)
defer.returnValue((user_id, access_token))
access_token = yield self.issue_access_token(user_id)
refresh_token = yield self.issue_refresh_token(user_id)
defer.returnValue((user_id, access_token, refresh_token))
@defer.inlineCallbacks
def _find_user_id_and_pwd_hash(self, user_id):
@@ -321,13 +324,52 @@ class AuthHandler(BaseHandler):
def _check_password(self, user_id, password, stored_hash):
"""Checks that user_id has passed password, raises LoginError if not."""
if not bcrypt.checkpw(password, stored_hash):
if not self.validate_hash(password, stored_hash):
logger.warn("Failed password login for user %s", user_id)
raise LoginError(403, "", errcode=Codes.FORBIDDEN)
@defer.inlineCallbacks
def issue_access_token(self, user_id):
access_token = self.generate_access_token(user_id)
yield self.store.add_access_token_to_user(user_id, access_token)
defer.returnValue(access_token)
@defer.inlineCallbacks
def issue_refresh_token(self, user_id):
refresh_token = self.generate_refresh_token(user_id)
yield self.store.add_refresh_token_to_user(user_id, refresh_token)
defer.returnValue(refresh_token)
def generate_access_token(self, user_id):
macaroon = self._generate_base_macaroon(user_id)
macaroon.add_first_party_caveat("type = access")
now = self.hs.get_clock().time_msec()
expiry = now + (60 * 60 * 1000)
macaroon.add_first_party_caveat("time < %d" % (expiry,))
return macaroon.serialize()
def generate_refresh_token(self, user_id):
m = self._generate_base_macaroon(user_id)
m.add_first_party_caveat("type = refresh")
# Important to add a nonce, because otherwise every refresh token for a
# user will be the same.
m.add_first_party_caveat("nonce = %s" % (
stringutils.random_string_with_symbols(16),
))
return m.serialize()
def _generate_base_macaroon(self, user_id):
macaroon = pymacaroons.Macaroon(
location=self.hs.config.server_name,
identifier="key",
key=self.hs.config.macaroon_secret_key)
macaroon.add_first_party_caveat("gen = 1")
macaroon.add_first_party_caveat("user_id = %s" % (user_id,))
return macaroon
@defer.inlineCallbacks
def set_password(self, user_id, newpassword):
password_hash = bcrypt.hashpw(newpassword, bcrypt.gensalt())
password_hash = self.hash(newpassword)
yield self.store.user_set_password_hash(user_id, password_hash)
yield self.store.user_delete_access_tokens(user_id)
@@ -349,3 +391,26 @@ class AuthHandler(BaseHandler):
def _remove_session(self, session):
logger.debug("Removing session %s", session)
del self.sessions[session["id"]]
def hash(self, password):
"""Computes a secure hash of password.
Args:
password (str): Password to hash.
Returns:
Hashed password (str).
"""
return bcrypt.hashpw(password, bcrypt.gensalt())
def validate_hash(self, password, stored_hash):
"""Validates that self.hash(password) == stored_hash.
Args:
password (str): Password to hash.
stored_hash (str): Expected hash value.
Returns:
Whether self.hash(password) == stored_hash (bool).
"""
return bcrypt.checkpw(password, stored_hash)

View File

@@ -183,7 +183,7 @@ class MessageHandler(BaseHandler):
@defer.inlineCallbacks
def create_and_send_event(self, event_dict, ratelimit=True,
client=None, txn_id=None):
token_id=None, txn_id=None):
""" Given a dict from a client, create and handle a new event.
Creates an FrozenEvent object, filling out auth_events, prev_events,
@@ -217,11 +217,8 @@ class MessageHandler(BaseHandler):
builder.content
)
if client is not None:
if client.token_id is not None:
builder.internal_metadata.token_id = client.token_id
if client.device_id is not None:
builder.internal_metadata.device_id = client.device_id
if token_id is not None:
builder.internal_metadata.token_id = token_id
if txn_id is not None:
builder.internal_metadata.txn_id = txn_id

View File

@@ -25,8 +25,6 @@ import synapse.util.stringutils as stringutils
from synapse.util.async import run_on_reactor
from synapse.http.client import CaptchaServerHttpClient
import base64
import bcrypt
import logging
import urllib
@@ -83,7 +81,7 @@ class RegistrationHandler(BaseHandler):
yield run_on_reactor()
password_hash = None
if password:
password_hash = bcrypt.hashpw(password, bcrypt.gensalt())
password_hash = self.auth_handler().hash(password)
if localpart:
yield self.check_username(localpart)
@@ -91,7 +89,7 @@ class RegistrationHandler(BaseHandler):
user = UserID(localpart, self.hs.hostname)
user_id = user.to_string()
token = self.generate_token(user_id)
token = self.auth_handler().generate_access_token(user_id)
yield self.store.register(
user_id=user_id,
token=token,
@@ -111,7 +109,7 @@ class RegistrationHandler(BaseHandler):
user_id = user.to_string()
yield self.check_user_id_is_valid(user_id)
token = self.generate_token(user_id)
token = self.auth_handler().generate_access_token(user_id)
yield self.store.register(
user_id=user_id,
token=token,
@@ -161,7 +159,7 @@ class RegistrationHandler(BaseHandler):
400, "Invalid user localpart for this application service.",
errcode=Codes.EXCLUSIVE
)
token = self.generate_token(user_id)
token = self.auth_handler().generate_access_token(user_id)
yield self.store.register(
user_id=user_id,
token=token,
@@ -208,7 +206,7 @@ class RegistrationHandler(BaseHandler):
user_id = user.to_string()
yield self.check_user_id_is_valid(user_id)
token = self.generate_token(user_id)
token = self.auth_handler().generate_access_token(user_id)
try:
yield self.store.register(
user_id=user_id,
@@ -273,13 +271,6 @@ class RegistrationHandler(BaseHandler):
errcode=Codes.EXCLUSIVE
)
def generate_token(self, user_id):
# urlsafe variant uses _ and - so use . as the separator and replace
# all =s with .s so http clients don't quote =s when it is used as
# query params.
return (base64.urlsafe_b64encode(user_id).replace('=', '.') + '.' +
stringutils.random_string(18))
def _generate_user_id(self):
return "-" + stringutils.random_string(18)
@@ -322,3 +313,6 @@ class RegistrationHandler(BaseHandler):
}
)
defer.returnValue(data)
def auth_handler(self):
return self.hs.get_handlers().auth_handler

View File

@@ -150,12 +150,15 @@ class RoomCreationHandler(BaseHandler):
for val in raw_initial_state:
initial_state[(val["type"], val.get("state_key", ""))] = val["content"]
creation_content = config.get("creation_content", {})
user = UserID.from_string(user_id)
creation_events = self._create_events_for_new_room(
user, room_id,
preset_config=preset_config,
invite_list=invite_list,
initial_state=initial_state,
creation_content=creation_content,
)
msg_handler = self.hs.get_handlers().message_handler
@@ -203,7 +206,7 @@ class RoomCreationHandler(BaseHandler):
defer.returnValue(result)
def _create_events_for_new_room(self, creator, room_id, preset_config,
invite_list, initial_state):
invite_list, initial_state, creation_content):
config = RoomCreationHandler.PRESETS_DICT[preset_config]
creator_id = creator.to_string()
@@ -225,9 +228,10 @@ class RoomCreationHandler(BaseHandler):
return e
creation_content.update({"creator": creator.to_string()})
creation_event = create(
etype=EventTypes.Create,
content={"creator": creator.to_string()},
content=creation_content,
)
join_event = create(

View File

@@ -28,7 +28,6 @@ logger = logging.getLogger(__name__)
SyncConfig = collections.namedtuple("SyncConfig", [
"user",
"client_info",
"limit",
"gap",
"sort",

View File

@@ -15,9 +15,10 @@
from synapse.api.errors import CodeMessageException
from synapse.util.logcontext import preserve_context_over_fn
from syutil.jsonutil import encode_canonical_json
import synapse.metrics
from canonicaljson import encode_canonical_json
from twisted.internet import defer, reactor
from twisted.web.client import (
Agent, readBody, FileBodyProducer, PartialDownloadError,

View File

@@ -25,13 +25,13 @@ from synapse.util.async import sleep
from synapse.util.logcontext import preserve_context_over_fn
import synapse.metrics
from syutil.jsonutil import encode_canonical_json
from canonicaljson import encode_canonical_json
from synapse.api.errors import (
SynapseError, Codes, HttpResponseException,
)
from syutil.crypto.jsonsign import sign_json
from signedjson.sign import sign_json
import simplejson as json
import logging

View File

@@ -21,8 +21,8 @@ from synapse.util.logcontext import LoggingContext, PreserveLoggingContext
import synapse.metrics
import synapse.events
from syutil.jsonutil import (
encode_canonical_json, encode_pretty_printed_json, encode_json
from canonicaljson import (
encode_canonical_json, encode_pretty_printed_json
)
from twisted.internet import defer
@@ -33,6 +33,7 @@ from twisted.web.util import redirectTo
import collections
import logging
import urllib
import ujson
logger = logging.getLogger(__name__)
@@ -270,12 +271,11 @@ def respond_with_json(request, code, json_object, send_cors=False,
if pretty_print:
json_bytes = encode_pretty_printed_json(json_object) + "\n"
else:
if canonical_json:
if canonical_json or synapse.events.USE_FROZEN_DICTS:
json_bytes = encode_canonical_json(json_object)
else:
json_bytes = encode_json(
json_object, using_frozen_dicts=synapse.events.USE_FROZEN_DICTS
)
# ujson doesn't like frozen_dicts.
json_bytes = ujson.dumps(json_object, ensure_ascii=False)
return respond_with_json_bytes(
request, code, json_bytes,

View File

@@ -17,7 +17,7 @@
from __future__ import absolute_import
import logging
from resource import getrusage, getpagesize, RUSAGE_SELF
from resource import getrusage, RUSAGE_SELF
import functools
import os
import stat
@@ -100,7 +100,6 @@ def render_all():
# process resource usage
rusage = None
PAGE_SIZE = getpagesize()
def update_resource_metrics():
@@ -113,8 +112,8 @@ resource_metrics = get_metrics_for("process.resource")
resource_metrics.register_callback("utime", lambda: rusage.ru_utime * 1000)
resource_metrics.register_callback("stime", lambda: rusage.ru_stime * 1000)
# pages
resource_metrics.register_callback("maxrss", lambda: rusage.ru_maxrss * PAGE_SIZE)
# kilobytes
resource_metrics.register_callback("maxrss", lambda: rusage.ru_maxrss * 1024)
TYPES = {
stat.S_IFSOCK: "SOCK",
@@ -131,6 +130,10 @@ def _process_fds():
counts = {(k,): 0 for k in TYPES.values()}
counts[("other",)] = 0
# Not every OS will have a /proc/self/fd directory
if not os.path.exists("/proc/self/fd"):
return counts
for fd in os.listdir("/proc/self/fd"):
try:
s = os.stat("/proc/self/fd/%s" % (fd))

View File

@@ -18,13 +18,15 @@ from distutils.version import LooseVersion
logger = logging.getLogger(__name__)
REQUIREMENTS = {
"syutil>=0.0.7": ["syutil>=0.0.7"],
"unpaddedbase64>=1.0.1": ["unpaddedbase64>=1.0.1"],
"canonicaljson>=1.0.0": ["canonicaljson>=1.0.0"],
"signedjson>=1.0.0": ["signedjson>=1.0.0"],
"Twisted>=15.1.0": ["twisted>=15.1.0"],
"service_identity>=1.0.0": ["service_identity>=1.0.0"],
"pyopenssl>=0.14": ["OpenSSL>=0.14"],
"pyyaml": ["yaml"],
"pyasn1": ["pyasn1"],
"pynacl>=0.0.3": ["nacl>=0.0.3"],
"pynacl>=0.3.0": ["nacl>=0.3.0"],
"daemonize": ["daemonize"],
"py-bcrypt": ["bcrypt"],
"frozendict>=0.4": ["frozendict"],
@@ -33,6 +35,7 @@ REQUIREMENTS = {
"ujson": ["ujson"],
"blist": ["blist"],
"pysaml2": ["saml2"],
"pymacaroons-pynacl": ["pymacaroons"],
}
CONDITIONAL_REQUIREMENTS = {
"web_client": {
@@ -53,16 +56,6 @@ def github_link(project, version, egg):
return "https://github.com/%s/tarball/%s/#egg=%s" % (project, version, egg)
DEPENDENCY_LINKS = {
"syutil": github_link(
project="matrix-org/syutil",
version="v0.0.7",
egg="syutil-0.0.7",
),
"matrix-angular-sdk": github_link(
project="matrix-org/matrix-angular-sdk",
version="v0.6.6",
egg="matrix_angular_sdk-0.6.6",
),
}

View File

@@ -31,7 +31,7 @@ class WhoisRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_GET(self, request, user_id):
target_user = UserID.from_string(user_id)
auth_user, client = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
is_admin = yield self.auth.is_server_admin(auth_user)
if not is_admin and target_user != auth_user:

View File

@@ -69,7 +69,7 @@ class ClientDirectoryServer(ClientV1RestServlet):
try:
# try to auth as a user
user, client = yield self.auth.get_user_by_req(request)
user, _ = yield self.auth.get_user_by_req(request)
try:
user_id = user.to_string()
yield dir_handler.create_association(
@@ -116,7 +116,7 @@ class ClientDirectoryServer(ClientV1RestServlet):
# fallback to default user behaviour if they aren't an AS
pass
user, client = yield self.auth.get_user_by_req(request)
user, _ = yield self.auth.get_user_by_req(request)
is_admin = yield self.auth.is_server_admin(user)
if not is_admin:

View File

@@ -34,7 +34,7 @@ class EventStreamRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_GET(self, request):
auth_user, client = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
try:
handler = self.handlers.event_stream_handler
pagin_config = PaginationConfig.from_request(request)
@@ -71,7 +71,7 @@ class EventRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_GET(self, request, event_id):
auth_user, client = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
handler = self.handlers.event_handler
event = yield handler.get_event(auth_user, event_id)

View File

@@ -25,7 +25,7 @@ class InitialSyncRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_GET(self, request):
user, client = yield self.auth.get_user_by_req(request)
user, _ = yield self.auth.get_user_by_req(request)
with_feedback = "feedback" in request.args
as_client_event = "raw" not in request.args
pagination_config = PaginationConfig.from_request(request)

View File

@@ -86,13 +86,15 @@ class LoginRestServlet(ClientV1RestServlet):
user_id, self.hs.hostname
).to_string()
user_id, token = yield self.handlers.auth_handler.login_with_password(
auth_handler = self.handlers.auth_handler
user_id, access_token, refresh_token = yield auth_handler.login_with_password(
user_id=user_id,
password=login_submission["password"])
result = {
"user_id": user_id, # may have changed
"access_token": token,
"access_token": access_token,
"refresh_token": refresh_token,
"home_server": self.hs.hostname,
}

View File

@@ -32,7 +32,7 @@ class PresenceStatusRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_GET(self, request, user_id):
auth_user, client = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
user = UserID.from_string(user_id)
state = yield self.handlers.presence_handler.get_state(
@@ -42,7 +42,7 @@ class PresenceStatusRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_PUT(self, request, user_id):
auth_user, client = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
user = UserID.from_string(user_id)
state = {}
@@ -77,7 +77,7 @@ class PresenceListRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_GET(self, request, user_id):
auth_user, client = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
user = UserID.from_string(user_id)
if not self.hs.is_mine(user):
@@ -97,7 +97,7 @@ class PresenceListRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_POST(self, request, user_id):
auth_user, client = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
user = UserID.from_string(user_id)
if not self.hs.is_mine(user):

View File

@@ -37,7 +37,7 @@ class ProfileDisplaynameRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_PUT(self, request, user_id):
auth_user, client = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
user = UserID.from_string(user_id)
try:
@@ -70,7 +70,7 @@ class ProfileAvatarURLRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_PUT(self, request, user_id):
auth_user, client = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
user = UserID.from_string(user_id)
try:

View File

@@ -27,7 +27,7 @@ class PusherRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_POST(self, request):
user, client = yield self.auth.get_user_by_req(request)
user, token_id = yield self.auth.get_user_by_req(request)
content = _parse_json(request)
@@ -65,7 +65,7 @@ class PusherRestServlet(ClientV1RestServlet):
try:
yield pusher_pool.add_pusher(
user_name=user.to_string(),
access_token=client.token_id,
access_token=token_id,
profile_tag=content['profile_tag'],
kind=content['kind'],
app_id=content['app_id'],

View File

@@ -62,7 +62,7 @@ class RoomCreateRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_POST(self, request):
auth_user, client = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
room_config = self.get_room_config(request)
info = yield self.make_room(room_config, auth_user, None)
@@ -125,7 +125,7 @@ class RoomStateEventRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_GET(self, request, room_id, event_type, state_key):
user, client = yield self.auth.get_user_by_req(request)
user, _ = yield self.auth.get_user_by_req(request)
msg_handler = self.handlers.message_handler
data = yield msg_handler.get_room_data(
@@ -143,7 +143,7 @@ class RoomStateEventRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_PUT(self, request, room_id, event_type, state_key, txn_id=None):
user, client = yield self.auth.get_user_by_req(request)
user, token_id = yield self.auth.get_user_by_req(request)
content = _parse_json(request)
@@ -159,7 +159,7 @@ class RoomStateEventRestServlet(ClientV1RestServlet):
msg_handler = self.handlers.message_handler
yield msg_handler.create_and_send_event(
event_dict, client=client, txn_id=txn_id,
event_dict, token_id=token_id, txn_id=txn_id,
)
defer.returnValue((200, {}))
@@ -175,7 +175,7 @@ class RoomSendEventRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_POST(self, request, room_id, event_type, txn_id=None):
user, client = yield self.auth.get_user_by_req(request)
user, token_id = yield self.auth.get_user_by_req(request)
content = _parse_json(request)
msg_handler = self.handlers.message_handler
@@ -186,7 +186,7 @@ class RoomSendEventRestServlet(ClientV1RestServlet):
"room_id": room_id,
"sender": user.to_string(),
},
client=client,
token_id=token_id,
txn_id=txn_id,
)
@@ -220,7 +220,7 @@ class JoinRoomAliasServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_POST(self, request, room_identifier, txn_id=None):
user, client = yield self.auth.get_user_by_req(request)
user, token_id = yield self.auth.get_user_by_req(request)
# the identifier could be a room alias or a room id. Try one then the
# other if it fails to parse, without swallowing other valid
@@ -250,7 +250,7 @@ class JoinRoomAliasServlet(ClientV1RestServlet):
"sender": user.to_string(),
"state_key": user.to_string(),
},
client=client,
token_id=token_id,
txn_id=txn_id,
)
@@ -289,7 +289,7 @@ class RoomMemberListRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_GET(self, request, room_id):
# TODO support Pagination stream API (limit/tokens)
user, client = yield self.auth.get_user_by_req(request)
user, _ = yield self.auth.get_user_by_req(request)
handler = self.handlers.room_member_handler
members = yield handler.get_room_members_as_pagination_chunk(
room_id=room_id,
@@ -317,7 +317,7 @@ class RoomMessageListRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_GET(self, request, room_id):
user, client = yield self.auth.get_user_by_req(request)
user, _ = yield self.auth.get_user_by_req(request)
pagination_config = PaginationConfig.from_request(
request, default_limit=10,
)
@@ -341,7 +341,7 @@ class RoomStateRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_GET(self, request, room_id):
user, client = yield self.auth.get_user_by_req(request)
user, _ = yield self.auth.get_user_by_req(request)
handler = self.handlers.message_handler
# Get all the current state for this room
events = yield handler.get_state_events(
@@ -357,7 +357,7 @@ class RoomInitialSyncRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_GET(self, request, room_id):
user, client = yield self.auth.get_user_by_req(request)
user, _ = yield self.auth.get_user_by_req(request)
pagination_config = PaginationConfig.from_request(request)
content = yield self.handlers.message_handler.room_initial_sync(
room_id=room_id,
@@ -402,7 +402,7 @@ class RoomMembershipRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_POST(self, request, room_id, membership_action, txn_id=None):
user, client = yield self.auth.get_user_by_req(request)
user, token_id = yield self.auth.get_user_by_req(request)
content = _parse_json(request)
@@ -427,7 +427,7 @@ class RoomMembershipRestServlet(ClientV1RestServlet):
"sender": user.to_string(),
"state_key": state_key,
},
client=client,
token_id=token_id,
txn_id=txn_id,
)
@@ -457,7 +457,7 @@ class RoomRedactEventRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_POST(self, request, room_id, event_id, txn_id=None):
user, client = yield self.auth.get_user_by_req(request)
user, token_id = yield self.auth.get_user_by_req(request)
content = _parse_json(request)
msg_handler = self.handlers.message_handler
@@ -469,7 +469,7 @@ class RoomRedactEventRestServlet(ClientV1RestServlet):
"sender": user.to_string(),
"redacts": event_id,
},
client=client,
token_id=token_id,
txn_id=txn_id,
)
@@ -497,7 +497,7 @@ class RoomTypingRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_PUT(self, request, room_id, user_id):
auth_user, client = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
room_id = urllib.unquote(room_id)
target_user = UserID.from_string(urllib.unquote(user_id))

View File

@@ -28,7 +28,7 @@ class VoipRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_GET(self, request):
auth_user, client = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
turnUris = self.hs.config.turn_uris
turnSecret = self.hs.config.turn_shared_secret
@@ -40,7 +40,7 @@ class VoipRestServlet(ClientV1RestServlet):
username = "%d:%s" % (expiry, auth_user.to_string())
mac = hmac.new(turnSecret, msg=username, digestmod=hashlib.sha1)
# We need to use standard base64 encoding here, *not* syutil's
# We need to use standard padded base64 encoding here
# encode_base64 because we need to add the standard padding to get the
# same result as the TURN server.
password = base64.b64encode(mac.digest())

View File

@@ -21,6 +21,7 @@ from . import (
auth,
receipts,
keys,
tokenrefresh,
)
from synapse.http.server import JsonResource
@@ -42,3 +43,4 @@ class ClientV2AlphaRestResource(JsonResource):
auth.register_servlets(hs, client_resource)
receipts.register_servlets(hs, client_resource)
keys.register_servlets(hs, client_resource)
tokenrefresh.register_servlets(hs, client_resource)

View File

@@ -55,7 +55,7 @@ class PasswordRestServlet(RestServlet):
if LoginType.PASSWORD in result:
# if using password, they should also be logged in
auth_user, client = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
if auth_user.to_string() != result[LoginType.PASSWORD]:
raise LoginError(400, "", Codes.UNKNOWN)
user_id = auth_user.to_string()
@@ -96,6 +96,7 @@ class ThreepidRestServlet(RestServlet):
self.hs = hs
self.identity_handler = hs.get_handlers().identity_handler
self.auth = hs.get_auth()
self.auth_handler = hs.get_handlers().auth_handler
@defer.inlineCallbacks
def on_GET(self, request):
@@ -119,7 +120,7 @@ class ThreepidRestServlet(RestServlet):
raise SynapseError(400, "Missing param", Codes.MISSING_PARAM)
threePidCreds = body['threePidCreds']
auth_user, client = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
threepid = yield self.identity_handler.threepid_from_creds(threePidCreds)

View File

@@ -40,7 +40,7 @@ class GetFilterRestServlet(RestServlet):
@defer.inlineCallbacks
def on_GET(self, request, user_id, filter_id):
target_user = UserID.from_string(user_id)
auth_user, client = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
if target_user != auth_user:
raise AuthError(403, "Cannot get filters for other users")
@@ -76,7 +76,7 @@ class CreateFilterRestServlet(RestServlet):
@defer.inlineCallbacks
def on_POST(self, request, user_id):
target_user = UserID.from_string(user_id)
auth_user, client = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
if target_user != auth_user:
raise AuthError(403, "Cannot create filters for other users")

View File

@@ -18,7 +18,8 @@ from twisted.internet import defer
from synapse.api.errors import SynapseError
from synapse.http.servlet import RestServlet
from synapse.types import UserID
from syutil.jsonutil import encode_canonical_json
from canonicaljson import encode_canonical_json
from ._base import client_v2_pattern
@@ -63,7 +64,7 @@ class KeyUploadServlet(RestServlet):
@defer.inlineCallbacks
def on_POST(self, request, device_id):
auth_user, client_info = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
user_id = auth_user.to_string()
# TODO: Check that the device_id matches that in the authentication
# or derive the device_id from the authentication instead.
@@ -108,7 +109,7 @@ class KeyUploadServlet(RestServlet):
@defer.inlineCallbacks
def on_GET(self, request, device_id):
auth_user, client_info = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
user_id = auth_user.to_string()
result = yield self.store.count_e2e_one_time_keys(user_id, device_id)
@@ -180,7 +181,7 @@ class KeyQueryServlet(RestServlet):
@defer.inlineCallbacks
def on_GET(self, request, user_id, device_id):
auth_user, client_info = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
auth_user_id = auth_user.to_string()
user_id = user_id if user_id else auth_user_id
device_ids = [device_id] if device_id else []

View File

@@ -39,7 +39,7 @@ class ReceiptRestServlet(RestServlet):
@defer.inlineCallbacks
def on_POST(self, request, room_id, receipt_type, event_id):
user, client = yield self.auth.get_user_by_req(request)
user, _ = yield self.auth.get_user_by_req(request)
yield self.receipts_handler.received_client_receipt(
room_id,

View File

@@ -87,7 +87,7 @@ class SyncRestServlet(RestServlet):
@defer.inlineCallbacks
def on_GET(self, request):
user, client = yield self.auth.get_user_by_req(request)
user, token_id = yield self.auth.get_user_by_req(request)
timeout = parse_integer(request, "timeout", default=0)
limit = parse_integer(request, "limit", required=True)
@@ -125,7 +125,6 @@ class SyncRestServlet(RestServlet):
sync_config = SyncConfig(
user=user,
client_info=client,
gap=gap,
limit=limit,
sort=sort,
@@ -152,7 +151,7 @@ class SyncRestServlet(RestServlet):
sync_result.private_user_data, filter, time_now
),
"rooms": self.encode_rooms(
sync_result.rooms, filter, time_now, client.token_id
sync_result.rooms, filter, time_now, token_id
),
"next_batch": sync_result.next_batch.to_string(),
}

View File

@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
# Copyright 2015 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from twisted.internet import defer
from synapse.api.errors import AuthError, StoreError, SynapseError
from synapse.http.servlet import RestServlet
from ._base import client_v2_pattern, parse_json_dict_from_request
class TokenRefreshRestServlet(RestServlet):
"""
Exchanges refresh tokens for a pair of an access token and a new refresh
token.
"""
PATTERN = client_v2_pattern("/tokenrefresh")
def __init__(self, hs):
super(TokenRefreshRestServlet, self).__init__()
self.hs = hs
self.store = hs.get_datastore()
@defer.inlineCallbacks
def on_POST(self, request):
body = parse_json_dict_from_request(request)
try:
old_refresh_token = body["refresh_token"]
auth_handler = self.hs.get_handlers().auth_handler
(user_id, new_refresh_token) = yield self.store.exchange_refresh_token(
old_refresh_token, auth_handler.generate_refresh_token)
new_access_token = yield auth_handler.issue_access_token(user_id)
defer.returnValue((200, {
"access_token": new_access_token,
"refresh_token": new_refresh_token,
}))
except KeyError:
raise SynapseError(400, "Missing required key 'refresh_token'.")
except StoreError:
raise AuthError(403, "Did not recognize refresh token")
def register_servlets(hs, http_server):
TokenRefreshRestServlet(hs).register(http_server)

View File

@@ -16,9 +16,9 @@
from twisted.web.resource import Resource
from synapse.http.server import respond_with_json_bytes
from syutil.crypto.jsonsign import sign_json
from syutil.base64util import encode_base64
from syutil.jsonutil import encode_canonical_json
from signedjson.sign import sign_json
from unpaddedbase64 import encode_base64
from canonicaljson import encode_canonical_json
from OpenSSL import crypto
import logging

View File

@@ -16,9 +16,9 @@
from twisted.web.resource import Resource
from synapse.http.server import respond_with_json_bytes
from syutil.crypto.jsonsign import sign_json
from syutil.base64util import encode_base64
from syutil.jsonutil import encode_canonical_json
from signedjson.sign import sign_json
from unpaddedbase64 import encode_base64
from canonicaljson import encode_canonical_json
from hashlib import sha256
from OpenSSL import crypto
import logging

View File

@@ -66,7 +66,7 @@ class ContentRepoResource(resource.Resource):
@defer.inlineCallbacks
def map_request_to_name(self, request):
# auth the user
auth_user, client = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
# namespace all file uploads on the user
prefix = base64.urlsafe_b64encode(

View File

@@ -70,7 +70,7 @@ class UploadResource(BaseMediaResource):
@request_handler
@defer.inlineCallbacks
def _async_render_POST(self, request):
auth_user, client = yield self.auth.get_user_by_req(request)
auth_user, _ = yield self.auth.get_user_by_req(request)
# TODO: The checks here are a bit late. The content will have
# already been uploaded to a tmp file at this point
content_length = request.getHeader("Content-Length")

View File

@@ -54,7 +54,7 @@ logger = logging.getLogger(__name__)
# Remember to update this number every time a change is made to database
# schema files, so the users will be informed on server restarts.
SCHEMA_VERSION = 22
SCHEMA_VERSION = 23
dir_path = os.path.abspath(os.path.dirname(__file__))
@@ -94,9 +94,9 @@ class DataStore(RoomMemberStore, RoomStore,
)
@defer.inlineCallbacks
def insert_client_ip(self, user, access_token, device_id, ip, user_agent):
def insert_client_ip(self, user, access_token, ip, user_agent):
now = int(self._clock.time_msec())
key = (user.to_string(), access_token, device_id, ip)
key = (user.to_string(), access_token, ip)
try:
last_seen = self.client_ip_last_seen.get(key)
@@ -120,7 +120,6 @@ class DataStore(RoomMemberStore, RoomStore,
"user_agent": user_agent,
},
values={
"device_id": device_id,
"last_seen": now,
},
desc="insert_client_ip",
@@ -132,7 +131,7 @@ class DataStore(RoomMemberStore, RoomStore,
table="user_ips",
keyvalues={"user_id": user.to_string()},
retcols=[
"device_id", "access_token", "ip", "user_agent", "last_seen"
"access_token", "ip", "user_agent", "last_seen"
],
desc="get_user_ip_and_agents",
)

View File

@@ -181,6 +181,7 @@ class SQLBaseStore(object):
self._transaction_id_gen = IdGenerator("sent_transactions", "id", self)
self._state_groups_id_gen = IdGenerator("state_groups", "id", self)
self._access_tokens_id_gen = IdGenerator("access_tokens", "id", self)
self._refresh_tokens_id_gen = IdGenerator("refresh_tokens", "id", self)
self._pushers_id_gen = IdGenerator("pushers", "id", self)
self._push_rule_id_gen = IdGenerator("push_rules", "id", self)
self._push_rules_enable_id_gen = IdGenerator("push_rules_enable", "id", self)

View File

@@ -17,7 +17,7 @@ from twisted.internet import defer
from ._base import SQLBaseStore
from synapse.util.caches.descriptors import cached
from syutil.base64util import encode_base64
from unpaddedbase64 import encode_base64
import logging
from Queue import PriorityQueue, Empty

View File

@@ -24,7 +24,7 @@ from synapse.util.logcontext import preserve_context_over_deferred
from synapse.util.logutils import log_function
from synapse.api.constants import EventTypes
from syutil.jsonutil import encode_json
from canonicaljson import encode_canonical_json
from contextlib import contextmanager
import logging
@@ -33,6 +33,13 @@ import ujson as json
logger = logging.getLogger(__name__)
def encode_json(json_object):
if USE_FROZEN_DICTS:
# ujson doesn't like frozen_dicts
return encode_canonical_json(json_object)
else:
return json.dumps(json_object, ensure_ascii=False)
# These values are used in the `enqueus_event` and `_do_fetch` methods to
# control how we batch/bulk fetch events from the database.
# The values are plucked out of thing air to make initial sync run faster
@@ -253,8 +260,7 @@ class EventsStore(SQLBaseStore):
)
metadata_json = encode_json(
event.internal_metadata.get_dict(),
using_frozen_dicts=USE_FROZEN_DICTS
event.internal_metadata.get_dict()
).decode("UTF-8")
sql = (
@@ -329,12 +335,9 @@ class EventsStore(SQLBaseStore):
"event_id": event.event_id,
"room_id": event.room_id,
"internal_metadata": encode_json(
event.internal_metadata.get_dict(),
using_frozen_dicts=USE_FROZEN_DICTS
).decode("UTF-8"),
"json": encode_json(
event_dict(event), using_frozen_dicts=USE_FROZEN_DICTS
event.internal_metadata.get_dict()
).decode("UTF-8"),
"json": encode_json(event_dict(event)).decode("UTF-8"),
}
for event, _ in events_and_contexts
],
@@ -353,9 +356,7 @@ class EventsStore(SQLBaseStore):
"type": event.type,
"processed": True,
"outlier": event.internal_metadata.is_outlier(),
"content": encode_json(
event.content, using_frozen_dicts=USE_FROZEN_DICTS
).decode("UTF-8"),
"content": encode_json(event.content).decode("UTF-8"),
}
for event, _ in events_and_contexts
],

View File

@@ -19,7 +19,7 @@ from synapse.util.caches.descriptors import cachedInlineCallbacks
from twisted.internet import defer
import OpenSSL
from syutil.crypto.signing_key import decode_verify_key_bytes
from signedjson.key import decode_verify_key_bytes
import hashlib

View File

@@ -18,7 +18,7 @@ from twisted.internet import defer
from synapse.api.errors import StoreError
from syutil.jsonutil import encode_canonical_json
from canonicaljson import encode_canonical_json
import logging
import simplejson as json

View File

@@ -50,6 +50,28 @@ class RegistrationStore(SQLBaseStore):
desc="add_access_token_to_user",
)
@defer.inlineCallbacks
def add_refresh_token_to_user(self, user_id, token):
"""Adds a refresh token for the given user.
Args:
user_id (str): The user ID.
token (str): The new refresh token to add.
Raises:
StoreError if there was a problem adding this.
"""
next_id = yield self._refresh_tokens_id_gen.get_next()
yield self._simple_insert(
"refresh_tokens",
{
"id": next_id,
"user_id": user_id,
"token": token
},
desc="add_refresh_token_to_user",
)
@defer.inlineCallbacks
def register(self, user_id, token, password_hash):
"""Attempts to register an account.
@@ -146,26 +168,65 @@ class RegistrationStore(SQLBaseStore):
user_id
)
for r in rows:
self.get_user_by_token.invalidate((r,))
self.get_user_by_access_token.invalidate((r,))
@cached()
def get_user_by_token(self, token):
def get_user_by_access_token(self, token):
"""Get a user from the given access token.
Args:
token (str): The access token of a user.
Returns:
dict: Including the name (user_id), device_id and whether they are
an admin.
dict: Including the name (user_id) and the ID of their access token.
Raises:
StoreError if no user was found.
"""
return self.runInteraction(
"get_user_by_token",
"get_user_by_access_token",
self._query_for_auth,
token
)
def exchange_refresh_token(self, refresh_token, token_generator):
"""Exchange a refresh token for a new access token and refresh token.
Doing so invalidates the old refresh token - refresh tokens are single
use.
Args:
token (str): The refresh token of a user.
token_generator (fn: str -> str): Function which, when given a
user ID, returns a unique refresh token for that user. This
function must never return the same value twice.
Returns:
tuple of (user_id, refresh_token)
Raises:
StoreError if no user was found with that refresh token.
"""
return self.runInteraction(
"exchange_refresh_token",
self._exchange_refresh_token,
refresh_token,
token_generator
)
def _exchange_refresh_token(self, txn, old_token, token_generator):
sql = "SELECT user_id FROM refresh_tokens WHERE token = ?"
txn.execute(sql, (old_token,))
rows = self.cursor_to_dict(txn)
if not rows:
raise StoreError(403, "Did not recognize refresh token")
user_id = rows[0]["user_id"]
# TODO(danielwh): Maybe perform a validation on the macaroon that
# macaroon.user_id == user_id.
new_token = token_generator(user_id)
sql = "UPDATE refresh_tokens SET token = ? WHERE token = ?"
txn.execute(sql, (new_token, old_token,))
return user_id, new_token
@defer.inlineCallbacks
def is_server_admin(self, user):
res = yield self._simple_select_one_onecol(
@@ -180,8 +241,7 @@ class RegistrationStore(SQLBaseStore):
def _query_for_auth(self, txn, token):
sql = (
"SELECT users.name, users.admin,"
" access_tokens.device_id, access_tokens.id as token_id"
"SELECT users.name, access_tokens.id as token_id"
" FROM users"
" INNER JOIN access_tokens on users.name = access_tokens.user_id"
" WHERE token = ?"

View File

@@ -0,0 +1,16 @@
/* Copyright 2015 OpenMarket 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.
*/
DROP INDEX IF EXISTS state_groups_state_tuple;

View File

@@ -0,0 +1,21 @@
/* Copyright 2015 OpenMarket 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.
*/
CREATE TABLE IF NOT EXISTS refresh_tokens(
id INTEGER PRIMARY KEY,
token TEXT NOT NULL,
user_id TEXT NOT NULL,
UNIQUE (token)
);

View File

@@ -17,7 +17,7 @@ from twisted.internet import defer
from _base import SQLBaseStore
from syutil.base64util import encode_base64
from unpaddedbase64 import encode_base64
from synapse.crypto.event_signing import compute_event_reference_hash

View File

@@ -18,7 +18,7 @@ from synapse.util.caches.descriptors import cached
from collections import namedtuple
from syutil.jsonutil import encode_canonical_json
from canonicaljson import encode_canonical_json
import logging
logger = logging.getLogger(__name__)

View File

@@ -209,7 +209,3 @@ class RoomStreamToken(namedtuple("_StreamToken", "topological stream")):
return "t%d-%d" % (self.topological, self.stream)
else:
return "s%d" % (self.stream,)
# token_id is the primary key ID of the access token, not the access token itself.
ClientInfo = namedtuple("ClientInfo", ("device_id", "token_id"))

View File

@@ -40,21 +40,19 @@ class AuthTestCase(unittest.TestCase):
self.store.get_app_service_by_token = Mock(return_value=None)
user_info = {
"name": self.test_user,
"device_id": "nothing",
"token_id": "ditto",
"admin": False
}
self.store.get_user_by_token = Mock(return_value=user_info)
self.store.get_user_by_access_token = Mock(return_value=user_info)
request = Mock(args={})
request.args["access_token"] = [self.test_token]
request.requestHeaders.getRawHeaders = Mock(return_value=[""])
(user, info) = yield self.auth.get_user_by_req(request)
(user, _) = yield self.auth.get_user_by_req(request)
self.assertEquals(user.to_string(), self.test_user)
def test_get_user_by_req_user_bad_token(self):
self.store.get_app_service_by_token = Mock(return_value=None)
self.store.get_user_by_token = Mock(return_value=None)
self.store.get_user_by_access_token = Mock(return_value=None)
request = Mock(args={})
request.args["access_token"] = [self.test_token]
@@ -66,11 +64,9 @@ class AuthTestCase(unittest.TestCase):
self.store.get_app_service_by_token = Mock(return_value=None)
user_info = {
"name": self.test_user,
"device_id": "nothing",
"token_id": "ditto",
"admin": False
}
self.store.get_user_by_token = Mock(return_value=user_info)
self.store.get_user_by_access_token = Mock(return_value=user_info)
request = Mock(args={})
request.requestHeaders.getRawHeaders = Mock(return_value=[""])
@@ -81,17 +77,17 @@ class AuthTestCase(unittest.TestCase):
def test_get_user_by_req_appservice_valid_token(self):
app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
self.store.get_app_service_by_token = Mock(return_value=app_service)
self.store.get_user_by_token = Mock(return_value=None)
self.store.get_user_by_access_token = Mock(return_value=None)
request = Mock(args={})
request.args["access_token"] = [self.test_token]
request.requestHeaders.getRawHeaders = Mock(return_value=[""])
(user, info) = yield self.auth.get_user_by_req(request)
(user, _) = yield self.auth.get_user_by_req(request)
self.assertEquals(user.to_string(), self.test_user)
def test_get_user_by_req_appservice_bad_token(self):
self.store.get_app_service_by_token = Mock(return_value=None)
self.store.get_user_by_token = Mock(return_value=None)
self.store.get_user_by_access_token = Mock(return_value=None)
request = Mock(args={})
request.args["access_token"] = [self.test_token]
@@ -102,7 +98,7 @@ class AuthTestCase(unittest.TestCase):
def test_get_user_by_req_appservice_missing_token(self):
app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
self.store.get_app_service_by_token = Mock(return_value=app_service)
self.store.get_user_by_token = Mock(return_value=None)
self.store.get_user_by_access_token = Mock(return_value=None)
request = Mock(args={})
request.requestHeaders.getRawHeaders = Mock(return_value=[""])
@@ -115,13 +111,13 @@ class AuthTestCase(unittest.TestCase):
app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
app_service.is_interested_in_user = Mock(return_value=True)
self.store.get_app_service_by_token = Mock(return_value=app_service)
self.store.get_user_by_token = Mock(return_value=None)
self.store.get_user_by_access_token = Mock(return_value=None)
request = Mock(args={})
request.args["access_token"] = [self.test_token]
request.args["user_id"] = [masquerading_user_id]
request.requestHeaders.getRawHeaders = Mock(return_value=[""])
(user, info) = yield self.auth.get_user_by_req(request)
(user, _) = yield self.auth.get_user_by_req(request)
self.assertEquals(user.to_string(), masquerading_user_id)
def test_get_user_by_req_appservice_valid_token_bad_user_id(self):
@@ -129,7 +125,7 @@ class AuthTestCase(unittest.TestCase):
app_service = Mock(token="foobar", url="a_url", sender=self.test_user)
app_service.is_interested_in_user = Mock(return_value=False)
self.store.get_app_service_by_token = Mock(return_value=app_service)
self.store.get_user_by_token = Mock(return_value=None)
self.store.get_user_by_access_token = Mock(return_value=None)
request = Mock(args={})
request.args["access_token"] = [self.test_token]

View File

@@ -0,0 +1,70 @@
# -*- coding: utf-8 -*-
# Copyright 2015 OpenMarket 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 pymacaroons
from mock import Mock, NonCallableMock
from synapse.handlers.auth import AuthHandler
from tests import unittest
from tests.utils import setup_test_homeserver
from twisted.internet import defer
class AuthHandlers(object):
def __init__(self, hs):
self.auth_handler = AuthHandler(hs)
class AuthTestCase(unittest.TestCase):
@defer.inlineCallbacks
def setUp(self):
self.hs = yield setup_test_homeserver(handlers=None)
self.hs.handlers = AuthHandlers(self.hs)
def test_token_is_a_macaroon(self):
self.hs.config.macaroon_secret_key = "this key is a huge secret"
token = self.hs.handlers.auth_handler.generate_access_token("some_user")
# Check that we can parse the thing with pymacaroons
macaroon = pymacaroons.Macaroon.deserialize(token)
# The most basic of sanity checks
if "some_user" not in macaroon.inspect():
self.fail("some_user was not in %s" % macaroon.inspect())
def test_macaroon_caveats(self):
self.hs.config.macaroon_secret_key = "this key is a massive secret"
self.hs.clock.now = 5000
token = self.hs.handlers.auth_handler.generate_access_token("a_user")
macaroon = pymacaroons.Macaroon.deserialize(token)
def verify_gen(caveat):
return caveat == "gen = 1"
def verify_user(caveat):
return caveat == "user_id = a_user"
def verify_type(caveat):
return caveat == "type = access"
def verify_expiry(caveat):
return caveat == "time < 8600000"
v = pymacaroons.Verifier()
v.satisfy_general(verify_gen)
v.satisfy_general(verify_user)
v.satisfy_general(verify_type)
v.satisfy_general(verify_expiry)
v.verify(macaroon, self.hs.config.macaroon_secret_key)

View File

@@ -70,15 +70,13 @@ class PresenceStateTestCase(unittest.TestCase):
return defer.succeed([])
self.datastore.get_presence_list = get_presence_list
def _get_user_by_token(token=None):
def _get_user_by_access_token(token=None):
return {
"user": UserID.from_string(myid),
"admin": False,
"device_id": None,
"token_id": 1,
}
hs.get_v1auth().get_user_by_token = _get_user_by_token
hs.get_v1auth().get_user_by_access_token = _get_user_by_access_token
room_member_handler = hs.handlers.room_member_handler = Mock(
spec=[
@@ -159,11 +157,9 @@ class PresenceListTestCase(unittest.TestCase):
)
self.datastore.has_presence_state = has_presence_state
def _get_user_by_token(token=None):
def _get_user_by_access_token(token=None):
return {
"user": UserID.from_string(myid),
"admin": False,
"device_id": None,
"token_id": 1,
}
@@ -173,7 +169,7 @@ class PresenceListTestCase(unittest.TestCase):
]
)
hs.get_v1auth().get_user_by_token = _get_user_by_token
hs.get_v1auth().get_user_by_access_token = _get_user_by_access_token
presence.register_servlets(hs, self.mock_resource)

View File

@@ -54,14 +54,12 @@ class RoomPermissionsTestCase(RestTestCase):
hs.get_handlers().federation_handler = Mock()
def _get_user_by_token(token=None):
def _get_user_by_access_token(token=None):
return {
"user": UserID.from_string(self.auth_user_id),
"admin": False,
"device_id": None,
"token_id": 1,
}
hs.get_v1auth().get_user_by_token = _get_user_by_token
hs.get_v1auth().get_user_by_access_token = _get_user_by_access_token
def _insert_client_ip(*args, **kwargs):
return defer.succeed(None)
@@ -441,14 +439,12 @@ class RoomsMemberListTestCase(RestTestCase):
self.auth_user_id = self.user_id
def _get_user_by_token(token=None):
def _get_user_by_access_token(token=None):
return {
"user": UserID.from_string(self.auth_user_id),
"admin": False,
"device_id": None,
"token_id": 1,
}
hs.get_v1auth().get_user_by_token = _get_user_by_token
hs.get_v1auth().get_user_by_access_token = _get_user_by_access_token
def _insert_client_ip(*args, **kwargs):
return defer.succeed(None)
@@ -521,14 +517,12 @@ class RoomsCreateTestCase(RestTestCase):
hs.get_handlers().federation_handler = Mock()
def _get_user_by_token(token=None):
def _get_user_by_access_token(token=None):
return {
"user": UserID.from_string(self.auth_user_id),
"admin": False,
"device_id": None,
"token_id": 1,
}
hs.get_v1auth().get_user_by_token = _get_user_by_token
hs.get_v1auth().get_user_by_access_token = _get_user_by_access_token
def _insert_client_ip(*args, **kwargs):
return defer.succeed(None)
@@ -614,15 +608,13 @@ class RoomTopicTestCase(RestTestCase):
hs.get_handlers().federation_handler = Mock()
def _get_user_by_token(token=None):
def _get_user_by_access_token(token=None):
return {
"user": UserID.from_string(self.auth_user_id),
"admin": False,
"device_id": None,
"token_id": 1,
}
hs.get_v1auth().get_user_by_token = _get_user_by_token
hs.get_v1auth().get_user_by_access_token = _get_user_by_access_token
def _insert_client_ip(*args, **kwargs):
return defer.succeed(None)
@@ -721,14 +713,12 @@ class RoomMemberStateTestCase(RestTestCase):
hs.get_handlers().federation_handler = Mock()
def _get_user_by_token(token=None):
def _get_user_by_access_token(token=None):
return {
"user": UserID.from_string(self.auth_user_id),
"admin": False,
"device_id": None,
"token_id": 1,
}
hs.get_v1auth().get_user_by_token = _get_user_by_token
hs.get_v1auth().get_user_by_access_token = _get_user_by_access_token
def _insert_client_ip(*args, **kwargs):
return defer.succeed(None)
@@ -848,14 +838,12 @@ class RoomMessagesTestCase(RestTestCase):
hs.get_handlers().federation_handler = Mock()
def _get_user_by_token(token=None):
def _get_user_by_access_token(token=None):
return {
"user": UserID.from_string(self.auth_user_id),
"admin": False,
"device_id": None,
"token_id": 1,
}
hs.get_v1auth().get_user_by_token = _get_user_by_token
hs.get_v1auth().get_user_by_access_token = _get_user_by_access_token
def _insert_client_ip(*args, **kwargs):
return defer.succeed(None)
@@ -945,14 +933,12 @@ class RoomInitialSyncTestCase(RestTestCase):
hs.get_handlers().federation_handler = Mock()
def _get_user_by_token(token=None):
def _get_user_by_access_token(token=None):
return {
"user": UserID.from_string(self.auth_user_id),
"admin": False,
"device_id": None,
"token_id": 1,
}
hs.get_v1auth().get_user_by_token = _get_user_by_token
hs.get_v1auth().get_user_by_access_token = _get_user_by_access_token
def _insert_client_ip(*args, **kwargs):
return defer.succeed(None)

View File

@@ -61,15 +61,13 @@ class RoomTypingTestCase(RestTestCase):
hs.get_handlers().federation_handler = Mock()
def _get_user_by_token(token=None):
def _get_user_by_access_token(token=None):
return {
"user": UserID.from_string(self.auth_user_id),
"admin": False,
"device_id": None,
"token_id": 1,
}
hs.get_v1auth().get_user_by_token = _get_user_by_token
hs.get_v1auth().get_user_by_access_token = _get_user_by_access_token
def _insert_client_ip(*args, **kwargs):
return defer.succeed(None)

View File

@@ -37,7 +37,7 @@ class RestTestCase(unittest.TestCase):
self.mock_resource = None
self.auth_user_id = None
def mock_get_user_by_token(self, token=None):
def mock_get_user_by_access_token(self, token=None):
return self.auth_user_id
@defer.inlineCallbacks

View File

@@ -43,14 +43,12 @@ class V2AlphaRestTestCase(unittest.TestCase):
resource_for_federation=self.mock_resource,
)
def _get_user_by_token(token=None):
def _get_user_by_access_token(token=None):
return {
"user": UserID.from_string(self.USER_ID),
"admin": False,
"device_id": None,
"token_id": 1,
}
hs.get_auth().get_user_by_token = _get_user_by_token
hs.get_auth().get_user_by_access_token = _get_user_by_access_token
for r in self.TO_REGISTER:
r.register_servlets(hs, self.mock_resource)

View File

@@ -17,7 +17,9 @@
from tests import unittest
from twisted.internet import defer
from synapse.api.errors import StoreError
from synapse.storage.registration import RegistrationStore
from synapse.util import stringutils
from tests.utils import setup_test_homeserver
@@ -27,6 +29,7 @@ class RegistrationStoreTestCase(unittest.TestCase):
@defer.inlineCallbacks
def setUp(self):
hs = yield setup_test_homeserver()
self.db_pool = hs.get_db_pool()
self.store = RegistrationStore(hs)
@@ -46,13 +49,11 @@ class RegistrationStoreTestCase(unittest.TestCase):
(yield self.store.get_user_by_id(self.user_id))
)
result = yield self.store.get_user_by_token(self.tokens[0])
result = yield self.store.get_user_by_access_token(self.tokens[0])
self.assertDictContainsSubset(
{
"admin": 0,
"device_id": None,
"name": self.user_id,
"name": self.user_id,
},
result
)
@@ -64,16 +65,66 @@ class RegistrationStoreTestCase(unittest.TestCase):
yield self.store.register(self.user_id, self.tokens[0], self.pwhash)
yield self.store.add_access_token_to_user(self.user_id, self.tokens[1])
result = yield self.store.get_user_by_token(self.tokens[1])
result = yield self.store.get_user_by_access_token(self.tokens[1])
self.assertDictContainsSubset(
{
"admin": 0,
"device_id": None,
"name": self.user_id,
"name": self.user_id,
},
result
)
self.assertTrue("token_id" in result)
@defer.inlineCallbacks
def test_exchange_refresh_token_valid(self):
uid = stringutils.random_string(32)
generator = TokenGenerator()
last_token = generator.generate(uid)
self.db_pool.runQuery(
"INSERT INTO refresh_tokens(user_id, token) VALUES(?,?)",
(uid, last_token,))
(found_user_id, refresh_token) = yield self.store.exchange_refresh_token(
last_token, generator.generate)
self.assertEqual(uid, found_user_id)
rows = yield self.db_pool.runQuery(
"SELECT token FROM refresh_tokens WHERE user_id = ?", (uid, ))
self.assertEqual([(refresh_token,)], rows)
# We issued token 1, then exchanged it for token 2
expected_refresh_token = u"%s-%d" % (uid, 2,)
self.assertEqual(expected_refresh_token, refresh_token)
@defer.inlineCallbacks
def test_exchange_refresh_token_none(self):
uid = stringutils.random_string(32)
generator = TokenGenerator()
last_token = generator.generate(uid)
with self.assertRaises(StoreError):
yield self.store.exchange_refresh_token(last_token, generator.generate)
@defer.inlineCallbacks
def test_exchange_refresh_token_invalid(self):
uid = stringutils.random_string(32)
generator = TokenGenerator()
last_token = generator.generate(uid)
wrong_token = "%s-wrong" % (last_token,)
self.db_pool.runQuery(
"INSERT INTO refresh_tokens(user_id, token) VALUES(?,?)",
(uid, wrong_token,))
with self.assertRaises(StoreError):
yield self.store.exchange_refresh_token(last_token, generator.generate)
class TokenGenerator:
def __init__(self):
self._last_issued_token = 0
def generate(self, user_id):
self._last_issued_token += 1
return u"%s-%d" % (user_id, self._last_issued_token,)

View File

@@ -204,8 +204,8 @@ class StateTestCase(unittest.TestCase):
nodes={
"START": DictObj(
type=EventTypes.Create,
state_key="creator",
content={"membership": "@user_id:example.com"},
state_key="",
content={"creator": "@user_id:example.com"},
depth=1,
),
"A": DictObj(
@@ -259,8 +259,8 @@ class StateTestCase(unittest.TestCase):
nodes={
"START": DictObj(
type=EventTypes.Create,
state_key="creator",
content={"membership": "@user_id:example.com"},
state_key="",
content={"creator": "@user_id:example.com"},
depth=1,
),
"A": DictObj(
@@ -432,13 +432,19 @@ class StateTestCase(unittest.TestCase):
def test_resolve_message_conflict(self):
event = create_event(type="test_message", name="event")
creation = create_event(
type=EventTypes.Create, state_key=""
)
old_state_1 = [
creation,
create_event(type="test1", state_key="1"),
create_event(type="test1", state_key="2"),
create_event(type="test2", state_key=""),
]
old_state_2 = [
creation,
create_event(type="test1", state_key="1"),
create_event(type="test3", state_key="2"),
create_event(type="test4", state_key=""),
@@ -446,7 +452,7 @@ class StateTestCase(unittest.TestCase):
context = yield self._get_context(event, old_state_1, old_state_2)
self.assertEqual(len(context.current_state), 5)
self.assertEqual(len(context.current_state), 6)
self.assertIsNone(context.state_group)
@@ -454,13 +460,19 @@ class StateTestCase(unittest.TestCase):
def test_resolve_state_conflict(self):
event = create_event(type="test4", state_key="", name="event")
creation = create_event(
type=EventTypes.Create, state_key=""
)
old_state_1 = [
creation,
create_event(type="test1", state_key="1"),
create_event(type="test1", state_key="2"),
create_event(type="test2", state_key=""),
]
old_state_2 = [
creation,
create_event(type="test1", state_key="1"),
create_event(type="test3", state_key="2"),
create_event(type="test4", state_key=""),
@@ -468,7 +480,7 @@ class StateTestCase(unittest.TestCase):
context = yield self._get_context(event, old_state_1, old_state_2)
self.assertEqual(len(context.current_state), 5)
self.assertEqual(len(context.current_state), 6)
self.assertIsNone(context.state_group)
@@ -484,36 +496,45 @@ class StateTestCase(unittest.TestCase):
}
)
creation = create_event(
type=EventTypes.Create, state_key="",
content={"creator": "@foo:bar"}
)
old_state_1 = [
creation,
member_event,
create_event(type="test1", state_key="1", depth=1),
]
old_state_2 = [
creation,
member_event,
create_event(type="test1", state_key="1", depth=2),
]
context = yield self._get_context(event, old_state_1, old_state_2)
self.assertEqual(old_state_2[1], context.current_state[("test1", "1")])
self.assertEqual(old_state_2[2], context.current_state[("test1", "1")])
# Reverse the depth to make sure we are actually using the depths
# during state resolution.
old_state_1 = [
creation,
member_event,
create_event(type="test1", state_key="1", depth=2),
]
old_state_2 = [
creation,
member_event,
create_event(type="test1", state_key="1", depth=1),
]
context = yield self._get_context(event, old_state_1, old_state_2)
self.assertEqual(old_state_1[1], context.current_state[("test1", "1")])
self.assertEqual(old_state_1[2], context.current_state[("test1", "1")])
def _get_context(self, event, old_state_1, old_state_2):
group_name_1 = "group_name_1"

View File

@@ -27,6 +27,7 @@ from twisted.enterprise.adbapi import ConnectionPool
from collections import namedtuple
from mock import patch, Mock
import hashlib
import urllib
import urlparse
@@ -44,6 +45,8 @@ def setup_test_homeserver(name="test", datastore=None, config=None, **kargs):
config.signing_key = [MockKey()]
config.event_cache_size = 1
config.disable_registration = False
config.macaroon_secret_key = "not even a little secret"
config.server_name = "server.under.test"
if "clock" not in kargs:
kargs["clock"] = MockClock()
@@ -65,6 +68,18 @@ def setup_test_homeserver(name="test", datastore=None, config=None, **kargs):
**kargs
)
# bcrypt is far too slow to be doing in unit tests
def swap_out_hash_for_testing(old_build_handlers):
def build_handlers():
handlers = old_build_handlers()
auth_handler = handlers.auth_handler
auth_handler.hash = lambda p: hashlib.md5(p).hexdigest()
auth_handler.validate_hash = lambda p, h: hashlib.md5(p).hexdigest() == h
return handlers
return build_handlers
hs.build_handlers = swap_out_hash_for_testing(hs.build_handlers)
defer.returnValue(hs)
@@ -275,12 +290,10 @@ class MemoryDataStore(object):
raise StoreError(400, "User in use.")
self.tokens_to_users[token] = user_id
def get_user_by_token(self, token):
def get_user_by_access_token(self, token):
try:
return {
"name": self.tokens_to_users[token],
"admin": 0,
"device_id": None,
}
except:
raise StoreError(400, "User does not exist.")
@@ -378,7 +391,7 @@ class MemoryDataStore(object):
def get_ops_levels(self, room_id):
return defer.succeed((5, 5, 5))
def insert_client_ip(self, user, device_id, access_token, ip, user_agent):
def insert_client_ip(self, user, access_token, ip, user_agent):
return defer.succeed(None)

25
tox.ini Normal file
View File

@@ -0,0 +1,25 @@
[tox]
envlist = packaging, py27, pep8
[testenv]
deps =
coverage
Twisted>=15.1
mock
setenv =
PYTHONDONTWRITEBYTECODE = no_byte_code
commands =
coverage run --source=synapse {envbindir}/trial {posargs:tests}
coverage report -m
[testenv:packaging]
deps =
check-manifest
commands =
check-manifest
[testenv:pep8]
basepython = python2.7
deps =
flake8
commands = flake8 synapse