Implement organization import for admins and owners (Fixes #178)

This commit is contained in:
Daniel García 2018-09-13 15:16:24 +02:00
parent 4d2c6e39b2
commit f397f0cbd0
No known key found for this signature in database
GPG key ID: FC8A7D14C3CD543A
3 changed files with 80 additions and 14 deletions

View file

@ -86,7 +86,7 @@ fn get_cipher_details(uuid: String, headers: Headers, conn: DbConn) -> JsonResul
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[allow(non_snake_case)] #[allow(non_snake_case)]
struct CipherData { pub struct CipherData {
// Id is optional as it is included only in bulk share // Id is optional as it is included only in bulk share
Id: Option<String>, Id: Option<String>,
// Folder id is not included in import // Folder id is not included in import
@ -100,8 +100,8 @@ struct CipherData {
Card = 3, Card = 3,
Identity = 4 Identity = 4
*/ */
Type: i32, // TODO: Change this to NumberOrString pub Type: i32, // TODO: Change this to NumberOrString
Name: String, pub Name: String,
Notes: Option<String>, Notes: Option<String>,
Fields: Option<Value>, Fields: Option<Value>,
@ -132,7 +132,7 @@ fn post_ciphers(data: JsonUpcase<CipherData>, headers: Headers, conn: DbConn) ->
Ok(Json(cipher.to_json(&headers.host, &headers.user.uuid, &conn))) 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 { if let Some(org_id) = data.OrganizationId {
match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, &conn) { 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"), None => err!("You don't have permission to add item to organization"),
@ -560,7 +560,7 @@ fn delete_cipher_selected(data: JsonUpcase<Value>, headers: Headers, conn: DbCon
let uuids = match data.get("Ids") { let uuids = match data.get("Ids") {
Some(ids) => match ids.as_array() { 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!("Posted ids field is not an array")
}, },
None => err!("Request missing ids field") None => err!("Request missing ids field")
@ -606,7 +606,7 @@ fn move_cipher_selected(data: JsonUpcase<Value>, headers: Headers, conn: DbConn)
let uuids = match data.get("Ids") { let uuids = match data.get("Ids") {
Some(ids) => match ids.as_array() { 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!("Posted ids field is not an array")
}, },
None => err!("Request missing ids field") None => err!("Request missing ids field")

View file

@ -110,6 +110,7 @@ pub fn routes() -> Vec<Route> {
put_organization_user, put_organization_user,
delete_user, delete_user,
post_delete_user, post_delete_user,
post_org_import,
clear_device_token, clear_device_token,
put_device_token, put_device_token,

View file

@ -1,5 +1,3 @@
#![allow(unused_imports)]
use rocket_contrib::{Json, Value}; use rocket_contrib::{Json, Value};
use CONFIG; use CONFIG;
use db::DbConn; use db::DbConn;
@ -140,9 +138,8 @@ fn get_user_collections(headers: Headers, conn: DbConn) -> JsonResult {
"Data": "Data":
Collection::find_by_user_uuid(&headers.user.uuid, &conn) Collection::find_by_user_uuid(&headers.user.uuid, &conn)
.iter() .iter()
.map(|collection| { .map(Collection::to_json)
collection.to_json() .collect::<Value>(),
}).collect::<Value>(),
"Object": "list" "Object": "list"
}))) })))
} }
@ -153,9 +150,8 @@ fn get_org_collections(org_id: String, _headers: AdminHeaders, conn: DbConn) ->
"Data": "Data":
Collection::find_by_organization(&org_id, &conn) Collection::find_by_organization(&org_id, &conn)
.iter() .iter()
.map(|collection| { .map(Collection::to_json)
collection.to_json() .collect::<Value>(),
}).collect::<Value>(),
"Object": "list" "Object": "list"
}))) })))
} }
@ -582,4 +578,73 @@ fn delete_user(org_id: String, org_user_id: String, headers: AdminHeaders, conn:
#[post("/organizations/<org_id>/users/<org_user_id>/delete")] #[post("/organizations/<org_id>/users/<org_user_id>/delete")]
fn post_delete_user(org_id: String, org_user_id: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult { 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) 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<CipherData>,
Collections: Vec<NewCollectionData>,
CollectionRelationships: Vec<RelationsData>,
}
#[derive(Deserialize)]
#[allow(non_snake_case)]
struct RelationsData {
// Cipher index
Key: usize,
// Collection index
Value: usize,
}
#[post("/ciphers/import-organization?<query>", data = "<data>")]
fn post_org_import(query: OrgIdData, data: JsonUpcase<ImportData>, 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.")
}
} }