Harden auth/vault path sanitization and clean WeKnora docs

This commit is contained in:
Agent Board
2026-07-28 17:04:27 +08:00
parent 2deaf59f7b
commit 26ff1a9c9a
190 changed files with 13454 additions and 4987 deletions
+90 -4
View File
@@ -19,12 +19,17 @@ pub struct UserRecord {
pub revision: i64,
}
/// 会话令牌哈希(领域前缀,避免与 share/password 等 token 类型混淆)。
/// 注意:改前缀会使既有 session 行失效,需用户重新登录。
pub fn session_token_hash(raw_token: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(b"mnote-session-token-v1:");
hasher.update(raw_token.as_bytes());
hex::encode(hasher.finalize())
format!("sha256-v1:{}", hex::encode(hasher.finalize()))
}
/// 密码哈希 v1:SHA-256 + 固定领域前缀(兼容既有 `sha256-v1:` 存档)。
/// 技术债:生产应迁移 Argon2id + 每用户随机盐;在此保持算法兼容以免批量锁死账号。
pub fn password_hash_v1(password: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(b"mnote-password-v1:");
@@ -39,27 +44,76 @@ pub fn share_token_hash_v1(token: &str) -> String {
format!("sha256-v1:{}", hex::encode(hasher.finalize()))
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UpsertUserInput {
pub id: Option<EntityId>,
pub email: Option<String>,
pub username: String,
pub display_name: String,
pub role: Option<String>,
/// 密码哈希;Debug 脱敏,避免日志泄露。
pub password_hash: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
impl std::fmt::Debug for UpsertUserInput {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("UpsertUserInput")
.field("id", &self.id)
.field("email", &self.email)
.field("username", &self.username)
.field("display_name", &self.display_name)
.field("role", &self.role)
.field(
"password_hash",
&self
.password_hash
.as_ref()
.map(|_| "<redacted>")
.unwrap_or("None"),
)
.finish()
}
}
#[derive(Clone, PartialEq, Eq, Deserialize)]
pub struct CreatePasswordIdentityInput {
pub user_id: EntityId,
pub email: Option<String>,
pub username: String,
/// 明文密码仅用于创建身份;Debug/Serialize 必须脱敏。
pub password: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
impl Serialize for CreatePasswordIdentityInput {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut state = serializer.serialize_struct("CreatePasswordIdentityInput", 4)?;
state.serialize_field("user_id", &self.user_id)?;
state.serialize_field("email", &self.email)?;
state.serialize_field("username", &self.username)?;
state.serialize_field("password", "<redacted>")?;
state.end()
}
}
impl std::fmt::Debug for CreatePasswordIdentityInput {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CreatePasswordIdentityInput")
.field("user_id", &self.user_id)
.field("email", &self.email)
.field("username", &self.username)
.field("password", &"<redacted>")
.finish()
}
}
#[derive(Clone, PartialEq, Eq, Deserialize)]
pub struct AuthenticatePasswordInput {
pub account: String,
/// 明文密码仅用于鉴权;Debug/Serialize 脱敏。
pub password: String,
pub session_id: Option<EntityId>,
pub token_hash: String,
@@ -68,6 +122,38 @@ pub struct AuthenticatePasswordInput {
pub expires_at: Option<Timestamp>,
}
impl Serialize for AuthenticatePasswordInput {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut state = serializer.serialize_struct("AuthenticatePasswordInput", 7)?;
state.serialize_field("account", &self.account)?;
state.serialize_field("password", "<redacted>")?;
state.serialize_field("session_id", &self.session_id)?;
state.serialize_field("token_hash", "<redacted>")?;
state.serialize_field("user_agent", &self.user_agent)?;
state.serialize_field("ip_hash", &self.ip_hash)?;
state.serialize_field("expires_at", &self.expires_at)?;
state.end()
}
}
impl std::fmt::Debug for AuthenticatePasswordInput {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AuthenticatePasswordInput")
.field("account", &self.account)
.field("password", &"<redacted>")
.field("session_id", &self.session_id)
.field("token_hash", &"<redacted>")
.field("user_agent", &self.user_agent)
.field("ip_hash", &self.ip_hash)
.field("expires_at", &self.expires_at)
.finish()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuthSessionRecord {
pub id: EntityId,