release(secrets-mcp): 0.5.9 — users.key_version 与会话失效;Web 条目解密 API 与列表增强
All checks were successful
Secrets MCP — Build & Release / 检查 / 构建 / 发版 (push) Successful in 5m24s
Secrets MCP — Build & Release / 部署 secrets-mcp (push) Successful in 1m36s

This commit is contained in:
voson
2026-04-06 16:39:26 +08:00
parent 8942718641
commit b0fcb83592
7 changed files with 406 additions and 53 deletions

View File

@@ -428,6 +428,9 @@ async fn migrate_schema(pool: &PgPool) -> Result<()> {
-- ── Drop legacy actor columns ─────────────────────────────────────────────
ALTER TABLE secrets_history DROP COLUMN IF EXISTS actor;
ALTER TABLE audit_log DROP COLUMN IF EXISTS actor;
-- ── key_version: incremented on passphrase change to invalidate other sessions ──
ALTER TABLE users ADD COLUMN IF NOT EXISTS key_version BIGINT NOT NULL DEFAULT 0;
"#,
)
.execute(pool)

View File

@@ -200,6 +200,8 @@ pub struct User {
pub key_params: Option<serde_json::Value>,
/// Plaintext API key for MCP Bearer authentication. Auto-created on first login.
pub api_key: Option<String>,
/// Incremented each time the passphrase is changed; used to invalidate sessions on other devices.
pub key_version: i64,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}

View File

@@ -31,7 +31,7 @@ pub async fn find_or_create_user(pool: &PgPool, profile: OAuthProfile) -> Result
if let Some(oa) = existing {
let user: User = sqlx::query_as(
"SELECT id, email, name, avatar_url, key_salt, key_check, key_params, api_key, created_at, updated_at \
"SELECT id, email, name, avatar_url, key_salt, key_check, key_params, api_key, key_version, created_at, updated_at \
FROM users WHERE id = $1",
)
.bind(oa.user_id)
@@ -50,7 +50,7 @@ pub async fn find_or_create_user(pool: &PgPool, profile: OAuthProfile) -> Result
let user: User = sqlx::query_as(
"INSERT INTO users (email, name, avatar_url) \
VALUES ($1, $2, $3) \
RETURNING id, email, name, avatar_url, key_salt, key_check, key_params, api_key, created_at, updated_at",
RETURNING id, email, name, avatar_url, key_salt, key_check, key_params, api_key, key_version, created_at, updated_at",
)
.bind(&profile.email)
.bind(&display_name)
@@ -108,7 +108,8 @@ pub async fn change_user_key(
}
sqlx::query(
"UPDATE users SET key_salt = $1, key_check = $2, key_params = $3, updated_at = NOW() \
"UPDATE users SET key_salt = $1, key_check = $2, key_params = $3, \
key_version = key_version + 1, updated_at = NOW() \
WHERE id = $4",
)
.bind(new_salt)
@@ -146,7 +147,7 @@ pub async fn update_user_key_setup(
/// Fetch a user by ID.
pub async fn get_user_by_id(pool: &PgPool, user_id: Uuid) -> Result<Option<User>> {
let user = sqlx::query_as(
"SELECT id, email, name, avatar_url, key_salt, key_check, key_params, api_key, created_at, updated_at \
"SELECT id, email, name, avatar_url, key_salt, key_check, key_params, api_key, key_version, created_at, updated_at \
FROM users WHERE id = $1",
)
.bind(user_id)