commit 1a6b822592fb2b8879e1afa23fa41204ad65b1a2
parent 9bcaedd00e7f52bd798944896801322b3920f803
Author: Antoine A <>
Date: Thu, 9 Jan 2025 12:13:58 +0100
magnet-bank: idempotent setup
Diffstat:
6 files changed, 84 insertions(+), 76 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -367,9 +367,9 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.5.23"
+version = "4.5.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
+checksum = "9560b07a799281c7e0958b9296854d6fafd4c5f31444a7e5bb1ad6dde5ccf1bd"
dependencies = [
"clap_builder",
"clap_derive",
@@ -377,9 +377,9 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.5.23"
+version = "4.5.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
+checksum = "874e0dd3eb68bf99058751ac9712f622e61e6f393a94f7128fa26e3f02f5c7cd"
dependencies = [
"anstream",
"anstyle",
@@ -389,9 +389,9 @@ dependencies = [
[[package]]
name = "clap_derive"
-version = "4.5.18"
+version = "4.5.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
+checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c"
dependencies = [
"heck",
"proc-macro2",
@@ -807,9 +807,9 @@ dependencies = [
[[package]]
name = "event-listener"
-version = "5.3.1"
+version = "5.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba"
+checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae"
dependencies = [
"concurrent-queue",
"parking",
@@ -1539,9 +1539,9 @@ dependencies = [
[[package]]
name = "linux-raw-sys"
-version = "0.4.14"
+version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "listenfd"
@@ -1587,6 +1587,7 @@ dependencies = [
"hmac",
"jiff",
"p256",
+ "passterm",
"percent-encoding",
"rand_core",
"reqwest",
@@ -1596,7 +1597,7 @@ dependencies = [
"sha1",
"spki",
"taler-common",
- "thiserror 2.0.9",
+ "thiserror 2.0.10",
"tokio",
"tracing",
"tracing-subscriber",
@@ -1862,6 +1863,15 @@ dependencies = [
]
[[package]]
+name = "passterm"
+version = "2.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "150ca2316c7813c688677784f20bb0a9efab639415ae1961869863ee99a81e51"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "pem-rfc7468"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2245,9 +2255,9 @@ dependencies = [
[[package]]
name = "rustix"
-version = "0.38.42"
+version = "0.38.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
+checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
dependencies = [
"bitflags",
"errno",
@@ -2603,7 +2613,7 @@ dependencies = [
"serde_json",
"sha2",
"smallvec",
- "thiserror 2.0.9",
+ "thiserror 2.0.10",
"tokio",
"tokio-stream",
"tracing",
@@ -2684,7 +2694,7 @@ dependencies = [
"smallvec",
"sqlx-core",
"stringprep",
- "thiserror 2.0.9",
+ "thiserror 2.0.10",
"tracing",
"whoami",
]
@@ -2721,7 +2731,7 @@ dependencies = [
"smallvec",
"sqlx-core",
"stringprep",
- "thiserror 2.0.9",
+ "thiserror 2.0.10",
"tracing",
"whoami",
]
@@ -2846,7 +2856,7 @@ dependencies = [
"sqlx",
"taler-common",
"test-utils",
- "thiserror 2.0.9",
+ "thiserror 2.0.10",
"tokio",
"tracing",
"tracing-subscriber",
@@ -2870,7 +2880,7 @@ dependencies = [
"serde_with",
"sqlx",
"tempfile",
- "thiserror 2.0.9",
+ "thiserror 2.0.10",
"tracing",
"url",
]
@@ -2912,11 +2922,11 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "2.0.9"
+version = "2.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
+checksum = "a3ac7f54ca534db81081ef1c1e7f6ea8a3ef428d2fc069097c079443d24124d3"
dependencies = [
- "thiserror-impl 2.0.9",
+ "thiserror-impl 2.0.10",
]
[[package]]
@@ -2932,9 +2942,9 @@ dependencies = [
[[package]]
name = "thiserror-impl"
-version = "2.0.9"
+version = "2.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
+checksum = "9e9465d30713b56a37ede7185763c3492a91be2f5fa68d958c44e41ab9248beb"
dependencies = [
"proc-macro2",
"quote",
@@ -3019,9 +3029,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
-version = "1.42.0"
+version = "1.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
+checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
dependencies = [
"backtrace",
"bytes",
@@ -3036,9 +3046,9 @@ dependencies = [
[[package]]
name = "tokio-macros"
-version = "2.4.0"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
+checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
diff --git a/wire-gateway/magnet-bank/Cargo.toml b/wire-gateway/magnet-bank/Cargo.toml
@@ -15,6 +15,7 @@ form_urlencoded = "1.2"
percent-encoding = "2.3"
serde_urlencoded = "0.7.1"
anyhow = "1.0"
+passterm = "2.0"
taler-common = { path = "../../common/taler-common" }
serde_json = { workspace = true, features = ["raw_value"] }
jiff = { workspace = true, features = ["serde"] }
diff --git a/wire-gateway/magnet-bank/src/keys.rs b/wire-gateway/magnet-bank/src/keys.rs
@@ -14,7 +14,7 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-use std::io::{BufRead, ErrorKind};
+use std::io::ErrorKind;
use p256::ecdsa::SigningKey;
use taler_common::{json_file, types::base32::Base32};
@@ -22,7 +22,10 @@ use tracing::info;
use crate::{
config::MagnetConfig,
- magnet::{AuthClient, Token, TokenAuth},
+ magnet::{
+ error::{ApiError, MagnetError},
+ AuthClient, Token, TokenAuth,
+ },
};
#[derive(Default, Debug, serde::Deserialize, serde::Serialize)]
@@ -53,21 +56,14 @@ pub async fn setup(cfg: MagnetConfig) -> Result<(), anyhow::Error> {
// TODO how to do it in a generic way ?
// TODO Ask MagnetBank if they could support out-of-band configuration
println!(
- "Login at {}?oauth_token={}\nEnter the result url>",
+ "Login at {}?oauth_token={}",
client.join("/NetBankOAuth/authtoken.xhtml"),
token_request.key
);
- // TODO better prompting
- let prompt = std::io::stdin()
- .lock()
- .lines()
- .next()
- .expect("Missing auth URL line")
- .expect("Reading auth URL prompt");
- let auth_url = reqwest::Url::parse(&prompt).expect("Auth URL malformed");
+ let auth_url = passterm::prompt_password_tty(Some("Enter the result URL>"))?;
+ let auth_url = reqwest::Url::parse(&auth_url)?;
let token_auth: TokenAuth =
- serde_urlencoded::from_str(auth_url.query().unwrap_or_default())
- .expect("Auth URL malformed");
+ serde_urlencoded::from_str(auth_url.query().unwrap_or_default())?;
assert_eq!(token_request.key, token_auth.oauth_token);
let access_token = client.token_access(&token_request, &token_auth).await?;
@@ -79,40 +75,40 @@ pub async fn setup(cfg: MagnetConfig) -> Result<(), anyhow::Error> {
info!("Setup Strong Customer Authentication");
// TODO find a proper way to check if SCA is required without trigerring SCA.GLOBAL_FEATURE_NOT_ENABLED
- let perform_sca = false;
- if perform_sca {
- let request = client.request_sms_code().await?;
- println!(
- "A SCA code have been sent through {} to {}\nEnter the code>",
- request.channel,
- request.sent_to.join(", ")
- );
- // TODO better prompting
- let prompt = std::io::stdin()
- .lock()
- .lines()
- .next()
- .expect("Missing SCA code line")
- .expect("Reading SCA code prompt");
- client.perform_sca(&prompt).await?;
+ let request = client.request_sms_code().await?;
+ println!(
+ "A SCA code have been sent through {} to {}",
+ request.channel,
+ request.sent_to.join(", ")
+ );
+ let sca_code = passterm::prompt_password_tty(Some("Enter the code>"))?;
+ if let Err(e) = client.perform_sca(&sca_code).await {
+ // Ignore error if SCA already performed
+ if !matches!(e, ApiError::Magnet(MagnetError { ref short_message, .. }) if short_message == "TOKEN_SCA_HITELESITETT")
+ {
+ return Err(e.into());
+ }
}
info!("Setup public key");
// TODO find a proper way to check if a public key have been setup
- let perform_public_key = false;
- if perform_public_key {
- // TODO use the beter from/to_array API in the next version of the crypto lib
- let signing_key = match keys.signing_key {
- Some(bytes) => SigningKey::from_slice(bytes.as_ref())?,
- None => {
- let rand = SigningKey::random(&mut rand_core::OsRng);
- let array: [u8; 32] = rand.to_bytes().as_slice().try_into().unwrap();
- keys.signing_key = Some(Base32::from(array));
- json_file::persist(&cfg.keys_path, &keys)?;
- rand
- }
- };
- client.setup_public_key(&signing_key).await?;
+ // TODO use the better from/to_array API in the next version of the crypto lib
+ let signing_key = match keys.signing_key {
+ Some(bytes) => SigningKey::from_slice(bytes.as_ref())?,
+ None => {
+ let rand = SigningKey::random(&mut rand_core::OsRng);
+ let array: [u8; 32] = rand.to_bytes().as_slice().try_into().unwrap();
+ keys.signing_key = Some(Base32::from(array));
+ json_file::persist(&cfg.keys_path, &keys)?;
+ rand
+ }
+ };
+ if let Err(e) = client.upload_public_key(&signing_key).await {
+ // Ignore error if public key already uploaded
+ if !matches!(e, ApiError::Magnet(MagnetError { ref short_message, .. }) if short_message == "KULCS_MAR_HASZNALATBAN")
+ {
+ return Err(e.into());
+ }
}
Ok(())
diff --git a/wire-gateway/magnet-bank/src/magnet.rs b/wire-gateway/magnet-bank/src/magnet.rs
@@ -174,11 +174,11 @@ impl ApiClient {
}))
.oauth(&self.consumer, Some(&self.access), None)
.await
- .magnet_json()
+ .magnet_empty()
.await
}
- pub async fn setup_public_key(&self, key: &SigningKey) -> ApiResult<Value> {
+ pub async fn upload_public_key(&self, key: &SigningKey) -> ApiResult<Value> {
let public_key = PublicKey::from_secret_scalar(key.as_nonzero_scalar());
let der = public_key.to_public_key_der().unwrap().to_vec();
self.client
diff --git a/wire-gateway/magnet-bank/src/magnet/error.rs b/wire-gateway/magnet-bank/src/magnet/error.rs
@@ -31,11 +31,11 @@ pub struct MagnetHeader {
#[error("{error_code} {short_message} '{long_message}'")]
pub struct MagnetError {
#[serde(alias = "errorCode")]
- error_code: u16,
+ pub error_code: u16,
#[serde(alias = "shortMessage")]
- short_message: String,
+ pub short_message: String,
#[serde(alias = "longMessage")]
- long_message: String,
+ pub long_message: String,
}
#[derive(Error, Debug)]
diff --git a/wire-gateway/magnet-bank/src/main.rs b/wire-gateway/magnet-bank/src/main.rs
@@ -52,7 +52,7 @@ enum Command {
fn setup(level: Option<tracing::Level>, app: impl Future<Output = Result<(), anyhow::Error>>) {
// Setup logger
let level = level.unwrap_or(Level::INFO);
- FmtSubscriber::builder()
+ let guard = FmtSubscriber::builder()
.with_max_level(level)
.finish()
.set_default();
@@ -69,6 +69,7 @@ fn setup(level: Option<tracing::Level>, app: impl Future<Output = Result<(), any
error!("{}", err);
std::process::exit(1);
}
+ drop(guard);
}
async fn app(args: Args) -> Result<(), anyhow::Error> {