Skip to main content

bitwarden_importers/importers/onepassword/access/
model.rs

1//! What a download yields: vaults holding items, each item still in its decrypted 1Password shape.
2//!
3//! Mapping these onto Bitwarden ciphers is the importer's job and happens elsewhere.
4
5use super::wire::{VaultItemDetails, VaultItemOverview};
6
7/// Everything recovered from an account, including source data that could not be read.
8pub struct DownloadedAccount {
9    /// Vaults that were opened, possibly with individual skipped items.
10    pub vaults: Vec<Vault>,
11    /// Vaults that could not be opened.
12    pub skipped_vaults: Vec<SkippedVault>,
13}
14
15/// A decrypted vault with its items.
16pub struct Vault {
17    /// The vault's 1Password uuid. The import goes by name.
18    #[allow(dead_code)]
19    pub id: String,
20    /// The vault's display name.
21    pub name: String,
22    /// Every item in the vault except the trashed ones.
23    pub items: Vec<Item>,
24    /// Non-trashed items whose encrypted payloads could not be read.
25    pub skipped_items: Vec<SkippedItem>,
26}
27
28/// A vault that could not be opened.
29#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
30#[cfg_attr(
31    feature = "wasm",
32    derive(serde::Serialize, serde::Deserialize, tsify::Tsify)
33)]
34pub struct SkippedVault {
35    /// The vault's 1Password uuid.
36    pub id: String,
37    /// The active item count advertised by 1Password, when present.
38    pub item_count: Option<u32>,
39    /// Why the vault could not be opened.
40    pub reason: SkippedReason,
41}
42
43/// An item that could not be read from an otherwise accessible vault.
44#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
45#[cfg_attr(
46    feature = "wasm",
47    derive(serde::Serialize, serde::Deserialize, tsify::Tsify)
48)]
49pub struct SkippedItem {
50    /// The item's 1Password uuid.
51    pub id: String,
52    /// The title, when the overview decrypted before another payload failed.
53    pub name: Option<String>,
54    /// The item's category, available without decrypting its payloads.
55    pub category: ItemCategory,
56    /// Why the item could not be read.
57    pub reason: SkippedReason,
58}
59
60/// A safe, structured reason for leaving source data unimported.
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
63#[cfg_attr(
64    feature = "wasm",
65    derive(serde::Serialize, serde::Deserialize, tsify::Tsify)
66)]
67pub enum SkippedReason {
68    /// The account does not have the key or permission required to read the data.
69    NoAccess,
70    /// The source data is encrypted with a scheme the importer does not support.
71    Unsupported,
72}
73
74/// A decrypted item: its identity plus both payloads exactly as 1Password sends them.
75pub struct Item {
76    /// The item's 1Password uuid. The server assigns imported ciphers their own.
77    #[allow(dead_code)]
78    pub id: String,
79    /// The item's category, derived from its template id.
80    pub category: ItemCategory,
81    /// The decrypted `encOverview`: title, websites, tags.
82    pub overview: VaultItemOverview,
83    /// The decrypted `encDetails`: login fields, sections, note.
84    pub details: VaultItemDetails,
85}
86
87/// The kind of a vault item, mapped from its template id. The ids are 1Password's standard
88/// category template UUIDs; an unrecognized id is preserved as [`ItemCategory::Unknown`] so nothing
89/// is lost.
90#[derive(Debug, Clone, PartialEq, Eq)]
91#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
92#[cfg_attr(
93    feature = "wasm",
94    derive(serde::Serialize, serde::Deserialize, tsify::Tsify)
95)]
96pub enum ItemCategory {
97    /// Template `001`.
98    Login,
99    /// Template `002`.
100    CreditCard,
101    /// Template `003`.
102    SecureNote,
103    /// Template `004`.
104    Identity,
105    /// Template `005`.
106    Password,
107    /// Template `006`.
108    Document,
109    /// Template `100`.
110    SoftwareLicense,
111    /// Template `101`.
112    BankAccount,
113    /// Template `102`.
114    Database,
115    /// Template `103`.
116    DriverLicense,
117    /// Template `104`.
118    OutdoorLicense,
119    /// Template `105`.
120    Membership,
121    /// Template `106`.
122    Passport,
123    /// Template `107`.
124    RewardProgram,
125    /// Template `108`.
126    SocialSecurityNumber,
127    /// Template `109`.
128    WirelessRouter,
129    /// Template `110`.
130    Server,
131    /// Template `111`.
132    EmailAccount,
133    /// Template `112`.
134    ApiCredential,
135    /// Template `113`.
136    MedicalRecord,
137    /// Template `114`.
138    SshKey,
139    /// A template id this crate does not know, kept verbatim.
140    Unknown(String),
141}
142
143impl ItemCategory {
144    /// Maps a 1Password template id to a category. Extends the `TemplateId` handling in
145    /// `Client.ConvertVaultItem` to the full standard template set.
146    pub(super) fn from_template_id(id: &str) -> ItemCategory {
147        match id {
148            "001" => ItemCategory::Login,
149            "002" => ItemCategory::CreditCard,
150            "003" => ItemCategory::SecureNote,
151            "004" => ItemCategory::Identity,
152            "005" => ItemCategory::Password,
153            "006" => ItemCategory::Document,
154            "100" => ItemCategory::SoftwareLicense,
155            "101" => ItemCategory::BankAccount,
156            "102" => ItemCategory::Database,
157            "103" => ItemCategory::DriverLicense,
158            "104" => ItemCategory::OutdoorLicense,
159            "105" => ItemCategory::Membership,
160            "106" => ItemCategory::Passport,
161            "107" => ItemCategory::RewardProgram,
162            "108" => ItemCategory::SocialSecurityNumber,
163            "109" => ItemCategory::WirelessRouter,
164            "110" => ItemCategory::Server,
165            "111" => ItemCategory::EmailAccount,
166            "112" => ItemCategory::ApiCredential,
167            "113" => ItemCategory::MedicalRecord,
168            "114" => ItemCategory::SshKey,
169            other => ItemCategory::Unknown(other.to_string()),
170        }
171    }
172}