diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs index f9e090c..b0bf574 100644 --- a/src/api/core/ciphers.rs +++ b/src/api/core/ciphers.rs @@ -86,7 +86,7 @@ fn get_cipher_details(uuid: String, headers: Headers, conn: DbConn) -> JsonResul #[derive(Deserialize, Debug)] #[allow(non_snake_case)] -struct CipherData { +pub struct CipherData { // Id is optional as it is included only in bulk share Id: Option, // Folder id is not included in import @@ -100,8 +100,8 @@ struct CipherData { Card = 3, Identity = 4 */ - Type: i32, // TODO: Change this to NumberOrString - Name: String, + pub Type: i32, // TODO: Change this to NumberOrString + pub Name: String, Notes: Option, Fields: Option, @@ -132,7 +132,7 @@ fn post_ciphers(data: JsonUpcase, headers: Headers, conn: DbConn) -> Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn))) } -fn update_cipher_from_data(cipher: &mut Cipher, data: CipherData, headers: &Headers, shared_to_collection: bool, conn: &DbConn) -> EmptyResult { +pub fn update_cipher_from_data(cipher: &mut Cipher, data: CipherData, headers: &Headers, shared_to_collection: bool, conn: &DbConn) -> EmptyResult { if let Some(org_id) = data.OrganizationId { match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn) { None => err!("You don't have permission to add item to organization"), @@ -560,7 +560,7 @@ fn delete_cipher_selected(data: JsonUpcase, headers: Headers, conn: DbCon let uuids = match data.get("Ids") { Some(ids) => match ids.as_array() { - Some(ids) => ids.iter().filter_map(|uuid| { uuid.as_str() }), + Some(ids) => ids.iter().filter_map(Value::as_str), None => err!("Posted ids field is not an array") }, None => err!("Request missing ids field") @@ -606,7 +606,7 @@ fn move_cipher_selected(data: JsonUpcase, headers: Headers, conn: DbConn) let uuids = match data.get("Ids") { Some(ids) => match ids.as_array() { - Some(ids) => ids.iter().filter_map(|uuid| { uuid.as_str() }), + Some(ids) => ids.iter().filter_map(Value::as_str), None => err!("Posted ids field is not an array") }, None => err!("Request missing ids field") diff --git a/src/api/core/mod.rs b/src/api/core/mod.rs index d2ad208..31e4d60 100644 --- a/src/api/core/mod.rs +++ b/src/api/core/mod.rs @@ -110,6 +110,7 @@ pub fn routes() -> Vec { put_organization_user, delete_user, post_delete_user, + post_org_import, clear_device_token, put_device_token, diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index 5852ea1..4d47eab 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -1,5 +1,3 @@ -#![allow(unused_imports)] - use rocket_contrib::{Json, Value}; use CONFIG; use db::DbConn; @@ -140,9 +138,8 @@ fn get_user_collections(headers: Headers, conn: DbConn) -> JsonResult { "Data": Collection::find_by_user_uuid(&headers.user.uuid, &conn) .iter() - .map(|collection| { - collection.to_json() - }).collect::(), + .map(Collection::to_json) + .collect::(), "Object": "list" }))) } @@ -153,9 +150,8 @@ fn get_org_collections(org_id: String, _headers: AdminHeaders, conn: DbConn) -> "Data": Collection::find_by_organization(&org_id, &conn) .iter() - .map(|collection| { - collection.to_json() - }).collect::(), + .map(Collection::to_json) + .collect::(), "Object": "list" }))) } @@ -582,4 +578,73 @@ fn delete_user(org_id: String, org_user_id: String, headers: AdminHeaders, conn: #[post("/organizations//users//delete")] fn post_delete_user(org_id: String, org_user_id: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult { delete_user(org_id, org_user_id, headers, conn) +} + +use super::ciphers::CipherData; +use super::ciphers::update_cipher_from_data; + +#[derive(Deserialize)] +#[allow(non_snake_case)] +struct ImportData { + Ciphers: Vec, + Collections: Vec, + CollectionRelationships: Vec, +} + +#[derive(Deserialize)] +#[allow(non_snake_case)] +struct RelationsData { + // Cipher index + Key: usize, + // Collection index + Value: usize, +} + +#[post("/ciphers/import-organization?", data = "")] +fn post_org_import(query: OrgIdData, data: JsonUpcase, headers: Headers, conn: DbConn) -> EmptyResult { + let data: ImportData = data.into_inner().data; + let org_id = query.organizationId; + + let org_user = match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn) { + Some(user) => user, + None => err!("User is not part of the organization") + }; + + if org_user.type_ > UserOrgType::Admin as i32 { + err!("Only admins or owners can import into an organization") + } + + // Read and create the collections + let collections: Vec<_> = data.Collections.into_iter().map(|coll| { + let mut collection = Collection::new(org_id.clone(), coll.Name); + collection.save(&conn); + collection + }).collect(); + + // Read the relations between collections and ciphers + let mut relations = Vec::new(); + for relation in data.CollectionRelationships { + relations.push((relation.Key, relation.Value)); + } + + // Read and create the ciphers + let ciphers: Vec<_> = data.Ciphers.into_iter().map(|cipher_data| { + let mut cipher = Cipher::new(cipher_data.Type, cipher_data.Name.clone()); + update_cipher_from_data(&mut cipher, cipher_data, &headers, false, &conn).ok(); + cipher + }).collect(); + + // Assign the collections + for (cipher_index, coll_index) in relations { + let cipher_id = &ciphers[cipher_index].uuid; + let coll_id = &collections[coll_index].uuid; + + CollectionCipher::save(cipher_id, coll_id, &conn); + } + + let mut user = headers.user; + match user.update_revision(&conn) { + Ok(()) => Ok(()), + Err(_) => err!("Failed to update the revision, please log out and log back in to finish import.") + } } \ No newline at end of file