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, Clone)]
38pub(crate) enum Tokens {
39 SdkManaged(SdkManagedTokens),
40 ClientManaged(Arc<dyn ClientManagedTokens>),
41}
42
43#[async_trait::async_trait]
45pub trait ClientManagedTokens: std::fmt::Debug + Send + Sync {
46 async fn get_access_token(&self) -> Option<String>;
48}
49
50#[derive(Debug, Default, Clone)]
52pub(crate) struct SdkManagedTokens {
53 #[allow(dead_code)]
56 access_token: Option<String>,
57 pub(crate) expires_on: Option<i64>,
58
59 #[cfg_attr(not(feature = "internal"), allow(dead_code))]
60 pub(crate) refresh_token: Option<String>,
61}
62
63#[allow(missing_docs)]
64#[derive(Debug)]
65pub struct InternalClient {
66 pub(crate) user_id: OnceLock<Uuid>,
67 pub(crate) tokens: RwLock<Tokens>,
68 pub(crate) login_method: RwLock<Option<Arc<LoginMethod>>>,
69
70 #[cfg(feature = "internal")]
71 pub(super) flags: RwLock<Flags>,
72
73 #[doc(hidden)]
76 pub(crate) __api_configurations: RwLock<Arc<ApiConfigurations>>,
77
78 #[allow(unused)]
80 pub(crate) external_client: reqwest::Client,
81
82 pub(super) key_store: KeyStore<KeyIds>,
83
84 #[cfg(feature = "internal")]
85 pub(crate) repository_map: StateRegistry,
86}
87
88impl InternalClient {
89 #[allow(missing_docs)]
90 #[cfg(feature = "internal")]
91 pub fn load_flags(&self, flags: std::collections::HashMap<String, bool>) {
92 *self.flags.write().expect("RwLock is not poisoned") = Flags::load_from_map(flags);
93 }
94
95 #[allow(missing_docs)]
96 #[cfg(feature = "internal")]
97 pub fn get_flags(&self) -> Flags {
98 self.flags.read().expect("RwLock is not poisoned").clone()
99 }
100
101 #[cfg(feature = "internal")]
102 pub(crate) fn get_login_method(&self) -> Option<Arc<LoginMethod>> {
103 self.login_method
104 .read()
105 .expect("RwLock is not poisoned")
106 .clone()
107 }
108
109 #[allow(missing_docs)]
110 pub fn get_access_token_organization(&self) -> Option<Uuid> {
111 match self
112 .login_method
113 .read()
114 .expect("RwLock is not poisoned")
115 .as_deref()
116 {
117 #[cfg(feature = "secrets")]
118 Some(LoginMethod::ServiceAccount(ServiceAccountLoginMethod::AccessToken {
119 organization_id,
120 ..
121 })) => Some(*organization_id),
122 _ => None,
123 }
124 }
125
126 #[cfg(any(feature = "internal", feature = "secrets"))]
127 pub(crate) fn set_login_method(&self, login_method: LoginMethod) {
128 use log::debug;
129
130 debug! {"setting login method: {login_method:#?}"}
131 *self.login_method.write().expect("RwLock is not poisoned") = Some(Arc::new(login_method));
132 }
133
134 pub(crate) fn set_tokens(&self, token: String, refresh_token: Option<String>, expires_in: u64) {
135 *self.tokens.write().expect("RwLock is not poisoned") =
136 Tokens::SdkManaged(SdkManagedTokens {
137 access_token: Some(token.clone()),
138 expires_on: Some(Utc::now().timestamp() + expires_in as i64),
139 refresh_token,
140 });
141 self.set_api_tokens_internal(token);
142 }
143
144 pub(crate) fn set_api_tokens_internal(&self, token: String) {
146 let mut guard = self
147 .__api_configurations
148 .write()
149 .expect("RwLock is not poisoned");
150
151 let inner = Arc::make_mut(&mut guard);
152 inner.identity.oauth_access_token = Some(token.clone());
153 inner.api.oauth_access_token = Some(token);
154 }
155
156 #[allow(missing_docs)]
157 #[cfg(feature = "internal")]
158 pub fn get_kdf(&self) -> Result<Kdf, NotAuthenticatedError> {
159 match self
160 .login_method
161 .read()
162 .expect("RwLock is not poisoned")
163 .as_deref()
164 {
165 Some(LoginMethod::User(
166 UserLoginMethod::Username { kdf, .. } | UserLoginMethod::ApiKey { kdf, .. },
167 )) => Ok(kdf.clone()),
168 _ => Err(NotAuthenticatedError),
169 }
170 }
171
172 #[allow(missing_docs)]
173 pub async fn get_api_configurations(&self) -> Arc<ApiConfigurations> {
174 renew_token(self).await.ok();
177 self.__api_configurations
178 .read()
179 .expect("RwLock is not poisoned")
180 .clone()
181 }
182
183 #[allow(missing_docs)]
184 #[cfg(feature = "internal")]
185 pub fn get_http_client(&self) -> &reqwest::Client {
186 &self.external_client
187 }
188
189 #[allow(missing_docs)]
190 pub fn get_key_store(&self) -> &KeyStore<KeyIds> {
191 &self.key_store
192 }
193
194 #[allow(missing_docs)]
195 pub fn init_user_id(&self, user_id: Uuid) -> Result<(), UserIdAlreadySetError> {
196 let set_uuid = self.user_id.get_or_init(|| user_id);
197
198 if *set_uuid != user_id {
202 Err(UserIdAlreadySetError)
203 } else {
204 Ok(())
205 }
206 }
207
208 #[allow(missing_docs)]
209 pub fn get_user_id(&self) -> Option<Uuid> {
210 self.user_id.get().copied()
211 }
212
213 #[cfg(feature = "internal")]
214 pub(crate) fn initialize_user_crypto_master_key(
215 &self,
216 master_key: MasterKey,
217 user_key: EncString,
218 private_key: EncString,
219 signing_key: Option<EncString>,
220 ) -> Result<(), EncryptionSettingsError> {
221 let user_key = master_key.decrypt_user_key(user_key)?;
222 EncryptionSettings::new_decrypted_key(user_key, private_key, signing_key, &self.key_store)?;
223
224 Ok(())
225 }
226
227 #[cfg(feature = "internal")]
228 pub(crate) fn initialize_user_crypto_decrypted_key(
229 &self,
230 user_key: SymmetricCryptoKey,
231 private_key: EncString,
232 signing_key: Option<EncString>,
233 ) -> Result<(), EncryptionSettingsError> {
234 EncryptionSettings::new_decrypted_key(user_key, private_key, signing_key, &self.key_store)?;
235
236 Ok(())
237 }
238
239 #[cfg(feature = "internal")]
240 pub(crate) fn initialize_user_crypto_pin(
241 &self,
242 pin_key: PinKey,
243 pin_protected_user_key: EncString,
244 private_key: EncString,
245 signing_key: Option<EncString>,
246 ) -> Result<(), EncryptionSettingsError> {
247 let decrypted_user_key = pin_key.decrypt_user_key(pin_protected_user_key)?;
248 self.initialize_user_crypto_decrypted_key(decrypted_user_key, private_key, signing_key)
249 }
250
251 #[cfg(feature = "secrets")]
252 pub(crate) fn initialize_crypto_single_org_key(
253 &self,
254 organization_id: Uuid,
255 key: SymmetricCryptoKey,
256 ) {
257 EncryptionSettings::new_single_org_key(organization_id, key, &self.key_store);
258 }
259
260 #[allow(missing_docs)]
261 #[cfg(feature = "internal")]
262 pub fn initialize_org_crypto(
263 &self,
264 org_keys: Vec<(Uuid, UnsignedSharedKey)>,
265 ) -> Result<(), EncryptionSettingsError> {
266 EncryptionSettings::set_org_keys(org_keys, &self.key_store)
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use crate::Client;
273
274 #[test]
275 fn initializing_user_multiple_times() {
276 use super::*;
277
278 let client = Client::new(None);
279 let user_id = Uuid::new_v4();
280
281 assert!(client.internal.init_user_id(user_id).is_ok());
283 assert_eq!(client.internal.get_user_id(), Some(user_id));
284
285 assert!(client.internal.init_user_id(user_id).is_ok());
287
288 let different_user_id = Uuid::new_v4();
290 assert!(client.internal.init_user_id(different_user_id).is_err());
291 }
292}