commit 6364c0578989d8ad374f91e98edee66352dbf553
parent f71f10eac6f9111217e9ed8016ce9baf9b06804e
Author: Daniel GarcĂa <dani-garcia@users.noreply.github.com>
Date: Tue, 27 Nov 2018 17:24:12 +0100
Fix attachments during key rotation, add individual attachment key
Diffstat:
6 files changed, 90 insertions(+), 35 deletions(-)
diff --git a/migrations/2018-11-27-152651_add_att_key_columns/down.sql b/migrations/2018-11-27-152651_add_att_key_columns/down.sql
diff --git a/migrations/2018-11-27-152651_add_att_key_columns/up.sql b/migrations/2018-11-27-152651_add_att_key_columns/up.sql
@@ -0,0 +1,3 @@
+ALTER TABLE attachments
+ ADD COLUMN
+ key TEXT;
+\ No newline at end of file
diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs
@@ -1,4 +1,4 @@
-use std::collections::HashSet;
+use std::collections::{HashSet, HashMap};
use std::path::Path;
use rocket::http::ContentType;
@@ -162,6 +162,18 @@ pub struct CipherData {
Favorite: Option<bool>,
PasswordHistory: Option<Value>,
+
+ // These are used during key rotation
+ #[serde(rename = "Attachments")]
+ _Attachments: Option<Value>, // Unused, contains map of {id: filename}
+ Attachments2: Option<HashMap<String, Attachments2Data>>
+}
+
+#[derive(Deserialize, Debug)]
+#[allow(non_snake_case)]
+pub struct Attachments2Data {
+ FileName: String,
+ Key: String,
}
#[post("/ciphers/admin", data = "<data>")]
@@ -221,6 +233,28 @@ pub fn update_cipher_from_data(cipher: &mut Cipher, data: CipherData, headers: &
}
}
+ // Modify attachments name and keys when rotating
+ if let Some(attachments) = data.Attachments2 {
+ for (id, attachment) in attachments {
+ let mut saved_att = match Attachment::find_by_id(&id, &conn) {
+ Some(att) => att,
+ None => err!("Attachment doesn't exist")
+ };
+
+ if saved_att.cipher_uuid != cipher.uuid {
+ err!("Attachment is not owned by the cipher")
+ }
+
+ saved_att.key = Some(attachment.Key);
+ saved_att.file_name = attachment.FileName;
+
+ match saved_att.save(&conn) {
+ Ok(()) => (),
+ Err(_) => err!("Failed to save attachment")
+ };
+ }
+ }
+
let type_data_opt = match data.Type {
1 => data.Login,
2 => data.SecureNote,
@@ -252,7 +286,7 @@ pub fn update_cipher_from_data(cipher: &mut Cipher, data: CipherData, headers: &
match cipher.save(&conn) {
Ok(()) => (),
- Err(_) => println!("Error: Failed to save cipher")
+ Err(_) => err!("Failed to save cipher")
};
ws.send_cipher_update(ut, &cipher, &cipher.update_users_revision(&conn));
@@ -299,7 +333,6 @@ fn post_ciphers_import(data: JsonUpcase<ImportData>, headers: Headers, conn: DbC
}
// Read the relations between folders and ciphers
- use std::collections::HashMap;
let mut relations_map = HashMap::new();
for relation in data.FolderRelationships {
@@ -542,37 +575,52 @@ fn post_attachment(uuid: String, data: Data, content_type: &ContentType, headers
let base_path = Path::new(&CONFIG.attachments_folder).join(&cipher.uuid);
+ let mut attachment_key = None;
+
Multipart::with_body(data.open(), boundary).foreach_entry(|mut field| {
- // This is provided by the client, don't trust it
- let name = field.headers.filename.expect("No filename provided");
-
- let file_name = HEXLOWER.encode(&crypto::get_random(vec![0; 10]));
- let path = base_path.join(&file_name);
-
- let size = match field.data.save()
- .memory_threshold(0)
- .size_limit(None)
- .with_path(path) {
- SaveResult::Full(SavedData::File(_, size)) => size as i32,
- SaveResult::Full(other) => {
- println!("Attachment is not a file: {:?}", other);
- return;
+ match field.headers.name.as_str() {
+ "key" => {
+ use std::io::Read;
+ let mut key_buffer = String::new();
+ if field.data.read_to_string(&mut key_buffer).is_ok() {
+ attachment_key = Some(key_buffer);
+ }
},
- SaveResult::Partial(_, reason) => {
- println!("Partial result: {:?}", reason);
- return;
+ "data" => {
+ // This is provided by the client, don't trust it
+ let name = field.headers.filename.expect("No filename provided");
+
+ let file_name = HEXLOWER.encode(&crypto::get_random(vec![0; 10]));
+ let path = base_path.join(&file_name);
+
+ let size = match field.data.save()
+ .memory_threshold(0)
+ .size_limit(None)
+ .with_path(path) {
+ SaveResult::Full(SavedData::File(_, size)) => size as i32,
+ SaveResult::Full(other) => {
+ println!("Attachment is not a file: {:?}", other);
+ return;
+ },
+ SaveResult::Partial(_, reason) => {
+ println!("Partial result: {:?}", reason);
+ return;
+ },
+ SaveResult::Error(e) => {
+ println!("Error: {:?}", e);
+ return;
+ }
+ };
+
+ let mut attachment = Attachment::new(file_name, cipher.uuid.clone(), name, size);
+ attachment.key = attachment_key.clone();
+ match attachment.save(&conn) {
+ Ok(()) => (),
+ Err(_) => println!("Error: failed to save attachment")
+ };
},
- SaveResult::Error(e) => {
- println!("Error: {:?}", e);
- return;
- }
- };
-
- let attachment = Attachment::new(file_name, cipher.uuid.clone(), name, size);
- match attachment.save(&conn) {
- Ok(()) => (),
- Err(_) => println!("Error: failed to save attachment")
- };
+ _ => println!("Error: invalid multipart name")
+ }
}).expect("Error processing multipart data");
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn)))
@@ -793,6 +841,6 @@ fn _delete_cipher_attachment_by_id(uuid: &str, attachment_id: &str, headers: &He
ws.send_cipher_update(UpdateType::SyncCipherDelete, &cipher, &cipher.update_users_revision(&conn));
Ok(())
}
- Err(_) => err!("Deleting attachement failed")
+ Err(_) => err!("Deleting attachment failed")
}
}
diff --git a/src/db/models/attachment.rs b/src/db/models/attachment.rs
@@ -12,6 +12,7 @@ pub struct Attachment {
pub cipher_uuid: String,
pub file_name: String,
pub file_size: i32,
+ pub key: Option<String>
}
/// Local methods
@@ -22,6 +23,7 @@ impl Attachment {
cipher_uuid,
file_name,
file_size,
+ key: None
}
}
@@ -41,6 +43,7 @@ impl Attachment {
"FileName": self.file_name,
"Size": self.file_size.to_string(),
"SizeName": display_size,
+ "Key": self.key,
"Object": "attachment"
})
}
@@ -92,8 +95,8 @@ impl Attachment {
}
pub fn delete_all_by_cipher(cipher_uuid: &str, conn: &DbConn) -> QueryResult<()> {
- for attachement in Attachment::find_by_cipher(&cipher_uuid, &conn) {
- attachement.delete(&conn)?;
+ for attachment in Attachment::find_by_cipher(&cipher_uuid, &conn) {
+ attachment.delete(&conn)?;
}
Ok(())
}
diff --git a/src/db/models/device.rs b/src/db/models/device.rs
@@ -71,7 +71,6 @@ impl Device {
let time_now = Utc::now().naive_utc();
self.updated_at = time_now;
-
let orgowner: Vec<_> = orgs.iter().filter(|o| o.type_ == 0).map(|o| o.org_uuid.clone()).collect();
let orgadmin: Vec<_> = orgs.iter().filter(|o| o.type_ == 1).map(|o| o.org_uuid.clone()).collect();
let orguser: Vec<_> = orgs.iter().filter(|o| o.type_ == 2).map(|o| o.org_uuid.clone()).collect();
diff --git a/src/db/schema.rs b/src/db/schema.rs
@@ -4,6 +4,7 @@ table! {
cipher_uuid -> Text,
file_name -> Text,
file_size -> Integer,
+ key -> Nullable<Text>,
}
}