def test_i18n(journalist_app, config): # Then delete it because using it won't test what we want del journalist_app sources = [ os.path.join(TESTS_DIR, "i18n/code.py"), os.path.join(TESTS_DIR, "i18n/template.html"), ] i18n_tool.I18NTool().main([ "--verbose", "translate-messages", "--mapping", os.path.join(TESTS_DIR, "i18n/babel.cfg"), "--translations-dir", config.TEMP_DIR, "--sources", ",".join(sources), "--extract-update", ]) pot = os.path.join(config.TEMP_DIR, "messages.pot") pybabel("init", "-i", pot, "-d", config.TEMP_DIR, "-l", "en_US") for (l, s) in ( ("fr_FR", "code bonjour"), ("zh_Hans", "code chinese"), ("ar", "code arabic"), ("nb_NO", "code norwegian"), ("es_ES", "code spanish"), ): pybabel("init", "-i", pot, "-d", config.TEMP_DIR, "-l", l) po = os.path.join(config.TEMP_DIR, l, "LC_MESSAGES/messages.po") sed("-i", "-e", '/code hello i18n/,+1s/msgstr ""/msgstr "{}"/'.format(s), po) i18n_tool.I18NTool().main([ "--verbose", "translate-messages", "--translations-dir", config.TEMP_DIR, "--compile", ]) fake_config = SDConfig() fake_config.SUPPORTED_LOCALES = [ "ar", "en_US", "fr_FR", "nb_NO", "zh_Hans" ] fake_config.TRANSLATION_DIRS = Path(config.TEMP_DIR) # Use our config (and not an app fixture) because the i18n module # grabs values at init time and we can't inject them later. for app in (journalist_app_module.create_app(fake_config), source_app.create_app(fake_config)): with app.app_context(): db.create_all() assert list(i18n.LOCALES.keys()) == fake_config.SUPPORTED_LOCALES verify_i18n(app)
def config( setup_journalist_key_and_gpg_folder: Tuple[str, Path] ) -> Generator[SDConfig, None, None]: config = SDConfig() journalist_key_fingerprint, gpg_key_dir = setup_journalist_key_and_gpg_folder config.GPG_KEY_DIR = str(gpg_key_dir) config.JOURNALIST_KEY = journalist_key_fingerprint # Setup the filesystem for the application with TemporaryDirectory() as data_dir_name: data_dir = Path(data_dir_name) config.SECUREDROP_DATA_ROOT = str(data_dir) store_dir = data_dir / "store" store_dir.mkdir() config.STORE_DIR = str(store_dir) tmp_dir = data_dir / "tmp" tmp_dir.mkdir() config.TEMP_DIR = str(tmp_dir) # Create the db file sqlite_db_path = data_dir / "db.sqlite" config.DATABASE_FILE = str(sqlite_db_path) subprocess.check_call(["sqlite3", config.DATABASE_FILE, ".databases"]) config.SUPPORTED_LOCALES = i18n.get_test_locales() # Set this newly-created config as the "global" config with mock.patch.object(sdconfig, "config", config): yield config
def test_i18n(journalist_app, config): # Then delete it because using it won't test what we want del journalist_app sources = [ os.path.join(TESTS_DIR, 'i18n/code.py'), os.path.join(TESTS_DIR, 'i18n/template.html'), ] i18n_tool.I18NTool().main([ '--verbose', 'translate-messages', '--mapping', os.path.join(TESTS_DIR, 'i18n/babel.cfg'), '--translations-dir', config.TEMP_DIR, '--sources', ",".join(sources), '--extract-update', ]) pot = os.path.join(config.TEMP_DIR, 'messages.pot') pybabel('init', '-i', pot, '-d', config.TEMP_DIR, '-l', 'en_US') for (l, s) in (('fr_FR', 'code bonjour'), ('zh_Hans_CN', 'code chinese'), ('ar', 'code arabic'), ('nb_NO', 'code norwegian'), ('es_ES', 'code spanish')): pybabel('init', '-i', pot, '-d', config.TEMP_DIR, '-l', l) po = os.path.join(config.TEMP_DIR, l, 'LC_MESSAGES/messages.po') sed('-i', '-e', '/code hello i18n/,+1s/msgstr ""/msgstr "{}"/'.format(s), po) i18n_tool.I18NTool().main([ '--verbose', 'translate-messages', '--translations-dir', config.TEMP_DIR, '--compile', ]) fake_config = SDConfig() fake_config.SUPPORTED_LOCALES = [ 'en_US', 'fr_FR', 'zh_Hans_CN', 'ar', 'nb_NO' ] fake_config.TRANSLATION_DIRS = config.TEMP_DIR # Use our config (and not an app fixture) because the i18n module # grabs values at init time and we can't inject them later. for app in (journalist_app_module.create_app(fake_config), source_app.create_app(fake_config)): with app.app_context(): db.create_all() assert i18n.LOCALES == fake_config.SUPPORTED_LOCALES verify_i18n(app)
def test_invalid_locales(config): """ An invalid locale raises an error during app configuration. """ fake_config = SDConfig() fake_config.SUPPORTED_LOCALES = [FALLBACK_LOCALE, "yy_ZZ"] fake_config.TRANSLATION_DIRS = Path(config.TEMP_DIR) with pytest.raises(UnknownLocaleError): journalist_app_module.create_app(fake_config) with pytest.raises(UnknownLocaleError): source_app.create_app(fake_config)
def test_valid_but_unusable_locales(config, caplog): """ The apps start with one or more unusable, but still valid, locales, but log an error for OSSEC to pick up. """ fake_config = SDConfig() fake_config.SUPPORTED_LOCALES = [FALLBACK_LOCALE, "wae_CH"] fake_config.TRANSLATION_DIRS = Path(config.TEMP_DIR) for app in (journalist_app_module.create_app(fake_config), source_app.create_app(fake_config)): with app.app_context(): assert "wae" in caplog.text assert "not in the set of usable locales" in caplog.text
def test_unusable_default_but_usable_fallback_locale(config, caplog): """ The apps start even if the default locale is unusable, as along as the fallback locale is usable, but log an error for OSSEC to pick up. """ fake_config = SDConfig() fake_config.DEFAULT_LOCALE = NEVER_LOCALE fake_config.SUPPORTED_LOCALES = [NEVER_LOCALE, FALLBACK_LOCALE] fake_config.TRANSLATION_DIRS = Path(config.TEMP_DIR) for app in (journalist_app_module.create_app(fake_config), source_app.create_app(fake_config)): with app.app_context(): assert NEVER_LOCALE in caplog.text assert "not in the set of usable locales" in caplog.text
def test_no_usable_fallback_locale(journalist_app, config): """ The apps fail if neither the default nor the fallback locale is usable. """ fake_config = SDConfig() fake_config.DEFAULT_LOCALE = NEVER_LOCALE fake_config.SUPPORTED_LOCALES = [NEVER_LOCALE] fake_config.TRANSLATION_DIRS = Path(config.TEMP_DIR) i18n.USABLE_LOCALES = set() with pytest.raises(ValueError, match="in the set of usable locales"): journalist_app_module.create_app(fake_config) with pytest.raises(ValueError, match="in the set of usable locales"): source_app.create_app(fake_config)
def config(tmpdir): '''Clone the module so we can modify it per test.''' cnf = SDConfig() data = tmpdir.mkdir('data') keys = data.mkdir('keys') os.chmod(str(keys), 0o700) store = data.mkdir('store') tmp = data.mkdir('tmp') sqlite = data.join('db.sqlite') # gpg 2.1+ requires gpg-agent, see #4013 gpg_agent_config = str(keys.join('gpg-agent.conf')) with open(gpg_agent_config, 'w+') as f: f.write('allow-loopback-pinentry') gpg = gnupg.GPG('gpg2', homedir=str(keys)) for ext in ['sec', 'pub']: with io.open( path.join(path.dirname(__file__), 'files', 'test_journalist_key.{}'.format(ext))) as f: gpg.import_keys(f.read()) cnf.SECUREDROP_DATA_ROOT = str(data) cnf.GPG_KEY_DIR = str(keys) cnf.STORE_DIR = str(store) cnf.TEMP_DIR = str(tmp) cnf.DATABASE_FILE = str(sqlite) # create the db file subprocess.check_call(['sqlite3', cnf.DATABASE_FILE, '.databases']) return cnf
def test_missing_config_attribute_is_handled(): """ Test handling of incomplete configurations. Long-running SecureDrop instances might not have ever updated config.py, so could be missing newer settings. This tests that sdconfig.SDConfig can be initialized without error with such a configuration. """ attributes_to_test = ( "JournalistInterfaceFlaskConfig", "SourceInterfaceFlaskConfig", "DATABASE_ENGINE", "DATABASE_FILE", "ADJECTIVES", "NOUNS", "GPG_KEY_DIR", "JOURNALIST_KEY", "JOURNALIST_TEMPLATES_DIR", "SCRYPT_GPG_PEPPER", "SCRYPT_ID_PEPPER", "SCRYPT_PARAMS", "SECUREDROP_DATA_ROOT", "SECUREDROP_ROOT", "SESSION_EXPIRATION_MINUTES", "SOURCE_TEMPLATES_DIR", "TEMP_DIR", "STORE_DIR", "WORKER_PIDFILE", ) try: importlib.reload(_config) for a in attributes_to_test: delattr(_config, a) from sdconfig import SDConfig SDConfig() finally: importlib.reload(_config)
def config(gpg_key_dir: Path) -> Generator[SDConfig, None, None]: config = SDConfig() config.GPG_KEY_DIR = str(gpg_key_dir) # Setup the filesystem for the application with TemporaryDirectory() as data_dir_name: data_dir = Path(data_dir_name) config.SECUREDROP_DATA_ROOT = str(data_dir) store_dir = data_dir / "store" store_dir.mkdir() config.STORE_DIR = str(store_dir) tmp_dir = data_dir / "tmp" tmp_dir.mkdir() config.TEMP_DIR = str(tmp_dir) # Create the db file sqlite_db_path = data_dir / "db.sqlite" config.DATABASE_FILE = str(sqlite_db_path) subprocess.check_call(["sqlite3", config.DATABASE_FILE, ".databases"]) yield config
def test_supported_locales(config): fake_config = SDConfig() # Check that an invalid locale raises an error during app # configuration. fake_config.SUPPORTED_LOCALES = ['en_US', 'yy_ZZ'] fake_config.TRANSLATION_DIRS = Path(config.TEMP_DIR) with pytest.raises(UnknownLocaleError): journalist_app_module.create_app(fake_config) with pytest.raises(UnknownLocaleError): source_app.create_app(fake_config) # Check that a valid but unsupported locale raises an error during # app configuration. fake_config.SUPPORTED_LOCALES = ['en_US', 'wae_CH'] fake_config.TRANSLATION_DIRS = Path(config.TEMP_DIR) with pytest.raises(ValueError, match="not in the set of translated locales"): journalist_app_module.create_app(fake_config) with pytest.raises(ValueError, match="not in the set of translated locales"): source_app.create_app(fake_config)