Skip to content

Commit cdfdc6f

Browse files
authored
Fix Org Import duplicate collections (#5200)
This fixes an issue with collections be duplicated same as was an issue with folders. Also made some optimizations by using HashSet where possible and device the Vec/Hash capacity. And instead of passing objects only use the UUID which was the only value we needed. Also found an issue with importing a personal export via the Org import where folders are used. Since Org's do not use folder we needed to clear those out, same as Bitwarden does. Fixes #5193 Signed-off-by: BlackDex <[email protected]>
1 parent 2393c3f commit cdfdc6f

File tree

3 files changed

+27
-27
lines changed

3 files changed

+27
-27
lines changed

src/api/core/ciphers.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ pub struct CipherData {
222222
// Id is optional as it is included only in bulk share
223223
pub id: Option<String>,
224224
// Folder id is not included in import
225-
folder_id: Option<String>,
225+
pub folder_id: Option<String>,
226226
// TODO: Some of these might appear all the time, no need for Option
227227
#[serde(alias = "organizationID")]
228228
pub organization_id: Option<String>,
@@ -585,11 +585,11 @@ async fn post_ciphers_import(
585585
Cipher::validate_cipher_data(&data.ciphers)?;
586586

587587
// Read and create the folders
588-
let existing_folders: Vec<String> =
589-
Folder::find_by_user(&headers.user.uuid, &mut conn).await.into_iter().map(|f| f.uuid).collect();
588+
let existing_folders: HashSet<Option<String>> =
589+
Folder::find_by_user(&headers.user.uuid, &mut conn).await.into_iter().map(|f| Some(f.uuid)).collect();
590590
let mut folders: Vec<String> = Vec::with_capacity(data.folders.len());
591591
for folder in data.folders.into_iter() {
592-
let folder_uuid = if folder.id.is_some() && existing_folders.contains(folder.id.as_ref().unwrap()) {
592+
let folder_uuid = if existing_folders.contains(&folder.id) {
593593
folder.id.unwrap()
594594
} else {
595595
let mut new_folder = Folder::new(headers.user.uuid.clone(), folder.name);
@@ -601,8 +601,8 @@ async fn post_ciphers_import(
601601
}
602602

603603
// Read the relations between folders and ciphers
604+
// Ciphers can only be in one folder at the same time
604605
let mut relations_map = HashMap::with_capacity(data.folder_relationships.len());
605-
606606
for relation in data.folder_relationships {
607607
relations_map.insert(relation.key, relation.value);
608608
}

src/api/core/organizations.rs

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use crate::{
1111
},
1212
auth::{decode_invite, AdminHeaders, ClientVersion, Headers, ManagerHeaders, ManagerHeadersLoose, OwnerHeaders},
1313
db::{models::*, DbConn},
14-
error::Error,
1514
mail,
1615
util::{convert_json_key_lcase_first, NumberOrString},
1716
CONFIG,
@@ -127,6 +126,7 @@ struct NewCollectionData {
127126
name: String,
128127
groups: Vec<NewCollectionObjectData>,
129128
users: Vec<NewCollectionObjectData>,
129+
id: Option<String>,
130130
external_id: Option<String>,
131131
}
132132

@@ -1598,40 +1598,43 @@ async fn post_org_import(
15981598
// TODO: See if we can optimize the whole cipher adding/importing and prevent duplicate code and checks.
15991599
Cipher::validate_cipher_data(&data.ciphers)?;
16001600

1601-
let mut collections = Vec::new();
1601+
let existing_collections: HashSet<Option<String>> =
1602+
Collection::find_by_organization(&org_id, &mut conn).await.into_iter().map(|c| (Some(c.uuid))).collect();
1603+
let mut collections: Vec<String> = Vec::with_capacity(data.collections.len());
16021604
for coll in data.collections {
1603-
let collection = Collection::new(org_id.clone(), coll.name, coll.external_id);
1604-
if collection.save(&mut conn).await.is_err() {
1605-
collections.push(Err(Error::new("Failed to create Collection", "Failed to create Collection")));
1605+
let collection_uuid = if existing_collections.contains(&coll.id) {
1606+
coll.id.unwrap()
16061607
} else {
1607-
collections.push(Ok(collection));
1608-
}
1608+
let new_collection = Collection::new(org_id.clone(), coll.name, coll.external_id);
1609+
new_collection.save(&mut conn).await?;
1610+
new_collection.uuid
1611+
};
1612+
1613+
collections.push(collection_uuid);
16091614
}
16101615

16111616
// Read the relations between collections and ciphers
1612-
let mut relations = Vec::new();
1617+
// Ciphers can be in multiple collections at the same time
1618+
let mut relations = Vec::with_capacity(data.collection_relationships.len());
16131619
for relation in data.collection_relationships {
16141620
relations.push((relation.key, relation.value));
16151621
}
16161622

16171623
let headers: Headers = headers.into();
16181624

1619-
let mut ciphers = Vec::new();
1620-
for cipher_data in data.ciphers {
1625+
let mut ciphers: Vec<String> = Vec::with_capacity(data.ciphers.len());
1626+
for mut cipher_data in data.ciphers {
1627+
// Always clear folder_id's via an organization import
1628+
cipher_data.folder_id = None;
16211629
let mut cipher = Cipher::new(cipher_data.r#type, cipher_data.name.clone());
16221630
update_cipher_from_data(&mut cipher, cipher_data, &headers, None, &mut conn, &nt, UpdateType::None).await.ok();
1623-
ciphers.push(cipher);
1631+
ciphers.push(cipher.uuid);
16241632
}
16251633

16261634
// Assign the collections
16271635
for (cipher_index, coll_index) in relations {
1628-
let cipher_id = &ciphers[cipher_index].uuid;
1629-
let coll = &collections[coll_index];
1630-
let coll_id = match coll {
1631-
Ok(coll) => coll.uuid.as_str(),
1632-
Err(_) => err!("Failed to assign to collection"),
1633-
};
1634-
1636+
let cipher_id = &ciphers[cipher_index];
1637+
let coll_id = &collections[coll_index];
16351638
CollectionCipher::save(cipher_id, coll_id, &mut conn).await?;
16361639
}
16371640

src/api/core/two_factor/duo_oidc.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,7 @@ impl DuoClient {
211211
nonce,
212212
};
213213

214-
let token = match self.encode_duo_jwt(jwt_payload) {
215-
Ok(token) => token,
216-
Err(e) => return Err(e),
217-
};
214+
let token = self.encode_duo_jwt(jwt_payload)?;
218215

219216
let authz_endpoint = format!("https://{}/oauth/v1/authorize", self.api_host);
220217
let mut auth_url = match Url::parse(authz_endpoint.as_str()) {

0 commit comments

Comments
 (0)