show client username, check whether credential exists before requesting confirmation from frontend
This commit is contained in:
parent
9bc9cb56c1
commit
c6e22fc91b
@ -5,7 +5,8 @@ use sysinfo::{
|
|||||||
SystemExt,
|
SystemExt,
|
||||||
Pid,
|
Pid,
|
||||||
PidExt,
|
PidExt,
|
||||||
ProcessExt
|
ProcessExt,
|
||||||
|
UserExt,
|
||||||
};
|
};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
@ -16,6 +17,7 @@ use crate::errors::*;
|
|||||||
pub struct Client {
|
pub struct Client {
|
||||||
pub pid: u32,
|
pub pid: u32,
|
||||||
pub exe: Option<PathBuf>,
|
pub exe: Option<PathBuf>,
|
||||||
|
pub username: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -23,6 +25,8 @@ pub fn get_client(pid: u32, parent: bool) -> Result<Client, ClientInfoError> {
|
|||||||
let sys_pid = Pid::from_u32(pid);
|
let sys_pid = Pid::from_u32(pid);
|
||||||
let mut sys = System::new();
|
let mut sys = System::new();
|
||||||
sys.refresh_process(sys_pid);
|
sys.refresh_process(sys_pid);
|
||||||
|
sys.refresh_users_list();
|
||||||
|
|
||||||
let mut proc = sys.process(sys_pid)
|
let mut proc = sys.process(sys_pid)
|
||||||
.ok_or(ClientInfoError::ProcessNotFound)?;
|
.ok_or(ClientInfoError::ProcessNotFound)?;
|
||||||
|
|
||||||
@ -34,10 +38,15 @@ pub fn get_client(pid: u32, parent: bool) -> Result<Client, ClientInfoError> {
|
|||||||
.ok_or(ClientInfoError::ParentProcessNotFound)?;
|
.ok_or(ClientInfoError::ParentProcessNotFound)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let username = proc.user_id()
|
||||||
|
.map(|uid| sys.get_user_by_id(uid))
|
||||||
|
.flatten()
|
||||||
|
.map(|u| u.name().to_owned());
|
||||||
|
|
||||||
let exe = match proc.exe() {
|
let exe = match proc.exe() {
|
||||||
p if p == Path::new("") => None,
|
p if p == Path::new("") => None,
|
||||||
p => Some(PathBuf::from(p)),
|
p => Some(PathBuf::from(p)),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Client { pid: proc.pid().as_u32(), exe })
|
Ok(Client { pid: proc.pid().as_u32(), exe, username })
|
||||||
}
|
}
|
||||||
|
@ -139,3 +139,10 @@ pub trait PersistentCredential: for<'a> Deserialize<'a> + Sized {
|
|||||||
Ok(creds)
|
Ok(creds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn random_uuid() -> Uuid {
|
||||||
|
// a bit weird to use salt() for this, but it's convenient
|
||||||
|
let random_bytes = Crypto::salt();
|
||||||
|
Uuid::from_slice(&random_bytes[..16]).unwrap()
|
||||||
|
}
|
||||||
|
@ -6,12 +6,11 @@ use ssh_agent_lib::proto::message::{
|
|||||||
};
|
};
|
||||||
use tauri::{AppHandle, Manager};
|
use tauri::{AppHandle, Manager};
|
||||||
use tokio_stream::StreamExt;
|
use tokio_stream::StreamExt;
|
||||||
use tokio::sync::oneshot;
|
|
||||||
use tokio_util::codec::Framed;
|
use tokio_util::codec::Framed;
|
||||||
|
|
||||||
use crate::clientinfo;
|
use crate::clientinfo;
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
use crate::ipc::{Approval, RequestNotification, RequestNotificationDetail};
|
use crate::ipc::{Approval, RequestNotificationDetail};
|
||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
|
|
||||||
use super::{CloseWaiter, Stream};
|
use super::{CloseWaiter, Stream};
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
use sqlx::types::uuid::Uuid;
|
use sqlx::types::uuid::Uuid;
|
||||||
use tauri::{AppHandle, Manager};
|
use tauri::{AppHandle, Manager};
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
use tokio::sync::oneshot;
|
|
||||||
|
|
||||||
use crate::clientinfo::{self, Client};
|
use crate::clientinfo::{self, Client};
|
||||||
use crate::credentials::{
|
use crate::credentials::{
|
||||||
|
self,
|
||||||
Credential,
|
Credential,
|
||||||
CredentialRecord,
|
CredentialRecord,
|
||||||
Crypto,
|
Crypto,
|
||||||
DockerCredential
|
DockerCredential,
|
||||||
};
|
};
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
use crate::ipc::{Approval, AwsRequestNotification, RequestNotificationDetail, RequestResponse};
|
use crate::ipc::{Approval, RequestNotificationDetail};
|
||||||
use crate::shortcuts::{self, ShortcutAction};
|
use crate::shortcuts::{self, ShortcutAction};
|
||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
use super::{
|
use super::{
|
||||||
@ -116,11 +116,22 @@ async fn get_docker_credential(
|
|||||||
app_handle: AppHandle,
|
app_handle: AppHandle,
|
||||||
waiter: CloseWaiter<'_>,
|
waiter: CloseWaiter<'_>,
|
||||||
) -> Result<CliResponse, HandlerError> {
|
) -> Result<CliResponse, HandlerError> {
|
||||||
|
let state = app_handle.state::<AppState>();
|
||||||
|
let credential_id = state.credential_id(&server_url).await.unwrap_or(None);
|
||||||
|
if credential_id.is_none() {
|
||||||
|
return Err(
|
||||||
|
HandlerError::NoCredentials(
|
||||||
|
GetCredentialsError::Load(
|
||||||
|
LoadCredentialsError::NoCredentials
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let detail = RequestNotificationDetail::new_docker(client, server_url.clone());
|
let detail = RequestNotificationDetail::new_docker(client, server_url.clone());
|
||||||
let response = super::send_credentials_request(detail, app_handle.clone(), waiter).await?;
|
let response = super::send_credentials_request(detail, app_handle.clone(), waiter).await?;
|
||||||
match response.approval {
|
match response.approval {
|
||||||
Approval::Approved => {
|
Approval::Approved => {
|
||||||
let state = app_handle.state::<AppState>();
|
|
||||||
let creds = state.get_docker_credential(&server_url).await?;
|
let creds = state.get_docker_credential(&server_url).await?;
|
||||||
Ok(CliResponse::Credential(CliCredential::Docker(creds)))
|
Ok(CliResponse::Credential(CliCredential::Docker(creds)))
|
||||||
},
|
},
|
||||||
@ -139,9 +150,12 @@ async fn store_docker_credential(
|
|||||||
|
|
||||||
// eventually ask the frontend to confirm here
|
// eventually ask the frontend to confirm here
|
||||||
|
|
||||||
// a bit weird but convenient
|
// for some reason Docker likes to call `store` immediately with whatever it gets
|
||||||
let random_bytes = Crypto::salt();
|
// back from every `get` operation, so we have to check for an existing credential
|
||||||
let id = Uuid::from_slice(&random_bytes[..16]).unwrap();
|
let id = state.credential_id(&docker_credential.server_url)
|
||||||
|
.await
|
||||||
|
.map_err(|e| GetCredentialsError::Load(e))?
|
||||||
|
.unwrap_or_else(|| credentials::random_uuid());
|
||||||
|
|
||||||
let record = CredentialRecord {
|
let record = CredentialRecord {
|
||||||
id,
|
id,
|
||||||
|
@ -9,7 +9,6 @@ use tokio::io::AsyncReadExt;
|
|||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
use crate::clientinfo::Client;
|
|
||||||
use crate::credentials::{
|
use crate::credentials::{
|
||||||
AwsBaseCredential,
|
AwsBaseCredential,
|
||||||
AwsSessionCredential,
|
AwsSessionCredential,
|
||||||
|
@ -148,6 +148,13 @@ impl AppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn credential_id(&self, name: &str) -> Result<Option<Uuid>, LoadCredentialsError> {
|
||||||
|
let res = sqlx::query_scalar!(r#"SELECT id as "id: Uuid" FROM credentials WHERE name = ?"#, name)
|
||||||
|
.fetch_optional(&self.pool)
|
||||||
|
.await;
|
||||||
|
Ok(res?)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn save_credential(&self, record: CredentialRecord) -> Result<(), SaveCredentialsError> {
|
pub async fn save_credential(&self, record: CredentialRecord) -> Result<(), SaveCredentialsError> {
|
||||||
let session = self.app_session.read().await;
|
let session = self.app_session.read().await;
|
||||||
let crypto = session.try_get_crypto()?;
|
let crypto = session.try_get_crypto()?;
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
import ShowResponse from './approve/ShowResponse.svelte';
|
import ShowResponse from './approve/ShowResponse.svelte';
|
||||||
import Unlock from './Unlock.svelte';
|
import Unlock from './Unlock.svelte';
|
||||||
|
|
||||||
|
console.log($appState.currentRequest);
|
||||||
|
|
||||||
// Extra 50ms so the window can finish disappearing before the redraw
|
// Extra 50ms so the window can finish disappearing before the redraw
|
||||||
const rehideDelay = Math.min(5000, $appState.config.rehide_ms + 100);
|
const rehideDelay = Math.min(5000, $appState.config.rehide_ms + 100);
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// Extract executable name from full path
|
// Extract executable name from full path
|
||||||
const client = $appState.currentRequest.client;
|
const client = $appState.currentRequest.client;
|
||||||
const m = client.exe?.match(/\/([^/]+?$)|\\([^\\]+?$)/);
|
const m = client.exe?.match(/\/([^/]+?$)|\\([^\\]+?$)/);
|
||||||
const appName = m[1] || m[2];
|
const appName = m ? m[1] || m[2] : '';
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
@ -61,6 +61,8 @@
|
|||||||
<code class="">{@html client.exe ? breakPath(client.exe) : 'Unknown'}</code>
|
<code class="">{@html client.exe ? breakPath(client.exe) : 'Unknown'}</code>
|
||||||
<div class="text-right">PID:</div>
|
<div class="text-right">PID:</div>
|
||||||
<code>{client.pid}</code>
|
<code>{client.pid}</code>
|
||||||
|
<div class="text-right">User:</div>
|
||||||
|
<code>{client.username ?? 'Unknown'}</code>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user