feat: vault core/CLI/workbench, vaultd token path, filetree view-state cleanup
Land password-vault dedicated workbench and mnote-vault-core/CLI, agent token read path design, vault transport split, and retire obsolete filetree smokes. Ignore local vault reimport scripts that trip secret scanners.
This commit is contained in:
@@ -813,6 +813,17 @@ impl ControlPlaneStore for SqliteControlPlaneStore {
|
||||
)
|
||||
.optional()?
|
||||
.ok_or_else(|| ControlPlaneError::Unauthorized("账号或密码错误".to_string()))?;
|
||||
// 登录成功后对齐同用户全部 password_* identity 的哈希。
|
||||
// 避免仅更新邮箱 identity 后,用户名登录仍用旧密码(liaibo 线上漂移过)。
|
||||
let now = now_text();
|
||||
let _ = conn.execute(
|
||||
"UPDATE auth_identities
|
||||
SET password_hash = ?1, updated_at = ?2
|
||||
WHERE user_id = ?3
|
||||
AND provider IN ('password_username', 'password_email')
|
||||
AND password_hash != ?1",
|
||||
params![expected_hash, now, user.id],
|
||||
)?;
|
||||
drop(conn);
|
||||
|
||||
let session = self.create_session(CreateSessionInput {
|
||||
@@ -4869,6 +4880,50 @@ mod tests {
|
||||
.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authenticate_password_syncs_sibling_password_identity_hashes() {
|
||||
let store = store();
|
||||
let current_password = ["current", "secret"].join("-");
|
||||
let stale_password = ["stale", "secret"].join("-");
|
||||
create_password_identity(&store, "dana", "dana@example.com", ¤t_password);
|
||||
|
||||
// 模拟仅邮箱 identity 被改密、用户名 identity 仍是旧哈希的漂移。
|
||||
{
|
||||
let conn = store.lock_conn().expect("lock");
|
||||
conn.execute(
|
||||
"UPDATE auth_identities
|
||||
SET password_hash = ?1
|
||||
WHERE provider = 'password_username' AND provider_subject = 'dana'",
|
||||
params![password_hash_v1(&stale_password)],
|
||||
)
|
||||
.expect("stale username hash");
|
||||
}
|
||||
|
||||
store
|
||||
.authenticate_password(AuthenticatePasswordInput {
|
||||
account: "dana@example.com".to_string(),
|
||||
password: current_password.clone(),
|
||||
session_id: None,
|
||||
token_hash: session_token_hash("dana-email-repair"),
|
||||
user_agent: None,
|
||||
ip_hash: None,
|
||||
expires_at: None,
|
||||
})
|
||||
.expect("email login should still work");
|
||||
|
||||
store
|
||||
.authenticate_password(AuthenticatePasswordInput {
|
||||
account: "dana".to_string(),
|
||||
password: current_password,
|
||||
session_id: None,
|
||||
token_hash: session_token_hash("dana-username-after-sync"),
|
||||
user_agent: None,
|
||||
ip_hash: None,
|
||||
expires_at: None,
|
||||
})
|
||||
.expect("username login should work after sibling hash sync");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ai_tool_events_append_and_list() {
|
||||
let store = store();
|
||||
|
||||
@@ -1337,6 +1337,17 @@ impl ControlPlaneStore for TursoControlPlaneStore {
|
||||
)
|
||||
.optional()?
|
||||
.ok_or_else(|| ControlPlaneError::Unauthorized("账号或密码错误".to_string()))?;
|
||||
// 登录成功后对齐同用户全部 password_* identity 的哈希。
|
||||
// 避免仅更新邮箱 identity 后,用户名登录仍用旧密码(liaibo 线上漂移过)。
|
||||
let now = now_text();
|
||||
let _ = conn.execute(
|
||||
"UPDATE auth_identities
|
||||
SET password_hash = ?1, updated_at = ?2
|
||||
WHERE user_id = ?3
|
||||
AND provider IN ('password_username', 'password_email')
|
||||
AND password_hash != ?1",
|
||||
params![expected_hash, now, user.id],
|
||||
)?;
|
||||
drop(conn);
|
||||
|
||||
let session = self.create_session(CreateSessionInput {
|
||||
@@ -5235,6 +5246,50 @@ mod tests {
|
||||
.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authenticate_password_syncs_sibling_password_identity_hashes() {
|
||||
let store = store();
|
||||
let current_password = ["current", "secret"].join("-");
|
||||
let stale_password = ["stale", "secret"].join("-");
|
||||
create_password_identity(&store, "dana", "dana@example.com", ¤t_password);
|
||||
|
||||
// 模拟仅邮箱 identity 被改密、用户名 identity 仍是旧哈希的漂移。
|
||||
{
|
||||
let conn = store.lock_conn().expect("lock");
|
||||
conn.execute(
|
||||
"UPDATE auth_identities
|
||||
SET password_hash = ?1
|
||||
WHERE provider = 'password_username' AND provider_subject = 'dana'",
|
||||
params![password_hash_v1(&stale_password)],
|
||||
)
|
||||
.expect("stale username hash");
|
||||
}
|
||||
|
||||
store
|
||||
.authenticate_password(AuthenticatePasswordInput {
|
||||
account: "dana@example.com".to_string(),
|
||||
password: current_password.clone(),
|
||||
session_id: None,
|
||||
token_hash: session_token_hash("dana-email-repair"),
|
||||
user_agent: None,
|
||||
ip_hash: None,
|
||||
expires_at: None,
|
||||
})
|
||||
.expect("email login should still work");
|
||||
|
||||
store
|
||||
.authenticate_password(AuthenticatePasswordInput {
|
||||
account: "dana".to_string(),
|
||||
password: current_password,
|
||||
session_id: None,
|
||||
token_hash: session_token_hash("dana-username-after-sync"),
|
||||
user_agent: None,
|
||||
ip_hash: None,
|
||||
expires_at: None,
|
||||
})
|
||||
.expect("username login should work after sibling hash sync");
|
||||
}
|
||||
|
||||
// --- Fault injection tests ---
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user