commit 312423c9d0b68cea486c3269ddcaf95fcb3314cc Author: Damian Johnson atagar@torproject.org Date: Mon Nov 28 10:08:20 2011 -0800
Function and testing for null authentication
Function for authenticating to open connections and integration testing for it. The tests both check the happy case and responses we get in a variety of 'authentication needed' scenarios. --- run_tests.py | 2 + stem/connection.py | 24 ++++++++++++ test/integ/connection/__init__.py | 2 +- test/integ/connection/authentication.py | 62 +++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 1 deletions(-)
diff --git a/run_tests.py b/run_tests.py index 8dd141e..eee52ff 100755 --- a/run_tests.py +++ b/run_tests.py @@ -22,6 +22,7 @@ import test.unit.util.system import test.integ.socket.control_message import test.integ.util.conf import test.integ.util.system +import test.integ.connection.authentication import test.integ.connection.protocolinfo
import stem.util.enum @@ -41,6 +42,7 @@ UNIT_TESTS = (("stem.socket.ControlMessage", test.unit.socket.control_message.Te )
INTEG_TESTS = (("stem.socket.ControlMessage", test.integ.socket.control_message.TestControlMessage), + ("stem.connection.authenticate_*", test.integ.connection.authentication.TestAuthenticate), ("stem.connection.ProtocolInfoResponse", test.integ.connection.protocolinfo.TestProtocolInfo), ("stem.util.conf", test.integ.util.conf.TestConf), ("stem.util.system", test.integ.util.system.TestSystem), diff --git a/stem/connection.py b/stem/connection.py index b740d0d..bef8d50 100644 --- a/stem/connection.py +++ b/stem/connection.py @@ -38,6 +38,30 @@ LOGGER = logging.getLogger("stem")
AuthMethod = stem.util.enum.Enum("NONE", "PASSWORD", "COOKIE", "UNKNOWN")
+def authenticate_none(control_socket): + """ + Authenticates to an open control socket. All control connections need to + authenticate before they can be used, even if tor hasn't been configured to + use any authentication. + + If authentication fails then tor will close the control socket. + + Arguments: + control_socket (stem.socket.ControlSocket) - socket to be authenticated + + Raises: + ValueError if the empty authentication credentials aren't accepted + stem.socket.ProtocolError the content from the socket is malformed + stem.socket.SocketError if problems arise in using the socket + """ + + control_socket.send("AUTHENTICATE") + auth_response = control_socket.recv() + + # if we got anything but an OK response then error + if str(auth_response) != "OK": + raise ValueError(str(auth_response)) + def get_protocolinfo_by_port(control_addr = "127.0.0.1", control_port = 9051, get_socket = False): """ Issues a PROTOCOLINFO query to a control port, getting information about the diff --git a/test/integ/connection/__init__.py b/test/integ/connection/__init__.py index dd0925e..d570669 100644 --- a/test/integ/connection/__init__.py +++ b/test/integ/connection/__init__.py @@ -2,5 +2,5 @@ Integration tests for stem.connection. """
-__all__ = ["protocolinfo"] +__all__ = ["authenticate", "protocolinfo"]
diff --git a/test/integ/connection/authentication.py b/test/integ/connection/authentication.py new file mode 100644 index 0000000..4ba0bfa --- /dev/null +++ b/test/integ/connection/authentication.py @@ -0,0 +1,62 @@ +""" +Integration tests for authenticating to the control socket via +stem.connection.authenticate_* functions. +""" + +import unittest + +import test.runner +import stem.connection + +# Responses given by tor for various authentication failures. These may change +# in the future and if they do then this test should be updated. + +COOKIE_AUTH_FAIL = "Authentication failed: Wrong length on authentication cookie." +PASSWORD_AUTH_FAIL = "Authentication failed: Password did not match HashedControlPassword value from configuration. Maybe you tried a plain text password? If so, the standard requires that you put it in double quotes." +MULTIPLE_AUTH_FAIL = "Authentication failed: Password did not match HashedControlPassword *or* authentication cookie." + +class TestAuthenticate(unittest.TestCase): + """ + Tests the authentication methods. This should be run with the 'CONN_ALL' + integ target to exercise the widest range of use cases. + """ + + def test_authenticate_none(self): + """ + Tests the authenticate_none function. + """ + + runner = test.runner.get_runner() + connection_type = runner.get_connection_type() + + if connection_type == test.runner.TorConnection.NONE: + self.skipTest("(no connection)") + + # If the connection has authentication then this will fail with a message + # based on the authentication type. If not then this will succeed. + + control_socket = test.runner.get_runner().get_tor_socket(False) + + connection_options = test.runner.CONNECTION_OPTS[connection_type] + cookie_auth = test.runner.OPT_COOKIE in connection_options + password_auth = test.runner.OPT_PASSWORD in connection_options + + if cookie_auth or password_auth: + if cookie_auth and password_auth: failure_msg = MULTIPLE_AUTH_FAIL + elif cookie_auth: failure_msg = COOKIE_AUTH_FAIL + else: failure_msg = PASSWORD_AUTH_FAIL + + try: + stem.connection.authenticate_none(control_socket) + self.fail() + except ValueError, exc: + self.assertEqual(failure_msg, str(exc)) + else: + stem.connection.authenticate_none(control_socket) + + # issues a 'GETINFO config-file' query to confirm that we can use the socket + + control_socket.send("GETINFO config-file") + config_file_response = control_socket.recv() + self.assertEquals("config-file=%s\nOK" % runner.get_torrc_path(), str(config_file_response)) +
tor-commits@lists.torproject.org