gui: support user/password RPC authentication

This change updates the GUI to support the latest lianad config
file with the user/password option for bitcoind RPC authentication.

The settings have been updated to set either cookie file path
or user and password.

The installer still only supports the cookie file and has only
been updated with the changes required to compile.

When starting, the internal bitcoind now looks for the cookie file
in the usual location rather than relying on the config file.
This commit is contained in:
jp1ac4 2023-12-21 13:55:50 +00:00
parent b8f8d1b944
commit 3ccdafbda2
No known key found for this signature in database
GPG Key ID: A7ACD32423568D7B
6 changed files with 164 additions and 38 deletions

2
gui/Cargo.lock generated
View File

@ -2431,7 +2431,7 @@ dependencies = [
[[package]]
name = "liana"
version = "4.0.0"
source = "git+https://github.com/wizardsardine/liana?branch=master#dee069e72343a67607f4429125e4c80dc0d73055"
source = "git+https://github.com/wizardsardine/liana?branch=master#b8f8d1b944120879986a71255bab19e5b342ecc8"
dependencies = [
"backtrace",
"bdk_coin_select",

View File

@ -8,12 +8,13 @@ use chrono::prelude::*;
use iced::Command;
use tracing::info;
use liana::config::{BitcoinConfig, BitcoindConfig, Config};
use liana::config::{BitcoinConfig, BitcoindConfig, BitcoindRpcAuth, Config};
use liana_ui::{component::form, widget::Element};
use crate::{
app::{cache::Cache, error::Error, message::Message, state::settings::Setting, view, State},
bitcoind::{RpcAuthType, RpcAuthValues},
daemon::Daemon,
};
@ -140,7 +141,8 @@ pub struct BitcoindSettings {
bitcoin_config: BitcoinConfig,
edit: bool,
processing: bool,
cookie_path: form::Value<String>,
rpc_auth_vals: RpcAuthValues,
selected_auth_type: RpcAuthType,
addr: form::Value<String>,
daemon_is_external: bool,
bitcoind_is_internal: bool,
@ -159,7 +161,33 @@ impl BitcoindSettings {
daemon_is_external: bool,
bitcoind_is_internal: bool,
) -> BitcoindSettings {
let path = bitcoind_config.cookie_path.to_str().unwrap().to_string();
let (rpc_auth_vals, selected_auth_type) = match &bitcoind_config.rpc_auth {
BitcoindRpcAuth::CookieFile(path) => (
RpcAuthValues {
cookie_path: form::Value {
valid: true,
value: path.to_str().unwrap().to_string(),
},
user: form::Value::default(),
password: form::Value::default(),
},
RpcAuthType::CookieFile,
),
BitcoindRpcAuth::UserPass(user, password) => (
RpcAuthValues {
cookie_path: form::Value::default(),
user: form::Value {
valid: true,
value: user.clone(),
},
password: form::Value {
valid: true,
value: password.clone(),
},
},
RpcAuthType::UserPass,
),
};
let addr = bitcoind_config.addr.to_string();
BitcoindSettings {
daemon_is_external,
@ -168,10 +196,8 @@ impl BitcoindSettings {
bitcoin_config,
edit: false,
processing: false,
cookie_path: form::Value {
valid: true,
value: path,
},
rpc_auth_vals,
selected_auth_type,
addr: form::Value {
valid: true,
value: addr,
@ -209,21 +235,41 @@ impl Setting for BitcoindSettings {
if !self.processing {
match field {
"socket_address" => self.addr.value = value,
"cookie_file_path" => self.cookie_path.value = value,
"cookie_file_path" => self.rpc_auth_vals.cookie_path.value = value,
"user" => self.rpc_auth_vals.user.value = value,
"password" => self.rpc_auth_vals.password.value = value,
_ => {}
}
}
}
view::SettingsEditMessage::BitcoindRpcAuthTypeSelected(auth_type) => {
if !self.processing {
self.selected_auth_type = auth_type;
}
}
view::SettingsEditMessage::Confirm => {
let new_addr = SocketAddr::from_str(&self.addr.value);
self.addr.valid = new_addr.is_ok();
let new_path = PathBuf::from_str(&self.cookie_path.value);
self.cookie_path.valid = new_path.is_ok();
let rpc_auth = match self.selected_auth_type {
RpcAuthType::CookieFile => {
let new_path = PathBuf::from_str(&self.rpc_auth_vals.cookie_path.value);
if let Ok(path) = new_path {
self.rpc_auth_vals.cookie_path.valid = true;
Some(BitcoindRpcAuth::CookieFile(path))
} else {
None
}
}
RpcAuthType::UserPass => Some(BitcoindRpcAuth::UserPass(
self.rpc_auth_vals.user.value.clone(),
self.rpc_auth_vals.password.value.clone(),
)),
};
if self.addr.valid & self.cookie_path.valid {
if let (true, Some(rpc_auth)) = (self.addr.valid, rpc_auth) {
let mut daemon_config = daemon.config().cloned().unwrap();
daemon_config.bitcoind_config = Some(liana::config::BitcoindConfig {
cookie_path: new_path.unwrap(),
rpc_auth,
addr: new_addr.unwrap(),
});
self.processing = true;
@ -242,7 +288,8 @@ impl Setting for BitcoindSettings {
self.bitcoin_config.network,
cache.blockheight,
&self.addr,
&self.cookie_path,
&self.rpc_auth_vals,
&self.selected_auth_type,
self.processing,
)
} else {
@ -342,6 +389,7 @@ impl Setting for RescanSetting {
Message::StartRescan,
);
}
_ => {}
};
Command::none()
}

View File

@ -1,4 +1,4 @@
use crate::app::menu::Menu;
use crate::{app::menu::Menu, bitcoind::RpcAuthType};
use liana::miniscript::bitcoin::bip32::Fingerprint;
#[derive(Debug, Clone)]
@ -75,6 +75,7 @@ pub enum SettingsMessage {
pub enum SettingsEditMessage {
Select,
FieldEdited(&'static str, String),
BitcoindRpcAuthTypeSelected(RpcAuthType),
Cancel,
Confirm,
}

View File

@ -3,11 +3,14 @@ use std::str::FromStr;
use iced::{
alignment,
widget::{scrollable, Space},
widget::{radio, scrollable, Space},
Alignment, Length,
};
use liana::miniscript::bitcoin::{bip32::Fingerprint, Network};
use liana::{
config::BitcoindRpcAuth,
miniscript::bitcoin::{bip32::Fingerprint, Network},
};
use super::{dashboard, message::*};
@ -26,6 +29,7 @@ use crate::{
menu::Menu,
view::{hw, warning::warn},
},
bitcoind::{RpcAuthType, RpcAuthValues},
hw::HardwareWallet,
};
@ -208,7 +212,8 @@ pub fn bitcoind_edit<'a>(
network: Network,
blockheight: i32,
addr: &form::Value<String>,
cookie_path: &form::Value<String>,
rpc_auth_vals: &RpcAuthValues,
selected_auth_type: &RpcAuthType,
processing: bool,
) -> Element<'a, SettingsEditMessage> {
let mut col = Column::new().spacing(20);
@ -244,18 +249,60 @@ pub fn bitcoind_edit<'a>(
col = col
.push(
Column::new()
.push(text("Cookie file path:").bold().small())
[RpcAuthType::CookieFile, RpcAuthType::UserPass]
.iter()
.fold(
Row::new()
.push(text("RPC authentication:").small().bold())
.spacing(10),
|row, auth_type| {
row.push(radio(
format!("{}", auth_type),
*auth_type,
Some(*selected_auth_type),
SettingsEditMessage::BitcoindRpcAuthTypeSelected,
))
.spacing(30)
.align_items(Alignment::Center)
},
),
)
.push(match selected_auth_type {
RpcAuthType::CookieFile => Column::new()
.push(
form::Form::new_trimmed("Cookie file path", cookie_path, |value| {
SettingsEditMessage::FieldEdited("cookie_file_path", value)
})
form::Form::new_trimmed(
"Cookie file path",
&rpc_auth_vals.cookie_path,
|value| SettingsEditMessage::FieldEdited("cookie_file_path", value),
)
.warning("Please enter a valid filesystem path")
.size(20)
.padding(5),
)
.spacing(5),
)
RpcAuthType::UserPass => Column::new()
.push(
Row::new()
.push(
form::Form::new_trimmed("User", &rpc_auth_vals.user, |value| {
SettingsEditMessage::FieldEdited("user", value)
})
.warning("Please enter a valid user")
.size(20)
.padding(5),
)
.push(
form::Form::new_trimmed("Password", &rpc_auth_vals.password, |value| {
SettingsEditMessage::FieldEdited("password", value)
})
.warning("Please enter a valid password")
.size(20)
.padding(5),
)
.spacing(10),
)
.spacing(5),
})
.push(
Column::new()
.push(text("Socket address:").bold().small())
@ -345,13 +392,17 @@ pub fn bitcoind<'a>(
.push(separation().width(Length::Fill));
}
let rows = vec![
(
"Cookie file path:",
config.cookie_path.to_str().unwrap().to_string(),
),
("Socket address:", config.addr.to_string()),
];
let mut rows = vec![];
match &config.rpc_auth {
BitcoindRpcAuth::CookieFile(path) => {
rows.push(("Cookie file path:", path.to_str().unwrap().to_string()));
}
BitcoindRpcAuth::UserPass(user, password) => {
rows.push(("User:", user.clone()));
rows.push(("Password:", password.clone()));
}
}
rows.push(("Socket address:", config.addr.to_string()));
let mut col_fields = Column::new();
for (k, v) in rows {

View File

@ -1,7 +1,9 @@
use liana::{
config::BitcoindConfig,
config::{BitcoindConfig, BitcoindRpcAuth},
miniscript::bitcoin::{self, Network},
};
use liana_ui::component::form;
use std::fmt;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::thread;
@ -222,6 +224,7 @@ impl Bitcoind {
// We've started bitcoind in the background, however it may fail to start for whatever
// reason. And we need its JSONRPC interface to be available to continue. Thus wait for it
// to have created the cookie file, regularly checking it did not fail to start.
let cookie_path = internal_bitcoind_cookie_path(&bitcoind_datadir, network);
loop {
match process.try_wait() {
Ok(None) => {}
@ -229,11 +232,11 @@ impl Bitcoind {
Ok(Some(status)) => {
log::error!("Bitcoind exited with status '{}'", status);
return Err(StartInternalBitcoindError::CookieFileNotFound(
config.cookie_path.to_string_lossy().into_owned(),
cookie_path.to_string_lossy().into_owned(),
));
}
}
if config.cookie_path.exists() {
if cookie_path.exists() {
log::info!("Bitcoind seems to have successfully started.");
break;
}
@ -241,9 +244,10 @@ impl Bitcoind {
thread::sleep(time::Duration::from_millis(500));
}
config.cookie_path = config.cookie_path.canonicalize().map_err(|e| {
config.rpc_auth = BitcoindRpcAuth::CookieFile(cookie_path.canonicalize().map_err(|e| {
StartInternalBitcoindError::CouldNotCanonicalizeCookiePath(e.to_string())
})?;
})?);
liana::BitcoinD::new(&config, "internal_bitcoind_start".to_string())
.map_err(|e| StartInternalBitcoindError::BitcoinDError(e.to_string()))?;
@ -273,3 +277,25 @@ pub fn stop_bitcoind(config: &BitcoindConfig) -> bool {
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum RpcAuthType {
CookieFile,
UserPass,
}
impl fmt::Display for RpcAuthType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
RpcAuthType::CookieFile => write!(f, "Cookie file path"),
RpcAuthType::UserPass => write!(f, "User and password"),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct RpcAuthValues {
pub cookie_path: form::Value<String>,
pub user: form::Value<String>,
pub password: form::Value<String>,
}

View File

@ -554,7 +554,7 @@ impl Step for DefineBitcoind {
}
(Ok(path), Ok(addr)) => {
ctx.bitcoind_config = Some(BitcoindConfig {
cookie_path: path,
rpc_auth: liana::config::BitcoindRpcAuth::CookieFile(path),
addr,
});
true
@ -803,7 +803,7 @@ impl Step for InternalBitcoindStep {
match Bitcoind::start(
&self.network,
BitcoindConfig {
cookie_path,
rpc_auth: liana::config::BitcoindRpcAuth::CookieFile(cookie_path),
addr: internal_bitcoind_address(rpc_port),
},
&self.liana_datadir,