mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
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:
parent
4aee3365aa
commit
9b2fab7bb1
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -1456,12 +1456,6 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "exitcode"
|
|
||||||
version = "1.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fake-simd"
|
name = "fake-simd"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -2849,6 +2843,7 @@ dependencies = [
|
||||||
name = "kanidm_client"
|
name = "kanidm_client"
|
||||||
version = "1.1.0-rc.14-dev"
|
version = "1.1.0-rc.14-dev"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"hyper",
|
||||||
"kanidm_proto",
|
"kanidm_proto",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -2917,7 +2912,6 @@ dependencies = [
|
||||||
"compact_jwt",
|
"compact_jwt",
|
||||||
"cursive",
|
"cursive",
|
||||||
"dialoguer",
|
"dialoguer",
|
||||||
"exitcode",
|
|
||||||
"futures-concurrency",
|
"futures-concurrency",
|
||||||
"kanidm_build_profiles",
|
"kanidm_build_profiles",
|
||||||
"kanidm_client",
|
"kanidm_client",
|
||||||
|
|
|
@ -18,9 +18,16 @@ kanidm_proto = { workspace = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
time = { workspace = true, features = ["serde", "std"] }
|
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 }
|
toml = { workspace = true }
|
||||||
uuid = { workspace = true, features = ["serde", "v4"] }
|
uuid = { workspace = true, features = ["serde", "v4"] }
|
||||||
url = { workspace = true, features = ["serde"] }
|
url = { workspace = true, features = ["serde"] }
|
||||||
webauthn-rs-proto = { workspace = true, features = ["wasm"] }
|
webauthn-rs-proto = { workspace = true, features = ["wasm"] }
|
||||||
|
hyper = { workspace = true }
|
||||||
|
|
|
@ -66,6 +66,7 @@ pub enum ClientError {
|
||||||
SystemError,
|
SystemError,
|
||||||
ConfigParseIssue(String),
|
ConfigParseIssue(String),
|
||||||
CertParseIssue(String),
|
CertParseIssue(String),
|
||||||
|
UntrustedCertificate(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[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 {
|
impl KanidmClient {
|
||||||
pub fn get_origin(&self) -> &Url {
|
pub fn get_origin(&self) -> &Url {
|
||||||
&self.origin
|
&self.origin
|
||||||
|
@ -541,6 +559,28 @@ impl KanidmClient {
|
||||||
*guard = false;
|
*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>(
|
async fn perform_simple_post_request<R: Serialize, T: DeserializeOwned>(
|
||||||
&self,
|
&self,
|
||||||
dest: &str,
|
dest: &str,
|
||||||
|
@ -554,7 +594,10 @@ impl KanidmClient {
|
||||||
.body(req_string)
|
.body(req_string)
|
||||||
.header(CONTENT_TYPE, APPLICATION_JSON);
|
.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;
|
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;
|
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;
|
self.expect_version(&response).await;
|
||||||
|
|
||||||
|
@ -730,7 +779,7 @@ impl KanidmClient {
|
||||||
.body(req_string)
|
.body(req_string)
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.map_err(ClientError::Transport)?;
|
.map_err(|err| self.handle_response_error(err))?;
|
||||||
|
|
||||||
self.expect_version(&response).await;
|
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;
|
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;
|
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;
|
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;
|
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;
|
self.expect_version(&response).await;
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,13 @@ default-run = "kanidm"
|
||||||
description = "Kanidm Client Tools"
|
description = "Kanidm Client Tools"
|
||||||
documentation = "https://kanidm.github.io/kanidm/stable/"
|
documentation = "https://kanidm.github.io/kanidm/stable/"
|
||||||
|
|
||||||
version = { workspace=true }
|
version = { workspace = true }
|
||||||
authors = { workspace=true }
|
authors = { workspace = true }
|
||||||
rust-version = { workspace=true }
|
rust-version = { workspace = true }
|
||||||
edition = { workspace=true }
|
edition = { workspace = true }
|
||||||
license = { workspace=true }
|
license = { workspace = true }
|
||||||
homepage = { workspace=true }
|
homepage = { workspace = true }
|
||||||
repository = { workspace=true }
|
repository = { workspace = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["unix"]
|
default = ["unix"]
|
||||||
|
@ -34,26 +34,25 @@ path = "src/ssh_authorizedkeys.rs"
|
||||||
async-recursion = { workspace = true }
|
async-recursion = { workspace = true }
|
||||||
clap = { workspace = true, features = ["derive", "env"] }
|
clap = { workspace = true, features = ["derive", "env"] }
|
||||||
compact_jwt = { workspace = true, features = ["openssl"] }
|
compact_jwt = { workspace = true, features = ["openssl"] }
|
||||||
dialoguer = { workspace=true }
|
dialoguer = { workspace = true }
|
||||||
futures-concurrency = { workspace=true }
|
futures-concurrency = { workspace = true }
|
||||||
libc = { workspace=true }
|
libc = { workspace = true }
|
||||||
kanidm_client = { workspace=true }
|
kanidm_client = { workspace = true }
|
||||||
kanidm_proto = { workspace=true }
|
kanidm_proto = { workspace = true }
|
||||||
qrcode = { workspace = true }
|
qrcode = { workspace = true }
|
||||||
rpassword = { workspace=true }
|
rpassword = { workspace = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace=true }
|
serde_json = { workspace = true }
|
||||||
shellexpand = { workspace=true }
|
shellexpand = { workspace = true }
|
||||||
time = { workspace = true, features = ["serde", "std"] }
|
time = { workspace = true, features = ["serde", "std"] }
|
||||||
tracing = { workspace=true }
|
tracing = { workspace = true }
|
||||||
tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] }
|
tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] }
|
||||||
tokio = { workspace = true, features = ["rt", "macros"] }
|
tokio = { workspace = true, features = ["rt", "macros"] }
|
||||||
url = { workspace = true, features = ["serde"] }
|
url = { workspace = true, features = ["serde"] }
|
||||||
uuid = { workspace=true }
|
uuid = { workspace = true }
|
||||||
zxcvbn = { workspace=true }
|
zxcvbn = { workspace = true }
|
||||||
lazy_static.workspace = true
|
lazy_static.workspace = true
|
||||||
regex.workspace = true
|
regex.workspace = true
|
||||||
exitcode = "1.1.2"
|
|
||||||
|
|
||||||
[dependencies.cursive]
|
[dependencies.cursive]
|
||||||
version = "0.20.0"
|
version = "0.20.0"
|
||||||
|
@ -63,9 +62,9 @@ features = ["crossterm-backend"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
clap = { workspace = true, features = ["derive"] }
|
clap = { workspace = true, features = ["derive"] }
|
||||||
clap_complete = { workspace=true }
|
clap_complete = { workspace = true }
|
||||||
kanidm_build_profiles = { workspace = true }
|
kanidm_build_profiles = { workspace = true }
|
||||||
uuid = { workspace=true }
|
uuid = { workspace = true }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
|
|
||||||
[target."cfg(target_os = \"windows\")".dependencies.webauthn-authenticator-rs]
|
[target."cfg(target_os = \"windows\")".dependencies.webauthn-authenticator-rs]
|
||||||
|
|
|
@ -62,20 +62,32 @@ impl CommonOpt {
|
||||||
let client_builder = match ca_path {
|
let client_builder = match ca_path {
|
||||||
Some(p) => {
|
Some(p) => {
|
||||||
debug!("Adding trusted CA cert {:?}", p);
|
debug!("Adding trusted CA cert {:?}", p);
|
||||||
client_builder
|
let client_builder = client_builder
|
||||||
.add_root_certificate_filepath(p)
|
.add_root_certificate_filepath(p)
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
error!("Failed to add ca certificate -- {:?}", e);
|
error!("Failed to add ca certificate -- {:?}", e);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
})
|
});
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"After attempting to add trusted CA cert, client builder state: {:?}",
|
||||||
|
client_builder
|
||||||
|
);
|
||||||
|
client_builder
|
||||||
}
|
}
|
||||||
None => client_builder,
|
None => client_builder,
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!(
|
let client_builder = match self.skip_hostname_verification {
|
||||||
"Post attempting to add trusted CA cert, client builder state: {:?}",
|
true => {
|
||||||
client_builder
|
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| {
|
client_builder.build().unwrap_or_else(|e| {
|
||||||
error!("Failed to build client instance -- {:?}", e);
|
error!("Failed to build client instance -- {:?}", e);
|
||||||
|
@ -176,7 +188,7 @@ impl CommonOpt {
|
||||||
match prompt_for_username_get_values() {
|
match prompt_for_username_get_values() {
|
||||||
Ok(tuple) => tuple,
|
Ok(tuple) => tuple,
|
||||||
Err(msg) => {
|
Err(msg) => {
|
||||||
error!("{}", msg);
|
error!("Error: {}", msg);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,13 +56,21 @@ pub(crate) fn handle_client_error(response: ClientError, _output_mode: &OutputMo
|
||||||
"Internal Server Error in response:{:?} {:?}",
|
"Internal Server Error in response:{:?} {:?}",
|
||||||
error_msg, message
|
error_msg, message
|
||||||
);
|
);
|
||||||
std::process::exit(exitcode::SOFTWARE);
|
std::process::exit(1);
|
||||||
} else if status == StatusCode::NOT_FOUND {
|
} else if status == StatusCode::NOT_FOUND {
|
||||||
error!("Item not found:{:?} {:?}", error_msg, message)
|
error!("Item not found:{:?} {:?}", error_msg, message)
|
||||||
} else {
|
} else {
|
||||||
error!("HTTP Error: {}{} {:?}", status, error_msg, message);
|
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);
|
eprintln!("{:?}", response);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,13 @@ pub struct CommonOpt {
|
||||||
/// Log format (still in very early development)
|
/// Log format (still in very early development)
|
||||||
#[clap(short, long = "output", env = "KANIDM_OUTPUT", default_value = "text")]
|
#[clap(short, long = "output", env = "KANIDM_OUTPUT", default_value = "text")]
|
||||||
output_mode: OutputMode,
|
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)]
|
#[derive(Debug, Args)]
|
||||||
|
|
Loading…
Reference in a new issue