Compare commits
No commits in common. "a49bd47e8ccd6f83d636305c11aa91664cabb0ce" and "a32e36be7edb7244ee355ecde65959ef6ffa7261" have entirely different histories.
a49bd47e8c
...
a32e36be7e
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "creddy",
|
"name": "creddy",
|
||||||
"version": "0.5.3",
|
"version": "0.5.2",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
|
2
src-tauri/Cargo.lock
generated
2
src-tauri/Cargo.lock
generated
@ -1196,7 +1196,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "creddy"
|
name = "creddy"
|
||||||
version = "0.5.3"
|
version = "0.5.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argon2",
|
"argon2",
|
||||||
"auto-launch",
|
"auto-launch",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "creddy"
|
name = "creddy"
|
||||||
version = "0.5.3"
|
version = "0.5.2"
|
||||||
description = "A friendly AWS credentials manager"
|
description = "A friendly AWS credentials manager"
|
||||||
authors = ["Joseph Montanaro"]
|
authors = ["Joseph Montanaro"]
|
||||||
license = ""
|
license = ""
|
||||||
|
@ -11,7 +11,13 @@ use std::{
|
|||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let res = match cli::parser().get_matches().subcommand() {
|
let args = cli::parser().get_matches();
|
||||||
|
if let Some(true) = args.get_one::<bool>("help") {
|
||||||
|
cli::parser().print_help().unwrap(); // if we can't print help we can't print an error
|
||||||
|
process::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = match args.subcommand() {
|
||||||
None | Some(("run", _)) => launch_gui(),
|
None | Some(("run", _)) => launch_gui(),
|
||||||
Some(("get", m)) => cli::get(m),
|
Some(("get", m)) => cli::get(m),
|
||||||
Some(("exec", m)) => cli::exec(m),
|
Some(("exec", m)) => cli::exec(m),
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::process::Command as ChildCommand;
|
use std::process::Command as ChildCommand;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@ -10,7 +9,6 @@ use clap::{
|
|||||||
ArgMatches,
|
ArgMatches,
|
||||||
ArgAction,
|
ArgAction,
|
||||||
builder::PossibleValuesParser,
|
builder::PossibleValuesParser,
|
||||||
value_parser,
|
|
||||||
};
|
};
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
|
|
||||||
@ -39,14 +37,6 @@ pub fn parser() -> Command<'static> {
|
|||||||
Command::new("creddy")
|
Command::new("creddy")
|
||||||
.version(env!("CARGO_PKG_VERSION"))
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
.about("A friendly AWS credentials manager")
|
.about("A friendly AWS credentials manager")
|
||||||
.arg(
|
|
||||||
Arg::new("server_addr")
|
|
||||||
.short('a')
|
|
||||||
.long("server-addr")
|
|
||||||
.takes_value(true)
|
|
||||||
.value_parser(value_parser!(PathBuf))
|
|
||||||
.help("Connect to the main Creddy process at this address")
|
|
||||||
)
|
|
||||||
.subcommand(
|
.subcommand(
|
||||||
Command::new("run")
|
Command::new("run")
|
||||||
.about("Launch Creddy")
|
.about("Launch Creddy")
|
||||||
@ -81,7 +71,6 @@ pub fn parser() -> Command<'static> {
|
|||||||
Arg::new("name")
|
Arg::new("name")
|
||||||
.short('n')
|
.short('n')
|
||||||
.long("name")
|
.long("name")
|
||||||
.takes_value(true)
|
|
||||||
.help("If unspecified, use default credentials")
|
.help("If unspecified, use default credentials")
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@ -105,9 +94,8 @@ pub fn parser() -> Command<'static> {
|
|||||||
pub fn get(args: &ArgMatches) -> Result<(), CliError> {
|
pub fn get(args: &ArgMatches) -> Result<(), CliError> {
|
||||||
let name = args.get_one("name").cloned();
|
let name = args.get_one("name").cloned();
|
||||||
let base = *args.get_one("base").unwrap_or(&false);
|
let base = *args.get_one("base").unwrap_or(&false);
|
||||||
let addr = args.get_one("server_addr").cloned();
|
|
||||||
|
|
||||||
let output = match make_request(addr, &Request::GetAwsCredentials { name, base })? {
|
let output = match make_request(&Request::GetAwsCredentials { name, base })? {
|
||||||
Response::AwsBase(creds) => serde_json::to_string(&creds).unwrap(),
|
Response::AwsBase(creds) => serde_json::to_string(&creds).unwrap(),
|
||||||
Response::AwsSession(creds) => serde_json::to_string(&creds).unwrap(),
|
Response::AwsSession(creds) => serde_json::to_string(&creds).unwrap(),
|
||||||
r => return Err(RequestError::Unexpected(r).into()),
|
r => return Err(RequestError::Unexpected(r).into()),
|
||||||
@ -120,7 +108,6 @@ pub fn get(args: &ArgMatches) -> Result<(), CliError> {
|
|||||||
pub fn exec(args: &ArgMatches) -> Result<(), CliError> {
|
pub fn exec(args: &ArgMatches) -> Result<(), CliError> {
|
||||||
let name = args.get_one("name").cloned();
|
let name = args.get_one("name").cloned();
|
||||||
let base = *args.get_one("base").unwrap_or(&false);
|
let base = *args.get_one("base").unwrap_or(&false);
|
||||||
let addr = args.get_one("server_addr").cloned();
|
|
||||||
let mut cmd_line = args.get_many("command")
|
let mut cmd_line = args.get_many("command")
|
||||||
.ok_or(ExecError::NoCommand)?;
|
.ok_or(ExecError::NoCommand)?;
|
||||||
|
|
||||||
@ -128,7 +115,7 @@ pub fn exec(args: &ArgMatches) -> Result<(), CliError> {
|
|||||||
let mut cmd = ChildCommand::new(cmd_name);
|
let mut cmd = ChildCommand::new(cmd_name);
|
||||||
cmd.args(cmd_line);
|
cmd.args(cmd_line);
|
||||||
|
|
||||||
match make_request(addr, &Request::GetAwsCredentials { name, base })? {
|
match make_request(&Request::GetAwsCredentials { name, base })? {
|
||||||
Response::AwsBase(creds) => {
|
Response::AwsBase(creds) => {
|
||||||
cmd.env("AWS_ACCESS_KEY_ID", creds.access_key_id);
|
cmd.env("AWS_ACCESS_KEY_ID", creds.access_key_id);
|
||||||
cmd.env("AWS_SECRET_ACCESS_KEY", creds.secret_access_key);
|
cmd.env("AWS_SECRET_ACCESS_KEY", creds.secret_access_key);
|
||||||
@ -173,7 +160,6 @@ pub fn exec(args: &ArgMatches) -> Result<(), CliError> {
|
|||||||
|
|
||||||
|
|
||||||
pub fn invoke_shortcut(args: &ArgMatches) -> Result<(), CliError> {
|
pub fn invoke_shortcut(args: &ArgMatches) -> Result<(), CliError> {
|
||||||
let addr = args.get_one("server_addr").cloned();
|
|
||||||
let action = match args.get_one::<String>("action").map(|s| s.as_str()) {
|
let action = match args.get_one::<String>("action").map(|s| s.as_str()) {
|
||||||
Some("show_window") => ShortcutAction::ShowWindow,
|
Some("show_window") => ShortcutAction::ShowWindow,
|
||||||
Some("launch_terminal") => ShortcutAction::LaunchTerminal,
|
Some("launch_terminal") => ShortcutAction::LaunchTerminal,
|
||||||
@ -181,7 +167,7 @@ pub fn invoke_shortcut(args: &ArgMatches) -> Result<(), CliError> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let req = Request::InvokeShortcut(action);
|
let req = Request::InvokeShortcut(action);
|
||||||
match make_request(addr, &req) {
|
match make_request(&req) {
|
||||||
Ok(Response::Empty) => Ok(()),
|
Ok(Response::Empty) => Ok(()),
|
||||||
Ok(r) => Err(RequestError::Unexpected(r).into()),
|
Ok(r) => Err(RequestError::Unexpected(r).into()),
|
||||||
Err(e) => Err(e.into()),
|
Err(e) => Err(e.into()),
|
||||||
@ -190,12 +176,12 @@ pub fn invoke_shortcut(args: &ArgMatches) -> Result<(), CliError> {
|
|||||||
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn make_request(addr: Option<PathBuf>, req: &Request) -> Result<Response, RequestError> {
|
async fn make_request(req: &Request) -> Result<Response, RequestError> {
|
||||||
let mut data = serde_json::to_string(req).unwrap();
|
let mut data = serde_json::to_string(req).unwrap();
|
||||||
// server expects newline marking end of request
|
// server expects newline marking end of request
|
||||||
data.push('\n');
|
data.push('\n');
|
||||||
|
|
||||||
let mut stream = connect(addr).await?;
|
let mut stream = connect().await?;
|
||||||
stream.write_all(&data.as_bytes()).await?;
|
stream.write_all(&data.as_bytes()).await?;
|
||||||
|
|
||||||
let mut buf = Vec::with_capacity(1024);
|
let mut buf = Vec::with_capacity(1024);
|
||||||
@ -206,10 +192,10 @@ async fn make_request(addr: Option<PathBuf>, req: &Request) -> Result<Response,
|
|||||||
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
async fn connect(addr: Option<PathBuf>) -> Result<NamedPipeClient, std::io::Error> {
|
async fn connect() -> Result<NamedPipeClient, std::io::Error> {
|
||||||
// apparently attempting to connect can fail if there's already a client connected
|
// apparently attempting to connect can fail if there's already a client connected
|
||||||
loop {
|
loop {
|
||||||
let addr = addr.unwrap_or_else(|| srv::addr("creddy-server"));
|
let addr = srv::addr("creddy-server");
|
||||||
match ClientOptions::new().open(&addr) {
|
match ClientOptions::new().open(&addr) {
|
||||||
Ok(stream) => return Ok(stream),
|
Ok(stream) => return Ok(stream),
|
||||||
Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY.0 as i32) => (),
|
Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY.0 as i32) => (),
|
||||||
@ -221,7 +207,7 @@ async fn connect(addr: Option<PathBuf>) -> Result<NamedPipeClient, std::io::Erro
|
|||||||
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
async fn connect(addr: Option<PathBuf>) -> Result<UnixStream, std::io::Error> {
|
async fn connect() -> Result<UnixStream, std::io::Error> {
|
||||||
let path = addr.unwrap_or_else(|| srv::addr("creddy-server"));
|
let path = srv::addr("creddy-server");
|
||||||
UnixStream::connect(&path).await
|
UnixStream::connect(&path).await
|
||||||
}
|
}
|
||||||
|
@ -203,6 +203,19 @@ mod tests {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_uuid() -> Uuid {
|
||||||
|
Uuid::try_parse("00000000-0000-0000-0000-000000000000").unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_uuid_2() -> Uuid {
|
||||||
|
Uuid::try_parse("ffffffff-ffff-ffff-ffff-ffffffffffff").unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_uuid_random() -> Uuid {
|
||||||
|
let bytes = Crypto::salt();
|
||||||
|
Uuid::from_slice(&bytes[..16]).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[sqlx::test(fixtures("aws_credentials"))]
|
#[sqlx::test(fixtures("aws_credentials"))]
|
||||||
async fn test_load(pool: SqlitePool) {
|
async fn test_load(pool: SqlitePool) {
|
||||||
|
@ -112,16 +112,15 @@ impl CredentialRecord {
|
|||||||
Ok(Self::from_parts(row, credential))
|
Ok(Self::from_parts(row, credential))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
// pub async fn load(id: &Uuid, crypto: &Crypto, pool: &SqlitePool) -> Result<Self, LoadCredentialsError> {
|
||||||
pub async fn load(id: &Uuid, crypto: &Crypto, pool: &SqlitePool) -> Result<Self, LoadCredentialsError> {
|
// let row: CredentialRow = sqlx::query_as("SELECT * FROM credentials WHERE id = ?")
|
||||||
let row: CredentialRow = sqlx::query_as("SELECT * FROM credentials WHERE id = ?")
|
// .bind(id)
|
||||||
.bind(id)
|
// .fetch_optional(pool)
|
||||||
.fetch_optional(pool)
|
// .await?
|
||||||
.await?
|
// .ok_or(LoadCredentialsError::NoCredentials)?;
|
||||||
.ok_or(LoadCredentialsError::NoCredentials)?;
|
|
||||||
|
|
||||||
Self::load_credential(row, crypto, pool).await
|
// Self::load_credential(row, crypto, pool).await
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub async fn load_by_name(name: &str, crypto: &Crypto, pool: &SqlitePool) -> Result<Self, LoadCredentialsError> {
|
pub async fn load_by_name(name: &str, crypto: &Crypto, pool: &SqlitePool) -> Result<Self, LoadCredentialsError> {
|
||||||
let row: CredentialRow = sqlx::query_as("SELECT * FROM credentials WHERE name = ?")
|
let row: CredentialRow = sqlx::query_as("SELECT * FROM credentials WHERE name = ?")
|
||||||
|
@ -298,6 +298,7 @@ fn deserialize_algorithm<'de, D>(deserializer: D) -> Result<Algorithm, D::Error>
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
|
use ssh_key::Fingerprint;
|
||||||
use sqlx::types::uuid::uuid;
|
use sqlx::types::uuid::uuid;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -446,7 +447,7 @@ mod tests {
|
|||||||
async fn test_load_db(pool: SqlitePool) {
|
async fn test_load_db(pool: SqlitePool) {
|
||||||
let crypto = Crypto::fixed();
|
let crypto = Crypto::fixed();
|
||||||
let id = uuid!("11111111-1111-1111-1111-111111111111");
|
let id = uuid!("11111111-1111-1111-1111-111111111111");
|
||||||
SshKey::load(&id, &crypto, &pool).await
|
let k = SshKey::load(&id, &crypto, &pool).await
|
||||||
.expect("Failed to load SSH key from database");
|
.expect("Failed to load SSH key from database");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,14 +44,12 @@ pub async fn load_bytes(pool: &SqlitePool, name: &str) -> Result<Option<Vec<u8>>
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// we don't have a need for this right now, but we will some day
|
// pub async fn delete(pool: &SqlitePool, name: &str) -> Result<(), sqlx::Error> {
|
||||||
#[cfg(test)]
|
// sqlx::query!("DELETE FROM kv WHERE name = ?", name)
|
||||||
pub async fn delete(pool: &SqlitePool, name: &str) -> Result<(), sqlx::Error> {
|
// .execute(pool)
|
||||||
sqlx::query!("DELETE FROM kv WHERE name = ?", name)
|
// .await?;
|
||||||
.execute(pool)
|
// Ok(())
|
||||||
.await?;
|
// }
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub async fn delete_multi(pool: &SqlitePool, names: &[&str]) -> Result<(), sqlx::Error> {
|
pub async fn delete_multi(pool: &SqlitePool, names: &[&str]) -> Result<(), sqlx::Error> {
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"productName": "creddy",
|
"productName": "creddy",
|
||||||
"version": "0.5.3",
|
"version": "0.5.2",
|
||||||
"identifier": "creddy",
|
"identifier": "creddy",
|
||||||
"plugins": {},
|
"plugins": {},
|
||||||
"app": {
|
"app": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user