Merge branch 'auth' into refresh

This commit is contained in:
Daniel Wagner-Hall 2015-08-20 11:03:47 +01:00
commit ade5342752
7 changed files with 115 additions and 24 deletions

View File

@ -126,12 +126,26 @@ sub on_unknown_event
if (!$bridgestate->{$room_id}->{gathered_candidates}) { if (!$bridgestate->{$room_id}->{gathered_candidates}) {
$bridgestate->{$room_id}->{gathered_candidates} = 1; $bridgestate->{$room_id}->{gathered_candidates} = 1;
my $offer = $bridgestate->{$room_id}->{offer}; my $offer = $bridgestate->{$room_id}->{offer};
my $candidate_block = ""; my $candidate_block = {
audio => '',
video => '',
};
foreach (@{$event->{content}->{candidates}}) { foreach (@{$event->{content}->{candidates}}) {
$candidate_block .= "a=" . $_->{candidate} . "\r\n"; if ($_->{sdpMid}) {
$candidate_block->{$_->{sdpMid}} .= "a=" . $_->{candidate} . "\r\n";
} }
# XXX: collate using the right m= line - for now assume audio call else {
$offer =~ s/(a=rtcp.*[\r\n]+)/$1$candidate_block/; $candidate_block->{audio} .= "a=" . $_->{candidate} . "\r\n";
$candidate_block->{video} .= "a=" . $_->{candidate} . "\r\n";
}
}
# 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", { my $f = send_verto_json_request("verto.invite", {
"sdp" => $offer, "sdp" => $offer,
@ -172,22 +186,18 @@ sub on_room_message
warn "[Matrix] in $room_id: $from: " . $content->{body} . "\n"; 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( Future->needs_all(
$bot_matrix->login( %{ $CONFIG{"matrix-bot"} } )->then( sub { $bot_matrix->login( %{ $CONFIG{"matrix-bot"} } )->then( sub {
$bot_matrix->start; $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; )->get;
$loop->attach_signal( $loop->attach_signal(

View File

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

View File

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

View File

@ -25,9 +25,9 @@ import synapse.util.stringutils as stringutils
from synapse.util.async import run_on_reactor from synapse.util.async import run_on_reactor
from synapse.http.client import CaptchaServerHttpClient from synapse.http.client import CaptchaServerHttpClient
import base64
import bcrypt import bcrypt
import logging import logging
import pymacaroons
import urllib import urllib
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -274,11 +274,18 @@ class RegistrationHandler(BaseHandler):
) )
def generate_token(self, user_id): def generate_token(self, user_id):
# urlsafe variant uses _ and - so use . as the separator and replace macaroon = pymacaroons.Macaroon(
# all =s with .s so http clients don't quote =s when it is used as location = self.hs.config.server_name,
# query params. identifier = "key",
return (base64.urlsafe_b64encode(user_id).replace('=', '.') + '.' + key = self.hs.config.macaroon_secret_key)
stringutils.random_string(18)) macaroon.add_first_party_caveat("gen = 1")
macaroon.add_first_party_caveat("user_id = %s" % (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_user_id(self): def _generate_user_id(self):
return "-" + stringutils.random_string(18) return "-" + stringutils.random_string(18)

View File

@ -33,6 +33,7 @@ REQUIREMENTS = {
"ujson": ["ujson"], "ujson": ["ujson"],
"blist": ["blist"], "blist": ["blist"],
"pysaml2": ["saml2"], "pysaml2": ["saml2"],
"pymacaroons-pynacl": ["pymacaroons"],
} }
CONDITIONAL_REQUIREMENTS = { CONDITIONAL_REQUIREMENTS = {
"web_client": { "web_client": {

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.register import RegistrationHandler
from tests import unittest
from tests.utils import setup_test_homeserver
from twisted.internet import defer
class RegisterHandlers(object):
def __init__(self, hs):
self.registration_handler = RegistrationHandler(hs)
class RegisterTestCase(unittest.TestCase):
@defer.inlineCallbacks
def setUp(self):
self.hs = yield setup_test_homeserver(handlers=None)
self.hs.handlers = RegisterHandlers(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.registration_handler.generate_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.registration_handler.generate_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

@ -44,6 +44,8 @@ def setup_test_homeserver(name="test", datastore=None, config=None, **kargs):
config.signing_key = [MockKey()] config.signing_key = [MockKey()]
config.event_cache_size = 1 config.event_cache_size = 1
config.disable_registration = False config.disable_registration = False
config.macaroon_secret_key = "not even a little secret"
config.server_name = "server.under.test"
if "clock" not in kargs: if "clock" not in kargs:
kargs["clock"] = MockClock() kargs["clock"] = MockClock()