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_json: Vec<Value> = ciphers
.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();
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
.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();
Ok(Json(json!({
@ -129,6 +129,10 @@ fn get_ciphers(headers: Headers, conn: DbConn) -> JsonResult {
#[get("/ciphers/<uuid>")]
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) {
Some(cipher) => cipher,
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")
}
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")]
fn get_cipher_admin(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
// TODO: Implement this correctly
get_cipher(uuid, headers, conn)
_get_cipher(uuid, headers, conn, "cipherMini")
}
#[get("/ciphers/<uuid>/details")]
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)]
@ -198,18 +202,22 @@ pub struct Attachments2Data {
#[post("/ciphers/admin", data = "<data>")]
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 mut cipher = Cipher::new(data.Cipher.Type, data.Cipher.Name.clone());
cipher.user_uuid = Some(headers.user.uuid.clone());
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>")]
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>")]
@ -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());
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(
@ -385,7 +393,7 @@ fn put_cipher_admin(
conn: DbConn,
nt: Notify,
) -> JsonResult {
put_cipher(uuid, data, headers, conn, nt)
_put_cipher(uuid, data, headers, conn, nt, "cipherMini")
}
#[post("/ciphers/<uuid>/admin", data = "<data>")]
@ -396,16 +404,20 @@ fn post_cipher_admin(
conn: DbConn,
nt: Notify,
) -> JsonResult {
post_cipher(uuid, data, headers, conn, nt)
_put_cipher(uuid, data, headers, conn, nt, "cipherMini")
}
#[post("/ciphers/<uuid>", data = "<data>")]
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>")]
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 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)?;
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)]
@ -522,7 +534,7 @@ fn post_cipher_share(
) -> JsonResult {
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>")]
@ -535,7 +547,7 @@ fn put_cipher_share(
) -> JsonResult {
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)]
@ -583,7 +595,7 @@ fn put_cipher_share_seleted(
};
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"),
};
}
@ -597,6 +609,7 @@ fn share_cipher_by_uuid(
headers: &Headers,
conn: &DbConn,
nt: &Notify,
resp_model: &str,
) -> JsonResult {
let mut cipher = match Cipher::find_by_uuid(&uuid, &conn) {
Some(cipher) => {
@ -643,7 +656,7 @@ fn share_cipher_by_uuid(
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>")]
@ -654,6 +667,18 @@ fn post_attachment(
headers: Headers,
conn: DbConn,
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 {
let cipher = match Cipher::find_by_uuid(&uuid, &conn) {
Some(cipher) => cipher,
@ -752,7 +777,7 @@ fn post_attachment(
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>")]
@ -764,7 +789,7 @@ fn post_attachment_admin(
conn: DbConn,
nt: Notify,
) -> 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>")]
@ -778,7 +803,7 @@ fn post_attachment_share(
nt: Notify,
) -> JsonResult {
_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")]

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_json: Vec<Value> = ciphers
.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();
Ok(Json(json!({

93
src/db/models/cipher.rs

@ -73,49 +73,77 @@ use crate::error::MapResult;
/// Database methods
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;
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);
// 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!({
"Fields":null,
"Fields": null,
"Name": self.name,
"Notes":null,
"Password":null,
"PasswordHistory":null,
"PasswordRevisionDate":null,
"Response":null,
"Totp":null,
"Uris":null,
"Username":null
"Notes": null,
"PasswordHistory": null
}));
// TODO: ******* Backwards compat start **********
// To remove backwards compatibility, just remove this entire section
// and remove the compat code from ciphers::update_cipher_from_data
if self.atype == 1 && data_json["Uris"].is_array() {
let uri = data_json["Uris"][0]["Uri"].clone();
data_json["Uri"] = uri;
if self.atype == 1 && (data_json["Uris"].is_array() || data_json["Uris"].is_null()) {
data_json["Uri"] = data_json["Uris"][0]["Uri"].clone();
}
// 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!({
"Id": self.uuid,
"Type": self.atype,
"RevisionDate": format_date(&self.updated_at),
"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,
"Attachments": attachments_json,
"OrganizationUseTotp": true,
"CollectionIds": self.get_collections(user_uuid, &conn),
"OrganizationUseTotp": self.organization_uuid.is_some(),
"Name": self.name,
"Notes": self.notes,
@ -123,12 +151,33 @@ impl Cipher {
"Data": data_json,
"Object": "cipher",
"Edit": true,
"Object": resp_model,
"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 {
1 => "Login",
2 => "SecureNote",

Loading…
Cancel
Save