98 lines
3.3 KiB
Rust
98 lines
3.3 KiB
Rust
use crate::auth::AuthedClient;
|
|
use crate::ui;
|
|
use anyhow::{anyhow, Result};
|
|
use argon2::password_hash::SaltString;
|
|
use argon2::{Argon2, PasswordHasher};
|
|
use rand::rngs::OsRng;
|
|
use rsh_types::{OpReq, OpResp};
|
|
|
|
pub fn hash_password(password: &str) -> Result<String> {
|
|
let salt = SaltString::generate(&mut OsRng);
|
|
Argon2::default()
|
|
.hash_password(password.as_bytes(), &salt)
|
|
.map(|h| h.to_string())
|
|
.map_err(|e| anyhow!("hash: {e}"))
|
|
}
|
|
|
|
pub async fn create(client: &AuthedClient, name: String) -> Result<()> {
|
|
let pw = inquire::Password::new("password (empty for none):")
|
|
.without_confirmation()
|
|
.with_display_mode(inquire::PasswordDisplayMode::Masked)
|
|
.prompt()
|
|
.map_err(|e| anyhow!("prompt: {e}"))?;
|
|
let password_hash = if pw.is_empty() { None } else { Some(hash_password(&pw)?) };
|
|
match client.req(OpReq::SessionCreate { name: name.clone(), password_hash }).await? {
|
|
OpResp::Ok => ui::print_ok(&format!("session '{name}' created")),
|
|
OpResp::Err(e) => return Err(anyhow!(e)),
|
|
other => return Err(anyhow!("unexpected: {other:?}")),
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn delete(client: &AuthedClient, name: String, yes: bool, disconnect: bool) -> Result<()> {
|
|
if !yes {
|
|
let ok = inquire::Confirm::new(&format!("delete session '{name}'?"))
|
|
.with_default(false)
|
|
.prompt()
|
|
.map_err(|e| anyhow!("prompt: {e}"))?;
|
|
if !ok {
|
|
ui::print_info("cancelled");
|
|
return Ok(());
|
|
}
|
|
}
|
|
match client.req(OpReq::SessionDelete { name: name.clone(), disconnect }).await? {
|
|
OpResp::Ok => ui::print_ok(&format!("session '{name}' deleted")),
|
|
OpResp::Err(e) => return Err(anyhow!(e)),
|
|
other => return Err(anyhow!("unexpected: {other:?}")),
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn update(
|
|
client: &AuthedClient,
|
|
name: String,
|
|
pw_flag: Option<Option<String>>,
|
|
disconnect: bool,
|
|
) -> Result<()> {
|
|
let set_password_hash = match pw_flag {
|
|
None => None,
|
|
Some(Some(p)) => Some(Some(hash_password(&p)?)),
|
|
Some(None) => {
|
|
let entered = inquire::Password::new("new password (empty clears):")
|
|
.without_confirmation()
|
|
.with_display_mode(inquire::PasswordDisplayMode::Masked)
|
|
.prompt()
|
|
.map_err(|e| anyhow!("prompt: {e}"))?;
|
|
if entered.is_empty() {
|
|
Some(None)
|
|
} else {
|
|
Some(Some(hash_password(&entered)?))
|
|
}
|
|
}
|
|
};
|
|
match client
|
|
.req(OpReq::SessionUpdate { name: name.clone(), set_password_hash, disconnect })
|
|
.await?
|
|
{
|
|
OpResp::Ok => ui::print_ok(&format!("session '{name}' updated")),
|
|
OpResp::Err(e) => return Err(anyhow!(e)),
|
|
other => return Err(anyhow!("unexpected: {other:?}")),
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn list(client: &AuthedClient) -> Result<()> {
|
|
match client.req(OpReq::SessionList).await? {
|
|
OpResp::Sessions(s) => {
|
|
if s.is_empty() {
|
|
ui::print_info("no sessions");
|
|
} else {
|
|
println!("{}", ui::sessions_table(&s));
|
|
}
|
|
}
|
|
OpResp::Err(e) => return Err(anyhow!(e)),
|
|
other => return Err(anyhow!("unexpected: {other:?}")),
|
|
}
|
|
Ok(())
|
|
}
|