1
0
Fork 0
mirror of https://github.com/dani-garcia/vaultwarden.git synced 2025-07-22 12:08:20 +00:00

Fix the web-vault v2023.2.0 API calls

- Supports the new Collection/Group/User editing UI's
- Support `/partial` endpoint for cipher updating to allow folder and favorite update for read-only ciphers.
- Prevent `Favorite`, `Folder`, `read-only` and `hide-passwords` from being added to the organizational sync.
- Added and corrected some `Object` key's to the output json.

Fixes #3279
This commit is contained in:
BlackDex 2023-02-27 16:37:58 +01:00
commit 7ec00d3850
No known key found for this signature in database
GPG key ID: 58C80A2AA6C765E1
10 changed files with 396 additions and 95 deletions

View file

@ -56,7 +56,9 @@ pub fn routes() -> Vec<Route> {
put_cipher_share,
put_cipher_share_selected,
post_cipher,
post_cipher_partial,
put_cipher,
put_cipher_partial,
delete_cipher_post,
delete_cipher_post_admin,
delete_cipher_put,
@ -109,7 +111,10 @@ async fn sync(data: SyncData, headers: Headers, mut conn: DbConn) -> Json<Value>
// Lets generate the ciphers_json using all the gathered info
let mut ciphers_json = Vec::with_capacity(ciphers.len());
for c in ciphers {
ciphers_json.push(c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), &mut conn).await);
ciphers_json.push(
c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), CipherSyncType::User, &mut conn)
.await,
);
}
let collections = Collection::find_by_user_uuid(headers.user.uuid.clone(), &mut conn).await;
@ -153,7 +158,10 @@ async fn get_ciphers(headers: Headers, mut conn: DbConn) -> Json<Value> {
let mut ciphers_json = Vec::with_capacity(ciphers.len());
for c in ciphers {
ciphers_json.push(c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), &mut conn).await);
ciphers_json.push(
c.to_json(&headers.host, &headers.user.uuid, Some(&cipher_sync_data), CipherSyncType::User, &mut conn)
.await,
);
}
Json(json!({
@ -174,7 +182,7 @@ async fn get_cipher(uuid: String, headers: Headers, mut conn: DbConn) -> JsonRes
err!("Cipher is not owned by user")
}
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &mut conn).await))
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await))
}
#[get("/ciphers/<uuid>/admin")]
@ -235,6 +243,13 @@ pub struct CipherData {
LastKnownRevisionDate: Option<String>,
}
#[derive(Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct PartialCipherData {
FolderId: Option<String>,
Favorite: bool,
}
#[derive(Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct Attachments2Data {
@ -314,7 +329,7 @@ async fn post_ciphers(
update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &ip, &nt, UpdateType::SyncCipherCreate)
.await?;
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &mut conn).await))
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await))
}
/// Enforces the personal ownership policy on user-owned ciphers, if applicable.
@ -646,7 +661,51 @@ async fn put_cipher(
update_cipher_from_data(&mut cipher, data, &headers, false, &mut conn, &ip, &nt, UpdateType::SyncCipherUpdate)
.await?;
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &mut conn).await))
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await))
}
#[post("/ciphers/<uuid>/partial", data = "<data>")]
async fn post_cipher_partial(
uuid: String,
data: JsonUpcase<PartialCipherData>,
headers: Headers,
conn: DbConn,
) -> JsonResult {
put_cipher_partial(uuid, data, headers, conn).await
}
// Only update the folder and favorite for the user, since this cipher is read-only
#[put("/ciphers/<uuid>/partial", data = "<data>")]
async fn put_cipher_partial(
uuid: String,
data: JsonUpcase<PartialCipherData>,
headers: Headers,
mut conn: DbConn,
) -> JsonResult {
let data: PartialCipherData = data.into_inner().data;
let cipher = match Cipher::find_by_uuid(&uuid, &mut conn).await {
Some(cipher) => cipher,
None => err!("Cipher doesn't exist"),
};
if let Some(ref folder_id) = data.FolderId {
match Folder::find_by_uuid(folder_id, &mut conn).await {
Some(folder) => {
if folder.user_uuid != headers.user.uuid {
err!("Folder is not owned by user")
}
}
None => err!("Folder doesn't exist"),
}
}
// Move cipher
cipher.move_to_folder(data.FolderId.clone(), &headers.user.uuid, &mut conn).await?;
// Update favorite
cipher.set_favorite(Some(data.Favorite), &headers.user.uuid, &mut conn).await?;
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await))
}
#[derive(Deserialize)]
@ -873,7 +932,7 @@ async fn share_cipher_by_uuid(
update_cipher_from_data(&mut cipher, data.Cipher, headers, shared_to_collection, conn, ip, nt, ut).await?;
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, conn).await))
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, conn).await))
}
/// v2 API for downloading an attachment. This just redirects the client to
@ -942,7 +1001,7 @@ async fn post_attachment_v2(
"AttachmentId": attachment_id,
"Url": url,
"FileUploadType": FileUploadType::Direct as i32,
response_key: cipher.to_json(&headers.host, &headers.user.uuid, None, &mut conn).await,
response_key: cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await,
})))
}
@ -1135,7 +1194,7 @@ async fn post_attachment(
let (cipher, mut conn) = save_attachment(attachment, uuid, data, &headers, conn, ip, nt).await?;
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, &mut conn).await))
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, &mut conn).await))
}
#[post("/ciphers/<uuid>/attachment-admin", format = "multipart/form-data", data = "<data>")]
@ -1616,7 +1675,7 @@ async fn _restore_cipher_by_uuid(
.await;
}
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, conn).await))
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, None, CipherSyncType::User, conn).await))
}
async fn _restore_multiple_ciphers(
@ -1716,6 +1775,7 @@ pub struct CipherSyncData {
pub user_group_full_access_for_organizations: HashSet<String>,
}
#[derive(Eq, PartialEq)]
pub enum CipherSyncType {
User,
Organization,