mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
Fix keyring backend env override (#3423)
This commit is contained in:
+1
-1
@@ -191,7 +191,7 @@ fn run_sentry_test_command(args: &[String]) -> Result<()> {
|
||||
/// 1. Variables already set in the process environment are **not** overwritten.
|
||||
/// 2. If `OPENHUMAN_DOTENV_PATH` is set, that file is loaded.
|
||||
/// 3. Otherwise, it searches for `.env` in the current working directory.
|
||||
fn load_dotenv_for_cli() -> Result<()> {
|
||||
pub(crate) fn load_dotenv_for_cli() -> Result<()> {
|
||||
match std::env::var("OPENHUMAN_DOTENV_PATH") {
|
||||
Ok(path) if !path.trim().is_empty() => {
|
||||
dotenvy::from_path(&path).map_err(|e| {
|
||||
|
||||
@@ -26,6 +26,7 @@ pub use openhuman::memory_store::{MemoryClient, MemoryState};
|
||||
///
|
||||
/// Returns an error if command execution fails.
|
||||
pub fn run_core_from_args(args: &[String]) -> anyhow::Result<()> {
|
||||
core::cli::load_dotenv_for_cli()?;
|
||||
openhuman::service::apply_startup_restart_delay_from_env();
|
||||
openhuman::keyring::init_master_key();
|
||||
core::cli::run_from_cli_args(args)
|
||||
|
||||
@@ -18,6 +18,7 @@ use parking_lot::Mutex;
|
||||
use crate::openhuman::keyring::backend::KeyringBackend;
|
||||
use crate::openhuman::keyring::crypto::{self, KEY_LEN};
|
||||
use crate::openhuman::keyring::error::KeyringError;
|
||||
use crate::openhuman::keyring::store::BackendKind;
|
||||
|
||||
const KEYCHAIN_SERVICE: &str = "openhuman";
|
||||
const KEYCHAIN_MASTER_KEY_USERNAME: &str = "app:master_key";
|
||||
@@ -42,8 +43,11 @@ pub fn init_master_key() {
|
||||
crate::openhuman::keyring::init_workspace(&dir);
|
||||
|
||||
MASTER_KEY.get_or_init(|| {
|
||||
if !is_staging_or_production() {
|
||||
log::debug!("[keyring:encrypted_file] skipping master key init (dev environment)");
|
||||
let backend_kind = crate::openhuman::keyring::store::effective_backend_kind();
|
||||
if backend_kind != BackendKind::EncryptedFile {
|
||||
log::debug!(
|
||||
"[keyring:encrypted_file] skipping master key init backend={backend_kind:?}"
|
||||
);
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -68,13 +72,6 @@ pub fn init_master_key() {
|
||||
});
|
||||
}
|
||||
|
||||
fn is_staging_or_production() -> bool {
|
||||
matches!(
|
||||
std::env::var("OPENHUMAN_APP_ENV").as_deref(),
|
||||
Ok("staging") | Ok("production")
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns `true` if the master key has been successfully loaded.
|
||||
pub fn is_master_key_available() -> bool {
|
||||
MASTER_KEY.get().and_then(|k| k.as_ref()).is_some()
|
||||
|
||||
@@ -9,6 +9,13 @@ use std::sync::OnceLock;
|
||||
|
||||
use crate::openhuman::keyring::backend::{self, KeyringBackend};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub(super) enum BackendKind {
|
||||
Os,
|
||||
File,
|
||||
EncryptedFile,
|
||||
}
|
||||
|
||||
// ── Global state ─────────────────────────────────────────────────────────────
|
||||
|
||||
/// The workspace directory provided by the caller at startup.
|
||||
@@ -42,12 +49,12 @@ pub(super) fn backend() -> &'static dyn KeyringBackend {
|
||||
pub(super) fn build_backend() -> Box<dyn KeyringBackend> {
|
||||
// Priority 1: explicit env var override.
|
||||
if let Ok(env_val) = std::env::var("OPENHUMAN_KEYRING_BACKEND") {
|
||||
match env_val.trim() {
|
||||
"os" => {
|
||||
match backend_kind_from_env_value(&env_val) {
|
||||
Some(BackendKind::Os) => {
|
||||
log::info!("[keyring] backend=os (OPENHUMAN_KEYRING_BACKEND override)");
|
||||
return Box::new(backend::OsBackend);
|
||||
}
|
||||
"file" => {
|
||||
Some(BackendKind::File) => {
|
||||
let path = workspace_dir_for_file_backend();
|
||||
log::info!(
|
||||
"[keyring] backend=file path={} (OPENHUMAN_KEYRING_BACKEND override)",
|
||||
@@ -55,7 +62,7 @@ pub(super) fn build_backend() -> Box<dyn KeyringBackend> {
|
||||
);
|
||||
return Box::new(backend::FileBackend::new(&path));
|
||||
}
|
||||
"encrypted_file" => {
|
||||
Some(BackendKind::EncryptedFile) => {
|
||||
let path = workspace_dir_for_file_backend();
|
||||
log::info!(
|
||||
"[keyring] backend=encrypted_file path={} (OPENHUMAN_KEYRING_BACKEND override)",
|
||||
@@ -65,9 +72,10 @@ pub(super) fn build_backend() -> Box<dyn KeyringBackend> {
|
||||
&path,
|
||||
));
|
||||
}
|
||||
other => {
|
||||
None => {
|
||||
log::warn!(
|
||||
"[keyring] unknown OPENHUMAN_KEYRING_BACKEND={other:?}; falling through to defaults"
|
||||
"[keyring] unknown OPENHUMAN_KEYRING_BACKEND={:?}; falling through to defaults",
|
||||
env_val.trim()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -98,12 +106,48 @@ pub(super) fn build_backend() -> Box<dyn KeyringBackend> {
|
||||
}
|
||||
|
||||
fn is_staging_or_production() -> bool {
|
||||
matches!(
|
||||
std::env::var("OPENHUMAN_APP_ENV").as_deref(),
|
||||
Ok("staging") | Ok("production")
|
||||
is_staging_or_production_value(std::env::var("OPENHUMAN_APP_ENV").as_deref().ok())
|
||||
}
|
||||
|
||||
pub(super) fn effective_backend_kind() -> BackendKind {
|
||||
effective_backend_kind_for(
|
||||
std::env::var("OPENHUMAN_APP_ENV").as_deref().ok(),
|
||||
std::env::var("OPENHUMAN_KEYRING_BACKEND").as_deref().ok(),
|
||||
cfg!(test),
|
||||
)
|
||||
}
|
||||
|
||||
fn effective_backend_kind_for(
|
||||
app_env: Option<&str>,
|
||||
backend_override: Option<&str>,
|
||||
cfg_test: bool,
|
||||
) -> BackendKind {
|
||||
if let Some(kind) = backend_override.and_then(backend_kind_from_env_value) {
|
||||
return kind;
|
||||
}
|
||||
if cfg_test {
|
||||
return BackendKind::File;
|
||||
}
|
||||
if is_staging_or_production_value(app_env) {
|
||||
BackendKind::EncryptedFile
|
||||
} else {
|
||||
BackendKind::File
|
||||
}
|
||||
}
|
||||
|
||||
fn backend_kind_from_env_value(value: &str) -> Option<BackendKind> {
|
||||
match value.trim().to_ascii_lowercase().as_str() {
|
||||
"os" => Some(BackendKind::Os),
|
||||
"file" => Some(BackendKind::File),
|
||||
"encrypted_file" => Some(BackendKind::EncryptedFile),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_staging_or_production_value(app_env: Option<&str>) -> bool {
|
||||
matches!(app_env.map(str::trim), Some("staging") | Some("production"))
|
||||
}
|
||||
|
||||
/// Derive the directory for keyring files (`secrets.enc`, `dev-keychain.json`).
|
||||
///
|
||||
/// Uses the registered value from [`init_workspace`] if set; otherwise falls
|
||||
@@ -127,3 +171,40 @@ pub fn workspace_dir_for_file_backend() -> PathBuf {
|
||||
};
|
||||
openhuman_dir
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{effective_backend_kind_for, BackendKind};
|
||||
|
||||
#[test]
|
||||
fn explicit_file_backend_wins_over_staging_environment() {
|
||||
assert_eq!(
|
||||
effective_backend_kind_for(Some("staging"), Some("file"), false),
|
||||
BackendKind::File
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_encrypted_file_backend_wins_in_dev_environment() {
|
||||
assert_eq!(
|
||||
effective_backend_kind_for(Some("development"), Some("encrypted_file"), false),
|
||||
BackendKind::EncryptedFile
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn staging_defaults_to_encrypted_file_without_override() {
|
||||
assert_eq!(
|
||||
effective_backend_kind_for(Some(" staging "), None, false),
|
||||
BackendKind::EncryptedFile
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_backend_override_falls_back_to_environment_default() {
|
||||
assert_eq!(
|
||||
effective_backend_kind_for(Some("production"), Some("bogus"), false),
|
||||
BackendKind::EncryptedFile
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user