summary refs log tree commit diff
diff options
context:
space:
mode:
authorRichard van der Hoff <richard@matrix.org>2016-07-19 18:38:26 +0100
committerRichard van der Hoff <richard@matrix.org>2016-07-20 16:38:27 +0100
commitb97a1356b149f62e5b2c28b09818d74b445cc635 (patch)
tree9b2e858408f706398fc0ab6c2c8b365252f68aad
parentMerge pull request #936 from matrix-org/erikj/log_rss (diff)
downloadsynapse-b97a1356b149f62e5b2c28b09818d74b445cc635.tar.xz
Register a device_id in the /v2/register flow.
This doesn't cover *all* of the registration flows, but it does cover the most
common ones: in particular: shared_secret registration, appservice
registration, and normal user/pass registration.

Pull device_id from the registration parameters. Register the device in the
devices table. Associate the device with the returned access and refresh
tokens. Profit.
-rw-r--r--synapse/rest/client/v2_alpha/register.py54
-rw-r--r--tests/rest/client/v2_alpha/test_register.py13
2 files changed, 49 insertions, 18 deletions
diff --git a/synapse/rest/client/v2_alpha/register.py b/synapse/rest/client/v2_alpha/register.py
index b7e03ea9d1..d401722224 100644
--- a/synapse/rest/client/v2_alpha/register.py
+++ b/synapse/rest/client/v2_alpha/register.py
@@ -93,6 +93,7 @@ class RegisterRestServlet(RestServlet):
         self.auth_handler = hs.get_auth_handler()
         self.registration_handler = hs.get_handlers().registration_handler
         self.identity_handler = hs.get_handlers().identity_handler
+        self.device_handler = hs.get_device_handler()
 
     @defer.inlineCallbacks
     def on_POST(self, request):
@@ -145,7 +146,7 @@ class RegisterRestServlet(RestServlet):
 
             if isinstance(desired_username, basestring):
                 result = yield self._do_appservice_registration(
-                    desired_username, request.args["access_token"][0]
+                    desired_username, request.args["access_token"][0], body
                 )
             defer.returnValue((200, result))  # we throw for non 200 responses
             return
@@ -155,7 +156,7 @@ class RegisterRestServlet(RestServlet):
             # FIXME: Should we really be determining if this is shared secret
             # auth based purely on the 'mac' key?
             result = yield self._do_shared_secret_registration(
-                desired_username, desired_password, body["mac"]
+                desired_username, desired_password, body
             )
             defer.returnValue((200, result))  # we throw for non 200 responses
             return
@@ -236,7 +237,7 @@ class RegisterRestServlet(RestServlet):
             add_email = True
 
         result = yield self._create_registration_details(
-            registered_user_id
+            registered_user_id, body
         )
 
         if add_email and result and LoginType.EMAIL_IDENTITY in result:
@@ -252,14 +253,14 @@ class RegisterRestServlet(RestServlet):
         return 200, {}
 
     @defer.inlineCallbacks
-    def _do_appservice_registration(self, username, as_token):
+    def _do_appservice_registration(self, username, as_token, body):
         user_id = yield self.registration_handler.appservice_register(
             username, as_token
         )
-        defer.returnValue((yield self._create_registration_details(user_id)))
+        defer.returnValue((yield self._create_registration_details(user_id, body)))
 
     @defer.inlineCallbacks
-    def _do_shared_secret_registration(self, username, password, mac):
+    def _do_shared_secret_registration(self, username, password, body):
         if not self.hs.config.registration_shared_secret:
             raise SynapseError(400, "Shared secret registration is not enabled")
 
@@ -267,7 +268,7 @@ class RegisterRestServlet(RestServlet):
 
         # str() because otherwise hmac complains that 'unicode' does not
         # have the buffer interface
-        got_mac = str(mac)
+        got_mac = str(body["mac"])
 
         want_mac = hmac.new(
             key=self.hs.config.registration_shared_secret,
@@ -284,7 +285,7 @@ class RegisterRestServlet(RestServlet):
             localpart=username, password=password, generate_token=False,
         )
 
-        result = yield self._create_registration_details(user_id)
+        result = yield self._create_registration_details(user_id, body)
         defer.returnValue(result)
 
     @defer.inlineCallbacks
@@ -358,35 +359,58 @@ class RegisterRestServlet(RestServlet):
         defer.returnValue()
 
     @defer.inlineCallbacks
-    def _create_registration_details(self, user_id):
+    def _create_registration_details(self, user_id, body):
         """Complete registration of newly-registered user
 
-        Issues access_token and refresh_token, and builds the success response
-        body.
+        Allocates device_id if one was not given; also creates access_token
+        and refresh_token.
 
         Args:
             (str) user_id: full canonical @user:id
-
+            (object) body: dictionary supplied to /register call, from
+               which we pull device_id and initial_device_name
 
         Returns:
             defer.Deferred: (object) dictionary for response from /register
         """
+        device_id = yield self._register_device(user_id, body)
 
         access_token = yield self.auth_handler.issue_access_token(
-            user_id
+            user_id, device_id=device_id
         )
 
         refresh_token = yield self.auth_handler.issue_refresh_token(
-            user_id
+            user_id, device_id=device_id
         )
-
         defer.returnValue({
             "user_id": user_id,
             "access_token": access_token,
             "home_server": self.hs.hostname,
             "refresh_token": refresh_token,
+            "device_id": device_id,
         })
 
+    def _register_device(self, user_id, body):
+        """Register a device for a user.
+
+        This is called after the user's credentials have been validated, but
+        before the access token has been issued.
+
+        Args:
+            (str) user_id: full canonical @user:id
+            (object) body: dictionary supplied to /register call, from
+               which we pull device_id and initial_device_name
+        Returns:
+            defer.Deferred: (str) device_id
+        """
+        # register the user's device
+        device_id = body.get("device_id")
+        initial_display_name = body.get("initial_device_display_name")
+        device_id = self.device_handler.check_device_registered(
+            user_id, device_id, initial_display_name
+        )
+        return device_id
+
     @defer.inlineCallbacks
     def _do_guest_registration(self):
         if not self.hs.config.allow_guest_access:
diff --git a/tests/rest/client/v2_alpha/test_register.py b/tests/rest/client/v2_alpha/test_register.py
index ccbb8776d3..3bd7065e32 100644
--- a/tests/rest/client/v2_alpha/test_register.py
+++ b/tests/rest/client/v2_alpha/test_register.py
@@ -30,6 +30,7 @@ class RegisterRestServletTestCase(unittest.TestCase):
         self.registration_handler = Mock()
         self.identity_handler = Mock()
         self.login_handler = Mock()
+        self.device_handler = Mock()
 
         # do the dance to hook it up to the hs global
         self.handlers = Mock(
@@ -42,6 +43,7 @@ class RegisterRestServletTestCase(unittest.TestCase):
         self.hs.get_auth = Mock(return_value=self.auth)
         self.hs.get_handlers = Mock(return_value=self.handlers)
         self.hs.get_auth_handler = Mock(return_value=self.auth_handler)
+        self.hs.get_device_handler = Mock(return_value=self.device_handler)
         self.hs.config.enable_registration = True
 
         # init the thing we're testing
@@ -107,9 +109,11 @@ class RegisterRestServletTestCase(unittest.TestCase):
     def test_POST_user_valid(self):
         user_id = "@kermit:muppet"
         token = "kermits_access_token"
+        device_id = "frogfone"
         self.request_data = json.dumps({
             "username": "kermit",
-            "password": "monkey"
+            "password": "monkey",
+            "device_id": device_id,
         })
         self.registration_handler.check_username = Mock(return_value=True)
         self.auth_result = (True, None, {
@@ -118,18 +122,21 @@ class RegisterRestServletTestCase(unittest.TestCase):
         }, None)
         self.registration_handler.register = Mock(return_value=(user_id, None))
         self.auth_handler.issue_access_token = Mock(return_value=token)
+        self.device_handler.check_device_registered = \
+            Mock(return_value=device_id)
 
         (code, result) = yield self.servlet.on_POST(self.request)
         self.assertEquals(code, 200)
         det_data = {
             "user_id": user_id,
             "access_token": token,
-            "home_server": self.hs.hostname
+            "home_server": self.hs.hostname,
+            "device_id": device_id,
         }
         self.assertDictContainsSubset(det_data, result)
         self.assertIn("refresh_token", result)
         self.auth_handler.issue_access_token.assert_called_once_with(
-            user_id)
+            user_id, device_id=device_id)
 
     def test_POST_disabled_registration(self):
         self.hs.config.enable_registration = False