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::{CryptoError, 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::{
24 encryption_settings::{AccountEncryptionKeys, EncryptionSettingsError},
25 flags::Flags,
26 login_method::UserLoginMethod,
27 },
28 error::NotAuthenticatedError,
29 key_management::{crypto::InitUserCryptoRequest, SecurityState, SignedSecurityState},
30};
31
32#[cfg(feature = "internal")]
34pub(crate) struct UserKeyState {
35 pub(crate) private_key: EncString,
36 pub(crate) signing_key: Option<EncString>,
37 pub(crate) security_state: Option<SignedSecurityState>,
38}
39#[cfg(feature = "internal")]
40impl From<&InitUserCryptoRequest> for UserKeyState {
41 fn from(req: &InitUserCryptoRequest) -> Self {
42 UserKeyState {
43 private_key: req.private_key.clone(),
44 signing_key: req.signing_key.clone(),
45 security_state: req.security_state.clone(),
46 }
47 }
48}
49
50#[allow(missing_docs)]
51#[derive(Debug, Clone)]
52pub struct ApiConfigurations {
53 pub identity: bitwarden_api_identity::apis::configuration::Configuration,
54 pub api: bitwarden_api_api::apis::configuration::Configuration,
55 pub device_type: DeviceType,
56}
57
58#[derive(Debug, Clone)]
60pub(crate) enum Tokens {
61 SdkManaged(SdkManagedTokens),
62 ClientManaged(Arc<dyn ClientManagedTokens>),
63}
64
65#[async_trait::async_trait]
67pub trait ClientManagedTokens: std::fmt::Debug + Send + Sync {
68 async fn get_access_token(&self) -> Option<String>;
70}
71
72#[derive(Debug, Default, Clone)]
74pub(crate) struct SdkManagedTokens {
75 #[allow(dead_code)]
78 access_token: Option<String>,
79 pub(crate) expires_on: Option<i64>,
80
81 #[cfg_attr(not(feature = "internal"), allow(dead_code))]
82 pub(crate) refresh_token: Option<String>,
83}
84
85#[allow(missing_docs)]
86#[derive(Debug)]
87pub struct InternalClient {
88 pub(crate) user_id: OnceLock<Uuid>,
89 pub(crate) tokens: RwLock<Tokens>,
90 pub(crate) login_method: RwLock<Option<Arc<LoginMethod>>>,
91
92 #[cfg(feature = "internal")]
93 pub(super) flags: RwLock<Flags>,
94
95 #[doc(hidden)]
98 pub(crate) __api_configurations: RwLock<Arc<ApiConfigurations>>,
99
100 #[allow(unused)]
102 pub(crate) external_client: reqwest::Client,
103
104 pub(super) key_store: KeyStore<KeyIds>,
105 #[cfg(feature = "internal")]
106 pub(crate) security_state: RwLock<Option<SecurityState>>,
107
108 #[cfg(feature = "internal")]
109 pub(crate) repository_map: StateRegistry,
110}
111
112impl InternalClient {
113 #[allow(missing_docs)]
114 #[cfg(feature = "internal")]
115 pub fn load_flags(&self, flags: std::collections::HashMap<String, bool>) {
116 *self.flags.write().expect("RwLock is not poisoned") = Flags::load_from_map(flags);
117 }
118
119 #[allow(missing_docs)]
120 #[cfg(feature = "internal")]
121 pub fn get_flags(&self) -> Flags {
122 self.flags.read().expect("RwLock is not poisoned").clone()
123 }
124
125 #[cfg(feature = "internal")]
126 pub(crate) fn get_login_method(&self) -> Option<Arc<LoginMethod>> {
127 self.login_method
128 .read()
129 .expect("RwLock is not poisoned")
130 .clone()
131 }
132
133 #[allow(missing_docs)]
134 pub fn get_access_token_organization(&self) -> Option<Uuid> {
135 match self
136 .login_method
137 .read()
138 .expect("RwLock is not poisoned")
139 .as_deref()
140 {
141 #[cfg(feature = "secrets")]
142 Some(LoginMethod::ServiceAccount(ServiceAccountLoginMethod::AccessToken {
143 organization_id,
144 ..
145 })) => Some(*organization_id),
146 _ => None,
147 }
148 }
149
150 #[cfg(any(feature = "internal", feature = "secrets"))]
151 pub(crate) fn set_login_method(&self, login_method: LoginMethod) {
152 use log::debug;
153
154 debug! {"setting login method: {login_method:#?}"}
155 *self.login_method.write().expect("RwLock is not poisoned") = Some(Arc::new(login_method));
156 }
157
158 pub(crate) fn set_tokens(&self, token: String, refresh_token: Option<String>, expires_in: u64) {
159 *self.tokens.write().expect("RwLock is not poisoned") =
160 Tokens::SdkManaged(SdkManagedTokens {
161 access_token: Some(token.clone()),
162 expires_on: Some(Utc::now().timestamp() + expires_in as i64),
163 refresh_token,
164 });
165 self.set_api_tokens_internal(token);
166 }
167
168 pub(crate) fn set_api_tokens_internal(&self, token: String) {
170 let mut guard = self
171 .__api_configurations
172 .write()
173 .expect("RwLock is not poisoned");
174
175 let inner = Arc::make_mut(&mut guard);
176 inner.identity.oauth_access_token = Some(token.clone());
177 inner.api.oauth_access_token = Some(token);
178 }
179
180 #[allow(missing_docs)]
181 #[cfg(feature = "internal")]
182 pub fn get_kdf(&self) -> Result<Kdf, NotAuthenticatedError> {
183 match self
184 .login_method
185 .read()
186 .expect("RwLock is not poisoned")
187 .as_deref()
188 {
189 Some(LoginMethod::User(
190 UserLoginMethod::Username { kdf, .. } | UserLoginMethod::ApiKey { kdf, .. },
191 )) => Ok(kdf.clone()),
192 _ => Err(NotAuthenticatedError),
193 }
194 }
195
196 #[allow(missing_docs)]
197 pub async fn get_api_configurations(&self) -> Arc<ApiConfigurations> {
198 renew_token(self).await.ok();
201 self.__api_configurations
202 .read()
203 .expect("RwLock is not poisoned")
204 .clone()
205 }
206
207 #[allow(missing_docs)]
208 #[cfg(feature = "internal")]
209 pub fn get_http_client(&self) -> &reqwest::Client {
210 &self.external_client
211 }
212
213 #[allow(missing_docs)]
214 pub fn get_key_store(&self) -> &KeyStore<KeyIds> {
215 &self.key_store
216 }
217
218 #[cfg(feature = "internal")]
222 pub fn get_security_version(&self) -> u64 {
223 self.security_state
224 .read()
225 .expect("RwLock is not poisoned")
226 .as_ref()
227 .map_or(1, |state| state.version())
228 }
229
230 #[allow(missing_docs)]
231 pub fn init_user_id(&self, user_id: Uuid) -> Result<(), UserIdAlreadySetError> {
232 let set_uuid = self.user_id.get_or_init(|| user_id);
233
234 if *set_uuid != user_id {
238 Err(UserIdAlreadySetError)
239 } else {
240 Ok(())
241 }
242 }
243
244 #[allow(missing_docs)]
245 pub fn get_user_id(&self) -> Option<Uuid> {
246 self.user_id.get().copied()
247 }
248
249 #[cfg(feature = "internal")]
250 pub(crate) fn initialize_user_crypto_master_key(
251 &self,
252 master_key: MasterKey,
253 user_key: EncString,
254 key_state: UserKeyState,
255 ) -> Result<(), EncryptionSettingsError> {
256 let user_key = master_key.decrypt_user_key(user_key)?;
257 self.initialize_user_crypto_decrypted_key(user_key, key_state)
258 }
259
260 #[cfg(feature = "internal")]
261 pub(crate) fn initialize_user_crypto_decrypted_key(
262 &self,
263 user_key: SymmetricCryptoKey,
264 key_state: UserKeyState,
265 ) -> Result<(), EncryptionSettingsError> {
266 match user_key {
267 SymmetricCryptoKey::Aes256CbcHmacKey(ref user_key) => {
268 EncryptionSettings::new_decrypted_key(
269 AccountEncryptionKeys::V1 {
270 user_key: user_key.clone(),
271 private_key: key_state.private_key,
272 },
273 &self.key_store,
274 &self.security_state,
275 )?;
276 }
277 SymmetricCryptoKey::XChaCha20Poly1305Key(ref user_key) => {
278 EncryptionSettings::new_decrypted_key(
279 AccountEncryptionKeys::V2 {
280 user_key: user_key.clone(),
281 private_key: key_state.private_key,
282 signing_key: key_state
283 .signing_key
284 .ok_or(EncryptionSettingsError::InvalidSigningKey)?,
285 security_state: key_state
286 .security_state
287 .ok_or(EncryptionSettingsError::InvalidSecurityState)?,
288 },
289 &self.key_store,
290 &self.security_state,
291 )?;
292 }
293 _ => {
294 return Err(CryptoError::InvalidKey.into());
295 }
296 }
297
298 Ok(())
299 }
300
301 #[cfg(feature = "internal")]
302 pub(crate) fn initialize_user_crypto_pin(
303 &self,
304 pin_key: PinKey,
305 pin_protected_user_key: EncString,
306 key_state: UserKeyState,
307 ) -> Result<(), EncryptionSettingsError> {
308 let decrypted_user_key = pin_key.decrypt_user_key(pin_protected_user_key)?;
309 self.initialize_user_crypto_decrypted_key(decrypted_user_key, key_state)
310 }
311
312 #[cfg(feature = "secrets")]
313 pub(crate) fn initialize_crypto_single_org_key(
314 &self,
315 organization_id: Uuid,
316 key: SymmetricCryptoKey,
317 ) {
318 EncryptionSettings::new_single_org_key(organization_id, key, &self.key_store);
319 }
320
321 #[allow(missing_docs)]
322 #[cfg(feature = "internal")]
323 pub fn initialize_org_crypto(
324 &self,
325 org_keys: Vec<(Uuid, UnsignedSharedKey)>,
326 ) -> Result<(), EncryptionSettingsError> {
327 EncryptionSettings::set_org_keys(org_keys, &self.key_store)
328 }
329}
330
331#[cfg(test)]
332mod tests {
333 use crate::Client;
334
335 #[test]
336 fn initializing_user_multiple_times() {
337 use super::*;
338
339 let client = Client::new(None);
340 let user_id = Uuid::new_v4();
341
342 assert!(client.internal.init_user_id(user_id).is_ok());
344 assert_eq!(client.internal.get_user_id(), Some(user_id));
345
346 assert!(client.internal.init_user_id(user_id).is_ok());
348
349 let different_user_id = Uuid::new_v4();
351 assert!(client.internal.init_user_id(different_user_id).is_err());
352 }
353}