Browse Source

Extend cipher response models to include cipher, cipherMini, cipherDetails, and cipherMiniDetails

pull/991/head
theycallmesteve 5 years ago
parent
commit
efa07fa918
No known key found for this signature in database GPG Key ID: 6240923F65CC698D
  1. 63
      src/api/core/ciphers.rs
  2. 2
      src/api/core/organizations.rs
  3. 93
      src/db/models/cipher.rs

63
src/api/core/ciphers.rs

@ -91,7 +91,7 @@ fn sync(data: Form<SyncData>, headers: Headers, conn: DbConn) -> JsonResult {
let ciphers = Cipher::find_by_user(&headers.user.uuid, &conn); let ciphers = Cipher::find_by_user(&headers.user.uuid, &conn);
let ciphers_json: Vec<Value> = ciphers let ciphers_json: Vec<Value> = ciphers
.iter() .iter()
.map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)) .map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn, "cipherDetails"))
.collect(); .collect();
let domains_json = if data.exclude_domains { let domains_json = if data.exclude_domains {
@ -117,7 +117,7 @@ fn get_ciphers(headers: Headers, conn: DbConn) -> JsonResult {
let ciphers_json: Vec<Value> = ciphers let ciphers_json: Vec<Value> = ciphers
.iter() .iter()
.map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)) .map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn, "cipherDetails"))
.collect(); .collect();
Ok(Json(json!({ Ok(Json(json!({
@ -129,6 +129,10 @@ fn get_ciphers(headers: Headers, conn: DbConn) -> JsonResult {
#[get("/ciphers/<uuid>")] #[get("/ciphers/<uuid>")]
fn get_cipher(uuid: String, headers: Headers, conn: DbConn) -> JsonResult { fn get_cipher(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
_get_cipher(uuid, headers, conn, "cipher")
}
fn _get_cipher(uuid: String, headers: Headers, conn: DbConn, resp_model: &str) -> JsonResult {
let cipher = match Cipher::find_by_uuid(&uuid, &conn) { let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
Some(cipher) => cipher, Some(cipher) => cipher,
None => err!("Cipher doesn't exist"), None => err!("Cipher doesn't exist"),
@ -138,18 +142,18 @@ fn get_cipher(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
err!("Cipher is not owned by user") err!("Cipher is not owned by user")
} }
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn))) Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn, resp_model)))
} }
#[get("/ciphers/<uuid>/admin")] #[get("/ciphers/<uuid>/admin")]
fn get_cipher_admin(uuid: String, headers: Headers, conn: DbConn) -> JsonResult { fn get_cipher_admin(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
// TODO: Implement this correctly // TODO: Implement this correctly
get_cipher(uuid, headers, conn) _get_cipher(uuid, headers, conn, "cipherMini")
} }
#[get("/ciphers/<uuid>/details")] #[get("/ciphers/<uuid>/details")]
fn get_cipher_details(uuid: String, headers: Headers, conn: DbConn) -> JsonResult { fn get_cipher_details(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
get_cipher(uuid, headers, conn) _get_cipher(uuid, headers, conn, "cipherDetails")
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@ -198,18 +202,22 @@ pub struct Attachments2Data {
#[post("/ciphers/admin", data = "<data>")] #[post("/ciphers/admin", data = "<data>")]
fn post_ciphers_admin(data: JsonUpcase<ShareCipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult { fn post_ciphers_admin(data: JsonUpcase<ShareCipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
_post_ciphers_admin(data, headers, conn, nt, "cipherMini")
}
fn _post_ciphers_admin(data: JsonUpcase<ShareCipherData>, headers: Headers, conn: DbConn, nt: Notify, resp_model: &str) -> JsonResult {
let data: ShareCipherData = data.into_inner().data; let data: ShareCipherData = data.into_inner().data;
let mut cipher = Cipher::new(data.Cipher.Type, data.Cipher.Name.clone()); let mut cipher = Cipher::new(data.Cipher.Type, data.Cipher.Name.clone());
cipher.user_uuid = Some(headers.user.uuid.clone()); cipher.user_uuid = Some(headers.user.uuid.clone());
cipher.save(&conn)?; cipher.save(&conn)?;
share_cipher_by_uuid(&cipher.uuid, data, &headers, &conn, &nt) share_cipher_by_uuid(&cipher.uuid, data, &headers, &conn, &nt, &resp_model)
} }
#[post("/ciphers/create", data = "<data>")] #[post("/ciphers/create", data = "<data>")]
fn post_ciphers_create(data: JsonUpcase<ShareCipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult { fn post_ciphers_create(data: JsonUpcase<ShareCipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
post_ciphers_admin(data, headers, conn, nt) _post_ciphers_admin(data, headers, conn, nt, "cipher")
} }
#[post("/ciphers", data = "<data>")] #[post("/ciphers", data = "<data>")]
@ -219,7 +227,7 @@ fn post_ciphers(data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt
let mut cipher = Cipher::new(data.Type, data.Name.clone()); let mut cipher = Cipher::new(data.Type, data.Name.clone());
update_cipher_from_data(&mut cipher, data, &headers, false, &conn, &nt, UpdateType::CipherCreate)?; update_cipher_from_data(&mut cipher, data, &headers, false, &conn, &nt, UpdateType::CipherCreate)?;
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn))) Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn, "cipher")))
} }
pub fn update_cipher_from_data( pub fn update_cipher_from_data(
@ -385,7 +393,7 @@ fn put_cipher_admin(
conn: DbConn, conn: DbConn,
nt: Notify, nt: Notify,
) -> JsonResult { ) -> JsonResult {
put_cipher(uuid, data, headers, conn, nt) _put_cipher(uuid, data, headers, conn, nt, "cipherMini")
} }
#[post("/ciphers/<uuid>/admin", data = "<data>")] #[post("/ciphers/<uuid>/admin", data = "<data>")]
@ -396,16 +404,20 @@ fn post_cipher_admin(
conn: DbConn, conn: DbConn,
nt: Notify, nt: Notify,
) -> JsonResult { ) -> JsonResult {
post_cipher(uuid, data, headers, conn, nt) _put_cipher(uuid, data, headers, conn, nt, "cipherMini")
} }
#[post("/ciphers/<uuid>", data = "<data>")] #[post("/ciphers/<uuid>", data = "<data>")]
fn post_cipher(uuid: String, data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult { fn post_cipher(uuid: String, data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
put_cipher(uuid, data, headers, conn, nt) _put_cipher(uuid, data, headers, conn, nt, "cipher")
} }
#[put("/ciphers/<uuid>", data = "<data>")] #[put("/ciphers/<uuid>", data = "<data>")]
fn put_cipher(uuid: String, data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult { fn put_cipher(uuid: String, data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt: Notify) -> JsonResult {
_put_cipher(uuid, data, headers, conn, nt, "cipher")
}
fn _put_cipher(uuid: String, data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn, nt: Notify, resp_model: &str) -> JsonResult {
let data: CipherData = data.into_inner().data; let data: CipherData = data.into_inner().data;
let mut cipher = match Cipher::find_by_uuid(&uuid, &conn) { let mut cipher = match Cipher::find_by_uuid(&uuid, &conn) {
@ -419,7 +431,7 @@ fn put_cipher(uuid: String, data: JsonUpcase<CipherData>, headers: Headers, conn
update_cipher_from_data(&mut cipher, data, &headers, false, &conn, &nt, UpdateType::CipherUpdate)?; update_cipher_from_data(&mut cipher, data, &headers, false, &conn, &nt, UpdateType::CipherUpdate)?;
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn))) Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn, resp_model)))
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -522,7 +534,7 @@ fn post_cipher_share(
) -> JsonResult { ) -> JsonResult {
let data: ShareCipherData = data.into_inner().data; let data: ShareCipherData = data.into_inner().data;
share_cipher_by_uuid(&uuid, data, &headers, &conn, &nt) share_cipher_by_uuid(&uuid, data, &headers, &conn, &nt, "cipher")
} }
#[put("/ciphers/<uuid>/share", data = "<data>")] #[put("/ciphers/<uuid>/share", data = "<data>")]
@ -535,7 +547,7 @@ fn put_cipher_share(
) -> JsonResult { ) -> JsonResult {
let data: ShareCipherData = data.into_inner().data; let data: ShareCipherData = data.into_inner().data;
share_cipher_by_uuid(&uuid, data, &headers, &conn, &nt) share_cipher_by_uuid(&uuid, data, &headers, &conn, &nt, "cipher")
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -583,7 +595,7 @@ fn put_cipher_share_seleted(
}; };
match shared_cipher_data.Cipher.Id.take() { match shared_cipher_data.Cipher.Id.take() {
Some(id) => share_cipher_by_uuid(&id, shared_cipher_data, &headers, &conn, &nt)?, Some(id) => share_cipher_by_uuid(&id, shared_cipher_data, &headers, &conn, &nt, "cipher")?,
None => err!("Request missing ids field"), None => err!("Request missing ids field"),
}; };
} }
@ -597,6 +609,7 @@ fn share_cipher_by_uuid(
headers: &Headers, headers: &Headers,
conn: &DbConn, conn: &DbConn,
nt: &Notify, nt: &Notify,
resp_model: &str,
) -> JsonResult { ) -> JsonResult {
let mut cipher = match Cipher::find_by_uuid(&uuid, &conn) { let mut cipher = match Cipher::find_by_uuid(&uuid, &conn) {
Some(cipher) => { Some(cipher) => {
@ -643,7 +656,7 @@ fn share_cipher_by_uuid(
UpdateType::CipherUpdate, UpdateType::CipherUpdate,
)?; )?;
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn))) Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn, resp_model)))
} }
#[post("/ciphers/<uuid>/attachment", format = "multipart/form-data", data = "<data>")] #[post("/ciphers/<uuid>/attachment", format = "multipart/form-data", data = "<data>")]
@ -654,6 +667,18 @@ fn post_attachment(
headers: Headers, headers: Headers,
conn: DbConn, conn: DbConn,
nt: Notify, nt: Notify,
) -> JsonResult {
_post_attachment(uuid, data, content_type, headers, conn, nt, "cipher")
}
fn _post_attachment(
uuid: String,
data: Data,
content_type: &ContentType,
headers: Headers,
conn: DbConn,
nt: Notify,
resp_model: &str,
) -> JsonResult { ) -> JsonResult {
let cipher = match Cipher::find_by_uuid(&uuid, &conn) { let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
Some(cipher) => cipher, Some(cipher) => cipher,
@ -752,7 +777,7 @@ fn post_attachment(
nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(&conn)); nt.send_cipher_update(UpdateType::CipherUpdate, &cipher, &cipher.update_users_revision(&conn));
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn))) Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn, &resp_model)))
} }
#[post("/ciphers/<uuid>/attachment-admin", format = "multipart/form-data", data = "<data>")] #[post("/ciphers/<uuid>/attachment-admin", format = "multipart/form-data", data = "<data>")]
@ -764,7 +789,7 @@ fn post_attachment_admin(
conn: DbConn, conn: DbConn,
nt: Notify, nt: Notify,
) -> JsonResult { ) -> JsonResult {
post_attachment(uuid, data, content_type, headers, conn, nt) _post_attachment(uuid, data, content_type, headers, conn, nt, "cipherMini")
} }
#[post("/ciphers/<uuid>/attachment/<attachment_id>/share", format = "multipart/form-data", data = "<data>")] #[post("/ciphers/<uuid>/attachment/<attachment_id>/share", format = "multipart/form-data", data = "<data>")]
@ -778,7 +803,7 @@ fn post_attachment_share(
nt: Notify, nt: Notify,
) -> JsonResult { ) -> JsonResult {
_delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &nt)?; _delete_cipher_attachment_by_id(&uuid, &attachment_id, &headers, &conn, &nt)?;
post_attachment(uuid, data, content_type, headers, conn, nt) _post_attachment(uuid, data, content_type, headers, conn, nt, "cipher")
} }
#[post("/ciphers/<uuid>/attachment/<attachment_id>/delete-admin")] #[post("/ciphers/<uuid>/attachment/<attachment_id>/delete-admin")]

2
src/api/core/organizations.rs

@ -425,7 +425,7 @@ fn get_org_details(data: Form<OrgIdData>, headers: Headers, conn: DbConn) -> Jso
let ciphers = Cipher::find_by_org(&data.organization_id, &conn); let ciphers = Cipher::find_by_org(&data.organization_id, &conn);
let ciphers_json: Vec<Value> = ciphers let ciphers_json: Vec<Value> = ciphers
.iter() .iter()
.map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn)) .map(|c| c.to_json(&headers.host, &headers.user.uuid, &conn, "cipherMiniDetails"))
.collect(); .collect();
Ok(Json(json!({ Ok(Json(json!({

93
src/db/models/cipher.rs

@ -73,49 +73,77 @@ use crate::error::MapResult;
/// Database methods /// Database methods
impl Cipher { impl Cipher {
pub fn to_json(&self, host: &str, user_uuid: &str, conn: &DbConn) -> Value { pub fn to_json(&self, host: &str, user_uuid: &str, conn: &DbConn, resp_model: &str) -> Value {
use crate::util::format_date; use crate::util::format_date;
let attachments = Attachment::find_by_cipher(&self.uuid, conn); let attachments = Attachment::find_by_cipher(&self.uuid, conn);
let attachments_json: Vec<Value> = attachments.iter().map(|c| c.to_json(host)).collect(); let attachments_json = if attachments.is_empty() {Value::Null} else {attachments.iter().map(|c| c.to_json(host)).collect()};
let fields_json = self.fields.as_ref().and_then(|s| serde_json::from_str(s).ok()).unwrap_or(Value::Null); let mut fields_json = self.fields.as_ref().and_then(|s| serde_json::from_str(s).ok()).unwrap_or(Value::Null);
let password_history_json = self.password_history.as_ref().and_then(|s| serde_json::from_str(s).ok()).unwrap_or(Value::Null); let password_history_json = self.password_history.as_ref().and_then(|s| serde_json::from_str(s).ok()).unwrap_or(Value::Null);
// Get the data or a default empty value to avoid issues with the mobile apps // Get the data or a default empty value of common items to avoid issues with the mobile apps
let mut data_json: Value = serde_json::from_str(&self.data).unwrap_or_else(|_| json!({ let mut data_json: Value = serde_json::from_str(&self.data).unwrap_or_else(|_| json!({
"Fields":null, "Fields": null,
"Name": self.name, "Name": self.name,
"Notes":null, "Notes": null,
"Password":null, "PasswordHistory": null
"PasswordHistory":null,
"PasswordRevisionDate":null,
"Response":null,
"Totp":null,
"Uris":null,
"Username":null
})); }));
// TODO: ******* Backwards compat start ********** // TODO: ******* Backwards compat start **********
// To remove backwards compatibility, just remove this entire section // To remove backwards compatibility, just remove this entire section
// and remove the compat code from ciphers::update_cipher_from_data // and remove the compat code from ciphers::update_cipher_from_data
if self.atype == 1 && data_json["Uris"].is_array() { if self.atype == 1 && (data_json["Uris"].is_array() || data_json["Uris"].is_null()) {
let uri = data_json["Uris"][0]["Uri"].clone(); data_json["Uri"] = data_json["Uris"][0]["Uri"].clone();
data_json["Uri"] = uri;
} }
// TODO: ******* Backwards compat end ********** // TODO: ******* Backwards compat end **********
// Make sure a SecureNote doesn't end up null
if self.atype == 2 && !data_json["Type"].is_i64() {
data_json["Type"] = json!(0);
}
// Do not include "Response" in data_json
data_json.as_object_mut().unwrap().remove("Response");
// Do not include "Response" in data_json.Fields
if data_json["Fields"].is_array() {
data_json["Fields"].as_array_mut()
.unwrap()
.iter_mut()
.for_each(|ref mut f| {
f.as_object_mut().unwrap().remove("Response");
});
};
// Do not include "Response" in fields_json
if fields_json.is_array() {
fields_json.as_array_mut()
.unwrap()
.iter_mut()
.for_each(|ref mut f| {
f.as_object_mut().unwrap().remove("Response");
});
};
// Do not include "Response" in Uris
if data_json["Uris"].is_array() {
data_json["Uris"].as_array_mut()
.unwrap()
.iter_mut()
.for_each(|ref mut f| {
f.as_object_mut().unwrap().remove("Response");
});
};
let mut json_object = json!({ let mut json_object = json!({
"Id": self.uuid, "Id": self.uuid,
"Type": self.atype, "Type": self.atype,
"RevisionDate": format_date(&self.updated_at), "RevisionDate": format_date(&self.updated_at),
"DeletedDate": self.deleted_at.map_or(Value::Null, |d| Value::String(format_date(&d))), "DeletedDate": self.deleted_at.map_or(Value::Null, |d| Value::String(format_date(&d))),
"FolderId": self.get_folder_uuid(&user_uuid, &conn),
"Favorite": self.favorite,
"OrganizationId": self.organization_uuid, "OrganizationId": self.organization_uuid,
"Attachments": attachments_json, "Attachments": attachments_json,
"OrganizationUseTotp": true, "OrganizationUseTotp": self.organization_uuid.is_some(),
"CollectionIds": self.get_collections(user_uuid, &conn),
"Name": self.name, "Name": self.name,
"Notes": self.notes, "Notes": self.notes,
@ -123,12 +151,33 @@ impl Cipher {
"Data": data_json, "Data": data_json,
"Object": "cipher", "Object": resp_model,
"Edit": true,
"PasswordHistory": password_history_json, "PasswordHistory": password_history_json,
// These are all included, but only gets populated
"Login": null,
"Card": null,
"Identity": null,
"SecureNote": null,
}); });
// Add additional items based on the requested response model
if resp_model == "cipher" || resp_model == "cipherDetails" {
json_object["FolderId"] = json!(self.get_folder_uuid(&user_uuid, &conn));
json_object["Favorite"] = json!(self.favorite);
json_object["Edit"] = json!(self.is_write_accessible_to_user(user_uuid, &conn));
}
if resp_model == "cipherDetails" || resp_model == "cipherMiniDetails" {
json_object["CollectionIds"] = json!(self.get_collections(user_uuid, &conn));
}
// data_json is reused for the json_object[key] value, but do not include the backwards compatibility items
data_json.as_object_mut().unwrap().remove("Fields");
data_json.as_object_mut().unwrap().remove("Name");
data_json.as_object_mut().unwrap().remove("Notes");
data_json.as_object_mut().unwrap().remove("PasswordHistory");
let key = match self.atype { let key = match self.atype {
1 => "Login", 1 => "Login",
2 => "SecureNote", 2 => "SecureNote",

Loading…
Cancel
Save