Skip to main content

soil_cli/params/
keystore_params.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
6
7use crate::{error, error::Result};
8use clap::Args;
9use soil_service::config::KeystoreConfig;
10use std::{
11	fs,
12	path::{Path, PathBuf},
13};
14use subsoil::core::crypto::SecretString;
15
16/// default sub directory for the key store
17const DEFAULT_KEYSTORE_CONFIG_PATH: &str = "keystore";
18
19/// Parameters of the keystore
20#[derive(Debug, Clone, Args)]
21pub struct KeystoreParams {
22	/// Specify custom keystore path.
23	#[arg(long, value_name = "PATH")]
24	pub keystore_path: Option<PathBuf>,
25
26	/// Use interactive shell for entering the password used by the keystore.
27	#[arg(long, conflicts_with_all = &["password", "password_filename"])]
28	pub password_interactive: bool,
29
30	/// Password used by the keystore.
31	///
32	/// This allows appending an extra user-defined secret to the seed.
33	#[arg(
34		long,
35		value_parser = secret_string_from_str,
36		conflicts_with_all = &["password_interactive", "password_filename"]
37	)]
38	pub password: Option<SecretString>,
39
40	/// File that contains the password used by the keystore.
41	#[arg(
42		long,
43		value_name = "PATH",
44		conflicts_with_all = &["password_interactive", "password"]
45	)]
46	pub password_filename: Option<PathBuf>,
47}
48
49/// Parse a secret string, returning a displayable error.
50pub fn secret_string_from_str(s: &str) -> std::result::Result<SecretString, String> {
51	std::str::FromStr::from_str(s).map_err(|_| "Could not get SecretString".to_string())
52}
53
54impl KeystoreParams {
55	/// Get the keystore configuration for the parameters
56	pub fn keystore_config(&self, config_dir: &Path) -> Result<KeystoreConfig> {
57		let password = if self.password_interactive {
58			Some(SecretString::new(input_keystore_password()?))
59		} else if let Some(ref file) = self.password_filename {
60			let password = fs::read_to_string(file).map_err(|e| format!("{}", e))?;
61			Some(SecretString::new(password))
62		} else {
63			self.password.clone()
64		};
65
66		let path = self
67			.keystore_path
68			.clone()
69			.unwrap_or_else(|| config_dir.join(DEFAULT_KEYSTORE_CONFIG_PATH));
70
71		Ok(KeystoreConfig::Path { path, password })
72	}
73
74	/// helper method to fetch password from `KeyParams` or read from stdin
75	pub fn read_password(&self) -> error::Result<Option<SecretString>> {
76		let (password_interactive, password) = (self.password_interactive, self.password.clone());
77
78		let pass = if password_interactive {
79			let password = rpassword::prompt_password("Key password: ")?;
80			Some(SecretString::new(password))
81		} else {
82			password
83		};
84
85		Ok(pass)
86	}
87}
88
89fn input_keystore_password() -> Result<String> {
90	rpassword::prompt_password("Keystore password: ").map_err(|e| format!("{:?}", e).into())
91}