summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Baker <dbkr@matrix.org>2014-09-03 18:22:27 +0100
committerDavid Baker <dbkr@matrix.org>2014-09-03 18:22:27 +0100
commitd6ecbbdf0ad4fb49d189044ebf73f333fdd323dc (patch)
tree00757748fadc3da07cc74a24b8b1ad38f162ea69
parentMerge branch 'master' into develop (diff)
downloadsynapse-d6ecbbdf0ad4fb49d189044ebf73f333fdd323dc.tar.xz
Add support for registering with a threepid to the HS (get credentials from the client and check them against an ID server).
-rw-r--r--synapse/handlers/register.py62
-rw-r--r--synapse/http/client.py40
-rw-r--r--synapse/rest/register.py7
3 files changed, 100 insertions, 9 deletions
diff --git a/synapse/handlers/register.py b/synapse/handlers/register.py
index 593c603346..d56d2663d7 100644
--- a/synapse/handlers/register.py
+++ b/synapse/handlers/register.py
@@ -20,9 +20,13 @@ from synapse.types import UserID
 from synapse.api.errors import SynapseError, RegistrationError
 from ._base import BaseHandler
 import synapse.util.stringutils as stringutils
+from synapse.http.client import PlainHttpClient
 
 import base64
 import bcrypt
+import logging
+
+logger = logging.getLogger(__name__)
 
 
 class RegistrationHandler(BaseHandler):
@@ -34,7 +38,7 @@ class RegistrationHandler(BaseHandler):
         self.distributor.declare("registered_user")
 
     @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.
 
         Args:
@@ -47,6 +51,20 @@ class RegistrationHandler(BaseHandler):
         Raises:
             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
         if password:
             password_hash = bcrypt.hashpw(password, bcrypt.gensalt())
@@ -61,7 +79,6 @@ class RegistrationHandler(BaseHandler):
                 password_hash=password_hash)
 
             self.distributor.fire("registered_user", user)
-            defer.returnValue((user_id, token))
         else:
             # autogen a random user ID
             attempts = 0
@@ -80,7 +97,6 @@ class RegistrationHandler(BaseHandler):
                         password_hash=password_hash)
 
                     self.distributor.fire("registered_user", user)
-                    defer.returnValue((user_id, token))
                 except SynapseError:
                     # if user id is taken, just generate another
                     user_id = None
@@ -90,6 +106,15 @@ class RegistrationHandler(BaseHandler):
                         raise RegistrationError(
                             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):
         # 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
@@ -99,3 +124,34 @@ class RegistrationHandler(BaseHandler):
 
     def _generate_user_id(self):
         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)
+        
+        
+
diff --git a/synapse/http/client.py b/synapse/http/client.py
index 093bdf0e3f..5a5e40a26e 100644
--- a/synapse/http/client.py
+++ b/synapse/http/client.py
@@ -15,7 +15,7 @@
 
 
 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 synapse.http.endpoint import matrix_endpoint
@@ -25,6 +25,8 @@ from syutil.jsonutil import encode_canonical_json
 
 from synapse.api.errors import CodeMessageException
 
+from StringIO import StringIO
+
 import json
 import logging
 import urllib
@@ -156,6 +158,26 @@ class TwistedHttpClient(HttpClient):
         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
     def _create_request(self, destination, method, path_bytes, param_bytes=b"",
                         query_bytes=b"", producer=None, headers_dict={}):
         """ Creates and sends a request to the given url
@@ -178,10 +200,7 @@ class TwistedHttpClient(HttpClient):
         retries_left = 5
 
         # TODO: setup and pass in an ssl_context to enable TLS
-        endpoint = matrix_endpoint(
-            reactor, destination, timeout=10,
-            ssl_context_factory=self.hs.tls_context_factory
-        )
+        endpoint = self._getEndpoint(reactor, destination);
 
         while True:
             try:
@@ -223,6 +242,17 @@ class TwistedHttpClient(HttpClient):
 
         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):
     if hasattr(e, "reasons") and e.reasons:
diff --git a/synapse/rest/register.py b/synapse/rest/register.py
index f17ec11cf4..27ab7f182b 100644
--- a/synapse/rest/register.py
+++ b/synapse/rest/register.py
@@ -47,10 +47,15 @@ class RegisterRestServlet(RestServlet):
         except KeyError:
             pass  # user_id is optional
 
+        threepidCreds = None
+        if 'threepidCreds' in register_json:
+            threepidCreds = register_json['threepidCreds']
+
         handler = self.handlers.registration_handler
         (user_id, token) = yield handler.register(
             localpart=desired_user_id,
-            password=password)
+            password=password,
+            threepidCreds=threepidCreds)
 
         result = {
             "user_id": user_id,