From 0ba0e013b54c033227fbc2329e0b01acf7846218 Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Sun, 15 May 2011 22:18:38 +0100 Subject: [PATCH] Force backwards incompatibility. Force clients prior to 1.4 to fail authentication, this was we might reduce tickets like #1852. --- deluge/core/rpcserver.py | 10 ++++- deluge/error.py | 3 ++ deluge/tests/test_client.py | 76 ++++++++++++++++++++++++++++++++++++- deluge/ui/client.py | 6 +-- 4 files changed, 88 insertions(+), 7 deletions(-) diff --git a/deluge/core/rpcserver.py b/deluge/core/rpcserver.py index 274aeae48..5536d2892 100644 --- a/deluge/core/rpcserver.py +++ b/deluge/core/rpcserver.py @@ -56,7 +56,8 @@ except ImportError: import deluge.component as component import deluge.configmanager from deluge.core.authmanager import AUTH_LEVEL_NONE, AUTH_LEVEL_DEFAULT, AUTH_LEVEL_ADMIN -from deluge.error import DelugeError, NotAuthorizedError, _PassthroughError +from deluge.error import (DelugeError, NotAuthorizedError, _PassthroughError, + IncompatibleClient) RPC_RESPONSE = 1 RPC_ERROR = 2 @@ -261,6 +262,13 @@ class DelugeRPCProtocol(Protocol): # We need to authenticate the user here log.debug("RPC dispatch daemon.login") try: + client_version = kwargs.pop('client_version', None) + if client_version is None: + raise IncompatibleClient( + "Your deluge client is not compatible with the daemon. " + "Please upgrade your client to %s" % + deluge.common.get_version() + ) ret = component.get("AuthManager").authorize(*args, **kwargs) if ret: self.factory.authorized_sessions[self.transport.sessionno] = (ret, args[0]) diff --git a/deluge/error.py b/deluge/error.py index d9669ec20..2e3c78305 100644 --- a/deluge/error.py +++ b/deluge/error.py @@ -65,6 +65,9 @@ class _PassthroughError(DelugeError): inst._kwargs = kwargs return inst +class IncompatibleClient(_PassthroughError): + pass + class NotAuthorizedError(_PassthroughError): def __init__(self, current_level, required_level): diff --git a/deluge/tests/test_client.py b/deluge/tests/test_client.py index 82171ee0c..0e9bf5c1b 100644 --- a/deluge/tests/test_client.py +++ b/deluge/tests/test_client.py @@ -1,11 +1,65 @@ import common +from twisted.internet import defer from twisted.trial import unittest from deluge import error -from deluge.core.authmanager import AUTH_LEVEL_ADMIN, AUTH_LEVEL_DEFAULT -from deluge.ui.client import client +from deluge.core.authmanager import AUTH_LEVEL_ADMIN +from deluge.ui.client import client, Client, DaemonSSLProxy + + +class NoVersionSendingDaemonSSLProxy(DaemonSSLProxy): + def authenticate(self, username, password): + self.login_deferred = defer.Deferred() + d = self.call("daemon.login", username, password) + d.addCallback(self.__on_login, username) + d.addErrback(self.__on_login_fail) + return self.login_deferred + + def __on_login(self, result, username): + self.login_deferred.callback(result) + + def __on_login_fail(self, result): + self.login_deferred.errback(result) + +class NoVersionSendingClient(Client): + + def connect(self, host="127.0.0.1", port=58846, username="", password="", + skip_authentication=False): + self._daemon_proxy = NoVersionSendingDaemonSSLProxy() + self._daemon_proxy.set_disconnect_callback(self.__on_disconnect) + + d = self._daemon_proxy.connect(host, port) + + def on_connect_fail(reason): + self.disconnect() + return reason + + def on_authenticate(result, daemon_info): + return result + + def on_authenticate_fail(reason): + return reason + + def on_connected(daemon_version): + return daemon_version + + def authenticate(daemon_version, username, password): + d = self._daemon_proxy.authenticate(username, password) + d.addCallback(on_authenticate, daemon_version) + d.addErrback(on_authenticate_fail) + return d + + d.addCallback(on_connected) + d.addErrback(on_connect_fail) + if not skip_authentication: + d.addCallback(authenticate, username, password) + return d + + def __on_disconnect(self): + if self.disconnect_callback: + self.disconnect_callback() class ClientTestCase(unittest.TestCase): @@ -78,3 +132,21 @@ class ClientTestCase(unittest.TestCase): d.addErrback(on_failure) return d + + def test_connect_without_sending_client_version_fails(self): + from deluge.ui import common + username, password = common.get_localhost_auth() + no_version_sending_client = NoVersionSendingClient() + d = no_version_sending_client.connect( + "localhost", 58846, username=username, password=password + ) + + def on_failure(failure): + self.assertEqual( + failure.trap(error.IncompatibleClient), + error.IncompatibleClient + ) + self.addCleanup(no_version_sending_client.disconnect) + + d.addErrback(on_failure) + return d diff --git a/deluge/ui/client.py b/deluge/ui/client.py index e281a30ca..e2105f2df 100644 --- a/deluge/ui/client.py +++ b/deluge/ui/client.py @@ -46,7 +46,6 @@ import zlib import deluge.common from deluge import error -from deluge.log import LOG as log from deluge.event import known_events if deluge.common.windows_check(): @@ -299,8 +298,6 @@ class DaemonSSLProxy(DaemonProxy): :param host: str, the host to connect to :param port: int, the listening port on the daemon - :param username: str, the username to login as - :param password: str, the password to login with :returns: twisted.Deferred @@ -451,7 +448,8 @@ class DaemonSSLProxy(DaemonProxy): def authenticate(self, username, password): log.debug("%s.authenticate: %s", self.__class__.__name__, username) self.login_deferred = defer.Deferred() - d = self.call("daemon.login", username, password) + d = self.call("daemon.login", username, password, + client_version=deluge.common.get_version()) d.addCallback(self.__on_login, username) d.addErrback(self.__on_login_fail) return self.login_deferred