[tor-commits] [arm/master] Moving function for connecting and authenticating to util

atagar at torproject.org atagar at torproject.org
Mon Dec 30 05:17:13 UTC 2013


commit b65341c6a69d93ebdca6b35975821426268b6319
Author: Damian Johnson <atagar at torproject.org>
Date:   Sun Dec 29 14:08:25 2013 -0800

    Moving function for connecting and authenticating to util
    
    Most of our starter's functions are pretty trivial but there were two notable
    exceptions: connecting and authenticating to tor. Moving these to our util
    allow our starter to be quite a bit shorter.
---
 arm/starter.py                 |   72 +++++------------------------------
 arm/util/__init__.py           |   81 ++++++++++++++++++++++++++++++++++++----
 test/starter/authenticate.py   |   79 ---------------------------------------
 test/starter/get_controller.py |   67 ---------------------------------
 test/util/authenticate.py      |   73 ++++++++++++++++++++++++++++++++++++
 test/util/init_controller.py   |   67 +++++++++++++++++++++++++++++++++
 6 files changed, 223 insertions(+), 216 deletions(-)

diff --git a/arm/starter.py b/arm/starter.py
index 0970b35..2cd45ce 100644
--- a/arm/starter.py
+++ b/arm/starter.py
@@ -5,7 +5,6 @@ connection.
 """
 
 import curses
-import getpass
 import locale
 import logging
 import os
@@ -19,17 +18,16 @@ import arm.arguments
 import arm.controller
 import arm.util.panel
 import arm.util.torConfig
+import arm.util.torTools
 import arm.util.tracker
 import arm.util.uiTools
 
 import stem
-import stem.connection
-import stem.control
 import stem.util.conf
 import stem.util.log
 import stem.util.system
 
-from arm.util import BASE_DIR, init_controller, msg, trace, info, notice, warn, load_settings
+from arm.util import BASE_DIR, init_controller, authenticate, msg, trace, info, notice, warn, load_settings
 
 CONFIG = stem.util.conf.get_config('arm')
 
@@ -68,9 +66,13 @@ def main():
   _load_user_armrc(args.config)
 
   try:
-    controller = _get_controller(args)
-    _authenticate(controller, CONFIG.get('tor.password', None))
-    init_controller(controller)
+    controller = init_controller(args)
+    authenticate(controller, CONFIG.get('tor.password', None), CONFIG.get('tor.chroot', ''))
+
+    # TODO: Our tor_controller() method will gradually replace the torTools
+    # module, but until that we need to initialize it too.
+
+    arm.util.torTools.getConn().init(controller)
   except ValueError as exc:
     print exc
     exit(1)
@@ -99,62 +101,6 @@ def main():
     _shutdown_daemons(controller)
 
 
-def _get_controller(args):
-  """
-  Provides a Controller for the endpoint specified in the given arguments.
-  """
-
-  if os.path.exists(args.control_socket):
-    try:
-      return stem.control.Controller.from_socket_file(args.control_socket)
-    except stem.SocketError as exc:
-      if args.user_provided_socket:
-        raise ValueError(msg('connect.unable_to_use_socket', path = args.control_socket, error = exc))
-  elif args.user_provided_socket:
-    raise ValueError(msg('connect.socket_doesnt_exist', path = args.control_socket))
-
-  try:
-    return stem.control.Controller.from_port(args.control_address, args.control_port)
-  except stem.SocketError as exc:
-    if args.user_provided_port:
-      raise ValueError(msg('connect.unable_to_use_port', address = args.control_address, port = args.control_port, error = exc))
-
-  if not stem.util.system.is_running('tor'):
-    raise ValueError(msg('connect.tor_isnt_running'))
-  else:
-    raise ValueError(msg('connect.no_control_port'))
-
-
-def _authenticate(controller, password):
-  """
-  Authenticates to the given Controller.
-  """
-
-  try:
-    controller.authenticate(password = password, chroot_path = CONFIG.get('tor.chroot', ''))
-  except stem.connection.IncorrectSocketType:
-    control_socket = controller.get_socket()
-
-    if isinstance(control_socket, stem.socket.ControlPort):
-      raise ValueError(msg('connect.wrong_port_type', port = control_socket.get_port()))
-    else:
-      raise ValueError(msg('connect.wrong_socket_type'))
-  except stem.connection.UnrecognizedAuthMethods as exc:
-    raise ValueError(msg('uncrcognized_auth_type', auth_methods = ', '.join(exc.unknown_auth_methods)))
-  except stem.connection.IncorrectPassword:
-    raise ValueError(msg('connect.incorrect_password'))
-  except stem.connection.MissingPassword:
-    if password:
-      raise ValueError(msg('connect.missing_password_bug'))
-
-    password = getpass.getpass(msg('connect.password_prompt') + ' ')
-    return _authenticate(controller, password)
-  except stem.connection.UnreadableCookieFile as exc:
-    raise ValueError(msg('connect.unreadable_cookie_file', path = exc.cookie_path, issue = str(exc)))
-  except stem.connection.AuthenticationFailure as exc:
-    raise ValueError(msg('connect.general_auth_failure', error = exc))
-
-
 def _setup_debug_logging(args):
   """
   Configures us to log at stem's trace level to debug log path, and notes some
diff --git a/arm/util/__init__.py b/arm/util/__init__.py
index 2603349..bd43761 100644
--- a/arm/util/__init__.py
+++ b/arm/util/__init__.py
@@ -6,10 +6,14 @@ and safely working with curses (hiding some of the gory details).
 
 __all__ = ["connections", "panel", "sysTools", "textInput", "torConfig", "torTools", "tracker", "uiTools"]
 
+import getpass
 import os
 
 import arm.util.torTools
 
+import stem
+import stem.connection
+import stem.control
 import stem.util.conf
 import stem.util.log
 
@@ -27,20 +31,57 @@ def tor_controller():
   return TOR_CONTROLLER
 
 
-def init_controller(controller):
+def init_controller(args):
   """
-  Registers an initialized tor controller.
+  Provides a Controller for the endpoint specified in the given arguments.
 
-  :param stem.control.Controller controller: tor controller for arm to use
+  :param namedtuple args: arguments that arm was started with
+
+  :returns: :class:`~stem.control.Controller` for the given arguments
+
+  :raises: **ValueError** if unable to acquire a controller connection
   """
 
   global TOR_CONTROLLER
-  TOR_CONTROLLER = controller
+  TOR_CONTROLLER = _get_controller(args)
+  return TOR_CONTROLLER
+
 
-  # TODO: Our controller() method will gradually replace the torTools module,
-  # but until that we need to initialize it too.
+def authenticate(controller, password, chroot_path = ''):
+  """
+  Authenticates to the given Controller.
+
+  :param stem.control.Controller controller: controller to be authenticated
+  :param str password: password to authenticate with, **None** if nothing was
+    provided
+  :param str chroot_path: chroot tor resides within
+
+  :raises: **ValueError** if unable to authenticate
+  """
 
-  arm.util.torTools.getConn().init(controller)
+  try:
+    controller.authenticate(password = password, chroot_path = chroot_path)
+  except stem.connection.IncorrectSocketType:
+    control_socket = controller.get_socket()
+
+    if isinstance(control_socket, stem.socket.ControlPort):
+      raise ValueError(msg('connect.wrong_port_type', port = control_socket.get_port()))
+    else:
+      raise ValueError(msg('connect.wrong_socket_type'))
+  except stem.connection.UnrecognizedAuthMethods as exc:
+    raise ValueError(msg('uncrcognized_auth_type', auth_methods = ', '.join(exc.unknown_auth_methods)))
+  except stem.connection.IncorrectPassword:
+    raise ValueError(msg('connect.incorrect_password'))
+  except stem.connection.MissingPassword:
+    if password:
+      raise ValueError(msg('connect.missing_password_bug'))
+
+    password = getpass.getpass(msg('connect.password_prompt') + ' ')
+    return authenticate(controller, password)
+  except stem.connection.UnreadableCookieFile as exc:
+    raise ValueError(msg('connect.unreadable_cookie_file', path = exc.cookie_path, issue = str(exc)))
+  except stem.connection.AuthenticationFailure as exc:
+    raise ValueError(msg('connect.general_auth_failure', error = exc))
 
 
 def msg(message, **attr):
@@ -116,3 +157,29 @@ def _log(runlevel, message, **attr):
   """
 
   stem.util.log.log(runlevel, msg(message, **attr))
+
+
+def _get_controller(args):
+  """
+  Provides a Controller for the endpoint specified in the given arguments.
+  """
+
+  if os.path.exists(args.control_socket):
+    try:
+      return stem.control.Controller.from_socket_file(args.control_socket)
+    except stem.SocketError as exc:
+      if args.user_provided_socket:
+        raise ValueError(msg('connect.unable_to_use_socket', path = args.control_socket, error = exc))
+  elif args.user_provided_socket:
+    raise ValueError(msg('connect.socket_doesnt_exist', path = args.control_socket))
+
+  try:
+    return stem.control.Controller.from_port(args.control_address, args.control_port)
+  except stem.SocketError as exc:
+    if args.user_provided_port:
+      raise ValueError(msg('connect.unable_to_use_port', address = args.control_address, port = args.control_port, error = exc))
+
+  if not stem.util.system.is_running('tor'):
+    raise ValueError(msg('connect.tor_isnt_running'))
+  else:
+    raise ValueError(msg('connect.no_control_port'))
diff --git a/test/starter/__init__.py b/test/starter/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/test/starter/authenticate.py b/test/starter/authenticate.py
deleted file mode 100644
index 1b228b5..0000000
--- a/test/starter/authenticate.py
+++ /dev/null
@@ -1,79 +0,0 @@
-import unittest
-
-from mock import Mock, patch
-
-from arm.starter import (
-  _get_controller,
-  _authenticate,
-)
-
-import stem
-import stem.connection
-import stem.socket
-import stem.util.conf
-
-
-class TestAuthenticate(unittest.TestCase):
-  def setUp(self):
-    arm_conf = stem.util.conf.get_config('arm')
-    arm_conf.set('tor.chroot', '')  # no chroot
-
-  def test_success(self):
-    controller = Mock()
-
-    _authenticate(controller, None)
-    controller.authenticate.assert_called_with(password = None, chroot_path = '')
-    controller.authenticate.reset_mock()
-
-    stem.util.conf.get_config('arm').set('tor.chroot', '/my/chroot')
-    _authenticate(controller, 's3krit!!!')
-    controller.authenticate.assert_called_with(password = 's3krit!!!', chroot_path = '/my/chroot')
-
-  @patch('getpass.getpass')
-  def test_success_with_password_prompt(self, getpass_mock):
-    controller = Mock()
-
-    def authenticate_mock(password, **kwargs):
-      if password is None:
-        raise stem.connection.MissingPassword('no password')
-      elif password == 'my_password':
-        return None  # success
-      else:
-        raise ValueError("Unexpected authenticate_mock input: %s" % password)
-
-    controller.authenticate.side_effect = authenticate_mock
-    getpass_mock.return_value = 'my_password'
-
-    _authenticate(controller, None)
-    controller.authenticate.assert_any_call(password = None, chroot_path = '')
-    controller.authenticate.assert_any_call(password = 'my_password', chroot_path = '')
-
-  def test_failure(self):
-    controller = Mock()
-
-    controller.authenticate.side_effect = stem.connection.IncorrectSocketType('unable to connect to socket')
-    controller.get_socket.return_value = stem.socket.ControlPort(connect = False)
-    self._assert_authenticate_fails_with(controller, 'Please check in your torrc that 9051 is the ControlPort.')
-
-    controller.get_socket.return_value = stem.socket.ControlSocketFile(connect = False)
-    self._assert_authenticate_fails_with(controller, 'Are you sure the interface you specified belongs to')
-
-    controller.authenticate.side_effect = stem.connection.UnrecognizedAuthMethods('unable to connect', ['telepathy'])
-    self._assert_authenticate_fails_with(controller, 'Tor is using a type of authentication we do not recognize...\n\n  telepathy')
-
-    controller.authenticate.side_effect = stem.connection.IncorrectPassword('password rejected')
-    self._assert_authenticate_fails_with(controller, 'Incorrect password')
-
-    controller.authenticate.side_effect = stem.connection.UnreadableCookieFile('permission denied', '/tmp/my_cookie', False)
-    self._assert_authenticate_fails_with(controller, "We were unable to read tor's authentication cookie...\n\n  Path: /tmp/my_cookie\n  Issue: permission denied")
-
-    controller.authenticate.side_effect = stem.connection.OpenAuthRejected('crazy failure')
-    self._assert_authenticate_fails_with(controller, 'Unable to authenticate: crazy failure')
-
-  def _assert_authenticate_fails_with(self, controller, msg):
-    try:
-      _get_controller(_authenticate(controller, None))
-      self.fail()
-    except ValueError, exc:
-      if not msg in str(exc):
-        self.fail("Expected...\n\n%s\n\n... which couldn't be found in...\n\n%s" % (msg, exc))
diff --git a/test/starter/get_controller.py b/test/starter/get_controller.py
deleted file mode 100644
index dcd5f0c..0000000
--- a/test/starter/get_controller.py
+++ /dev/null
@@ -1,67 +0,0 @@
-import unittest
-
-from mock import Mock, patch
-
-from arm.arguments import parse
-from arm.starter import _get_controller
-
-import stem
-import stem.connection
-import stem.socket
-
-
-class TestGetController(unittest.TestCase):
-  @patch('os.path.exists', Mock(return_value = True))
-  @patch('stem.util.system.is_running')
-  @patch('stem.control.Controller.from_socket_file', Mock(side_effect = stem.SocketError('failed')))
-  @patch('stem.control.Controller.from_port', Mock(side_effect = stem.SocketError('failed')))
-  def test_failue_with_the_default_endpoint(self, is_running_mock):
-    is_running_mock.return_value = False
-    self._assert_get_controller_fails_with([], "Unable to connect to tor. Are you sure it's running?")
-
-    is_running_mock.return_value = True
-    self._assert_get_controller_fails_with([], "Unable to connect to tor. Maybe it's running without a ControlPort?")
-
-  @patch('os.path.exists')
-  @patch('stem.util.system.is_running', Mock(return_value = True))
-  @patch('stem.control.Controller.from_socket_file', Mock(side_effect = stem.SocketError('failed')))
-  @patch('stem.control.Controller.from_port', Mock(side_effect = stem.SocketError('failed')))
-  def test_failure_with_a_custom_endpoint(self, path_exists_mock):
-    path_exists_mock.return_value = True
-    self._assert_get_controller_fails_with(['--interface', '80'], "Unable to connect to 127.0.0.1:80: failed")
-    self._assert_get_controller_fails_with(['--socket', '/tmp/my_socket'], "Unable to connect to '/tmp/my_socket': failed")
-
-    path_exists_mock.return_value = False
-    self._assert_get_controller_fails_with(['--interface', '80'], "Unable to connect to 127.0.0.1:80: failed")
-    self._assert_get_controller_fails_with(['--socket', '/tmp/my_socket'], "The socket file you specified (/tmp/my_socket) doesn't exist")
-
-  @patch('os.path.exists', Mock(return_value = False))
-  @patch('stem.control.Controller.from_port')
-  def test_getting_a_control_port(self, from_port_mock):
-    from_port_mock.return_value = 'success'
-
-    self.assertEqual('success', _get_controller(parse([])))
-    from_port_mock.assert_called_once_with('127.0.0.1', 9051)
-    from_port_mock.reset_mock()
-
-    self.assertEqual('success', _get_controller(parse(['--interface', '255.0.0.10:80'])))
-    from_port_mock.assert_called_once_with('255.0.0.10', 80)
-
-  @patch('os.path.exists', Mock(return_value = True))
-  @patch('stem.control.Controller.from_socket_file')
-  def test_getting_a_control_socket(self, from_socket_file_mock):
-    from_socket_file_mock.return_value = 'success'
-
-    self.assertEqual('success', _get_controller(parse([])))
-    from_socket_file_mock.assert_called_once_with('/var/run/tor/control')
-    from_socket_file_mock.reset_mock()
-
-    self.assertEqual('success', _get_controller(parse(['--socket', '/tmp/my_socket'])))
-    from_socket_file_mock.assert_called_once_with('/tmp/my_socket')
-
-  def _assert_get_controller_fails_with(self, args, msg):
-    try:
-      _get_controller(parse(args))
-      self.fail()
-    except ValueError, exc:
-      self.assertEqual(msg, str(exc))
diff --git a/test/util/__init__.py b/test/util/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/test/util/authenticate.py b/test/util/authenticate.py
new file mode 100644
index 0000000..11bd92b
--- /dev/null
+++ b/test/util/authenticate.py
@@ -0,0 +1,73 @@
+import unittest
+
+from mock import Mock, patch
+
+from arm.util import (
+  init_controller,
+  authenticate,
+)
+
+import stem
+import stem.connection
+import stem.socket
+
+
+class TestAuthenticate(unittest.TestCase):
+  def test_success(self):
+    controller = Mock()
+
+    authenticate(controller, None)
+    controller.authenticate.assert_called_with(password = None, chroot_path = '')
+    controller.authenticate.reset_mock()
+
+    authenticate(controller, 's3krit!!!', '/my/chroot')
+    controller.authenticate.assert_called_with(password = 's3krit!!!', chroot_path = '/my/chroot')
+
+  @patch('getpass.getpass')
+  def test_success_with_password_prompt(self, getpass_mock):
+    controller = Mock()
+
+    def authenticate_mock(password, **kwargs):
+      if password is None:
+        raise stem.connection.MissingPassword('no password')
+      elif password == 'my_password':
+        return None  # success
+      else:
+        raise ValueError("Unexpected authenticate_mock input: %s" % password)
+
+    controller.authenticate.side_effect = authenticate_mock
+    getpass_mock.return_value = 'my_password'
+
+    authenticate(controller, None)
+    controller.authenticate.assert_any_call(password = None, chroot_path = '')
+    controller.authenticate.assert_any_call(password = 'my_password', chroot_path = '')
+
+  def test_failure(self):
+    controller = Mock()
+
+    controller.authenticate.side_effect = stem.connection.IncorrectSocketType('unable to connect to socket')
+    controller.get_socket.return_value = stem.socket.ControlPort(connect = False)
+    self._assert_authenticate_fails_with(controller, 'Please check in your torrc that 9051 is the ControlPort.')
+
+    controller.get_socket.return_value = stem.socket.ControlSocketFile(connect = False)
+    self._assert_authenticate_fails_with(controller, 'Are you sure the interface you specified belongs to')
+
+    controller.authenticate.side_effect = stem.connection.UnrecognizedAuthMethods('unable to connect', ['telepathy'])
+    self._assert_authenticate_fails_with(controller, 'Tor is using a type of authentication we do not recognize...\n\n  telepathy')
+
+    controller.authenticate.side_effect = stem.connection.IncorrectPassword('password rejected')
+    self._assert_authenticate_fails_with(controller, 'Incorrect password')
+
+    controller.authenticate.side_effect = stem.connection.UnreadableCookieFile('permission denied', '/tmp/my_cookie', False)
+    self._assert_authenticate_fails_with(controller, "We were unable to read tor's authentication cookie...\n\n  Path: /tmp/my_cookie\n  Issue: permission denied")
+
+    controller.authenticate.side_effect = stem.connection.OpenAuthRejected('crazy failure')
+    self._assert_authenticate_fails_with(controller, 'Unable to authenticate: crazy failure')
+
+  def _assert_authenticate_fails_with(self, controller, msg):
+    try:
+      init_controller(authenticate(controller, None))
+      self.fail()
+    except ValueError, exc:
+      if not msg in str(exc):
+        self.fail("Expected...\n\n%s\n\n... which couldn't be found in...\n\n%s" % (msg, exc))
diff --git a/test/util/init_controller.py b/test/util/init_controller.py
new file mode 100644
index 0000000..a106a06
--- /dev/null
+++ b/test/util/init_controller.py
@@ -0,0 +1,67 @@
+import unittest
+
+from mock import Mock, patch
+
+from arm.arguments import parse
+from arm.util import init_controller
+
+import stem
+import stem.connection
+import stem.socket
+
+
+class TestGetController(unittest.TestCase):
+  @patch('os.path.exists', Mock(return_value = True))
+  @patch('stem.util.system.is_running')
+  @patch('stem.control.Controller.from_socket_file', Mock(side_effect = stem.SocketError('failed')))
+  @patch('stem.control.Controller.from_port', Mock(side_effect = stem.SocketError('failed')))
+  def test_failue_with_the_default_endpoint(self, is_running_mock):
+    is_running_mock.return_value = False
+    self._assert_init_controller_fails_with([], "Unable to connect to tor. Are you sure it's running?")
+
+    is_running_mock.return_value = True
+    self._assert_init_controller_fails_with([], "Unable to connect to tor. Maybe it's running without a ControlPort?")
+
+  @patch('os.path.exists')
+  @patch('stem.util.system.is_running', Mock(return_value = True))
+  @patch('stem.control.Controller.from_socket_file', Mock(side_effect = stem.SocketError('failed')))
+  @patch('stem.control.Controller.from_port', Mock(side_effect = stem.SocketError('failed')))
+  def test_failure_with_a_custom_endpoint(self, path_exists_mock):
+    path_exists_mock.return_value = True
+    self._assert_init_controller_fails_with(['--interface', '80'], "Unable to connect to 127.0.0.1:80: failed")
+    self._assert_init_controller_fails_with(['--socket', '/tmp/my_socket'], "Unable to connect to '/tmp/my_socket': failed")
+
+    path_exists_mock.return_value = False
+    self._assert_init_controller_fails_with(['--interface', '80'], "Unable to connect to 127.0.0.1:80: failed")
+    self._assert_init_controller_fails_with(['--socket', '/tmp/my_socket'], "The socket file you specified (/tmp/my_socket) doesn't exist")
+
+  @patch('os.path.exists', Mock(return_value = False))
+  @patch('stem.control.Controller.from_port')
+  def test_getting_a_control_port(self, from_port_mock):
+    from_port_mock.return_value = 'success'
+
+    self.assertEqual('success', init_controller(parse([])))
+    from_port_mock.assert_called_once_with('127.0.0.1', 9051)
+    from_port_mock.reset_mock()
+
+    self.assertEqual('success', init_controller(parse(['--interface', '255.0.0.10:80'])))
+    from_port_mock.assert_called_once_with('255.0.0.10', 80)
+
+  @patch('os.path.exists', Mock(return_value = True))
+  @patch('stem.control.Controller.from_socket_file')
+  def test_getting_a_control_socket(self, from_socket_file_mock):
+    from_socket_file_mock.return_value = 'success'
+
+    self.assertEqual('success', init_controller(parse([])))
+    from_socket_file_mock.assert_called_once_with('/var/run/tor/control')
+    from_socket_file_mock.reset_mock()
+
+    self.assertEqual('success', init_controller(parse(['--socket', '/tmp/my_socket'])))
+    from_socket_file_mock.assert_called_once_with('/tmp/my_socket')
+
+  def _assert_init_controller_fails_with(self, args, msg):
+    try:
+      init_controller(parse(args))
+      self.fail()
+    except ValueError, exc:
+      self.assertEqual(msg, str(exc))



More information about the tor-commits mailing list