From 08e81363c97a5b90c80fad99c5968a1a89bc18da Mon Sep 17 00:00:00 2001 From: agent Date: Fri, 27 Mar 2026 10:45:12 +0800 Subject: [PATCH] =?UTF-8?q?release(secrets-mcp):=20v0.3.2=20=E2=80=94=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20key=5Fref=20=E5=A4=9A=E7=A7=9F=E6=88=B7?= =?UTF-8?q?=E4=B8=8E=E6=AD=A7=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - env_map:key_ref 解析传入 user_id;支持 folder/name;多条匹配时报错 - 文档同步 key_ref 说明 - bump secrets-mcp 0.3.1 → 0.3.2,更新 Cargo.lock Made-with: Cursor --- AGENTS.md | 2 +- Cargo.lock | 2 +- README.md | 2 +- crates/secrets-core/src/service/env_map.rs | 33 ++++++++++++++++++---- crates/secrets-mcp/Cargo.toml | 2 +- 5 files changed, 32 insertions(+), 9 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 67bec0a..a0a3068 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -118,7 +118,7 @@ oauth_accounts ( ### PEM 共享(`key_ref`) -将共享 PEM 存为 **`type=key`** 的 entry;其它记录在 `metadata.key_ref` 指向该 key 的 `name`。更新 key 记录后,引用方通过服务层解析合并逻辑即可使用新密钥(实现见 `secrets_core::service`)。 +将共享 PEM 存为 **`type=key`** 的 entry;其它记录在 `metadata.key_ref` 指向该 key 的 `name`(支持 `folder/name` 格式消歧)。更新 key 记录后,引用方通过服务层解析合并逻辑即可使用新密钥(实现见 `secrets_core::service::env_map`)。 ## 代码规范 diff --git a/Cargo.lock b/Cargo.lock index e0f7af2..be9d9a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1968,7 +1968,7 @@ dependencies = [ [[package]] name = "secrets-mcp" -version = "0.3.1" +version = "0.3.2" dependencies = [ "anyhow", "askama", diff --git a/README.md b/README.md index ee23dbf..d1fa020 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ flowchart LR ### PEM 共享(`key_ref`) -同一 PEM 可被多条 `server` 等记录引用:将 PEM 存为 **`type=key`** 的 entry,在其它条目的 `metadata.key_ref` 中写该 key 条目的 `name`;轮换时只更新 key 对应记录即可。 +同一 PEM 可被多条 `server` 等记录引用:将 PEM 存为 **`type=key`** 的 entry,在其它条目的 `metadata.key_ref` 中写该 key 条目的 `name`(支持 `folder/name` 格式消歧);轮换时只更新 key 记录即可。 ## 审计日志 diff --git a/crates/secrets-core/src/service/env_map.rs b/crates/secrets-core/src/service/env_map.rs index ff90712..1fa979e 100644 --- a/crates/secrets-core/src/service/env_map.rs +++ b/crates/secrets-core/src/service/env_map.rs @@ -26,7 +26,8 @@ pub async fn build_env_map( let mut combined: HashMap = HashMap::new(); for entry in &entries { - let entry_map = build_entry_env_map(pool, entry, only_fields, prefix, master_key).await?; + let entry_map = + build_entry_env_map(pool, entry, only_fields, prefix, master_key, user_id).await?; combined.extend(entry_map); } @@ -39,6 +40,7 @@ async fn build_entry_env_map( only_fields: &[String], prefix: &str, master_key: &[u8; 32], + user_id: Option, ) -> Result> { let entry_ids = vec![entry.id]; let secrets_map = fetch_secrets_for_entries(pool, &entry_ids).await?; @@ -66,10 +68,31 @@ async fn build_entry_env_map( map.insert(key, json_to_env_string(&decrypted)); } - // Resolve key_ref + // Resolve key_ref. Supported formats: "name" or "folder/name". if let Some(key_ref) = entry.metadata.get("key_ref").and_then(|v| v.as_str()) { - let key_entries = - fetch_entries(pool, None, Some("key"), Some(key_ref), &[], None, None).await?; + let (ref_folder, ref_name) = if let Some((f, n)) = key_ref.split_once('/') { + (Some(f), n) + } else { + (None, key_ref) + }; + let key_entries = fetch_entries( + pool, + ref_folder, + Some("key"), + Some(ref_name), + &[], + None, + user_id, + ) + .await?; + + if key_entries.len() > 1 { + anyhow::bail!( + "key_ref '{}' matched {} entries; qualify with folder/name to resolve the ambiguity", + key_ref, + key_entries.len() + ); + } if let Some(key_entry) = key_entries.first() { let key_ids = vec![key_entry.id]; @@ -87,7 +110,7 @@ async fn build_entry_env_map( map.insert(key_var, json_to_env_string(&decrypted)); } } else { - tracing::warn!(key_ref, "key_ref target not found"); + tracing::warn!(key_ref, ?user_id, "key_ref target not found"); } } diff --git a/crates/secrets-mcp/Cargo.toml b/crates/secrets-mcp/Cargo.toml index f493e52..1f8aeb9 100644 --- a/crates/secrets-mcp/Cargo.toml +++ b/crates/secrets-mcp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "secrets-mcp" -version = "0.3.1" +version = "0.3.2" edition.workspace = true [[bin]]