CLI and kanidm_client changes to handle errors and TLS validation changes (#2127)

* pulling out exitcode, adding hyper dep to handle errors (was already transitively there due to reqwest)
* adding better error handling, more options for client things
This commit is contained in:
James Hodgkinson 2023-09-19 13:31:19 +10:00 committed by GitHub
parent 4aee3365aa
commit 9b2fab7bb1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 138 additions and 47 deletions

8
Cargo.lock generated
View file

@ -1456,12 +1456,6 @@ dependencies = [
"libc",
]
[[package]]
name = "exitcode"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193"
[[package]]
name = "fake-simd"
version = "0.1.2"
@ -2849,6 +2843,7 @@ dependencies = [
name = "kanidm_client"
version = "1.1.0-rc.14-dev"
dependencies = [
"hyper",
"kanidm_proto",
"reqwest",
"serde",
@ -2917,7 +2912,6 @@ dependencies = [
"compact_jwt",
"cursive",
"dialoguer",
"exitcode",
"futures-concurrency",
"kanidm_build_profiles",
"kanidm_client",

View file

@ -18,9 +18,16 @@ kanidm_proto = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
time = { workspace = true, features = ["serde", "std"] }
tokio = { workspace = true, features = ["rt", "net", "time", "macros", "sync", "signal"] }
tokio = { workspace = true, features = [
"rt",
"net",
"time",
"macros",
"sync",
"signal",
] }
toml = { workspace = true }
uuid = { workspace = true, features = ["serde", "v4"] }
url = { workspace = true, features = ["serde"] }
webauthn-rs-proto = { workspace = true, features = ["wasm"] }
hyper = { workspace = true }

View file

@ -66,6 +66,7 @@ pub enum ClientError {
SystemError,
ConfigParseIssue(String),
CertParseIssue(String),
UntrustedCertificate(String),
}
#[derive(Debug, Deserialize, Serialize)]
@ -468,6 +469,23 @@ fn test_make_url() {
);
}
/// This is probably pretty jank but it works and was pulled from here:
/// <https://github.com/seanmonstar/reqwest/issues/1602#issuecomment-1220996681>
fn find_reqwest_error_source<E: std::error::Error + 'static>(
orig: &dyn std::error::Error,
) -> Option<&E> {
let mut cause = orig.source();
while let Some(err) = cause {
if let Some(typed) = err.downcast_ref::<E>() {
return Some(typed);
}
cause = err.source();
}
// else
None
}
impl KanidmClient {
pub fn get_origin(&self) -> &Url {
&self.origin
@ -541,6 +559,28 @@ impl KanidmClient {
*guard = false;
}
/// You've got the response from a reqwest and you want to turn it into a `ClientError`
fn handle_response_error(&self, error: reqwest::Error) -> ClientError {
if error.is_connect() {
if find_reqwest_error_source::<std::io::Error>(&error).is_some() {
// TODO: one day handle IO errors better
trace!("Got an IO error! {:?}", &error);
return ClientError::Transport(error);
}
if let Some(hyper_error) = find_reqwest_error_source::<hyper::Error>(&error) {
// hyper errors can be *anything* depending on the underlying client libraries
// ref: https://github.com/hyperium/hyper/blob/9feb70e9249d9fb99634ec96f83566e6bb3b3128/src/error.rs#L26C2-L26C2
if format!("{:?}", hyper_error)
.to_lowercase()
.contains("certificate")
{
return ClientError::UntrustedCertificate(format!("{}", hyper_error));
}
}
}
ClientError::Transport(error)
}
async fn perform_simple_post_request<R: Serialize, T: DeserializeOwned>(
&self,
dest: &str,
@ -554,7 +594,10 @@ impl KanidmClient {
.body(req_string)
.header(CONTENT_TYPE, APPLICATION_JSON);
let response = response.send().await.map_err(ClientError::Transport)?;
let response = response
.send()
.await
.map_err(|err| self.handle_response_error(err))?;
self.expect_version(&response).await;
@ -617,7 +660,10 @@ impl KanidmClient {
}
};
let response = response.send().await.map_err(ClientError::Transport)?;
let response = response
.send()
.await
.map_err(|err| self.handle_response_error(err))?;
self.expect_version(&response).await;
@ -677,7 +723,10 @@ impl KanidmClient {
}
};
let response = response.send().await.map_err(ClientError::Transport)?;
let response = response
.send()
.await
.map_err(|err| self.handle_response_error(err))?;
self.expect_version(&response).await;
@ -730,7 +779,7 @@ impl KanidmClient {
.body(req_string)
.send()
.await
.map_err(ClientError::Transport)?;
.map_err(|err| self.handle_response_error(err))?;
self.expect_version(&response).await;
@ -781,7 +830,10 @@ impl KanidmClient {
}
};
let response = response.send().await.map_err(ClientError::Transport)?;
let response = response
.send()
.await
.map_err(|err| self.handle_response_error(err))?;
self.expect_version(&response).await;
@ -825,7 +877,10 @@ impl KanidmClient {
}
};
let response = response.send().await.map_err(ClientError::Transport)?;
let response = response
.send()
.await
.map_err(|err| self.handle_response_error(err))?;
self.expect_version(&response).await;
@ -870,7 +925,10 @@ impl KanidmClient {
}
};
let response = response.send().await.map_err(ClientError::Transport)?;
let response = response
.send()
.await
.map_err(|err| self.handle_response_error(err))?;
self.expect_version(&response).await;
@ -920,7 +978,10 @@ impl KanidmClient {
}
};
let response = response.send().await.map_err(ClientError::Transport)?;
let response = response
.send()
.await
.map_err(|err| self.handle_response_error(err))?;
self.expect_version(&response).await;
@ -1390,7 +1451,10 @@ impl KanidmClient {
}
};
let response = response.send().await.map_err(ClientError::Transport)?;
let response = response
.send()
.await
.map_err(|err| self.handle_response_error(err))?;
self.expect_version(&response).await;

View file

@ -53,7 +53,6 @@ uuid = { workspace=true }
zxcvbn = { workspace = true }
lazy_static.workspace = true
regex.workspace = true
exitcode = "1.1.2"
[dependencies.cursive]
version = "0.20.0"

View file

@ -62,20 +62,32 @@ impl CommonOpt {
let client_builder = match ca_path {
Some(p) => {
debug!("Adding trusted CA cert {:?}", p);
client_builder
let client_builder = client_builder
.add_root_certificate_filepath(p)
.unwrap_or_else(|e| {
error!("Failed to add ca certificate -- {:?}", e);
std::process::exit(1);
})
});
debug!(
"After attempting to add trusted CA cert, client builder state: {:?}",
client_builder
);
client_builder
}
None => client_builder,
};
debug!(
"Post attempting to add trusted CA cert, client builder state: {:?}",
client_builder
let client_builder = match self.skip_hostname_verification {
true => {
warn!(
"Accepting invalid hostnames on the certificate for {:?}",
&self.addr
);
client_builder.danger_accept_invalid_hostnames(true)
}
false => client_builder,
};
client_builder.build().unwrap_or_else(|e| {
error!("Failed to build client instance -- {:?}", e);
@ -176,7 +188,7 @@ impl CommonOpt {
match prompt_for_username_get_values() {
Ok(tuple) => tuple,
Err(msg) => {
error!("{}", msg);
error!("Error: {}", msg);
std::process::exit(1);
}
}

View file

@ -56,13 +56,21 @@ pub(crate) fn handle_client_error(response: ClientError, _output_mode: &OutputMo
"Internal Server Error in response:{:?} {:?}",
error_msg, message
);
std::process::exit(exitcode::SOFTWARE);
std::process::exit(1);
} else if status == StatusCode::NOT_FOUND {
error!("Item not found:{:?} {:?}", error_msg, message)
} else {
error!("HTTP Error: {}{} {:?}", status, error_msg, message);
}
}
ClientError::Transport(e) => {
error!("HTTP-Transport Related Error: {:?}", e);
std::process::exit(1);
}
ClientError::UntrustedCertificate(e) => {
error!("Untrusted Certificate Error: {:?}", e);
std::process::exit(1);
}
_ => {
eprintln!("{:?}", response);
}

View file

@ -49,6 +49,13 @@ pub struct CommonOpt {
/// Log format (still in very early development)
#[clap(short, long = "output", env = "KANIDM_OUTPUT", default_value = "text")]
output_mode: OutputMode,
/// Skip hostname verification
#[clap(
long = "skip-hostname-verification",
env = "KANIDM_SKIP_HOSTNAME_VERIFICATION",
default_value_t = false
)]
skip_hostname_verification: bool,
}
#[derive(Debug, Args)]