diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 7d48a4957c..9d27a50a88 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -29,9 +29,11 @@ dependencies = [ "console", "convert_case", "fs_extra", + "home", "indicatif", "log", "nix 0.27.1", + "reqwest", "serde", "serde_json", "serde_yaml", @@ -425,10 +427,10 @@ dependencies = [ "base64 0.21.7", "bytes", "futures-util", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "http-body-util", - "hyper", + "hyper 1.1.0", "hyper-util", "itoa", "matchit", @@ -460,8 +462,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "http-body-util", "mime", "pin-project-lite", @@ -483,8 +485,8 @@ dependencies = [ "bytes", "futures-util", "headers", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "http-body-util", "mime", "pin-project-lite", @@ -843,6 +845,16 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -1016,6 +1028,15 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + [[package]] name = "enumflags2" version = "0.7.8" @@ -1132,6 +1153,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1296,6 +1332,25 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "h2" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.11", + "indexmap 2.1.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.2" @@ -1307,7 +1362,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 1.0.0", "indexmap 2.1.0", "slab", "tokio", @@ -1342,7 +1397,7 @@ dependencies = [ "base64 0.21.7", "bytes", "headers-core", - "http", + "http 1.0.0", "httpdate", "mime", "sha1", @@ -1354,7 +1409,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ - "http", + "http 1.0.0", ] [[package]] @@ -1384,6 +1439,26 @@ dependencies = [ "digest", ] +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.0.0" @@ -1395,6 +1470,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.11", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.0" @@ -1402,7 +1488,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http", + "http 1.0.0", ] [[package]] @@ -1413,8 +1499,8 @@ checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" dependencies = [ "bytes", "futures-util", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "pin-project-lite", ] @@ -1430,6 +1516,30 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.24", + "http 0.2.11", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.5", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.1.0" @@ -1439,9 +1549,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2", - "http", - "http-body", + "h2 0.4.2", + "http 1.0.0", + "http-body 1.0.0", "httparse", "httpdate", "itoa", @@ -1449,6 +1559,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper 0.14.28", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "hyper-util" version = "0.1.3" @@ -1457,9 +1580,9 @@ checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" dependencies = [ "bytes", "futures-util", - "http", - "http-body", - "hyper", + "http 1.0.0", + "http-body 1.0.0", + "hyper 1.1.0", "pin-project-lite", "socket2 0.5.5", "tokio", @@ -1559,6 +1682,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "iso8601" version = "0.5.1" @@ -1820,6 +1949,24 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nix" version = "0.26.4" @@ -2014,6 +2161,32 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "openssl-probe" version = "0.1.5" @@ -2022,9 +2195,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.97" +version = "0.9.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" +checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" dependencies = [ "cc", "libc", @@ -2462,6 +2635,46 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "reqwest" +version = "0.11.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.24", + "http 0.2.11", + "http-body 0.4.6", + "hyper 0.14.28", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "ring" version = "0.17.7" @@ -2537,6 +2750,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + [[package]] name = "rustversion" version = "1.0.14" @@ -2564,6 +2786,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.196" @@ -2848,6 +3093,27 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "systemd-journal-logger" version = "1.0.0" @@ -3011,6 +3277,16 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.14" @@ -3119,8 +3395,8 @@ dependencies = [ "bitflags 2.4.1", "bytes", "futures-core", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "http-body-util", "pin-project-lite", "tokio", @@ -3211,6 +3487,12 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "tungstenite" version = "0.21.0" @@ -3220,7 +3502,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 1.0.0", "httparse", "log", "rand", @@ -3396,6 +3678,15 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3427,6 +3718,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.91" @@ -3456,6 +3759,16 @@ version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +[[package]] +name = "web-sys" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3712,6 +4025,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "xdg-home" version = "1.0.0" diff --git a/rust/agama-cli/Cargo.toml b/rust/agama-cli/Cargo.toml index 51873d5e41..8a073f259e 100644 --- a/rust/agama-cli/Cargo.toml +++ b/rust/agama-cli/Cargo.toml @@ -25,6 +25,8 @@ nix = { version = "0.27.1", features = ["user"] } zbus = { version = "3", default-features = false, features = ["tokio"] } tokio = { version = "1.33.0", features = ["macros", "rt-multi-thread"] } async-trait = "0.1.77" +reqwest = { version = "0.11", features = ["json"] } +home = "0.5.9" [[bin]] name = "agama" diff --git a/rust/agama-cli/src/auth.rs b/rust/agama-cli/src/auth.rs new file mode 100644 index 0000000000..0e1c34bf13 --- /dev/null +++ b/rust/agama-cli/src/auth.rs @@ -0,0 +1,243 @@ +use clap::{arg, Args, Subcommand}; +use home; +use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE}; +use std::fs; +use std::fs::File; +use std::io; +use std::io::{BufRead, BufReader}; +use std::os::unix::fs::PermissionsExt; +use std::path::{Path, PathBuf}; + +const DEFAULT_JWT_FILE: &str = ".agama/agama-jwt"; +const DEFAULT_AUTH_URL: &str = "http://localhost:3000/api/authenticate"; +const DEFAULT_FILE_MODE: u32 = 0o600; + +#[derive(Subcommand, Debug)] +pub enum AuthCommands { + /// Login with defined server. Result is JWT stored locally and made available to + /// further use. Password can be provided by commandline option, from a file or it fallbacks + /// into an interactive prompt. + Login(LoginArgs), + /// Release currently stored JWT + Logout, + /// Prints currently stored JWT to stdout + Show, +} + +/// Main entry point called from agama CLI main loop +pub async fn run(subcommand: AuthCommands) -> anyhow::Result<()> { + match subcommand { + AuthCommands::Login(options) => login(LoginArgs::proceed(options).password()?).await, + AuthCommands::Logout => logout(), + AuthCommands::Show => show(), + } +} + +/// Reads stored token and returns it +fn jwt() -> anyhow::Result { + if let Some(file) = jwt_file() { + if let Ok(token) = read_line_from_file(&file.as_path()) { + return Ok(token); + } + } + + Err(anyhow::anyhow!("Authentication token not available")) +} + +/// Stores user provided configuration for login command +#[derive(Args, Debug)] +pub struct LoginArgs { + #[arg(long, short = 'p')] + password: Option, + #[arg(long, short = 'f')] + file: Option, +} + +impl LoginArgs { + /// Transforms user provided options into internal representation + /// See Credentials trait + fn proceed(options: LoginArgs) -> Box { + if let Some(password) = options.password { + Box::new(KnownCredentials { password }) + } else if let Some(path) = options.file { + Box::new(FileCredentials { path }) + } else { + Box::new(MissingCredentials {}) + } + } +} + +/// Placeholder for no configuration provided by user +struct MissingCredentials; + +/// Stores whatever is needed for reading credentials from a file +struct FileCredentials { + path: PathBuf, +} + +/// Stores credentials as provided by the user directly +struct KnownCredentials { + password: String, +} + +/// Transforms credentials from user's input into format used internaly +trait Credentials { + fn password(&self) -> io::Result; +} + +impl Credentials for KnownCredentials { + fn password(&self) -> io::Result { + Ok(self.password.clone()) + } +} + +impl Credentials for FileCredentials { + fn password(&self) -> io::Result { + read_line_from_file(&self.path.as_path()) + } +} + +impl Credentials for MissingCredentials { + fn password(&self) -> io::Result { + let password = read_credential("Password".to_string())?; + + Ok(password) + } +} + +/// Path to file where JWT is stored +fn jwt_file() -> Option { + Some(home::home_dir()?.join(DEFAULT_JWT_FILE)) +} + +/// Reads first line from given file +fn read_line_from_file(path: &Path) -> io::Result { + if !path.exists() { + return Err(io::Error::new( + io::ErrorKind::Other, + "Cannot find the file containing the credentials.", + )); + } + + if let Ok(file) = File::open(&path) { + // cares only of first line, take everything. No comments + // or something like that supported + let raw = BufReader::new(file).lines().next(); + + if let Some(line) = raw { + return line; + } + } + + Err(io::Error::new( + io::ErrorKind::Other, + "Failed to open the file", + )) +} + +/// Asks user to provide a line of input. Displays a prompt. +fn read_credential(caption: String) -> io::Result { + let mut cred = String::new(); + + println!("{}: ", caption); + + io::stdin().read_line(&mut cred)?; + if cred.pop().is_none() || cred.is_empty() { + return Err(io::Error::new( + io::ErrorKind::Other, + format!("Failed to read {}", caption), + )); + } + + Ok(cred) +} + +/// Sets the archive owner to root:root. Also sets the file permissions to read/write for the +/// owner only. +fn set_file_permissions(file: &Path) -> io::Result<()> { + let attr = fs::metadata(file)?; + let mut permissions = attr.permissions(); + + // set the file file permissions to -rw------- + permissions.set_mode(DEFAULT_FILE_MODE); + fs::set_permissions(file, permissions)?; + + Ok(()) +} + +/// 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".to_string()); + + 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(password: String) -> anyhow::Result<()> { + // 1) ask web server for JWT + let res = get_jwt(DEFAULT_AUTH_URL.to_string(), password).await?; + + // 2) if successful store the JWT for later use + if let Some(path) = jwt_file() { + if let Some(dir) = path.parent() { + fs::create_dir_all(dir)?; + } else { + return Err(anyhow::anyhow!("Cannot store the authentication token")); + } + + fs::write(path.as_path(), res)?; + set_file_permissions(path.as_path())?; + } + + Ok(()) +} + +/// Releases JWT +fn logout() -> anyhow::Result<()> { + let path = jwt_file(); + + if !&path.clone().is_some_and(|p| p.exists()) { + // mask if the file with the JWT doesn't exist (most probably no login before logout) + return Ok(()); + } + + // panicking is right thing to do if expect fails, becase it was already checked twice that + // the path exists + let file = path.expect("Cannot locate stored JWT"); + + Ok(fs::remove_file(file)?) +} + +/// Shows stored JWT on stdout +fn show() -> anyhow::Result<()> { + // we do not care if jwt() fails or not. If there is something to print, show it otherwise + // stay silent + if let Ok(token) = jwt() { + println!("{}", token); + } + + Ok(()) +} diff --git a/rust/agama-cli/src/commands.rs b/rust/agama-cli/src/commands.rs index 99d51ece9f..46615a4abe 100644 --- a/rust/agama-cli/src/commands.rs +++ b/rust/agama-cli/src/commands.rs @@ -1,3 +1,4 @@ +use crate::auth::AuthCommands; use crate::config::ConfigCommands; use crate::logs::LogsCommands; use crate::profile::ProfileCommands; @@ -35,4 +36,7 @@ pub enum Commands { /// Collects logs #[command(subcommand)] Logs(LogsCommands), + /// Request an action on the web server like Login / Logout + #[command(subcommand)] + Auth(AuthCommands), } diff --git a/rust/agama-cli/src/main.rs b/rust/agama-cli/src/main.rs index 52a3fa4a89..33117ae481 100644 --- a/rust/agama-cli/src/main.rs +++ b/rust/agama-cli/src/main.rs @@ -1,5 +1,6 @@ use clap::Parser; +mod auth; mod commands; mod config; mod error; @@ -13,6 +14,7 @@ use crate::error::CliError; use agama_lib::error::ServiceError; use agama_lib::manager::ManagerClient; use agama_lib::progress::ProgressMonitor; +use auth::run as run_auth_cmd; use commands::Commands; use config::run as run_config_cmd; use logs::run as run_logs_cmd; @@ -135,6 +137,7 @@ async fn run_command(cli: Cli) -> anyhow::Result<()> { } Commands::Questions(subcommand) => run_questions_cmd(subcommand).await, Commands::Logs(subcommand) => run_logs_cmd(subcommand).await, + Commands::Auth(subcommand) => run_auth_cmd(subcommand).await, _ => unimplemented!(), } } diff --git a/rust/package/agama.changes b/rust/package/agama.changes index 656cd074eb..8e64449ffe 100644 --- a/rust/package/agama.changes +++ b/rust/package/agama.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Thu Mar 7 10:52:58 UTC 2024 - Michal Filka + +- CLI: added auth command with login / logout / show subcommands + for handling authentication token management with new agama web + server + ------------------------------------------------------------------- Tue Feb 27 15:55:28 UTC 2024 - Imobach Gonzalez Sosa