Ejemplo n.º 1
0
    def test_all_use_cases(self, authenticate_safecookie_mock,
                           authenticate_cookie_mock,
                           authenticate_password_mock, authenticate_none_mock):
        """
    Does basic validation that all valid use cases for the PROTOCOLINFO input
    and dependent functions result in either success or a AuthenticationFailed
    subclass being raised.
    """

        # mute the logger for this test since otherwise the output is overwhelming

        stem_logger = log.get_logger()
        stem_logger.setLevel(log.logging_level(None))

        # exceptions that the authentication functions are documented to raise

        all_auth_none_exc = (None, stem.connection.OpenAuthRejected(None))

        all_auth_password_exc = (None,
                                 stem.connection.PasswordAuthRejected(None),
                                 stem.connection.IncorrectPassword(None))

        all_auth_cookie_exc = (
            None, stem.connection.IncorrectCookieSize(None, False, None),
            stem.connection.UnreadableCookieFile(None, False, None),
            stem.connection.CookieAuthRejected(None, False, None),
            stem.connection.IncorrectCookieValue(None, False, None),
            stem.connection.UnrecognizedAuthChallengeMethod(None, None, None),
            stem.connection.AuthChallengeFailed(None, None),
            stem.connection.AuthSecurityFailure(None, None),
            stem.connection.InvalidClientNonce(None, None))

        # authentication functions might raise a controller error when
        # 'suppress_ctl_errors' is False, so including those

        control_exc = (stem.ProtocolError(None), stem.SocketError(None),
                       stem.SocketClosed(None))

        all_auth_none_exc += control_exc
        all_auth_password_exc += control_exc
        all_auth_cookie_exc += control_exc

        auth_method_combinations = mocking.get_all_combinations(
            [
                stem.connection.AuthMethod.NONE,
                stem.connection.AuthMethod.PASSWORD,
                stem.connection.AuthMethod.COOKIE,
                stem.connection.AuthMethod.SAFECOOKIE,
                stem.connection.AuthMethod.UNKNOWN,
            ],
            include_empty=True)

        for protocolinfo_auth_methods in auth_method_combinations:
            # protocolinfo input for the authenticate() call we'll be making
            protocolinfo_arg = mocking.get_protocolinfo_response(
                auth_methods=protocolinfo_auth_methods,
                cookie_path='/tmp/blah',
            )

            for auth_none_exc in all_auth_none_exc:
                for auth_password_exc in all_auth_password_exc:
                    for auth_cookie_exc in all_auth_cookie_exc:
                        # Determine if the authenticate() call will succeed and mock each
                        # of the authenticate_* function to raise its given exception.
                        #
                        # This implementation is slightly inaccurate in a couple regards...
                        # a. it raises safecookie exceptions from authenticate_cookie()
                        # b. exceptions raised by authenticate_cookie() and
                        #    authenticate_safecookie() are always the same
                        #
                        # However, adding another loop for safe_cookie exceptions means
                        # multiplying our runtime many fold. This exercises everything that
                        # matters so the above inaccuracies seem fine.

                        expect_success = False
                        auth_mocks = {
                            stem.connection.AuthMethod.NONE:
                            (authenticate_none_mock, auth_none_exc),
                            stem.connection.AuthMethod.PASSWORD:
                            (authenticate_password_mock, auth_password_exc),
                            stem.connection.AuthMethod.COOKIE:
                            (authenticate_cookie_mock, auth_cookie_exc),
                            stem.connection.AuthMethod.SAFECOOKIE:
                            (authenticate_safecookie_mock, auth_cookie_exc),
                        }

                        for auth_method in auth_mocks:
                            auth_mock, raised_exc = auth_mocks[auth_method]

                            if not raised_exc:
                                # Mocking this authentication method so it will succeed. If
                                # it's among the protocolinfo methods then expect success.

                                auth_mock.side_effect = None
                                expect_success |= auth_method in protocolinfo_auth_methods
                            else:
                                auth_mock.side_effect = raised_exc

                        if expect_success:
                            stem.connection.authenticate(
                                None, 'blah', None, protocolinfo_arg)
                        else:
                            self.assertRaises(
                                stem.connection.AuthenticationFailure,
                                stem.connection.authenticate, None, 'blah',
                                None, protocolinfo_arg)

        # revert logging back to normal
        stem_logger.setLevel(log.logging_level(log.TRACE))
Ejemplo n.º 2
0
    def test_all_use_cases(self, authenticate_safecookie_mock,
                           authenticate_cookie_mock,
                           authenticate_password_mock, authenticate_none_mock):
        """
    Does basic validation that all valid use cases for the PROTOCOLINFO input
    and dependent functions result in either success or a AuthenticationFailed
    subclass being raised.
    """

        # mute the logger for this test since otherwise the output is overwhelming

        stem_logger = log.get_logger()
        stem_logger.setLevel(log.logging_level(None))

        # exceptions that the authentication functions are documented to raise

        all_auth_none_exc = (None, stem.connection.OpenAuthRejected(None),
                             stem.ControllerError(None))

        all_auth_password_exc = (None,
                                 stem.connection.PasswordAuthRejected(None),
                                 stem.connection.IncorrectPassword(None),
                                 stem.ControllerError(None))

        all_auth_cookie_exc = (None,
                               stem.connection.CookieAuthRejected(
                                   None, False, None),
                               stem.connection.IncorrectCookieValue(
                                   None, False, None),
                               stem.connection.IncorrectCookieSize(
                                   None, False, None),
                               stem.connection.UnreadableCookieFile(
                                   None, False, None),
                               stem.connection.AuthChallengeFailed(None, None),
                               stem.ControllerError(None))

        auth_method_combinations = test.get_all_combinations(
            [
                stem.connection.AuthMethod.NONE,
                stem.connection.AuthMethod.PASSWORD,
                stem.connection.AuthMethod.COOKIE,
                stem.connection.AuthMethod.SAFECOOKIE,
                stem.connection.AuthMethod.UNKNOWN,
            ],
            include_empty=True)

        protocolinfo = ControlMessage.from_str(
            '250-PROTOCOLINFO 1\r\n250 OK\r\n', 'PROTOCOLINFO')
        protocolinfo.cookie_path = '/tmp/blah'

        for auth_methods in auth_method_combinations:
            for auth_none_exc in all_auth_none_exc:
                for auth_password_exc in all_auth_password_exc:
                    for auth_cookie_exc in all_auth_cookie_exc:
                        # Skip iteration if it's to test exceptions for authentication
                        # we're not using.

                        if auth_none_exc and stem.connection.AuthMethod.NONE not in auth_methods:
                            continue
                        elif auth_password_exc and stem.connection.AuthMethod.PASSWORD not in auth_methods:
                            continue
                        elif auth_cookie_exc and stem.connection.AuthMethod.COOKIE not in auth_methods and stem.connection.AuthMethod.SAFECOOKIE not in auth_methods:
                            continue

                        # Determine if the authenticate() call will succeed and mock each
                        # of the authenticate_* function to raise its given exception.
                        #
                        # This implementation is slightly inaccurate in a couple regards...
                        # a. it raises safecookie exceptions from authenticate_cookie()
                        # b. exceptions raised by authenticate_cookie() and
                        #    authenticate_safecookie() are always the same
                        #
                        # However, adding another loop for safe_cookie exceptions means
                        # multiplying our runtime many fold. This exercises everything that
                        # matters so the above inaccuracies seem fine.

                        expect_success = False
                        protocolinfo.auth_methods = auth_methods

                        for auth_method in auth_methods:
                            if auth_method == stem.connection.AuthMethod.NONE:
                                auth_mock, raised_exc = authenticate_none_mock, auth_none_exc
                            elif auth_method == stem.connection.AuthMethod.PASSWORD:
                                auth_mock, raised_exc = authenticate_password_mock, auth_password_exc
                            elif auth_method == stem.connection.AuthMethod.COOKIE:
                                auth_mock, raised_exc = authenticate_cookie_mock, auth_cookie_exc
                            elif auth_method == stem.connection.AuthMethod.SAFECOOKIE:
                                auth_mock, raised_exc = authenticate_safecookie_mock, auth_cookie_exc

                            if raised_exc:
                                auth_mock.side_effect = raised_exc
                            else:
                                auth_mock.side_effect = None
                                expect_success = True

                        if expect_success:
                            stem.connection.authenticate(
                                None, 'blah', None, protocolinfo)
                        else:
                            self.assertRaises(
                                stem.connection.AuthenticationFailure,
                                stem.connection.authenticate, None, 'blah',
                                None, protocolinfo)

        # revert logging back to normal
        stem_logger.setLevel(log.logging_level(log.TRACE))
Ejemplo n.º 3
0
 def test_all_use_cases(self):
   """
   Does basic validation that all valid use cases for the PROTOCOLINFO input
   and dependent functions result in either success or a AuthenticationFailed
   subclass being raised.
   """
   
   # mute the logger for this test since otherwise the output is overwhelming
   
   stem_logger = log.get_logger()
   stem_logger.setLevel(log.logging_level(None))
   
   # exceptions that the authentication functions are documented to raise
   all_auth_none_exc = (None,
     stem.connection.OpenAuthRejected(None))
   
   all_auth_password_exc = (None,
     stem.connection.PasswordAuthRejected(None),
     stem.connection.IncorrectPassword(None))
   
   all_auth_cookie_exc = (None,
     stem.connection.IncorrectCookieSize(None, None),
     stem.connection.UnreadableCookieFile(None, None),
     stem.connection.CookieAuthRejected(None, None),
     stem.connection.IncorrectCookieValue(None, None))
   
   # authentication functions might raise a controller error when
   # 'suppress_ctl_errors' is False, so including those
   
   control_exc = (
     stem.socket.ProtocolError(None),
     stem.socket.SocketError(None),
     stem.socket.SocketClosed(None))
   
   all_auth_none_exc += control_exc
   all_auth_password_exc += control_exc
   all_auth_cookie_exc += control_exc
   
   for protocolinfo_auth_methods in _get_all_auth_method_combinations():
     # protocolinfo input for the authenticate() call we'll be making
     protocolinfo_arg = mocking.get_protocolinfo_response(
       auth_methods = protocolinfo_auth_methods,
       cookie_path = "/tmp/blah",
     )
     
     for auth_none_exc in all_auth_none_exc:
       for auth_password_exc in all_auth_password_exc:
         for auth_cookie_exc in all_auth_cookie_exc:
           # determine if the authenticate() call will succeed and mock each
           # of the authenticate_* function to raise its given exception
           
           expect_success = False
           auth_mocks = {
             stem.connection.AuthMethod.NONE:
               (stem.connection.authenticate_none, auth_none_exc),
             stem.connection.AuthMethod.PASSWORD:
               (stem.connection.authenticate_password, auth_password_exc),
             stem.connection.AuthMethod.COOKIE:
               (stem.connection.authenticate_cookie, auth_cookie_exc),
           }
           
           for auth_method in auth_mocks:
             auth_function, raised_exc = auth_mocks[auth_method]
             
             if not raised_exc:
               # Mocking this authentication method so it will succeed. If
               # it's among the protocolinfo methods then expect success.
               
               mocking.mock(auth_function, mocking.no_op())
               expect_success |= auth_method in protocolinfo_auth_methods
             else:
               mocking.mock(auth_function, mocking.raise_exception(raised_exc))
           
           if expect_success:
             stem.connection.authenticate(None, "blah", None, protocolinfo_arg)
           else:
             self.assertRaises(stem.connection.AuthenticationFailure, stem.connection.authenticate, None, "blah", None, protocolinfo_arg)
   
   # revert logging back to normal
   stem_logger.setLevel(log.logging_level(log.TRACE))
Ejemplo n.º 4
0
  def test_all_use_cases(self):
    """
    Does basic validation that all valid use cases for the PROTOCOLINFO input
    and dependent functions result in either success or a AuthenticationFailed
    subclass being raised.
    """

    # mute the logger for this test since otherwise the output is overwhelming

    stem_logger = log.get_logger()
    stem_logger.setLevel(log.logging_level(None))

    # exceptions that the authentication functions are documented to raise

    all_auth_none_exc = (None, stem.connection.OpenAuthRejected(None))

    all_auth_password_exc = (
      None,
      stem.connection.PasswordAuthRejected(None),
      stem.connection.IncorrectPassword(None))

    all_auth_cookie_exc = (
      None,
      stem.connection.IncorrectCookieSize(None, False, None),
      stem.connection.UnreadableCookieFile(None, False, None),
      stem.connection.CookieAuthRejected(None, False, None),
      stem.connection.IncorrectCookieValue(None, False, None),
      stem.connection.UnrecognizedAuthChallengeMethod(None, None, None),
      stem.connection.AuthChallengeFailed(None, None),
      stem.connection.AuthSecurityFailure(None, None),
      stem.connection.InvalidClientNonce(None, None))

    # authentication functions might raise a controller error when
    # 'suppress_ctl_errors' is False, so including those

    control_exc = (
      stem.ProtocolError(None),
      stem.SocketError(None),
      stem.SocketClosed(None))

    all_auth_none_exc += control_exc
    all_auth_password_exc += control_exc
    all_auth_cookie_exc += control_exc

    auth_method_combinations = mocking.get_all_combinations([
      stem.connection.AuthMethod.NONE,
      stem.connection.AuthMethod.PASSWORD,
      stem.connection.AuthMethod.COOKIE,
      stem.connection.AuthMethod.SAFECOOKIE,
      stem.connection.AuthMethod.UNKNOWN,
    ], include_empty = True)

    for protocolinfo_auth_methods in auth_method_combinations:
      # protocolinfo input for the authenticate() call we'll be making
      protocolinfo_arg = mocking.get_protocolinfo_response(
        auth_methods = protocolinfo_auth_methods,
        cookie_path = "/tmp/blah",
      )

      for auth_none_exc in all_auth_none_exc:
        for auth_password_exc in all_auth_password_exc:
          for auth_cookie_exc in all_auth_cookie_exc:
            # Determine if the authenticate() call will succeed and mock each
            # of the authenticate_* function to raise its given exception.
            #
            # This implementation is slightly inaccurate in a couple regards...
            # a. it raises safecookie exceptions from authenticate_cookie()
            # b. exceptions raised by authenticate_cookie() and
            #    authenticate_safecookie() are always the same
            #
            # However, adding another loop for safe_cookie exceptions means
            # multiplying our runtime many fold. This exercises everything that
            # matters so the above inaccuracies seem fine.

            expect_success = False
            auth_mocks = {
              stem.connection.AuthMethod.NONE:
                (stem.connection.authenticate_none, auth_none_exc),
              stem.connection.AuthMethod.PASSWORD:
                (stem.connection.authenticate_password, auth_password_exc),
              stem.connection.AuthMethod.COOKIE:
                (stem.connection.authenticate_cookie, auth_cookie_exc),
              stem.connection.AuthMethod.SAFECOOKIE:
                (stem.connection.authenticate_safecookie, auth_cookie_exc),
            }

            for auth_method in auth_mocks:
              auth_function, raised_exc = auth_mocks[auth_method]

              if not raised_exc:
                # Mocking this authentication method so it will succeed. If
                # it's among the protocolinfo methods then expect success.

                mocking.mock(auth_function, mocking.no_op())
                expect_success |= auth_method in protocolinfo_auth_methods
              else:
                mocking.mock(auth_function, mocking.raise_exception(raised_exc))

            if expect_success:
              stem.connection.authenticate(None, "blah", None, protocolinfo_arg)
            else:
              self.assertRaises(stem.connection.AuthenticationFailure, stem.connection.authenticate, None, "blah", None, protocolinfo_arg)

    # revert logging back to normal
    stem_logger.setLevel(log.logging_level(log.TRACE))
Ejemplo n.º 5
0
 def __init__(self):
     self._tor_lock = True
     
     # mute the logger for this test since otherwise the output is overwhelming
     stem_logger = log.get_logger()
     stem_logger.setLevel(log.logging_level(None))
Ejemplo n.º 6
0
    def __init__(self, config: dict):
        self.config = utils.AttributedDict(config)
        self.config['stamped_version'] = stamped_version

        #####
        # Host System data
        # from ..system import get_system_manager
        self.system = get_system_manager()

        #####
        # Logging System: Part 1

        self.log = logging.getLogger('theonionbox')

        # We will Filter everything through the GlobalFilter
        # NOTSET + 1 just ensures that there IS a LEVEL set, even if we don't rely on it!
        self.log.setLevel(logging.NOTSET + 1)
        boxLogGF = log.getGlobalFilter()
        boxLogGF.setLevel('NOTICE')
        self.log.addFilter(boxLogGF)

        # This is the handler to output messages to stdout on the host
        # If daemonized, stdout will be re-routed to syslog.
        # Optionally messages will be sent to a directory (if advised to do so via command line)
        boxLogHandler = logging.StreamHandler(sys.stdout)

        if os.getenv('PYCHARM_RUNNING_TOB', None) == '1':
            boxLogHandler.setFormatter(log.PyCharmFormatter())
        elif sys.stdout.isatty():
            boxLogHandler.setFormatter(log.ConsoleFormatter())
        else:
            boxLogHandler.setFormatter(log.LogFormatter())

        self.log.addHandler(boxLogHandler)

        # We need to buffer some warnings here...
        warn = []

        # Optional LogFile handler; can be invoked (only) from command line
        if self.config.log is not None:

            from logging.handlers import TimedRotatingFileHandler

            boxLogPath = self.config.log

            if os.access(boxLogPath, os.F_OK | os.W_OK | os.X_OK) is True:
                try:
                    boxLogPath = os.path.join(boxLogPath, 'theonionbox.log')
                    boxLogFileHandler = TimedRotatingFileHandler(
                        boxLogPath, when='midnight', backupCount=5)
                except Exception as exc:
                    warn.append(f'Failed to create LogFile handler: {exc}')
                else:
                    boxLogFileHandler.setFormatter(log.FileFormatter())
                    self.log.addHandler(boxLogFileHandler)
            else:
                warn.append(
                    f"Failed to establish LogFile handler for directory '{self.config['log']}'."
                )

        #####
        # Say Hello to the World!

        self.log.notice(f'{stamp.__title__}: {stamp.__description__}')
        self.log.notice(f'Version {stamped_version}')
        self.log.notice(f'Running on a {self.system.system} host.')
        if self.system.user is not None:
            self.log.notice(
                f"Running with permissions of user '{self.system.user}'.")
        if self.system.venv is not None:
            self.log.notice('This seems to be a Python VirtualEnv.')
        if sys.executable:
            self.log.notice('Python version is {}.{}.{} ({}).'.format(
                sys.version_info.major, sys.version_info.minor,
                sys.version_info.micro, sys.executable))
        else:
            self.log.notice('Python version is {}.{}.{}.'.format(
                sys.version_info.major, sys.version_info.minor,
                sys.version_info.micro))

        for w in warn:
            self.log.warning(w)

        if py36 is False:
            self.log.error(
                "The Onion Box demands Python version 3.6 or higher to run. Please upgrade."
            )
            self.log.notice(
                "If you're unable to upgrade your Python version, please use version 4.x of The Onion Box."
            )
            sys.exit()

        #####
        # Logging System: Part 2 -> Trace or Debug

        # This is the Handler to connect stem with boxLog
        from stem.util.log import get_logger as get_stem_logger, logging_level, Runlevel
        stemLog = get_stem_logger()
        stemLog.setLevel(logging_level(Runlevel.DEBUG))  # we log DEBUG...

        # Bottle logging
        bottle._stderr = self.log.debug
        bottle._stdout = self.log.trace

        if self.config.trace:
            boxLogGF.setLevel('TRACE')
            self.log.notice('Trace Mode activated from command line.')

            bottle.debug(True)
            # Part 3 -> add_hook for PATH_INFO (around line 320)

            # stem: Forward DEBUG
            stemFwrd = log.ForwardHandler(level=logging_level(Runlevel.DEBUG),
                                          tag='stem')

        elif self.config.debug:
            boxLogGF.setLevel('DEBUG')
            self.log.notice('Debug Mode activated from command line.')

            bottle.debug(True)

            # stem: Forward NOTICE
            stemFwrd = log.ForwardHandler(level=logging_level(Runlevel.NOTICE),
                                          tag='stem')

        else:
            bottle.debug(False)

            # stem: Forward WARNING
            stemFwrd = log.ForwardHandler(level=logging_level(Runlevel.WARN),
                                          tag='stem')

        stemLog.addHandler(stemFwrd)
        stemFwrd.setTarget(self.log)

        #####
        # Data persistance management
        # ... has to be setup here to prepare for self.nodes
        from .persistor import Storage
        self.storage = Storage(self.config.box.persistance_dir,
                               self.system.user)

        #####
        # SOCKS Proxy definition
        from .proxy import Proxy
        from .config import ProxyConfig
        self.proxy = Proxy(ProxyConfig(self.config.proxy))

        #####
        # The Onionoo Interface
        from .onionoo import getOnionoo
        # This creates __OnionooManager__

        OnionooManager = getOnionoo()
        OnionooManager.proxy = self.proxy
        # clients will now call getOnionoo and get the configured OOManager

        #####
        # Management of Nodes
        from .nodes import Manager as NodesManager
        from .config import DefaultNodeConfig
        self.nodes = NodesManager(DefaultNodeConfig(self.config.tor),
                                  database=self.storage)

        # #####
        # # GeoIP2 interface
        self.geoip2 = None

        if self.config.box.geoip2_city is not None:
            try:
                import geoip2
            except ImportError:
                self.log.warning(
                    "Usage of a GeoIP2 City database demands availability of Python module 'geoip2'."
                )
            else:
                if os.path.exists(self.config.box.geoip2_city):
                    from .geoip import GeoIP2
                    self.geoip2 = GeoIP2(self.config.box.geoip2_city)
                    self.log.notice(
                        "Operating with GeoIP Database '{}'.".format(
                            self.config.box.geoip2_city))

        if self.geoip2 is None:
            from .geoip import GeoIPOO
            self.geoip2 = GeoIPOO()

        #####
        # Our cron
        from .scheduler import Scheduler
        self.cron = Scheduler()

        # Check proper setting of Timezone
        # to compensate for a potential exception in the scheduler.
        # Thanks to Sergey (@senovr) for detecting this:
        # https://github.com/ralphwetzel/theonionbox/issues/19#issuecomment-263110953

        if self.cron.check_tz() is False:
            self.log.error(
                "Unable to determine the name of the local timezone. "
                "Please run 'tzinfo' to set it explicitely.")
            sys.exit(0)

        self.cron.start()

        #####
        # Time Management
        #
        from .deviation import getTimer

        self.time = getTimer(self.config.box.ntp_server or self.system.ntp)

        def update_time_deviation():

            if self.time.ntp is None:
                return

            ret_val = self.time.update_time_deviation()

            if ret_val is False:
                self.log.warning(
                    "Failed to communicate to NTP-Server '{}'!".format(
                        self.time.ntp_server))
            else:
                self.log.notice(
                    "Server Time aligned against Time from '{}'; adjusted delta: {:+.2f} seconds."
                    .format(self.time.ntp, ret_val))

        update_time_deviation()
        self.cron.add_job(update_time_deviation, 'interval', id='ntp', hours=8)

        #####
        # The Onion Box Version Service
        from .version import VersionManager
        from datetime import datetime

        def check_version(checker, relaunch_job=False):
            from random import randint

            if checker is None:
                return

            next_run = None

            if checker.update() is True:
                self.log.info('Latest version of The Onion Box: {}'.format(
                    checker.Box.latest_version()))

                if relaunch_job is True:
                    next_run = randint(30 * 60, 60 * 60)

            else:
                next_run = randint(15, 60)

            # TODO: This is a bit over-complicated; simplify?
            if next_run is not None:
                run_date = datetime.fromtimestamp(int(self.time()) + next_run)
                self.cron.add_job(check_version,
                                  'date',
                                  id='updates',
                                  run_date=run_date,
                                  args=[checker, True])

        self.version = VersionManager(self.proxy, stamp.__stamp__
                                      or stamp.__version__, self.system.system,
                                      self.system.release)
        check_version(self.version, True)

        #####
        # SESSION Management
        # from tob.session import SessionFactory, make_short_id
        from .session import SessionManager, make_short_id

        # This function is called when a session is deleted.
        # We use it to ensure that the nodes for this session are closed properly!
        def del_session_callback(session_id):
            #TODO 29100815: Necessary?
            return

            if session_id in boxNodes_old:
                for key, node in boxNodes_old[session_id].items():
                    node.shutdown()
                del boxNodes_old[session_id]
            return

        self.sessions = SessionManager(
            30, delete_session_callback=del_session_callback)

        #####
        # Recording of ...
        # ... the CPU Load, Memory Load & Temperature
        self.cron.add_job(self.system.record_performance_data,
                          'interval',
                          seconds=1)

        #####
        # Almost done; lets compose the app...

        # Single node dashboard = default application
        from .apps import Dashboard
        theonionbox = Dashboard(sessions=self.sessions,
                                nodes=self.nodes,
                                proxy=self.proxy,
                                version=self.version,
                                config=self.config,
                                system=self.system,
                                geoip=self.geoip2)

        # Controlcenter
        if self.config.cc is not None:

            from .apps import ControlCenter
            try:
                cc = ControlCenter(self.sessions, self.nodes, self.proxy,
                                   self.version, self.config)
            except Exception as exc:
                self.log.warning(f"Failed to launch ControlCenter: {exc}")
            else:
                theonionbox.merge(cc.routes)

                # Change default page
                theonionbox.default_page = 'cc.html'

        #####
        # Logging System: Part 3

        def debug_request():
            self.log.debug(bottle.request.environ['PATH_INFO'])

        if self.config.trace:
            # Log connection requests...
            theonionbox.app.add_hook('before_request', debug_request)

        # Static files - which are many meanwhile
        from .static import SessionFileProvider

        boxLibsPath = Path(self.config['cwd']) / 'libs'

        libs = SessionFileProvider(
            self.sessions,
            boxLibsPath / 'jquery-3.4.1' / 'jquery-3.4.1.min.js', '/jquery.js')

        libs.add(
            boxLibsPath / 'bootstrap-4.3.1' / 'js' / 'bootstrap.bundle.min.js',
            '/bootstrap.js')
        libs.add(boxLibsPath / 'bootstrap-4.3.1' / 'css' / 'bootstrap.min.css',
                 '/bootstrap.css')

        libs.add(boxLibsPath / 'glide-3.4.1' / 'dist' / 'glide.js',
                 '/glide.js')
        libs.add(
            boxLibsPath / 'glide-3.4.1' / 'dist' / 'css' / 'glide.core.css',
            '/glide.core.css')
        libs.add(
            boxLibsPath / 'glide-3.4.1' / 'dist' / 'css' / 'glide.theme.css',
            '/glide.theme.css')

        libs.add(boxLibsPath / 'scrollMonitor-1.2.4' / 'scrollMonitor.js',
                 '/scrollMonitor.js')

        libs.add(boxLibsPath / 'smoothie-1.36' / 'smoothie.js', '/smoothie.js')

        # libs.add(boxLibsPath / 'underscore-1.9.1' / 'underscore-min.js', '/underscore.js')

        libs.add(boxLibsPath / 'js-md5-0.7.3' / 'md5.js', '/md5.js')

        libs.add(boxLibsPath / 'jquery.pep-0.6.10' / 'jquery.pep.js',
                 '/pep.js')

        # libs.add(boxLibsPath / 'toggle-3.5.0' / 'js' / 'bootstrap4-toggle.min.js', '/toggle.js')
        # libs.add(boxLibsPath / 'toggle-3.5.0' / 'css' / 'bootstrap4-toggle.min.css', '/toggle.css')

        # libs.add(boxLibsPath / 'HackTimer-20181012' / 'HackTimer.js', '/hacktimer.js')

        # This is not really a library ... but the scheme works here as well.
        libs.add(Path(self.config['cwd']) / 'scripts' / 'chart.js', 'chart.js')

        theonionbox.merge(libs)

        # Those are static files as well - yet created by a template run & stored into the session object

        from .static import TemplateFileProvider

        templt = TemplateFileProvider(self.sessions, 'box.js', '/box.js')
        templt.add('box.css', '/box.css')
        templt.add('cc.js', '/cc.js')
        templt.add('cc.css', '/cc.css')
        templt.add('auth.js', '/auth.js')

        theonionbox.merge(templt)

        # LatoLatin
        from .libraries import LatoLatin
        libLatoLatin = LatoLatin(
            self.sessions,
            os.path.join(boxLibsPath, 'LatoLatin'),
            valid_status=['ok', 'auto', 'error', 'login', 'frame'])
        theonionbox.merge(libLatoLatin)

        # Fontawesome
        from .libraries import FontAwesome
        libFontAwesome = FontAwesome(self.sessions,
                                     os.path.join(
                                         boxLibsPath,
                                         'fontawesome-free-5.11.2-web'),
                                     valid_status='frame')
        theonionbox.merge(libFontAwesome)

        self.box = theonionbox.app

        #####
        # Our Web Server
        # This is a customization of the standard (v0.13) bottle.py CherootServer
        class CherootServer(bottle.ServerAdapter):
            def run(self, handler):  # pragma: no cover
                from cheroot import wsgi
                from cheroot.ssl import builtin
                self.options['bind_addr'] = (self.host, self.port)
                self.options['wsgi_app'] = handler
                certfile = self.options.pop('certfile', None)
                keyfile = self.options.pop('keyfile', None)
                chainfile = self.options.pop('chainfile', None)
                self.server = wsgi.Server(**self.options)
                if certfile and keyfile:
                    self.server.ssl_adapter = builtin.BuiltinSSLAdapter(
                        certfile, keyfile, chainfile)

                self.server.start()

            def shutdown(self):
                if self.server is not None:
                    self.server.stop()

        if self.config.box.ssl_key is not None:
            # SSL enabled
            self.server = CherootServer(
                host=self.config.box.host,
                port=self.config.box.port,
                certfile=self.config.box.ssl_certificate,
                keyfile=self.config.box.ssl_key)
            self.log.notice('Operating in SSL mode!')
        else:
            # Standard
            self.server = CherootServer(host=self.config.box.host,
                                        port=self.config.box.port)
Ejemplo n.º 7
0
 def __init__(self, app):
     self.app = app
     self.services = dict()
     self.tor_process = None
     self.socks_port_result = asyncio.Future()
     log.get_logger().level = log.logging_level(log.Runlevel.INFO)
Ejemplo n.º 8
0
  def test_all_use_cases(self, authenticate_safecookie_mock, authenticate_cookie_mock, authenticate_password_mock, authenticate_none_mock):
    """
    Does basic validation that all valid use cases for the PROTOCOLINFO input
    and dependent functions result in either success or a AuthenticationFailed
    subclass being raised.
    """

    # mute the logger for this test since otherwise the output is overwhelming

    stem_logger = log.get_logger()
    stem_logger.setLevel(log.logging_level(None))

    # exceptions that the authentication functions are documented to raise

    all_auth_none_exc = (
      None,
      stem.connection.OpenAuthRejected(None),
      stem.ControllerError(None))

    all_auth_password_exc = (
      None,
      stem.connection.PasswordAuthRejected(None),
      stem.connection.IncorrectPassword(None),
      stem.ControllerError(None))

    all_auth_cookie_exc = (
      None,
      stem.connection.CookieAuthRejected(None, False, None),
      stem.connection.IncorrectCookieValue(None, False, None),
      stem.connection.IncorrectCookieSize(None, False, None),
      stem.connection.UnreadableCookieFile(None, False, None),
      stem.connection.AuthChallengeFailed(None, None),
      stem.ControllerError(None))

    auth_method_combinations = test.get_all_combinations([
      stem.connection.AuthMethod.NONE,
      stem.connection.AuthMethod.PASSWORD,
      stem.connection.AuthMethod.COOKIE,
      stem.connection.AuthMethod.SAFECOOKIE,
      stem.connection.AuthMethod.UNKNOWN,
    ], include_empty = True)

    protocolinfo = ControlMessage.from_str('250-PROTOCOLINFO 1\r\n250 OK\r\n', 'PROTOCOLINFO')
    protocolinfo.cookie_path = '/tmp/blah'

    for auth_methods in auth_method_combinations:
      for auth_none_exc in all_auth_none_exc:
        for auth_password_exc in all_auth_password_exc:
          for auth_cookie_exc in all_auth_cookie_exc:
            # Skip iteration if it's to test exceptions for authentication
            # we're not using.

            if auth_none_exc and stem.connection.AuthMethod.NONE not in auth_methods:
              continue
            elif auth_password_exc and stem.connection.AuthMethod.PASSWORD not in auth_methods:
              continue
            elif auth_cookie_exc and stem.connection.AuthMethod.COOKIE not in auth_methods and stem.connection.AuthMethod.SAFECOOKIE not in auth_methods:
              continue

            # Determine if the authenticate() call will succeed and mock each
            # of the authenticate_* function to raise its given exception.
            #
            # This implementation is slightly inaccurate in a couple regards...
            # a. it raises safecookie exceptions from authenticate_cookie()
            # b. exceptions raised by authenticate_cookie() and
            #    authenticate_safecookie() are always the same
            #
            # However, adding another loop for safe_cookie exceptions means
            # multiplying our runtime many fold. This exercises everything that
            # matters so the above inaccuracies seem fine.

            expect_success = False
            protocolinfo.auth_methods = auth_methods

            for auth_method in auth_methods:
              if auth_method == stem.connection.AuthMethod.NONE:
                auth_mock, raised_exc = authenticate_none_mock, auth_none_exc
              elif auth_method == stem.connection.AuthMethod.PASSWORD:
                auth_mock, raised_exc = authenticate_password_mock, auth_password_exc
              elif auth_method == stem.connection.AuthMethod.COOKIE:
                auth_mock, raised_exc = authenticate_cookie_mock, auth_cookie_exc
              elif auth_method == stem.connection.AuthMethod.SAFECOOKIE:
                auth_mock, raised_exc = authenticate_safecookie_mock, auth_cookie_exc

              if raised_exc:
                auth_mock.side_effect = raised_exc
              else:
                auth_mock.side_effect = None
                expect_success = True

            if expect_success:
              stem.connection.authenticate(None, 'blah', None, protocolinfo)
            else:
              self.assertRaises(stem.connection.AuthenticationFailure, stem.connection.authenticate, None, 'blah', None, protocolinfo)

    # revert logging back to normal
    stem_logger.setLevel(log.logging_level(log.TRACE))