Compare commits
No commits in common. "02ba19d709e90bf7346f5166cd2cb891af14794d" and "27c2f467c458b6f5571ee37892b3368fa02797a5" have entirely different histories.
02ba19d709
...
27c2f467c4
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "creddy",
|
"name": "creddy",
|
||||||
"version": "0.5.4",
|
"version": "0.5.3",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
|
176
src-tauri/Cargo.lock
generated
176
src-tauri/Cargo.lock
generated
@ -110,55 +110,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstream"
|
|
||||||
version = "0.6.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
|
|
||||||
dependencies = [
|
|
||||||
"anstyle",
|
|
||||||
"anstyle-parse",
|
|
||||||
"anstyle-query",
|
|
||||||
"anstyle-wincon",
|
|
||||||
"colorchoice",
|
|
||||||
"is_terminal_polyfill",
|
|
||||||
"utf8parse",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstyle"
|
|
||||||
version = "1.0.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstyle-parse"
|
|
||||||
version = "0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
|
|
||||||
dependencies = [
|
|
||||||
"utf8parse",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstyle-query"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
|
|
||||||
dependencies = [
|
|
||||||
"windows-sys 0.52.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstyle-wincon"
|
|
||||||
version = "3.0.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
|
|
||||||
dependencies = [
|
|
||||||
"anstyle",
|
|
||||||
"windows-sys 0.52.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.86"
|
version = "1.0.86"
|
||||||
@ -376,6 +327,17 @@ version = "1.1.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi 0.1.19",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "auto-launch"
|
name = "auto-launch"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@ -1061,43 +1023,42 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.9"
|
version = "3.2.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
|
checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"atty",
|
||||||
|
"bitflags 1.3.2",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clap_builder"
|
|
||||||
version = "4.5.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
|
|
||||||
dependencies = [
|
|
||||||
"anstream",
|
|
||||||
"anstyle",
|
|
||||||
"clap_lex",
|
"clap_lex",
|
||||||
"strsim",
|
"indexmap 1.9.3",
|
||||||
|
"once_cell",
|
||||||
|
"strsim 0.10.0",
|
||||||
|
"termcolor",
|
||||||
|
"textwrap",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.8"
|
version = "3.2.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
|
checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.5.0",
|
"heck 0.4.1",
|
||||||
|
"proc-macro-error",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.68",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.7.1"
|
version = "0.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
|
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
||||||
|
dependencies = [
|
||||||
|
"os_str_bytes",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cocoa"
|
name = "cocoa"
|
||||||
@ -1129,12 +1090,6 @@ dependencies = [
|
|||||||
"objc",
|
"objc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "colorchoice"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "combine"
|
name = "combine"
|
||||||
version = "4.6.7"
|
version = "4.6.7"
|
||||||
@ -1241,7 +1196,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "creddy"
|
name = "creddy"
|
||||||
version = "0.5.4"
|
version = "0.5.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argon2",
|
"argon2",
|
||||||
"auto-launch",
|
"auto-launch",
|
||||||
@ -1249,8 +1204,9 @@ dependencies = [
|
|||||||
"aws-sdk-sts",
|
"aws-sdk-sts",
|
||||||
"aws-smithy-types",
|
"aws-smithy-types",
|
||||||
"aws-types",
|
"aws-types",
|
||||||
|
"base64 0.22.1",
|
||||||
"chacha20poly1305",
|
"chacha20poly1305",
|
||||||
"creddy_cli",
|
"clap",
|
||||||
"dirs 5.0.1",
|
"dirs 5.0.1",
|
||||||
"futures",
|
"futures",
|
||||||
"is-terminal",
|
"is-terminal",
|
||||||
@ -1285,18 +1241,6 @@ dependencies = [
|
|||||||
"windows 0.51.1",
|
"windows 0.51.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "creddy_cli"
|
|
||||||
version = "0.5.4"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"clap",
|
|
||||||
"dirs 5.0.1",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crossbeam-channel"
|
||||||
version = "0.5.13"
|
version = "0.5.13"
|
||||||
@ -1455,7 +1399,7 @@ dependencies = [
|
|||||||
"ident_case",
|
"ident_case",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"strsim",
|
"strsim 0.11.1",
|
||||||
"syn 2.0.68",
|
"syn 2.0.68",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2491,6 +2435,15 @@ version = "0.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
@ -2813,12 +2766,6 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "is_terminal_polyfill"
|
|
||||||
version = "1.70.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "0.4.8"
|
version = "0.4.8"
|
||||||
@ -3569,6 +3516,12 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "os_str_bytes"
|
||||||
|
version = "6.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "outref"
|
name = "outref"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
@ -4666,9 +4619,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.120"
|
version = "1.0.118"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
|
checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa 1.0.11",
|
"itoa 1.0.11",
|
||||||
"ryu",
|
"ryu",
|
||||||
@ -5285,6 +5238,12 @@ dependencies = [
|
|||||||
"unicode-properties",
|
"unicode-properties",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
@ -5772,6 +5731,21 @@ dependencies = [
|
|||||||
"utf-8",
|
"utf-8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termcolor"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.16.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thin-slice"
|
name = "thin-slice"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@ -6243,12 +6217,6 @@ version = "0.7.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "utf8parse"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.9.1"
|
version = "1.9.1"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "creddy"
|
name = "creddy"
|
||||||
version = "0.5.4"
|
version = "0.5.3"
|
||||||
description = "A friendly AWS credentials manager"
|
description = "A friendly AWS credentials manager"
|
||||||
authors = ["Joseph Montanaro"]
|
authors = ["Joseph Montanaro"]
|
||||||
license = ""
|
license = ""
|
||||||
@ -9,40 +9,37 @@ default-run = "creddy"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.57"
|
rust-version = "1.57"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "creddy_cli"
|
||||||
|
path = "src/bin/creddy_cli.rs"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "creddy"
|
name = "creddy"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
# we use a workspace so that we can split out the CLI and make it possible to build independently
|
|
||||||
[workspace]
|
|
||||||
members = ["creddy_cli"]
|
|
||||||
|
|
||||||
[workspace.dependencies]
|
|
||||||
dirs = "5.0"
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
|
||||||
serde_json = "1.0"
|
|
||||||
tokio = { version = ">=1.19", features = ["full"] }
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "2.0.0-beta", features = [] }
|
tauri-build = { version = "2.0.0-beta", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
creddy_cli = { path = "./creddy_cli" }
|
serde_json = "1.0"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
tauri = { version = "2.0.0-beta", features = ["tray-icon"] }
|
tauri = { version = "2.0.0-beta", features = ["tray-icon"] }
|
||||||
sodiumoxide = "0.2.7"
|
sodiumoxide = "0.2.7"
|
||||||
|
tokio = { version = ">=1.19", features = ["full"] }
|
||||||
sysinfo = "0.26.8"
|
sysinfo = "0.26.8"
|
||||||
aws-config = "1.5.3"
|
aws-config = "1.5.3"
|
||||||
aws-types = "1.3.2"
|
aws-types = "1.3.2"
|
||||||
aws-sdk-sts = "1.33.0"
|
aws-sdk-sts = "1.33.0"
|
||||||
aws-smithy-types = "1.2.0"
|
aws-smithy-types = "1.2.0"
|
||||||
dirs = { workspace = true }
|
|
||||||
thiserror = "1.0.38"
|
thiserror = "1.0.38"
|
||||||
once_cell = "1.16.0"
|
once_cell = "1.16.0"
|
||||||
strum = "0.24"
|
strum = "0.24"
|
||||||
strum_macros = "0.24"
|
strum_macros = "0.24"
|
||||||
auto-launch = "0.4.0"
|
auto-launch = "0.4.0"
|
||||||
|
dirs = "5.0"
|
||||||
|
clap = { version = "3.2.23", features = ["derive"] }
|
||||||
is-terminal = "0.4.7"
|
is-terminal = "0.4.7"
|
||||||
argon2 = { version = "0.5.0", features = ["std"] }
|
argon2 = { version = "0.5.0", features = ["std"] }
|
||||||
chacha20poly1305 = { version = "0.10.1", features = ["std"] }
|
chacha20poly1305 = { version = "0.10.1", features = ["std"] }
|
||||||
@ -58,10 +55,7 @@ ssh-agent-lib = "0.4.0"
|
|||||||
ssh-key = { version = "0.6.6", features = ["rsa", "ed25519", "encryption"] }
|
ssh-key = { version = "0.6.6", features = ["rsa", "ed25519", "encryption"] }
|
||||||
signature = "2.2.0"
|
signature = "2.2.0"
|
||||||
tokio-stream = "0.1.15"
|
tokio-stream = "0.1.15"
|
||||||
serde = { workspace = true }
|
|
||||||
serde_json = { workspace = true }
|
|
||||||
sqlx = { version = "0.7.4", features = ["sqlite", "runtime-tokio", "uuid"] }
|
sqlx = { version = "0.7.4", features = ["sqlite", "runtime-tokio", "uuid"] }
|
||||||
tokio = { workspace = true }
|
|
||||||
tokio-util = { version = "0.7.11", features = ["codec"] }
|
tokio-util = { version = "0.7.11", features = ["codec"] }
|
||||||
futures = "0.3.30"
|
futures = "0.3.30"
|
||||||
openssl = "0.10.64"
|
openssl = "0.10.64"
|
||||||
@ -77,5 +71,8 @@ default = ["custom-protocol"]
|
|||||||
# DO NOT remove this
|
# DO NOT remove this
|
||||||
custom-protocol = ["tauri/custom-protocol"]
|
custom-protocol = ["tauri/custom-protocol"]
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
base64 = "0.22.1"
|
||||||
|
|
||||||
# [profile.dev.build-override]
|
# [profile.dev.build-override]
|
||||||
# opt-level = 3
|
# opt-level = 3
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "creddy_cli"
|
|
||||||
version = "0.5.4"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
anyhow = "1.0.86"
|
|
||||||
clap = { version = "4", features = ["derive"] }
|
|
||||||
dirs = { workspace = true }
|
|
||||||
serde = { workspace = true }
|
|
||||||
serde_json = { workspace = true }
|
|
||||||
tokio = { workspace = true }
|
|
@ -1,208 +0,0 @@
|
|||||||
use std::path::PathBuf;
|
|
||||||
use std::process::Command as ChildCommand;
|
|
||||||
#[cfg(unix)]
|
|
||||||
use std::os::unix::process::CommandExt;
|
|
||||||
#[cfg(windows)]
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use anyhow::{bail, Context};
|
|
||||||
use clap::{
|
|
||||||
Args,
|
|
||||||
Parser,
|
|
||||||
Subcommand
|
|
||||||
};
|
|
||||||
use clap::builder::styling::{Styles, AnsiColor};
|
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
||||||
|
|
||||||
use crate::proto::{
|
|
||||||
CliCredential,
|
|
||||||
CliRequest,
|
|
||||||
CliResponse,
|
|
||||||
ServerError,
|
|
||||||
ShortcutAction,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
|
||||||
#[command(
|
|
||||||
about,
|
|
||||||
version,
|
|
||||||
name = "creddy",
|
|
||||||
bin_name = "creddy",
|
|
||||||
styles = Styles::styled()
|
|
||||||
.header(AnsiColor::Yellow.on_default())
|
|
||||||
.usage(AnsiColor::Yellow.on_default())
|
|
||||||
.literal(AnsiColor::Green.on_default())
|
|
||||||
.placeholder(AnsiColor::Green.on_default())
|
|
||||||
)]
|
|
||||||
/// A friendly credential manager
|
|
||||||
pub struct Cli {
|
|
||||||
#[command(flatten)]
|
|
||||||
pub global_args: GlobalArgs,
|
|
||||||
|
|
||||||
#[command(subcommand)]
|
|
||||||
pub action: Option<Action>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Cli {
|
|
||||||
// proxy the Parser method so that main crate doesn't have to depend on Clap
|
|
||||||
pub fn parse() -> Self {
|
|
||||||
<Self as Parser>::parse()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Args)]
|
|
||||||
pub struct GlobalArgs {
|
|
||||||
/// Connect to the main Creddy application at this path
|
|
||||||
#[arg(long, short = 'a')]
|
|
||||||
server_addr: Option<PathBuf>,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Subcommand)]
|
|
||||||
pub enum Action {
|
|
||||||
/// Launch Creddy
|
|
||||||
Run,
|
|
||||||
/// Request credentials from Creddy and output to stdout
|
|
||||||
Get(GetArgs),
|
|
||||||
/// Inject credentials into the environment of another command
|
|
||||||
Exec(ExecArgs),
|
|
||||||
/// Invoke an action normally triggered by hotkey (e.g. launch terminal)
|
|
||||||
Shortcut(InvokeArgs),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
|
||||||
pub struct GetArgs {
|
|
||||||
/// If unspecified, use default credentials
|
|
||||||
#[arg(short, long)]
|
|
||||||
name: Option<String>,
|
|
||||||
/// Use base credentials instead of session credentials (only applicable to AWS)
|
|
||||||
#[arg(long, short, default_value_t = false)]
|
|
||||||
base: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
|
||||||
pub struct ExecArgs {
|
|
||||||
#[command(flatten)]
|
|
||||||
get_args: GetArgs,
|
|
||||||
#[arg(trailing_var_arg = true)]
|
|
||||||
/// Command to be wrapped
|
|
||||||
command: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
|
||||||
pub struct InvokeArgs {
|
|
||||||
#[arg(value_name = "ACTION", value_enum)]
|
|
||||||
shortcut_action: ShortcutAction,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get(args: GetArgs, global: GlobalArgs) -> anyhow::Result<()> {
|
|
||||||
let req = CliRequest::GetCredential {
|
|
||||||
name: args.name,
|
|
||||||
base: args.base,
|
|
||||||
};
|
|
||||||
|
|
||||||
let output = match make_request(global.server_addr, &req)?? {
|
|
||||||
CliResponse::Credential(CliCredential::AwsBase(c)) => {
|
|
||||||
serde_json::to_string_pretty(&c).unwrap()
|
|
||||||
},
|
|
||||||
CliResponse::Credential(CliCredential::AwsSession(c)) => {
|
|
||||||
serde_json::to_string_pretty(&c).unwrap()
|
|
||||||
},
|
|
||||||
r => bail!("Unexpected response from server: {r}"),
|
|
||||||
};
|
|
||||||
println!("{output}");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub fn exec(args: ExecArgs, global: GlobalArgs) -> anyhow::Result<()> {
|
|
||||||
// Clap guarantees that cmd_line will be a sequence of at least 1 item
|
|
||||||
// test this!
|
|
||||||
let mut cmd_line = args.command.iter();
|
|
||||||
let cmd_name = cmd_line.next().unwrap();
|
|
||||||
let mut cmd = ChildCommand::new(cmd_name);
|
|
||||||
cmd.args(cmd_line);
|
|
||||||
|
|
||||||
let req = CliRequest::GetCredential {
|
|
||||||
name: args.get_args.name,
|
|
||||||
base: args.get_args.base,
|
|
||||||
};
|
|
||||||
|
|
||||||
match make_request(global.server_addr, &req)?? {
|
|
||||||
CliResponse::Credential(CliCredential::AwsBase(creds)) => {
|
|
||||||
cmd.env("AWS_ACCESS_KEY_ID", creds.access_key_id);
|
|
||||||
cmd.env("AWS_SECRET_ACCESS_KEY", creds.secret_access_key);
|
|
||||||
},
|
|
||||||
CliResponse::Credential(CliCredential::AwsSession(creds)) => {
|
|
||||||
cmd.env("AWS_ACCESS_KEY_ID", creds.access_key_id);
|
|
||||||
cmd.env("AWS_SECRET_ACCESS_KEY", creds.secret_access_key);
|
|
||||||
cmd.env("AWS_SESSION_TOKEN", creds.session_token);
|
|
||||||
},
|
|
||||||
r => bail!("Unexpected response from server: {r}"),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
{
|
|
||||||
let e = cmd.exec();
|
|
||||||
// cmd.exec() never returns if successful, so we never hit this line unless there's an error
|
|
||||||
Err(e).with_context(|| {
|
|
||||||
// eventually figure out how to display the actual command
|
|
||||||
format!("Failed to execute command: {}", args.command.join(" "))
|
|
||||||
})?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
{
|
|
||||||
let mut child = match cmd.spawn() {
|
|
||||||
Ok(c) => c,
|
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
|
||||||
let name: OsString = cmd_name.into();
|
|
||||||
return Err(ExecError::NotFound(name).into());
|
|
||||||
}
|
|
||||||
Err(e) => return Err(ExecError::ExecutionFailed(e).into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let status = child.wait()
|
|
||||||
.map_err(|e| ExecError::ExecutionFailed(e))?;
|
|
||||||
std::process::exit(status.code().unwrap_or(1));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub fn invoke_shortcut(args: InvokeArgs, global: GlobalArgs) -> anyhow::Result<()> {
|
|
||||||
let req = CliRequest::InvokeShortcut(args.shortcut_action);
|
|
||||||
match make_request(global.server_addr, &req)?? {
|
|
||||||
CliResponse::Empty => Ok(()),
|
|
||||||
r => bail!("Unexpected response from server: {r}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Explanation for double-result: the server will return a (serialized) Result
|
|
||||||
// to indicate when the operation succeeded or failed, which we deserialize.
|
|
||||||
// However, the operation may fail to even communicate with the server, in
|
|
||||||
// which case we return the outer Result
|
|
||||||
#[tokio::main]
|
|
||||||
async fn make_request(
|
|
||||||
addr: Option<PathBuf>,
|
|
||||||
req: &CliRequest
|
|
||||||
) -> anyhow::Result<Result<CliResponse, ServerError>> {
|
|
||||||
let mut data = serde_json::to_string(req).unwrap();
|
|
||||||
// server expects newline marking end of request
|
|
||||||
data.push('\n');
|
|
||||||
|
|
||||||
let mut stream = crate::connect(addr).await?;
|
|
||||||
stream.write_all(&data.as_bytes()).await?;
|
|
||||||
|
|
||||||
let mut buf = Vec::with_capacity(1024);
|
|
||||||
stream.read_to_end(&mut buf).await?;
|
|
||||||
let res: Result<CliResponse, ServerError> = serde_json::from_slice(&buf)?;
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
mod cli;
|
|
||||||
pub use cli::{
|
|
||||||
Cli,
|
|
||||||
Action,
|
|
||||||
exec,
|
|
||||||
get,
|
|
||||||
invoke_shortcut,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) use platform::connect;
|
|
||||||
pub use platform::server_addr;
|
|
||||||
|
|
||||||
mod proto;
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
mod platform {
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use tokio::net::UnixStream;
|
|
||||||
|
|
||||||
pub async fn connect(addr: Option<PathBuf>) -> Result<UnixStream, std::io::Error> {
|
|
||||||
let path = addr.unwrap_or_else(|| server_addr("creddy-server"));
|
|
||||||
UnixStream::connect(&path).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn server_addr(sock_name: &str) -> PathBuf {
|
|
||||||
let mut path = dirs::runtime_dir()
|
|
||||||
.unwrap_or_else(|| PathBuf::from("/tmp"));
|
|
||||||
path.push(format!("{sock_name}.sock"));
|
|
||||||
path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
mod platform {
|
|
||||||
pub fn server_addr(sock_name: &str) -> String {
|
|
||||||
format!(r"\\.\pipe\{sock_name}")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
use std::env;
|
|
||||||
use std::process::{self, Command};
|
|
||||||
|
|
||||||
use creddy_cli::{Action, Cli};
|
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let cli = Cli::parse();
|
|
||||||
let res = match cli.action {
|
|
||||||
None | Some(Action::Run)=> launch_gui(),
|
|
||||||
Some(Action::Get(args)) => creddy_cli::get(args, cli.global_args),
|
|
||||||
Some(Action::Exec(args)) => creddy_cli::exec(args, cli.global_args),
|
|
||||||
Some(Action::Shortcut(args)) => creddy_cli::invoke_shortcut(args, cli.global_args),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(e) = res {
|
|
||||||
eprintln!("Error: {e:?}");
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn launch_gui() -> anyhow::Result<()> {
|
|
||||||
let mut path = env::current_exe()?;
|
|
||||||
path.pop(); // bin dir
|
|
||||||
|
|
||||||
// binaries are colocated in dev, but not in production
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
path.pop(); // install dir
|
|
||||||
|
|
||||||
path.push("creddy.exe"); // exe in main install dir (aka gui exe)
|
|
||||||
|
|
||||||
Command::new(path).spawn()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
use std::fmt::{
|
|
||||||
Display,
|
|
||||||
Formatter,
|
|
||||||
Error as FmtError
|
|
||||||
};
|
|
||||||
|
|
||||||
use clap::ValueEnum;
|
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub enum CliRequest {
|
|
||||||
GetCredential {
|
|
||||||
name: Option<String>,
|
|
||||||
base: bool,
|
|
||||||
},
|
|
||||||
InvokeShortcut(ShortcutAction),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, ValueEnum)]
|
|
||||||
pub enum ShortcutAction {
|
|
||||||
ShowWindow,
|
|
||||||
LaunchTerminal,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub enum CliResponse {
|
|
||||||
Credential(CliCredential),
|
|
||||||
Empty,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for CliResponse {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
|
|
||||||
match self {
|
|
||||||
CliResponse::Credential(CliCredential::AwsBase(_)) => write!(f, "Credential (AwsBase)"),
|
|
||||||
CliResponse::Credential(CliCredential::AwsSession(_)) => write!(f, "Credential (AwsSession)"),
|
|
||||||
CliResponse::Empty => write!(f, "Empty"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub enum CliCredential {
|
|
||||||
AwsBase(AwsBaseCredential),
|
|
||||||
AwsSession(AwsSessionCredential),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "PascalCase")]
|
|
||||||
pub struct AwsBaseCredential {
|
|
||||||
#[serde(default = "default_aws_version")]
|
|
||||||
pub version: usize,
|
|
||||||
pub access_key_id: String,
|
|
||||||
pub secret_access_key: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "PascalCase")]
|
|
||||||
pub struct AwsSessionCredential {
|
|
||||||
#[serde(default = "default_aws_version")]
|
|
||||||
pub version: usize,
|
|
||||||
pub access_key_id: String,
|
|
||||||
pub secret_access_key: String,
|
|
||||||
pub session_token: String,
|
|
||||||
// we don't need to know the expiration for the CLI, so just use a string here
|
|
||||||
pub expiration: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn default_aws_version() -> usize { 1 }
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct ServerError {
|
|
||||||
code: String,
|
|
||||||
msg: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for ServerError {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
|
|
||||||
write!(f, "Error response ({}) from server: {}", self.code, self.msg)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for ServerError {}
|
|
42
src-tauri/src/bin/creddy_cli.rs
Normal file
42
src-tauri/src/bin/creddy_cli.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Windows isn't really amenable to having a single executable work as both a CLI and GUI app,
|
||||||
|
// so we just have a second binary for CLI usage
|
||||||
|
use creddy::{
|
||||||
|
cli,
|
||||||
|
errors::CliError,
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
env,
|
||||||
|
process::{self, Command},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let global_matches = cli::parser().get_matches();
|
||||||
|
let res = match global_matches.subcommand() {
|
||||||
|
None | Some(("run", _)) => launch_gui(),
|
||||||
|
Some(("get", m)) => cli::get(m, &global_matches),
|
||||||
|
Some(("exec", m)) => cli::exec(m, &global_matches),
|
||||||
|
Some(("shortcut", m)) => cli::invoke_shortcut(m, &global_matches),
|
||||||
|
_ => unreachable!("Unknown subcommand"),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = res {
|
||||||
|
eprintln!("Error: {e}");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn launch_gui() -> Result<(), CliError> {
|
||||||
|
let mut path = env::current_exe()?;
|
||||||
|
path.pop(); // bin dir
|
||||||
|
|
||||||
|
// binaries are colocated in dev, but not in production
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
path.pop(); // install dir
|
||||||
|
|
||||||
|
path.push("creddy.exe"); // exe in main install dir (aka gui exe)
|
||||||
|
|
||||||
|
Command::new(path).spawn()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
227
src-tauri/src/cli.rs
Normal file
227
src-tauri/src/cli.rs
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
use std::ffi::OsString;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process::Command as ChildCommand;
|
||||||
|
#[cfg(windows)]
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use clap::{
|
||||||
|
Command,
|
||||||
|
Arg,
|
||||||
|
ArgMatches,
|
||||||
|
ArgAction,
|
||||||
|
builder::PossibleValuesParser,
|
||||||
|
value_parser,
|
||||||
|
};
|
||||||
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
|
|
||||||
|
use crate::errors::*;
|
||||||
|
use crate::srv::{
|
||||||
|
self,
|
||||||
|
Request,
|
||||||
|
Response
|
||||||
|
};
|
||||||
|
use crate::shortcuts::ShortcutAction;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
use {
|
||||||
|
std::os::unix::process::CommandExt,
|
||||||
|
tokio::net::UnixStream,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
use {
|
||||||
|
tokio::net::windows::named_pipe::{NamedPipeClient, ClientOptions},
|
||||||
|
windows::Win32::Foundation::ERROR_PIPE_BUSY,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
pub fn parser() -> Command<'static> {
|
||||||
|
Command::new("creddy")
|
||||||
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
|
.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(
|
||||||
|
Command::new("run")
|
||||||
|
.about("Launch Creddy")
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
Command::new("get")
|
||||||
|
.about("Request AWS credentials from Creddy and output to stdout")
|
||||||
|
.arg(
|
||||||
|
Arg::new("base")
|
||||||
|
.short('b')
|
||||||
|
.long("base")
|
||||||
|
.action(ArgAction::SetTrue)
|
||||||
|
.help("Use base credentials instead of session credentials")
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("name")
|
||||||
|
.help("If unspecified, use default credentials")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
Command::new("exec")
|
||||||
|
.about("Inject AWS credentials into the environment of another command")
|
||||||
|
.trailing_var_arg(true)
|
||||||
|
.arg(
|
||||||
|
Arg::new("base")
|
||||||
|
.short('b')
|
||||||
|
.long("base")
|
||||||
|
.action(ArgAction::SetTrue)
|
||||||
|
.help("Use base credentials instead of session credentials")
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("name")
|
||||||
|
.short('n')
|
||||||
|
.long("name")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("If unspecified, use default credentials")
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("command")
|
||||||
|
.multiple_values(true)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
Command::new("shortcut")
|
||||||
|
.about("Invoke an action normally trigged by hotkey (e.g. launch terminal)")
|
||||||
|
.arg(
|
||||||
|
Arg::new("action")
|
||||||
|
.value_parser(
|
||||||
|
PossibleValuesParser::new(["show_window", "launch_terminal"])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn get(args: &ArgMatches, global_args: &ArgMatches) -> Result<(), CliError> {
|
||||||
|
let name = args.get_one("name").cloned();
|
||||||
|
let base = *args.get_one("base").unwrap_or(&false);
|
||||||
|
let addr = global_args.get_one("server_addr").cloned();
|
||||||
|
|
||||||
|
let output = match make_request(addr, &Request::GetAwsCredentials { name, base })? {
|
||||||
|
Response::AwsBase(creds) => serde_json::to_string(&creds).unwrap(),
|
||||||
|
Response::AwsSession(creds) => serde_json::to_string(&creds).unwrap(),
|
||||||
|
r => return Err(RequestError::Unexpected(r).into()),
|
||||||
|
};
|
||||||
|
println!("{output}");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn exec(args: &ArgMatches, global_args: &ArgMatches) -> Result<(), CliError> {
|
||||||
|
let name = args.get_one("name").cloned();
|
||||||
|
let base = *args.get_one("base").unwrap_or(&false);
|
||||||
|
let addr = global_args.get_one("server_addr").cloned();
|
||||||
|
let mut cmd_line = args.get_many("command")
|
||||||
|
.ok_or(ExecError::NoCommand)?;
|
||||||
|
|
||||||
|
let cmd_name: &String = cmd_line.next().unwrap(); // Clap guarantees that there will be at least one
|
||||||
|
let mut cmd = ChildCommand::new(cmd_name);
|
||||||
|
cmd.args(cmd_line);
|
||||||
|
|
||||||
|
match make_request(addr, &Request::GetAwsCredentials { name, base })? {
|
||||||
|
Response::AwsBase(creds) => {
|
||||||
|
cmd.env("AWS_ACCESS_KEY_ID", creds.access_key_id);
|
||||||
|
cmd.env("AWS_SECRET_ACCESS_KEY", creds.secret_access_key);
|
||||||
|
},
|
||||||
|
Response::AwsSession(creds) => {
|
||||||
|
cmd.env("AWS_ACCESS_KEY_ID", creds.access_key_id);
|
||||||
|
cmd.env("AWS_SECRET_ACCESS_KEY", creds.secret_access_key);
|
||||||
|
cmd.env("AWS_SESSION_TOKEN", creds.session_token);
|
||||||
|
},
|
||||||
|
r => return Err(RequestError::Unexpected(r).into()),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
// cmd.exec() never returns if successful
|
||||||
|
let e = cmd.exec();
|
||||||
|
match e.kind() {
|
||||||
|
std::io::ErrorKind::NotFound => {
|
||||||
|
let name: OsString = cmd_name.into();
|
||||||
|
Err(ExecError::NotFound(name).into())
|
||||||
|
}
|
||||||
|
_ => Err(ExecError::ExecutionFailed(e).into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
let mut child = match cmd.spawn() {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||||
|
let name: OsString = cmd_name.into();
|
||||||
|
return Err(ExecError::NotFound(name).into());
|
||||||
|
}
|
||||||
|
Err(e) => return Err(ExecError::ExecutionFailed(e).into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let status = child.wait()
|
||||||
|
.map_err(|e| ExecError::ExecutionFailed(e))?;
|
||||||
|
std::process::exit(status.code().unwrap_or(1));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn invoke_shortcut(args: &ArgMatches, global_args: &ArgMatches) -> Result<(), CliError> {
|
||||||
|
let addr = global_args.get_one("server_addr").cloned();
|
||||||
|
let action = match args.get_one::<String>("action").map(|s| s.as_str()) {
|
||||||
|
Some("show_window") => ShortcutAction::ShowWindow,
|
||||||
|
Some("launch_terminal") => ShortcutAction::LaunchTerminal,
|
||||||
|
Some(&_) | None => unreachable!("Unknown shortcut action"), // guaranteed by clap
|
||||||
|
};
|
||||||
|
|
||||||
|
let req = Request::InvokeShortcut(action);
|
||||||
|
match make_request(addr, &req) {
|
||||||
|
Ok(Response::Empty) => Ok(()),
|
||||||
|
Ok(r) => Err(RequestError::Unexpected(r).into()),
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn make_request(addr: Option<PathBuf>, req: &Request) -> Result<Response, RequestError> {
|
||||||
|
let mut data = serde_json::to_string(req).unwrap();
|
||||||
|
// server expects newline marking end of request
|
||||||
|
data.push('\n');
|
||||||
|
|
||||||
|
let mut stream = connect(addr).await?;
|
||||||
|
stream.write_all(&data.as_bytes()).await?;
|
||||||
|
|
||||||
|
let mut buf = Vec::with_capacity(1024);
|
||||||
|
stream.read_to_end(&mut buf).await?;
|
||||||
|
let res: Result<Response, ServerError> = serde_json::from_slice(&buf)?;
|
||||||
|
Ok(res?)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
async fn connect(addr: Option<PathBuf>) -> Result<NamedPipeClient, std::io::Error> {
|
||||||
|
// apparently attempting to connect can fail if there's already a client connected
|
||||||
|
loop {
|
||||||
|
let addr = addr.unwrap_or_else(|| srv::addr("creddy-server"));
|
||||||
|
match ClientOptions::new().open(&addr) {
|
||||||
|
Ok(stream) => return Ok(stream),
|
||||||
|
Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY.0 as i32) => (),
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
async fn connect(addr: Option<PathBuf>) -> Result<UnixStream, std::io::Error> {
|
||||||
|
let path = addr.unwrap_or_else(|| srv::addr("creddy-server"));
|
||||||
|
UnixStream::connect(&path).await
|
||||||
|
}
|
@ -370,7 +370,7 @@ pub enum RequestError {
|
|||||||
#[error("Error response from server: {0}")]
|
#[error("Error response from server: {0}")]
|
||||||
Server(ServerError),
|
Server(ServerError),
|
||||||
#[error("Unexpected response from server")]
|
#[error("Unexpected response from server")]
|
||||||
Unexpected(crate::srv::CliResponse),
|
Unexpected(crate::srv::Response),
|
||||||
#[error("The server did not respond with valid JSON")]
|
#[error("The server did not respond with valid JSON")]
|
||||||
InvalidJson(#[from] serde_json::Error),
|
InvalidJson(#[from] serde_json::Error),
|
||||||
#[error("Error reading/writing stream: {0}")]
|
#[error("Error reading/writing stream: {0}")]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
pub mod app;
|
pub mod app;
|
||||||
|
pub mod cli;
|
||||||
mod config;
|
mod config;
|
||||||
mod credentials;
|
mod credentials;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
|
@ -3,24 +3,24 @@
|
|||||||
windows_subsystem = "windows"
|
windows_subsystem = "windows"
|
||||||
)]
|
)]
|
||||||
|
|
||||||
|
|
||||||
use creddy::{
|
use creddy::{
|
||||||
app,
|
app,
|
||||||
|
cli,
|
||||||
errors::ShowError,
|
errors::ShowError,
|
||||||
};
|
};
|
||||||
use creddy_cli::{Action, Cli};
|
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let cli = Cli::parse();
|
let global_matches = cli::parser().get_matches();
|
||||||
let res = match cli.action {
|
let res = match global_matches.subcommand() {
|
||||||
None | Some(Action::Run) => {
|
None | Some(("run", _)) => {
|
||||||
app::run().error_popup("Creddy encountered an error");
|
app::run().error_popup("Creddy encountered an error");
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
Some(Action::Get(args)) => creddy_cli::get(args, cli.global_args),
|
Some(("get", m)) => cli::get(m, &global_matches),
|
||||||
Some(Action::Exec(args)) => creddy_cli::exec(args, cli.global_args),
|
Some(("exec", m)) => cli::exec(m, &global_matches),
|
||||||
Some(Action::Shortcut(args)) => creddy_cli::invoke_shortcut(args, cli.global_args),
|
Some(("shortcut", m)) => cli::invoke_shortcut(m, &global_matches),
|
||||||
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
|
@ -9,9 +9,8 @@ use crate::shortcuts::{self, ShortcutAction};
|
|||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
use super::{
|
use super::{
|
||||||
CloseWaiter,
|
CloseWaiter,
|
||||||
CliCredential,
|
Request,
|
||||||
CliRequest,
|
Response,
|
||||||
CliResponse,
|
|
||||||
Stream,
|
Stream,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -44,12 +43,13 @@ async fn handle(
|
|||||||
let waiter = CloseWaiter { stream: &mut stream };
|
let waiter = CloseWaiter { stream: &mut stream };
|
||||||
|
|
||||||
|
|
||||||
let req: CliRequest = serde_json::from_slice(&buf)?;
|
let req: Request = serde_json::from_slice(&buf)?;
|
||||||
let res = match req {
|
let res = match req {
|
||||||
CliRequest::GetCredential{ name, base } => get_aws_credentials(
|
Request::GetAwsCredentials { name, base } => get_aws_credentials(
|
||||||
name, base, client, app_handle, waiter
|
name, base, client, app_handle, waiter
|
||||||
).await,
|
).await,
|
||||||
CliRequest::InvokeShortcut(action) => invoke_shortcut(action).await,
|
Request::InvokeShortcut(action) => invoke_shortcut(action).await,
|
||||||
|
Request::GetSshSignature(_) => return Err(HandlerError::Denied),
|
||||||
};
|
};
|
||||||
|
|
||||||
// doesn't make sense to send the error to the client if the client has already left
|
// doesn't make sense to send the error to the client if the client has already left
|
||||||
@ -63,9 +63,9 @@ async fn handle(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async fn invoke_shortcut(action: ShortcutAction) -> Result<CliResponse, HandlerError> {
|
async fn invoke_shortcut(action: ShortcutAction) -> Result<Response, HandlerError> {
|
||||||
shortcuts::exec_shortcut(action);
|
shortcuts::exec_shortcut(action);
|
||||||
Ok(CliResponse::Empty)
|
Ok(Response::Empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ async fn get_aws_credentials(
|
|||||||
client: Client,
|
client: Client,
|
||||||
app_handle: AppHandle,
|
app_handle: AppHandle,
|
||||||
mut waiter: CloseWaiter<'_>,
|
mut waiter: CloseWaiter<'_>,
|
||||||
) -> Result<CliResponse, HandlerError> {
|
) -> Result<Response, HandlerError> {
|
||||||
let state = app_handle.state::<AppState>();
|
let state = app_handle.state::<AppState>();
|
||||||
let rehide_ms = {
|
let rehide_ms = {
|
||||||
let config = state.config.read().await;
|
let config = state.config.read().await;
|
||||||
@ -108,11 +108,11 @@ async fn get_aws_credentials(
|
|||||||
Approval::Approved => {
|
Approval::Approved => {
|
||||||
if response.base {
|
if response.base {
|
||||||
let creds = state.get_aws_base(name).await?;
|
let creds = state.get_aws_base(name).await?;
|
||||||
Ok(CliResponse::Credential(CliCredential::AwsBase(creds)))
|
Ok(Response::AwsBase(creds))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let creds = state.get_aws_session(name).await?.clone();
|
let creds = state.get_aws_session(name).await?;
|
||||||
Ok(CliResponse::Credential(CliCredential::AwsSession(creds)))
|
Ok(Response::AwsSession(creds.clone()))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Approval::Denied => Err(HandlerError::Denied),
|
Approval::Denied => Err(HandlerError::Denied),
|
||||||
|
@ -6,6 +6,7 @@ use tauri::{
|
|||||||
};
|
};
|
||||||
use tokio::io::AsyncReadExt;
|
use tokio::io::AsyncReadExt;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
use ssh_agent_lib::proto::message::SignRequest;
|
||||||
|
|
||||||
use crate::credentials::{AwsBaseCredential, AwsSessionCredential};
|
use crate::credentials::{AwsBaseCredential, AwsSessionCredential};
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
@ -14,32 +15,25 @@ use crate::shortcuts::ShortcutAction;
|
|||||||
pub mod creddy_server;
|
pub mod creddy_server;
|
||||||
pub mod agent;
|
pub mod agent;
|
||||||
use platform::Stream;
|
use platform::Stream;
|
||||||
|
pub use platform::addr;
|
||||||
|
|
||||||
|
|
||||||
// These types match what's defined in creddy_cli, but they are separate types
|
|
||||||
// so that we avoid polluting the standalone CLI with a bunch of dependencies
|
|
||||||
// that would make it impossible to build a completely static-linked version
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub enum CliRequest {
|
pub enum Request {
|
||||||
GetCredential {
|
GetAwsCredentials {
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
base: bool,
|
base: bool,
|
||||||
},
|
},
|
||||||
|
GetSshSignature(SignRequest),
|
||||||
InvokeShortcut(ShortcutAction),
|
InvokeShortcut(ShortcutAction),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub enum CliResponse {
|
pub enum Response {
|
||||||
Credential(CliCredential),
|
|
||||||
Empty,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub enum CliCredential {
|
|
||||||
AwsBase(AwsBaseCredential),
|
AwsBase(AwsBaseCredential),
|
||||||
AwsSession(AwsSessionCredential),
|
AwsSession(AwsSessionCredential),
|
||||||
|
Empty,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -98,7 +92,7 @@ mod platform {
|
|||||||
pub type Stream = UnixStream;
|
pub type Stream = UnixStream;
|
||||||
|
|
||||||
pub fn bind(sock_name: &str) -> std::io::Result<(UnixListener, PathBuf)> {
|
pub fn bind(sock_name: &str) -> std::io::Result<(UnixListener, PathBuf)> {
|
||||||
let path = creddy_cli::server_addr(sock_name);
|
let path = addr(sock_name);
|
||||||
match std::fs::remove_file(&path) {
|
match std::fs::remove_file(&path) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(e) if e.kind() == ErrorKind::NotFound => (),
|
Err(e) if e.kind() == ErrorKind::NotFound => (),
|
||||||
@ -118,6 +112,14 @@ mod platform {
|
|||||||
|
|
||||||
Ok((stream, pid))
|
Ok((stream, pid))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn addr(sock_name: &str) -> PathBuf {
|
||||||
|
let mut path = dirs::runtime_dir()
|
||||||
|
.unwrap_or_else(|| PathBuf::from("/tmp"));
|
||||||
|
path.push(format!("{sock_name}.sock"));
|
||||||
|
path
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -138,7 +140,7 @@ mod platform {
|
|||||||
pub type Stream = NamedPipeServer;
|
pub type Stream = NamedPipeServer;
|
||||||
|
|
||||||
pub fn bind(sock_name: &str) -> std::io::Result<(String, NamedPipeServer)> {
|
pub fn bind(sock_name: &str) -> std::io::Result<(String, NamedPipeServer)> {
|
||||||
let addr = creddy_cli::server_addr(sock_name);
|
let addr = addr(sock_name);
|
||||||
let listener = ServerOptions::new()
|
let listener = ServerOptions::new()
|
||||||
.first_pipe_instance(true)
|
.first_pipe_instance(true)
|
||||||
.create(&addr)?;
|
.create(&addr)?;
|
||||||
@ -161,4 +163,8 @@ mod platform {
|
|||||||
unsafe { GetNamedPipeClientProcessId(handle, &mut pid as *mut u32)? };
|
unsafe { GetNamedPipeClientProcessId(handle, &mut pid as *mut u32)? };
|
||||||
Ok((stream, pid))
|
Ok((stream, pid))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn addr(sock_name: &str) -> String {
|
||||||
|
format!(r"\\.\pipe\{sock_name}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"productName": "creddy",
|
"productName": "creddy",
|
||||||
"version": "0.5.4",
|
"version": "0.5.3",
|
||||||
"identifier": "creddy",
|
"identifier": "creddy",
|
||||||
"plugins": {},
|
"plugins": {},
|
||||||
"app": {
|
"app": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user