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:
parent
ddc16d8642
commit
d6ecbbdf0a
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue