Merge remote-tracking branch 'origin/develop' into rav/refactor_accesstoken_delete

This commit is contained in:
David Baker 2017-11-01 16:20:19 +00:00
commit 4f0488b307
15 changed files with 65 additions and 98 deletions

View File

@ -531,9 +531,9 @@ class TransportLayerClient(object):
ignore_backoff=True, ignore_backoff=True,
) )
def add_room_to_group(self, destination, group_id, requester_user_id, room_id, def update_room_group_association(self, destination, group_id, requester_user_id,
content): room_id, content):
"""Add a room to a group """Add or update an association between room and group
""" """
path = PREFIX + "/groups/%s/room/%s" % (group_id, room_id,) path = PREFIX + "/groups/%s/room/%s" % (group_id, room_id,)
@ -545,7 +545,8 @@ class TransportLayerClient(object):
ignore_backoff=True, ignore_backoff=True,
) )
def remove_room_from_group(self, destination, group_id, requester_user_id, room_id): def delete_room_group_association(self, destination, group_id, requester_user_id,
room_id):
"""Remove a room from a group """Remove a room from a group
""" """
path = PREFIX + "/groups/%s/room/%s" % (group_id, room_id,) path = PREFIX + "/groups/%s/room/%s" % (group_id, room_id,)

View File

@ -684,7 +684,7 @@ class FederationGroupsAddRoomsServlet(BaseFederationServlet):
if get_domain_from_id(requester_user_id) != origin: if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin") raise SynapseError(403, "requester_user_id doesn't match origin")
new_content = yield self.handler.add_room_to_group( new_content = yield self.handler.update_room_group_association(
group_id, requester_user_id, room_id, content group_id, requester_user_id, room_id, content
) )
@ -696,7 +696,7 @@ class FederationGroupsAddRoomsServlet(BaseFederationServlet):
if get_domain_from_id(requester_user_id) != origin: if get_domain_from_id(requester_user_id) != origin:
raise SynapseError(403, "requester_user_id doesn't match origin") raise SynapseError(403, "requester_user_id doesn't match origin")
new_content = yield self.handler.remove_room_from_group( new_content = yield self.handler.delete_room_group_association(
group_id, requester_user_id, room_id, group_id, requester_user_id, room_id,
) )

View File

@ -531,8 +531,9 @@ class GroupsServerHandler(object):
}) })
@defer.inlineCallbacks @defer.inlineCallbacks
def add_room_to_group(self, group_id, requester_user_id, room_id, content): def update_room_group_association(self, group_id, requester_user_id, room_id,
"""Add room to group content):
"""Add or update an association between room and group
""" """
RoomID.from_string(room_id) # Ensure valid room id RoomID.from_string(room_id) # Ensure valid room id
@ -542,19 +543,21 @@ class GroupsServerHandler(object):
is_public = _parse_visibility_from_contents(content) is_public = _parse_visibility_from_contents(content)
yield self.store.add_room_to_group(group_id, room_id, is_public=is_public) yield self.store.update_room_group_association(
group_id, room_id, is_public=is_public
)
defer.returnValue({}) defer.returnValue({})
@defer.inlineCallbacks @defer.inlineCallbacks
def remove_room_from_group(self, group_id, requester_user_id, room_id): def delete_room_group_association(self, group_id, requester_user_id, room_id):
"""Remove room from group """Remove room from group
""" """
yield self.check_group_is_ours( yield self.check_group_is_ours(
group_id, requester_user_id, and_exists=True, and_is_admin=requester_user_id group_id, requester_user_id, and_exists=True, and_is_admin=requester_user_id
) )
yield self.store.remove_room_from_group(group_id, room_id) yield self.store.delete_room_group_association(group_id, room_id)
defer.returnValue({}) defer.returnValue({})

View File

@ -75,7 +75,6 @@ class AuthHandler(BaseHandler):
logger.info("Extra password_providers: %r", self.password_providers) logger.info("Extra password_providers: %r", self.password_providers)
self.hs = hs # FIXME better possibility to access registrationHandler later? self.hs = hs # FIXME better possibility to access registrationHandler later?
self.device_handler = hs.get_device_handler()
self.macaroon_gen = hs.get_macaroon_generator() self.macaroon_gen = hs.get_macaroon_generator()
self._password_enabled = hs.config.password_enabled self._password_enabled = hs.config.password_enabled
@ -406,8 +405,7 @@ class AuthHandler(BaseHandler):
return self.sessions[session_id] return self.sessions[session_id]
@defer.inlineCallbacks @defer.inlineCallbacks
def get_access_token_for_user_id(self, user_id, device_id=None, def get_access_token_for_user_id(self, user_id, device_id=None):
initial_display_name=None):
""" """
Creates a new access token for the user with the given user ID. Creates a new access token for the user with the given user ID.
@ -421,13 +419,10 @@ class AuthHandler(BaseHandler):
device_id (str|None): the device ID to associate with the tokens. device_id (str|None): the device ID to associate with the tokens.
None to leave the tokens unassociated with a device (deprecated: None to leave the tokens unassociated with a device (deprecated:
we should always have a device ID) we should always have a device ID)
initial_display_name (str): display name to associate with the
device if it needs re-registering
Returns: Returns:
The access token for the user's session. The access token for the user's session.
Raises: Raises:
StoreError if there was a problem storing the token. StoreError if there was a problem storing the token.
LoginError if there was an authentication problem.
""" """
logger.info("Logging in user %s on device %s", user_id, device_id) logger.info("Logging in user %s on device %s", user_id, device_id)
access_token = yield self.issue_access_token(user_id, device_id) access_token = yield self.issue_access_token(user_id, device_id)
@ -437,9 +432,11 @@ class AuthHandler(BaseHandler):
# really don't want is active access_tokens without a record of the # really don't want is active access_tokens without a record of the
# device, so we double-check it here. # device, so we double-check it here.
if device_id is not None: if device_id is not None:
yield self.device_handler.check_device_registered( try:
user_id, device_id, initial_display_name yield self.store.get_device(user_id, device_id)
) except StoreError:
yield self.store.delete_access_token(access_token)
raise StoreError(400, "Login raced against device deletion")
defer.returnValue(access_token) defer.returnValue(access_token)

View File

@ -162,7 +162,6 @@ class DeviceHandler(BaseHandler):
yield self._auth_handler.delete_access_tokens_for_user( yield self._auth_handler.delete_access_tokens_for_user(
user_id, device_id=device_id, user_id, device_id=device_id,
delete_refresh_tokens=True,
) )
yield self.store.delete_e2e_keys_by_device( yield self.store.delete_e2e_keys_by_device(
@ -197,7 +196,6 @@ class DeviceHandler(BaseHandler):
for device_id in device_ids: for device_id in device_ids:
yield self._auth_handler.delete_access_tokens_for_user( yield self._auth_handler.delete_access_tokens_for_user(
user_id, device_id=device_id, user_id, device_id=device_id,
delete_refresh_tokens=True,
) )
yield self.store.delete_e2e_keys_by_device( yield self.store.delete_e2e_keys_by_device(
user_id=user_id, device_id=device_id user_id=user_id, device_id=device_id

View File

@ -70,8 +70,8 @@ class GroupsLocalHandler(object):
get_invited_users_in_group = _create_rerouter("get_invited_users_in_group") get_invited_users_in_group = _create_rerouter("get_invited_users_in_group")
add_room_to_group = _create_rerouter("add_room_to_group") update_room_group_association = _create_rerouter("update_room_group_association")
remove_room_from_group = _create_rerouter("remove_room_from_group") delete_room_group_association = _create_rerouter("delete_room_group_association")
update_group_summary_room = _create_rerouter("update_group_summary_room") update_group_summary_room = _create_rerouter("update_group_summary_room")
delete_group_summary_room = _create_rerouter("delete_group_summary_room") delete_group_summary_room = _create_rerouter("delete_group_summary_room")

View File

@ -166,6 +166,16 @@ class LoginRestServlet(ClientV1RestServlet):
Returns: Returns:
(int, object): HTTP code/response (int, object): HTTP code/response
""" """
# Log the request we got, but only certain fields to minimise the chance of
# logging someone's password (even if they accidentally put it in the wrong
# field)
logger.info(
"Got login request with identifier: %r, medium: %r, address: %r, user: %r",
login_submission.get('identifier'),
login_submission.get('medium'),
login_submission.get('address'),
login_submission.get('user'),
)
login_submission_legacy_convert(login_submission) login_submission_legacy_convert(login_submission)
if "identifier" not in login_submission: if "identifier" not in login_submission:
@ -219,7 +229,6 @@ class LoginRestServlet(ClientV1RestServlet):
) )
access_token = yield auth_handler.get_access_token_for_user_id( access_token = yield auth_handler.get_access_token_for_user_id(
canonical_user_id, device_id, canonical_user_id, device_id,
login_submission.get("initial_device_display_name"),
) )
result = { result = {
@ -241,7 +250,6 @@ class LoginRestServlet(ClientV1RestServlet):
device_id = yield self._register_device(user_id, login_submission) device_id = yield self._register_device(user_id, login_submission)
access_token = yield auth_handler.get_access_token_for_user_id( access_token = yield auth_handler.get_access_token_for_user_id(
user_id, device_id, user_id, device_id,
login_submission.get("initial_device_display_name"),
) )
result = { result = {
"user_id": user_id, # may have changed "user_id": user_id, # may have changed
@ -284,7 +292,6 @@ class LoginRestServlet(ClientV1RestServlet):
) )
access_token = yield auth_handler.get_access_token_for_user_id( access_token = yield auth_handler.get_access_token_for_user_id(
registered_user_id, device_id, registered_user_id, device_id,
login_submission.get("initial_device_display_name"),
) )
result = { result = {

View File

@ -451,7 +451,7 @@ class GroupAdminRoomsServlet(RestServlet):
requester_user_id = requester.user.to_string() requester_user_id = requester.user.to_string()
content = parse_json_object_from_request(request) content = parse_json_object_from_request(request)
result = yield self.groups_handler.add_room_to_group( result = yield self.groups_handler.update_room_group_association(
group_id, requester_user_id, room_id, content, group_id, requester_user_id, room_id, content,
) )
@ -462,7 +462,7 @@ class GroupAdminRoomsServlet(RestServlet):
requester = yield self.auth.get_user_by_req(request) requester = yield self.auth.get_user_by_req(request)
requester_user_id = requester.user.to_string() requester_user_id = requester.user.to_string()
result = yield self.groups_handler.remove_room_from_group( result = yield self.groups_handler.delete_room_group_association(
group_id, requester_user_id, room_id, group_id, requester_user_id, room_id,
) )

View File

@ -566,7 +566,6 @@ class RegisterRestServlet(RestServlet):
access_token = ( access_token = (
yield self.auth_handler.get_access_token_for_user_id( yield self.auth_handler.get_access_token_for_user_id(
user_id, device_id=device_id, user_id, device_id=device_id,
initial_display_name=params.get("initial_device_display_name")
) )
) )

View File

@ -846,19 +846,25 @@ class GroupServerStore(SQLBaseStore):
) )
return self.runInteraction("remove_user_from_group", _remove_user_from_group_txn) return self.runInteraction("remove_user_from_group", _remove_user_from_group_txn)
def add_room_to_group(self, group_id, room_id, is_public): def update_room_group_association(self, group_id, room_id, is_public):
return self._simple_insert( return self._simple_upsert(
table="group_rooms", table="group_rooms",
values={ keyvalues={
"group_id": group_id, "group_id": group_id,
"room_id": room_id, "room_id": room_id,
},
values={
"is_public": is_public, "is_public": is_public,
}, },
desc="add_room_to_group", insertion_values={
"group_id": group_id,
"room_id": room_id,
},
desc="update_room_group_association",
) )
def remove_room_from_group(self, group_id, room_id): def delete_room_group_association(self, group_id, room_id):
def _remove_room_from_group_txn(txn): def _delete_room_group_association_txn(txn):
self._simple_delete_txn( self._simple_delete_txn(
txn, txn,
table="group_rooms", table="group_rooms",
@ -877,7 +883,7 @@ class GroupServerStore(SQLBaseStore):
}, },
) )
return self.runInteraction( return self.runInteraction(
"remove_room_from_group", _remove_room_from_group_txn, "delete_room_group_association", _delete_room_group_association_txn,
) )
def get_publicised_groups_for_user(self, user_id): def get_publicised_groups_for_user(self, user_id):

View File

@ -36,12 +36,15 @@ class RegistrationStore(background_updates.BackgroundUpdateStore):
columns=["user_id", "device_id"], columns=["user_id", "device_id"],
) )
self.register_background_index_update( # we no longer use refresh tokens, but it's possible that some people
"refresh_tokens_device_index", # might have a background update queued to build this index. Just
index_name="refresh_tokens_device_id", # clear the background update.
table="refresh_tokens", @defer.inlineCallbacks
columns=["user_id", "device_id"], def noop_update(progress, batch_size):
) yield self._end_background_update("refresh_tokens_device_index")
defer.returnValue(1)
self.register_background_update_handler(
"refresh_tokens_device_index", noop_update)
@defer.inlineCallbacks @defer.inlineCallbacks
def add_access_token_to_user(self, user_id, token, device_id=None): def add_access_token_to_user(self, user_id, token, device_id=None):
@ -238,10 +241,9 @@ class RegistrationStore(background_updates.BackgroundUpdateStore):
@defer.inlineCallbacks @defer.inlineCallbacks
def user_delete_access_tokens(self, user_id, except_token_id=None, def user_delete_access_tokens(self, user_id, except_token_id=None,
device_id=None, device_id=None):
delete_refresh_tokens=False):
""" """
Invalidate access/refresh tokens belonging to a user Invalidate access tokens belonging to a user
Args: Args:
user_id (str): ID of user the tokens belong to user_id (str): ID of user the tokens belong to
@ -250,8 +252,6 @@ class RegistrationStore(background_updates.BackgroundUpdateStore):
device_id (str|None): ID of device the tokens are associated with. device_id (str|None): ID of device the tokens are associated with.
If None, tokens associated with any device (or no device) will If None, tokens associated with any device (or no device) will
be deleted be deleted
delete_refresh_tokens (bool): True to delete refresh tokens as
well as access tokens.
Returns: Returns:
defer.Deferred: defer.Deferred:
""" """
@ -262,13 +262,6 @@ class RegistrationStore(background_updates.BackgroundUpdateStore):
if device_id is not None: if device_id is not None:
keyvalues["device_id"] = device_id keyvalues["device_id"] = device_id
if delete_refresh_tokens:
self._simple_delete_txn(
txn,
table="refresh_tokens",
keyvalues=keyvalues,
)
items = keyvalues.items() items = keyvalues.items()
where_clause = " AND ".join(k + " = ?" for k, _ in items) where_clause = " AND ".join(k + " = ?" for k, _ in items)
values = [v for _, v in items] values = [v for _, v in items]

View File

@ -1,21 +0,0 @@
/* Copyright 2015, 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.
*/
CREATE TABLE IF NOT EXISTS refresh_tokens(
id INTEGER PRIMARY KEY,
token TEXT NOT NULL,
user_id TEXT NOT NULL,
UNIQUE (token)
);

View File

@ -1,17 +0,0 @@
/* 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.
*/
INSERT INTO background_updates (update_name, progress_json) VALUES
('refresh_tokens_device_index', '{}');

View File

@ -1,4 +1,4 @@
/* Copyright 2016 OpenMarket Ltd /* Copyright 2017 New Vector Ltd
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -13,4 +13,5 @@
* limitations under the License. * limitations under the License.
*/ */
ALTER TABLE refresh_tokens ADD COLUMN device_id TEXT; /* we no longer use (or create) the refresh_tokens table */
DROP TABLE IF EXISTS refresh_tokens;

View File

@ -86,7 +86,8 @@ class RegistrationStoreTestCase(unittest.TestCase):
# now delete some # now delete some
yield self.store.user_delete_access_tokens( yield self.store.user_delete_access_tokens(
self.user_id, device_id=self.device_id, delete_refresh_tokens=True) self.user_id, device_id=self.device_id,
)
# check they were deleted # check they were deleted
user = yield self.store.get_user_by_access_token(self.tokens[1]) user = yield self.store.get_user_by_access_token(self.tokens[1])
@ -97,8 +98,7 @@ class RegistrationStoreTestCase(unittest.TestCase):
self.assertEqual(self.user_id, user["name"]) self.assertEqual(self.user_id, user["name"])
# now delete the rest # now delete the rest
yield self.store.user_delete_access_tokens( yield self.store.user_delete_access_tokens(self.user_id)
self.user_id, delete_refresh_tokens=True)
user = yield self.store.get_user_by_access_token(self.tokens[0]) user = yield self.store.get_user_by_access_token(self.tokens[0])
self.assertIsNone(user, self.assertIsNone(user,