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))
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))
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))
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))
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))
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)
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)
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))