mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2024-11-24 13:55:40 +03:00
Implement organization import for admins and owners (Fixes #178)
This commit is contained in:
parent
4d2c6e39b2
commit
f397f0cbd0
3 changed files with 80 additions and 14 deletions
|
@ -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")
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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.")
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue