1use std::sync::{Arc, OnceLock, RwLock};
2
3use bitwarden_crypto::KeyStore;
4#[cfg(any(feature = "internal", feature = "secrets"))]
5use bitwarden_crypto::SymmetricCryptoKey;
6#[cfg(feature = "internal")]
7use bitwarden_crypto::{EncString, Kdf, MasterKey, PinKey, UnsignedSharedKey};
8#[cfg(feature = "internal")]
9use bitwarden_state::registry::StateRegistry;
10use chrono::Utc;
11use uuid::Uuid;
12
13#[cfg(any(feature = "internal", feature = "secrets"))]
14use crate::client::encryption_settings::EncryptionSettings;
15#[cfg(feature = "secrets")]
16use crate::client::login_method::ServiceAccountLoginMethod;
17use crate::{
18 auth::renew::renew_token, client::login_method::LoginMethod, error::UserIdAlreadySetError,
19 key_management::KeyIds, DeviceType,
20};
21#[cfg(feature = "internal")]
22use crate::{
23 client::encryption_settings::EncryptionSettingsError,
24 client::{flags::Flags, login_method::UserLoginMethod},
25 error::NotAuthenticatedError,
26};
27
28#[allow(missing_docs)]
29#[derive(Debug, Clone)]
30pub struct ApiConfigurations {
31 pub identity: bitwarden_api_identity::apis::configuration::Configuration,
32 pub api: bitwarden_api_api::apis::configuration::Configuration,
33 pub device_type: DeviceType,
34}
35
36#[derive(Debug, Default, Clone)]
37pub(crate) struct Tokens {
38 #[cfg_attr(not(feature = "internal"), allow(dead_code))]
41 access_token: Option<String>,
42 pub(crate) expires_on: Option<i64>,
43
44 #[cfg_attr(not(feature = "internal"), allow(dead_code))]
45 pub(crate) refresh_token: Option<String>,
46}
47
48#[allow(missing_docs)]
49#[derive(Debug)]
50pub struct InternalClient {
51 pub(crate) user_id: OnceLock<Uuid>,
52 pub(crate) tokens: RwLock<Tokens>,
53 pub(crate) login_method: RwLock<Option<Arc<LoginMethod>>>,
54
55 #[cfg(feature = "internal")]
56 pub(super) flags: RwLock<Flags>,
57
58 #[doc(hidden)]
61 pub(crate) __api_configurations: RwLock<Arc<ApiConfigurations>>,
62
63 #[allow(unused)]
65 pub(crate) external_client: reqwest::Client,
66
67 pub(super) key_store: KeyStore<KeyIds>,
68
69 #[cfg(feature = "internal")]
70 pub(crate) repository_map: StateRegistry,
71}
72
73impl InternalClient {
74 #[allow(missing_docs)]
75 #[cfg(feature = "internal")]
76 pub fn load_flags(&self, flags: std::collections::HashMap<String, bool>) {
77 *self.flags.write().expect("RwLock is not poisoned") = Flags::load_from_map(flags);
78 }
79
80 #[allow(missing_docs)]
81 #[cfg(feature = "internal")]
82 pub fn get_flags(&self) -> Flags {
83 self.flags.read().expect("RwLock is not poisoned").clone()
84 }
85
86 #[cfg(feature = "internal")]
87 pub(crate) fn get_login_method(&self) -> Option<Arc<LoginMethod>> {
88 self.login_method
89 .read()
90 .expect("RwLock is not poisoned")
91 .clone()
92 }
93
94 #[allow(missing_docs)]
95 pub fn get_access_token_organization(&self) -> Option<Uuid> {
96 match self
97 .login_method
98 .read()
99 .expect("RwLock is not poisoned")
100 .as_deref()
101 {
102 #[cfg(feature = "secrets")]
103 Some(LoginMethod::ServiceAccount(ServiceAccountLoginMethod::AccessToken {
104 organization_id,
105 ..
106 })) => Some(*organization_id),
107 _ => None,
108 }
109 }
110
111 #[cfg(any(feature = "internal", feature = "secrets"))]
112 pub(crate) fn set_login_method(&self, login_method: LoginMethod) {
113 use log::debug;
114
115 debug! {"setting login method: {:#?}", login_method}
116 *self.login_method.write().expect("RwLock is not poisoned") = Some(Arc::new(login_method));
117 }
118
119 pub(crate) fn set_tokens(&self, token: String, refresh_token: Option<String>, expires_in: u64) {
120 *self.tokens.write().expect("RwLock is not poisoned") = Tokens {
121 access_token: Some(token.clone()),
122 expires_on: Some(Utc::now().timestamp() + expires_in as i64),
123 refresh_token,
124 };
125 let mut guard = self
126 .__api_configurations
127 .write()
128 .expect("RwLock is not poisoned");
129
130 let inner = Arc::make_mut(&mut guard);
131 inner.identity.oauth_access_token = Some(token.clone());
132 inner.api.oauth_access_token = Some(token);
133 }
134
135 #[allow(missing_docs)]
136 #[cfg(feature = "internal")]
137 pub fn is_authed(&self) -> bool {
138 let is_token_set = self
139 .tokens
140 .read()
141 .expect("RwLock is not poisoned")
142 .access_token
143 .is_some();
144 let is_login_method_set = self
145 .login_method
146 .read()
147 .expect("RwLock is not poisoned")
148 .is_some();
149
150 is_token_set || is_login_method_set
151 }
152
153 #[allow(missing_docs)]
154 #[cfg(feature = "internal")]
155 pub fn get_kdf(&self) -> Result<Kdf, NotAuthenticatedError> {
156 match self
157 .login_method
158 .read()
159 .expect("RwLock is not poisoned")
160 .as_deref()
161 {
162 Some(LoginMethod::User(
163 UserLoginMethod::Username { kdf, .. } | UserLoginMethod::ApiKey { kdf, .. },
164 )) => Ok(kdf.clone()),
165 _ => Err(NotAuthenticatedError),
166 }
167 }
168
169 #[allow(missing_docs)]
170 pub async fn get_api_configurations(&self) -> Arc<ApiConfigurations> {
171 renew_token(self).await.ok();
174 self.__api_configurations
175 .read()
176 .expect("RwLock is not poisoned")
177 .clone()
178 }
179
180 #[allow(missing_docs)]
181 #[cfg(feature = "internal")]
182 pub fn get_http_client(&self) -> &reqwest::Client {
183 &self.external_client
184 }
185
186 #[allow(missing_docs)]
187 pub fn get_key_store(&self) -> &KeyStore<KeyIds> {
188 &self.key_store
189 }
190
191 #[allow(missing_docs)]
192 pub fn init_user_id(&self, user_id: Uuid) -> Result<(), UserIdAlreadySetError> {
193 let set_uuid = self.user_id.get_or_init(|| user_id);
194
195 if *set_uuid != user_id {
199 Err(UserIdAlreadySetError)
200 } else {
201 Ok(())
202 }
203 }
204
205 #[allow(missing_docs)]
206 pub fn get_user_id(&self) -> Option<Uuid> {
207 self.user_id.get().copied()
208 }
209
210 #[cfg(feature = "internal")]
211 pub(crate) fn initialize_user_crypto_master_key(
212 &self,
213 master_key: MasterKey,
214 user_key: EncString,
215 private_key: EncString,
216 signing_key: Option<EncString>,
217 ) -> Result<(), EncryptionSettingsError> {
218 let user_key = master_key.decrypt_user_key(user_key)?;
219 EncryptionSettings::new_decrypted_key(user_key, private_key, signing_key, &self.key_store)?;
220
221 Ok(())
222 }
223
224 #[cfg(feature = "internal")]
225 pub(crate) fn initialize_user_crypto_decrypted_key(
226 &self,
227 user_key: SymmetricCryptoKey,
228 private_key: EncString,
229 signing_key: Option<EncString>,
230 ) -> Result<(), EncryptionSettingsError> {
231 EncryptionSettings::new_decrypted_key(user_key, private_key, signing_key, &self.key_store)?;
232
233 Ok(())
234 }
235
236 #[cfg(feature = "internal")]
237 pub(crate) fn initialize_user_crypto_pin(
238 &self,
239 pin_key: PinKey,
240 pin_protected_user_key: EncString,
241 private_key: EncString,
242 signing_key: Option<EncString>,
243 ) -> Result<(), EncryptionSettingsError> {
244 let decrypted_user_key = pin_key.decrypt_user_key(pin_protected_user_key)?;
245 self.initialize_user_crypto_decrypted_key(decrypted_user_key, private_key, signing_key)
246 }
247
248 #[cfg(feature = "secrets")]
249 pub(crate) fn initialize_crypto_single_org_key(
250 &self,
251 organization_id: Uuid,
252 key: SymmetricCryptoKey,
253 ) {
254 EncryptionSettings::new_single_org_key(organization_id, key, &self.key_store);
255 }
256
257 #[allow(missing_docs)]
258 #[cfg(feature = "internal")]
259 pub fn initialize_org_crypto(
260 &self,
261 org_keys: Vec<(Uuid, UnsignedSharedKey)>,
262 ) -> Result<(), EncryptionSettingsError> {
263 EncryptionSettings::set_org_keys(org_keys, &self.key_store)
264 }
265}
266
267#[cfg(test)]
268mod tests {
269 use crate::Client;
270
271 #[test]
272 fn initializing_user_multiple_times() {
273 use super::*;
274
275 let client = Client::new(None);
276 let user_id = Uuid::new_v4();
277
278 assert!(client.internal.init_user_id(user_id).is_ok());
280 assert_eq!(client.internal.get_user_id(), Some(user_id));
281
282 assert!(client.internal.init_user_id(user_id).is_ok());
284
285 let different_user_id = Uuid::new_v4();
287 assert!(client.internal.init_user_id(different_user_id).is_err());
288 }
289}