diff --git a/docs/password_auth_providers.rst b/docs/password_auth_providers.rst
index ca05a76617..2dbebcd72c 100644
--- a/docs/password_auth_providers.rst
+++ b/docs/password_auth_providers.rst
@@ -30,22 +30,55 @@ Password auth provider classes must provide the following methods:
and a ``synapse.handlers.auth._AccountHandler`` object which allows the
password provider to check if accounts exist and/or create new ones.
-``someprovider.check_password``\(*user_id*, *password*)
-
- This is the method that actually does the work. It is passed a qualified
- ``@localpart:domain`` user id, and the password provided by the user.
-
- The method should return a Twisted ``Deferred`` object, which resolves to
- ``True`` if authentication is successful, and ``False`` if not.
-
Optional methods
----------------
-Password provider classes may optionally provide the following methods.
+Password auth provider classes may optionally provide the following methods.
-*class* ``SomeProvider.get_db_schema_files()``
+*class* ``SomeProvider.get_db_schema_files``\()
This method, if implemented, should return an Iterable of ``(name,
stream)`` pairs of database schema files. Each file is applied in turn at
initialisation, and a record is then made in the database so that it is
not re-applied on the next start.
+
+``someprovider.get_supported_login_types``\()
+
+ This method, if implemented, should return a ``dict`` mapping from a login
+ type identifier (such as ``m.login.password``) to an iterable giving the
+ fields which must be provided by the user in the submission to the
+ ``/login`` api. These fields are passed in the ``login_dict`` dictionary
+ to ``check_auth``.
+
+ For example, if a password auth provider wants to implement a custom login
+ type of ``com.example.custom_login``, where the client is expected to pass
+ the fields ``secret1`` and ``secret2``, the provider should implement this
+ method and return the following dict::
+
+ {"com.example.custom_login": ("secret1", "secret2")}
+
+``someprovider.check_auth``\(*username*, *login_type*, *login_dict*)
+
+ This method is the one that does the real work. If implemented, it will be
+ called for each login attempt where the login type matches one of the keys
+ returned by ``get_supported_login_types``.
+
+ It is passed the (possibly UNqualified) ``user`` provided by the client,
+ the login type, and a dictionary of login secrets passed by the client.
+
+ The method should return a Twisted ``Deferred`` object, which resolves to
+ the canonical ``@localpart:domain`` user id if authentication is successful,
+ and ``None`` if not.
+
+``someprovider.check_password``\(*user_id*, *password*)
+
+ This method provides a simpler interface than ``get_supported_login_types``
+ and ``check_auth`` for password auth providers that just want to provide a
+ mechanism for validating ``m.login.password`` logins.
+
+ Iif implemented, it will be called to check logins with an
+ ``m.login.password`` login type. It is passed a qualified
+ ``@localpart:domain`` user id, and the password provided by the user.
+
+ The method should return a Twisted ``Deferred`` object, which resolves to
+ ``True`` if authentication is successful, and ``False`` if not.
diff --git a/synapse/federation/transport/client.py b/synapse/federation/transport/client.py
index d25ae1b282..ed41dfc7ee 100644
--- a/synapse/federation/transport/client.py
+++ b/synapse/federation/transport/client.py
@@ -531,9 +531,9 @@ class TransportLayerClient(object):
ignore_backoff=True,
)
- def add_room_to_group(self, destination, group_id, requester_user_id, room_id,
- content):
- """Add a room to a group
+ def update_room_group_association(self, destination, group_id, requester_user_id,
+ room_id, content):
+ """Add or update an association between room and group
"""
path = PREFIX + "/groups/%s/room/%s" % (group_id, room_id,)
@@ -545,7 +545,8 @@ class TransportLayerClient(object):
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
"""
path = PREFIX + "/groups/%s/room/%s" % (group_id, room_id,)
diff --git a/synapse/federation/transport/server.py b/synapse/federation/transport/server.py
index 8f3c14c303..ded6d4edc9 100644
--- a/synapse/federation/transport/server.py
+++ b/synapse/federation/transport/server.py
@@ -684,7 +684,7 @@ class FederationGroupsAddRoomsServlet(BaseFederationServlet):
if get_domain_from_id(requester_user_id) != 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
)
@@ -696,7 +696,7 @@ class FederationGroupsAddRoomsServlet(BaseFederationServlet):
if get_domain_from_id(requester_user_id) != 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,
)
diff --git a/synapse/groups/groups_server.py b/synapse/groups/groups_server.py
index dedc9cc7fd..e21ac8e49e 100644
--- a/synapse/groups/groups_server.py
+++ b/synapse/groups/groups_server.py
@@ -531,8 +531,9 @@ class GroupsServerHandler(object):
})
@defer.inlineCallbacks
- def add_room_to_group(self, group_id, requester_user_id, room_id, content):
- """Add room to group
+ def update_room_group_association(self, group_id, requester_user_id, room_id,
+ content):
+ """Add or update an association between room and group
"""
RoomID.from_string(room_id) # Ensure valid room id
@@ -542,19 +543,21 @@ class GroupsServerHandler(object):
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.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
"""
yield self.check_group_is_ours(
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({})
diff --git a/synapse/handlers/auth.py b/synapse/handlers/auth.py
index 12c50f32f2..0e5be98daa 100644
--- a/synapse/handlers/auth.py
+++ b/synapse/handlers/auth.py
@@ -75,13 +75,17 @@ class AuthHandler(BaseHandler):
logger.info("Extra password_providers: %r", self.password_providers)
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._password_enabled = hs.config.password_enabled
login_types = set()
if self._password_enabled:
login_types.add(LoginType.PASSWORD)
+ for provider in self.password_providers:
+ if hasattr(provider, "get_supported_login_types"):
+ login_types.update(
+ provider.get_supported_login_types().keys()
+ )
self._supported_login_types = frozenset(login_types)
@defer.inlineCallbacks
@@ -406,8 +410,7 @@ class AuthHandler(BaseHandler):
return self.sessions[session_id]
@defer.inlineCallbacks
- def get_access_token_for_user_id(self, user_id, device_id=None,
- initial_display_name=None):
+ def get_access_token_for_user_id(self, user_id, device_id=None):
"""
Creates a new access token for the user with the given user ID.
@@ -421,13 +424,10 @@ class AuthHandler(BaseHandler):
device_id (str|None): the device ID to associate with the tokens.
None to leave the tokens unassociated with a device (deprecated:
we should always have a device ID)
- initial_display_name (str): display name to associate with the
- device if it needs re-registering
Returns:
The access token for the user's session.
Raises:
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)
access_token = yield self.issue_access_token(user_id, device_id)
@@ -437,9 +437,11 @@ class AuthHandler(BaseHandler):
# really don't want is active access_tokens without a record of the
# device, so we double-check it here.
if device_id is not None:
- yield self.device_handler.check_device_registered(
- user_id, device_id, initial_display_name
- )
+ try:
+ 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)
@@ -504,14 +506,14 @@ class AuthHandler(BaseHandler):
return self._supported_login_types
@defer.inlineCallbacks
- def validate_login(self, user_id, login_submission):
+ def validate_login(self, username, login_submission):
"""Authenticates the user for the /login API
Also used by the user-interactive auth flow to validate
m.login.password auth types.
Args:
- user_id (str): user_id supplied by the user
+ username (str): username supplied by the user
login_submission (dict): the whole of the login submission
(including 'type' and other relevant fields)
Returns:
@@ -522,32 +524,81 @@ class AuthHandler(BaseHandler):
LoginError if there was an authentication problem.
"""
- if not user_id.startswith('@'):
- user_id = UserID(
- user_id, self.hs.hostname
+ if username.startswith('@'):
+ qualified_user_id = username
+ else:
+ qualified_user_id = UserID(
+ username, self.hs.hostname
).to_string()
login_type = login_submission.get("type")
+ known_login_type = False
- if login_type != LoginType.PASSWORD:
- raise SynapseError(400, "Bad login type.")
- if not self._password_enabled:
- raise SynapseError(400, "Password login has been disabled.")
- if "password" not in login_submission:
- raise SynapseError(400, "Missing parameter: password")
+ # special case to check for "password" for the check_password interface
+ # for the auth providers
+ password = login_submission.get("password")
+ if login_type == LoginType.PASSWORD:
+ if not self._password_enabled:
+ raise SynapseError(400, "Password login has been disabled.")
+ if not password:
+ raise SynapseError(400, "Missing parameter: password")
- password = login_submission["password"]
for provider in self.password_providers:
- is_valid = yield provider.check_password(user_id, password)
- if is_valid:
- defer.returnValue(user_id)
+ if (hasattr(provider, "check_password")
+ and login_type == LoginType.PASSWORD):
+ known_login_type = True
+ is_valid = yield provider.check_password(
+ qualified_user_id, password,
+ )
+ if is_valid:
+ defer.returnValue(qualified_user_id)
+
+ if (not hasattr(provider, "get_supported_login_types")
+ or not hasattr(provider, "check_auth")):
+ # this password provider doesn't understand custom login types
+ continue
+
+ supported_login_types = provider.get_supported_login_types()
+ if login_type not in supported_login_types:
+ # this password provider doesn't understand this login type
+ continue
+
+ known_login_type = True
+ login_fields = supported_login_types[login_type]
+
+ missing_fields = []
+ login_dict = {}
+ for f in login_fields:
+ if f not in login_submission:
+ missing_fields.append(f)
+ else:
+ login_dict[f] = login_submission[f]
+ if missing_fields:
+ raise SynapseError(
+ 400, "Missing parameters for login type %s: %s" % (
+ login_type,
+ missing_fields,
+ ),
+ )
- canonical_user_id = yield self._check_local_password(
- user_id, password,
- )
+ returned_user_id = yield provider.check_auth(
+ username, login_type, login_dict,
+ )
+ if returned_user_id:
+ defer.returnValue(returned_user_id)
+
+ if login_type == LoginType.PASSWORD:
+ known_login_type = True
- if canonical_user_id:
- defer.returnValue(canonical_user_id)
+ canonical_user_id = yield self._check_local_password(
+ qualified_user_id, password,
+ )
+
+ if canonical_user_id:
+ defer.returnValue(canonical_user_id)
+
+ if not known_login_type:
+ raise SynapseError(400, "Unknown login type %s" % login_type)
# unknown username or invalid password. We raise a 403 here, but note
# that if we're doing user-interactive login, it turns all LoginErrors
@@ -608,14 +659,59 @@ class AuthHandler(BaseHandler):
if e.code == 404:
raise SynapseError(404, "Unknown user", Codes.NOT_FOUND)
raise e
- yield self.store.user_delete_access_tokens(
- user_id, except_access_token_id
+ yield self.delete_access_tokens_for_user(
+ user_id, except_token_id=except_access_token_id,
)
yield self.hs.get_pusherpool().remove_pushers_by_user(
user_id, except_access_token_id
)
@defer.inlineCallbacks
+ def deactivate_account(self, user_id):
+ """Deactivate a user's account
+
+ Args:
+ user_id (str): ID of user to be deactivated
+
+ Returns:
+ Deferred
+ """
+ # FIXME: Theoretically there is a race here wherein user resets
+ # password using threepid.
+ yield self.delete_access_tokens_for_user(user_id)
+ yield self.store.user_delete_threepids(user_id)
+ yield self.store.user_set_password_hash(user_id, None)
+
+ def delete_access_token(self, access_token):
+ """Invalidate a single access token
+
+ Args:
+ access_token (str): access token to be deleted
+
+ Returns:
+ Deferred
+ """
+ return self.store.delete_access_token(access_token)
+
+ def delete_access_tokens_for_user(self, user_id, except_token_id=None,
+ device_id=None):
+ """Invalidate access tokens belonging to a user
+
+ Args:
+ user_id (str): ID of user the tokens belong to
+ except_token_id (str|None): access_token ID which should *not* be
+ deleted
+ device_id (str|None): ID of device the tokens are associated with.
+ If None, tokens associated with any device (or no device) will
+ be deleted
+ Returns:
+ Deferred
+ """
+ return self.store.user_delete_access_tokens(
+ user_id, except_token_id=except_token_id, device_id=device_id,
+ )
+
+ @defer.inlineCallbacks
def add_threepid(self, user_id, medium, address, validated_at):
# 'Canonicalise' email addresses down to lower case.
# We've now moving towards the Home Server being the entity that
@@ -732,11 +828,31 @@ class _AccountHandler(object):
self._check_user_exists = check_user_exists
self._store = hs.get_datastore()
+ def get_qualified_user_id(self, username):
+ """Qualify a user id, if necessary
+
+ Takes a user id provided by the user and adds the @ and :domain to
+ qualify it, if necessary
+
+ Args:
+ username (str): provided user id
+
+ Returns:
+ str: qualified @user:id
+ """
+ if username.startswith('@'):
+ return username
+ return UserID(username, self.hs.hostname).to_string()
+
def check_user_exists(self, user_id):
- """Check if user exissts.
+ """Check if user exists.
+
+ Args:
+ user_id (str): Complete @user:id
Returns:
- Deferred(bool)
+ Deferred[str|None]: Canonical (case-corrected) user_id, or None
+ if the user is not registered.
"""
return self._check_user_exists(user_id)
diff --git a/synapse/handlers/device.py b/synapse/handlers/device.py
index dac4b3f4e0..579d8477ba 100644
--- a/synapse/handlers/device.py
+++ b/synapse/handlers/device.py
@@ -34,6 +34,7 @@ class DeviceHandler(BaseHandler):
self.hs = hs
self.state = hs.get_state_handler()
+ self._auth_handler = hs.get_auth_handler()
self.federation_sender = hs.get_federation_sender()
self.federation = hs.get_replication_layer()
@@ -159,9 +160,8 @@ class DeviceHandler(BaseHandler):
else:
raise
- yield self.store.user_delete_access_tokens(
+ yield self._auth_handler.delete_access_tokens_for_user(
user_id, device_id=device_id,
- delete_refresh_tokens=True,
)
yield self.store.delete_e2e_keys_by_device(
@@ -194,9 +194,8 @@ class DeviceHandler(BaseHandler):
# Delete access tokens and e2e keys for each device. Not optimised as it is not
# considered as part of a critical path.
for device_id in device_ids:
- yield self.store.user_delete_access_tokens(
+ yield self._auth_handler.delete_access_tokens_for_user(
user_id, device_id=device_id,
- delete_refresh_tokens=True,
)
yield self.store.delete_e2e_keys_by_device(
user_id=user_id, device_id=device_id
diff --git a/synapse/handlers/groups_local.py b/synapse/handlers/groups_local.py
index 6699d0888f..dabc2a3fbb 100644
--- a/synapse/handlers/groups_local.py
+++ b/synapse/handlers/groups_local.py
@@ -70,8 +70,8 @@ class GroupsLocalHandler(object):
get_invited_users_in_group = _create_rerouter("get_invited_users_in_group")
- add_room_to_group = _create_rerouter("add_room_to_group")
- remove_room_from_group = _create_rerouter("remove_room_from_group")
+ update_room_group_association = _create_rerouter("update_room_group_association")
+ delete_room_group_association = _create_rerouter("delete_room_group_association")
update_group_summary_room = _create_rerouter("update_group_summary_room")
delete_group_summary_room = _create_rerouter("delete_group_summary_room")
diff --git a/synapse/handlers/register.py b/synapse/handlers/register.py
index 49dc33c147..f6e7e58563 100644
--- a/synapse/handlers/register.py
+++ b/synapse/handlers/register.py
@@ -36,6 +36,7 @@ class RegistrationHandler(BaseHandler):
super(RegistrationHandler, self).__init__(hs)
self.auth = hs.get_auth()
+ self._auth_handler = hs.get_auth_handler()
self.profile_handler = hs.get_profile_handler()
self.captcha_client = CaptchaServerHttpClient(hs)
@@ -416,7 +417,7 @@ class RegistrationHandler(BaseHandler):
create_profile_with_localpart=user.localpart,
)
else:
- yield self.store.user_delete_access_tokens(user_id=user_id)
+ yield self._auth_handler.delete_access_tokens_for_user(user_id)
yield self.store.add_access_token_to_user(user_id=user_id, token=token)
if displayname is not None:
diff --git a/synapse/rest/client/v1/admin.py b/synapse/rest/client/v1/admin.py
index 465b25033d..1197158fdc 100644
--- a/synapse/rest/client/v1/admin.py
+++ b/synapse/rest/client/v1/admin.py
@@ -137,7 +137,7 @@ class DeactivateAccountRestServlet(ClientV1RestServlet):
PATTERNS = client_path_patterns("/admin/deactivate/(?P<target_user_id>[^/]*)")
def __init__(self, hs):
- self.store = hs.get_datastore()
+ self._auth_handler = hs.get_auth_handler()
super(DeactivateAccountRestServlet, self).__init__(hs)
@defer.inlineCallbacks
@@ -149,12 +149,7 @@ class DeactivateAccountRestServlet(ClientV1RestServlet):
if not is_admin:
raise AuthError(403, "You are not a server admin")
- # FIXME: Theoretically there is a race here wherein user resets password
- # using threepid.
- yield self.store.user_delete_access_tokens(target_user_id)
- yield self.store.user_delete_threepids(target_user_id)
- yield self.store.user_set_password_hash(target_user_id, None)
-
+ yield self._auth_handler.deactivate_account(target_user_id)
defer.returnValue((200, {}))
diff --git a/synapse/rest/client/v1/login.py b/synapse/rest/client/v1/login.py
index d24590011b..d25a68e753 100644
--- a/synapse/rest/client/v1/login.py
+++ b/synapse/rest/client/v1/login.py
@@ -166,6 +166,16 @@ class LoginRestServlet(ClientV1RestServlet):
Returns:
(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)
if "identifier" not in login_submission:
@@ -219,7 +229,6 @@ class LoginRestServlet(ClientV1RestServlet):
)
access_token = yield auth_handler.get_access_token_for_user_id(
canonical_user_id, device_id,
- login_submission.get("initial_device_display_name"),
)
result = {
@@ -241,7 +250,6 @@ class LoginRestServlet(ClientV1RestServlet):
device_id = yield self._register_device(user_id, login_submission)
access_token = yield auth_handler.get_access_token_for_user_id(
user_id, device_id,
- login_submission.get("initial_device_display_name"),
)
result = {
"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(
registered_user_id, device_id,
- login_submission.get("initial_device_display_name"),
)
result = {
diff --git a/synapse/rest/client/v1/logout.py b/synapse/rest/client/v1/logout.py
index 1358d0acab..6add754782 100644
--- a/synapse/rest/client/v1/logout.py
+++ b/synapse/rest/client/v1/logout.py
@@ -30,7 +30,7 @@ class LogoutRestServlet(ClientV1RestServlet):
def __init__(self, hs):
super(LogoutRestServlet, self).__init__(hs)
- self.store = hs.get_datastore()
+ self._auth_handler = hs.get_auth_handler()
def on_OPTIONS(self, request):
return (200, {})
@@ -38,7 +38,7 @@ class LogoutRestServlet(ClientV1RestServlet):
@defer.inlineCallbacks
def on_POST(self, request):
access_token = get_access_token_from_request(request)
- yield self.store.delete_access_token(access_token)
+ yield self._auth_handler.delete_access_token(access_token)
defer.returnValue((200, {}))
@@ -47,8 +47,8 @@ class LogoutAllRestServlet(ClientV1RestServlet):
def __init__(self, hs):
super(LogoutAllRestServlet, self).__init__(hs)
- self.store = hs.get_datastore()
self.auth = hs.get_auth()
+ self._auth_handler = hs.get_auth_handler()
def on_OPTIONS(self, request):
return (200, {})
@@ -57,7 +57,7 @@ class LogoutAllRestServlet(ClientV1RestServlet):
def on_POST(self, request):
requester = yield self.auth.get_user_by_req(request)
user_id = requester.user.to_string()
- yield self.store.user_delete_access_tokens(user_id)
+ yield self._auth_handler.delete_access_tokens_for_user(user_id)
defer.returnValue((200, {}))
diff --git a/synapse/rest/client/v2_alpha/account.py b/synapse/rest/client/v2_alpha/account.py
index 1a0d57a04a..3062e04c59 100644
--- a/synapse/rest/client/v2_alpha/account.py
+++ b/synapse/rest/client/v2_alpha/account.py
@@ -162,7 +162,6 @@ class DeactivateAccountRestServlet(RestServlet):
def __init__(self, hs):
self.hs = hs
- self.store = hs.get_datastore()
self.auth = hs.get_auth()
self.auth_handler = hs.get_auth_handler()
super(DeactivateAccountRestServlet, self).__init__()
@@ -180,7 +179,9 @@ class DeactivateAccountRestServlet(RestServlet):
# allow ASes to dectivate their own users
if requester and requester.app_service:
- yield self._deactivate_account(requester.user.to_string())
+ yield self.auth_handler.deactivate_account(
+ requester.user.to_string()
+ )
defer.returnValue((200, {}))
authed, result, params, _ = yield self.auth_handler.check_auth([
@@ -205,17 +206,9 @@ class DeactivateAccountRestServlet(RestServlet):
logger.error("Auth succeeded but no known type!", result.keys())
raise SynapseError(500, "", Codes.UNKNOWN)
- yield self._deactivate_account(user_id)
+ yield self.auth_handler.deactivate_account(user_id)
defer.returnValue((200, {}))
- @defer.inlineCallbacks
- def _deactivate_account(self, user_id):
- # FIXME: Theoretically there is a race here wherein user resets
- # password using threepid.
- yield self.store.user_delete_access_tokens(user_id)
- yield self.store.user_delete_threepids(user_id)
- yield self.store.user_set_password_hash(user_id, None)
-
class EmailThreepidRequestTokenRestServlet(RestServlet):
PATTERNS = client_v2_patterns("/account/3pid/email/requestToken$")
diff --git a/synapse/rest/client/v2_alpha/groups.py b/synapse/rest/client/v2_alpha/groups.py
index c97885cfc7..792608cd48 100644
--- a/synapse/rest/client/v2_alpha/groups.py
+++ b/synapse/rest/client/v2_alpha/groups.py
@@ -451,7 +451,7 @@ class GroupAdminRoomsServlet(RestServlet):
requester_user_id = requester.user.to_string()
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,
)
@@ -462,7 +462,7 @@ class GroupAdminRoomsServlet(RestServlet):
requester = yield self.auth.get_user_by_req(request)
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,
)
diff --git a/synapse/rest/client/v2_alpha/register.py b/synapse/rest/client/v2_alpha/register.py
index d9a8cdbbb5..a077146c89 100644
--- a/synapse/rest/client/v2_alpha/register.py
+++ b/synapse/rest/client/v2_alpha/register.py
@@ -566,7 +566,6 @@ class RegisterRestServlet(RestServlet):
access_token = (
yield self.auth_handler.get_access_token_for_user_id(
user_id, device_id=device_id,
- initial_display_name=params.get("initial_device_display_name")
)
)
diff --git a/synapse/storage/group_server.py b/synapse/storage/group_server.py
index 8c4ad0a9a9..f6924e1a32 100644
--- a/synapse/storage/group_server.py
+++ b/synapse/storage/group_server.py
@@ -846,19 +846,25 @@ class GroupServerStore(SQLBaseStore):
)
return self.runInteraction("remove_user_from_group", _remove_user_from_group_txn)
- def add_room_to_group(self, group_id, room_id, is_public):
- return self._simple_insert(
+ def update_room_group_association(self, group_id, room_id, is_public):
+ return self._simple_upsert(
table="group_rooms",
- values={
+ keyvalues={
"group_id": group_id,
"room_id": room_id,
+ },
+ values={
"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 _remove_room_from_group_txn(txn):
+ def delete_room_group_association(self, group_id, room_id):
+ def _delete_room_group_association_txn(txn):
self._simple_delete_txn(
txn,
table="group_rooms",
@@ -877,7 +883,7 @@ class GroupServerStore(SQLBaseStore):
},
)
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):
diff --git a/synapse/storage/registration.py b/synapse/storage/registration.py
index 20acd58fcf..65ddefda92 100644
--- a/synapse/storage/registration.py
+++ b/synapse/storage/registration.py
@@ -36,12 +36,15 @@ class RegistrationStore(background_updates.BackgroundUpdateStore):
columns=["user_id", "device_id"],
)
- self.register_background_index_update(
- "refresh_tokens_device_index",
- index_name="refresh_tokens_device_id",
- table="refresh_tokens",
- columns=["user_id", "device_id"],
- )
+ # we no longer use refresh tokens, but it's possible that some people
+ # might have a background update queued to build this index. Just
+ # clear the background update.
+ @defer.inlineCallbacks
+ 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
def add_access_token_to_user(self, user_id, token, device_id=None):
@@ -177,9 +180,11 @@ class RegistrationStore(background_updates.BackgroundUpdateStore):
)
if create_profile_with_localpart:
+ # set a default displayname serverside to avoid ugly race
+ # between auto-joins and clients trying to set displaynames
txn.execute(
- "INSERT INTO profiles(user_id) VALUES (?)",
- (create_profile_with_localpart,)
+ "INSERT INTO profiles(user_id, displayname) VALUES (?,?)",
+ (create_profile_with_localpart, create_profile_with_localpart)
)
self._invalidate_cache_and_stream(
@@ -238,10 +243,9 @@ class RegistrationStore(background_updates.BackgroundUpdateStore):
@defer.inlineCallbacks
def user_delete_access_tokens(self, user_id, except_token_id=None,
- device_id=None,
- delete_refresh_tokens=False):
+ device_id=None):
"""
- Invalidate access/refresh tokens belonging to a user
+ Invalidate access tokens belonging to a user
Args:
user_id (str): ID of user the tokens belong to
@@ -250,8 +254,6 @@ class RegistrationStore(background_updates.BackgroundUpdateStore):
device_id (str|None): ID of device the tokens are associated with.
If None, tokens associated with any device (or no device) will
be deleted
- delete_refresh_tokens (bool): True to delete refresh tokens as
- well as access tokens.
Returns:
defer.Deferred:
"""
@@ -262,13 +264,6 @@ class RegistrationStore(background_updates.BackgroundUpdateStore):
if device_id is not None:
keyvalues["device_id"] = device_id
- if delete_refresh_tokens:
- self._simple_delete_txn(
- txn,
- table="refresh_tokens",
- keyvalues=keyvalues,
- )
-
items = keyvalues.items()
where_clause = " AND ".join(k + " = ?" for k, _ in items)
values = [v for _, v in items]
diff --git a/synapse/storage/schema/delta/23/refresh_tokens.sql b/synapse/storage/schema/delta/23/refresh_tokens.sql
deleted file mode 100644
index 34db0cf12b..0000000000
--- a/synapse/storage/schema/delta/23/refresh_tokens.sql
+++ /dev/null
@@ -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)
-);
diff --git a/synapse/storage/schema/delta/33/refreshtoken_device_index.sql b/synapse/storage/schema/delta/33/refreshtoken_device_index.sql
deleted file mode 100644
index bb225dafbf..0000000000
--- a/synapse/storage/schema/delta/33/refreshtoken_device_index.sql
+++ /dev/null
@@ -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', '{}');
diff --git a/synapse/storage/schema/delta/33/refreshtoken_device.sql b/synapse/storage/schema/delta/46/drop_refresh_tokens.sql
index 290bd6da86..68c48a89a9 100644
--- a/synapse/storage/schema/delta/33/refreshtoken_device.sql
+++ b/synapse/storage/schema/delta/46/drop_refresh_tokens.sql
@@ -1,4 +1,4 @@
-/* Copyright 2016 OpenMarket Ltd
+/* Copyright 2017 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,4 +13,5 @@
* 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;
diff --git a/tests/storage/test_registration.py b/tests/storage/test_registration.py
index 316ecdb32d..7c7b164ee6 100644
--- a/tests/storage/test_registration.py
+++ b/tests/storage/test_registration.py
@@ -86,7 +86,8 @@ class RegistrationStoreTestCase(unittest.TestCase):
# now delete some
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
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"])
# now delete the rest
- yield self.store.user_delete_access_tokens(
- self.user_id, delete_refresh_tokens=True)
+ yield self.store.user_delete_access_tokens(self.user_id)
user = yield self.store.get_user_by_access_token(self.tokens[0])
self.assertIsNone(user,
|