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 #[cfg(feature = "internal")]
116 pub fn load_flags(&self, flags: std::collections::HashMap<String, bool>) {
117 *self.flags.write().expect("RwLock is not poisoned") = Flags::load_from_map(flags);
118 }
119
120 #[cfg(feature = "internal")]
122 pub fn get_flags(&self) -> Flags {
123 self.flags.read().expect("RwLock is not poisoned").clone()
124 }
125
126 #[cfg(feature = "internal")]
127 pub(crate) fn get_login_method(&self) -> Option<Arc<LoginMethod>> {
128 self.login_method
129 .read()
130 .expect("RwLock is not poisoned")
131 .clone()
132 }
133
134 #[allow(missing_docs)]
135 pub fn get_access_token_organization(&self) -> Option<Uuid> {
136 match self
137 .login_method
138 .read()
139 .expect("RwLock is not poisoned")
140 .as_deref()
141 {
142 #[cfg(feature = "secrets")]
143 Some(LoginMethod::ServiceAccount(ServiceAccountLoginMethod::AccessToken {
144 organization_id,
145 ..
146 })) => Some(*organization_id),
147 _ => None,
148 }
149 }
150
151 #[cfg(any(feature = "internal", feature = "secrets"))]
152 pub(crate) fn set_login_method(&self, login_method: LoginMethod) {
153 use log::debug;
154
155 debug! {"setting login method: {login_method:#?}"}
156 *self.login_method.write().expect("RwLock is not poisoned") = Some(Arc::new(login_method));
157 }
158
159 pub(crate) fn set_tokens(&self, token: String, refresh_token: Option<String>, expires_in: u64) {
160 *self.tokens.write().expect("RwLock is not poisoned") =
161 Tokens::SdkManaged(SdkManagedTokens {
162 access_token: Some(token.clone()),
163 expires_on: Some(Utc::now().timestamp() + expires_in as i64),
164 refresh_token,
165 });
166 self.set_api_tokens_internal(token);
167 }
168
169 pub(crate) fn set_api_tokens_internal(&self, token: String) {
171 let mut guard = self
172 .__api_configurations
173 .write()
174 .expect("RwLock is not poisoned");
175
176 let inner = Arc::make_mut(&mut guard);
177 inner.identity.oauth_access_token = Some(token.clone());
178 inner.api.oauth_access_token = Some(token);
179 }
180
181 #[allow(missing_docs)]
182 #[cfg(feature = "internal")]
183 pub fn get_kdf(&self) -> Result<Kdf, NotAuthenticatedError> {
184 match self
185 .login_method
186 .read()
187 .expect("RwLock is not poisoned")
188 .as_deref()
189 {
190 Some(LoginMethod::User(
191 UserLoginMethod::Username { kdf, .. } | UserLoginMethod::ApiKey { kdf, .. },
192 )) => Ok(kdf.clone()),
193 _ => Err(NotAuthenticatedError),
194 }
195 }
196
197 #[allow(missing_docs)]
198 pub async fn get_api_configurations(&self) -> Arc<ApiConfigurations> {
199 renew_token(self).await.ok();
202 self.__api_configurations
203 .read()
204 .expect("RwLock is not poisoned")
205 .clone()
206 }
207
208 #[allow(missing_docs)]
209 #[cfg(feature = "internal")]
210 pub fn get_http_client(&self) -> &reqwest::Client {
211 &self.external_client
212 }
213
214 #[allow(missing_docs)]
215 pub fn get_key_store(&self) -> &KeyStore<KeyIds> {
216 &self.key_store
217 }
218
219 #[cfg(feature = "internal")]
223 pub fn get_security_version(&self) -> u64 {
224 self.security_state
225 .read()
226 .expect("RwLock is not poisoned")
227 .as_ref()
228 .map_or(1, |state| state.version())
229 }
230
231 #[allow(missing_docs)]
232 pub fn init_user_id(&self, user_id: Uuid) -> Result<(), UserIdAlreadySetError> {
233 let set_uuid = self.user_id.get_or_init(|| user_id);
234
235 if *set_uuid != user_id {
239 Err(UserIdAlreadySetError)
240 } else {
241 Ok(())
242 }
243 }
244
245 #[allow(missing_docs)]
246 pub fn get_user_id(&self) -> Option<Uuid> {
247 self.user_id.get().copied()
248 }
249
250 #[cfg(feature = "internal")]
251 pub(crate) fn initialize_user_crypto_master_key(
252 &self,
253 master_key: MasterKey,
254 user_key: EncString,
255 key_state: UserKeyState,
256 ) -> Result<(), EncryptionSettingsError> {
257 let user_key = master_key.decrypt_user_key(user_key)?;
258 self.initialize_user_crypto_decrypted_key(user_key, key_state)
259 }
260
261 #[cfg(feature = "internal")]
262 pub(crate) fn initialize_user_crypto_decrypted_key(
263 &self,
264 user_key: SymmetricCryptoKey,
265 key_state: UserKeyState,
266 ) -> Result<(), EncryptionSettingsError> {
267 match user_key {
268 SymmetricCryptoKey::Aes256CbcHmacKey(ref user_key) => {
269 EncryptionSettings::new_decrypted_key(
270 AccountEncryptionKeys::V1 {
271 user_key: user_key.clone(),
272 private_key: key_state.private_key,
273 },
274 &self.key_store,
275 &self.security_state,
276 )?;
277 }
278 SymmetricCryptoKey::XChaCha20Poly1305Key(ref user_key) => {
279 EncryptionSettings::new_decrypted_key(
280 AccountEncryptionKeys::V2 {
281 user_key: user_key.clone(),
282 private_key: key_state.private_key,
283 signing_key: key_state
284 .signing_key
285 .ok_or(EncryptionSettingsError::InvalidSigningKey)?,
286 security_state: key_state
287 .security_state
288 .ok_or(EncryptionSettingsError::InvalidSecurityState)?,
289 },
290 &self.key_store,
291 &self.security_state,
292 )?;
293 }
294 _ => {
295 return Err(CryptoError::InvalidKey.into());
296 }
297 }
298
299 Ok(())
300 }
301
302 #[cfg(feature = "internal")]
303 pub(crate) fn initialize_user_crypto_pin(
304 &self,
305 pin_key: PinKey,
306 pin_protected_user_key: EncString,
307 key_state: UserKeyState,
308 ) -> Result<(), EncryptionSettingsError> {
309 let decrypted_user_key = pin_key.decrypt_user_key(pin_protected_user_key)?;
310 self.initialize_user_crypto_decrypted_key(decrypted_user_key, key_state)
311 }
312
313 #[cfg(feature = "secrets")]
314 pub(crate) fn initialize_crypto_single_org_key(
315 &self,
316 organization_id: Uuid,
317 key: SymmetricCryptoKey,
318 ) {
319 EncryptionSettings::new_single_org_key(organization_id, key, &self.key_store);
320 }
321
322 #[allow(missing_docs)]
323 #[cfg(feature = "internal")]
324 pub fn initialize_org_crypto(
325 &self,
326 org_keys: Vec<(Uuid, UnsignedSharedKey)>,
327 ) -> Result<(), EncryptionSettingsError> {
328 EncryptionSettings::set_org_keys(org_keys, &self.key_store)
329 }
330}
331
332#[cfg(test)]
333mod tests {
334 use crate::Client;
335
336 #[test]
337 fn initializing_user_multiple_times() {
338 use super::*;
339
340 let client = Client::new(None);
341 let user_id = Uuid::new_v4();
342
343 assert!(client.internal.init_user_id(user_id).is_ok());
345 assert_eq!(client.internal.get_user_id(), Some(user_id));
346
347 assert!(client.internal.init_user_id(user_id).is_ok());
349
350 let different_user_id = Uuid::new_v4();
352 assert!(client.internal.init_user_id(different_user_id).is_err());
353 }
354}