fix: add support for updating attachment keys and filenames during key rotation

This commit is contained in:
qaz741wsd856 2025-12-27 03:22:56 +00:00
parent 84067f41f0
commit 58847e9788
3 changed files with 34 additions and 1 deletions

View file

@ -754,6 +754,7 @@ pub async fn post_rotatekey(
// Only update personal ciphers (organization_id is None)
let mut cipher_statements: Vec<D1PreparedStatement> =
Vec::with_capacity(personal_ciphers.len());
let mut attachment_statements: Vec<D1PreparedStatement> = Vec::new();
for cipher in personal_ciphers {
// id is guaranteed to exist (validated above)
let cipher_id = cipher.id.as_ref().unwrap();
@ -778,8 +779,27 @@ pub async fn post_rotatekey(
)
.map_err(|_| AppError::Database)?;
cipher_statements.push(stmt);
// Update attachments key and encrypted filename when rotating.
// The Bitwarden clients send `attachments2` only during key rotation.
if let Some(attachments2) = &cipher.attachments2 {
for (attachment_id, attachment) in attachments2 {
let stmt = query!(
&db,
"UPDATE attachments SET file_name = ?1, akey = ?2, updated_at = ?3 WHERE id = ?4 AND cipher_id = ?5",
attachment.file_name,
attachment.key,
now,
attachment_id,
cipher_id
)
.map_err(|_| AppError::Database)?;
attachment_statements.push(stmt);
}
}
}
db::execute_in_batches(&db, cipher_statements, batch_size).await?;
db::execute_in_batches(&db, attachment_statements, batch_size).await?;
// Generate new salt and hash the new password
let new_salt = generate_salt()?;

View file

@ -1,3 +1,5 @@
use std::collections::HashMap;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use serde_json::{json, Map, Value};
@ -317,12 +319,23 @@ pub struct CipherRequestData {
pub favorite: Option<bool>,
#[serde(flatten)]
pub type_fields: CipherTypeFields,
/// Used during key rotation to update attachment keys and encrypted filenames.
#[serde(skip_serializing_if = "Option::is_none")]
pub attachments2: Option<HashMap<String, Attachments2Data>>,
// The revision datetime (in ISO 8601 format) of the client's local copy
// Used to prevent updating a cipher when client doesn't have the latest version
#[serde(skip_serializing_if = "Option::is_none")]
pub last_known_revision_date: Option<String>,
}
/// Attachment metadata sent by clients during key rotation.
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Attachments2Data {
pub file_name: String,
pub key: String,
}
/// Represents the full request payload for creating a cipher with collections.
/// Supports both camelCase and PascalCase for compatibility with different clients.
#[derive(Debug, Deserialize)]

View file

@ -14,7 +14,7 @@ pub struct User {
pub master_password_hash: String,
pub master_password_hint: Option<String>,
pub password_salt: Option<String>, // Salt for server-side PBKDF2 (NULL for legacy users)
pub password_iterations: i32, // Server-side PBKDF2 iterations used for master_password_hash
pub password_iterations: i32, // Server-side PBKDF2 iterations used for master_password_hash
pub key: String,
pub private_key: String,
pub public_key: String,