Skip to content

Commit 58a9bf9

Browse files
authored
[PM-21691] - Added tree and test with implementation for CollectionView (#279)
## 🎟️ Tracking [PM-21691](https://bitwarden.atlassian.net/browse/PM-21691) ## 📔 Objective This splits the collections code out of the vault crate and adds a Tree implementation for representing the parent child relationship between nested collections. This ideally will then be used as a unified representation for the nested collection structure across all clients, instead of each one having to implement their own.
1 parent 49d027c commit 58a9bf9

File tree

21 files changed

+672
-62
lines changed

21 files changed

+672
-62
lines changed

Cargo.lock

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ bitwarden-api-api = { path = "crates/bitwarden-api-api", version = "=1.0.0" }
2323
bitwarden-api-identity = { path = "crates/bitwarden-api-identity", version = "=1.0.0" }
2424
bitwarden-auth = { path = "crates/bitwarden-auth", version = "=1.0.0" }
2525
bitwarden-cli = { path = "crates/bitwarden-cli", version = "=1.0.0" }
26+
bitwarden-collections = { path = "crates/bitwarden-collections", version = "=1.0.0" }
2627
bitwarden-core = { path = "crates/bitwarden-core", version = "=1.0.0" }
2728
bitwarden-crypto = { path = "crates/bitwarden-crypto", version = "=1.0.0" }
2829
bitwarden-error = { path = "crates/bitwarden-error", version = "=1.0.0" }

README.md

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -52,22 +52,20 @@ You can also browse the latest published documentation:
5252
The project is structured as a monorepo using cargo workspaces. Some of the more noteworthy crates
5353
are:
5454

55-
- [`bitwarden-api-api`](./crates/bitwarden-api-api/): Auto-generated API bindings for the API
56-
server.
57-
- [`bitwarden-api-identity`](./crates/bitwarden-api-identity/): Auto-generated API bindings for the
55+
- [`bitwarden-api-api`](./crates/bitwarden-api-api): Auto-generated API bindings for the API server.
56+
- [`bitwarden-api-identity`](./crates/bitwarden-api-identity): Auto-generated API bindings for the
5857
Identity server.
59-
- [`bitwarden-core`](./crates/bitwarden-core/): The core functionality consumed by the other crates.
60-
- [`bitwarden-crypto`](./crates/bitwarden-crypto/): Crypto library.
61-
- [`bitwarden-wasm-internal`](./crates/bitwarden-wasm-internal/): WASM bindings for the internal
62-
SDK.
63-
- [`bitwarden-uniffi`](./crates/bitwarden-uniffi/): Mobile bindings for swift and kotlin using
58+
- [`bitwarden-core`](./crates/bitwarden-core): The core functionality consumed by the other crates.
59+
- [`bitwarden-crypto`](./crates/bitwarden-crypto): Crypto library.
60+
- [`bitwarden-wasm-internal`](./crates/bitwarden-wasm-internal): WASM bindings for the internal SDK.
61+
- [`bitwarden-uniffi`](./crates/bitwarden-uniffi): Mobile bindings for swift and kotlin using
6462
[UniFFI](https://github.com/mozilla/uniffi-rs/).
6563

6664
## API Bindings
6765

68-
We autogenerate the server bindings using
69-
[openapi-generator](https://github.com/OpenAPITools/openapi-generator). To do this we first need to
70-
build the internal swagger documentation.
66+
We autogenerate the server bindings
67+
using[openapi-generator](https://github.com/OpenAPITools/openapi-generator). To do this, we first
68+
need to build the internal swagger documentation.
7169

7270
### Swagger generation
7371

@@ -83,15 +81,16 @@ ASPNETCORE_ENVIRONMENT=development dotnet swagger tofile --output ../../identity
8381

8482
### OpenApi Generator
8583

86-
To generate a new version of the bindings run the following script from the root of the SDK project.
84+
To generate a new version of the bindings, run the following script from the root of the SDK
85+
project.
8786

8887
```bash
8988
./support/build-api.sh
9089
```
9190

92-
This project uses customized templates which lives in the `support/openapi-templates` directory.
93-
These templates resolves some outstanding issues we've experienced with the rust generator. But we
94-
strive towards modifying the templates as little as possible to ease future upgrades.
91+
This project uses customized templates that live in the `support/openapi-templates` directory. These
92+
templates resolve some outstanding issues we've experienced with the rust generator. But we strive
93+
towards modifying the templates as little as possible to ease future upgrades.
9594

9695
### Note
9796

@@ -102,9 +101,9 @@ strive towards modifying the templates as little as possible to ease future upgr
102101

103102
## Developer tools
104103

105-
This project recommends the use of certain developer tools, and also includes configurations for
106-
them to make developers lives easier. The use of these tools is optional and they might require a
107-
separate installation step.
104+
This project recommends the use of certain developer tools and includes configurations for them to
105+
make developers' lives easier. The use of these tools is optional, and they might require a separate
106+
installation step.
108107

109108
The list of developer tools is:
110109

@@ -122,11 +121,11 @@ The list of developer tools is:
122121
## Formatting & Linting
123122

124123
This repository uses various tools to check formatting and linting before it's merged. It's
125-
recommended to run the checks prior to submitting a PR.
124+
recommended to run the checks before submitting a PR.
126125

127126
### Installation
128127

129-
Please see the [lint.yml](./.github/workflows/lint.yml) file for example installation commands &
128+
Please see the [lint.yml](./.github/workflows/lint.yml) file, for example, installation commands and
130129
versions. Here are the cli tools we use:
131130

132131
- Nightly [cargo fmt](https://github.com/rust-lang/rustfmt) and
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
[package]
2+
name = "bitwarden-collections"
3+
version.workspace = true
4+
authors.workspace = true
5+
edition.workspace = true
6+
rust-version.workspace = true
7+
homepage.workspace = true
8+
repository.workspace = true
9+
license-file.workspace = true
10+
readme.workspace = true
11+
keywords.workspace = true
12+
13+
[features]
14+
uniffi = [
15+
"bitwarden-core/uniffi",
16+
"bitwarden-crypto/uniffi",
17+
"dep:uniffi"
18+
] # Uniffi bindings
19+
wasm = [
20+
"bitwarden-core/wasm",
21+
"dep:tsify",
22+
"dep:wasm-bindgen"
23+
] # WASM support
24+
25+
[dependencies]
26+
bitwarden-api-api = { workspace = true }
27+
bitwarden-core = { workspace = true, features = ["internal"] }
28+
bitwarden-crypto = { workspace = true }
29+
bitwarden-error = { workspace = true }
30+
serde = { workspace = true }
31+
thiserror = { workspace = true }
32+
tsify = { workspace = true, optional = true }
33+
uniffi = { workspace = true, optional = true }
34+
uuid = { workspace = true }
35+
wasm-bindgen = { workspace = true, optional = true }
36+
37+
[lints]
38+
workspace = true
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Bitwarden Collections
2+
3+
Defines the data model for collections both encrypted and decrypted. It also handles conversions
4+
between those two states by implementing `Encryptable`. It also provides `Tree` struct which allows
5+
all structs implementing `TreeItem` to be represented in a tree structure along with functions to
6+
access each node.

crates/bitwarden-vault/src/collection.rs renamed to crates/bitwarden-collections/src/collection.rs

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,52 +6,42 @@ use bitwarden_core::{
66
use bitwarden_crypto::{CryptoError, Decryptable, EncString, IdentifyKey, KeyStoreContext};
77
use serde::{Deserialize, Serialize};
88
use uuid::Uuid;
9+
#[cfg(feature = "wasm")]
10+
use {tsify::Tsify, wasm_bindgen::prelude::*};
911

10-
use crate::VaultParseError;
12+
use crate::{error::CollectionsParseError, tree::TreeItem};
1113

1214
#[allow(missing_docs)]
1315
#[derive(Serialize, Deserialize, Debug)]
1416
#[serde(rename_all = "camelCase", deny_unknown_fields)]
1517
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
16-
#[cfg_attr(
17-
feature = "wasm",
18-
derive(tsify::Tsify),
19-
tsify(into_wasm_abi, from_wasm_abi)
20-
)]
18+
#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
2119
pub struct Collection {
2220
pub id: Option<Uuid>,
2321
pub organization_id: Uuid,
24-
2522
pub name: EncString,
26-
2723
pub external_id: Option<String>,
2824
pub hide_passwords: bool,
2925
pub read_only: bool,
3026
pub manage: bool,
3127
}
3228

3329
#[allow(missing_docs)]
34-
#[derive(Serialize, Deserialize, Debug)]
30+
#[derive(Serialize, Deserialize, Debug, Clone)]
3531
#[serde(rename_all = "camelCase", deny_unknown_fields)]
3632
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
33+
#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
3734
pub struct CollectionView {
3835
pub id: Option<Uuid>,
3936
pub organization_id: Uuid,
40-
4137
pub name: String,
42-
4338
pub external_id: Option<String>,
4439
pub hide_passwords: bool,
4540
pub read_only: bool,
4641
pub manage: bool,
4742
}
4843

49-
impl IdentifyKey<SymmetricKeyId> for Collection {
50-
fn key_identifier(&self) -> SymmetricKeyId {
51-
SymmetricKeyId::Organization(self.organization_id)
52-
}
53-
}
54-
44+
#[allow(missing_docs)]
5545
impl Decryptable<KeyIds, SymmetricKeyId, CollectionView> for Collection {
5646
fn decrypt(
5747
&self,
@@ -61,9 +51,7 @@ impl Decryptable<KeyIds, SymmetricKeyId, CollectionView> for Collection {
6151
Ok(CollectionView {
6252
id: self.id,
6353
organization_id: self.organization_id,
64-
6554
name: self.name.decrypt(ctx, key).ok().unwrap_or_default(),
66-
6755
external_id: self.external_id.clone(),
6856
hide_passwords: self.hide_passwords,
6957
read_only: self.read_only,
@@ -72,8 +60,9 @@ impl Decryptable<KeyIds, SymmetricKeyId, CollectionView> for Collection {
7260
}
7361
}
7462

63+
#[allow(missing_docs)]
7564
impl TryFrom<CollectionDetailsResponseModel> for Collection {
76-
type Error = VaultParseError;
65+
type Error = CollectionsParseError;
7766

7867
fn try_from(collection: CollectionDetailsResponseModel) -> Result<Self, Self::Error> {
7968
Ok(Collection {
@@ -87,3 +76,30 @@ impl TryFrom<CollectionDetailsResponseModel> for Collection {
8776
})
8877
}
8978
}
79+
80+
#[allow(missing_docs)]
81+
impl IdentifyKey<SymmetricKeyId> for Collection {
82+
fn key_identifier(&self) -> SymmetricKeyId {
83+
SymmetricKeyId::Organization(self.organization_id)
84+
}
85+
}
86+
87+
#[allow(missing_docs)]
88+
impl TreeItem for CollectionView {
89+
fn id(&self) -> Uuid {
90+
self.id.unwrap_or_default()
91+
}
92+
93+
fn short_name(&self) -> &str {
94+
self.path().last().unwrap_or(&"")
95+
}
96+
97+
fn path(&self) -> Vec<&str> {
98+
self.name
99+
.split(Self::DELIMITER)
100+
.filter(|s| !s.is_empty())
101+
.collect::<Vec<&str>>()
102+
}
103+
104+
const DELIMITER: char = '/';
105+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use bitwarden_error::bitwarden_error;
2+
use thiserror::Error;
3+
4+
#[allow(missing_docs)]
5+
#[bitwarden_error(flat)]
6+
#[derive(Debug, Error)]
7+
pub enum CollectionDecryptError {
8+
#[error(transparent)]
9+
Crypto(#[from] bitwarden_crypto::CryptoError),
10+
}
11+
12+
#[allow(missing_docs)]
13+
#[derive(Debug, Error)]
14+
pub enum CollectionsParseError {
15+
#[error(transparent)]
16+
Crypto(#[from] bitwarden_crypto::CryptoError),
17+
#[error(transparent)]
18+
MissingFieldError(#[from] bitwarden_core::MissingFieldError),
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#![doc = include_str!("../README.md")]
2+
3+
#[cfg(feature = "uniffi")]
4+
uniffi::setup_scaffolding!();
5+
#[cfg(feature = "uniffi")]
6+
mod uniffi_support;
7+
8+
///
9+
/// Module containing the collection data models. It also contains the implementations for
10+
/// Encryptable, TryFrom, and TreeItem
11+
pub mod collection;
12+
///
13+
/// Module containing the error types.
14+
pub mod error;
15+
///
16+
/// Module containing Tree struct that is a tree representation of all structs implementing TreeItem
17+
/// trait. It is made using an index vector to hold the data and another vector to hold the
18+
/// parent child relationships between those nodes.
19+
pub mod tree;

0 commit comments

Comments
 (0)