diff --git a/tools/cli/src/cli/person.rs b/tools/cli/src/cli/person.rs index 1d0a44f8d..ce14fdf5b 100644 --- a/tools/cli/src/cli/person.rs +++ b/tools/cli/src/cli/person.rs @@ -162,7 +162,7 @@ impl PersonOpt { } PersonPosix::SetPassword(aopt) => { let client = aopt.copt.to_client(OpType::Write).await; - let password = match password_prompt("Enter new posix (sudo) password: ") { + let password = match password_prompt("Enter new posix (sudo) password") { Some(v) => v, None => { println!("Passwords do not match"); diff --git a/tools/cli/src/cli/session.rs b/tools/cli/src/cli/session.rs index fb96b7b10..d5d1378ae 100644 --- a/tools/cli/src/cli/session.rs +++ b/tools/cli/src/cli/session.rs @@ -254,7 +254,7 @@ async fn do_password( password.to_owned() } None => dialoguer::Password::new() - .with_prompt("Enter password: ") + .with_prompt("Enter password") .interact() .unwrap_or_else(|e| { error!("Failed to create password prompt -- {:?}", e); diff --git a/unix_integration/common/src/client.rs b/unix_integration/common/src/client.rs index 7dfca0170..85414a623 100644 --- a/unix_integration/common/src/client.rs +++ b/unix_integration/common/src/client.rs @@ -29,11 +29,11 @@ impl Decoder for ClientCodec { } } -impl Encoder for ClientCodec { +impl Encoder<&ClientRequest> for ClientCodec { type Error = IoError; - fn encode(&mut self, msg: ClientRequest, dst: &mut BytesMut) -> Result<(), Self::Error> { - let data = serde_json::to_vec(&msg).map_err(|e| { + fn encode(&mut self, msg: &ClientRequest, dst: &mut BytesMut) -> Result<(), Self::Error> { + let data = serde_json::to_vec(msg).map_err(|e| { error!("socket encoding error -> {:?}", e); IoError::new(ErrorKind::Other, "JSON encode error") })?; @@ -49,48 +49,63 @@ impl ClientCodec { } } -async fn call_daemon_inner( - path: &str, - req: ClientRequest, -) -> Result> { - trace!(?path, ?req); - let stream = UnixStream::connect(path).await?; - trace!("connected"); +pub struct DaemonClient { + req_stream: Framed, + default_timeout: u64, +} - let mut reqs = Framed::new(stream, ClientCodec::new()); +impl DaemonClient { + pub async fn new(path: &str, default_timeout: u64) -> Result> { + trace!(?path); + let stream = UnixStream::connect(path).await.inspect_err(|e| { + error!( + "Unix socket stream setup error while connecting to {} -> {:?}", + path, e + ); + })?; - reqs.send(req).await?; - reqs.flush().await?; - trace!("flushed, waiting ..."); + let req_stream = Framed::new(stream, ClientCodec::new()); - match reqs.next().await { - Some(Ok(res)) => { - debug!("Response -> {:?}", res); - Ok(res) + trace!("connected"); + + Ok(DaemonClient { + req_stream, + default_timeout, + }) + } + + async fn call_inner(&mut self, req: &ClientRequest) -> Result> { + self.req_stream.send(req).await?; + self.req_stream.flush().await?; + trace!("flushed, waiting ..."); + match self.req_stream.next().await { + Some(Ok(res)) => { + debug!("Response -> {:?}", res); + Ok(res) + } + _ => { + error!("Error making request to kanidm_unixd"); + Err(Box::new(IoError::new(ErrorKind::Other, "oh no!"))) + } } - _ => { - error!("Error making request to kanidm_unixd"); - Err(Box::new(IoError::new(ErrorKind::Other, "oh no!"))) - } - } -} - -/// Makes a call to kanidm_unixd via a unix socket at `path` -pub async fn call_daemon( - path: &str, - req: ClientRequest, - timeout: u64, -) -> Result> { - let sleep = time::sleep(Duration::from_secs(timeout)); - tokio::pin!(sleep); - - tokio::select! { - _ = &mut sleep => { - error!(?timeout, "Timed out making request to kanidm_unixd"); - Err(Box::new(IoError::new(ErrorKind::Other, "timeout"))) - } - res = call_daemon_inner(path, req) => { - res + } + + pub async fn call( + &mut self, + req: &ClientRequest, + timeout: Option, + ) -> Result> { + let sleep = time::sleep(Duration::from_secs(timeout.unwrap_or(self.default_timeout))); + tokio::pin!(sleep); + + tokio::select! { + _ = &mut sleep => { + error!(?timeout, "Timed out making request to kanidm_unixd"); + Err(Box::new(IoError::new(ErrorKind::Other, "timeout"))) + } + res = self.call_inner(req) => { + res + } } } } diff --git a/unix_integration/resolver/src/bin/kanidm-unix.rs b/unix_integration/resolver/src/bin/kanidm-unix.rs index 9c377149b..b27424769 100644 --- a/unix_integration/resolver/src/bin/kanidm-unix.rs +++ b/unix_integration/resolver/src/bin/kanidm-unix.rs @@ -16,17 +16,55 @@ extern crate tracing; use std::process::ExitCode; use clap::Parser; -use kanidm_unix_common::client::call_daemon; +use kanidm_unix_common::client::DaemonClient; use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH; use kanidm_unix_common::unix_config::KanidmUnixdConfig; use kanidm_unix_common::unix_proto::{ ClientRequest, ClientResponse, PamAuthRequest, PamAuthResponse, PamServiceInfo, }; -// use std::io; use std::path::PathBuf; include!("../opt/tool.rs"); +macro_rules! setup_client { + () => {{ + let Ok(cfg) = + KanidmUnixdConfig::new().read_options_from_optional_config(DEFAULT_CONFIG_PATH) + else { + error!("Failed to parse {}", DEFAULT_CONFIG_PATH); + return ExitCode::FAILURE; + }; + + debug!("Connecting to resolver ..."); + + debug!( + "Using kanidm_unixd socket path: {:?}", + cfg.sock_path.as_str() + ); + + // see if the kanidm_unixd socket exists and quit if not + if !PathBuf::from(&cfg.sock_path).exists() { + error!( + "Failed to find unix socket at {}, quitting!", + cfg.sock_path.as_str() + ); + return ExitCode::FAILURE; + } + + match DaemonClient::new(cfg.sock_path.as_str(), cfg.unix_sock_timeout).await { + Ok(dc) => dc, + Err(err) => { + error!( + "Failed to connect to resolver at {}-> {:?}", + cfg.sock_path.as_str(), + err + ); + return ExitCode::FAILURE; + } + } + }}; +} + #[tokio::main(flavor = "current_thread")] async fn main() -> ExitCode { let opt = KanidmUnixParser::parse(); @@ -54,12 +92,7 @@ async fn main() -> ExitCode { } => { debug!("Starting PAM auth tester tool ..."); - let Ok(cfg) = - KanidmUnixdConfig::new().read_options_from_optional_config(DEFAULT_CONFIG_PATH) - else { - error!("Failed to parse {}", DEFAULT_CONFIG_PATH); - return ExitCode::FAILURE; - }; + let mut daemon_client = setup_client!(); info!("Sending request for user {}", &account_id); @@ -72,7 +105,7 @@ async fn main() -> ExitCode { }, }; loop { - match call_daemon(cfg.sock_path.as_str(), req, cfg.unix_sock_timeout).await { + match daemon_client.call(&req, None).await { Ok(r) => match r { ClientResponse::PamAuthenticateStepResponse(PamAuthResponse::Success) => { println!("auth success!"); @@ -90,7 +123,7 @@ async fn main() -> ExitCode { ClientResponse::PamAuthenticateStepResponse(PamAuthResponse::Password) => { // Prompt for and get the password let cred = match dialoguer::Password::new() - .with_prompt("Enter Unix password: ") + .with_prompt("Enter Unix password") .interact() { Ok(p) => p, @@ -133,7 +166,7 @@ async fn main() -> ExitCode { let sereq = ClientRequest::PamAccountAllowed(account_id); - match call_daemon(cfg.sock_path.as_str(), sereq, cfg.unix_sock_timeout).await { + match daemon_client.call(&sereq, None).await { Ok(r) => match r { ClientResponse::PamStatus(Some(true)) => { println!("account success!"); @@ -158,15 +191,7 @@ async fn main() -> ExitCode { KanidmUnixOpt::CacheClear { debug: _, really } => { debug!("Starting cache clear tool ..."); - let cfg = match KanidmUnixdConfig::new() - .read_options_from_optional_config(DEFAULT_CONFIG_PATH) - { - Ok(c) => c, - Err(_e) => { - error!("Failed to parse {}", DEFAULT_CONFIG_PATH); - return ExitCode::FAILURE; - } - }; + let mut daemon_client = setup_client!(); if !really { error!("Are you sure you want to proceed? If so use --really"); @@ -175,7 +200,7 @@ async fn main() -> ExitCode { let req = ClientRequest::ClearCache; - match call_daemon(cfg.sock_path.as_str(), req, cfg.unix_sock_timeout).await { + match daemon_client.call(&req, None).await { Ok(r) => match r { ClientResponse::Ok => info!("success"), _ => { @@ -192,19 +217,11 @@ async fn main() -> ExitCode { KanidmUnixOpt::CacheInvalidate { debug: _ } => { debug!("Starting cache invalidate tool ..."); - let cfg = match KanidmUnixdConfig::new() - .read_options_from_optional_config(DEFAULT_CONFIG_PATH) - { - Ok(c) => c, - Err(_e) => { - error!("Failed to parse {}", DEFAULT_CONFIG_PATH); - return ExitCode::FAILURE; - } - }; + let mut daemon_client = setup_client!(); let req = ClientRequest::InvalidateCache; - match call_daemon(cfg.sock_path.as_str(), req, cfg.unix_sock_timeout).await { + match daemon_client.call(&req, None).await { Ok(r) => match r { ClientResponse::Ok => info!("success"), _ => { @@ -221,43 +238,26 @@ async fn main() -> ExitCode { KanidmUnixOpt::Status { debug: _ } => { trace!("Starting cache status tool ..."); - let cfg = match KanidmUnixdConfig::new() - .read_options_from_optional_config(DEFAULT_CONFIG_PATH) - { - Ok(c) => c, - Err(_e) => { - error!("Failed to parse {}", DEFAULT_CONFIG_PATH); - return ExitCode::FAILURE; - } - }; - + let mut daemon_client = setup_client!(); let req = ClientRequest::Status; - let spath = PathBuf::from(cfg.sock_path.as_str()); - if !spath.exists() { - error!( - "kanidm_unixd socket {} does not exist - is the service running?", - cfg.sock_path - ) - } else { - match call_daemon(cfg.sock_path.as_str(), req, cfg.unix_sock_timeout).await { - Ok(r) => match r { - ClientResponse::ProviderStatus(results) => { - for provider in results { - println!( - "{}: {}", - provider.name, - if provider.online { "online" } else { "offline" } - ); - } + match daemon_client.call(&req, None).await { + Ok(r) => match r { + ClientResponse::ProviderStatus(results) => { + for provider in results { + println!( + "{}: {}", + provider.name, + if provider.online { "online" } else { "offline" } + ); } - _ => { - error!("Error: unexpected response -> {:?}", r); - } - }, - Err(e) => { - error!("Error -> {:?}", e); } + _ => { + error!("Error: unexpected response -> {:?}", r); + } + }, + Err(e) => { + error!("Error -> {:?}", e); } } ExitCode::SUCCESS diff --git a/unix_integration/resolver/src/bin/kanidm_ssh_authorizedkeys.rs b/unix_integration/resolver/src/bin/kanidm_ssh_authorizedkeys.rs index 6adea63ce..e20ff3178 100644 --- a/unix_integration/resolver/src/bin/kanidm_ssh_authorizedkeys.rs +++ b/unix_integration/resolver/src/bin/kanidm_ssh_authorizedkeys.rs @@ -17,7 +17,7 @@ use std::path::PathBuf; use std::process::ExitCode; use clap::Parser; -use kanidm_unix_common::client::call_daemon; +use kanidm_unix_common::client::DaemonClient; use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH; use kanidm_unix_common::unix_config::KanidmUnixdConfig; use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse}; @@ -66,21 +66,37 @@ async fn main() -> ExitCode { ); return ExitCode::FAILURE; } + + let mut daemon_client = + match DaemonClient::new(cfg.sock_path.as_str(), cfg.unix_sock_timeout).await { + Ok(dc) => dc, + Err(err) => { + error!( + "Failed to connect to resolver at {}-> {:?}", + cfg.sock_path.as_str(), + err + ); + return ExitCode::FAILURE; + } + }; + // safe because we've already thrown an error if it's not there let req = ClientRequest::SshKey(opt.account_id.unwrap_or("".to_string())); - match call_daemon(cfg.sock_path.as_str(), req, cfg.unix_sock_timeout).await { - Ok(r) => match r { - ClientResponse::SshKeys(sk) => sk.iter().for_each(|k| { + match daemon_client.call(&req, None).await { + Ok(ClientResponse::SshKeys(sk)) => { + sk.iter().for_each(|k| { println!("{}", k); - }), - _ => { - error!("Error calling kanidm_unixd: unexpected response -> {:?}", r); - } - }, + }); + ExitCode::SUCCESS + } + Ok(r) => { + error!("Error calling kanidm_unixd: unexpected response -> {:?}", r); + ExitCode::FAILURE + } Err(e) => { error!("Error calling kanidm_unixd -> {:?}", e); + ExitCode::FAILURE } - }; - ExitCode::SUCCESS + } }