diff --git a/Cargo.lock b/Cargo.lock index 5c38b289f5..51c744a846 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2446,6 +2446,7 @@ dependencies = [ name = "nu_plugin_fetch" version = "0.14.1" dependencies = [ + "base64 0.12.1", "futures 0.3.5", "nu-build", "nu-errors", diff --git a/crates/nu_plugin_fetch/Cargo.toml b/crates/nu_plugin_fetch/Cargo.toml index 0d2476329a..0ed9d6ecdb 100644 --- a/crates/nu_plugin_fetch/Cargo.toml +++ b/crates/nu_plugin_fetch/Cargo.toml @@ -17,6 +17,7 @@ nu-errors = { path = "../nu-errors", version = "0.14.1" } futures = { version = "0.3", features = ["compat", "io-compat"] } surf = "1.0.3" url = "2.1.1" +base64 = "0.12.1" [build-dependencies] nu-build = { version = "0.14.1", path = "../nu-build" } diff --git a/crates/nu_plugin_fetch/src/fetch.rs b/crates/nu_plugin_fetch/src/fetch.rs index 1a196606c0..b9e2d61b0e 100644 --- a/crates/nu_plugin_fetch/src/fetch.rs +++ b/crates/nu_plugin_fetch/src/fetch.rs @@ -1,3 +1,4 @@ +use base64::encode; use mime::Mime; use nu_errors::ShellError; use nu_protocol::{CallInfo, CommandAction, ReturnSuccess, ReturnValue, UntaggedValue, Value}; @@ -11,6 +12,8 @@ pub struct Fetch { pub path: Option, pub tag: Tag, pub has_raw: bool, + pub user: Option, + pub password: Option, } impl Fetch { @@ -19,6 +22,8 @@ impl Fetch { path: None, tag: Tag::unknown(), has_raw: false, + user: None, + password: None, } } @@ -37,15 +42,30 @@ impl Fetch { self.has_raw = call_info.args.has("raw"); + self.user = match call_info.args.get("user") { + Some(user) => Some(user.as_string()?), + None => None, + }; + + self.password = match call_info.args.get("password") { + Some(password) => Some(password.as_string()?), + None => None, + }; + ReturnSuccess::value(UntaggedValue::nothing().into_untagged_value()) } } -pub async fn fetch_helper(path: &Value, has_raw: bool) -> ReturnValue { +pub async fn fetch_helper( + path: &Value, + has_raw: bool, + user: Option, + password: Option, +) -> ReturnValue { let path_str = path.as_string()?; let path_span = path.tag.span; - let result = fetch(&path_str, path_span, has_raw).await; + let result = fetch(&path_str, path_span, has_raw, user, password).await; if let Err(e) = result { return Err(e); @@ -76,6 +96,8 @@ pub async fn fetch( location: &str, span: Span, has_raw: bool, + user: Option, + password: Option, ) -> Result<(Option, UntaggedValue, Tag), ShellError> { if url::Url::parse(location).is_err() { return Err(ShellError::labeled_error( @@ -84,9 +106,16 @@ pub async fn fetch( span, )); } - - let response = surf::get(location).await; - match response { + let login = match (user, password) { + (Some(user), Some(password)) => Some(encode(&format!("{}:{}", user, password))), + (Some(user), _) => Some(encode(&format!("{}:", user))), + _ => None, + }; + let mut response = surf::get(location); + if let Some(login) = login { + response = response.set_header("Authorization", format!("Basic {}", login)); + } + match response.await { Ok(mut r) => match r.headers().get("content-type") { Some(content_type) => { let content_type = Mime::from_str(content_type).map_err(|_| { diff --git a/crates/nu_plugin_fetch/src/nu/mod.rs b/crates/nu_plugin_fetch/src/nu/mod.rs index 231b1cac6a..5989646807 100644 --- a/crates/nu_plugin_fetch/src/nu/mod.rs +++ b/crates/nu_plugin_fetch/src/nu/mod.rs @@ -15,6 +15,18 @@ impl Plugin for Fetch { SyntaxShape::String, "the URL to fetch the contents from", ) + .named( + "user", + SyntaxShape::Any, + "the username when authenticating", + Some('u'), + ) + .named( + "password", + SyntaxShape::Any, + "the password when authenticating", + Some('p'), + ) .switch("raw", "fetch contents as text rather than a table", Some('r')) .filter()) } @@ -26,6 +38,8 @@ impl Plugin for Fetch { ShellError::labeled_error("internal error: path not set", "path not set", &self.tag) })?, self.has_raw, + self.user.clone(), + self.password.clone(), ))]) } }