From d98660a60daaf1cc8d83cb2d64daa5f20a34139c Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 23 Mar 2015 14:20:28 +0000 Subject: Implement password changing (finally) along with a start on making client/server auth more general. --- synapse/storage/registration.py | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) (limited to 'synapse/storage') diff --git a/synapse/storage/registration.py b/synapse/storage/registration.py index f24154f146..7e60dc3951 100644 --- a/synapse/storage/registration.py +++ b/synapse/storage/registration.py @@ -95,11 +95,36 @@ class RegistrationStore(SQLBaseStore): "get_user_by_id", self.cursor_to_dict, query, user_id ) + def user_set_password_hash(self, user_id, password_hash): + """ + NB. This does *not* evict any cache because the one use for this + removes most of the entries subsequently anyway so it would be + pointless. Use flush_user separately. + """ + return self._simple_update_one('users', { + 'name': user_id + }, { + 'password_hash': password_hash + }) + + def user_delete_access_tokens_apart_from(self, user_id, token_id): + return self._execute( + "delete_access_tokens_apart_from", None, + "DELETE FROM access_tokens WHERE user_id = ? AND id != ?", + user_id, token_id + ) + + @defer.inlineCallbacks + def flush_user(self, user_id): + rows = yield self._execute( + 'user_delete_access_tokens_apart_from', None, + "SELECT token FROM access_tokens WHERE user_id = ?", + user_id + ) + for r in rows: + self.get_user_by_token.invalidate(r) + @cached() - # TODO(paul): Currently there's no code to invalidate this cache. That - # means if/when we ever add internal ways to invalidate access tokens or - # change whether a user is a server admin, those will need to invoke - # store.get_user_by_token.invalidate(token) def get_user_by_token(self, token): """Get a user from the given access token. -- cgit 1.4.1 From d19e79ecc956e5ba7ed6b6fd37e80ec6a737b048 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 24 Mar 2015 15:33:48 +0000 Subject: Make deleting other access tokens when you change your password actually work --- synapse/rest/client/v2_alpha/password.py | 5 ++++- synapse/storage/registration.py | 16 +++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) (limited to 'synapse/storage') diff --git a/synapse/rest/client/v2_alpha/password.py b/synapse/rest/client/v2_alpha/password.py index 1277532110..85954c71cd 100644 --- a/synapse/rest/client/v2_alpha/password.py +++ b/synapse/rest/client/v2_alpha/password.py @@ -65,12 +65,15 @@ class PasswordRestServlet(RestServlet): raise SynapseError(400, "", Codes.MISSING_PARAM) new_password = body['new_password'] - self.login_handler.set_password( + yield self.login_handler.set_password( user_id, new_password, client.token_id ) defer.returnValue((200, {})) + def on_OPTIONS(self, _): + return 200, {} + def register_servlets(hs, http_server): PasswordRestServlet(hs).register(http_server) diff --git a/synapse/storage/registration.py b/synapse/storage/registration.py index 7e60dc3951..0364d10858 100644 --- a/synapse/storage/registration.py +++ b/synapse/storage/registration.py @@ -89,35 +89,41 @@ class RegistrationStore(SQLBaseStore): "VALUES (?,?)", [txn.lastrowid, token]) def get_user_by_id(self, user_id): - query = ("SELECT users.name, users.password_hash FROM users" + query = ("SELECT users.id, users.name, users.password_hash FROM users" " WHERE users.name = ?") return self._execute( "get_user_by_id", self.cursor_to_dict, query, user_id ) + @defer.inlineCallbacks def user_set_password_hash(self, user_id, password_hash): """ NB. This does *not* evict any cache because the one use for this removes most of the entries subsequently anyway so it would be pointless. Use flush_user separately. """ - return self._simple_update_one('users', { + yield self._simple_update_one('users', { 'name': user_id }, { 'password_hash': password_hash }) + @defer.inlineCallbacks def user_delete_access_tokens_apart_from(self, user_id, token_id): - return self._execute( + rows = yield self.get_user_by_id(user_id) + if len(rows) == 0: + raise Exception("No such user!") + + yield self._execute( "delete_access_tokens_apart_from", None, "DELETE FROM access_tokens WHERE user_id = ? AND id != ?", - user_id, token_id + rows[0]['id'], token_id ) @defer.inlineCallbacks def flush_user(self, user_id): rows = yield self._execute( - 'user_delete_access_tokens_apart_from', None, + 'flush_user', None, "SELECT token FROM access_tokens WHERE user_id = ?", user_id ) -- cgit 1.4.1 From c7023f21555a0adf0d8bb5040c817a8198bbf5a8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 24 Mar 2015 17:24:15 +0000 Subject: 1) Pushers are now associated with an access token 2) Change places where we mean unauthenticated to 401, not 403, in C/S v2: hack so it stays as 403 in v1 because web client relies on it. --- synapse/api/auth.py | 43 +++++++++++++++++++-------------- synapse/push/pusherpool.py | 9 ++++--- synapse/rest/client/v1/base.py | 2 +- synapse/rest/client/v1/pusher.py | 3 ++- synapse/server.py | 10 ++++++++ synapse/storage/pusher.py | 3 ++- synapse/storage/registration.py | 2 +- synapse/storage/schema/delta/15/v15.sql | 2 ++ 8 files changed, 48 insertions(+), 26 deletions(-) create mode 100644 synapse/storage/schema/delta/15/v15.sql (limited to 'synapse/storage') diff --git a/synapse/api/auth.py b/synapse/api/auth.py index 64f605b962..d08faf23f1 100644 --- a/synapse/api/auth.py +++ b/synapse/api/auth.py @@ -40,6 +40,7 @@ class Auth(object): self.hs = hs self.store = hs.get_datastore() self.state = hs.get_state_handler() + self.TOKEN_NOT_FOUND_HTTP_STATUS = 401 def check(self, event, auth_events): """ Checks if this event is correctly authed. @@ -373,7 +374,9 @@ class Auth(object): defer.returnValue((user, ClientInfo(device_id, token_id))) except KeyError: - raise AuthError(403, "Missing access token.") + raise AuthError( + self.TOKEN_NOT_FOUND_HTTP_STATUS, "Missing access token." + ) @defer.inlineCallbacks def get_user_by_token(self, token): @@ -387,21 +390,20 @@ class Auth(object): Raises: AuthError if no user by that token exists or the token is invalid. """ - try: - ret = yield self.store.get_user_by_token(token) - if not ret: - raise StoreError(400, "Unknown token") - user_info = { - "admin": bool(ret.get("admin", False)), - "device_id": ret.get("device_id"), - "user": UserID.from_string(ret.get("name")), - "token_id": ret.get("token_id", None), - } + ret = yield self.store.get_user_by_token(token) + if not ret: + raise AuthError( + self.TOKEN_NOT_FOUND_HTTP_STATUS, "Unrecognised access token.", + errcode=Codes.UNKNOWN_TOKEN + ) + user_info = { + "admin": bool(ret.get("admin", False)), + "device_id": ret.get("device_id"), + "user": UserID.from_string(ret.get("name")), + "token_id": ret.get("token_id", None), + } - defer.returnValue(user_info) - except StoreError: - raise AuthError(403, "Unrecognised access token.", - errcode=Codes.UNKNOWN_TOKEN) + defer.returnValue(user_info) @defer.inlineCallbacks def get_appservice_by_req(self, request): @@ -409,11 +411,16 @@ class Auth(object): token = request.args["access_token"][0] service = yield self.store.get_app_service_by_token(token) if not service: - raise AuthError(403, "Unrecognised access token.", - errcode=Codes.UNKNOWN_TOKEN) + raise AuthError( + self.TOKEN_NOT_FOUND_HTTP_STATUS, + "Unrecognised access token.", + errcode=Codes.UNKNOWN_TOKEN + ) defer.returnValue(service) except KeyError: - raise AuthError(403, "Missing access token.") + raise AuthError( + self.TOKEN_NOT_FOUND_HTTP_STATUS, "Missing access token." + ) def is_server_admin(self, user): return self.store.is_server_admin(user) diff --git a/synapse/push/pusherpool.py b/synapse/push/pusherpool.py index 90babd7224..f75eebf8bf 100644 --- a/synapse/push/pusherpool.py +++ b/synapse/push/pusherpool.py @@ -57,7 +57,7 @@ class PusherPool: self._start_pushers(pushers) @defer.inlineCallbacks - def add_pusher(self, user_name, profile_tag, kind, app_id, + def add_pusher(self, user_name, access_token, profile_tag, kind, app_id, app_display_name, device_display_name, pushkey, lang, data): # we try to create the pusher just to validate the config: it # will then get pulled out of the database, @@ -79,17 +79,18 @@ class PusherPool: "failing_since": None }) yield self._add_pusher_to_store( - user_name, profile_tag, kind, app_id, + user_name, access_token, profile_tag, kind, app_id, app_display_name, device_display_name, pushkey, lang, data ) @defer.inlineCallbacks - def _add_pusher_to_store(self, user_name, profile_tag, kind, app_id, - app_display_name, device_display_name, + def _add_pusher_to_store(self, user_name, access_token, profile_tag, kind, + app_id, app_display_name, device_display_name, pushkey, lang, data): yield self.store.add_pusher( user_name=user_name, + access_token=access_token, profile_tag=profile_tag, kind=kind, app_id=app_id, diff --git a/synapse/rest/client/v1/base.py b/synapse/rest/client/v1/base.py index 72332bdb10..504a5e432f 100644 --- a/synapse/rest/client/v1/base.py +++ b/synapse/rest/client/v1/base.py @@ -48,5 +48,5 @@ class ClientV1RestServlet(RestServlet): self.hs = hs self.handlers = hs.get_handlers() self.builder_factory = hs.get_event_builder_factory() - self.auth = hs.get_auth() + self.auth = hs.get_v1auth() self.txns = HttpTransactionStore() diff --git a/synapse/rest/client/v1/pusher.py b/synapse/rest/client/v1/pusher.py index 6045e86f34..87e89c9305 100644 --- a/synapse/rest/client/v1/pusher.py +++ b/synapse/rest/client/v1/pusher.py @@ -27,7 +27,7 @@ class PusherRestServlet(ClientV1RestServlet): @defer.inlineCallbacks def on_POST(self, request): - user, _ = yield self.auth.get_user_by_req(request) + user, client = yield self.auth.get_user_by_req(request) content = _parse_json(request) @@ -54,6 +54,7 @@ class PusherRestServlet(ClientV1RestServlet): try: yield pusher_pool.add_pusher( user_name=user.to_string(), + access_token=client.token_id, profile_tag=content['profile_tag'], kind=content['kind'], app_id=content['app_id'], diff --git a/synapse/server.py b/synapse/server.py index c7772244ba..4c4f6ca239 100644 --- a/synapse/server.py +++ b/synapse/server.py @@ -65,6 +65,7 @@ class BaseHomeServer(object): 'replication_layer', 'datastore', 'handlers', + 'v1auth', 'auth', 'rest_servlet_factory', 'state_handler', @@ -182,6 +183,15 @@ class HomeServer(BaseHomeServer): def build_auth(self): return Auth(self) + def build_v1auth(self): + orf = Auth(self) + # Matrix spec makes no reference to what HTTP status code is returned, + # but the V1 API uses 403 where it means 401, and the webclient + # relies on this behaviour, so V1 gets its own copy of the auth + # with backwards compat behaviour. + orf.TOKEN_NOT_FOUND_HTTP_STATUS = 403 + return orf + def build_state_handler(self): return StateHandler(self) diff --git a/synapse/storage/pusher.py b/synapse/storage/pusher.py index 000502b4ff..1ef8e06ac6 100644 --- a/synapse/storage/pusher.py +++ b/synapse/storage/pusher.py @@ -95,7 +95,7 @@ class PusherStore(SQLBaseStore): defer.returnValue(ret) @defer.inlineCallbacks - def add_pusher(self, user_name, profile_tag, kind, app_id, + def add_pusher(self, user_name, access_token, profile_tag, kind, app_id, app_display_name, device_display_name, pushkey, pushkey_ts, lang, data): try: @@ -107,6 +107,7 @@ class PusherStore(SQLBaseStore): ), dict( user_name=user_name, + access_token=access_token, kind=kind, profile_tag=profile_tag, app_display_name=app_display_name, diff --git a/synapse/storage/registration.py b/synapse/storage/registration.py index 0364d10858..f61d8fdb6a 100644 --- a/synapse/storage/registration.py +++ b/synapse/storage/registration.py @@ -174,4 +174,4 @@ class RegistrationStore(SQLBaseStore): if rows: return rows[0] - raise StoreError(404, "Token not found.") + return None diff --git a/synapse/storage/schema/delta/15/v15.sql b/synapse/storage/schema/delta/15/v15.sql new file mode 100644 index 0000000000..fc3e436877 --- /dev/null +++ b/synapse/storage/schema/delta/15/v15.sql @@ -0,0 +1,2 @@ +ALTER TABLE pushers ADD COLUMN access_token INTEGER DEFAULT NULL; + -- cgit 1.4.1 From c1a256cc4c82ce746eae8e719b9aa2344fe66177 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 25 Mar 2015 19:06:22 +0000 Subject: Allow multiple pushers for a single app ID & pushkey, honouring the 'append' flag in the API. --- synapse/push/__init__.py | 10 +++++-- synapse/push/pusherpool.py | 47 ++++++++++++++++++++++++++------- synapse/rest/client/v1/pusher.py | 13 ++++++++- synapse/storage/pusher.py | 27 ++++++++++--------- synapse/storage/schema/delta/15/v15.sql | 27 +++++++++++++++++-- 5 files changed, 96 insertions(+), 28 deletions(-) (limited to 'synapse/storage') diff --git a/synapse/push/__init__.py b/synapse/push/__init__.py index 0727f772a5..5575c847f9 100644 --- a/synapse/push/__init__.py +++ b/synapse/push/__init__.py @@ -253,7 +253,8 @@ class Pusher(object): self.user_name, config, timeout=0) self.last_token = chunk['end'] self.store.update_pusher_last_token( - self.app_id, self.pushkey, self.last_token) + self.app_id, self.pushkey, self.user_name, self.last_token + ) logger.info("Pusher %s for user %s starting from token %s", self.pushkey, self.user_name, self.last_token) @@ -314,7 +315,7 @@ class Pusher(object): pk ) yield self.hs.get_pusherpool().remove_pusher( - self.app_id, pk + self.app_id, pk, self.user_name ) if not self.alive: @@ -326,6 +327,7 @@ class Pusher(object): self.store.update_pusher_last_token_and_success( self.app_id, self.pushkey, + self.user_name, self.last_token, self.clock.time_msec() ) @@ -334,6 +336,7 @@ class Pusher(object): self.store.update_pusher_failing_since( self.app_id, self.pushkey, + self.user_name, self.failing_since) else: if not self.failing_since: @@ -341,6 +344,7 @@ class Pusher(object): self.store.update_pusher_failing_since( self.app_id, self.pushkey, + self.user_name, self.failing_since ) @@ -358,6 +362,7 @@ class Pusher(object): self.store.update_pusher_last_token( self.app_id, self.pushkey, + self.user_name, self.last_token ) @@ -365,6 +370,7 @@ class Pusher(object): self.store.update_pusher_failing_since( self.app_id, self.pushkey, + self.user_name, self.failing_since ) else: diff --git a/synapse/push/pusherpool.py b/synapse/push/pusherpool.py index f75eebf8bf..cda072839c 100644 --- a/synapse/push/pusherpool.py +++ b/synapse/push/pusherpool.py @@ -84,6 +84,21 @@ class PusherPool: pushkey, lang, data ) + @defer.inlineCallbacks + def remove_pushers_by_app_id_and_pushkey_not_user(self, app_id, pushkey, + not_user_id): + to_remove = yield self.store.get_pushers_by_app_id_and_pushkey( + app_id, pushkey + ) + for p in to_remove: + if p['user_name'] != not_user_id: + logger.info( + "Removing pusher for app id %s, pushkey %s, user %s", + app_id, pushkey, p['user_name'] + ) + self.remove_pusher(p['app_id'], p['pushkey'], p['user_name']) + + @defer.inlineCallbacks def _add_pusher_to_store(self, user_name, access_token, profile_tag, kind, app_id, app_display_name, device_display_name, @@ -101,7 +116,7 @@ class PusherPool: lang=lang, data=encode_canonical_json(data).decode("UTF-8"), ) - self._refresh_pusher((app_id, pushkey)) + self._refresh_pusher(app_id, pushkey, user_name) def _create_pusher(self, pusherdict): if pusherdict['kind'] == 'http': @@ -126,30 +141,42 @@ class PusherPool: ) @defer.inlineCallbacks - def _refresh_pusher(self, app_id_pushkey): - p = yield self.store.get_pushers_by_app_id_and_pushkey( - app_id_pushkey + def _refresh_pusher(self, app_id, pushkey, user_name): + resultlist = yield self.store.get_pushers_by_app_id_and_pushkey( + app_id, pushkey ) - p['data'] = json.loads(p['data']) + p = None + for r in resultlist: + if r['user_name'] == user_name: + p = r - self._start_pushers([p]) + if p: + p['data'] = json.loads(p['data']) + + self._start_pushers([p]) def _start_pushers(self, pushers): logger.info("Starting %d pushers", len(pushers)) for pusherdict in pushers: p = self._create_pusher(pusherdict) if p: - fullid = "%s:%s" % (pusherdict['app_id'], pusherdict['pushkey']) + fullid = "%s:%s:%s" % ( + pusherdict['app_id'], + pusherdict['pushkey'], + pusherdict['user_name'] + ) if fullid in self.pushers: self.pushers[fullid].stop() self.pushers[fullid] = p p.start() @defer.inlineCallbacks - def remove_pusher(self, app_id, pushkey): - fullid = "%s:%s" % (app_id, pushkey) + def remove_pusher(self, app_id, pushkey, user_name): + fullid = "%s:%s:%s" % (app_id, pushkey, user_name) if fullid in self.pushers: logger.info("Stopping pusher %s", fullid) self.pushers[fullid].stop() del self.pushers[fullid] - yield self.store.delete_pusher_by_app_id_pushkey(app_id, pushkey) + yield self.store.delete_pusher_by_app_id_pushkey_user_name( + app_id, pushkey, user_name + ) diff --git a/synapse/rest/client/v1/pusher.py b/synapse/rest/client/v1/pusher.py index 87e89c9305..c83287c028 100644 --- a/synapse/rest/client/v1/pusher.py +++ b/synapse/rest/client/v1/pusher.py @@ -37,7 +37,7 @@ class PusherRestServlet(ClientV1RestServlet): and 'kind' in content and content['kind'] is None): yield pusher_pool.remove_pusher( - content['app_id'], content['pushkey'] + content['app_id'], content['pushkey'], user_name=user.to_string() ) defer.returnValue((200, {})) @@ -51,6 +51,17 @@ class PusherRestServlet(ClientV1RestServlet): raise SynapseError(400, "Missing parameters: "+','.join(missing), errcode=Codes.MISSING_PARAM) + append = False + if 'append' in content: + append = content['append'] + + if not append: + yield pusher_pool.remove_pushers_by_app_id_and_pushkey_not_user( + app_id=content['app_id'], + pushkey=content['pushkey'], + not_user_id=user.to_string() + ) + try: yield pusher_pool.add_pusher( user_name=user.to_string(), diff --git a/synapse/storage/pusher.py b/synapse/storage/pusher.py index 1ef8e06ac6..423878c6a0 100644 --- a/synapse/storage/pusher.py +++ b/synapse/storage/pusher.py @@ -27,7 +27,7 @@ logger = logging.getLogger(__name__) class PusherStore(SQLBaseStore): @defer.inlineCallbacks - def get_pushers_by_app_id_and_pushkey(self, app_id_and_pushkey): + def get_pushers_by_app_id_and_pushkey(self, app_id, pushkey): sql = ( "SELECT id, user_name, kind, profile_tag, app_id," "app_display_name, device_display_name, pushkey, ts, data, " @@ -38,7 +38,7 @@ class PusherStore(SQLBaseStore): rows = yield self._execute( "get_pushers_by_app_id_and_pushkey", None, sql, - app_id_and_pushkey[0], app_id_and_pushkey[1] + app_id, pushkey ) ret = [ @@ -60,7 +60,7 @@ class PusherStore(SQLBaseStore): for r in rows ] - defer.returnValue(ret[0]) + defer.returnValue(ret) @defer.inlineCallbacks def get_all_pushers(self): @@ -104,9 +104,9 @@ class PusherStore(SQLBaseStore): dict( app_id=app_id, pushkey=pushkey, + user_name=user_name, ), dict( - user_name=user_name, access_token=access_token, kind=kind, profile_tag=profile_tag, @@ -123,37 +123,38 @@ class PusherStore(SQLBaseStore): raise StoreError(500, "Problem creating pusher.") @defer.inlineCallbacks - def delete_pusher_by_app_id_pushkey(self, app_id, pushkey): + def delete_pusher_by_app_id_pushkey_user_name(self, app_id, pushkey, user_name): yield self._simple_delete_one( PushersTable.table_name, - {"app_id": app_id, "pushkey": pushkey}, - desc="delete_pusher_by_app_id_pushkey", + {"app_id": app_id, "pushkey": pushkey, 'user_name': user_name}, + desc="delete_pusher_by_app_id_pushkey_user_name", ) @defer.inlineCallbacks - def update_pusher_last_token(self, app_id, pushkey, last_token): + def update_pusher_last_token(self, app_id, pushkey, user_name, last_token): yield self._simple_update_one( PushersTable.table_name, - {'app_id': app_id, 'pushkey': pushkey}, + {'app_id': app_id, 'pushkey': pushkey, 'user_name': user_name}, {'last_token': last_token}, desc="update_pusher_last_token", ) @defer.inlineCallbacks - def update_pusher_last_token_and_success(self, app_id, pushkey, + def update_pusher_last_token_and_success(self, app_id, pushkey, user_name, last_token, last_success): yield self._simple_update_one( PushersTable.table_name, - {'app_id': app_id, 'pushkey': pushkey}, + {'app_id': app_id, 'pushkey': pushkey, 'user_name': user_name}, {'last_token': last_token, 'last_success': last_success}, desc="update_pusher_last_token_and_success", ) @defer.inlineCallbacks - def update_pusher_failing_since(self, app_id, pushkey, failing_since): + def update_pusher_failing_since(self, app_id, pushkey, user_name, + failing_since): yield self._simple_update_one( PushersTable.table_name, - {'app_id': app_id, 'pushkey': pushkey}, + {'app_id': app_id, 'pushkey': pushkey, 'user_name': user_name}, {'failing_since': failing_since}, desc="update_pusher_failing_since", ) diff --git a/synapse/storage/schema/delta/15/v15.sql b/synapse/storage/schema/delta/15/v15.sql index fc3e436877..f5b2a08ca4 100644 --- a/synapse/storage/schema/delta/15/v15.sql +++ b/synapse/storage/schema/delta/15/v15.sql @@ -1,2 +1,25 @@ -ALTER TABLE pushers ADD COLUMN access_token INTEGER DEFAULT NULL; - +-- Drop, copy & recreate pushers table to change unique key +-- Also add access_token column at the same time +CREATE TABLE IF NOT EXISTS pushers2 ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_name TEXT NOT NULL, + access_token INTEGER DEFAULT NULL, + profile_tag varchar(32) NOT NULL, + kind varchar(8) NOT NULL, + app_id varchar(64) NOT NULL, + app_display_name varchar(64) NOT NULL, + device_display_name varchar(128) NOT NULL, + pushkey blob NOT NULL, + ts BIGINT NOT NULL, + lang varchar(8), + data blob, + last_token TEXT, + last_success BIGINT, + failing_since BIGINT, + FOREIGN KEY(user_name) REFERENCES users(name), + UNIQUE (app_id, pushkey, user_name) +); +INSERT INTO pushers2 (id, user_name, profile_tag, kind, app_id, app_display_name, device_display_name, pushkey, ts, lang, data, last_token, last_success, failing_since) + SELECT id, user_name, profile_tag, kind, app_id, app_display_name, device_display_name, pushkey, ts, lang, data, last_token, last_success, failing_since FROM pushers; +DROP TABLE pushers; +ALTER TABLE pushers2 RENAME TO pushers; -- cgit 1.4.1 From a32e876ef43df22cec37aad748c32c0cda30428a Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 26 Mar 2015 13:40:16 +0000 Subject: Delete pushers when changing password --- synapse/handlers/login.py | 3 +++ synapse/push/pusherpool.py | 20 ++++++++++++++++++-- synapse/storage/pusher.py | 45 ++++++++++----------------------------------- 3 files changed, 31 insertions(+), 37 deletions(-) (limited to 'synapse/storage') diff --git a/synapse/handlers/login.py b/synapse/handlers/login.py index 7aff2e69e6..04f6dbb95e 100644 --- a/synapse/handlers/login.py +++ b/synapse/handlers/login.py @@ -70,4 +70,7 @@ class LoginHandler(BaseHandler): yield self.store.user_set_password_hash(user_id, password_hash) yield self.store.user_delete_access_tokens_apart_from(user_id, token_id) + yield self.hs.get_pusherpool().remove_pushers_by_user_access_token( + user_id, token_id + ) yield self.store.flush_user(user_id) diff --git a/synapse/push/pusherpool.py b/synapse/push/pusherpool.py index 46444157c9..0fdd7ea786 100644 --- a/synapse/push/pusherpool.py +++ b/synapse/push/pusherpool.py @@ -71,7 +71,7 @@ class PusherPool: "app_display_name": app_display_name, "device_display_name": device_display_name, "pushkey": pushkey, - "pushkey_ts": self.hs.get_clock().time_msec(), + "ts": self.hs.get_clock().time_msec(), "lang": lang, "data": data, "last_token": None, @@ -98,6 +98,22 @@ class PusherPool: ) self.remove_pusher(p['app_id'], p['pushkey'], p['user_name']) + @defer.inlineCallbacks + def remove_pushers_by_user_access_token(self, user_id, not_access_token_id): + all = yield self.store.get_all_pushers() + logger.info( + "Removing all pushers for user %s except access token %s", + user_id, not_access_token_id + ) + for p in all: + if (p['user_name'] == user_id and + p['access_token'] != not_access_token_id): + logger.info( + "Removing pusher for app id %s, pushkey %s, user %s", + p['app_id'], p['pushkey'], p['user_name'] + ) + self.remove_pusher(p['app_id'], p['pushkey'], p['user_name']) + @defer.inlineCallbacks def _add_pusher_to_store(self, user_name, access_token, profile_tag, kind, app_id, app_display_name, device_display_name, @@ -127,7 +143,7 @@ class PusherPool: app_display_name=pusherdict['app_display_name'], device_display_name=pusherdict['device_display_name'], pushkey=pusherdict['pushkey'], - pushkey_ts=pusherdict['pushkey_ts'], + pushkey_ts=pusherdict['ts'], data=pusherdict['data'], last_token=pusherdict['last_token'], last_success=pusherdict['last_success'], diff --git a/synapse/storage/pusher.py b/synapse/storage/pusher.py index 423878c6a0..1c657beddb 100644 --- a/synapse/storage/pusher.py +++ b/synapse/storage/pusher.py @@ -28,11 +28,9 @@ logger = logging.getLogger(__name__) class PusherStore(SQLBaseStore): @defer.inlineCallbacks def get_pushers_by_app_id_and_pushkey(self, app_id, pushkey): + cols = ",".join(PushersTable.fields) sql = ( - "SELECT id, user_name, kind, profile_tag, app_id," - "app_display_name, device_display_name, pushkey, ts, data, " - "last_token, last_success, failing_since " - "FROM pushers " + "SELECT "+cols+" FROM pushers " "WHERE app_id = ? AND pushkey = ?" ) @@ -43,51 +41,26 @@ class PusherStore(SQLBaseStore): ret = [ { - "id": r[0], - "user_name": r[1], - "kind": r[2], - "profile_tag": r[3], - "app_id": r[4], - "app_display_name": r[5], - "device_display_name": r[6], - "pushkey": r[7], - "pushkey_ts": r[8], - "data": r[9], - "last_token": r[10], - "last_success": r[11], - "failing_since": r[12] + k: r[i] for i, k in enumerate(PushersTable.fields) } for r in rows ] + print ret defer.returnValue(ret) @defer.inlineCallbacks def get_all_pushers(self): + cols = ",".join(PushersTable.fields) sql = ( - "SELECT id, user_name, kind, profile_tag, app_id," - "app_display_name, device_display_name, pushkey, ts, data, " - "last_token, last_success, failing_since " - "FROM pushers" + "SELECT "+cols+" FROM pushers" ) rows = yield self._execute("get_all_pushers", None, sql) ret = [ { - "id": r[0], - "user_name": r[1], - "kind": r[2], - "profile_tag": r[3], - "app_id": r[4], - "app_display_name": r[5], - "device_display_name": r[6], - "pushkey": r[7], - "pushkey_ts": r[8], - "data": r[9], - "last_token": r[10], - "last_success": r[11], - "failing_since": r[12] + k: r[i] for i, k in enumerate(PushersTable.fields) } for r in rows ] @@ -166,13 +139,15 @@ class PushersTable(Table): fields = [ "id", "user_name", + "access_token", "kind", "profile_tag", "app_id", "app_display_name", "device_display_name", "pushkey", - "pushkey_ts", + "ts", + "lang", "data", "last_token", "last_success", -- cgit 1.4.1 From f96ab9d18dcebf995700f096792101a490b3a9b8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 17 Apr 2015 16:44:49 +0100 Subject: make add3pid servlet work --- synapse/handlers/login.py | 7 ++++++ synapse/rest/client/v2_alpha/account.py | 38 ++++++++++++++++++++++++++++++++ synapse/rest/client/v2_alpha/register.py | 38 ++++++++++++++++++++++---------- synapse/storage/registration.py | 11 +++++++++ 4 files changed, 82 insertions(+), 12 deletions(-) (limited to 'synapse/storage') diff --git a/synapse/handlers/login.py b/synapse/handlers/login.py index 04f6dbb95e..5c39356d71 100644 --- a/synapse/handlers/login.py +++ b/synapse/handlers/login.py @@ -74,3 +74,10 @@ class LoginHandler(BaseHandler): user_id, token_id ) yield self.store.flush_user(user_id) + + @defer.inlineCallbacks + def add_threepid(self, user_id, medium, address, validated_at): + yield self.store.user_add_threepid( + user_id, medium, address, validated_at, + self.hs.get_clock().time_msec() + ) \ No newline at end of file diff --git a/synapse/rest/client/v2_alpha/account.py b/synapse/rest/client/v2_alpha/account.py index 750d826f91..6045b016ef 100644 --- a/synapse/rest/client/v2_alpha/account.py +++ b/synapse/rest/client/v2_alpha/account.py @@ -18,6 +18,7 @@ from twisted.internet import defer from synapse.api.constants import LoginType from synapse.api.errors import LoginError, SynapseError, Codes from synapse.http.servlet import RestServlet +from synapse.util.async import run_on_reactor from ._base import client_v2_pattern, parse_json_dict_from_request @@ -39,6 +40,8 @@ class PasswordRestServlet(RestServlet): @defer.inlineCallbacks def on_POST(self, request): + yield run_on_reactor() + body = parse_json_dict_from_request(request) authed, result, params = yield self.auth_handler.check_auth([ @@ -78,16 +81,51 @@ class PasswordRestServlet(RestServlet): class ThreepidRestServlet(RestServlet): PATTERN = client_v2_pattern("/account/3pid") + def __init__(self, hs): + super(ThreepidRestServlet, self).__init__() + self.hs = hs + self.login_handler = hs.get_handlers().login_handler + self.identity_handler = hs.get_handlers().identity_handler + self.auth = hs.get_auth() + @defer.inlineCallbacks def on_POST(self, request): + yield run_on_reactor() + body = parse_json_dict_from_request(request) if 'threePidCreds' not in body: raise SynapseError(400, "Missing param", Codes.MISSING_PARAM) + threePidCreds = body['threePidCreds'] auth_user, client = yield self.auth.get_user_by_req(request) + threepid = yield self.identity_handler.threepid_from_creds(threePidCreds) + if not threepid: + raise SynapseError(400, "Failed to auth 3pid") + + for reqd in ['medium', 'address', 'validatedAt']: + if reqd not in threepid: + logger.warn("Couldn't add 3pid: invalid response from ID sevrer") + raise SynapseError(500, "Invalid response from ID Server") + + yield self.login_handler.add_threepid( + auth_user.to_string(), + threepid['medium'], + threepid['address'], + threepid['validatedAt'], + ) + + if 'bind' in body and body['bind']: + logger.debug("Binding emails %s to %s" % ( + threepid, auth_user.to_string() + )) + yield self.identity_handler.bind_threepid( + threePidCreds, auth_user.to_string() + ) + + defer.returnValue((200, {})) def register_servlets(hs, http_server): diff --git a/synapse/rest/client/v2_alpha/register.py b/synapse/rest/client/v2_alpha/register.py index a5fec45dce..e93897e285 100644 --- a/synapse/rest/client/v2_alpha/register.py +++ b/synapse/rest/client/v2_alpha/register.py @@ -50,6 +50,7 @@ class RegisterRestServlet(RestServlet): self.auth_handler = hs.get_handlers().auth_handler self.registration_handler = hs.get_handlers().registration_handler self.identity_handler = hs.get_handlers().identity_handler + self.login_handler = hs.get_handlers().login_handler @defer.inlineCallbacks def on_POST(self, request): @@ -61,7 +62,6 @@ class RegisterRestServlet(RestServlet): if 'username' in body: desired_username = body['username'] - print "username in body" yield self.registration_handler.check_username(desired_username) is_using_shared_secret = False @@ -118,17 +118,31 @@ class RegisterRestServlet(RestServlet): password=new_password ) - if 'bind_email' in params and params['bind_email']: - logger.info("bind_email specified: binding") - - emailThreepid = result[LoginType.EMAIL_IDENTITY] - threepidCreds = emailThreepid['threepidCreds'] - logger.debug("Binding emails %s to %s" % ( - emailThreepid, user_id - )) - yield self.identity_handler.bind_threepid(threepidCreds, user_id) - else: - logger.info("bind_email not specified: not binding email") + if LoginType.EMAIL_IDENTITY in result: + threepid = result[LoginType.EMAIL_IDENTITY] + + for reqd in ['medium', 'address', 'validatedAt']: + if reqd not in threepid: + logger.info("Can't add incomplete 3pid") + else: + yield self.login_handler.add_threepid( + user_id, + threepid['medium'], + threepid['address'], + threepid['validatedAt'], + ) + + if 'bind_email' in params and params['bind_email']: + logger.info("bind_email specified: binding") + + emailThreepid = result[LoginType.EMAIL_IDENTITY] + threepidCreds = emailThreepid['threepidCreds'] + logger.debug("Binding emails %s to %s" % ( + emailThreepid, user_id + )) + yield self.identity_handler.bind_threepid(threepidCreds, user_id) + else: + logger.info("bind_email not specified: not binding email") result = { "user_id": user_id, diff --git a/synapse/storage/registration.py b/synapse/storage/registration.py index f61d8fdb6a..4bc01f3cc2 100644 --- a/synapse/storage/registration.py +++ b/synapse/storage/registration.py @@ -175,3 +175,14 @@ class RegistrationStore(SQLBaseStore): return rows[0] return None + + @defer.inlineCallbacks + def user_add_threepid(self, user_id, medium, address, validated_at, added_at): + yield self._simple_upsert("user_threepids", { + "user": user_id, + "medium": medium, + "address": address, + }, { + "validated_at": validated_at, + "added_at": added_at, + }) \ No newline at end of file -- cgit 1.4.1 From 4eea5cf6c2a301938466828b02557d8500197bb3 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 17 Apr 2015 16:46:45 +0100 Subject: pep8 --- synapse/handlers/identity.py | 6 +++--- synapse/handlers/login.py | 2 +- synapse/handlers/register.py | 5 +---- synapse/storage/registration.py | 2 +- 4 files changed, 6 insertions(+), 9 deletions(-) (limited to 'synapse/storage') diff --git a/synapse/handlers/identity.py b/synapse/handlers/identity.py index cb5e1e80ac..5c72635915 100644 --- a/synapse/handlers/identity.py +++ b/synapse/handlers/identity.py @@ -42,8 +42,8 @@ class IdentityHandler(BaseHandler): # each request http_client = SimpleHttpClient(self.hs) # XXX: make this configurable! - trustedIdServers = ['matrix.org', 'localhost:8090'] - #trustedIdServers = ['matrix.org'] + # trustedIdServers = ['matrix.org', 'localhost:8090'] + trustedIdServers = ['matrix.org'] if not creds['idServer'] in trustedIdServers: logger.warn('%s is not a trusted ID server: rejecting 3pid ' + 'credentials', creds['idServer']) @@ -86,4 +86,4 @@ class IdentityHandler(BaseHandler): logger.debug("bound threepid %r to %s", creds, mxid) except CodeMessageException as e: data = json.loads(e.msg) - defer.returnValue(data) \ No newline at end of file + defer.returnValue(data) diff --git a/synapse/handlers/login.py b/synapse/handlers/login.py index 5c39356d71..f7f3698340 100644 --- a/synapse/handlers/login.py +++ b/synapse/handlers/login.py @@ -80,4 +80,4 @@ class LoginHandler(BaseHandler): yield self.store.user_add_threepid( user_id, medium, address, validated_at, self.hs.get_clock().time_msec() - ) \ No newline at end of file + ) diff --git a/synapse/handlers/register.py b/synapse/handlers/register.py index d4483c3a1d..7b68585a17 100644 --- a/synapse/handlers/register.py +++ b/synapse/handlers/register.py @@ -18,18 +18,15 @@ from twisted.internet import defer from synapse.types import UserID from synapse.api.errors import ( - AuthError, Codes, SynapseError, RegistrationError, InvalidCaptchaError, - CodeMessageException + AuthError, Codes, SynapseError, RegistrationError, InvalidCaptchaError ) from ._base import BaseHandler import synapse.util.stringutils as stringutils from synapse.util.async import run_on_reactor -from synapse.http.client import SimpleHttpClient from synapse.http.client import CaptchaServerHttpClient import base64 import bcrypt -import json import logging import urllib diff --git a/synapse/storage/registration.py b/synapse/storage/registration.py index 4bc01f3cc2..8f62e5c6f2 100644 --- a/synapse/storage/registration.py +++ b/synapse/storage/registration.py @@ -185,4 +185,4 @@ class RegistrationStore(SQLBaseStore): }, { "validated_at": validated_at, "added_at": added_at, - }) \ No newline at end of file + }) -- cgit 1.4.1 From 117f35ac4ac4f8d344ae1efbc629a3f8bc25f459 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 17 Apr 2015 17:20:18 +0100 Subject: Add endpoint to get threepids from server --- synapse/rest/client/v2_alpha/account.py | 12 ++++++++++++ synapse/storage/registration.py | 11 +++++++++++ 2 files changed, 23 insertions(+) (limited to 'synapse/storage') diff --git a/synapse/rest/client/v2_alpha/account.py b/synapse/rest/client/v2_alpha/account.py index 6045b016ef..5ac3ac0f71 100644 --- a/synapse/rest/client/v2_alpha/account.py +++ b/synapse/rest/client/v2_alpha/account.py @@ -88,6 +88,18 @@ class ThreepidRestServlet(RestServlet): self.identity_handler = hs.get_handlers().identity_handler self.auth = hs.get_auth() + @defer.inlineCallbacks + def on_GET(self, request): + yield run_on_reactor() + + auth_user, _ = yield self.auth.get_user_by_req(request) + + threepids = yield self.hs.get_datastore().user_get_threepids( + auth_user.to_string() + ) + + defer.returnValue((200, {'threepids': threepids})) + @defer.inlineCallbacks def on_POST(self, request): yield run_on_reactor() diff --git a/synapse/storage/registration.py b/synapse/storage/registration.py index 8f62e5c6f2..08d60f0817 100644 --- a/synapse/storage/registration.py +++ b/synapse/storage/registration.py @@ -186,3 +186,14 @@ class RegistrationStore(SQLBaseStore): "validated_at": validated_at, "added_at": added_at, }) + + @defer.inlineCallbacks + def user_get_threepids(self, user_id): + ret = yield self._simple_select_list( + "user_threepids", { + "user": user_id + }, + ['medium', 'address', 'validated_at', 'added_at'], + 'user_get_threepids' + ) + defer.returnValue(ret) \ No newline at end of file -- cgit 1.4.1 From 8db6832db8a8ad1a68ff6781b90f3e2cb1a72fc0 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 17 Apr 2015 19:53:47 +0100 Subject: Password reset, finally. --- synapse/handlers/auth.py | 8 +++++++- synapse/rest/client/v2_alpha/account.py | 21 ++++++++++++++++----- synapse/storage/registration.py | 16 +++++++++++++++- 3 files changed, 38 insertions(+), 7 deletions(-) (limited to 'synapse/storage') diff --git a/synapse/handlers/auth.py b/synapse/handlers/auth.py index 1f927e67ad..7b0ab4829b 100644 --- a/synapse/handlers/auth.py +++ b/synapse/handlers/auth.py @@ -195,12 +195,18 @@ class AuthHandler(BaseHandler): def _check_email_identity(self, authdict, _): yield run_on_reactor() + if 'threepidCreds' not in authdict: + raise LoginError(400, "Missing threepidCreds", Codes.MISSING_PARAM) + threepidCreds = authdict['threepidCreds'] identity_handler = self.hs.get_handlers().identity_handler - logger.debug("Getting validated threepid. threepidcreds: %r" % (threepidCreds,)) + logger.info("Getting validated threepid. threepidcreds: %r" % (threepidCreds,)) threepid = yield identity_handler.threepid_from_creds(threepidCreds) + if not threepid: + raise LoginError(401, "", errcode=Codes.UNAUTHORIZED) + threepid['threepidCreds'] = authdict['threepidCreds'] defer.returnValue(threepid) diff --git a/synapse/rest/client/v2_alpha/account.py b/synapse/rest/client/v2_alpha/account.py index 5ac3ac0f71..e33607b799 100644 --- a/synapse/rest/client/v2_alpha/account.py +++ b/synapse/rest/client/v2_alpha/account.py @@ -45,31 +45,42 @@ class PasswordRestServlet(RestServlet): body = parse_json_dict_from_request(request) authed, result, params = yield self.auth_handler.check_auth([ - [LoginType.PASSWORD] + [LoginType.PASSWORD], + [LoginType.EMAIL_IDENTITY] ], body) if not authed: defer.returnValue((401, result)) - auth_user = None + user_id = None if LoginType.PASSWORD in result: # if using password, they should also be logged in auth_user, client = yield self.auth.get_user_by_req(request) if auth_user.to_string() != result[LoginType.PASSWORD]: raise LoginError(400, "", Codes.UNKNOWN) + user_id = auth_user.to_string() + elif LoginType.EMAIL_IDENTITY in result: + threepid = result[LoginType.EMAIL_IDENTITY] + if 'medium' not in threepid or 'address' not in threepid: + raise SynapseError(500, "Malformed threepid") + # if using email, we must know about the email they're authing with! + threepid_user = yield self.hs.get_datastore().get_user_by_threepid( + threepid['medium'], threepid['address'] + ) + if not threepid_user: + raise SynapseError(404, "Email address not found", Codes.NOT_FOUND) + user_id = threepid_user else: logger.error("Auth succeeded but no known type!", result.keys()) raise SynapseError(500, "", Codes.UNKNOWN) - user_id = auth_user.to_string() - if 'new_password' not in params: raise SynapseError(400, "", Codes.MISSING_PARAM) new_password = params['new_password'] yield self.login_handler.set_password( - user_id, new_password, client.token_id + user_id, new_password, None ) defer.returnValue((200, {})) diff --git a/synapse/storage/registration.py b/synapse/storage/registration.py index 08d60f0817..ab43856023 100644 --- a/synapse/storage/registration.py +++ b/synapse/storage/registration.py @@ -196,4 +196,18 @@ class RegistrationStore(SQLBaseStore): ['medium', 'address', 'validated_at', 'added_at'], 'user_get_threepids' ) - defer.returnValue(ret) \ No newline at end of file + defer.returnValue(ret) + + @defer.inlineCallbacks + def get_user_by_threepid(self, medium, address): + ret = yield self._simple_select_one( + "user_threepids", + { + "medium": medium, + "address": address + }, + ['user'], True, 'get_user_by_threepid' + ) + if ret: + defer.returnValue(ret['user']) + defer.returnValue(None) \ No newline at end of file -- cgit 1.4.1 From 91c8f828e13c71fb4d5124191740b0c9ac8a4bc2 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 17 Apr 2015 19:56:04 +0100 Subject: pep8 --- synapse/storage/registration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'synapse/storage') diff --git a/synapse/storage/registration.py b/synapse/storage/registration.py index ab43856023..f85cbb0d9d 100644 --- a/synapse/storage/registration.py +++ b/synapse/storage/registration.py @@ -210,4 +210,4 @@ class RegistrationStore(SQLBaseStore): ) if ret: defer.returnValue(ret['user']) - defer.returnValue(None) \ No newline at end of file + defer.returnValue(None) -- cgit 1.4.1 From a7b51f4539af90460d47efe0bae2692de285cd26 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 24 Apr 2015 18:11:21 +0100 Subject: Check users in our table aren't on a different domain to the one we're configured with to try & fix SYN-266 --- synapse/app/homeserver.py | 19 +++++++++++++++++++ synapse/storage/registration.py | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+) (limited to 'synapse/storage') diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py index 27e53a9e56..5f6cf4deeb 100755 --- a/synapse/app/homeserver.py +++ b/synapse/app/homeserver.py @@ -26,6 +26,7 @@ from synapse.server import HomeServer from synapse.python_dependencies import check_requirements from twisted.internet import reactor +from twisted.internet import defer from twisted.application import service from twisted.enterprise import adbapi from twisted.web.resource import Resource @@ -241,6 +242,22 @@ class SynapseHomeServer(HomeServer): ) logger.info("Metrics now running on 127.0.0.1 port %d", config.metrics_port) + @defer.inlineCallbacks + def post_startup_check(self): + all_users_native = yield self.get_datastore().all_users_on_domain( + self.hostname + ) + if not all_users_native: + sys.stderr.write( + "\n" + "******************************************************\n" + "Found users in database not native to %s!\n" + "You cannot changed a synapse server_name after it's been configured\n" + "******************************************************\n" + "\n" + ) + reactor.stop() + def get_version_string(): try: @@ -399,6 +416,8 @@ def setup(config_options): hs.get_datastore().start_profiling() hs.get_replication_layer().start_get_pdu_cache() + reactor.callWhenRunning(hs.post_startup_check) + return hs diff --git a/synapse/storage/registration.py b/synapse/storage/registration.py index f24154f146..c2efc3fd32 100644 --- a/synapse/storage/registration.py +++ b/synapse/storage/registration.py @@ -144,3 +144,21 @@ class RegistrationStore(SQLBaseStore): return rows[0] raise StoreError(404, "Token not found.") + + @defer.inlineCallbacks + def all_users_on_domain(self, domain): + res = yield self.runInteraction( + "all_users_on_domain", + self._all_users_on_domain_txn, + domain + ) + defer.returnValue(res) + + def _all_users_on_domain_txn(self, txn, domain): + sql = "SELECT COUNT(*) FROM users WHERE name NOT LIKE ?" + pat = "%:"+domain + cursor = txn.execute(sql, (pat,)) + num_not_matching = cursor.fetchall()[0][0] + if num_not_matching == 0: + return True + return False \ No newline at end of file -- cgit 1.4.1 From 2f475bd5d5cd9426a38d748238a214aaf77be4a2 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 24 Apr 2015 18:15:07 +0100 Subject: pep8 --- synapse/storage/registration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'synapse/storage') diff --git a/synapse/storage/registration.py b/synapse/storage/registration.py index c2efc3fd32..697d487126 100644 --- a/synapse/storage/registration.py +++ b/synapse/storage/registration.py @@ -156,9 +156,9 @@ class RegistrationStore(SQLBaseStore): def _all_users_on_domain_txn(self, txn, domain): sql = "SELECT COUNT(*) FROM users WHERE name NOT LIKE ?" - pat = "%:"+domain + pat = "%:" + domain cursor = txn.execute(sql, (pat,)) num_not_matching = cursor.fetchall()[0][0] if num_not_matching == 0: return True - return False \ No newline at end of file + return False -- cgit 1.4.1 From f8152f2708cc0c476f5e1ec028a63ca632927eff Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 27 Apr 2015 10:16:26 +0100 Subject: rename db method to be more informative --- synapse/app/homeserver.py | 2 +- synapse/storage/registration.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'synapse/storage') diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py index 5f6cf4deeb..8da1a4bafc 100755 --- a/synapse/app/homeserver.py +++ b/synapse/app/homeserver.py @@ -244,7 +244,7 @@ class SynapseHomeServer(HomeServer): @defer.inlineCallbacks def post_startup_check(self): - all_users_native = yield self.get_datastore().all_users_on_domain( + all_users_native = yield self.get_datastore().are_all_users_on_domain( self.hostname ) if not all_users_native: diff --git a/synapse/storage/registration.py b/synapse/storage/registration.py index 697d487126..65ae58a39c 100644 --- a/synapse/storage/registration.py +++ b/synapse/storage/registration.py @@ -146,15 +146,15 @@ class RegistrationStore(SQLBaseStore): raise StoreError(404, "Token not found.") @defer.inlineCallbacks - def all_users_on_domain(self, domain): + def are_all_users_on_domain(self, domain): res = yield self.runInteraction( - "all_users_on_domain", - self._all_users_on_domain_txn, + "are_all_users_on_domain", + self._are_all_users_on_domain_txn, domain ) defer.returnValue(res) - def _all_users_on_domain_txn(self, txn, domain): + def _are_all_users_on_domain_txn(self, txn, domain): sql = "SELECT COUNT(*) FROM users WHERE name NOT LIKE ?" pat = "%:" + domain cursor = txn.execute(sql, (pat,)) -- cgit 1.4.1 From b02e1006b9d7282cdc9983d52ac478d4670a8361 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 27 Apr 2015 11:46:00 +0100 Subject: Run database check before daemonizing, at the cost of database hygiene. --- synapse/app/homeserver.py | 17 ++++++++--------- synapse/storage/__init__.py | 10 ++++++++++ synapse/storage/registration.py | 18 ------------------ 3 files changed, 18 insertions(+), 27 deletions(-) (limited to 'synapse/storage') diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py index 8da1a4bafc..8a00b21aa5 100755 --- a/synapse/app/homeserver.py +++ b/synapse/app/homeserver.py @@ -18,7 +18,8 @@ import sys sys.dont_write_bytecode = True from synapse.storage import ( - prepare_database, prepare_sqlite3_database, UpgradeDatabaseException, + prepare_database, prepare_sqlite3_database, are_all_users_on_domain, + UpgradeDatabaseException, ) from synapse.server import HomeServer @@ -242,10 +243,9 @@ class SynapseHomeServer(HomeServer): ) logger.info("Metrics now running on 127.0.0.1 port %d", config.metrics_port) - @defer.inlineCallbacks - def post_startup_check(self): - all_users_native = yield self.get_datastore().are_all_users_on_domain( - self.hostname + def run_startup_checks(self, db_conn): + all_users_native = are_all_users_on_domain( + db_conn, self.hostname ) if not all_users_native: sys.stderr.write( @@ -254,9 +254,9 @@ class SynapseHomeServer(HomeServer): "Found users in database not native to %s!\n" "You cannot changed a synapse server_name after it's been configured\n" "******************************************************\n" - "\n" + "\n" % (self.hostname,) ) - reactor.stop() + sys.exit(1) def get_version_string(): @@ -392,6 +392,7 @@ def setup(config_options): with sqlite3.connect(db_name) as db_conn: prepare_sqlite3_database(db_conn) prepare_database(db_conn) + hs.run_startup_checks(db_conn) except UpgradeDatabaseException: sys.stderr.write( "\nFailed to upgrade database.\n" @@ -416,8 +417,6 @@ def setup(config_options): hs.get_datastore().start_profiling() hs.get_replication_layer().start_get_pdu_cache() - reactor.callWhenRunning(hs.post_startup_check) - return hs diff --git a/synapse/storage/__init__.py b/synapse/storage/__init__.py index f4dec70393..0c47443689 100644 --- a/synapse/storage/__init__.py +++ b/synapse/storage/__init__.py @@ -421,3 +421,13 @@ def prepare_sqlite3_database(db_conn): " VALUES (?,?)", (row[0], False) ) + + +def are_all_users_on_domain(txn, domain): + sql = "SELECT COUNT(*) FROM users WHERE name NOT LIKE ?" + pat = "%:" + domain + cursor = txn.execute(sql, (pat,)) + num_not_matching = cursor.fetchall()[0][0] + if num_not_matching == 0: + return True + return False \ No newline at end of file diff --git a/synapse/storage/registration.py b/synapse/storage/registration.py index 65ae58a39c..f24154f146 100644 --- a/synapse/storage/registration.py +++ b/synapse/storage/registration.py @@ -144,21 +144,3 @@ class RegistrationStore(SQLBaseStore): return rows[0] raise StoreError(404, "Token not found.") - - @defer.inlineCallbacks - def are_all_users_on_domain(self, domain): - res = yield self.runInteraction( - "are_all_users_on_domain", - self._are_all_users_on_domain_txn, - domain - ) - defer.returnValue(res) - - def _are_all_users_on_domain_txn(self, txn, domain): - sql = "SELECT COUNT(*) FROM users WHERE name NOT LIKE ?" - pat = "%:" + domain - cursor = txn.execute(sql, (pat,)) - num_not_matching = cursor.fetchall()[0][0] - if num_not_matching == 0: - return True - return False -- cgit 1.4.1 From 8a5be236e0113125ab2fa9a5fb2f950b546acea9 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 27 Apr 2015 11:49:18 +0100 Subject: pep8 --- synapse/storage/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'synapse/storage') diff --git a/synapse/storage/__init__.py b/synapse/storage/__init__.py index 0c47443689..9b6471aec2 100644 --- a/synapse/storage/__init__.py +++ b/synapse/storage/__init__.py @@ -430,4 +430,4 @@ def are_all_users_on_domain(txn, domain): num_not_matching = cursor.fetchall()[0][0] if num_not_matching == 0: return True - return False \ No newline at end of file + return False -- cgit 1.4.1