|
|
@ -1,6 +1,7 @@
|
|
|
|
use std::collections::{HashMap, HashSet};
|
|
|
|
use std::collections::{HashMap, HashSet};
|
|
|
|
use std::path::Path;
|
|
|
|
use std::path::Path;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use chrono::{NaiveDateTime, Utc};
|
|
|
|
use rocket::{http::ContentType, request::Form, Data, Route};
|
|
|
|
use rocket::{http::ContentType, request::Form, Data, Route};
|
|
|
|
use rocket_contrib::json::Json;
|
|
|
|
use rocket_contrib::json::Json;
|
|
|
|
use serde_json::Value;
|
|
|
|
use serde_json::Value;
|
|
|
@ -194,6 +195,14 @@ pub struct CipherData {
|
|
|
|
#[serde(rename = "Attachments")]
|
|
|
|
#[serde(rename = "Attachments")]
|
|
|
|
_Attachments: Option<Value>, // Unused, contains map of {id: filename}
|
|
|
|
_Attachments: Option<Value>, // Unused, contains map of {id: filename}
|
|
|
|
Attachments2: Option<HashMap<String, Attachments2Data>>,
|
|
|
|
Attachments2: Option<HashMap<String, Attachments2Data>>,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// The revision datetime (in ISO 8601 format) of the client's local copy
|
|
|
|
|
|
|
|
// of the cipher. This is used to prevent a client from updating a cipher
|
|
|
|
|
|
|
|
// when it doesn't have the latest version, as that can result in data
|
|
|
|
|
|
|
|
// loss. It's not an error when no value is provided; this can happen
|
|
|
|
|
|
|
|
// when using older client versions, or if the operation doesn't involve
|
|
|
|
|
|
|
|
// updating an existing cipher.
|
|
|
|
|
|
|
|
LastKnownRevisionDate: Option<String>,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
|
@ -238,6 +247,17 @@ pub fn update_cipher_from_data(
|
|
|
|
nt: &Notify,
|
|
|
|
nt: &Notify,
|
|
|
|
ut: UpdateType,
|
|
|
|
ut: UpdateType,
|
|
|
|
) -> EmptyResult {
|
|
|
|
) -> EmptyResult {
|
|
|
|
|
|
|
|
// Check that the client isn't updating an existing cipher with stale data.
|
|
|
|
|
|
|
|
if let Some(dt) = data.LastKnownRevisionDate {
|
|
|
|
|
|
|
|
match NaiveDateTime::parse_from_str(&dt, "%+") { // ISO 8601 format
|
|
|
|
|
|
|
|
Err(err) =>
|
|
|
|
|
|
|
|
warn!("Error parsing LastKnownRevisionDate '{}': {}", dt, err),
|
|
|
|
|
|
|
|
Ok(dt) if cipher.updated_at.signed_duration_since(dt).num_seconds() > 1 =>
|
|
|
|
|
|
|
|
err!("The client copy of this cipher is out of date. Resync the client and try again."),
|
|
|
|
|
|
|
|
Ok(_) => (),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if cipher.organization_uuid.is_some() && cipher.organization_uuid != data.OrganizationId {
|
|
|
|
if cipher.organization_uuid.is_some() && cipher.organization_uuid != data.OrganizationId {
|
|
|
|
err!("Organization mismatch. Please resync the client before updating the cipher")
|
|
|
|
err!("Organization mismatch. Please resync the client before updating the cipher")
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -1030,7 +1050,7 @@ fn _delete_cipher_by_uuid(uuid: &str, headers: &Headers, conn: &DbConn, soft_del
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if soft_delete {
|
|
|
|
if soft_delete {
|
|
|
|
cipher.deleted_at = Some(chrono::Utc::now().naive_utc());
|
|
|
|
cipher.deleted_at = Some(Utc::now().naive_utc());
|
|
|
|
cipher.save(&conn)?;
|
|
|
|
cipher.save(&conn)?;
|
|
|
|
nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(&conn));
|
|
|
|
nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(&conn));
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|