From 7a7e12816db7c5398bb55447effea1f2d9b98bf0 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Wed, 17 Jul 2024 08:30:01 +0200 Subject: [PATCH 01/36] Some basic support for global options in agama-cli --- rust/agama-cli/src/auth.rs | 6 +++--- rust/agama-cli/src/lib.rs | 14 +++++++++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/rust/agama-cli/src/auth.rs b/rust/agama-cli/src/auth.rs index 14fa68ab0d..3629291232 100644 --- a/rust/agama-cli/src/auth.rs +++ b/rust/agama-cli/src/auth.rs @@ -45,7 +45,7 @@ pub enum AuthCommands { /// Main entry point called from agama CLI main loop pub async fn run(subcommand: AuthCommands) -> anyhow::Result<()> { match subcommand { - AuthCommands::Login => login(read_password()?).await, + AuthCommands::Login => login(DEFAULT_AUTH_URL.to_string(), read_password()?).await, AuthCommands::Logout => logout(), AuthCommands::Show => show(), } @@ -108,9 +108,9 @@ async fn get_jwt(url: String, password: String) -> anyhow::Result { } /// Logs into the installation web server and stores JWT for later use. -async fn login(password: String) -> anyhow::Result<()> { +async fn login(server: String, password: String) -> anyhow::Result<()> { // 1) ask web server for JWT - let res = get_jwt(DEFAULT_AUTH_URL.to_string(), password).await?; + let res = get_jwt(server, password).await?; let token = AuthToken::new(&res); Ok(token.write_user_token()?) } diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index 42d265e557..129a7299ac 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -18,7 +18,7 @@ // To contact SUSE LLC about this file by physical or electronic mail, you may // find current contact information at www.suse.com. -use clap::Parser; +use clap::{Args, Parser}; mod auth; mod commands; @@ -46,6 +46,15 @@ use std::{ time::Duration, }; +/// Agama's CLI global options +#[derive(Args)] +struct GlobalOpts { + #[clap(long)] + /// uri pointing to agama's remote api. If not provided, default https://localhost/api is + /// used + pub uri: String, +} + /// Agama's command-line interface /// /// This program allows inspecting or changing Agama's configuration, handling installation @@ -55,6 +64,9 @@ use std::{ #[derive(Parser)] #[command(name = "agama", about, long_about, max_term_width = 100)] pub struct Cli { + #[clap(flatten)] + pub opts: GlobalOpts, + #[command(subcommand)] pub command: Commands, } From 927c3387bba093edf3f48024aa7157b7eb14ad6e Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Wed, 24 Jul 2024 10:18:04 +0200 Subject: [PATCH 02/36] auth commmand rewritten using BaseHTTPClient --- rust/agama-cli/src/auth.rs | 91 +++++++++++++++++++++++--------------- rust/agama-cli/src/lib.rs | 4 +- 2 files changed, 58 insertions(+), 37 deletions(-) diff --git a/rust/agama-cli/src/auth.rs b/rust/agama-cli/src/auth.rs index 3629291232..62ccb2aa97 100644 --- a/rust/agama-cli/src/auth.rs +++ b/rust/agama-cli/src/auth.rs @@ -18,15 +18,62 @@ // To contact SUSE LLC about this file by physical or electronic mail, you may // find current contact information at www.suse.com. -use agama_lib::auth::AuthToken; +use agama_lib::{auth::AuthToken, error::ServiceError}; use clap::Subcommand; +use agama_lib::base_http_client::BaseHTTPClient; use crate::error::CliError; use inquire::Password; use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE}; use std::io::{self, IsTerminal}; -const DEFAULT_AUTH_URL: &str = "http://localhost/api/auth"; +/// HTTP Client for auth queries +struct AuthHTTPClient { + api: BaseHTTPClient, +} + +impl AuthHTTPClient { + pub fn load(client: BaseHTTPClient) -> Result { + Ok( Self { + api: client, + }) + } + + /// Necessary http request header for authenticate + fn authenticate_headers() -> HeaderMap { + let mut headers = HeaderMap::new(); + + headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); + + headers + } + + /// Query web server for JWT + /// + /// TODO: + /// for now it doesn't use BaseHTTPClient's post and similar methods as it needs + /// to update query headers + pub async fn get_jwt(&self, password: String) -> anyhow::Result { + let client = reqwest::Client::new(); + let response = client + // TODO: BaseHTTPClient::url is private, so duplicate it for now. + .post(self.api.base_url.clone() + "/auth") + .headers(Self::authenticate_headers()) + .body(format!("{{\"password\": \"{}\"}}", password)) + .send() + .await?; + let body = response + .json::>() + .await?; + let value = body.get("token"); + + if let Some(token) = value { + return Ok(token.clone()); + } + + Err(anyhow::anyhow!("Failed to get authentication token")) + } +} #[derive(Subcommand, Debug)] pub enum AuthCommands { @@ -43,9 +90,11 @@ pub enum AuthCommands { } /// Main entry point called from agama CLI main loop -pub async fn run(subcommand: AuthCommands) -> anyhow::Result<()> { +pub async fn run(client: BaseHTTPClient, subcommand: AuthCommands) -> anyhow::Result<()> { + let auth_client = AuthHTTPClient::load(client)?; + match subcommand { - AuthCommands::Login => login(DEFAULT_AUTH_URL.to_string(), read_password()?).await, + AuthCommands::Login => login(auth_client, read_password()?).await, AuthCommands::Logout => logout(), AuthCommands::Show => show(), } @@ -77,40 +126,10 @@ fn ask_password() -> Result { .map_err(CliError::InteractivePassword) } -/// Necessary http request header for authenticate -fn authenticate_headers() -> HeaderMap { - let mut headers = HeaderMap::new(); - - headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); - - headers -} - -/// Query web server for JWT -async fn get_jwt(url: String, password: String) -> anyhow::Result { - let client = reqwest::Client::new(); - let response = client - .post(url) - .headers(authenticate_headers()) - .body(format!("{{\"password\": \"{}\"}}", password)) - .send() - .await?; - let body = response - .json::>() - .await?; - let value = body.get("token"); - - if let Some(token) = value { - return Ok(token.clone()); - } - - Err(anyhow::anyhow!("Failed to get authentication token")) -} - /// Logs into the installation web server and stores JWT for later use. -async fn login(server: String, password: String) -> anyhow::Result<()> { +async fn login(client: AuthHTTPClient, password: String) -> anyhow::Result<()> { // 1) ask web server for JWT - let res = get_jwt(server, password).await?; + let res = client.get_jwt(password).await?; let token = AuthToken::new(&res); Ok(token.write_user_token()?) } diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index 129a7299ac..dd4b8b49e9 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -151,6 +151,8 @@ async fn build_manager<'a>() -> anyhow::Result> { } pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { + let client = BaseHTTPClient::new()?; + match cli.command { Commands::Config(subcommand) => { let manager = build_manager().await?; @@ -169,7 +171,7 @@ pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { } Commands::Questions(subcommand) => run_questions_cmd(subcommand).await?, Commands::Logs(subcommand) => run_logs_cmd(subcommand).await?, - Commands::Auth(subcommand) => run_auth_cmd(subcommand).await?, + Commands::Auth(subcommand) => run_auth_cmd(client, subcommand).await?, Commands::Download { url } => Transfer::get(&url, std::io::stdout())?, }; From 89e1d9b1041c52c25018fb898eb6fb19eebbba74 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Wed, 24 Jul 2024 10:42:55 +0200 Subject: [PATCH 03/36] Formatting --- rust/agama-cli/src/auth.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/rust/agama-cli/src/auth.rs b/rust/agama-cli/src/auth.rs index 62ccb2aa97..1ec4a85135 100644 --- a/rust/agama-cli/src/auth.rs +++ b/rust/agama-cli/src/auth.rs @@ -21,8 +21,8 @@ use agama_lib::{auth::AuthToken, error::ServiceError}; use clap::Subcommand; -use agama_lib::base_http_client::BaseHTTPClient; use crate::error::CliError; +use agama_lib::base_http_client::BaseHTTPClient; use inquire::Password; use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE}; use std::io::{self, IsTerminal}; @@ -34,9 +34,7 @@ struct AuthHTTPClient { impl AuthHTTPClient { pub fn load(client: BaseHTTPClient) -> Result { - Ok( Self { - api: client, - }) + Ok(Self { api: client }) } /// Necessary http request header for authenticate From 8c6f985f04c1e2dec23c9ac7e1ae839c90ac9787 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 8 Aug 2024 10:58:39 +0200 Subject: [PATCH 04/36] Agama CLI auth login command fully transformed to use new http client --- rust/agama-cli/src/auth.rs | 27 ++++++++------------------ rust/agama-cli/src/lib.rs | 9 +++++++-- rust/agama-lib/src/base_http_client.rs | 9 +++++++++ 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/rust/agama-cli/src/auth.rs b/rust/agama-cli/src/auth.rs index 1ec4a85135..c411e40a46 100644 --- a/rust/agama-cli/src/auth.rs +++ b/rust/agama-cli/src/auth.rs @@ -24,7 +24,7 @@ use clap::Subcommand; use crate::error::CliError; use agama_lib::base_http_client::BaseHTTPClient; use inquire::Password; -use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE}; +use std::collections::HashMap; use std::io::{self, IsTerminal}; /// HTTP Client for auth queries @@ -37,31 +37,19 @@ impl AuthHTTPClient { Ok(Self { api: client }) } - /// Necessary http request header for authenticate - fn authenticate_headers() -> HeaderMap { - let mut headers = HeaderMap::new(); - - headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); - - headers - } - /// Query web server for JWT /// /// TODO: /// for now it doesn't use BaseHTTPClient's post and similar methods as it needs /// to update query headers pub async fn get_jwt(&self, password: String) -> anyhow::Result { - let client = reqwest::Client::new(); - let response = client - // TODO: BaseHTTPClient::url is private, so duplicate it for now. - .post(self.api.base_url.clone() + "/auth") - .headers(Self::authenticate_headers()) - .body(format!("{{\"password\": \"{}\"}}", password)) - .send() - .await?; + let mut auth_body = HashMap::new(); + + auth_body.insert("password", password); + + let response = self.api.post_response("/auth", &auth_body).await?; let body = response - .json::>() + .json::>() .await?; let value = body.get("token"); @@ -104,6 +92,7 @@ pub async fn run(client: BaseHTTPClient, subcommand: AuthCommands) -> anyhow::Re /// user. fn read_password() -> Result { let stdin = io::stdin(); + let password = if stdin.is_terminal() { ask_password()? } else { diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index dd4b8b49e9..ce02ca84d3 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -49,7 +49,7 @@ use std::{ /// Agama's CLI global options #[derive(Args)] struct GlobalOpts { - #[clap(long)] + #[clap(long, default_value = "http://localhost/api/")] /// uri pointing to agama's remote api. If not provided, default https://localhost/api is /// used pub uri: String, @@ -153,6 +153,8 @@ async fn build_manager<'a>() -> anyhow::Result> { pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { let client = BaseHTTPClient::new()?; + // we need to distinguish commands on those which assume that JWT is already available + // and those which not (or don't need it) match cli.command { Commands::Config(subcommand) => { let manager = build_manager().await?; @@ -171,8 +173,11 @@ pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { } Commands::Questions(subcommand) => run_questions_cmd(subcommand).await?, Commands::Logs(subcommand) => run_logs_cmd(subcommand).await?, - Commands::Auth(subcommand) => run_auth_cmd(client, subcommand).await?, Commands::Download { url } => Transfer::get(&url, std::io::stdout())?, + Commands::Auth(subcommand) => { + let client = BaseHTTPClient::bare()?; + run_auth_cmd(client, subcommand).await?; + } }; Ok(()) diff --git a/rust/agama-lib/src/base_http_client.rs b/rust/agama-lib/src/base_http_client.rs index 7e2cb59b34..0b3c00bd0b 100644 --- a/rust/agama-lib/src/base_http_client.rs +++ b/rust/agama-lib/src/base_http_client.rs @@ -61,6 +61,15 @@ impl Default for BaseHTTPClient { } impl BaseHTTPClient { + // Provides new client with no specialities like authorization headers or so. + // If needed, there is place for helpers like "self::to_authorized" or so. + pub fn bare() -> Result { + Ok(Self { + client: reqwest::Client::new(), + base_url: API_URL.to_string(), + }) + } + /// Uses `localhost`, authenticates with [`AuthToken`]. pub fn new() -> Result { Ok(Self { From 38e3e2bc6ade13994fe52ee89b3922793cfb9b66 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 1 Aug 2024 09:19:09 +0200 Subject: [PATCH 05/36] Formatting --- rust/agama-cli/src/auth.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rust/agama-cli/src/auth.rs b/rust/agama-cli/src/auth.rs index c411e40a46..1feff6460c 100644 --- a/rust/agama-cli/src/auth.rs +++ b/rust/agama-cli/src/auth.rs @@ -48,9 +48,7 @@ impl AuthHTTPClient { auth_body.insert("password", password); let response = self.api.post_response("/auth", &auth_body).await?; - let body = response - .json::>() - .await?; + let body = response.json::>().await?; let value = body.get("token"); if let Some(token) = value { From bf1613de6193b4eb67a560eb3da0a5d2d5e68d8d Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 1 Aug 2024 09:24:59 +0200 Subject: [PATCH 06/36] Cleanup --- rust/agama-cli/src/auth.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/rust/agama-cli/src/auth.rs b/rust/agama-cli/src/auth.rs index 1feff6460c..433d1aef96 100644 --- a/rust/agama-cli/src/auth.rs +++ b/rust/agama-cli/src/auth.rs @@ -38,10 +38,6 @@ impl AuthHTTPClient { } /// Query web server for JWT - /// - /// TODO: - /// for now it doesn't use BaseHTTPClient's post and similar methods as it needs - /// to update query headers pub async fn get_jwt(&self, password: String) -> anyhow::Result { let mut auth_body = HashMap::new(); From 2715ba760415a67d0510340169882ed792184efc Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 8 Aug 2024 09:59:32 +0200 Subject: [PATCH 07/36] Put custom url to use --- rust/agama-cli/src/auth.rs | 1 + rust/agama-cli/src/lib.rs | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/rust/agama-cli/src/auth.rs b/rust/agama-cli/src/auth.rs index 433d1aef96..4b9620f269 100644 --- a/rust/agama-cli/src/auth.rs +++ b/rust/agama-cli/src/auth.rs @@ -43,6 +43,7 @@ impl AuthHTTPClient { auth_body.insert("password", password); + eprintln!("get_jwt asks url: {}", self.api.base_url); let response = self.api.post_response("/auth", &auth_body).await?; let body = response.json::>().await?; let value = body.get("token"); diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index ce02ca84d3..f9af5acc05 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -49,10 +49,10 @@ use std::{ /// Agama's CLI global options #[derive(Args)] struct GlobalOpts { - #[clap(long, default_value = "http://localhost/api/")] + #[clap(long, default_value = "http://localhost/api")] /// uri pointing to agama's remote api. If not provided, default https://localhost/api is /// used - pub uri: String, + pub api: String, } /// Agama's command-line interface @@ -151,8 +151,6 @@ async fn build_manager<'a>() -> anyhow::Result> { } pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { - let client = BaseHTTPClient::new()?; - // we need to distinguish commands on those which assume that JWT is already available // and those which not (or don't need it) match cli.command { @@ -175,7 +173,10 @@ pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { Commands::Logs(subcommand) => run_logs_cmd(subcommand).await?, Commands::Download { url } => Transfer::get(&url, std::io::stdout())?, Commands::Auth(subcommand) => { - let client = BaseHTTPClient::bare()?; + let mut client = BaseHTTPClient::bare()?; + + client.base_url = cli.opts.api; + run_auth_cmd(client, subcommand).await?; } }; From e2180028587a835f5eb86d732bf0cc8b502396e5 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Fri, 2 Aug 2024 10:59:01 +0200 Subject: [PATCH 08/36] Minor fine tuning on user provided url when needed. --- rust/agama-cli/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index f9af5acc05..38ac3046ca 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -175,7 +175,7 @@ pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { Commands::Auth(subcommand) => { let mut client = BaseHTTPClient::bare()?; - client.base_url = cli.opts.api; + client.base_url = cli.opts.api.strip_suffix("/").unwrap_or(client.base_url.as_str()).to_string(); run_auth_cmd(client, subcommand).await?; } From 96f2ece29692c7735e63644fb903e4722175e932 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 8 Aug 2024 10:04:38 +0200 Subject: [PATCH 09/36] Formatting --- rust/agama-cli/src/lib.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index 38ac3046ca..4b543116c1 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -175,7 +175,12 @@ pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { Commands::Auth(subcommand) => { let mut client = BaseHTTPClient::bare()?; - client.base_url = cli.opts.api.strip_suffix("/").unwrap_or(client.base_url.as_str()).to_string(); + client.base_url = cli + .opts + .api + .strip_suffix("/") + .unwrap_or(client.base_url.as_str()) + .to_string(); run_auth_cmd(client, subcommand).await?; } From eb891282c311d4be02b041170c38343d1b7de9b3 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Wed, 14 Aug 2024 10:25:18 +0200 Subject: [PATCH 10/36] Adapted to changes in agama-lib's base_http_client --- rust/agama-cli/src/auth.rs | 6 ++---- rust/agama-cli/src/lib.rs | 2 +- rust/agama-lib/src/base_http_client.rs | 9 --------- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/rust/agama-cli/src/auth.rs b/rust/agama-cli/src/auth.rs index 4b9620f269..b8d94daf77 100644 --- a/rust/agama-cli/src/auth.rs +++ b/rust/agama-cli/src/auth.rs @@ -43,10 +43,8 @@ impl AuthHTTPClient { auth_body.insert("password", password); - eprintln!("get_jwt asks url: {}", self.api.base_url); - let response = self.api.post_response("/auth", &auth_body).await?; - let body = response.json::>().await?; - let value = body.get("token"); + let response = self.api.post::>("/auth", &auth_body).await?; + let value = response.get("token"); if let Some(token) = value { return Ok(token.clone()); diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index 4b543116c1..4fa74d387d 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -173,7 +173,7 @@ pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { Commands::Logs(subcommand) => run_logs_cmd(subcommand).await?, Commands::Download { url } => Transfer::get(&url, std::io::stdout())?, Commands::Auth(subcommand) => { - let mut client = BaseHTTPClient::bare()?; + let mut client = BaseHTTPClient::default(); client.base_url = cli .opts diff --git a/rust/agama-lib/src/base_http_client.rs b/rust/agama-lib/src/base_http_client.rs index 0b3c00bd0b..7e2cb59b34 100644 --- a/rust/agama-lib/src/base_http_client.rs +++ b/rust/agama-lib/src/base_http_client.rs @@ -61,15 +61,6 @@ impl Default for BaseHTTPClient { } impl BaseHTTPClient { - // Provides new client with no specialities like authorization headers or so. - // If needed, there is place for helpers like "self::to_authorized" or so. - pub fn bare() -> Result { - Ok(Self { - client: reqwest::Client::new(), - base_url: API_URL.to_string(), - }) - } - /// Uses `localhost`, authenticates with [`AuthToken`]. pub fn new() -> Result { Ok(Self { From 8a2ac5ec1875a33fe37904cba801c14a50e7fd62 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Wed, 14 Aug 2024 10:31:44 +0200 Subject: [PATCH 11/36] Formatting --- rust/agama-cli/src/auth.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rust/agama-cli/src/auth.rs b/rust/agama-cli/src/auth.rs index b8d94daf77..4bdef7c378 100644 --- a/rust/agama-cli/src/auth.rs +++ b/rust/agama-cli/src/auth.rs @@ -43,7 +43,10 @@ impl AuthHTTPClient { auth_body.insert("password", password); - let response = self.api.post::>("/auth", &auth_body).await?; + let response = self + .api + .post::>("/auth", &auth_body) + .await?; let value = response.get("token"); if let Some(token) = value { From fe150a985110dbe305207711d956a056afcb0e19 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 22 Aug 2024 09:05:40 +0200 Subject: [PATCH 12/36] Some comments --- rust/agama-cli/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index 4fa74d387d..e5f8409cce 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -170,6 +170,8 @@ pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { install(&manager, 3).await? } Commands::Questions(subcommand) => run_questions_cmd(subcommand).await?, + // TODO: logs command was originally designed with idea that agama's cli and agama + // installation runs on the same machine, so it is unable to do remote connection Commands::Logs(subcommand) => run_logs_cmd(subcommand).await?, Commands::Download { url } => Transfer::get(&url, std::io::stdout())?, Commands::Auth(subcommand) => { From dbd7aabed74fb1754d59abc723648267c7a61ddb Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Mon, 26 Aug 2024 11:00:03 +0200 Subject: [PATCH 13/36] Turned NetworkClient structure to use BaseHTTPClient internally. Access to the backend should be done via http api and preferably the same way everywhere --- rust/agama-cli/$HOME/.agama/agama-jwt | 1 + rust/agama-cli/src/config.rs | 16 ++-- rust/agama-cli/src/lib.rs | 7 +- rust/agama-cli/src/profile.rs | 7 +- rust/agama-lib/src/network/client.rs | 75 ++++-------------- rust/agama-lib/src/network/store.rs | 3 +- rust/agama-lib/src/store.rs | 8 +- rust/agama-logs/journalctl --dmesg_err.log | 4 + rust/agama-logs/journalctl --dmesg_out.log | 0 .../journalctl -u agama-auto_err.log | 4 + .../journalctl -u agama-auto_out.log | 0 rust/agama-logs/journalctl -u agama_err.log | 4 + rust/agama-logs/journalctl -u agama_out.log | 0 .../agama-server/src/.agama-web-server.rs.swp | Bin 0 -> 16384 bytes 14 files changed, 50 insertions(+), 79 deletions(-) create mode 100644 rust/agama-cli/$HOME/.agama/agama-jwt create mode 100644 rust/agama-logs/journalctl --dmesg_err.log create mode 100644 rust/agama-logs/journalctl --dmesg_out.log create mode 100644 rust/agama-logs/journalctl -u agama-auto_err.log create mode 100644 rust/agama-logs/journalctl -u agama-auto_out.log create mode 100644 rust/agama-logs/journalctl -u agama_err.log create mode 100644 rust/agama-logs/journalctl -u agama_out.log create mode 100644 rust/agama-server/src/.agama-web-server.rs.swp diff --git a/rust/agama-cli/$HOME/.agama/agama-jwt b/rust/agama-cli/$HOME/.agama/agama-jwt new file mode 100644 index 0000000000..6b8cb55095 --- /dev/null +++ b/rust/agama-cli/$HOME/.agama/agama-jwt @@ -0,0 +1 @@ +eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MDk4MTQ1Njd9.Zq0L1cg-Mfv4hRhrnTuQHIaULn69yXlWU5BhHs6_CZo \ No newline at end of file diff --git a/rust/agama-cli/src/config.rs b/rust/agama-cli/src/config.rs index b77eeb086a..4b1317b504 100644 --- a/rust/agama-cli/src/config.rs +++ b/rust/agama-cli/src/config.rs @@ -25,7 +25,11 @@ use std::{ }; use crate::show_progress; -use agama_lib::{auth::AuthToken, install_settings::InstallSettings, Store as SettingsStore}; +use agama_lib::{ + connection, + install_settings::InstallSettings, Store as SettingsStore, + base_http_client::BaseHTTPClient, +}; use anyhow::anyhow; use clap::Subcommand; use std::io::Write; @@ -59,14 +63,8 @@ pub enum ConfigCommands { }, } -pub async fn run(subcommand: ConfigCommands) -> anyhow::Result<()> { - let Some(token) = AuthToken::find() else { - println!("You need to login for generating a valid token: agama auth login"); - return Ok(()); - }; - - let client = agama_lib::http_client(token.as_str())?; - let store = SettingsStore::new(client).await?; +pub async fn run(http_client: BaseHTTPClient, subcommand: ConfigCommands) -> anyhow::Result<()> { + let store = SettingsStore::new(connection().await?, http_client).await?; match subcommand { ConfigCommands::Show => { diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index e5f8409cce..1dce64da31 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -155,9 +155,10 @@ pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { // and those which not (or don't need it) match cli.command { Commands::Config(subcommand) => { - let manager = build_manager().await?; - wait_for_services(&manager).await?; - run_config_cmd(subcommand).await? + // this deals with authentication need inside + let client = BaseHTTPClient::new()?; + + run_config_cmd(client, subcommand).await? } Commands::Probe => { let manager = build_manager().await?; diff --git a/rust/agama-cli/src/profile.rs b/rust/agama-cli/src/profile.rs index 9585cb4c37..698edd62ce 100644 --- a/rust/agama-cli/src/profile.rs +++ b/rust/agama-cli/src/profile.rs @@ -19,7 +19,8 @@ // find current contact information at www.suse.com. use agama_lib::{ - auth::AuthToken, + base_http_client::BaseHTTPClient, + connection, install_settings::InstallSettings, profile::{AutoyastProfile, ProfileEvaluator, ProfileValidator, ValidationResult}, transfer::Transfer, @@ -153,9 +154,7 @@ async fn import(url_string: String, dir: Option) -> anyhow::Result<()> } async fn store_settings>(path: P) -> anyhow::Result<()> { - let token = AuthToken::find().context("You are not logged in")?; - let client = agama_lib::http_client(token.as_str())?; - let store = SettingsStore::new(client).await?; + let store = SettingsStore::new(connection().await?, BaseHTTPClient::new()?).await?; let settings = InstallSettings::from_file(&path)?; store.store(&settings).await?; Ok(()) diff --git a/rust/agama-lib/src/network/client.rs b/rust/agama-lib/src/network/client.rs index 058f2b96fe..a722e0cf86 100644 --- a/rust/agama-lib/src/network/client.rs +++ b/rust/agama-lib/src/network/client.rs @@ -19,72 +19,36 @@ // find current contact information at www.suse.com. use super::{settings::NetworkConnection, types::Device}; +use crate::base_http_client::BaseHTTPClient; use crate::error::ServiceError; -use reqwest::{Client, Response}; -use serde_json; - -const API_URL: &str = "http://localhost/api/network"; /// HTTP/JSON client for the network service pub struct NetworkClient { - pub client: Client, + pub client: BaseHTTPClient, } impl NetworkClient { - pub async fn new(client: Client) -> Result { + pub async fn new(client: BaseHTTPClient) -> Result { Ok(Self { client }) } - async fn text_for(&self, response: Response) -> Result { - let status = response.status(); - let text = response - .text() - .await - .map_err(|e| ServiceError::NetworkClientError(e.to_string()))?; - - if status != 200 { - return Err(ServiceError::NetworkClientError(text)); - } - - Ok(text) - } - - async fn get(&self, path: &str) -> Result { - let response = self - .client - .get(format!("{API_URL}{path}")) - .send() - .await - .map_err(|e| ServiceError::NetworkClientError(e.to_string()))?; - - self.text_for(response).await - } - /// Returns an array of network devices pub async fn devices(&self) -> Result, ServiceError> { - let text = self.get("/devices").await?; - - let json: Vec = serde_json::from_str(&text) - .map_err(|e| ServiceError::NetworkClientError(e.to_string()))?; + let json = self.client.get::>("/network/devices").await?; Ok(json) } /// Returns an array of network connections pub async fn connections(&self) -> Result, ServiceError> { - let text = self.get("/connections").await?; - - let json: Vec = serde_json::from_str(&text) - .map_err(|e| ServiceError::NetworkClientError(e.to_string()))?; + let json = self.client.get::>("/network/connections").await?; Ok(json) } /// Returns an array of network connections pub async fn connection(&self, id: &str) -> Result { - let text = self.get(format!("/connections/{id}").as_str()).await?; - let json: NetworkConnection = serde_json::from_str(&text) - .map_err(|e| ServiceError::NetworkClientError(e.to_string()))?; + let json = self.client.get::(format!("/network/connections/{id}").as_str()).await?; Ok(json) } @@ -98,20 +62,10 @@ impl NetworkClient { let response = self.connection(id.as_str()).await; if response.is_ok() { - let path = format!("{API_URL}/connections/{id}"); - self.client - .put(path) - .json(&connection) - .send() - .await - .map_err(|e| ServiceError::NetworkClientError(e.to_string()))?; + let path = format!("/network/connections/{id}"); + self.client.put_void(&path.as_str(), &connection).await? } else { - self.client - .post(format!("{API_URL}/connections").as_str()) - .json(&connection) - .send() - .await - .map_err(|e| ServiceError::NetworkClientError(e.to_string()))?; + self.client.post_void(format!("/network/connections").as_str(), &connection).await? } Ok(()) @@ -119,11 +73,12 @@ impl NetworkClient { /// Returns an array of network connections pub async fn apply(&self) -> Result<(), ServiceError> { - self.client - .put(format!("{API_URL}/system/apply")) - .send() - .await - .map_err(|e| ServiceError::NetworkClientError(e.to_string()))?; + // trying to be tricky here. If something breaks then we need a put method on + // BaseHTTPClient which doesn't require a serialiable object for the body + let empty_body: [String; 0] = []; + + eprintln!("Trying to be tricky works?"); + self.client.put_void(&format!("/network/system/apply").as_str(), &empty_body).await?; Ok(()) } diff --git a/rust/agama-lib/src/network/store.rs b/rust/agama-lib/src/network/store.rs index 59b5137342..3b7378ad5e 100644 --- a/rust/agama-lib/src/network/store.rs +++ b/rust/agama-lib/src/network/store.rs @@ -19,6 +19,7 @@ // find current contact information at www.suse.com. use super::settings::NetworkConnection; +use crate::base_http_client::BaseHTTPClient; use crate::error::ServiceError; use crate::network::{NetworkClient, NetworkSettings}; @@ -28,7 +29,7 @@ pub struct NetworkStore { } impl NetworkStore { - pub async fn new(client: reqwest::Client) -> Result { + pub async fn new(client: BaseHTTPClient) -> Result { Ok(Self { network_client: NetworkClient::new(client).await?, }) diff --git a/rust/agama-lib/src/store.rs b/rust/agama-lib/src/store.rs index 83ac062756..36e4e4018c 100644 --- a/rust/agama-lib/src/store.rs +++ b/rust/agama-lib/src/store.rs @@ -21,6 +21,7 @@ //! Load/store the settings from/to the D-Bus services. // TODO: quickly explain difference between FooSettings and FooStore, with an example +use crate::base_http_client::BaseHTTPClient; use crate::error::ServiceError; use crate::install_settings::InstallSettings; use crate::{ @@ -43,8 +44,11 @@ pub struct Store { localization: LocalizationStore, } -impl Store { - pub async fn new(http_client: reqwest::Client) -> Result { +impl<'a> Store<'a> { + pub async fn new( + connection: Connection, + http_client: BaseHTTPClient + ) -> Result, ServiceError> { Ok(Self { localization: LocalizationStore::new()?, users: UsersStore::new()?, diff --git a/rust/agama-logs/journalctl --dmesg_err.log b/rust/agama-logs/journalctl --dmesg_err.log new file mode 100644 index 0000000000..a048ad4629 --- /dev/null +++ b/rust/agama-logs/journalctl --dmesg_err.log @@ -0,0 +1,4 @@ +Hint: You are currently not seeing messages from other users and the system. + Users in the 'systemd-journal' group can see all messages. Pass -q to + turn off this notice. +No journal files were opened due to insufficient permissions. diff --git a/rust/agama-logs/journalctl --dmesg_out.log b/rust/agama-logs/journalctl --dmesg_out.log new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rust/agama-logs/journalctl -u agama-auto_err.log b/rust/agama-logs/journalctl -u agama-auto_err.log new file mode 100644 index 0000000000..a048ad4629 --- /dev/null +++ b/rust/agama-logs/journalctl -u agama-auto_err.log @@ -0,0 +1,4 @@ +Hint: You are currently not seeing messages from other users and the system. + Users in the 'systemd-journal' group can see all messages. Pass -q to + turn off this notice. +No journal files were opened due to insufficient permissions. diff --git a/rust/agama-logs/journalctl -u agama-auto_out.log b/rust/agama-logs/journalctl -u agama-auto_out.log new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rust/agama-logs/journalctl -u agama_err.log b/rust/agama-logs/journalctl -u agama_err.log new file mode 100644 index 0000000000..a048ad4629 --- /dev/null +++ b/rust/agama-logs/journalctl -u agama_err.log @@ -0,0 +1,4 @@ +Hint: You are currently not seeing messages from other users and the system. + Users in the 'systemd-journal' group can see all messages. Pass -q to + turn off this notice. +No journal files were opened due to insufficient permissions. diff --git a/rust/agama-logs/journalctl -u agama_out.log b/rust/agama-logs/journalctl -u agama_out.log new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rust/agama-server/src/.agama-web-server.rs.swp b/rust/agama-server/src/.agama-web-server.rs.swp new file mode 100644 index 0000000000000000000000000000000000000000..b1d680df9d521396cb609103a4ee47d3765b52ad GIT binary patch literal 16384 zcmeHOZ;T{G6>kv*R8;Wa2Z+?onX^adX7&y|v~PDWd%Jsa;f`Hy?+(KSJ2gEuJH_^N zw_V*cJ1pxTP(X~)nE1iy7f=&1YGR^^e=bp@iHTo`B$5bX{2;+ZFrxTLMSriVdZu^p zHlW}a)06w%^i)^9diCC`SMR;Fx8JmBE-M#p*NE%V(^T;@36zQS=r8RZ+) z{sNyY@3%uS6r$5qxe~kf{;($4>g<=1%6J%xw3X&6Ywk;_Z5x}Z z+LdT^qmn=+ffp%(%bjZv9+;xeduw~xo31?lBF9zTDhX5)s3cHHpprl(fl30E1S$zs z68L|RK$cwWd;|-*$S!7JzhANA`(O5Rt8E|dxc|Jp-?i<(+;RVv_IHtSTyG~R=CQY{ zuSx=y1S$zs5~w6lNuZKIC4ou;l>{mYR1&BpP)Xo_AOYTSoa3N7O$7iv|1a|YzrDe6 zo&g>Oz5{##_!J<3Ho$>-paonF{OR?M^GD!O;1|GGfqQ^EfFW=*a0s{xc;<4)`7v-8 za2s$0csFnf@R!#)&a=RGf%|}af!l#wfoWhL@XTu+=XbzwfhT~U0pAC{0c-&R_y8~i z)Bpxt1N`$fj`J|^86X4B0yhIU0yhAAfNOxa1D62L;XR0lfV04*z+W$QoIe1M0q20b zfjfZ@0;|9x@J`^VOTZuSbKo5CE#R}j3a|vUfHwn|0e^or`T-vWMt~0-1oi+9@cgU5 zH}Ev@Xw*t_*PP){h)^N_1GO#Y`{8nG#`lzmjW_ z=&;flCc7-c>n3RBQHQB4P6d-0>&7VyK{I9gHIy>dEaicW);v!}-FS~%n1 z`h=@3CPF3H<~BC7Q2Fg#h8>YMmB{=gl>=HD7VmduDBKmE^^WFU&+Ddf-zAwe*s2PT z;a)aMgy*d+p4hqeom?J|d54%pSCXp7Hn(aE$a-LZ4d-?mk*oo$a4xCxrr^(`H-mwz_(p6{N9xvIRa_E8Q)x7a~~`kw{^O7`@H3Azw%KlaQ^_ zYgTjVn!+03sW) zAdWIFBjPRV2?lEH^b%S-qNzJ~+PU(1r;`e$Jhqyp=xOL$Y7C#A2ova{b_x_0_8-l` zzZs!7HKvF#o1P-pv&_*uV_0E2kb$V_d5DZP4u8PIToAoHk{e!vtvG{s%_8$=Sqlu+ z3U4oLycIWnPM9=`W8uiEGV-aE2j&rhXlzo!}Csck(*#L(z_e3tIxCD7jhF6 z_yR0z2VwR1pR#4W7daXBGP7m%c<@Dw%q)S2&@S}e;V`kn79hw_s!T*Ij$V?iE@+Mu zGjE)4NS^0iJ2f>0_gk3iN-F!iGB?>Mh3&?XVtEu|6RvEsIC}(@Xxs z(#d*LMp^9Ejh8hJg*MRSihiXc%hQO_M%;EnP(xE}K&Q$r`+D-!bPrp2GWot9j*oit z_E<)4tr^Av4_m6oQ(?Asab(+rXL;S#{Xs%yaNZ≻0}gZteZcCl+fo8+>;cBK9W^ z5KO>x)08;6Cmu4%+i+-o=&j?id2^u(pJ8vfDsS6MNY2W5K9tt4lb7*%JI3)d!)P~K z3n`jvI0e#7m7E*B|JvpX)-kuT)U*x@ugEGm=!UcRY%wn`t@2iQ3Xgc6`j&C%@I)GZ z>3ms`Dhc_>%(b$_I&qNq1-v-ooG4jcXi5wB- z{jsbdgfW+^V>y*L#V%19JGFe{;!XasrQ?ep+XoSn2QT^UEM6B;?XY7ypKpt$LAk=K z#1q?S85Lu!Lor02#WLZZr`MUt!@AK1Uq1Axf>gEz&gUc*VctQo_Ac8e9#S(&0q0K9 zwk~6Te6r)bhxZk{U%KmgY7_;Y*G>`81zb@i+~YXYLeptHSjPsNAfaQ$A}rY!IO-oJ zBH1#ddk7~}KhGrM3L|}x$BF5A91Hb49Zrb!{LU1SZ=Q<5{^EuR&=qT1V$mY%x$qj`T@hX1LKO#ZRGu;zg8hf18A7*Xw-{`o;Q#11cS$O!V=Jh zqO`_1TJp7JH5ft!M1GNkBW1QF6$#h#NL7F5_u}U0)D(uZJ zmApFzK;Gu!hRo&>9YNM2g*^4Hv(rJ0NDL(QH;IQlAO?^-Y7|b%c8ozu$2MLUS7gWL z*i?%fnHemhdArP8QkfNU?AldmpSbBz{(lwn%zKfGQvP2&pMM-V{bRrbz!~5Q;3vr4 z?*rz525=4V1akIAfnNa+0bc{|1|$#wZD1B?0#^agA%Fih@IByjz}>(G&;bHq3TOfE z0BXP<;A!OUKLSQT1cbmka2;?p@ECISuLEBJ9t0i$I>0n=CGZdA?oR=a0Ox?u1NQ^> z0rvtva0Ac)7;q6lx&E`jpMi&gZvyuN9|!INJ_e)!<^9(KZv);6JPA4e6nGH$67WUf zlK{#09-#VE5~w6lNuZKIC4rIzOtyqfqOb2vsB8*V>Z6NClXOZTiI1`{LUnV~ zJ3@s*U1YL_Oq&&9jBA<^TT{4GWD!9q>rFo7D5Ih5`opPa0XXd0F z4WrNtJRwx3Uigg)st3IkiCZQTLt@8i`TNIqo?Qx1ZNf(Ro$QDj1P@`Bcf3L}b zK{pkY{p#|ePS=VNAg)VXrLan;z~$L(Myo{!&JUPaiIO6!O=dkRF%6Llv0;jY&162y z>*zUE>Y1b1r&Ko&QGukm8L?i|p&ZfVJcSm)4-q~I#RvtOCb`44E?;)bgDqRgSy5Tk z(x)m+b4Y=kk5YlF1Jou*&3T04-lY6UifU5&;8L(y6eV>ll`!3@_`0bnLs2(siY^KO zqSR>W?WNYDP^wEoGi*nQX-WZRJoVJPK}1FE(L~eG59nqv2&l{~M0eDTnoN!Az0}A2 z(z+zWFneIyty4|ZN@ijo^EUl9Q7`gZHZ!a1s<<3mv&~Lv$&>y%cXeZ3;Z&$V&+7Ui zo<7=0uC4SHneP1_4BjSYOq{y}K~n}`Yss5A19EX}jvoWG;~l zF;Yd+DOTjxn^}y93nI%#{_QHphHB=~Fy#qsK#SIP9Is+`2L7wmf4gNmB+>1epZVo^ zRF=`EYEn>Ty$LcP8bcq55P$R=WQ7BvwylX7(obvrbXk0m5|69v#bW`Zo1zDO)1=7A zckjS2e9QVGmGEKY;;)j^tf=ri1^H$l!oHK=3C)k2uq{*6yy!RW0J~(5oe{ybKa0I! z3ycDPxJ;cU<~zR8Omfw8xglb`vwl9p>EQ?p?y70~9;9tRREljmrh$$F5$vJz{J8gR zR^m4Aq;dA>Am3kDruoKEK}BhD?vjV{4pRW?a`>K3jqHm?pZEBytP$Nqi=dd zm3xm@R&E>@{@^PM7lMa+@cS2f^oIhKE@{#cpqfT{7xMm%iX-ea zYCU*L0H+QX Date: Mon, 26 Aug 2024 11:16:20 +0200 Subject: [PATCH 14/36] Formatting --- rust/agama-cli/src/config.rs | 5 ++--- rust/agama-lib/src/network/client.rs | 19 ++++++++++++++----- rust/agama-lib/src/store.rs | 2 +- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/rust/agama-cli/src/config.rs b/rust/agama-cli/src/config.rs index 4b1317b504..763ae37e20 100644 --- a/rust/agama-cli/src/config.rs +++ b/rust/agama-cli/src/config.rs @@ -26,9 +26,8 @@ use std::{ use crate::show_progress; use agama_lib::{ - connection, - install_settings::InstallSettings, Store as SettingsStore, - base_http_client::BaseHTTPClient, + base_http_client::BaseHTTPClient, connection, install_settings::InstallSettings, + Store as SettingsStore, }; use anyhow::anyhow; use clap::Subcommand; diff --git a/rust/agama-lib/src/network/client.rs b/rust/agama-lib/src/network/client.rs index a722e0cf86..8da3a6894b 100644 --- a/rust/agama-lib/src/network/client.rs +++ b/rust/agama-lib/src/network/client.rs @@ -41,14 +41,20 @@ impl NetworkClient { /// Returns an array of network connections pub async fn connections(&self) -> Result, ServiceError> { - let json = self.client.get::>("/network/connections").await?; + let json = self + .client + .get::>("/network/connections") + .await?; Ok(json) } /// Returns an array of network connections pub async fn connection(&self, id: &str) -> Result { - let json = self.client.get::(format!("/network/connections/{id}").as_str()).await?; + let json = self + .client + .get::(format!("/network/connections/{id}").as_str()) + .await?; Ok(json) } @@ -65,7 +71,9 @@ impl NetworkClient { let path = format!("/network/connections/{id}"); self.client.put_void(&path.as_str(), &connection).await? } else { - self.client.post_void(format!("/network/connections").as_str(), &connection).await? + self.client + .post_void(format!("/network/connections").as_str(), &connection) + .await? } Ok(()) @@ -77,8 +85,9 @@ impl NetworkClient { // BaseHTTPClient which doesn't require a serialiable object for the body let empty_body: [String; 0] = []; - eprintln!("Trying to be tricky works?"); - self.client.put_void(&format!("/network/system/apply").as_str(), &empty_body).await?; + self.client + .put_void(&format!("/network/system/apply").as_str(), &empty_body) + .await?; Ok(()) } diff --git a/rust/agama-lib/src/store.rs b/rust/agama-lib/src/store.rs index 36e4e4018c..1c1de8799a 100644 --- a/rust/agama-lib/src/store.rs +++ b/rust/agama-lib/src/store.rs @@ -47,7 +47,7 @@ pub struct Store { impl<'a> Store<'a> { pub async fn new( connection: Connection, - http_client: BaseHTTPClient + http_client: BaseHTTPClient, ) -> Result, ServiceError> { Ok(Self { localization: LocalizationStore::new()?, From fb56ac7b3dd701f8b4c8ba7027e85090038b7001 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Tue, 27 Aug 2024 09:01:04 +0200 Subject: [PATCH 15/36] Small refactoring: share the HTTP Client initialization for CLI cmds --- rust/agama-cli/src/lib.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index 1dce64da31..b3385cf120 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -153,11 +153,23 @@ async fn build_manager<'a>() -> anyhow::Result> { pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { // we need to distinguish commands on those which assume that JWT is already available // and those which not (or don't need it) + let mut client = if matches!(cli.command, Commands::Auth(_)) { + BaseHTTPClient::default() + } + else { + // this deals with authentication need inside + BaseHTTPClient::new()? + }; + + client.base_url = cli + .opts + .api + .strip_suffix("/") + .unwrap_or(client.base_url.as_str()) + .to_string(); + match cli.command { Commands::Config(subcommand) => { - // this deals with authentication need inside - let client = BaseHTTPClient::new()?; - run_config_cmd(client, subcommand).await? } Commands::Probe => { @@ -176,15 +188,6 @@ pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { Commands::Logs(subcommand) => run_logs_cmd(subcommand).await?, Commands::Download { url } => Transfer::get(&url, std::io::stdout())?, Commands::Auth(subcommand) => { - let mut client = BaseHTTPClient::default(); - - client.base_url = cli - .opts - .api - .strip_suffix("/") - .unwrap_or(client.base_url.as_str()) - .to_string(); - run_auth_cmd(client, subcommand).await?; } }; From a32b6a8ee59a37217781135a366a2ad04c7db138 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 29 Aug 2024 09:25:12 +0200 Subject: [PATCH 16/36] Formatting --- rust/agama-cli/src/lib.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index b3385cf120..75e452c6df 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -153,10 +153,9 @@ async fn build_manager<'a>() -> anyhow::Result> { pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { // we need to distinguish commands on those which assume that JWT is already available // and those which not (or don't need it) - let mut client = if matches!(cli.command, Commands::Auth(_)) { + let mut client = if let Commands::Auth(_) = cli.command { BaseHTTPClient::default() - } - else { + } else { // this deals with authentication need inside BaseHTTPClient::new()? }; @@ -169,9 +168,7 @@ pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { .to_string(); match cli.command { - Commands::Config(subcommand) => { - run_config_cmd(client, subcommand).await? - } + Commands::Config(subcommand) => run_config_cmd(client, subcommand).await?, Commands::Probe => { let manager = build_manager().await?; wait_for_services(&manager).await?; From 989d1183d4efbe9861728718a70b395e15799a7a Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 29 Aug 2024 10:57:33 +0200 Subject: [PATCH 17/36] Adapted LocalizationStore to accept base http client created outside Done the same way as in similar NetworkStore --- rust/agama-lib/src/base_http_client.rs | 1 + rust/agama-lib/src/localization/store.rs | 5 +++-- rust/agama-lib/src/store.rs | 10 +++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/rust/agama-lib/src/base_http_client.rs b/rust/agama-lib/src/base_http_client.rs index 7e2cb59b34..6ffd4b0c96 100644 --- a/rust/agama-lib/src/base_http_client.rs +++ b/rust/agama-lib/src/base_http_client.rs @@ -40,6 +40,7 @@ use crate::{auth::AuthToken, error::ServiceError}; /// client.get("/questions").await /// } /// ``` + #[derive(Clone)] pub struct BaseHTTPClient { client: reqwest::Client, diff --git a/rust/agama-lib/src/localization/store.rs b/rust/agama-lib/src/localization/store.rs index c4294df549..7b5b36071b 100644 --- a/rust/agama-lib/src/localization/store.rs +++ b/rust/agama-lib/src/localization/store.rs @@ -22,6 +22,7 @@ // TODO: for an overview see crate::store (?) use super::{LocalizationHTTPClient, LocalizationSettings}; +use crate::base_http_client::BaseHTTPClient; use crate::error::ServiceError; use crate::localization::model::LocaleConfig; @@ -31,9 +32,9 @@ pub struct LocalizationStore { } impl LocalizationStore { - pub fn new() -> Result { + pub fn new(client: BaseHTTPClient) -> Result { Ok(Self { - localization_client: LocalizationHTTPClient::new()?, + localization_client: LocalizationHTTPClient::new_with_base(client)?, }) } diff --git a/rust/agama-lib/src/store.rs b/rust/agama-lib/src/store.rs index 1c1de8799a..ecdef07ee4 100644 --- a/rust/agama-lib/src/store.rs +++ b/rust/agama-lib/src/store.rs @@ -50,12 +50,12 @@ impl<'a> Store<'a> { http_client: BaseHTTPClient, ) -> Result, ServiceError> { Ok(Self { - localization: LocalizationStore::new()?, + localization: LocalizationStore::new(http_client.clone())?, users: UsersStore::new()?, - network: NetworkStore::new(http_client).await?, - product: ProductStore::new()?, - software: SoftwareStore::new()?, - storage: StorageStore::new()?, + network: NetworkStore::new(http_client.clone()).await?, + product: ProductStore::new(connection.clone()).await?, + software: SoftwareStore::new(connection.clone()).await?, + storage: StorageStore::new(connection).await?, }) } From c7a03ada20de4760743c449172fad3f31ae749e9 Mon Sep 17 00:00:00 2001 From: Martin Vidner Date: Thu, 29 Aug 2024 13:45:24 +0200 Subject: [PATCH 18/36] FIXME revert: install dbus-1-daemon this is a workaround, instead it should be installed in https://github.com/yast/ci-ruby-container --- .github/workflows/ci-rust.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci-rust.yml b/.github/workflows/ci-rust.yml index 45a8efe234..0bb629435b 100644 --- a/.github/workflows/ci-rust.yml +++ b/.github/workflows/ci-rust.yml @@ -69,6 +69,7 @@ jobs: - name: Install required packages run: zypper --non-interactive install + dbus-1-daemon clang-devel dbus-1-daemon jq From f62c5f0d175ec962997ff1fb56e732fe8684c606 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Fri, 30 Aug 2024 07:56:22 +0200 Subject: [PATCH 19/36] Made UsersStore to use new http client internally --- rust/agama-lib/src/store.rs | 2 +- rust/agama-lib/src/users/store.rs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/rust/agama-lib/src/store.rs b/rust/agama-lib/src/store.rs index ecdef07ee4..82f7e812ce 100644 --- a/rust/agama-lib/src/store.rs +++ b/rust/agama-lib/src/store.rs @@ -51,7 +51,7 @@ impl<'a> Store<'a> { ) -> Result, ServiceError> { Ok(Self { localization: LocalizationStore::new(http_client.clone())?, - users: UsersStore::new()?, + users: UsersStore::new(http_client.clone())?, network: NetworkStore::new(http_client.clone()).await?, product: ProductStore::new(connection.clone()).await?, software: SoftwareStore::new(connection.clone()).await?, diff --git a/rust/agama-lib/src/users/store.rs b/rust/agama-lib/src/users/store.rs index 54087f493b..30ef38e4e0 100644 --- a/rust/agama-lib/src/users/store.rs +++ b/rust/agama-lib/src/users/store.rs @@ -19,6 +19,7 @@ // find current contact information at www.suse.com. use super::{FirstUser, FirstUserSettings, RootUserSettings, UserSettings, UsersHTTPClient}; +use crate::base_http_client::BaseHTTPClient; use crate::error::ServiceError; /// Loads and stores the users settings from/to the D-Bus service. @@ -27,9 +28,9 @@ pub struct UsersStore { } impl UsersStore { - pub fn new() -> Result { + pub fn new(client: BaseHTTPClient) -> Result { Ok(Self { - users_client: UsersHTTPClient::new()?, + users_client: UsersHTTPClient::new_with_base(client)?, }) } From 0ccdbd8dfd58a6f175d0076598adeefb994af59b Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Mon, 30 Sep 2024 10:36:02 +0200 Subject: [PATCH 20/36] After merge fixups --- rust/agama-cli/src/config.rs | 4 ++-- rust/agama-cli/src/profile.rs | 3 +-- rust/agama-lib/src/store.rs | 11 +++++------ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/rust/agama-cli/src/config.rs b/rust/agama-cli/src/config.rs index 763ae37e20..9645f76893 100644 --- a/rust/agama-cli/src/config.rs +++ b/rust/agama-cli/src/config.rs @@ -26,7 +26,7 @@ use std::{ use crate::show_progress; use agama_lib::{ - base_http_client::BaseHTTPClient, connection, install_settings::InstallSettings, + base_http_client::BaseHTTPClient, install_settings::InstallSettings, Store as SettingsStore, }; use anyhow::anyhow; @@ -63,7 +63,7 @@ pub enum ConfigCommands { } pub async fn run(http_client: BaseHTTPClient, subcommand: ConfigCommands) -> anyhow::Result<()> { - let store = SettingsStore::new(connection().await?, http_client).await?; + let store = SettingsStore::new(http_client).await?; match subcommand { ConfigCommands::Show => { diff --git a/rust/agama-cli/src/profile.rs b/rust/agama-cli/src/profile.rs index 698edd62ce..e0c01f8d1e 100644 --- a/rust/agama-cli/src/profile.rs +++ b/rust/agama-cli/src/profile.rs @@ -20,7 +20,6 @@ use agama_lib::{ base_http_client::BaseHTTPClient, - connection, install_settings::InstallSettings, profile::{AutoyastProfile, ProfileEvaluator, ProfileValidator, ValidationResult}, transfer::Transfer, @@ -154,7 +153,7 @@ async fn import(url_string: String, dir: Option) -> anyhow::Result<()> } async fn store_settings>(path: P) -> anyhow::Result<()> { - let store = SettingsStore::new(connection().await?, BaseHTTPClient::new()?).await?; + let store = SettingsStore::new(BaseHTTPClient::new()?).await?; let settings = InstallSettings::from_file(&path)?; store.store(&settings).await?; Ok(()) diff --git a/rust/agama-lib/src/store.rs b/rust/agama-lib/src/store.rs index 82f7e812ce..170b0057c2 100644 --- a/rust/agama-lib/src/store.rs +++ b/rust/agama-lib/src/store.rs @@ -44,18 +44,17 @@ pub struct Store { localization: LocalizationStore, } -impl<'a> Store<'a> { +impl Store { pub async fn new( - connection: Connection, http_client: BaseHTTPClient, - ) -> Result, ServiceError> { + ) -> Result { Ok(Self { localization: LocalizationStore::new(http_client.clone())?, users: UsersStore::new(http_client.clone())?, network: NetworkStore::new(http_client.clone()).await?, - product: ProductStore::new(connection.clone()).await?, - software: SoftwareStore::new(connection.clone()).await?, - storage: StorageStore::new(connection).await?, + product: ProductStore::new()?, + software: SoftwareStore::new()?, + storage: StorageStore::new()?, }) } From 572595397b8159db7ed7c047f6daf12e70a24660 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Wed, 2 Oct 2024 09:15:22 +0200 Subject: [PATCH 21/36] Replaced remaining pieces (software, product, storage) to use http clients instead of dbus ones --- rust/agama-lib/src/product/store.rs | 7 ++++--- rust/agama-lib/src/software/store.rs | 5 +++-- rust/agama-lib/src/storage/store.rs | 5 +++-- rust/agama-lib/src/store.rs | 6 +++--- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/rust/agama-lib/src/product/store.rs b/rust/agama-lib/src/product/store.rs index a6dc902093..7760dec037 100644 --- a/rust/agama-lib/src/product/store.rs +++ b/rust/agama-lib/src/product/store.rs @@ -20,6 +20,7 @@ //! Implements the store for the product settings. use super::{ProductHTTPClient, ProductSettings}; +use crate::base_http_client::BaseHTTPClient; use crate::error::ServiceError; use crate::manager::http_client::ManagerHTTPClient; @@ -30,10 +31,10 @@ pub struct ProductStore { } impl ProductStore { - pub fn new() -> Result { + pub fn new(client: BaseHTTPClient) -> Result { Ok(Self { - product_client: ProductHTTPClient::new()?, - manager_client: ManagerHTTPClient::new()?, + product_client: ProductHTTPClient::new_with_base(client.clone()), + manager_client: ManagerHTTPClient::new_with_base(client.clone()), }) } diff --git a/rust/agama-lib/src/software/store.rs b/rust/agama-lib/src/software/store.rs index a152a49177..78fa380631 100644 --- a/rust/agama-lib/src/software/store.rs +++ b/rust/agama-lib/src/software/store.rs @@ -23,6 +23,7 @@ use std::collections::HashMap; use super::{SoftwareHTTPClient, SoftwareSettings}; +use crate::base_http_client::BaseHTTPClient; use crate::error::ServiceError; /// Loads and stores the software settings from/to the D-Bus service. @@ -31,9 +32,9 @@ pub struct SoftwareStore { } impl SoftwareStore { - pub fn new() -> Result { + pub fn new(client: BaseHTTPClient) -> Result { Ok(Self { - software_client: SoftwareHTTPClient::new()?, + software_client: SoftwareHTTPClient::new_with_base(client), }) } diff --git a/rust/agama-lib/src/storage/store.rs b/rust/agama-lib/src/storage/store.rs index 5b8935e280..1ab9a77fb5 100644 --- a/rust/agama-lib/src/storage/store.rs +++ b/rust/agama-lib/src/storage/store.rs @@ -21,6 +21,7 @@ //! Implements the store for the storage settings. use super::StorageSettings; +use crate::base_http_client::BaseHTTPClient; use crate::error::ServiceError; use crate::storage::http_client::StorageHTTPClient; @@ -30,9 +31,9 @@ pub struct StorageStore { } impl StorageStore { - pub fn new() -> Result { + pub fn new(client: BaseHTTPClient) -> Result { Ok(Self { - storage_client: StorageHTTPClient::new()?, + storage_client: StorageHTTPClient::new_with_base(client), }) } diff --git a/rust/agama-lib/src/store.rs b/rust/agama-lib/src/store.rs index 170b0057c2..2f1775aa9b 100644 --- a/rust/agama-lib/src/store.rs +++ b/rust/agama-lib/src/store.rs @@ -52,9 +52,9 @@ impl Store { localization: LocalizationStore::new(http_client.clone())?, users: UsersStore::new(http_client.clone())?, network: NetworkStore::new(http_client.clone()).await?, - product: ProductStore::new()?, - software: SoftwareStore::new()?, - storage: StorageStore::new()?, + product: ProductStore::new(http_client.clone())?, + software: SoftwareStore::new(http_client.clone())?, + storage: StorageStore::new(http_client.clone())?, }) } From 23bd685b87b8c9fdd032751fc2e3d66dc9a00fef Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 3 Oct 2024 09:38:08 +0200 Subject: [PATCH 22/36] Automatically accept self-signed certificates (including other invalid!) --- rust/agama-cli/src/lib.rs | 7 +++---- rust/agama-lib/src/base_http_client.rs | 7 ++++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index 75e452c6df..151b4a7170 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -151,8 +151,8 @@ async fn build_manager<'a>() -> anyhow::Result> { } pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { - // we need to distinguish commands on those which assume that JWT is already available - // and those which not (or don't need it) + // we need to distinguish commands on those which assume that authentication JWT is already + // available and those which not (or don't need it) let mut client = if let Commands::Auth(_) = cli.command { BaseHTTPClient::default() } else { @@ -163,8 +163,7 @@ pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { client.base_url = cli .opts .api - .strip_suffix("/") - .unwrap_or(client.base_url.as_str()) + .trim_end_matches('/') .to_string(); match cli.command { diff --git a/rust/agama-lib/src/base_http_client.rs b/rust/agama-lib/src/base_http_client.rs index 6ffd4b0c96..f1bb529ee2 100644 --- a/rust/agama-lib/src/base_http_client.rs +++ b/rust/agama-lib/src/base_http_client.rs @@ -54,8 +54,12 @@ impl Default for BaseHTTPClient { /// - is NOT authenticated (maybe you want to call `new` instead) /// - uses `localhost` fn default() -> Self { + let default_client = reqwest::Client::new(); + Self { - client: reqwest::Client::new(), + client: reqwest::Client::builder() + .danger_accept_invalid_certs(true) + .build().unwrap_or(default_client), base_url: API_URL.to_owned(), } } @@ -83,6 +87,7 @@ impl BaseHTTPClient { headers.insert(header::AUTHORIZATION, value); let client = reqwest::Client::builder() + .danger_accept_invalid_certs(true) .default_headers(headers) .build()?; Ok(client) From 1f23cd6d9b731bb8be19e320ee5055987eb73c10 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 3 Oct 2024 09:44:23 +0200 Subject: [PATCH 23/36] Cleaning some mess --- rust/agama-cli/$HOME/.agama/agama-jwt | 1 - rust/agama-logs/journalctl --dmesg_err.log | 4 ---- rust/agama-logs/journalctl --dmesg_out.log | 0 .../agama-logs/journalctl -u agama-auto_err.log | 4 ---- .../agama-logs/journalctl -u agama-auto_out.log | 0 rust/agama-logs/journalctl -u agama_err.log | 4 ---- rust/agama-logs/journalctl -u agama_out.log | 0 rust/agama-server/src/.agama-web-server.rs.swp | Bin 16384 -> 0 bytes 8 files changed, 13 deletions(-) delete mode 100644 rust/agama-cli/$HOME/.agama/agama-jwt delete mode 100644 rust/agama-logs/journalctl --dmesg_err.log delete mode 100644 rust/agama-logs/journalctl --dmesg_out.log delete mode 100644 rust/agama-logs/journalctl -u agama-auto_err.log delete mode 100644 rust/agama-logs/journalctl -u agama-auto_out.log delete mode 100644 rust/agama-logs/journalctl -u agama_err.log delete mode 100644 rust/agama-logs/journalctl -u agama_out.log delete mode 100644 rust/agama-server/src/.agama-web-server.rs.swp diff --git a/rust/agama-cli/$HOME/.agama/agama-jwt b/rust/agama-cli/$HOME/.agama/agama-jwt deleted file mode 100644 index 6b8cb55095..0000000000 --- a/rust/agama-cli/$HOME/.agama/agama-jwt +++ /dev/null @@ -1 +0,0 @@ -eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MDk4MTQ1Njd9.Zq0L1cg-Mfv4hRhrnTuQHIaULn69yXlWU5BhHs6_CZo \ No newline at end of file diff --git a/rust/agama-logs/journalctl --dmesg_err.log b/rust/agama-logs/journalctl --dmesg_err.log deleted file mode 100644 index a048ad4629..0000000000 --- a/rust/agama-logs/journalctl --dmesg_err.log +++ /dev/null @@ -1,4 +0,0 @@ -Hint: You are currently not seeing messages from other users and the system. - Users in the 'systemd-journal' group can see all messages. Pass -q to - turn off this notice. -No journal files were opened due to insufficient permissions. diff --git a/rust/agama-logs/journalctl --dmesg_out.log b/rust/agama-logs/journalctl --dmesg_out.log deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/rust/agama-logs/journalctl -u agama-auto_err.log b/rust/agama-logs/journalctl -u agama-auto_err.log deleted file mode 100644 index a048ad4629..0000000000 --- a/rust/agama-logs/journalctl -u agama-auto_err.log +++ /dev/null @@ -1,4 +0,0 @@ -Hint: You are currently not seeing messages from other users and the system. - Users in the 'systemd-journal' group can see all messages. Pass -q to - turn off this notice. -No journal files were opened due to insufficient permissions. diff --git a/rust/agama-logs/journalctl -u agama-auto_out.log b/rust/agama-logs/journalctl -u agama-auto_out.log deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/rust/agama-logs/journalctl -u agama_err.log b/rust/agama-logs/journalctl -u agama_err.log deleted file mode 100644 index a048ad4629..0000000000 --- a/rust/agama-logs/journalctl -u agama_err.log +++ /dev/null @@ -1,4 +0,0 @@ -Hint: You are currently not seeing messages from other users and the system. - Users in the 'systemd-journal' group can see all messages. Pass -q to - turn off this notice. -No journal files were opened due to insufficient permissions. diff --git a/rust/agama-logs/journalctl -u agama_out.log b/rust/agama-logs/journalctl -u agama_out.log deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/rust/agama-server/src/.agama-web-server.rs.swp b/rust/agama-server/src/.agama-web-server.rs.swp deleted file mode 100644 index b1d680df9d521396cb609103a4ee47d3765b52ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeHOZ;T{G6>kv*R8;Wa2Z+?onX^adX7&y|v~PDWd%Jsa;f`Hy?+(KSJ2gEuJH_^N zw_V*cJ1pxTP(X~)nE1iy7f=&1YGR^^e=bp@iHTo`B$5bX{2;+ZFrxTLMSriVdZu^p zHlW}a)06w%^i)^9diCC`SMR;Fx8JmBE-M#p*NE%V(^T;@36zQS=r8RZ+) z{sNyY@3%uS6r$5qxe~kf{;($4>g<=1%6J%xw3X&6Ywk;_Z5x}Z z+LdT^qmn=+ffp%(%bjZv9+;xeduw~xo31?lBF9zTDhX5)s3cHHpprl(fl30E1S$zs z68L|RK$cwWd;|-*$S!7JzhANA`(O5Rt8E|dxc|Jp-?i<(+;RVv_IHtSTyG~R=CQY{ zuSx=y1S$zs5~w6lNuZKIC4ou;l>{mYR1&BpP)Xo_AOYTSoa3N7O$7iv|1a|YzrDe6 zo&g>Oz5{##_!J<3Ho$>-paonF{OR?M^GD!O;1|GGfqQ^EfFW=*a0s{xc;<4)`7v-8 za2s$0csFnf@R!#)&a=RGf%|}af!l#wfoWhL@XTu+=XbzwfhT~U0pAC{0c-&R_y8~i z)Bpxt1N`$fj`J|^86X4B0yhIU0yhAAfNOxa1D62L;XR0lfV04*z+W$QoIe1M0q20b zfjfZ@0;|9x@J`^VOTZuSbKo5CE#R}j3a|vUfHwn|0e^or`T-vWMt~0-1oi+9@cgU5 zH}Ev@Xw*t_*PP){h)^N_1GO#Y`{8nG#`lzmjW_ z=&;flCc7-c>n3RBQHQB4P6d-0>&7VyK{I9gHIy>dEaicW);v!}-FS~%n1 z`h=@3CPF3H<~BC7Q2Fg#h8>YMmB{=gl>=HD7VmduDBKmE^^WFU&+Ddf-zAwe*s2PT z;a)aMgy*d+p4hqeom?J|d54%pSCXp7Hn(aE$a-LZ4d-?mk*oo$a4xCxrr^(`H-mwz_(p6{N9xvIRa_E8Q)x7a~~`kw{^O7`@H3Azw%KlaQ^_ zYgTjVn!+03sW) zAdWIFBjPRV2?lEH^b%S-qNzJ~+PU(1r;`e$Jhqyp=xOL$Y7C#A2ova{b_x_0_8-l` zzZs!7HKvF#o1P-pv&_*uV_0E2kb$V_d5DZP4u8PIToAoHk{e!vtvG{s%_8$=Sqlu+ z3U4oLycIWnPM9=`W8uiEGV-aE2j&rhXlzo!}Csck(*#L(z_e3tIxCD7jhF6 z_yR0z2VwR1pR#4W7daXBGP7m%c<@Dw%q)S2&@S}e;V`kn79hw_s!T*Ij$V?iE@+Mu zGjE)4NS^0iJ2f>0_gk3iN-F!iGB?>Mh3&?XVtEu|6RvEsIC}(@Xxs z(#d*LMp^9Ejh8hJg*MRSihiXc%hQO_M%;EnP(xE}K&Q$r`+D-!bPrp2GWot9j*oit z_E<)4tr^Av4_m6oQ(?Asab(+rXL;S#{Xs%yaNZ≻0}gZteZcCl+fo8+>;cBK9W^ z5KO>x)08;6Cmu4%+i+-o=&j?id2^u(pJ8vfDsS6MNY2W5K9tt4lb7*%JI3)d!)P~K z3n`jvI0e#7m7E*B|JvpX)-kuT)U*x@ugEGm=!UcRY%wn`t@2iQ3Xgc6`j&C%@I)GZ z>3ms`Dhc_>%(b$_I&qNq1-v-ooG4jcXi5wB- z{jsbdgfW+^V>y*L#V%19JGFe{;!XasrQ?ep+XoSn2QT^UEM6B;?XY7ypKpt$LAk=K z#1q?S85Lu!Lor02#WLZZr`MUt!@AK1Uq1Axf>gEz&gUc*VctQo_Ac8e9#S(&0q0K9 zwk~6Te6r)bhxZk{U%KmgY7_;Y*G>`81zb@i+~YXYLeptHSjPsNAfaQ$A}rY!IO-oJ zBH1#ddk7~}KhGrM3L|}x$BF5A91Hb49Zrb!{LU1SZ=Q<5{^EuR&=qT1V$mY%x$qj`T@hX1LKO#ZRGu;zg8hf18A7*Xw-{`o;Q#11cS$O!V=Jh zqO`_1TJp7JH5ft!M1GNkBW1QF6$#h#NL7F5_u}U0)D(uZJ zmApFzK;Gu!hRo&>9YNM2g*^4Hv(rJ0NDL(QH;IQlAO?^-Y7|b%c8ozu$2MLUS7gWL z*i?%fnHemhdArP8QkfNU?AldmpSbBz{(lwn%zKfGQvP2&pMM-V{bRrbz!~5Q;3vr4 z?*rz525=4V1akIAfnNa+0bc{|1|$#wZD1B?0#^agA%Fih@IByjz}>(G&;bHq3TOfE z0BXP<;A!OUKLSQT1cbmka2;?p@ECISuLEBJ9t0i$I>0n=CGZdA?oR=a0Ox?u1NQ^> z0rvtva0Ac)7;q6lx&E`jpMi&gZvyuN9|!INJ_e)!<^9(KZv);6JPA4e6nGH$67WUf zlK{#09-#VE5~w6lNuZKIC4rIzOtyqfqOb2vsB8*V>Z6NClXOZTiI1`{LUnV~ zJ3@s*U1YL_Oq&&9jBA<^TT{4GWD!9q>rFo7D5Ih5`opPa0XXd0F z4WrNtJRwx3Uigg)st3IkiCZQTLt@8i`TNIqo?Qx1ZNf(Ro$QDj1P@`Bcf3L}b zK{pkY{p#|ePS=VNAg)VXrLan;z~$L(Myo{!&JUPaiIO6!O=dkRF%6Llv0;jY&162y z>*zUE>Y1b1r&Ko&QGukm8L?i|p&ZfVJcSm)4-q~I#RvtOCb`44E?;)bgDqRgSy5Tk z(x)m+b4Y=kk5YlF1Jou*&3T04-lY6UifU5&;8L(y6eV>ll`!3@_`0bnLs2(siY^KO zqSR>W?WNYDP^wEoGi*nQX-WZRJoVJPK}1FE(L~eG59nqv2&l{~M0eDTnoN!Az0}A2 z(z+zWFneIyty4|ZN@ijo^EUl9Q7`gZHZ!a1s<<3mv&~Lv$&>y%cXeZ3;Z&$V&+7Ui zo<7=0uC4SHneP1_4BjSYOq{y}K~n}`Yss5A19EX}jvoWG;~l zF;Yd+DOTjxn^}y93nI%#{_QHphHB=~Fy#qsK#SIP9Is+`2L7wmf4gNmB+>1epZVo^ zRF=`EYEn>Ty$LcP8bcq55P$R=WQ7BvwylX7(obvrbXk0m5|69v#bW`Zo1zDO)1=7A zckjS2e9QVGmGEKY;;)j^tf=ri1^H$l!oHK=3C)k2uq{*6yy!RW0J~(5oe{ybKa0I! z3ycDPxJ;cU<~zR8Omfw8xglb`vwl9p>EQ?p?y70~9;9tRREljmrh$$F5$vJz{J8gR zR^m4Aq;dA>Am3kDruoKEK}BhD?vjV{4pRW?a`>K3jqHm?pZEBytP$Nqi=dd zm3xm@R&E>@{@^PM7lMa+@cS2f^oIhKE@{#cpqfT{7xMm%iX-ea zYCU*L0H+QX Date: Mon, 7 Oct 2024 10:15:36 +0200 Subject: [PATCH 24/36] Ask user if insecure connection to api server is allowed --- rust/agama-cli/src/lib.rs | 48 ++++++++++++++++++++++---- rust/agama-lib/src/base_http_client.rs | 26 +++++++++----- 2 files changed, 59 insertions(+), 15 deletions(-) diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index 151b4a7170..c72abd1216 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -36,11 +36,13 @@ use agama_lib::{ use auth::run as run_auth_cmd; use commands::Commands; use config::run as run_config_cmd; +use inquire::Confirm; use logs::run as run_logs_cmd; use profile::run as run_profile_cmd; use progress::InstallerProgress; use questions::run as run_questions_cmd; use std::{ + collections::HashMap, process::{ExitCode, Termination}, thread::sleep, time::Duration, @@ -150,21 +152,53 @@ async fn build_manager<'a>() -> anyhow::Result> { Ok(ManagerClient::new(conn).await?) } +#[derive(PartialEq)] +enum InsecureApi { + Secure, + Insecure, + Forbidden +} + +/// Returns if insecure connection to remote api server is required and user allowed that +async fn require_insecure(api_url: String) -> Result { + // fake client used for remote site detection + let mut ping_client = BaseHTTPClient::default(); + ping_client.base_url = api_url; + + // decide whether access to remote site has to be insecure (self-signed certificate or not) + if let Err(err) = ping_client.get::>("/ping").await { + if Confirm::new("Remote API uses self-signed certificate. Do you want to continue?") + .with_default(false) + .prompt() + .map_err(|_| err)? { + Ok(InsecureApi::Insecure) + } else { + Ok(InsecureApi::Forbidden) + } + } else { + Ok(InsecureApi::Secure) + } +} + pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { + // somehow check whether we need to ask user for self-signed certificate acceptance + let api_url = cli + .opts + .api + .trim_end_matches('/') + .to_string(); + let insecure = require_insecure(api_url.clone()).await? == InsecureApi::Insecure; + // we need to distinguish commands on those which assume that authentication JWT is already // available and those which not (or don't need it) let mut client = if let Commands::Auth(_) = cli.command { - BaseHTTPClient::default() + BaseHTTPClient::bare(insecure) } else { // this deals with authentication need inside - BaseHTTPClient::new()? + BaseHTTPClient::new_with_params(insecure)? }; - client.base_url = cli - .opts - .api - .trim_end_matches('/') - .to_string(); + client.base_url = api_url.clone(); match cli.command { Commands::Config(subcommand) => run_config_cmd(client, subcommand).await?, diff --git a/rust/agama-lib/src/base_http_client.rs b/rust/agama-lib/src/base_http_client.rs index f1bb529ee2..4125686111 100644 --- a/rust/agama-lib/src/base_http_client.rs +++ b/rust/agama-lib/src/base_http_client.rs @@ -54,27 +54,37 @@ impl Default for BaseHTTPClient { /// - is NOT authenticated (maybe you want to call `new` instead) /// - uses `localhost` fn default() -> Self { + Self { + client: reqwest::Client::new(), + base_url: API_URL.to_owned(), + } + } +} + +impl BaseHTTPClient { + pub fn new() -> Result { + Self::new_with_params(false) + } + + pub fn bare(insecure: bool) -> Self { let default_client = reqwest::Client::new(); Self { client: reqwest::Client::builder() - .danger_accept_invalid_certs(true) + .danger_accept_invalid_certs(insecure) .build().unwrap_or(default_client), base_url: API_URL.to_owned(), } } -} - -impl BaseHTTPClient { /// Uses `localhost`, authenticates with [`AuthToken`]. - pub fn new() -> Result { + pub fn new_with_params(insecure: bool) -> Result { Ok(Self { - client: Self::authenticated_client()?, + client: Self::authenticated_client(insecure)?, ..Default::default() }) } - fn authenticated_client() -> Result { + fn authenticated_client(insecure: bool) -> Result { // TODO: this error is subtly misleading, leading me to believe the SERVER said it, // but in fact it is the CLIENT not finding an auth token let token = AuthToken::find().ok_or(ServiceError::NotAuthenticated)?; @@ -87,7 +97,7 @@ impl BaseHTTPClient { headers.insert(header::AUTHORIZATION, value); let client = reqwest::Client::builder() - .danger_accept_invalid_certs(true) + .danger_accept_invalid_certs(insecure) .default_headers(headers) .build()?; Ok(client) From 0960b46f8f608dcade3acda52519535b5ed73fbc Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Mon, 7 Oct 2024 10:27:58 +0200 Subject: [PATCH 25/36] Minor doc --- rust/agama-cli/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index c72abd1216..a56bb435ff 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -154,9 +154,9 @@ async fn build_manager<'a>() -> anyhow::Result> { #[derive(PartialEq)] enum InsecureApi { - Secure, - Insecure, - Forbidden + Secure, /// Remote api is secure + Insecure, /// Remote api is insecure - e.g. self-signed certificate + Forbidden /// Remote api is insecure and its use is forbidden (e.g. user decided not to use it) } /// Returns if insecure connection to remote api server is required and user allowed that From fa4594645aca803fc2242f4497a439e53a11a14f Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Mon, 7 Oct 2024 10:50:42 +0200 Subject: [PATCH 26/36] Minor tweaks --- rust/agama-lib/src/network/client.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rust/agama-lib/src/network/client.rs b/rust/agama-lib/src/network/client.rs index 8da3a6894b..44dcbfee98 100644 --- a/rust/agama-lib/src/network/client.rs +++ b/rust/agama-lib/src/network/client.rs @@ -83,10 +83,8 @@ impl NetworkClient { pub async fn apply(&self) -> Result<(), ServiceError> { // trying to be tricky here. If something breaks then we need a put method on // BaseHTTPClient which doesn't require a serialiable object for the body - let empty_body: [String; 0] = []; - self.client - .put_void(&format!("/network/system/apply").as_str(), &empty_body) + .put_void(&format!("/network/system/apply").as_str(), &()) .await?; Ok(()) From 23828db7c2d8bf0b77a308e5136c6763779b51c5 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Mon, 7 Oct 2024 12:04:35 +0200 Subject: [PATCH 27/36] Small refactoring in remote api detection --- rust/agama-cli/src/lib.rs | 40 ++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index a56bb435ff..5ebde6769d 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -154,29 +154,39 @@ async fn build_manager<'a>() -> anyhow::Result> { #[derive(PartialEq)] enum InsecureApi { - Secure, /// Remote api is secure - Insecure, /// Remote api is insecure - e.g. self-signed certificate - Forbidden /// Remote api is insecure and its use is forbidden (e.g. user decided not to use it) + Secure, // Remote api is secure + Insecure, // Remote api is insecure - e.g. self-signed certificate + Forbidden, // Remote api is insecure and its use is forbidden (e.g. user decided not to use it) + Unrecheable // Remote api is unrecheable } /// Returns if insecure connection to remote api server is required and user allowed that -async fn require_insecure(api_url: String) -> Result { +async fn check_remote_api(api_url: String) -> Result { // fake client used for remote site detection let mut ping_client = BaseHTTPClient::default(); ping_client.base_url = api_url; // decide whether access to remote site has to be insecure (self-signed certificate or not) - if let Err(err) = ping_client.get::>("/ping").await { - if Confirm::new("Remote API uses self-signed certificate. Do you want to continue?") - .with_default(false) - .prompt() - .map_err(|_| err)? { - Ok(InsecureApi::Insecure) - } else { - Ok(InsecureApi::Forbidden) + match ping_client.get::>("/ping").await { + Ok(res) => { + if res["status"] == "success" { + Ok(InsecureApi::Secure) + } else { + Ok(InsecureApi::Unrecheable) + } + } + Err(err) => { + // So far we only know that we cannot communicate with the remote site, it can mean + // the issue with a self-signed certificate or whatever else + if Confirm::new("Remote API uses self-signed certificate. Do you want to continue?") + .with_default(false) + .prompt() + .map_err(|_| err)? { + Ok(InsecureApi::Insecure) + } else { + Ok(InsecureApi::Forbidden) + } } - } else { - Ok(InsecureApi::Secure) } } @@ -187,7 +197,7 @@ pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { .api .trim_end_matches('/') .to_string(); - let insecure = require_insecure(api_url.clone()).await? == InsecureApi::Insecure; + let insecure = check_remote_api(api_url.clone()).await? == InsecureApi::Insecure; // we need to distinguish commands on those which assume that authentication JWT is already // available and those which not (or don't need it) From ae7227c388a1c1cc3618735ef358cc5c5e3b1229 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 10 Oct 2024 07:50:06 +0200 Subject: [PATCH 28/36] Minor tweaks from the review --- .github/workflows/ci-rust.yml | 1 - rust/agama-cli/src/lib.rs | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-rust.yml b/.github/workflows/ci-rust.yml index 0bb629435b..45a8efe234 100644 --- a/.github/workflows/ci-rust.yml +++ b/.github/workflows/ci-rust.yml @@ -69,7 +69,6 @@ jobs: - name: Install required packages run: zypper --non-interactive install - dbus-1-daemon clang-devel dbus-1-daemon jq diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index 5ebde6769d..8cb1fedb4b 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -157,7 +157,7 @@ enum InsecureApi { Secure, // Remote api is secure Insecure, // Remote api is insecure - e.g. self-signed certificate Forbidden, // Remote api is insecure and its use is forbidden (e.g. user decided not to use it) - Unrecheable // Remote api is unrecheable + Unreachable // Remote api is unrecheable } /// Returns if insecure connection to remote api server is required and user allowed that @@ -172,7 +172,7 @@ async fn check_remote_api(api_url: String) -> Result if res["status"] == "success" { Ok(InsecureApi::Secure) } else { - Ok(InsecureApi::Unrecheable) + Ok(InsecureApi::Unreachable) } } Err(err) => { From 3040bd9cfddd3c4debbd52230529cafb08a63526 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Wed, 16 Oct 2024 10:26:49 +0200 Subject: [PATCH 29/36] Added import missed in rebase, formatting --- rust/agama-cli/src/config.rs | 3 +-- rust/agama-cli/src/lib.rs | 20 +++++++++----------- rust/agama-lib/src/base_http_client.rs | 3 ++- rust/agama-lib/src/store.rs | 4 +--- 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/rust/agama-cli/src/config.rs b/rust/agama-cli/src/config.rs index 9645f76893..d1f11fd100 100644 --- a/rust/agama-cli/src/config.rs +++ b/rust/agama-cli/src/config.rs @@ -26,8 +26,7 @@ use std::{ use crate::show_progress; use agama_lib::{ - base_http_client::BaseHTTPClient, install_settings::InstallSettings, - Store as SettingsStore, + base_http_client::BaseHTTPClient, install_settings::InstallSettings, Store as SettingsStore, }; use anyhow::anyhow; use clap::Subcommand; diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index 8cb1fedb4b..c74906c96c 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -30,6 +30,7 @@ mod progress; mod questions; use crate::error::CliError; +use agama_lib::base_http_client::BaseHTTPClient; use agama_lib::{ error::ServiceError, manager::ManagerClient, progress::ProgressMonitor, transfer::Transfer, }; @@ -50,7 +51,7 @@ use std::{ /// Agama's CLI global options #[derive(Args)] -struct GlobalOpts { +pub struct GlobalOpts { #[clap(long, default_value = "http://localhost/api")] /// uri pointing to agama's remote api. If not provided, default https://localhost/api is /// used @@ -154,10 +155,10 @@ async fn build_manager<'a>() -> anyhow::Result> { #[derive(PartialEq)] enum InsecureApi { - Secure, // Remote api is secure - Insecure, // Remote api is insecure - e.g. self-signed certificate - Forbidden, // Remote api is insecure and its use is forbidden (e.g. user decided not to use it) - Unreachable // Remote api is unrecheable + Secure, // Remote api is secure + Insecure, // Remote api is insecure - e.g. self-signed certificate + Forbidden, // Remote api is insecure and its use is forbidden (e.g. user decided not to use it) + Unreachable, // Remote api is unrecheable } /// Returns if insecure connection to remote api server is required and user allowed that @@ -181,7 +182,8 @@ async fn check_remote_api(api_url: String) -> Result if Confirm::new("Remote API uses self-signed certificate. Do you want to continue?") .with_default(false) .prompt() - .map_err(|_| err)? { + .map_err(|_| err)? + { Ok(InsecureApi::Insecure) } else { Ok(InsecureApi::Forbidden) @@ -192,11 +194,7 @@ async fn check_remote_api(api_url: String) -> Result pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { // somehow check whether we need to ask user for self-signed certificate acceptance - let api_url = cli - .opts - .api - .trim_end_matches('/') - .to_string(); + let api_url = cli.opts.api.trim_end_matches('/').to_string(); let insecure = check_remote_api(api_url.clone()).await? == InsecureApi::Insecure; // we need to distinguish commands on those which assume that authentication JWT is already diff --git a/rust/agama-lib/src/base_http_client.rs b/rust/agama-lib/src/base_http_client.rs index 4125686111..59d50613d0 100644 --- a/rust/agama-lib/src/base_http_client.rs +++ b/rust/agama-lib/src/base_http_client.rs @@ -72,7 +72,8 @@ impl BaseHTTPClient { Self { client: reqwest::Client::builder() .danger_accept_invalid_certs(insecure) - .build().unwrap_or(default_client), + .build() + .unwrap_or(default_client), base_url: API_URL.to_owned(), } } diff --git a/rust/agama-lib/src/store.rs b/rust/agama-lib/src/store.rs index 2f1775aa9b..c291456154 100644 --- a/rust/agama-lib/src/store.rs +++ b/rust/agama-lib/src/store.rs @@ -45,9 +45,7 @@ pub struct Store { } impl Store { - pub async fn new( - http_client: BaseHTTPClient, - ) -> Result { + pub async fn new(http_client: BaseHTTPClient) -> Result { Ok(Self { localization: LocalizationStore::new(http_client.clone())?, users: UsersStore::new(http_client.clone())?, From 7db1db1f1a4c8dda63e9833d270ef458837546d3 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Thu, 10 Oct 2024 09:18:08 +0200 Subject: [PATCH 30/36] Updated changelog --- rust/package/agama.changes | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rust/package/agama.changes b/rust/package/agama.changes index 2fedadd04e..19c931031c 100644 --- a/rust/package/agama.changes +++ b/rust/package/agama.changes @@ -1,3 +1,8 @@ +------------------------------------------------------------------- +Wed Oct 16 07:55:27 UTC 2024 - Michal Filka + +- Implemented option for providing remote API address for the CLI + ------------------------------------------------------------------- Mon Oct 14 13:53:10 UTC 2024 - Josef Reidinger From fdc35d9d530d07a3754fdf33b42b3e4708f82702 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Mon, 14 Oct 2024 13:15:59 +0200 Subject: [PATCH 31/36] Minor tweaks inspired by the review --- rust/agama-cli/src/auth.rs | 8 ++++---- rust/agama-cli/src/lib.rs | 4 ++-- rust/agama-lib/src/base_http_client.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/rust/agama-cli/src/auth.rs b/rust/agama-cli/src/auth.rs index 4bdef7c378..ee28e22542 100644 --- a/rust/agama-cli/src/auth.rs +++ b/rust/agama-cli/src/auth.rs @@ -27,7 +27,7 @@ use inquire::Password; use std::collections::HashMap; use std::io::{self, IsTerminal}; -/// HTTP Client for auth queries +/// HTTP client to handle authentication struct AuthHTTPClient { api: BaseHTTPClient, } @@ -38,7 +38,7 @@ impl AuthHTTPClient { } /// Query web server for JWT - pub async fn get_jwt(&self, password: String) -> anyhow::Result { + pub async fn receive_jwt(&self, password: String) -> anyhow::Result { let mut auth_body = HashMap::new(); auth_body.insert("password", password); @@ -53,7 +53,7 @@ impl AuthHTTPClient { return Ok(token.clone()); } - Err(anyhow::anyhow!("Failed to get authentication token")) + Err(anyhow::anyhow!("Authentication failed. Cannot get the authentication token.")) } } @@ -112,7 +112,7 @@ fn ask_password() -> Result { /// Logs into the installation web server and stores JWT for later use. async fn login(client: AuthHTTPClient, password: String) -> anyhow::Result<()> { // 1) ask web server for JWT - let res = client.get_jwt(password).await?; + let res = client.receive_jwt(password).await?; let token = AuthToken::new(&res); Ok(token.write_user_token()?) } diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index c74906c96c..7c998b9c45 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -53,7 +53,7 @@ use std::{ #[derive(Args)] pub struct GlobalOpts { #[clap(long, default_value = "http://localhost/api")] - /// uri pointing to agama's remote api. If not provided, default https://localhost/api is + /// URI pointing to Agama's remote API. If not provided, default https://localhost/api is /// used pub api: String, } @@ -200,7 +200,7 @@ pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { // we need to distinguish commands on those which assume that authentication JWT is already // available and those which not (or don't need it) let mut client = if let Commands::Auth(_) = cli.command { - BaseHTTPClient::bare(insecure) + BaseHTTPClient::unauthenticated(insecure) } else { // this deals with authentication need inside BaseHTTPClient::new_with_params(insecure)? diff --git a/rust/agama-lib/src/base_http_client.rs b/rust/agama-lib/src/base_http_client.rs index 59d50613d0..f81aacffec 100644 --- a/rust/agama-lib/src/base_http_client.rs +++ b/rust/agama-lib/src/base_http_client.rs @@ -66,7 +66,7 @@ impl BaseHTTPClient { Self::new_with_params(false) } - pub fn bare(insecure: bool) -> Self { + pub fn unauthenticated(insecure: bool) -> Self { let default_client = reqwest::Client::new(); Self { From 6abd98f8c64051e4899d234911c7de04f425cf32 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Mon, 14 Oct 2024 14:00:51 +0200 Subject: [PATCH 32/36] Introduced --insecure option. Simplified check on remote API to avoid overengineering --- rust/agama-cli/src/lib.rs | 50 +++++++------------ rust/agama-lib/src/base_http_client.rs | 15 +++--- .../agama-lib/src/localization/http_client.rs | 8 +-- rust/agama-lib/src/localization/store.rs | 4 +- rust/agama-lib/src/manager/http_client.rs | 8 +-- rust/agama-lib/src/product/http_client.rs | 8 +-- rust/agama-lib/src/product/store.rs | 8 +-- rust/agama-lib/src/software/http_client.rs | 8 +-- rust/agama-lib/src/software/store.rs | 4 +- rust/agama-lib/src/storage/http_client.rs | 8 +-- rust/agama-lib/src/storage/store.rs | 4 +- rust/agama-lib/src/users/http_client.rs | 8 +-- rust/agama-lib/src/users/store.rs | 4 +- 13 files changed, 44 insertions(+), 93 deletions(-) diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index 7c998b9c45..0bb8f48bfc 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -56,6 +56,10 @@ pub struct GlobalOpts { /// URI pointing to Agama's remote API. If not provided, default https://localhost/api is /// used pub api: String, + + #[clap(long, default_value = "false")] + /// Whether to accept invalid (self-signed, ...) certificates or not + pub insecure: bool, } /// Agama's command-line interface @@ -153,49 +157,31 @@ async fn build_manager<'a>() -> anyhow::Result> { Ok(ManagerClient::new(conn).await?) } -#[derive(PartialEq)] -enum InsecureApi { - Secure, // Remote api is secure - Insecure, // Remote api is insecure - e.g. self-signed certificate - Forbidden, // Remote api is insecure and its use is forbidden (e.g. user decided not to use it) - Unreachable, // Remote api is unrecheable -} - -/// Returns if insecure connection to remote api server is required and user allowed that -async fn check_remote_api(api_url: String) -> Result { +/// True if use of the remote API is allowed (yes by default when the API is secure, the user is +/// asked if the API is insecure - e.g. when it uses self-signed certificate) +async fn allowed_insecure_api(use_insecure: bool, api_url: String) -> Result { // fake client used for remote site detection let mut ping_client = BaseHTTPClient::default(); ping_client.base_url = api_url; // decide whether access to remote site has to be insecure (self-signed certificate or not) - match ping_client.get::>("/ping").await { - Ok(res) => { - if res["status"] == "success" { - Ok(InsecureApi::Secure) - } else { - Ok(InsecureApi::Unreachable) - } - } - Err(err) => { - // So far we only know that we cannot communicate with the remote site, it can mean - // the issue with a self-signed certificate or whatever else - if Confirm::new("Remote API uses self-signed certificate. Do you want to continue?") - .with_default(false) - .prompt() - .map_err(|_| err)? - { - Ok(InsecureApi::Insecure) - } else { - Ok(InsecureApi::Forbidden) - } - } + return match ping_client.get::>("/ping").await { + // Problem with http remote API reachability + Err(ServiceError::HTTPError(_)) => Ok(use_insecure || Confirm::new("There was a problem with the remote API and it is treated as insecure. Do you want to continue?") + .with_default(false) + .prompt() + .unwrap_or(false)), + // another error + Err(e) => Err(e), + // success doesn't bother us here + Ok(_) => Ok(false) } } pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { // somehow check whether we need to ask user for self-signed certificate acceptance let api_url = cli.opts.api.trim_end_matches('/').to_string(); - let insecure = check_remote_api(api_url.clone()).await? == InsecureApi::Insecure; + let insecure = allowed_insecure_api(cli.opts.insecure, api_url.clone()).await?; // we need to distinguish commands on those which assume that authentication JWT is already // available and those which not (or don't need it) diff --git a/rust/agama-lib/src/base_http_client.rs b/rust/agama-lib/src/base_http_client.rs index f81aacffec..fe02f0d62d 100644 --- a/rust/agama-lib/src/base_http_client.rs +++ b/rust/agama-lib/src/base_http_client.rs @@ -66,6 +66,14 @@ impl BaseHTTPClient { Self::new_with_params(false) } + /// Uses `localhost`, authenticates with [`AuthToken`]. + pub fn new_with_params(insecure: bool) -> Result { + Ok(Self { + client: Self::authenticated_client(insecure)?, + ..Default::default() + }) + } + pub fn unauthenticated(insecure: bool) -> Self { let default_client = reqwest::Client::new(); @@ -77,13 +85,6 @@ impl BaseHTTPClient { base_url: API_URL.to_owned(), } } - /// Uses `localhost`, authenticates with [`AuthToken`]. - pub fn new_with_params(insecure: bool) -> Result { - Ok(Self { - client: Self::authenticated_client(insecure)?, - ..Default::default() - }) - } fn authenticated_client(insecure: bool) -> Result { // TODO: this error is subtly misleading, leading me to believe the SERVER said it, diff --git a/rust/agama-lib/src/localization/http_client.rs b/rust/agama-lib/src/localization/http_client.rs index 33950ab1c1..5e24e46aaa 100644 --- a/rust/agama-lib/src/localization/http_client.rs +++ b/rust/agama-lib/src/localization/http_client.rs @@ -26,13 +26,7 @@ pub struct LocalizationHTTPClient { } impl LocalizationHTTPClient { - pub fn new() -> Result { - Ok(Self { - client: BaseHTTPClient::new()?, - }) - } - - pub fn new_with_base(base: BaseHTTPClient) -> Result { + pub fn new(base: BaseHTTPClient) -> Result { Ok(Self { client: base }) } diff --git a/rust/agama-lib/src/localization/store.rs b/rust/agama-lib/src/localization/store.rs index 7b5b36071b..7946e8476c 100644 --- a/rust/agama-lib/src/localization/store.rs +++ b/rust/agama-lib/src/localization/store.rs @@ -34,7 +34,7 @@ pub struct LocalizationStore { impl LocalizationStore { pub fn new(client: BaseHTTPClient) -> Result { Ok(Self { - localization_client: LocalizationHTTPClient::new_with_base(client)?, + localization_client: LocalizationHTTPClient::new(client)?, }) } @@ -101,7 +101,7 @@ mod test { ) -> Result { let mut bhc = BaseHTTPClient::default(); bhc.base_url = mock_server_url; - let client = LocalizationHTTPClient::new_with_base(bhc)?; + let client = LocalizationHTTPClient::new(bhc)?; LocalizationStore::new_with_client(client) } diff --git a/rust/agama-lib/src/manager/http_client.rs b/rust/agama-lib/src/manager/http_client.rs index e3b1b4eb12..7cfd373fda 100644 --- a/rust/agama-lib/src/manager/http_client.rs +++ b/rust/agama-lib/src/manager/http_client.rs @@ -25,13 +25,7 @@ pub struct ManagerHTTPClient { } impl ManagerHTTPClient { - pub fn new() -> Result { - Ok(Self { - client: BaseHTTPClient::new()?, - }) - } - - pub fn new_with_base(base: BaseHTTPClient) -> Self { + pub fn new(base: BaseHTTPClient) -> Self { Self { client: base } } diff --git a/rust/agama-lib/src/product/http_client.rs b/rust/agama-lib/src/product/http_client.rs index eaf0f8fbaa..424a8b49f9 100644 --- a/rust/agama-lib/src/product/http_client.rs +++ b/rust/agama-lib/src/product/http_client.rs @@ -28,13 +28,7 @@ pub struct ProductHTTPClient { } impl ProductHTTPClient { - pub fn new() -> Result { - Ok(Self { - client: BaseHTTPClient::new()?, - }) - } - - pub fn new_with_base(base: BaseHTTPClient) -> Self { + pub fn new(base: BaseHTTPClient) -> Self { Self { client: base } } diff --git a/rust/agama-lib/src/product/store.rs b/rust/agama-lib/src/product/store.rs index 7760dec037..d43a6166c4 100644 --- a/rust/agama-lib/src/product/store.rs +++ b/rust/agama-lib/src/product/store.rs @@ -33,8 +33,8 @@ pub struct ProductStore { impl ProductStore { pub fn new(client: BaseHTTPClient) -> Result { Ok(Self { - product_client: ProductHTTPClient::new_with_base(client.clone()), - manager_client: ManagerHTTPClient::new_with_base(client.clone()), + product_client: ProductHTTPClient::new(client.clone()), + manager_client: ManagerHTTPClient::new(client.clone()), }) } @@ -101,8 +101,8 @@ mod test { fn product_store(mock_server_url: String) -> ProductStore { let mut bhc = BaseHTTPClient::default(); bhc.base_url = mock_server_url; - let p_client = ProductHTTPClient::new_with_base(bhc.clone()); - let m_client = ManagerHTTPClient::new_with_base(bhc); + let p_client = ProductHTTPClient::new(bhc.clone()); + let m_client = ManagerHTTPClient::new(bhc); ProductStore { product_client: p_client, manager_client: m_client, diff --git a/rust/agama-lib/src/software/http_client.rs b/rust/agama-lib/src/software/http_client.rs index 6bb377787f..4a770e6afb 100644 --- a/rust/agama-lib/src/software/http_client.rs +++ b/rust/agama-lib/src/software/http_client.rs @@ -27,13 +27,7 @@ pub struct SoftwareHTTPClient { } impl SoftwareHTTPClient { - pub fn new() -> Result { - Ok(Self { - client: BaseHTTPClient::new()?, - }) - } - - pub fn new_with_base(base: BaseHTTPClient) -> Self { + pub fn new(base: BaseHTTPClient) -> Self { Self { client: base } } diff --git a/rust/agama-lib/src/software/store.rs b/rust/agama-lib/src/software/store.rs index 78fa380631..dbe4c6f3fa 100644 --- a/rust/agama-lib/src/software/store.rs +++ b/rust/agama-lib/src/software/store.rs @@ -34,7 +34,7 @@ pub struct SoftwareStore { impl SoftwareStore { pub fn new(client: BaseHTTPClient) -> Result { Ok(Self { - software_client: SoftwareHTTPClient::new_with_base(client), + software_client: SoftwareHTTPClient::new(client), }) } @@ -66,7 +66,7 @@ mod test { fn software_store(mock_server_url: String) -> SoftwareStore { let mut bhc = BaseHTTPClient::default(); bhc.base_url = mock_server_url; - let client = SoftwareHTTPClient::new_with_base(bhc); + let client = SoftwareHTTPClient::new(bhc); SoftwareStore { software_client: client, } diff --git a/rust/agama-lib/src/storage/http_client.rs b/rust/agama-lib/src/storage/http_client.rs index 6be50dbf98..402dc261a5 100644 --- a/rust/agama-lib/src/storage/http_client.rs +++ b/rust/agama-lib/src/storage/http_client.rs @@ -28,13 +28,7 @@ pub struct StorageHTTPClient { } impl StorageHTTPClient { - pub fn new() -> Result { - Ok(Self { - client: BaseHTTPClient::new()?, - }) - } - - pub fn new_with_base(base: BaseHTTPClient) -> Self { + pub fn new(base: BaseHTTPClient) -> Self { Self { client: base } } diff --git a/rust/agama-lib/src/storage/store.rs b/rust/agama-lib/src/storage/store.rs index 1ab9a77fb5..6f835bc4c2 100644 --- a/rust/agama-lib/src/storage/store.rs +++ b/rust/agama-lib/src/storage/store.rs @@ -33,7 +33,7 @@ pub struct StorageStore { impl StorageStore { pub fn new(client: BaseHTTPClient) -> Result { Ok(Self { - storage_client: StorageHTTPClient::new_with_base(client), + storage_client: StorageHTTPClient::new(client), }) } @@ -58,7 +58,7 @@ mod test { fn storage_store(mock_server_url: String) -> StorageStore { let mut bhc = BaseHTTPClient::default(); bhc.base_url = mock_server_url; - let client = StorageHTTPClient::new_with_base(bhc); + let client = StorageHTTPClient::new(bhc); StorageStore { storage_client: client, } diff --git a/rust/agama-lib/src/users/http_client.rs b/rust/agama-lib/src/users/http_client.rs index 9db72ed933..fa867fb3d1 100644 --- a/rust/agama-lib/src/users/http_client.rs +++ b/rust/agama-lib/src/users/http_client.rs @@ -27,13 +27,7 @@ pub struct UsersHTTPClient { } impl UsersHTTPClient { - pub fn new() -> Result { - Ok(Self { - client: BaseHTTPClient::new()?, - }) - } - - pub fn new_with_base(client: BaseHTTPClient) -> Result { + pub fn new(client: BaseHTTPClient) -> Result { Ok(Self { client }) } diff --git a/rust/agama-lib/src/users/store.rs b/rust/agama-lib/src/users/store.rs index 30ef38e4e0..1511a00a4d 100644 --- a/rust/agama-lib/src/users/store.rs +++ b/rust/agama-lib/src/users/store.rs @@ -30,7 +30,7 @@ pub struct UsersStore { impl UsersStore { pub fn new(client: BaseHTTPClient) -> Result { Ok(Self { - users_client: UsersHTTPClient::new_with_base(client)?, + users_client: UsersHTTPClient::new(client)?, }) } @@ -110,7 +110,7 @@ mod test { fn users_store(mock_server_url: String) -> Result { let mut bhc = BaseHTTPClient::default(); bhc.base_url = mock_server_url; - let client = UsersHTTPClient::new_with_base(bhc)?; + let client = UsersHTTPClient::new(bhc)?; UsersStore::new_with_client(client) } From 8fa7850f9c75337676ff02adcd7b416bc8f083b9 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Wed, 16 Oct 2024 09:45:24 +0200 Subject: [PATCH 33/36] Refactoring: simplified BaseHTTPClient interface --- rust/agama-cli/src/lib.rs | 17 +++++++---- rust/agama-cli/src/profile.rs | 2 +- rust/agama-lib/src/base_http_client.rs | 31 ++++++++++++--------- rust/agama-lib/src/questions/http_client.rs | 2 +- 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index 0bb8f48bfc..c5314dfb03 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -181,19 +181,24 @@ async fn allowed_insecure_api(use_insecure: bool, api_url: String) -> Result Result<(), ServiceError> { // somehow check whether we need to ask user for self-signed certificate acceptance let api_url = cli.opts.api.trim_end_matches('/').to_string(); - let insecure = allowed_insecure_api(cli.opts.insecure, api_url.clone()).await?; + + let mut client = BaseHTTPClient::default(); + + client.base_url = api_url.clone(); + + if allowed_insecure_api(cli.opts.insecure, api_url.clone()).await? { + client = client.insecure(); + } // we need to distinguish commands on those which assume that authentication JWT is already // available and those which not (or don't need it) - let mut client = if let Commands::Auth(_) = cli.command { - BaseHTTPClient::unauthenticated(insecure) + client = if let Commands::Auth(_) = cli.command { + client.unauthenticated()? } else { // this deals with authentication need inside - BaseHTTPClient::new_with_params(insecure)? + client.authenticated()? }; - client.base_url = api_url.clone(); - match cli.command { Commands::Config(subcommand) => run_config_cmd(client, subcommand).await?, Commands::Probe => { diff --git a/rust/agama-cli/src/profile.rs b/rust/agama-cli/src/profile.rs index e0c01f8d1e..b6950f7dfd 100644 --- a/rust/agama-cli/src/profile.rs +++ b/rust/agama-cli/src/profile.rs @@ -153,7 +153,7 @@ async fn import(url_string: String, dir: Option) -> anyhow::Result<()> } async fn store_settings>(path: P) -> anyhow::Result<()> { - let store = SettingsStore::new(BaseHTTPClient::new()?).await?; + let store = SettingsStore::new(BaseHTTPClient::default().authenticated()?).await?; let settings = InstallSettings::from_file(&path)?; store.store(&settings).await?; Ok(()) diff --git a/rust/agama-lib/src/base_http_client.rs b/rust/agama-lib/src/base_http_client.rs index fe02f0d62d..299318773e 100644 --- a/rust/agama-lib/src/base_http_client.rs +++ b/rust/agama-lib/src/base_http_client.rs @@ -44,6 +44,7 @@ use crate::{auth::AuthToken, error::ServiceError}; #[derive(Clone)] pub struct BaseHTTPClient { client: reqwest::Client, + insecure: bool, pub base_url: String, } @@ -56,34 +57,38 @@ impl Default for BaseHTTPClient { fn default() -> Self { Self { client: reqwest::Client::new(), + insecure: false, base_url: API_URL.to_owned(), } } } impl BaseHTTPClient { - pub fn new() -> Result { - Self::new_with_params(false) + /// Allows the client to connect to remote API with insecure certificate (e.g. self-signed) + pub fn insecure(self) -> Self { + Self { + insecure: true, + ..self + } } /// Uses `localhost`, authenticates with [`AuthToken`]. - pub fn new_with_params(insecure: bool) -> Result { + pub fn authenticated(self) -> Result { Ok(Self { - client: Self::authenticated_client(insecure)?, - ..Default::default() + client: Self::authenticated_client(self.insecure)?, + ..self }) } - pub fn unauthenticated(insecure: bool) -> Self { - let default_client = reqwest::Client::new(); - - Self { + /// Configures itself for connection(s) without authentication token + pub fn unauthenticated(self) -> Result { + Ok(Self { client: reqwest::Client::builder() - .danger_accept_invalid_certs(insecure) + .danger_accept_invalid_certs(self.insecure) .build() - .unwrap_or(default_client), - base_url: API_URL.to_owned(), - } + .map_err(anyhow::Error::new)?, + ..self + }) } fn authenticated_client(insecure: bool) -> Result { diff --git a/rust/agama-lib/src/questions/http_client.rs b/rust/agama-lib/src/questions/http_client.rs index d2ad37b1d9..ec0a76532f 100644 --- a/rust/agama-lib/src/questions/http_client.rs +++ b/rust/agama-lib/src/questions/http_client.rs @@ -34,7 +34,7 @@ pub struct HTTPClient { impl HTTPClient { pub fn new() -> Result { Ok(Self { - client: BaseHTTPClient::new()?, + client: BaseHTTPClient::default().authenticated()?, }) } From 4e69f830c4b7f583b3a39f551e3460926e733949 Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Wed, 16 Oct 2024 10:01:39 +0200 Subject: [PATCH 34/36] Formatting --- rust/agama-cli/src/auth.rs | 4 +++- rust/agama-cli/src/lib.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/rust/agama-cli/src/auth.rs b/rust/agama-cli/src/auth.rs index ee28e22542..3250307fa5 100644 --- a/rust/agama-cli/src/auth.rs +++ b/rust/agama-cli/src/auth.rs @@ -53,7 +53,9 @@ impl AuthHTTPClient { return Ok(token.clone()); } - Err(anyhow::anyhow!("Authentication failed. Cannot get the authentication token.")) + Err( + anyhow::anyhow!("Authentication failed. Cannot get the authentication token." + )) } } diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index c5314dfb03..10b68bfca0 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -175,7 +175,7 @@ async fn allowed_insecure_api(use_insecure: bool, api_url: String) -> Result Err(e), // success doesn't bother us here Ok(_) => Ok(false) - } + }; } pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { From d63476665ed2f90f245c9a5e6311e9ff135cdb4a Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Wed, 16 Oct 2024 10:15:58 +0200 Subject: [PATCH 35/36] Adapted Questions subcommand to work with new --api option --- rust/agama-cli/src/auth.rs | 4 ++-- rust/agama-cli/src/lib.rs | 2 +- rust/agama-cli/src/questions.rs | 19 +++++++++++-------- rust/agama-lib/src/questions/http_client.rs | 6 ++---- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/rust/agama-cli/src/auth.rs b/rust/agama-cli/src/auth.rs index 3250307fa5..7bc84c2d1b 100644 --- a/rust/agama-cli/src/auth.rs +++ b/rust/agama-cli/src/auth.rs @@ -53,8 +53,8 @@ impl AuthHTTPClient { return Ok(token.clone()); } - Err( - anyhow::anyhow!("Authentication failed. Cannot get the authentication token." + Err(anyhow::anyhow!( + "Authentication failed. Cannot get the authentication token." )) } } diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index 10b68bfca0..d43bdeb71d 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -211,7 +211,7 @@ pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { let manager = build_manager().await?; install(&manager, 3).await? } - Commands::Questions(subcommand) => run_questions_cmd(subcommand).await?, + Commands::Questions(subcommand) => run_questions_cmd(client, subcommand).await?, // TODO: logs command was originally designed with idea that agama's cli and agama // installation runs on the same machine, so it is unable to do remote connection Commands::Logs(subcommand) => run_logs_cmd(subcommand).await?, diff --git a/rust/agama-cli/src/questions.rs b/rust/agama-cli/src/questions.rs index a763f19999..e3b6299fd8 100644 --- a/rust/agama-cli/src/questions.rs +++ b/rust/agama-cli/src/questions.rs @@ -20,7 +20,7 @@ use agama_lib::proxies::Questions1Proxy; use agama_lib::questions::http_client::HTTPClient; -use agama_lib::{connection, error::ServiceError}; +use agama_lib::{base_http_client::BaseHTTPClient, connection, error::ServiceError}; use clap::{Args, Subcommand, ValueEnum}; // TODO: use for answers also JSON to be consistent @@ -74,8 +74,8 @@ async fn set_answers(proxy: Questions1Proxy<'_>, path: String) -> Result<(), Ser .map_err(|e| e.into()) } -async fn list_questions() -> Result<(), ServiceError> { - let client = HTTPClient::new()?; +async fn list_questions(client: BaseHTTPClient) -> Result<(), ServiceError> { + let client = HTTPClient::new(client)?; let questions = client.list_questions().await?; // FIXME: if performance is bad, we can skip converting json from http to struct and then // serialize it, but it won't be pretty string @@ -85,8 +85,8 @@ async fn list_questions() -> Result<(), ServiceError> { Ok(()) } -async fn ask_question() -> Result<(), ServiceError> { - let client = HTTPClient::new()?; +async fn ask_question(client: BaseHTTPClient) -> Result<(), ServiceError> { + let client = HTTPClient::new(client)?; let question = serde_json::from_reader(std::io::stdin())?; let created_question = client.create_question(&question).await?; @@ -104,14 +104,17 @@ async fn ask_question() -> Result<(), ServiceError> { Ok(()) } -pub async fn run(subcommand: QuestionsCommands) -> Result<(), ServiceError> { +pub async fn run( + client: BaseHTTPClient, + subcommand: QuestionsCommands, +) -> Result<(), ServiceError> { let connection = connection().await?; let proxy = Questions1Proxy::new(&connection).await?; match subcommand { QuestionsCommands::Mode(value) => set_mode(proxy, value.value).await, QuestionsCommands::Answers { path } => set_answers(proxy, path).await, - QuestionsCommands::List => list_questions().await, - QuestionsCommands::Ask => ask_question().await, + QuestionsCommands::List => list_questions(client).await, + QuestionsCommands::Ask => ask_question(client).await, } } diff --git a/rust/agama-lib/src/questions/http_client.rs b/rust/agama-lib/src/questions/http_client.rs index ec0a76532f..631e78a066 100644 --- a/rust/agama-lib/src/questions/http_client.rs +++ b/rust/agama-lib/src/questions/http_client.rs @@ -32,10 +32,8 @@ pub struct HTTPClient { } impl HTTPClient { - pub fn new() -> Result { - Ok(Self { - client: BaseHTTPClient::default().authenticated()?, - }) + pub fn new(client: BaseHTTPClient) -> Result { + Ok(Self { client: client }) } pub async fn list_questions(&self) -> Result, ServiceError> { From e3a9d773752c0b77b56b7d00cb7eeca03b5e3f3a Mon Sep 17 00:00:00 2001 From: Michal Filka Date: Wed, 16 Oct 2024 11:14:40 +0200 Subject: [PATCH 36/36] (One of the) final polishing(s) --- rust/agama-cli/src/auth.rs | 14 +++++--------- rust/agama-cli/src/lib.rs | 4 ++-- rust/package/agama.changes | 3 ++- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/rust/agama-cli/src/auth.rs b/rust/agama-cli/src/auth.rs index 7bc84c2d1b..03a37bbb5c 100644 --- a/rust/agama-cli/src/auth.rs +++ b/rust/agama-cli/src/auth.rs @@ -38,7 +38,7 @@ impl AuthHTTPClient { } /// Query web server for JWT - pub async fn receive_jwt(&self, password: String) -> anyhow::Result { + pub async fn authenticate(&self, password: String) -> anyhow::Result { let mut auth_body = HashMap::new(); auth_body.insert("password", password); @@ -47,15 +47,11 @@ impl AuthHTTPClient { .api .post::>("/auth", &auth_body) .await?; - let value = response.get("token"); - if let Some(token) = value { - return Ok(token.clone()); + match response.get("token") { + Some(token) => Ok(token.clone()), + None => Err(anyhow::anyhow!("Failed to get authentication token")), } - - Err(anyhow::anyhow!( - "Authentication failed. Cannot get the authentication token." - )) } } @@ -114,7 +110,7 @@ fn ask_password() -> Result { /// Logs into the installation web server and stores JWT for later use. async fn login(client: AuthHTTPClient, password: String) -> anyhow::Result<()> { // 1) ask web server for JWT - let res = client.receive_jwt(password).await?; + let res = client.authenticate(password).await?; let token = AuthToken::new(&res); Ok(token.write_user_token()?) } diff --git a/rust/agama-cli/src/lib.rs b/rust/agama-cli/src/lib.rs index d43bdeb71d..3bec4aa2ec 100644 --- a/rust/agama-cli/src/lib.rs +++ b/rust/agama-cli/src/lib.rs @@ -165,7 +165,7 @@ async fn allowed_insecure_api(use_insecure: bool, api_url: String) -> Result>("/ping").await { + match ping_client.get::>("/ping").await { // Problem with http remote API reachability Err(ServiceError::HTTPError(_)) => Ok(use_insecure || Confirm::new("There was a problem with the remote API and it is treated as insecure. Do you want to continue?") .with_default(false) @@ -175,7 +175,7 @@ async fn allowed_insecure_api(use_insecure: bool, api_url: String) -> Result Err(e), // success doesn't bother us here Ok(_) => Ok(false) - }; + } } pub async fn run_command(cli: Cli) -> Result<(), ServiceError> { diff --git a/rust/package/agama.changes b/rust/package/agama.changes index 19c931031c..41813d410c 100644 --- a/rust/package/agama.changes +++ b/rust/package/agama.changes @@ -1,7 +1,8 @@ ------------------------------------------------------------------- Wed Oct 16 07:55:27 UTC 2024 - Michal Filka -- Implemented option for providing remote API address for the CLI +- Implemented option for providing remote API address for the CLI + gh#agama-project/agama#1495 ------------------------------------------------------------------- Mon Oct 14 13:53:10 UTC 2024 - Josef Reidinger