secrets: add local key store
This commit is contained in:
@@ -331,7 +331,7 @@ impl crate::WebSearchConfig {
|
||||
Self {
|
||||
enabled: upper.enabled.or(self.enabled),
|
||||
provider: upper.provider.or(self.provider),
|
||||
api_key_env: upper.api_key_env.or(self.api_key_env),
|
||||
api_key_secret: upper.api_key_secret.or(self.api_key_secret),
|
||||
timeout_secs: upper.timeout_secs.or(self.timeout_secs),
|
||||
base_url: upper.base_url.or(self.base_url),
|
||||
country: upper.country.or(self.country),
|
||||
@@ -517,7 +517,7 @@ fn ensure_absolute(field: &'static str, path: &Path) -> Result<(), ResolveError>
|
||||
}
|
||||
}
|
||||
|
||||
/// `AuthRef::ApiKey { file, .. }` が相対パスのとき `base` を前置する。
|
||||
/// `AuthRef::ApiKey { file }` が相対パスのとき `base` を前置する。
|
||||
fn resolve_auth_file(auth: &mut Option<AuthRef>, base: &Path) {
|
||||
if let Some(AuthRef::ApiKey { file: Some(p), .. }) = auth.as_mut() {
|
||||
*p = join_if_relative(base, p);
|
||||
@@ -692,10 +692,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn api_key_file_auth(path: PathBuf) -> AuthRef {
|
||||
AuthRef::ApiKey {
|
||||
env: None,
|
||||
file: Some(path),
|
||||
}
|
||||
AuthRef::ApiKey { file: Some(path) }
|
||||
}
|
||||
|
||||
fn minimal_valid() -> PodManifestConfig {
|
||||
@@ -1089,7 +1086,7 @@ mod tests {
|
||||
}),
|
||||
web: Some(WebConfig {
|
||||
search: Some(crate::WebSearchConfig {
|
||||
api_key_env: Some("LOWER_BRAVE_KEY".into()),
|
||||
api_key_secret: Some("web/brave/lower".into()),
|
||||
timeout_secs: Some(12),
|
||||
..Default::default()
|
||||
}),
|
||||
@@ -1118,7 +1115,7 @@ mod tests {
|
||||
assert_eq!(c.prune_protected_tokens, Some(5_000));
|
||||
let search = merged.web.unwrap().search.unwrap();
|
||||
assert_eq!(search.timeout_secs, Some(3));
|
||||
assert_eq!(search.api_key_env.as_deref(), Some("LOWER_BRAVE_KEY"));
|
||||
assert_eq!(search.api_key_secret.as_deref(), Some("web/brave/lower"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -122,10 +122,10 @@ pub struct WebSearchConfig {
|
||||
pub enabled: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub provider: Option<WebSearchProvider>,
|
||||
/// Environment variable that stores the provider API key. Raw secrets do
|
||||
/// not belong in manifest files.
|
||||
/// Local secret-store id for the provider API key. Raw secrets do not
|
||||
/// belong in manifest files.
|
||||
#[serde(default)]
|
||||
pub api_key_env: Option<String>,
|
||||
pub api_key_secret: Option<String>,
|
||||
/// Request timeout in seconds. Tool implementation applies a safe default
|
||||
/// when this is omitted.
|
||||
#[serde(default)]
|
||||
@@ -651,7 +651,7 @@ permission = "write"
|
||||
#[test]
|
||||
fn parse_web_config() {
|
||||
let toml = format!(
|
||||
"{}\n[web]\nenabled = true\n\n[web.search]\nprovider = \"brave\"\napi_key_env = \"BRAVE_SEARCH_API_KEY\"\ntimeout_secs = 12\n\n[web.fetch]\ntimeout_secs = 7\nredirect_limit = 3\nmax_response_bytes = 12345\nmax_output_bytes = 2048\n",
|
||||
"{}\n[web]\nenabled = true\n\n[web.search]\nprovider = \"brave\"\napi_key_secret = \"web/brave/default\"\ntimeout_secs = 12\n\n[web.fetch]\ntimeout_secs = 7\nredirect_limit = 3\nmax_response_bytes = 12345\nmax_output_bytes = 2048\n",
|
||||
MINIMAL_REQUIRED
|
||||
);
|
||||
let manifest = PodManifest::from_toml(&toml).unwrap();
|
||||
|
||||
@@ -97,7 +97,7 @@ pub enum SchemeKind {
|
||||
|
||||
/// 認証の参照。
|
||||
///
|
||||
/// 実際のトークン値の解決(env / file 読取、OAuth refresh 等)は
|
||||
/// 実際のトークン値の解決(local secret store / file 読取、OAuth refresh 等)は
|
||||
/// `crates/provider` で行う。ここはあくまで「どこから取るか」の宣言。
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
|
||||
#[serde(tag = "kind", rename_all = "snake_case")]
|
||||
@@ -105,11 +105,10 @@ pub enum AuthRef {
|
||||
/// 認証不要(ローカル Ollama 等)
|
||||
#[default]
|
||||
None,
|
||||
/// API key。env / file のいずれか(両方指定された場合は env が優先)
|
||||
/// API key file reference. Prefer [`AuthRef::SecretRef`] for normal
|
||||
/// provider credentials; this remains an explicit file source for low-level
|
||||
/// manifests and tests.
|
||||
ApiKey {
|
||||
/// 環境変数名。未指定のときは scheme ごとの既定(`INSOMNIA_API_KEY_*`)
|
||||
#[serde(default)]
|
||||
env: Option<String>,
|
||||
/// key を書き込んだファイル(絶対パス)
|
||||
#[serde(default)]
|
||||
file: Option<PathBuf>,
|
||||
@@ -117,8 +116,8 @@ pub enum AuthRef {
|
||||
/// ChatGPT OAuth(`~/.codex/auth.json`)。実装は `llm-auth-codex-oauth` チケット
|
||||
#[serde(rename = "codex_oauth")]
|
||||
CodexOAuth,
|
||||
/// Typed secret-store reference. The profile resolver preserves this
|
||||
/// reference verbatim; secret-store lookup/decryption is intentionally a
|
||||
/// Typed local secret-store reference. The profile resolver preserves this
|
||||
/// reference verbatim; secret-store lookup/deobfuscation is intentionally a
|
||||
/// later consumer-boundary concern.
|
||||
#[serde(rename = "secret_ref")]
|
||||
SecretRef {
|
||||
@@ -126,16 +125,3 @@ pub enum AuthRef {
|
||||
ref_: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl SchemeKind {
|
||||
/// 既定の環境変数名(`INSOMNIA_API_KEY_*`)。
|
||||
///
|
||||
/// `AuthRef::ApiKey { env: None, .. }` の env 未指定時に使う。
|
||||
pub fn default_env_var(self) -> &'static str {
|
||||
match self {
|
||||
Self::Anthropic => "INSOMNIA_API_KEY_ANTHROPIC",
|
||||
Self::OpenaiChat | Self::OpenaiResponses => "INSOMNIA_API_KEY_OPENAI",
|
||||
Self::Gemini => "INSOMNIA_API_KEY_GEMINI",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1038,9 +1038,7 @@ fn reject_absolute_auth_file(
|
||||
auth: &Option<AuthRef>,
|
||||
field: &'static str,
|
||||
) -> Result<(), ProfileError> {
|
||||
if let Some(AuthRef::ApiKey {
|
||||
file: Some(file), ..
|
||||
}) = auth
|
||||
if let Some(AuthRef::ApiKey { file: Some(file) }) = auth
|
||||
&& file.is_absolute()
|
||||
{
|
||||
return Err(ProfileError::InvalidProfile(format!(
|
||||
|
||||
Reference in New Issue
Block a user