Add device_id support to /login

Add a 'devices' table to the storage, as well as a 'device_id' column to
refresh_tokens.

Allow the client to pass a device_id, and initial_device_display_name, to
/login. If login is successful, then register the device in the devices table
if it wasn't known already. If no device_id was supplied, make one up.

Associate the device_id with the access token and refresh token, so that we can
get at it again later. Ensure that the device_id is copied from the refresh
token to the access_token when the token is refreshed.
This commit is contained in:
Richard van der Hoff 2016-07-15 13:19:07 +01:00
parent 93efcb8526
commit f863a52cea
12 changed files with 354 additions and 31 deletions

View file

@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
# Copyright 2016 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.
from twisted.internet import defer
from synapse.handlers.device import DeviceHandler
from tests import unittest
from tests.utils import setup_test_homeserver
class DeviceHandlers(object):
def __init__(self, hs):
self.device_handler = DeviceHandler(hs)
class DeviceTestCase(unittest.TestCase):
@defer.inlineCallbacks
def setUp(self):
self.hs = yield setup_test_homeserver(handlers=None)
self.hs.handlers = handlers = DeviceHandlers(self.hs)
self.handler = handlers.device_handler
@defer.inlineCallbacks
def test_device_is_created_if_doesnt_exist(self):
res = yield self.handler.check_device_registered(
user_id="boris",
device_id="fco",
initial_device_display_name="display name"
)
self.assertEqual(res, "fco")
dev = yield self.handler.store.get_device("boris", "fco")
self.assertEqual(dev["display_name"], "display name")
@defer.inlineCallbacks
def test_device_is_preserved_if_exists(self):
res1 = yield self.handler.check_device_registered(
user_id="boris",
device_id="fco",
initial_device_display_name="display name"
)
self.assertEqual(res1, "fco")
res2 = yield self.handler.check_device_registered(
user_id="boris",
device_id="fco",
initial_device_display_name="new display name"
)
self.assertEqual(res2, "fco")
dev = yield self.handler.store.get_device("boris", "fco")
self.assertEqual(dev["display_name"], "display name")
@defer.inlineCallbacks
def test_device_id_is_made_up_if_unspecified(self):
device_id = yield self.handler.check_device_registered(
user_id="theresa",
device_id=None,
initial_device_display_name="display"
)
dev = yield self.handler.store.get_device("theresa", device_id)
self.assertEqual(dev["display_name"], "display")

View file

@ -38,6 +38,7 @@ class RegistrationStoreTestCase(unittest.TestCase):
"BcDeFgHiJkLmNoPqRsTuVwXyZa"
]
self.pwhash = "{xx1}123456789"
self.device_id = "akgjhdjklgshg"
@defer.inlineCallbacks
def test_register(self):
@ -64,13 +65,15 @@ class RegistrationStoreTestCase(unittest.TestCase):
@defer.inlineCallbacks
def test_add_tokens(self):
yield self.store.register(self.user_id, self.tokens[0], self.pwhash)
yield self.store.add_access_token_to_user(self.user_id, self.tokens[1])
yield self.store.add_access_token_to_user(self.user_id, self.tokens[1],
self.device_id)
result = yield self.store.get_user_by_access_token(self.tokens[1])
self.assertDictContainsSubset(
{
"name": self.user_id,
"device_id": self.device_id,
},
result
)
@ -80,20 +83,24 @@ class RegistrationStoreTestCase(unittest.TestCase):
@defer.inlineCallbacks
def test_exchange_refresh_token_valid(self):
uid = stringutils.random_string(32)
device_id = stringutils.random_string(16)
generator = TokenGenerator()
last_token = generator.generate(uid)
self.db_pool.runQuery(
"INSERT INTO refresh_tokens(user_id, token) VALUES(?,?)",
(uid, last_token,))
"INSERT INTO refresh_tokens(user_id, token, device_id) "
"VALUES(?,?,?)",
(uid, last_token, device_id))
(found_user_id, refresh_token) = yield self.store.exchange_refresh_token(
last_token, generator.generate)
(found_user_id, refresh_token, device_id) = \
yield self.store.exchange_refresh_token(last_token,
generator.generate)
self.assertEqual(uid, found_user_id)
rows = yield self.db_pool.runQuery(
"SELECT token FROM refresh_tokens WHERE user_id = ?", (uid, ))
self.assertEqual([(refresh_token,)], rows)
"SELECT token, device_id FROM refresh_tokens WHERE user_id = ?",
(uid, ))
self.assertEqual([(refresh_token, device_id)], rows)
# We issued token 1, then exchanged it for token 2
expected_refresh_token = u"%s-%d" % (uid, 2,)
self.assertEqual(expected_refresh_token, refresh_token)