Add keyring template function

This commit is contained in:
Gregory Schier
2025-09-29 08:56:24 -07:00
parent b3d6d87bee
commit 0b0b05d29c
12 changed files with 73 additions and 134 deletions

126
src-tauri/Cargo.lock generated
View File

@@ -503,15 +503,6 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "block-padding"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
dependencies = [
"generic-array",
]
[[package]] [[package]]
name = "block2" name = "block2"
version = "0.5.1" version = "0.5.1"
@@ -739,15 +730,6 @@ dependencies = [
"toml 0.8.23", "toml 0.8.23",
] ]
[[package]]
name = "cbc"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
dependencies = [
"cipher",
]
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.26" version = "1.2.26"
@@ -1220,31 +1202,23 @@ checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
[[package]] [[package]]
name = "dbus" name = "dbus"
version = "0.9.7" version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" checksum = "190b6255e8ab55a7b568df5a883e9497edc3e4821c06396612048b430e5ad1e9"
dependencies = [ dependencies = [
"libc", "libc",
"libdbus-sys", "libdbus-sys",
"winapi", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
name = "dbus-secret-service" name = "dbus-secret-service"
version = "4.0.3" version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42a16374481d92aed73ae45b1f120207d8e71d24fb89f357fadbd8f946fd84b" checksum = "708b509edf7889e53d7efb0ffadd994cc6c2345ccb62f55cfd6b0682165e4fa6"
dependencies = [ dependencies = [
"aes",
"block-padding",
"cbc",
"dbus", "dbus",
"futures-util", "zeroize",
"hkdf",
"num",
"once_cell",
"rand 0.8.5",
"sha2",
] ]
[[package]] [[package]]
@@ -2273,15 +2247,6 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hkdf"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
dependencies = [
"hmac",
]
[[package]] [[package]]
name = "hmac" name = "hmac"
version = "0.12.1" version = "0.12.1"
@@ -2695,7 +2660,6 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [ dependencies = [
"block-padding",
"generic-array", "generic-array",
] ]
@@ -2855,15 +2819,17 @@ dependencies = [
[[package]] [[package]]
name = "keyring" name = "keyring"
version = "4.0.0-rc.1" version = "3.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb06f73ca0ea1cbd3858e54404585e33dccb860cb4fc8a66ad5e75a5736f3f19" checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"dbus-secret-service", "dbus-secret-service",
"log", "log",
"security-framework 2.11.1",
"security-framework 3.2.0", "security-framework 3.2.0",
"windows-sys 0.59.0", "windows-sys 0.60.2",
"zeroize",
] ]
[[package]] [[package]]
@@ -2936,9 +2902,9 @@ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]] [[package]]
name = "libdbus-sys" name = "libdbus-sys"
version = "0.2.5" version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" checksum = "5cbe856efeb50e4681f010e9aaa2bf0a644e10139e54cde10fc83a307c23bd9f"
dependencies = [ dependencies = [
"pkg-config", "pkg-config",
] ]
@@ -3359,76 +3325,12 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d"
[[package]]
name = "num"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "num-conv" name = "num-conv"
version = "0.1.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
@@ -7939,7 +7841,6 @@ dependencies = [
"charset", "charset",
"chrono", "chrono",
"cookie", "cookie",
"encoding_rs",
"eventsource-client", "eventsource-client",
"http", "http",
"log", "log",
@@ -8142,6 +8043,7 @@ dependencies = [
"dunce", "dunce",
"futures-util", "futures-util",
"hex", "hex",
"keyring",
"log", "log",
"md5 0.7.0", "md5 0.7.0",
"path-slash", "path-slash",

View File

@@ -42,7 +42,6 @@ openssl-sys = { version = "0.9.105", features = ["vendored"] } # For Ubuntu inst
[dependencies] [dependencies]
chrono = { workspace = true, features = ["serde"] } chrono = { workspace = true, features = ["serde"] }
cookie = "0.18.1" cookie = "0.18.1"
encoding_rs = "0.8.35"
eventsource-client = { git = "https://github.com/yaakapp/rust-eventsource-client", version = "0.14.0" } eventsource-client = { git = "https://github.com/yaakapp/rust-eventsource-client", version = "0.14.0" }
http = { version = "1.2.0", default-features = false } http = { version = "1.2.0", default-features = false }
log = "0.4.27" log = "0.4.27"
@@ -88,6 +87,7 @@ charset = "0.1.5"
[workspace.dependencies] [workspace.dependencies]
chrono = "0.4.41" chrono = "0.4.41"
hex = "0.4.3" hex = "0.4.3"
keyring = { version = "3.6.3", features = ["apple-native", "windows-native", "sync-secret-service"] }
reqwest = "0.12.20" reqwest = "0.12.20"
serde = "1.0.219" serde = "1.0.219"
serde_json = "1.0.140" serde_json = "1.0.140"

View File

@@ -9,7 +9,7 @@ publish = false
base32 = "0.5.1" # For encoding human-readable key base32 = "0.5.1" # For encoding human-readable key
base64 = "0.22.1" # For encoding in the database base64 = "0.22.1" # For encoding in the database
chacha20poly1305 = "0.10.1" chacha20poly1305 = "0.10.1"
keyring = { version = "4.0.0-rc.1" } keyring = { workspace = true }
log = "0.4.26" log = "0.4.26"
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
tauri = { workspace = true } tauri = { workspace = true }

View File

@@ -29,9 +29,6 @@ pub enum Error {
#[error("Multiple base environments for {0}. Delete duplicates before continuing.")] #[error("Multiple base environments for {0}. Delete duplicates before continuing.")]
MultipleBaseEnvironments(String), MultipleBaseEnvironments(String),
#[error("Multiple folder environments for {0}. Delete duplicates before continuing.")]
MultipleFolderEnvironments(String),
#[error("unknown error")] #[error("unknown error")]
Unknown, Unknown,

View File

@@ -1,7 +1,5 @@
use crate::db_context::DbContext; use crate::db_context::DbContext;
use crate::error::Error::{ use crate::error::Error::{MissingBaseEnvironment, MultipleBaseEnvironments};
MissingBaseEnvironment, MultipleBaseEnvironments, MultipleFolderEnvironments,
};
use crate::error::Result; use crate::error::Result;
use crate::models::{Environment, EnvironmentIden, EnvironmentVariable}; use crate::models::{Environment, EnvironmentIden, EnvironmentVariable};
use crate::util::UpdateSource; use crate::util::UpdateSource;

View File

@@ -31,6 +31,7 @@ yaak-templates = { workspace = true }
zip-extract = "0.4.0" zip-extract = "0.4.0"
chrono = { workspace = true } chrono = { workspace = true }
hex = { workspace = true } hex = { workspace = true }
keyring = { workspace = true }
[build-dependencies] [build-dependencies]
tauri-plugin = { workspace = true, features = ["build"] } tauri-plugin = { workspace = true, features = ["build"] }

View File

@@ -32,7 +32,7 @@ pub enum Error {
#[error("JSON error: {0}")] #[error("JSON error: {0}")]
JsonErr(#[from] serde_json::Error), JsonErr(#[from] serde_json::Error),
#[error("API Error: {0}")] #[error("API Error: {0}")]
ApiErr(String), ApiErr(String),

View File

@@ -1,5 +1,4 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap; use std::collections::HashMap;
use tauri::{Runtime, WebviewWindow}; use tauri::{Runtime, WebviewWindow};
use ts_rs::TS; use ts_rs::TS;
@@ -163,7 +162,7 @@ pub enum InternalEventPayload {
impl InternalEventPayload { impl InternalEventPayload {
pub fn type_name(&self) -> String { pub fn type_name(&self) -> String {
if let Ok(Value::Object(map)) = serde_json::to_value(self) { if let Ok(serde_json::Value::Object(map)) = serde_json::to_value(self) {
map.get("type").map(|s| s.as_str().unwrap_or("unknown").to_string()) map.get("type").map(|s| s.as_str().unwrap_or("unknown").to_string())
} else { } else {
None None

View File

@@ -14,7 +14,7 @@ use crate::events::{
ImportResponse, InternalEvent, InternalEventPayload, JsonPrimitive, PluginWindowContext, ImportResponse, InternalEvent, InternalEventPayload, JsonPrimitive, PluginWindowContext,
RenderPurpose, RenderPurpose,
}; };
use crate::native_template_functions::template_function_secure; use crate::native_template_functions::{template_function_keyring, template_function_secure};
use crate::nodejs::start_nodejs_plugin_runtime; use crate::nodejs::start_nodejs_plugin_runtime;
use crate::plugin_handle::PluginHandle; use crate::plugin_handle::PluginHandle;
use crate::server_ws::PluginRuntimeServerWebsocket; use crate::server_ws::PluginRuntimeServerWebsocket;
@@ -514,7 +514,7 @@ impl PluginManager {
// Add Rust-based functions // Add Rust-based functions
result.push(GetTemplateFunctionsResponse { result.push(GetTemplateFunctionsResponse {
plugin_ref_id: "__NATIVE__".to_string(), // Meh plugin_ref_id: "__NATIVE__".to_string(), // Meh
functions: vec![template_function_secure()], functions: vec![template_function_secure(), template_function_keyring()],
}); });
Ok(result) Ok(result)

View File

@@ -6,6 +6,8 @@ use crate::template_callback::PluginTemplateCallback;
use base64::Engine; use base64::Engine;
use base64::prelude::BASE64_STANDARD; use base64::prelude::BASE64_STANDARD;
use std::collections::HashMap; use std::collections::HashMap;
use keyring::Error::NoEntry;
use log::debug;
use tauri::{AppHandle, Runtime}; use tauri::{AppHandle, Runtime};
use yaak_crypto::manager::EncryptionManagerExt; use yaak_crypto::manager::EncryptionManagerExt;
use yaak_templates::error::Error::RenderError; use yaak_templates::error::Error::RenderError;
@@ -32,6 +34,34 @@ pub(crate) fn template_function_secure() -> TemplateFunction {
} }
} }
pub(crate) fn template_function_keyring() -> TemplateFunction {
TemplateFunction {
name: "keyring".to_string(),
description: Some("Get a password from the OS keychain/keyring".to_string()),
aliases: None,
args: vec![
TemplateFunctionArg::FormInput(FormInput::Text(FormInputText {
base: FormInputBase {
name: "service".to_string(),
label: Some("Service".to_string()),
description: Some("App or URL for the password".to_string()),
..Default::default()
},
..Default::default()
})),
TemplateFunctionArg::FormInput(FormInput::Text(FormInputText {
base: FormInputBase {
name: "account".to_string(),
label: Some("Account".to_string()),
description: Some("Username or email address".to_string()),
..Default::default()
},
..Default::default()
})),
],
}
}
pub fn template_function_secure_run<R: Runtime>( pub fn template_function_secure_run<R: Runtime>(
app_handle: &AppHandle<R>, app_handle: &AppHandle<R>,
args: HashMap<String, serde_json::Value>, args: HashMap<String, serde_json::Value>,
@@ -163,3 +193,15 @@ pub fn encrypt_secure_template_function<R: Runtime>(
)? )?
.to_string()) .to_string())
} }
pub fn template_function_keychain_run(args: HashMap<String, serde_json::Value>) -> Result<String> {
let service = args.get("service").and_then(|v| v.as_str()).unwrap_or_default().to_owned();
let user = args.get("account").and_then(|v| v.as_str()).unwrap_or_default().to_owned();
debug!("Getting password for service {} and user {}", service, user);
let entry = keyring::Entry::new(&service, &user).map_err(|e| RenderError(e.to_string()))?;
match entry.get_password() {
Ok(p) => Ok(p),
Err(NoEntry) => Err(RenderError(format!("No password found for '{}' and '{}'", service, user))),
Err(e) => Err(RenderError(e.to_string())),
}
}

View File

@@ -1,12 +1,13 @@
use crate::events::{PluginWindowContext, RenderPurpose}; use crate::events::{PluginWindowContext, RenderPurpose};
use crate::manager::PluginManager; use crate::manager::PluginManager;
use crate::native_template_functions::{ use crate::native_template_functions::{
template_function_secure_run, template_function_secure_transform_arg, template_function_keychain_run, template_function_secure_run,
template_function_secure_transform_arg,
}; };
use std::collections::HashMap; use std::collections::HashMap;
use tauri::{AppHandle, Manager, Runtime}; use tauri::{AppHandle, Manager, Runtime};
use yaak_templates::error::Result;
use yaak_templates::TemplateCallback; use yaak_templates::TemplateCallback;
use yaak_templates::error::Result;
#[derive(Clone)] #[derive(Clone)]
pub struct PluginTemplateCallback<R: Runtime> { pub struct PluginTemplateCallback<R: Runtime> {
@@ -37,6 +38,8 @@ impl<R: Runtime> TemplateCallback for PluginTemplateCallback<R> {
if fn_name == "secure" { if fn_name == "secure" {
return template_function_secure_run(&self.app_handle, args, &self.window_context); return template_function_secure_run(&self.app_handle, args, &self.window_context);
} else if fn_name == "keyring" {
return template_function_keychain_run(args);
} }
let plugin_manager = &*self.app_handle.state::<PluginManager>(); let plugin_manager = &*self.app_handle.state::<PluginManager>();
@@ -51,12 +54,7 @@ impl<R: Runtime> TemplateCallback for PluginTemplateCallback<R> {
Ok(resp) Ok(resp)
} }
fn transform_arg( fn transform_arg(&self, fn_name: &str, arg_name: &str, arg_value: &str) -> Result<String> {
&self,
fn_name: &str,
arg_name: &str,
arg_value: &str,
) -> Result<String> {
if fn_name == "secure" { if fn_name == "secure" {
return template_function_secure_transform_arg( return template_function_secure_transform_arg(
&self.app_handle, &self.app_handle,

View File

@@ -47,7 +47,9 @@ export function HttpAuthenticationEditor({ model }: Props) {
if (model.authenticationType != null && authConfig.data == null) { if (model.authenticationType != null && authConfig.data == null) {
return ( return (
<EmptyStateText> <EmptyStateText>
Unknown authentication <InlineCode>{authConfig.data}</InlineCode> <p>
Auth plugin not found for <InlineCode>{model.authenticationType}</InlineCode>
</p>
</EmptyStateText> </EmptyStateText>
); );
} }