From 3de7ca94a2a97935e53e6ab9a86b2b4fe251c29c Mon Sep 17 00:00:00 2001 From: Jacob Kiers Date: Thu, 29 Feb 2024 22:07:37 +0100 Subject: [PATCH] Formatting Signed-off-by: Jacob Kiers --- src/config.rs | 287 +++++++++++++++++++++------------------- src/lib.rs | 4 +- src/monetary_account.rs | 76 +++++------ src/payment.rs | 201 ++++++++++++++-------------- 4 files changed, 290 insertions(+), 278 deletions(-) diff --git a/src/config.rs b/src/config.rs index 9b9d1f9..e893bde 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,138 +1,149 @@ -use anyhow::anyhow; -use isahc::{ReadResponseExt, RequestExt}; -use isahc::http::StatusCode; -use rsa::pkcs1::{DecodeRsaPrivateKey, EncodeRsaPrivateKey, LineEnding}; -use rsa::pkcs8::EncodePublicKey; -use rsa::RsaPrivateKey; -use serde::{Deserialize, Serialize}; - -use crate::{BASE_URL, BunqClient, deserialize_retarded_response, sign}; - -#[derive(Serialize, Deserialize, Default)] -struct AppState { - token: String, - pem_private: String, -} - -#[derive(Serialize, Deserialize, Default)] -pub struct BunqConfig { - api_key: String, - state: Option, -} - -impl BunqConfig { - pub fn load() -> anyhow::Result { - println!("Loading config file from {}", confy::get_configuration_file_path("bunq-rs", "bunq-rs")?.to_string_lossy()); - Ok(confy::load("bunq-rs", "bunq-rs")?) - } - pub fn save(&self) -> anyhow::Result<()> { - println!("Storing config file in {}", confy::get_configuration_file_path("bunq-rs", None)?.to_string_lossy()); - confy::store("bunq-rs", "bunq-rs", self)?; - Ok(()) - } - pub fn install(mut self) -> anyhow::Result { - let api_key = &self.api_key; - - let keypair = if let Some(state) = &self.state { - RsaPrivateKey::from_pkcs1_pem(&state.pem_private)? - } else { - let mut rng = rand::thread_rng(); - - let bits = 2048; - let keypair = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key"); - - let pem_public = keypair.to_public_key().to_public_key_pem(LineEnding::CRLF)?; - - let body = Installation { - client_public_key: &pem_public, - }; - let response = isahc::post( - format!("{}/v1/installation", BASE_URL), - serde_json::to_string(&body)?, - )? - .text()?; - let response: InstallationResponse = deserialize_retarded_response(&response)?.response; - let token = response.token.token; - - let body = DeviceServer { - description: "JK Test Server", - secret: api_key, - permitted_ips: &["77.175.86.236", "*"], - }; - let body = serde_json::to_string(&body)?; - let mut response = isahc::http::Request::post(format!("{}/v1/device-server", BASE_URL)) - .header("X-Bunq-Client-Authentication", &token) - .body(body)? - .send()?; - - let response_text = response.text()?; - println!("{}", response_text); - - if response.status() != StatusCode::OK { - return Err(anyhow!(response_text)); - } - - self.state = Some(AppState { pem_private: keypair.to_pkcs1_pem(LineEnding::CRLF)?.to_string(), token }); - self.save()?; - - keypair - }; - let token = self.state.unwrap().token; - let body = SessionServer { secret: api_key }; - let body = serde_json::to_string(&body)?; - let sig = sign(&body, &keypair)?; - let response = isahc::http::Request::post(format!("{}/v1/session-server", BASE_URL)) - .header("X-Bunq-Client-Authentication", &token) - .header("X-Bunq-Client-Signature", &sig) - .body(body)? - .send()? - .text()?; - let r: SessionServerResponse = deserialize_retarded_response(&response)?.response; - - Ok(BunqClient { - keypair, - token: r.token.token, - user_id: r.user_person.id, - }) - } -} - -#[derive(Serialize)] -pub(super) struct Installation<'a> { - pub(super) client_public_key: &'a str, -} - -#[derive(Deserialize)] -pub(super) struct Token { - pub(super) token: String, -} - -#[derive(Deserialize)] -#[serde(rename_all = "PascalCase")] -pub(super) struct InstallationResponse { - pub(super) token: Token, -} - -#[derive(Serialize)] -pub(super) struct DeviceServer<'a> { - pub(super) description: &'a str, - pub(super) secret: &'a str, - pub(super) permitted_ips: &'a [&'a str], -} - -#[derive(Serialize)] -pub(super) struct SessionServer<'a> { - pub(super) secret: &'a str, -} - -#[derive(Deserialize)] -#[serde(rename_all = "PascalCase")] -pub(super) struct SessionServerResponse { - pub(super) token: Token, - pub(super) user_person: UserPerson, -} - -#[derive(Deserialize)] -pub(super) struct UserPerson { - pub(super) id: i64, -} +use anyhow::anyhow; +use isahc::http::StatusCode; +use isahc::{ReadResponseExt, RequestExt}; +use rsa::pkcs1::{DecodeRsaPrivateKey, EncodeRsaPrivateKey, LineEnding}; +use rsa::pkcs8::EncodePublicKey; +use rsa::RsaPrivateKey; +use serde::{Deserialize, Serialize}; + +use crate::{deserialize_retarded_response, sign, BunqClient, BASE_URL}; + +#[derive(Serialize, Deserialize, Default)] +struct AppState { + token: String, + pem_private: String, +} + +#[derive(Serialize, Deserialize, Default)] +pub struct BunqConfig { + api_key: String, + state: Option, +} + +impl BunqConfig { + pub fn load() -> anyhow::Result { + println!( + "Loading config file from {}", + confy::get_configuration_file_path("bunq-rs", "bunq-rs")?.to_string_lossy() + ); + Ok(confy::load("bunq-rs", "bunq-rs")?) + } + pub fn save(&self) -> anyhow::Result<()> { + println!( + "Storing config file in {}", + confy::get_configuration_file_path("bunq-rs", None)?.to_string_lossy() + ); + confy::store("bunq-rs", "bunq-rs", self)?; + Ok(()) + } + pub fn install(mut self) -> anyhow::Result { + let api_key = &self.api_key; + + let keypair = if let Some(state) = &self.state { + RsaPrivateKey::from_pkcs1_pem(&state.pem_private)? + } else { + let mut rng = rand::thread_rng(); + + let bits = 2048; + let keypair = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key"); + + let pem_public = keypair + .to_public_key() + .to_public_key_pem(LineEnding::CRLF)?; + + let body = Installation { + client_public_key: &pem_public, + }; + let response = isahc::post( + format!("{}/v1/installation", BASE_URL), + serde_json::to_string(&body)?, + )? + .text()?; + let response: InstallationResponse = deserialize_retarded_response(&response)?.response; + let token = response.token.token; + + let body = DeviceServer { + description: "JK Test Server", + secret: api_key, + permitted_ips: &["77.175.86.236", "*"], + }; + let body = serde_json::to_string(&body)?; + let mut response = isahc::http::Request::post(format!("{}/v1/device-server", BASE_URL)) + .header("X-Bunq-Client-Authentication", &token) + .body(body)? + .send()?; + + let response_text = response.text()?; + println!("{}", response_text); + + if response.status() != StatusCode::OK { + return Err(anyhow!(response_text)); + } + + self.state = Some(AppState { + pem_private: keypair.to_pkcs1_pem(LineEnding::CRLF)?.to_string(), + token, + }); + self.save()?; + + keypair + }; + let token = self.state.unwrap().token; + let body = SessionServer { secret: api_key }; + let body = serde_json::to_string(&body)?; + let sig = sign(&body, &keypair)?; + let response = isahc::http::Request::post(format!("{}/v1/session-server", BASE_URL)) + .header("X-Bunq-Client-Authentication", &token) + .header("X-Bunq-Client-Signature", &sig) + .body(body)? + .send()? + .text()?; + let r: SessionServerResponse = deserialize_retarded_response(&response)?.response; + + Ok(BunqClient { + keypair, + token: r.token.token, + user_id: r.user_person.id, + }) + } +} + +#[derive(Serialize)] +pub(super) struct Installation<'a> { + pub(super) client_public_key: &'a str, +} + +#[derive(Deserialize)] +pub(super) struct Token { + pub(super) token: String, +} + +#[derive(Deserialize)] +#[serde(rename_all = "PascalCase")] +pub(super) struct InstallationResponse { + pub(super) token: Token, +} + +#[derive(Serialize)] +pub(super) struct DeviceServer<'a> { + pub(super) description: &'a str, + pub(super) secret: &'a str, + pub(super) permitted_ips: &'a [&'a str], +} + +#[derive(Serialize)] +pub(super) struct SessionServer<'a> { + pub(super) secret: &'a str, +} + +#[derive(Deserialize)] +#[serde(rename_all = "PascalCase")] +pub(super) struct SessionServerResponse { + pub(super) token: Token, + pub(super) user_person: UserPerson, +} + +#[derive(Deserialize)] +pub(super) struct UserPerson { + pub(super) id: i64, +} diff --git a/src/lib.rs b/src/lib.rs index 1a047b4..9952caf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ use anyhow::Result; use rsa::pkcs1v15::SigningKey; -use rsa::RsaPrivateKey; use rsa::sha2::Sha256; use rsa::signature::RandomizedSigner; +use rsa::RsaPrivateKey; use serde::{de::DeserializeOwned, Deserialize}; pub use config::BunqConfig; @@ -94,7 +94,7 @@ fn deserialize_normal_response(r: &str) -> Result Result { - use base64::prelude::{BASE64_STANDARD, Engine}; + use base64::prelude::{Engine, BASE64_STANDARD}; let signing_key = SigningKey::::new(key.clone()); let mut rng = rand::thread_rng(); diff --git a/src/monetary_account.rs b/src/monetary_account.rs index 08a1cda..f43c74a 100644 --- a/src/monetary_account.rs +++ b/src/monetary_account.rs @@ -1,38 +1,38 @@ -use isahc::{ReadResponseExt, RequestExt}; -use serde::Deserialize; - -use crate::{BASE_URL, BunqClient}; - -pub(super) fn get(client: &BunqClient) -> anyhow::Result> { - let response = isahc::http::Request::get(format!( - "{}/v1/user/{}/monetary-account", - BASE_URL, client.user_id - )) - .header("X-Bunq-Client-Authentication", &client.token) - .body(())? - .send()? - .text()?; - - let accounts = crate::deserialize_normal_response::>(&response)?; - - Ok(accounts.response) -} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "PascalCase")] -pub enum MonetaryAccount { - MonetaryAccountBank(MonetaryAccountBank), - MonetaryAccountSavings(MonetaryAccountSavings), -} - -#[derive(Deserialize, Debug)] -pub struct MonetaryAccountBank { - pub id: i64, - pub description: String, -} - -#[derive(Deserialize, Debug)] -pub struct MonetaryAccountSavings { - pub id: i64, - pub description: String, -} +use isahc::{ReadResponseExt, RequestExt}; +use serde::Deserialize; + +use crate::{BunqClient, BASE_URL}; + +pub(super) fn get(client: &BunqClient) -> anyhow::Result> { + let response = isahc::http::Request::get(format!( + "{}/v1/user/{}/monetary-account", + BASE_URL, client.user_id + )) + .header("X-Bunq-Client-Authentication", &client.token) + .body(())? + .send()? + .text()?; + + let accounts = crate::deserialize_normal_response::>(&response)?; + + Ok(accounts.response) +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "PascalCase")] +pub enum MonetaryAccount { + MonetaryAccountBank(MonetaryAccountBank), + MonetaryAccountSavings(MonetaryAccountSavings), +} + +#[derive(Deserialize, Debug)] +pub struct MonetaryAccountBank { + pub id: i64, + pub description: String, +} + +#[derive(Deserialize, Debug)] +pub struct MonetaryAccountSavings { + pub id: i64, + pub description: String, +} diff --git a/src/payment.rs b/src/payment.rs index 2de2e45..38d9d16 100644 --- a/src/payment.rs +++ b/src/payment.rs @@ -1,100 +1,101 @@ -use anyhow::Result; -use isahc::{ReadResponseExt, RequestExt}; -use serde::Deserialize; - -use crate::{BASE_URL, BunqClient, MonetaryAccount, Pagination, Response}; - -pub(super) fn get_from_to(client: &BunqClient, - acc: &MonetaryAccount, - from: Option, - to: Option) -> Result> -{ - let next_page = |url: &str| -> Result<(_, _)> { - let response = isahc::http::Request::get(url) - .header("X-Bunq-Client-Authentication", &client.token) - .body(())? - .send()? - .text()?; - let Response { - response, - pagination, - } = crate::deserialize_normal_response::>(&response)?; - Ok(( - response.into_iter().map(|p| p.payment).collect(), - pagination, - )) - }; - - let account_id = match acc { - MonetaryAccount::MonetaryAccountBank(bank) => bank.id, - MonetaryAccount::MonetaryAccountSavings(savings) => savings.id, - }; - - let mut url = format!( - "/v1/user/{}/monetary-account/{}/payment", - client.user_id, account_id - ); - - if let Some(to) = to { - url = format!("{}?newer_id={}", url, to); - } - - let mut all = Vec::new(); - loop { - let (mut payments, pag) = next_page(&format!("{}{}", BASE_URL, url))?; - dbg!(&payments); - all.append(&mut payments); - dbg!(&pag); - if let Some(Pagination { - older_url: Some(older_url), - .. - }) = pag - { - if let (Some(latest), Some(from)) = (all.last(), from) { - if latest.id <= from { - all.retain(|p| p.id >= from); - break; - } - } - url = older_url; - } else { - break; - } - } - Ok(all) -} - -#[derive(Deserialize, Debug)] -pub struct LabelMonetaryAccount { - pub iban: Option, - pub display_name: String, - pub merchant_category_code: Option, -} - -#[derive(Deserialize, Debug)] -pub struct Amount { - pub value: String, - pub currency: String, -} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "PascalCase")] -struct PaymentPayment { - payment: Payment, -} - -#[derive(Deserialize, Debug)] -pub struct Payment { - pub alias: LabelMonetaryAccount, - pub counterparty_alias: LabelMonetaryAccount, - pub amount: Amount, - pub balance_after_mutation: Amount, - pub created: String, - pub updated: String, - pub description: String, - pub id: i64, - pub monetary_account_id: i64, - #[serde(rename = "type")] - pub type_: String, - pub sub_type: String, -} \ No newline at end of file +use anyhow::Result; +use isahc::{ReadResponseExt, RequestExt}; +use serde::Deserialize; + +use crate::{BunqClient, MonetaryAccount, Pagination, Response, BASE_URL}; + +pub(super) fn get_from_to( + client: &BunqClient, + acc: &MonetaryAccount, + from: Option, + to: Option, +) -> Result> { + let next_page = |url: &str| -> Result<(_, _)> { + let response = isahc::http::Request::get(url) + .header("X-Bunq-Client-Authentication", &client.token) + .body(())? + .send()? + .text()?; + let Response { + response, + pagination, + } = crate::deserialize_normal_response::>(&response)?; + Ok(( + response.into_iter().map(|p| p.payment).collect(), + pagination, + )) + }; + + let account_id = match acc { + MonetaryAccount::MonetaryAccountBank(bank) => bank.id, + MonetaryAccount::MonetaryAccountSavings(savings) => savings.id, + }; + + let mut url = format!( + "/v1/user/{}/monetary-account/{}/payment", + client.user_id, account_id + ); + + if let Some(to) = to { + url = format!("{}?newer_id={}", url, to); + } + + let mut all = Vec::new(); + loop { + let (mut payments, pag) = next_page(&format!("{}{}", BASE_URL, url))?; + dbg!(&payments); + all.append(&mut payments); + dbg!(&pag); + if let Some(Pagination { + older_url: Some(older_url), + .. + }) = pag + { + if let (Some(latest), Some(from)) = (all.last(), from) { + if latest.id <= from { + all.retain(|p| p.id >= from); + break; + } + } + url = older_url; + } else { + break; + } + } + Ok(all) +} + +#[derive(Deserialize, Debug)] +pub struct LabelMonetaryAccount { + pub iban: Option, + pub display_name: String, + pub merchant_category_code: Option, +} + +#[derive(Deserialize, Debug)] +pub struct Amount { + pub value: String, + pub currency: String, +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "PascalCase")] +struct PaymentPayment { + payment: Payment, +} + +#[derive(Deserialize, Debug)] +pub struct Payment { + pub alias: LabelMonetaryAccount, + pub counterparty_alias: LabelMonetaryAccount, + pub amount: Amount, + pub balance_after_mutation: Amount, + pub created: String, + pub updated: String, + pub description: String, + pub id: i64, + pub monetary_account_id: i64, + #[serde(rename = "type")] + pub type_: String, + pub sub_type: String, +}