Browse Source

Align cipher org checks & attachment notification with upstream (#7906)

main
Daniel García 10 hours ago
committed by GitHub
parent
commit
64ae55ceb2
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 52
      src/api/core/ciphers.rs

52
src/api/core/ciphers.rs

@ -666,6 +666,9 @@ async fn post_ciphers_import(data: Json<ImportData>, headers: Headers, conn: DbC
for (index, mut cipher_data) in data.ciphers.into_iter().enumerate() { for (index, mut cipher_data) in data.ciphers.into_iter().enumerate() {
let folder_id = relations_map.get(&index).and_then(|i| folders.get(*i).cloned()); let folder_id = relations_map.get(&index).and_then(|i| folders.get(*i).cloned());
cipher_data.folder_id = folder_id; cipher_data.folder_id = folder_id;
// A personal import always creates personal ciphers, the organization ID is ignored instead of rejected
// Upstream: https://github.com/bitwarden/server/blob/b06b81c1fb33ce9c9872b96397ba2b4d879b66af/src/Api/Tools/Controllers/ImportCiphersController.cs#L58
cipher_data.organization_id = None;
let mut cipher = Cipher::new(cipher_data.r#type, cipher_data.name.clone()); let mut cipher = Cipher::new(cipher_data.r#type, cipher_data.name.clone());
update_cipher_from_data(&mut cipher, cipher_data, &headers, None, &conn, &nt, UpdateType::None).await?; update_cipher_from_data(&mut cipher, cipher_data, &headers, None, &conn, &nt, UpdateType::None).await?;
@ -687,6 +690,11 @@ async fn put_cipher_admin(
conn: DbConn, conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
// Only organization ciphers can be edited here, put_cipher() rejects an organization ID that isn't the cipher's
if data.organization_id.is_none() {
err_code!("Resource not found.", "Cipher is not an organization cipher", Status::NotFound.code);
}
put_cipher(cipher_id, data, headers, conn, nt).await put_cipher(cipher_id, data, headers, conn, nt).await
} }
@ -698,7 +706,7 @@ async fn post_cipher_admin(
conn: DbConn, conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
post_cipher(cipher_id, data, headers, conn, nt).await put_cipher_admin(cipher_id, data, headers, conn, nt).await
} }
#[post("/ciphers/<cipher_id>", data = "<data>")] #[post("/ciphers/<cipher_id>", data = "<data>")]
@ -735,6 +743,11 @@ async fn put_cipher(
err!("Cipher is not write accessible") err!("Cipher is not write accessible")
} }
// Moving a cipher into an organization is only possible through the share endpoints
if cipher.organization_uuid != data.organization_id {
err!("Organization mismatch. Please resync the client before updating the cipher")
}
update_cipher_from_data(&mut cipher, data, &headers, None, &conn, &nt, UpdateType::SyncCipherUpdate).await?; update_cipher_from_data(&mut cipher, data, &headers, None, &conn, &nt, UpdateType::SyncCipherUpdate).await?;
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &conn).await?)) Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &conn).await?))
@ -993,9 +1006,7 @@ async fn post_cipher_share(
conn: DbConn, conn: DbConn,
nt: Notify<'_>, nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
let data: ShareCipherData = data.into_inner(); put_cipher_share(cipher_id, data, headers, conn, nt).await
share_cipher_by_uuid(&cipher_id, data, &headers, &conn, &nt, None).await
} }
#[put("/ciphers/<cipher_id>/share", data = "<data>")] #[put("/ciphers/<cipher_id>/share", data = "<data>")]
@ -1008,6 +1019,14 @@ async fn put_cipher_share(
) -> JsonResult { ) -> JsonResult {
let data: ShareCipherData = data.into_inner(); let data: ShareCipherData = data.into_inner();
if data.cipher.organization_id.is_none() {
err!("Cipher OrganizationId is required.")
}
if data.collection_ids.is_empty() {
err!("You must select at least one collection.")
}
share_cipher_by_uuid(&cipher_id, data, &headers, &conn, &nt, None).await share_cipher_by_uuid(&cipher_id, data, &headers, &conn, &nt, None).await
} }
@ -1078,6 +1097,9 @@ async fn put_cipher_share_selected(
if cipher.id.is_none() { if cipher.id.is_none() {
err!("Request missing ids field") err!("Request missing ids field")
} }
if cipher.organization_id.is_none() {
err!("Cipher OrganizationId is required.")
}
} }
let mut responses = Vec::with_capacity(data.ciphers.len()); let mut responses = Vec::with_capacity(data.ciphers.len());
@ -1258,6 +1280,7 @@ async fn post_attachment_v2(
data: Json<AttachmentRequestData>, data: Json<AttachmentRequestData>,
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
nt: Notify<'_>,
) -> JsonResult { ) -> JsonResult {
let Some(cipher) = Cipher::find_by_uuid(&cipher_id, &conn).await else { let Some(cipher) = Cipher::find_by_uuid(&cipher_id, &conn).await else {
err!("Cipher doesn't exist") err!("Cipher doesn't exist")
@ -1288,6 +1311,7 @@ async fn post_attachment_v2(
let attachment = let attachment =
Attachment::new(attachment_id.clone(), cipher.uuid.clone(), data.file_name, file_size, Some(data.key)); Attachment::new(attachment_id.clone(), cipher.uuid.clone(), data.file_name, file_size, Some(data.key));
attachment.save(&conn).await.expect("Error saving attachment"); attachment.save(&conn).await.expect("Error saving attachment");
notify_attachment_created(&cipher, &headers, &conn, &nt).await;
let url = format!("/ciphers/{}/attachment/{attachment_id}", cipher.uuid); let url = format!("/ciphers/{}/attachment/{attachment_id}", cipher.uuid);
let response_key = match data.admin_request { let response_key = match data.admin_request {
@ -1407,13 +1431,23 @@ async fn save_attachment(
save_temp_file(&PathType::Attachments, &format!("{cipher_id}/{file_id}"), data.data, true).await?; save_temp_file(&PathType::Attachments, &format!("{cipher_id}/{file_id}"), data.data, true).await?;
// With the v2 API, post_attachment_v2() already did this when it created the attachment record
if attachment.is_none() {
notify_attachment_created(&cipher, headers, &conn, &nt).await;
}
Ok((cipher, conn))
}
/// Sends the cipher update to the clients and logs the attachment event
async fn notify_attachment_created(cipher: &Cipher, headers: &Headers, conn: &DbConn, nt: &Notify<'_>) {
nt.send_cipher_update( nt.send_cipher_update(
UpdateType::SyncCipherUpdate, UpdateType::SyncCipherUpdate,
&cipher, cipher,
&cipher.update_users_revision(&conn).await, &cipher.update_users_revision(conn).await,
&headers.device, &headers.device,
None, None,
&conn, conn,
) )
.await; .await;
@ -1425,12 +1459,10 @@ async fn save_attachment(
&headers.user.uuid, &headers.user.uuid,
headers.device.atype, headers.device.atype,
&headers.ip.ip, &headers.ip.ip,
&conn, conn,
) )
.await; .await;
} }
Ok((cipher, conn))
} }
/// v2 API for uploading the actual data content of an attachment. /// v2 API for uploading the actual data content of an attachment.

Loading…
Cancel
Save