Add support for registering with a threepid to the HS (get credentials from the client and check them against an ID server).

This commit is contained in:
David Baker 2014-09-03 18:22:27 +01:00
parent ddc16d8642
commit d6ecbbdf0a
3 changed files with 100 additions and 9 deletions

View file

@ -20,9 +20,13 @@ from synapse.types import UserID
from synapse.api.errors import SynapseError, RegistrationError from synapse.api.errors import SynapseError, RegistrationError
from ._base import BaseHandler from ._base import BaseHandler
import synapse.util.stringutils as stringutils import synapse.util.stringutils as stringutils
from synapse.http.client import PlainHttpClient
import base64 import base64
import bcrypt import bcrypt
import logging
logger = logging.getLogger(__name__)
class RegistrationHandler(BaseHandler): class RegistrationHandler(BaseHandler):
@ -34,7 +38,7 @@ class RegistrationHandler(BaseHandler):
self.distributor.declare("registered_user") self.distributor.declare("registered_user")
@defer.inlineCallbacks @defer.inlineCallbacks
def register(self, localpart=None, password=None): def register(self, localpart=None, password=None, threepidCreds=None):
"""Registers a new client on the server. """Registers a new client on the server.
Args: Args:
@ -47,6 +51,20 @@ class RegistrationHandler(BaseHandler):
Raises: Raises:
RegistrationError if there was a problem registering. RegistrationError if there was a problem registering.
""" """
if threepidCreds:
for c in threepidCreds:
logger.info("validating theeepidcred sid %s on id server %s", c['sid'], c['idServer'])
try:
threepid = yield self._threepid_from_creds(c)
except:
logger.err()
raise RegistrationError(400, "Couldn't validate 3pid")
if not threepid:
raise RegistrationError(400, "Couldn't validate 3pid")
logger.info("got threepid medium %s address %s", threepid['medium'], threepid['address'])
password_hash = None password_hash = None
if password: if password:
password_hash = bcrypt.hashpw(password, bcrypt.gensalt()) password_hash = bcrypt.hashpw(password, bcrypt.gensalt())
@ -61,7 +79,6 @@ class RegistrationHandler(BaseHandler):
password_hash=password_hash) password_hash=password_hash)
self.distributor.fire("registered_user", user) self.distributor.fire("registered_user", user)
defer.returnValue((user_id, token))
else: else:
# autogen a random user ID # autogen a random user ID
attempts = 0 attempts = 0
@ -80,7 +97,6 @@ class RegistrationHandler(BaseHandler):
password_hash=password_hash) password_hash=password_hash)
self.distributor.fire("registered_user", user) self.distributor.fire("registered_user", user)
defer.returnValue((user_id, token))
except SynapseError: except SynapseError:
# if user id is taken, just generate another # if user id is taken, just generate another
user_id = None user_id = None
@ -90,6 +106,15 @@ class RegistrationHandler(BaseHandler):
raise RegistrationError( raise RegistrationError(
500, "Cannot generate user ID.") 500, "Cannot generate user ID.")
# Now we have a matrix ID, bind it to the threepids we were given
if threepidCreds:
for c in threepidCreds:
# XXX: This should be a deferred list, shouldn't it?
yield self._bind_threepid(c, user_id)
defer.returnValue((user_id, token))
def _generate_token(self, user_id): def _generate_token(self, user_id):
# urlsafe variant uses _ and - so use . as the separator and replace # 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 # all =s with .s so http clients don't quote =s when it is used as
@ -99,3 +124,34 @@ class RegistrationHandler(BaseHandler):
def _generate_user_id(self): def _generate_user_id(self):
return "-" + stringutils.random_string(18) return "-" + stringutils.random_string(18)
@defer.inlineCallbacks
def _threepid_from_creds(self, creds):
httpCli = PlainHttpClient(self.hs)
# XXX: make this configurable!
trustedIdServers = [ 'matrix.org:8090' ]
if not creds['idServer'] in trustedIdServers:
logger.warn('%s is not a trusted ID server: rejecting 3pid credentials', creds['idServer'])
defer.returnValue(None)
data = yield httpCli.get_json(
creds['idServer'],
"/_matrix/identity/api/v1/3pid/getValidated3pid",
{ 'sid': creds['sid'], 'clientSecret': creds['clientSecret'] }
)
if 'medium' in data:
defer.returnValue(data)
defer.returnValue(None)
@defer.inlineCallbacks
def _bind_threepid(self, creds, mxid):
httpCli = PlainHttpClient(self.hs)
data = yield httpCli.post_urlencoded_get_json(
creds['idServer'],
"/_matrix/identity/api/v1/3pid/bind",
{ 'sid': creds['sid'], 'clientSecret': creds['clientSecret'], 'mxid':mxid }
)
defer.returnValue(data)

View file

@ -15,7 +15,7 @@
from twisted.internet import defer, reactor from twisted.internet import defer, reactor
from twisted.web.client import _AgentBase, _URI, readBody from twisted.web.client import _AgentBase, _URI, readBody, FileBodyProducer
from twisted.web.http_headers import Headers from twisted.web.http_headers import Headers
from synapse.http.endpoint import matrix_endpoint from synapse.http.endpoint import matrix_endpoint
@ -25,6 +25,8 @@ from syutil.jsonutil import encode_canonical_json
from synapse.api.errors import CodeMessageException from synapse.api.errors import CodeMessageException
from StringIO import StringIO
import json import json
import logging import logging
import urllib import urllib
@ -155,6 +157,26 @@ class TwistedHttpClient(HttpClient):
defer.returnValue(json.loads(body)) defer.returnValue(json.loads(body))
@defer.inlineCallbacks
def post_urlencoded_get_json(self, destination, path, args={}):
if destination in _destination_mappings:
destination = _destination_mappings[destination]
logger.debug("post_urlencoded_get_json args: %s", args)
query_bytes = urllib.urlencode(args, True)
response = yield self._create_request(
destination.encode("ascii"),
"POST",
path.encode("ascii"),
producer=FileBodyProducer(StringIO(urllib.urlencode(args))),
headers_dict={"Content-Type": ["application/x-www-form-urlencoded"]}
)
body = yield readBody(response)
defer.returnValue(json.loads(body))
@defer.inlineCallbacks @defer.inlineCallbacks
def _create_request(self, destination, method, path_bytes, param_bytes=b"", def _create_request(self, destination, method, path_bytes, param_bytes=b"",
query_bytes=b"", producer=None, headers_dict={}): query_bytes=b"", producer=None, headers_dict={}):
@ -178,10 +200,7 @@ class TwistedHttpClient(HttpClient):
retries_left = 5 retries_left = 5
# TODO: setup and pass in an ssl_context to enable TLS # TODO: setup and pass in an ssl_context to enable TLS
endpoint = matrix_endpoint( endpoint = self._getEndpoint(reactor, destination);
reactor, destination, timeout=10,
ssl_context_factory=self.hs.tls_context_factory
)
while True: while True:
try: try:
@ -223,6 +242,17 @@ class TwistedHttpClient(HttpClient):
defer.returnValue(response) defer.returnValue(response)
def _getEndpoint(self, reactor, destination):
return matrix_endpoint(
reactor, destination, timeout=10,
ssl_context_factory=self.hs.tls_context_factory
)
class PlainHttpClient(TwistedHttpClient):
def _getEndpoint(self, reactor, destination):
return matrix_endpoint(reactor, destination, timeout=10)
def _print_ex(e): def _print_ex(e):
if hasattr(e, "reasons") and e.reasons: if hasattr(e, "reasons") and e.reasons:

View file

@ -47,10 +47,15 @@ class RegisterRestServlet(RestServlet):
except KeyError: except KeyError:
pass # user_id is optional pass # user_id is optional
threepidCreds = None
if 'threepidCreds' in register_json:
threepidCreds = register_json['threepidCreds']
handler = self.handlers.registration_handler handler = self.handlers.registration_handler
(user_id, token) = yield handler.register( (user_id, token) = yield handler.register(
localpart=desired_user_id, localpart=desired_user_id,
password=password) password=password,
threepidCreds=threepidCreds)
result = { result = {
"user_id": user_id, "user_id": user_id,