def test_update_secure_config(self): password = "******" secrets_manager = ETHKeyFileSecretManger(password) store_password_verification(secrets_manager) Security.login(secrets_manager) binance_config = ClientConfigAdapter( BinanceConfigMap(binance_api_key=self.api_key, binance_api_secret=self.api_secret) ) self.async_run_with_timeout(Security.wait_til_decryption_done()) Security.update_secure_config(binance_config) self.reset_security() Security.login(secrets_manager) self.async_run_with_timeout(Security.wait_til_decryption_done(), timeout=2) binance_loaded_config = Security.decrypted_value(binance_config.connector) self.assertEqual(binance_config, binance_loaded_config) binance_config.binance_api_key = "someOtherApiKey" Security.update_secure_config(binance_config) self.reset_security() Security.login(secrets_manager) self.async_run_with_timeout(Security.wait_til_decryption_done(), timeout=2) binance_loaded_config = Security.decrypted_value(binance_config.connector) self.assertEqual(binance_config, binance_loaded_config)
async def load_yml_into_cm(yml_path: str, template_file_path: str, cm: Dict[str, ConfigVar]): try: data = {} conf_version = -1 if isfile(yml_path): with open(yml_path) as stream: data = yaml_parser.load(stream) or {} conf_version = data.get("template_version", 0) with open(template_file_path, "r") as template_fd: template_data = yaml_parser.load(template_fd) template_version = template_data.get("template_version", 0) for key in template_data: if key in {"template_version"}: continue cvar = cm.get(key) if cvar is None: logging.getLogger().error( f"Cannot find corresponding config to key {key} in template." ) continue # Skip this step since the values are not saved in the yml file if cvar.is_secure: cvar.value = Security.decrypted_value(key) continue val_in_file = data.get(key, None) if (val_in_file is None or val_in_file == "") and cvar.default is not None: cvar.value = cvar.default continue # Todo: the proper process should be first validate the value then assign it cvar.value = parse_cvar_value(cvar, val_in_file) if cvar.value is not None: err_msg = await cvar.validate(str(cvar.value)) if err_msg is not None: # Instead of raising an exception, simply skip over this variable and wait till the user is prompted logging.getLogger().error( "Invalid value %s for config variable %s: %s" % (val_in_file, cvar.key, err_msg)) cvar.value = None if conf_version < template_version: # delete old config file if isfile(yml_path): unlink(yml_path) # copy the new file template shutil.copy(template_file_path, yml_path) # save the old variables into the new config file save_to_yml(yml_path, cm) except Exception as e: logging.getLogger().error( "Error loading configs. Your config file may be corrupt. %s" % (e, ), exc_info=True)
async def _test_existing_password(self): # check the 2 encrypted files exist self.assertTrue( os.path.exists(f"{temp_folder}encrypted_test_key_1.json")) self.assertTrue( os.path.exists(f"{temp_folder}encrypted_test_key_2.json")) self.assertTrue(Security.any_encryped_files()) self.assertFalse(Security.new_password_required()) # login fails with incorrect password result = Security.login("b") self.assertFalse(result) # login passes with correct password result = Security.login("a") self.assertTrue(result) # right after logging in, the decryption shouldn't finished yet self.assertFalse(Security.is_decryption_done()) await Security.wait_til_decryption_done() self.assertEqual(len(Security.all_decrypted_values()), 2) config_value = Security.decrypted_value("test_key_1") self.assertEqual("test_value_1", config_value) Security.update_secure_config("test_key_1", "new_value") self.assertEqual("new_value", Security.decrypted_value("test_key_1"))
async def connect_exchange(self, # type: HummingbotApplication exchange): self.app.clear_input() self.placeholder_mode = True self.app.hide_input = True if exchange == "kraken": self._notify("Reminder: Please ensure your Kraken API Key Nonce Window is at least 10.") exchange_configs = [c for c in global_config_map.values() if c.key in settings.CONNECTOR_SETTINGS[exchange].config_keys and c.is_connect_key] to_connect = True if Security.encrypted_file_exists(exchange_configs[0].key): await Security.wait_til_decryption_done() api_key_config = [c for c in exchange_configs if "api_key" in c.key] if api_key_config: api_key_config = api_key_config[0] api_key = Security.decrypted_value(api_key_config.key) prompt = f"Would you like to replace your existing {exchange} API key {api_key} (Yes/No)? >>> " else: prompt = f"Would you like to replace your existing {exchange_configs[0].key} (Yes/No)? >>> " answer = await self.app.prompt(prompt=prompt) if self.app.to_stop_config: self.app.to_stop_config = False return if answer.lower() not in ("yes", "y"): to_connect = False if to_connect: for config in exchange_configs: await self.prompt_a_config(config) if self.app.to_stop_config: self.app.to_stop_config = False return Security.update_secure_config(config.key, config.value) api_keys = await Security.api_keys(exchange) network_timeout = float(global_config_map["other_commands_timeout"].value) try: err_msg = await asyncio.wait_for( UserBalances.instance().add_exchange(exchange, **api_keys), network_timeout ) except asyncio.TimeoutError: self._notify("\nA network error prevented the connection to complete. See logs for more details.") self.placeholder_mode = False self.app.hide_input = False self.app.change_prompt(prompt=">>> ") raise if err_msg is None: self._notify(f"\nYou are now connected to {exchange}.") else: self._notify(f"\nError: {err_msg}") self.placeholder_mode = False self.app.hide_input = False self.app.change_prompt(prompt=">>> ")
async def connect_exchange( self, # type: HummingbotApplication exchange): self.app.clear_input() self.placeholder_mode = True self.app.hide_input = True if exchange == "kraken": self._notify( "Reminder: Please ensure your Kraken API Key Nonce Window is at least 10." ) exchange_configs = [ c for c in global_config_map.values() if c.key in settings.CONNECTOR_SETTINGS[exchange].config_keys and c.is_connect_key ] to_connect = True if Security.encrypted_file_exists(exchange_configs[0].key): await Security.wait_til_decryption_done() api_key_config = [ c for c in exchange_configs if "api_key" in c.key ][0] api_key = Security.decrypted_value(api_key_config.key) answer = await self.app.prompt( prompt= f"Would you like to replace your existing {exchange} API key " f"{api_key} (Yes/No)? >>> ") if self.app.to_stop_config: self.app.to_stop_config = False return if answer.lower() not in ("yes", "y"): to_connect = False if to_connect: for config in exchange_configs: await self.prompt_a_config(config) if self.app.to_stop_config: self.app.to_stop_config = False return Security.update_secure_config(config.key, config.value) api_keys = await Security.api_keys(exchange) err_msg = await UserBalances.instance().add_exchange( exchange, **api_keys) if err_msg is None: self._notify(f"\nYou are now connected to {exchange}.") else: self._notify(f"\nError: {err_msg}") self.placeholder_mode = False self.app.hide_input = False self.app.change_prompt(prompt=">>> ")
async def validate_n_connect_celo(self, to_reconnect: bool = False, celo_address: str = None, celo_password: str = None) -> Optional[str]: if celo_address is None: celo_address = global_config_map["celo_address"].value if celo_password is None: await Security.wait_til_decryption_done() celo_password = Security.decrypted_value("celo_password") if celo_address is None or celo_password is None: return "Celo address and/or password have not been added." if CeloCLI.unlocked and not to_reconnect: return None err_msg = CeloCLI.validate_node_synced() if err_msg is not None: return err_msg err_msg = CeloCLI.unlock_account(celo_address, celo_password) return err_msg
async def connect_exchange( self, # type: HummingbotApplication exchange): self.app.clear_input() self.placeholder_mode = True self.app.hide_input = True exchange_configs = [ c for c in global_config_map.values() if exchange in c.key and c.is_connect_key ] to_connect = True if Security.encrypted_file_exists(exchange_configs[0].key): await Security.wait_til_decryption_done() api_key_config = [ c for c in exchange_configs if "api_key" in c.key ][0] api_key = Security.decrypted_value(api_key_config.key) answer = await self.app.prompt( prompt= f"Would you like to replace your existing {exchange} API key " f"...{api_key[-4:]} (Yes/No)? >>> ") if answer.lower() not in ("yes", "y"): to_connect = False if to_connect: for config in exchange_configs: await self.prompt_a_config(config) Security.update_secure_config(config.key, config.value) api_keys = (await Security.api_keys(exchange)).values() err_msg = await UserBalances.instance().add_exchange( exchange, *api_keys) if err_msg is None: self._notify(f"\nYou are now connected to {exchange}.") else: self._notify(f"\nError: {err_msg}") self.placeholder_mode = False self.app.hide_input = False self.app.change_prompt(prompt=">>> ")
def load_secure_values(config_map): for key, config in config_map.items(): if config.is_secure: config.value = Security.decrypted_value(key)