def render_GET(self, request): err, args = get_args(request, ('sid', 'client_secret')) if err: return err sid = args['sid'] clientSecret = args['client_secret'] valSessionStore = ThreePidValSessionStore(self.sydent) noMatchError = {'errcode': 'M_NO_VALID_SESSION', 'error': "No valid session was found matching that sid and client secret"} try: s = valSessionStore.getValidatedSession(sid, clientSecret) except IncorrectClientSecretException: return noMatchError except SessionExpiredException: return {'errcode': 'M_SESSION_EXPIRED', 'error': "This validation session has expired: call requestToken again"} except InvalidSessionIdException: return noMatchError except SessionNotValidatedException: return {'errcode': 'M_SESSION_NOT_VALIDATED', 'error': "This validation session has not yet been completed"} return { 'medium': s.medium, 'address': s.address, 'validated_at': s.mtime }
def render_POST(self, request): send_cors(request) err, args = get_args(request, ('sid', 'client_secret', 'mxid')) if err: return err sid = args['sid'] mxid = args['mxid'] clientSecret = args['client_secret'] # Return the same error for not found / bad client secret otherwise people can get information about # sessions without knowing the secret noMatchError = {'errcode': 'M_NO_VALID_SESSION', 'error': "No valid session was found matching that sid and client secret"} try: valSessionStore = ThreePidValSessionStore(self.sydent) s = valSessionStore.getValidatedSession(sid, clientSecret) except IncorrectClientSecretException: return noMatchError except SessionExpiredException: return {'errcode': 'M_SESSION_EXPIRED', 'error': "This validation session has expired: call requestToken again"} except InvalidSessionIdException: return noMatchError except SessionNotValidatedException: return {'errcode': 'M_SESSION_NOT_VALIDATED', 'error': "This validation session has not yet been completed"} res = self.sydent.threepidBinder.addBinding(s.medium, s.address, mxid) return res
def render_GET(self, request): authIfV2(self.sydent, request) args = get_args(request, ('sid', 'client_secret')) sid = args['sid'] clientSecret = args['client_secret'] if not is_valid_client_secret(clientSecret): request.setResponseCode(400) return { 'errcode': 'M_INVALID_PARAM', 'error': 'Invalid client_secret provided' } valSessionStore = ThreePidValSessionStore(self.sydent) noMatchError = { 'errcode': 'M_NO_VALID_SESSION', 'error': "No valid session was found matching that sid and client secret" } try: s = valSessionStore.getValidatedSession(sid, clientSecret) except (IncorrectClientSecretException, InvalidSessionIdException): request.setResponseCode(404) return noMatchError except SessionExpiredException: request.setResponseCode(400) return { 'errcode': 'M_SESSION_EXPIRED', 'error': "This validation session has expired: call requestToken again" } except SessionNotValidatedException: request.setResponseCode(400) return { 'errcode': 'M_SESSION_NOT_VALIDATED', 'error': "This validation session has not yet been completed" } return { 'medium': s.medium, 'address': s.address, 'validated_at': s.mtime }
def render_POST(self, request): send_cors(request) account = authIfV2(self.sydent, request) args = get_args(request, ('sid', 'client_secret', 'mxid')) sid = args['sid'] mxid = args['mxid'] clientSecret = args['client_secret'] # Return the same error for not found / bad client secret otherwise people can get information about # sessions without knowing the secret noMatchError = { 'errcode': 'M_NO_VALID_SESSION', 'error': "No valid session was found matching that sid and client secret" } if account: # This is a v2 API so only allow binding to the logged in user id if account.userId != mxid: raise MatrixRestError( 403, 'M_UNAUTHORIZED', "This user is prohibited from binding to the mxid") try: valSessionStore = ThreePidValSessionStore(self.sydent) s = valSessionStore.getValidatedSession(sid, clientSecret) except IncorrectClientSecretException: return noMatchError except SessionExpiredException: return { 'errcode': 'M_SESSION_EXPIRED', 'error': "This validation session has expired: call requestToken again" } except InvalidSessionIdException: return noMatchError except SessionNotValidatedException: return { 'errcode': 'M_SESSION_NOT_VALIDATED', 'error': "This validation session has not yet been completed" } res = self.sydent.threepidBinder.addBinding(s.medium, s.address, mxid) return res
def addBinding(self, valSessionId, clientSecret, mxid): valSessionStore = ThreePidValSessionStore(self.sydent) localAssocStore = LocalAssociationStore(self.sydent) s = valSessionStore.getValidatedSession(valSessionId, clientSecret) createdAt = time_msec() expires = createdAt + ThreepidBinder.THREEPID_ASSOCIATION_LIFETIME_MS assoc = ThreepidAssociation(s.medium, s.address, mxid, createdAt, createdAt, expires) localAssocStore.addOrUpdateAssociation(assoc) self.sydent.pusher.doLocalPush() assocSigner = AssociationSigner(self.sydent) sgassoc = assocSigner.signedThreePidAssociation(assoc) return sgassoc
def render_POST(self, request): send_cors(request) err, args = get_args(request, ('sid', 'client_secret', 'mxid')) if err: return err sid = args['sid'] mxid = args['mxid'] clientSecret = args['client_secret'] # Return the same error for not found / bad client secret otherwise people can get information about # sessions without knowing the secret noMatchError = { 'errcode': 'M_NO_VALID_SESSION', 'error': "No valid session was found matching that sid and client secret" } try: valSessionStore = ThreePidValSessionStore(self.sydent) s = valSessionStore.getValidatedSession(sid, clientSecret) except IncorrectClientSecretException: return noMatchError except SessionExpiredException: return { 'errcode': 'M_SESSION_EXPIRED', 'error': "This validation session has expired: call requestToken again" } except InvalidSessionIdException: return noMatchError except SessionNotValidatedException: return { 'errcode': 'M_SESSION_NOT_VALIDATED', 'error': "This validation session has not yet been completed" } res = self.sydent.threepidBinder.addBinding(s.medium, s.address, mxid) return res
def addBinding(self, valSessionId, clientSecret, mxid): valSessionStore = ThreePidValSessionStore(self.sydent) localAssocStore = LocalAssociationStore(self.sydent) s = valSessionStore.getValidatedSession(valSessionId, clientSecret) createdAt = time_msec() expires = createdAt + ThreepidBinder.THREEPID_ASSOCIATION_LIFETIME_MS assoc = ThreepidAssociation(s.medium, s.address, mxid, createdAt, createdAt, expires) localAssocStore.addOrUpdateAssociation(assoc) self.sydent.pusher.doLocalPush() joinTokenStore = JoinTokenStore(self.sydent) pendingJoinTokens = joinTokenStore.getTokens(s.medium, s.address) invites = [] for token in pendingJoinTokens: token["mxid"] = mxid token["signed"] = { "mxid": mxid, "token": token["token"], } token["signed"] = signedjson.sign.sign_json( token["signed"], self.sydent.server_name, self.sydent.keyring.ed25519) invites.append(token) if invites: assoc.extra_fields["invites"] = invites joinTokenStore.markTokensAsSent(s.medium, s.address) assocSigner = AssociationSigner(self.sydent) sgassoc = assocSigner.signedThreePidAssociation(assoc) self._notify(sgassoc, 0) return sgassoc
def render_GET(self, request): # err = require_args(request, ('sid', 'client_secret')) err = require_args(request, ('sid',)) if err: return err sid = request.args['sid'][0] #clientSecret = request.args['client_secret'][0] if 'client_secret' in request.args: clientSecret = request.args['client_secret'][0] elif 'clientSecret' in request.args: clientSecret = request.args['clientSecret'][0] else: request.setResponseCode(400) return {'errcode': 'M_MISSING_PARAM', 'error':'No client_secret'} valSessionStore = ThreePidValSessionStore(self.sydent) noMatchError = {'errcode': 'M_NO_VALID_SESSION', 'error': "No valid session was found matching that sid and client secret"} try: s = valSessionStore.getValidatedSession(sid, clientSecret) except IncorrectClientSecretException: return noMatchError except SessionExpiredException: return {'errcode': 'M_SESSION_EXPIRED', 'error': "This validation session has expired: call requestToken again"} except InvalidSessionIdException: return noMatchError except SessionNotValidatedException: return {'errcode': 'M_SESSION_NOT_VALIDATED', 'error': "This validation session has not yet been completed"} return { 'medium': s.medium, 'address': s.address, 'validated_at': s.mtime }
def addBinding(self, valSessionId, clientSecret, mxid): valSessionStore = ThreePidValSessionStore(self.sydent) localAssocStore = LocalAssociationStore(self.sydent) s = valSessionStore.getValidatedSession(valSessionId, clientSecret) createdAt = time_msec() expires = createdAt + ThreepidBinder.THREEPID_ASSOCIATION_LIFETIME_MS assoc = ThreepidAssociation(s.medium, s.address, mxid, createdAt, createdAt, expires) localAssocStore.addOrUpdateAssociation(assoc) self.sydent.pusher.doLocalPush() joinTokenStore = JoinTokenStore(self.sydent) pendingJoinTokens = joinTokenStore.getTokens(s.medium, s.address) invites = [] for token in pendingJoinTokens: token["mxid"] = mxid token["signed"] = { "mxid": mxid, "token": token["token"], } token["signed"] = signedjson.sign.sign_json(token["signed"], self.sydent.server_name, self.sydent.keyring.ed25519) invites.append(token) if invites: assoc.extra_fields["invites"] = invites joinTokenStore.markTokensAsSent(s.medium, s.address) assocSigner = AssociationSigner(self.sydent) sgassoc = assocSigner.signedThreePidAssociation(assoc) self._notify(sgassoc, 0) return sgassoc
def _async_render_POST(self, request): try: try: body = json.load(request.content) except ValueError: request.setResponseCode(400) request.write(json.dumps({'errcode': 'M_BAD_JSON', 'error': 'Malformed JSON'})) request.finish() return missing = [k for k in ("threepid", "mxid") if k not in body] if len(missing) > 0: request.setResponseCode(400) msg = "Missing parameters: "+(",".join(missing)) request.write(json.dumps({'errcode': 'M_MISSING_PARAMS', 'error': msg})) request.finish() return threepid = body['threepid'] mxid = body['mxid'] if 'medium' not in threepid or 'address' not in threepid: request.setResponseCode(400) request.write(json.dumps({'errcode': 'M_MISSING_PARAMS', 'error': 'Threepid lacks medium / address'})) request.finish() return # We now check for authentication in two different ways, depending # on the contents of the request. If the user has supplied "sid" # (the Session ID returned by Sydent during the original binding) # and "client_secret" fields, they are trying to provie that they # were the original author of the bind. We then check that what # they supply matches and if it does, allow the unbind. # # However if these fields are not supplied, we instead check # whether the request originated from a homeserver, and if so the # same homeserver that originally created the bind. We do this by # checking the signature of the request. If it all matches up, we # allow the unbind. # # Only one method of authentication is required. if 'sid' in body and 'client_secret' in body: sid = body['sid'] client_secret = body['client_secret'] valSessionStore = ThreePidValSessionStore(self.sydent) noMatchError = {'errcode': 'M_NO_VALID_SESSION', 'error': "No valid session was found matching that sid and client secret"} try: s = valSessionStore.getValidatedSession(sid, client_secret) except IncorrectClientSecretException: request.setResponseCode(401) request.write(json.dumps(noMatchError)) request.finish() return except InvalidSessionIdException: request.setResponseCode(401) request.write(json.dumps(noMatchError)) request.finish() return except SessionNotValidatedException: request.setResponseCode(403) request.write(json.dumps({ 'errcode': 'M_SESSION_NOT_VALIDATED', 'error': "This validation session has not yet been completed" })) return if s.medium != threepid['medium'] or s.address != threepid['address']: request.setResponseCode(403) request.write(json.dumps({ 'errcode': 'M_FORBIDDEN', 'error': 'Provided session information does not match medium/address combo', })) request.finish() return else: try: origin_server_name = yield self.sydent.sig_verifier.authenticate_request(request, body) except SignatureVerifyException as ex: request.setResponseCode(401) request.write(json.dumps({'errcode': 'M_FORBIDDEN', 'error': ex.message})) request.finish() return except NoAuthenticationError as ex: request.setResponseCode(401) request.write(json.dumps({'errcode': 'M_FORBIDDEN', 'error': ex.message})) request.finish() return except: logger.exception("Exception whilst authenticating unbind request") request.setResponseCode(500) request.write(json.dumps({'errcode': 'M_UNKNOWN', 'error': 'Internal Server Error'})) request.finish() return if not mxid.endswith(':' + origin_server_name): request.setResponseCode(403) request.write(json.dumps({'errcode': 'M_FORBIDDEN', 'error': 'Origin server name does not match mxid'})) request.finish() return try: res = self.sydent.threepidBinder.removeBinding(threepid, mxid) except ValueError: # User could have provided correct 3PID/sid/client_secret # details but not the correct mxid, which would cause the # binding removal to fail request.setResponseCode(400) request.write(json.dumps({'errcode': 'M_UNKNOWN', 'error': "Association between provided mxid and 3pid not found"})) request.finish() return request.write(json.dumps({})) request.finish() except Exception as ex: logger.exception("Exception whilst handling unbind") request.setResponseCode(500) request.write(json.dumps({'errcode': 'M_UNKNOWN', 'error': ex.message})) request.finish()
async def _async_render_POST(self, request: Request) -> None: try: try: # TODO: we should really validate that this gives us a dict, and # not some other json value like str, list, int etc # json.loads doesn't allow bytes in Python 3.5 body: JsonDict = json_decoder.decode( request.content.read().decode("UTF-8")) except ValueError: request.setResponseCode(HTTPStatus.BAD_REQUEST) request.write( dict_to_json_bytes({ "errcode": "M_BAD_JSON", "error": "Malformed JSON" })) request.finish() return missing = [k for k in ("threepid", "mxid") if k not in body] if len(missing) > 0: request.setResponseCode(HTTPStatus.BAD_REQUEST) msg = "Missing parameters: " + (",".join(missing)) request.write( dict_to_json_bytes({ "errcode": "M_MISSING_PARAMS", "error": msg })) request.finish() return threepid = body["threepid"] mxid = body["mxid"] if "medium" not in threepid or "address" not in threepid: request.setResponseCode(HTTPStatus.BAD_REQUEST) request.write( dict_to_json_bytes({ "errcode": "M_MISSING_PARAMS", "error": "Threepid lacks medium / address", })) request.finish() return # We now check for authentication in two different ways, depending # on the contents of the request. If the user has supplied "sid" # (the Session ID returned by Sydent during the original binding) # and "client_secret" fields, they are trying to prove that they # were the original author of the bind. We then check that what # they supply matches and if it does, allow the unbind. # # However if these fields are not supplied, we instead check # whether the request originated from a homeserver, and if so the # same homeserver that originally created the bind. We do this by # checking the signature of the request. If it all matches up, we # allow the unbind. # # Only one method of authentication is required. if "sid" in body and "client_secret" in body: sid = body["sid"] client_secret = body["client_secret"] if not is_valid_client_secret(client_secret): request.setResponseCode(HTTPStatus.BAD_REQUEST) request.write( dict_to_json_bytes({ "errcode": "M_INVALID_PARAM", "error": "Invalid client_secret provided", })) request.finish() return valSessionStore = ThreePidValSessionStore(self.sydent) try: s = valSessionStore.getValidatedSession(sid, client_secret) except (IncorrectClientSecretException, InvalidSessionIdException): request.setResponseCode(HTTPStatus.UNAUTHORIZED) request.write( dict_to_json_bytes({ "errcode": "M_NO_VALID_SESSION", "error": "No valid session was found matching that sid and client secret", })) request.finish() return except SessionNotValidatedException: request.setResponseCode(HTTPStatus.FORBIDDEN) request.write( dict_to_json_bytes({ "errcode": "M_SESSION_NOT_VALIDATED", "error": "This validation session has not yet been completed", })) return if s.medium != threepid["medium"] or s.address != threepid[ "address"]: request.setResponseCode(HTTPStatus.FORBIDDEN) request.write( dict_to_json_bytes({ "errcode": "M_FORBIDDEN", "error": "Provided session information does not match medium/address combo", })) request.finish() return else: try: origin_server_name = ( await self.sydent.sig_verifier.authenticate_request( request, body)) except SignatureVerifyException as ex: request.setResponseCode(HTTPStatus.UNAUTHORIZED) request.write( dict_to_json_bytes({ "errcode": "M_FORBIDDEN", "error": str(ex) })) request.finish() return except NoAuthenticationError as ex: request.setResponseCode(HTTPStatus.UNAUTHORIZED) request.write( dict_to_json_bytes({ "errcode": "M_FORBIDDEN", "error": str(ex) })) request.finish() return except InvalidServerName as ex: request.setResponseCode(HTTPStatus.BAD_REQUEST) request.write( dict_to_json_bytes({ "errcode": "M_INVALID_PARAM", "error": str(ex) })) request.finish() return except (DNSLookupError, ConnectError, ResponseFailed) as e: msg = (f"Unable to contact the Matrix homeserver to " f"authenticate request ({type(e).__name__})") logger.warning(msg) request.setResponseCode(HTTPStatus.INTERNAL_SERVER_ERROR) request.write( dict_to_json_bytes({ "errcode": "M_UNKNOWN", "error": msg, })) request.finish() return except Exception: logger.exception( "Exception whilst authenticating unbind request") request.setResponseCode(HTTPStatus.INTERNAL_SERVER_ERROR) request.write( dict_to_json_bytes({ "errcode": "M_UNKNOWN", "error": "Internal Server Error" })) request.finish() return if not mxid.endswith(":" + origin_server_name): request.setResponseCode(HTTPStatus.FORBIDDEN) request.write( dict_to_json_bytes({ "errcode": "M_FORBIDDEN", "error": "Origin server name does not match mxid", })) request.finish() return self.sydent.threepidBinder.removeBinding(threepid, mxid) request.write(dict_to_json_bytes({})) request.finish() except Exception as ex: logger.exception("Exception whilst handling unbind") request.setResponseCode(HTTPStatus.INTERNAL_SERVER_ERROR) request.write( dict_to_json_bytes({ "errcode": "M_UNKNOWN", "error": str(ex) })) request.finish()
def _async_render_POST(self, request): try: try: body = json.load(request.content) except ValueError: request.setResponseCode(400) request.write( json.dumps({ 'errcode': 'M_BAD_JSON', 'error': 'Malformed JSON' })) request.finish() return missing = [k for k in ("threepid", "mxid") if k not in body] if len(missing) > 0: request.setResponseCode(400) msg = "Missing parameters: " + (",".join(missing)) request.write( json.dumps({ 'errcode': 'M_MISSING_PARAMS', 'error': msg })) request.finish() return threepid = body['threepid'] mxid = body['mxid'] if 'medium' not in threepid or 'address' not in threepid: request.setResponseCode(400) request.write( json.dumps({ 'errcode': 'M_MISSING_PARAMS', 'error': 'Threepid lacks medium / address' })) request.finish() return # We now check for authentication in two different ways, depending # on the contents of the request. If the user has supplied "sid" # (the Session ID returned by Sydent during the original binding) # and "client_secret" fields, they are trying to provie that they # were the original author of the bind. We then check that what # they supply matches and if it does, allow the unbind. # # However if these fields are not supplied, we instead check # whether the request originated from a homeserver, and if so the # same homeserver that originally created the bind. We do this by # checking the signature of the request. If it all matches up, we # allow the unbind. # # Only one method of authentication is required. if 'sid' in body and 'client_secret' in body: sid = body['sid'] client_secret = body['client_secret'] valSessionStore = ThreePidValSessionStore(self.sydent) noMatchError = { 'errcode': 'M_NO_VALID_SESSION', 'error': "No valid session was found matching that sid and client secret" } try: s = valSessionStore.getValidatedSession(sid, client_secret) except IncorrectClientSecretException: request.setResponseCode(401) request.write(json.dumps(noMatchError)) request.finish() return except InvalidSessionIdException: request.setResponseCode(401) request.write(json.dumps(noMatchError)) request.finish() return except SessionNotValidatedException: request.setResponseCode(403) request.write( json.dumps({ 'errcode': 'M_SESSION_NOT_VALIDATED', 'error': "This validation session has not yet been completed" })) return if s.medium != threepid['medium'] or s.address != threepid[ 'address']: request.setResponseCode(403) request.write( json.dumps({ 'errcode': 'M_FORBIDDEN', 'error': 'Provided session information does not match medium/address combo', })) request.finish() return else: try: origin_server_name = yield self.sydent.sig_verifier.authenticate_request( request, body) except SignatureVerifyException as ex: request.setResponseCode(401) request.write( json.dumps({ 'errcode': 'M_FORBIDDEN', 'error': ex.message })) request.finish() return except NoAuthenticationError as ex: request.setResponseCode(401) request.write( json.dumps({ 'errcode': 'M_FORBIDDEN', 'error': ex.message })) request.finish() return except: logger.exception( "Exception whilst authenticating unbind request") request.setResponseCode(500) request.write( json.dumps({ 'errcode': 'M_UNKNOWN', 'error': 'Internal Server Error' })) request.finish() return if not mxid.endswith(':' + origin_server_name): request.setResponseCode(403) request.write( json.dumps({ 'errcode': 'M_FORBIDDEN', 'error': 'Origin server name does not match mxid' })) request.finish() return try: res = self.sydent.threepidBinder.removeBinding(threepid, mxid) except ValueError: # User could have provided correct 3PID/sid/client_secret # details but not the correct mxid, which would cause the # binding removal to fail request.setResponseCode(400) request.write( json.dumps({ 'errcode': 'M_UNKNOWN', 'error': "Association between provided mxid and 3pid not found" })) request.finish() return request.write(json.dumps({})) request.finish() except Exception as ex: logger.exception("Exception whilst handling unbind") request.setResponseCode(500) request.write( json.dumps({ 'errcode': 'M_UNKNOWN', 'error': ex.message })) request.finish()