|
|
@ -91,7 +91,9 @@ fn sync(data: Form<SyncData>, headers: Headers, conn: DbConn) -> JsonResult {
|
|
|
|
let folders_json: Vec<Value> = folders.iter().map(Folder::to_json).collect();
|
|
|
|
let folders_json: Vec<Value> = folders.iter().map(Folder::to_json).collect();
|
|
|
|
|
|
|
|
|
|
|
|
let collections = Collection::find_by_user_uuid(&headers.user.uuid, &conn);
|
|
|
|
let collections = Collection::find_by_user_uuid(&headers.user.uuid, &conn);
|
|
|
|
let collections_json: Vec<Value> = collections.iter().map(Collection::to_json).collect();
|
|
|
|
let collections_json: Vec<Value> = collections.iter()
|
|
|
|
|
|
|
|
.map(|c| c.to_json_details(&headers.user.uuid, &conn))
|
|
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
|
|
let policies = OrgPolicy::find_by_user(&headers.user.uuid, &conn);
|
|
|
|
let policies = OrgPolicy::find_by_user(&headers.user.uuid, &conn);
|
|
|
|
let policies_json: Vec<Value> = policies.iter().map(OrgPolicy::to_json).collect();
|
|
|
|
let policies_json: Vec<Value> = policies.iter().map(OrgPolicy::to_json).collect();
|
|
|
@ -225,6 +227,12 @@ fn post_ciphers_admin(data: JsonUpcase<ShareCipherData>, headers: Headers, conn:
|
|
|
|
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 {
|
|
|
|
let mut data: ShareCipherData = data.into_inner().data;
|
|
|
|
let mut data: ShareCipherData = data.into_inner().data;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check if there are one more more collections selected when this cipher is part of an organization.
|
|
|
|
|
|
|
|
// err if this is not the case before creating an empty cipher.
|
|
|
|
|
|
|
|
if data.Cipher.OrganizationId.is_some() && data.CollectionIds.is_empty() {
|
|
|
|
|
|
|
|
err!("You must select at least one collection.");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// This check is usually only needed in update_cipher_from_data(), but we
|
|
|
|
// This check is usually only needed in update_cipher_from_data(), but we
|
|
|
|
// need it here as well to avoid creating an empty cipher in the call to
|
|
|
|
// need it here as well to avoid creating an empty cipher in the call to
|
|
|
|
// cipher.save() below.
|
|
|
|
// cipher.save() below.
|
|
|
@ -323,6 +331,11 @@ pub fn update_cipher_from_data(
|
|
|
|
|| cipher.is_write_accessible_to_user(&headers.user.uuid, &conn)
|
|
|
|
|| cipher.is_write_accessible_to_user(&headers.user.uuid, &conn)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
cipher.organization_uuid = Some(org_id);
|
|
|
|
cipher.organization_uuid = Some(org_id);
|
|
|
|
|
|
|
|
// After some discussion in PR #1329 re-added the user_uuid = None again.
|
|
|
|
|
|
|
|
// TODO: Audit/Check the whole save/update cipher chain.
|
|
|
|
|
|
|
|
// Upstream uses the user_uuid to allow a cipher added by a user to an org to still allow the user to view/edit the cipher
|
|
|
|
|
|
|
|
// even when the user has hide-passwords configured as there policy.
|
|
|
|
|
|
|
|
// Removing the line below would fix that, but we have to check which effect this would have on the rest of the code.
|
|
|
|
cipher.user_uuid = None;
|
|
|
|
cipher.user_uuid = None;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
err!("You don't have permission to add cipher directly to organization")
|
|
|
|
err!("You don't have permission to add cipher directly to organization")
|
|
|
@ -366,6 +379,23 @@ pub fn update_cipher_from_data(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Cleanup cipher data, like removing the 'Response' key.
|
|
|
|
|
|
|
|
// This key is somewhere generated during Javascript so no way for us this fix this.
|
|
|
|
|
|
|
|
// Also, upstream only retrieves keys they actually want to store, and thus skip the 'Response' key.
|
|
|
|
|
|
|
|
// We do not mind which data is in it, the keep our model more flexible when there are upstream changes.
|
|
|
|
|
|
|
|
// But, we at least know we do not need to store and return this specific key.
|
|
|
|
|
|
|
|
fn _clean_cipher_data(mut json_data: Value) -> Value {
|
|
|
|
|
|
|
|
if json_data.is_array() {
|
|
|
|
|
|
|
|
json_data.as_array_mut()
|
|
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
|
|
.iter_mut()
|
|
|
|
|
|
|
|
.for_each(|ref mut f| {
|
|
|
|
|
|
|
|
f.as_object_mut().unwrap().remove("Response");
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
json_data
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let type_data_opt = match data.Type {
|
|
|
|
let type_data_opt = match data.Type {
|
|
|
|
1 => data.Login,
|
|
|
|
1 => data.Login,
|
|
|
|
2 => data.SecureNote,
|
|
|
|
2 => data.SecureNote,
|
|
|
@ -374,23 +404,22 @@ pub fn update_cipher_from_data(
|
|
|
|
_ => err!("Invalid type"),
|
|
|
|
_ => err!("Invalid type"),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let mut type_data = match type_data_opt {
|
|
|
|
let type_data = match type_data_opt {
|
|
|
|
Some(data) => data,
|
|
|
|
Some(mut data) => {
|
|
|
|
|
|
|
|
// Remove the 'Response' key from the base object.
|
|
|
|
|
|
|
|
data.as_object_mut().unwrap().remove("Response");
|
|
|
|
|
|
|
|
// Remove the 'Response' key from every Uri.
|
|
|
|
|
|
|
|
if data["Uris"].is_array() {
|
|
|
|
|
|
|
|
data["Uris"] = _clean_cipher_data(data["Uris"].clone());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
data
|
|
|
|
|
|
|
|
},
|
|
|
|
None => err!("Data missing"),
|
|
|
|
None => err!("Data missing"),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: ******* Backwards compat start **********
|
|
|
|
|
|
|
|
// To remove backwards compatibility, just delete this code,
|
|
|
|
|
|
|
|
// and remove the compat code from cipher::to_json
|
|
|
|
|
|
|
|
type_data["Name"] = Value::String(data.Name.clone());
|
|
|
|
|
|
|
|
type_data["Notes"] = data.Notes.clone().map(Value::String).unwrap_or(Value::Null);
|
|
|
|
|
|
|
|
type_data["Fields"] = data.Fields.clone().unwrap_or(Value::Null);
|
|
|
|
|
|
|
|
type_data["PasswordHistory"] = data.PasswordHistory.clone().unwrap_or(Value::Null);
|
|
|
|
|
|
|
|
// TODO: ******* Backwards compat end **********
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cipher.name = data.Name;
|
|
|
|
cipher.name = data.Name;
|
|
|
|
cipher.notes = data.Notes;
|
|
|
|
cipher.notes = data.Notes;
|
|
|
|
cipher.fields = data.Fields.map(|f| f.to_string());
|
|
|
|
cipher.fields = data.Fields.map(|f| _clean_cipher_data(f).to_string() );
|
|
|
|
cipher.data = type_data.to_string();
|
|
|
|
cipher.data = type_data.to_string();
|
|
|
|
cipher.password_history = data.PasswordHistory.map(|f| f.to_string());
|
|
|
|
cipher.password_history = data.PasswordHistory.map(|f| f.to_string());
|
|
|
|
|
|
|
|
|
|
|
@ -1064,7 +1093,6 @@ fn delete_all(
|
|
|
|
Some(user_org) => {
|
|
|
|
Some(user_org) => {
|
|
|
|
if user_org.atype == UserOrgType::Owner {
|
|
|
|
if user_org.atype == UserOrgType::Owner {
|
|
|
|
Cipher::delete_all_by_organization(&org_data.org_id, &conn)?;
|
|
|
|
Cipher::delete_all_by_organization(&org_data.org_id, &conn)?;
|
|
|
|
Collection::delete_all_by_organization(&org_data.org_id, &conn)?;
|
|
|
|
|
|
|
|
nt.send_user_update(UpdateType::Vault, &user);
|
|
|
|
nt.send_user_update(UpdateType::Vault, &user);
|
|
|
|
Ok(())
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|