Implement login for the fetch command (#1915)

This commit is contained in:
Tobias Tschinkowitz 2020-05-30 01:22:38 +02:00 committed by GitHub
parent 48ee20782f
commit 3a6a3d7409
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 5 deletions

1
Cargo.lock generated
View file

@ -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",

View file

@ -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" }

View file

@ -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<Value>,
pub tag: Tag,
pub has_raw: bool,
pub user: Option<String>,
pub password: Option<String>,
}
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<String>,
password: Option<String>,
) -> 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<String>,
password: Option<String>,
) -> Result<(Option<String>, 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(|_| {

View file

@ -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(),
))])
}
}