rwarden/
lib.rs

1#![warn(rust_2018_idioms, missing_debug_implementations)]
2
3use derive_setters::Setters;
4use serde::{Deserialize, Serialize};
5use serde_repr::Serialize_repr as SerializeRepr;
6use std::{result::Result as StdResult, time::SystemTime};
7use url::Url;
8use uuid::Uuid;
9
10pub use client::{AnonymousClient, Client, ClientBuilder};
11pub use error::{Error, RequestResponseError};
12pub use rwarden_crypto as crypto;
13
14#[macro_use]
15mod util;
16
17mod client;
18mod error;
19
20pub mod account;
21pub mod cache;
22pub mod cipher;
23pub mod collection;
24pub mod folder;
25pub mod response;
26pub mod settings;
27pub mod sync;
28
29/// Type alias for `Result<TOk, Error<TCacheError>>`.
30pub type Result<TOk, TCacheError> = StdResult<TOk, Error<TCacheError>>;
31
32pub trait Request<'request, 'client, TCache> {
33    type Output;
34    fn send(&'request self, client: &'client mut Client<TCache>) -> Self::Output;
35}
36
37/// Struct for specifying the URLs of API endpoints.
38#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
39pub struct Urls {
40    pub base: Url,
41    pub auth: Url,
42    // pub icon: Url,
43    // pub notifications: Url,
44    // pub events: Url,
45}
46
47impl Urls {
48    /// Creates a new [`Urls`] type with the URLs of the official server.
49    ///
50    /// | Field    | URL                                          |
51    /// |----------|----------------------------------------------|
52    /// | [`base`] | https://api.bitwarden.com                    |
53    /// | [`auth`] | https://identity.bitwarden.com/connect/token |
54    ///
55    /// [`base`]: Self::base
56    /// [`auth`]: Self::auth
57    pub fn official() -> Self {
58        Self {
59            base: Url::parse("https://api.bitwarden.com").unwrap(),
60            auth: Url::parse("https://identity.bitwarden.com/connect/token").unwrap(),
61        }
62    }
63
64    /// Creates a new [`Urls`] type with the URLs of a custom server.
65    ///
66    /// | Field    | URL                              |
67    /// |----------|----------------------------------|
68    /// | [`base`] | *\<url\>*/api                    |
69    /// | [`auth`] | *\<url\>*/identity/connect/token |
70    ///
71    /// [`base`]: Self::base
72    /// [`auth`]: Self::auth
73    ///
74    /// # Example
75    ///
76    /// ```
77    /// # use rwarden::Urls;
78    /// # use url::Url;
79    /// # fn main() -> Result<(), url::ParseError> {
80    /// let urls = Urls::custom("https://example.com")?;
81    /// assert_eq!(urls.base, Url::parse("https://example.com/api").unwrap());
82    /// assert_eq!(urls.auth, Url::parse("https://example.com/identity/connect/token").unwrap());
83    /// # Ok(())
84    /// # }
85    /// ```
86    pub fn custom<S: AsRef<str>>(url: S) -> StdResult<Self, url::ParseError> {
87        let url = Url::parse(url.as_ref())?;
88        Ok(Self {
89            base: url.join("api")?,
90            auth: url.join("identity/connect/token")?,
91        })
92    }
93}
94
95/// An access token and its expiry time.
96#[derive(Debug, Clone, PartialEq, Eq, Hash)]
97pub struct AccessTokenData {
98    pub access_token: String,
99    pub expiry_time: SystemTime,
100}
101
102impl AccessTokenData {
103    fn token_has_expired(&self) -> bool {
104        self.expiry_time < SystemTime::now()
105    }
106}
107
108/// The type of a device.
109#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SerializeRepr)]
110#[repr(u8)]
111pub enum DeviceType {
112    Android = 0,
113    Ios = 1,
114    ChromeExtension = 2,
115    FirefoxExtension = 3,
116    OperaExtension = 4,
117    EdgeExtension = 5,
118    WindowsDesktop = 6,
119    MacOsDesktop = 7,
120    LinuxDesktop = 8,
121    ChromeBrowser = 9,
122    FirefoxBrowser = 10,
123    OperaBrowser = 11,
124    EdgeBrowser = 12,
125    IeBrowser = 13,
126    UnknownBrowser = 14,
127    AndroidAmazon = 15,
128    Uwp = 16,
129    SafariBrowser = 17,
130    VivaldiBrowser = 18,
131    VivaldiExtension = 19,
132    SafariExtension = 20,
133}
134
135/// The provider for two factor authentication.
136#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SerializeRepr)]
137#[repr(u8)]
138pub enum TwoFactorProvider {
139    Authenticator = 0,
140    Email = 1,
141    Duo = 2,
142    YubiKey = 3,
143    U2f = 4,
144    Remember = 5,
145    OrganizationDuo = 6,
146    WebAuthn = 7,
147}
148
149/// Data used for performing logins.
150#[derive(Debug, Clone, PartialEq, Eq, Hash, Setters)]
151#[setters(strip_option, prefix = "with_")]
152pub struct LoginData {
153    /// The email address.
154    #[setters(skip)]
155    pub email: String,
156    /// The master password.
157    #[setters(skip)]
158    pub password: String,
159    #[setters(skip)]
160    pub client_id: String,
161    #[setters(into)]
162    pub device_name: Option<String>,
163    pub device_type: Option<DeviceType>,
164    #[setters(into)]
165    pub device_push_token: Option<String>,
166    pub two_factor_provider: Option<TwoFactorProvider>,
167    #[setters(into)]
168    pub two_factor_token: Option<String>,
169    pub two_factor_remember: bool,
170}
171
172impl LoginData {
173    /// Creates a new [`LoginData`].
174    pub fn new<E, P, C>(email: E, password: P, client_id: C) -> Self
175    where
176        E: Into<String>,
177        P: Into<String>,
178        C: Into<String>,
179    {
180        Self {
181            client_id: client_id.into(),
182            email: email.into(),
183            password: password.into(),
184            device_name: None,
185            device_type: None,
186            device_push_token: None,
187            two_factor_provider: None,
188            two_factor_token: None,
189            two_factor_remember: false,
190        }
191    }
192}
193
194/// Data used for registering a user.
195#[derive(Debug, Clone, PartialEq, Eq, Hash, Setters)]
196#[setters(strip_option, prefix = "with_")]
197pub struct RegisterData {
198    /// The email address.
199    #[setters(skip)]
200    pub email: String,
201    /// The master password.
202    #[setters(skip)]
203    pub password: String,
204    /// The hint for the master password.
205    #[setters(into)]
206    pub password_hint: Option<String>,
207    /// The name of the user.
208    #[setters(into)]
209    pub name: Option<String>,
210    /// The ID of an organization that the user will be part of.
211    pub organization_user_id: Option<Uuid>,
212    /// The KDF type. Defaults to [`KdfType::Pbkdf2Sha256`].
213    ///
214    /// [`KdfType::Pbkdf2Sha256`]: crypto::KdfType::Pbkdf2Sha256
215    pub kdf_type: Option<crypto::KdfType>,
216    /// The number of KDF iterations. Defaults to `100_000`.
217    pub kdf_iterations: Option<u32>,
218}
219
220impl RegisterData {
221    /// Creates a new [`RegisterData`].
222    pub fn new<E, P>(email: E, password: P) -> Self
223    where
224        E: Into<String>,
225        P: Into<String>,
226    {
227        Self {
228            email: email.into(),
229            password: password.into(),
230            password_hint: None,
231            name: None,
232            organization_user_id: None,
233            kdf_type: None,
234            kdf_iterations: None,
235        }
236    }
237}