Skip to content

Commit

Permalink
Improved the way we inject Passphrase
Browse files Browse the repository at this point in the history
  • Loading branch information
amigin committed Nov 26, 2024
1 parent f8d0651 commit 467d626
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 63 deletions.
12 changes: 5 additions & 7 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,10 @@ fn ActiveApp() -> Element {

let main_state_read_access = main_state.read();

if let Some(ssh_cert_prompt) = main_state_read_access.prompt_ssh_cert {
if ssh_cert_prompt {
return rsx! {
EnterCert {}
};
}
if main_state_read_access.ssh_pass_key_promt {
return rsx! {
EnterCert {}
};
}

rsx! {
Expand All @@ -100,6 +98,6 @@ pub async fn get_envs() -> Result<GetEnvsModel, ServerFnError> {

Ok(GetEnvsModel {
envs: result.envs.keys().cloned().collect(),
ssh_cert_prompt: result.ssh_cert_prompt,
ssh_pass_key_promt: result.ssh_pass_key_promt.unwrap_or(false),
})
}
6 changes: 3 additions & 3 deletions src/main_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::models::{AppVersionsHttpModel, GetEnvsModel};
pub struct MainState {
pub selected_env: Rc<String>,
pub envs: Option<Vec<Rc<String>>>,
pub prompt_ssh_cert: Option<bool>,
pub ssh_pass_key_promt: bool,
pub data: DataState<AppVersionsHttpModel>,
}

Expand All @@ -18,7 +18,7 @@ impl MainState {
selected_env: Rc::new(selected_env),
envs: None,
data: DataState::new(),
prompt_ssh_cert: None,
ssh_pass_key_promt: false,
}
}

Expand All @@ -40,7 +40,7 @@ impl MainState {
self.selected_env = envs.first().unwrap().clone();
}

self.prompt_ssh_cert = Some(model.ssh_cert_prompt);
self.ssh_pass_key_promt = model.ssh_pass_key_promt;

self.envs = Some(envs);
}
Expand Down
2 changes: 1 addition & 1 deletion src/models/get_envs_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct GetEnvsModel {
pub envs: Vec<String>,
pub ssh_cert_prompt: bool,
pub ssh_pass_key_promt: bool,
}
52 changes: 36 additions & 16 deletions src/server/app_ctx/app_ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,49 @@ use flurl::FlUrl;
use my_settings_reader::SettingsReader;
use tokio::sync::Mutex;

pub struct SshCertificate {
pub private_key: String,
pub pass_phrase: String,
}
use super::{CertData, SshPrivateKeyCache};

pub struct AppContext {
pub settings_reader: SettingsReader<SettingsModel>,
pub cert: Mutex<Option<SshCertificate>>,
pub ssh_private_key: Mutex<Option<SshPrivateKeyCache>>,
}

impl AppContext {
pub fn new() -> Self {
Self {
settings_reader: SettingsReader::new("~/.versions-control-ui"),
cert: Mutex::new(None),
ssh_private_key: Mutex::new(None),
}
}

pub async fn post_ssh_pass_phrase(&self, pass_phrase: Option<String>) {
let mut ssh_private_key = self.ssh_private_key.lock().await;

if let Some(ssh_private_key) = &mut *ssh_private_key {
ssh_private_key.set_ssh_pass_phrase(pass_phrase);
return;
}

let path = self
.settings_reader
.get(|settings| settings.ssh_private_key_path.clone())
.await;
*ssh_private_key = Some(SshPrivateKeyCache::new(path, pass_phrase).await);
}

async fn get_ssh_private_key(&self) -> Option<CertData> {
let mut ssh_private_key = self.ssh_private_key.lock().await;

loop {
if let Some(ssh_private_key) = &*ssh_private_key {
return ssh_private_key.get_cert_data();
}

let path = self
.settings_reader
.get(|settings| settings.ssh_private_key_path.clone())
.await;
*ssh_private_key = Some(SshPrivateKeyCache::new(path, None).await);
}
}

Expand All @@ -41,17 +69,9 @@ impl AppContext {
let fl_url = FlUrl::new(url);

if fl_url.via_ssh() {
let ssh_key_access = self.cert.lock().await;

if let Some(ssh_key) = ssh_key_access.as_ref() {
let pass_phrase = if ssh_key.pass_phrase.is_empty() {
None
} else {
Some(ssh_key.pass_phrase.clone())
};

if let Some(cert_data) = self.get_ssh_private_key().await {
return (
fl_url.set_ssh_private_key(ssh_key.private_key.clone(), pass_phrase),
fl_url.set_ssh_private_key(cert_data.private_key, cert_data.pass_phrase),
host,
);
}
Expand Down
2 changes: 2 additions & 0 deletions src/server/app_ctx/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
mod app_ctx;
pub use app_ctx::*;
mod ssh_private_key_cache;
pub use ssh_private_key_cache::*;
47 changes: 47 additions & 0 deletions src/server/app_ctx/ssh_private_key_cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#[derive(Debug, Clone)]
pub struct CertData {
pub private_key: String,
pub pass_phrase: Option<String>,
}

pub struct SshPrivateKeyCache {
cert_data: Option<CertData>,
}

impl SshPrivateKeyCache {
pub async fn new(path: Option<String>, pass_key: Option<String>) -> Self {
if path.is_none() {
return Self { cert_data: None };
}

let path = rust_extensions::file_utils::format_path(path.unwrap());

let cert = tokio::fs::read_to_string(path.as_str()).await;

if let Err(err) = &cert {
panic!(
"Can not read ssh private key file '{}'. Error: {}",
path.as_str(),
err
);
}

Self {
cert_data: CertData {
private_key: cert.unwrap(),
pass_phrase: pass_key,
}
.into(),
}
}

pub fn get_cert_data(&self) -> Option<CertData> {
self.cert_data.clone()
}

pub fn set_ssh_pass_phrase(&mut self, pass_phrase: Option<String>) {
if let Some(cert_data) = &mut self.cert_data {
cert_data.pass_phrase = pass_phrase;
}
}
}
4 changes: 0 additions & 4 deletions src/server/requests/set_to_release.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
use std::collections::BTreeMap;

use serde::*;

use crate::models::*;

pub async fn set_to_release(
env_id: &str,
tag: String,
Expand Down
8 changes: 2 additions & 6 deletions src/server/settings_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,10 @@ use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct SettingsModel {
pub envs: BTreeMap<String, EnvModel>,
pub ssh_cert_prompt: bool,
pub ssh_pass_key_promt: Option<bool>,
pub ssh_private_key_path: Option<String>,
}

impl SettingsModel {
pub fn get_envs(&self) -> Vec<String> {
self.envs.keys().cloned().collect()
}
}
#[derive(Serialize, Deserialize)]
pub struct EnvModel {
pub url: String,
Expand Down
36 changes: 10 additions & 26 deletions src/views/enter_cert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ pub fn EnterCert() -> Element {
};
}

let disabled = state_read_access.private_key.len() == 0;

rsx! {

div { style: "text-align: center; padding: 20px; width: 500px; margin: auto",
Expand All @@ -30,32 +28,16 @@ pub fn EnterCert() -> Element {
}
}

div {
label { "Enter ssh cert private key" }

textarea {
class: "form-control",
style: "min-height: 300px;",

value: state_read_access.private_key.as_str(),

oninput: move |e| {
state.write().private_key = e.value();
}
}
}

button {
class: "btn btn-primary",
disabled,
onclick: move |_| {
let cert_data = state.read().clone();
spawn(async move {
match post_cert(cert_data.private_key, cert_data.pass_phrase).await {
match post_cert(cert_data.pass_phrase).await {
Ok(_) => {
consume_context::<Signal<MainState>>()
.write()
.prompt_ssh_cert = Some(false);
.ssh_pass_key_promt = false;
}
Err(err) => {
state.write().err = Some(err.to_string());
Expand All @@ -71,17 +53,19 @@ pub fn EnterCert() -> Element {

#[derive(Default, Clone)]
pub struct EnterCertState {
pub private_key: String,
pub pass_phrase: String,
pub err: Option<String>,
}

#[server]
async fn post_cert(private_key: String, pass_phrase: String) -> Result<(), ServerFnError> {
let ssh_cert = crate::server::SshCertificate {
private_key,
pass_phrase,
async fn post_cert(pass_phrase: String) -> Result<(), ServerFnError> {
let pass_phrase = if pass_phrase.is_empty() {
None
} else {
Some(pass_phrase)
};
*crate::server::APP_CTX.cert.lock().await = Some(ssh_cert);
crate::server::APP_CTX
.post_ssh_pass_phrase(pass_phrase)
.await;
Ok(())
}

0 comments on commit 467d626

Please sign in to comment.