1use crate::{
7 device::{DeviceManager, DeviceSigner},
8 DelegatedAccess, Error, PrivateIdentity, Result, UrnLookup,
9};
10use async_trait::async_trait;
11use secrecy::{ExposeSecret, SecretBox, SecretString};
12use sos_backend::{
13 database::entity::{AccountEntity, AccountRow, FolderEntity, FolderRow},
14 AccessPoint, BackendTarget, Folder, FolderEventLog,
15};
16use sos_core::{
17 constants::LOGIN_AGE_KEY_URN, crypto::AccessKey, encode,
18 events::EventLogType, AccountId, AuthenticationError, VaultFlags,
19 VaultId,
20};
21use sos_filesystem::write_exclusive;
22use sos_vault::Summary;
23use sos_vault::{
24 secret::{Secret, SecretId, SecretMeta, SecretRow, SecretSigner},
25 BuilderCredentials, SecretAccess, Vault, VaultBuilder,
26};
27use std::sync::Arc;
28use tokio::sync::RwLock;
29use urn::Urn;
30
31pub struct IdentityFolder {
34 #[doc(hidden)]
36 pub folder: Folder,
37 #[doc(hidden)]
39 pub index: UrnLookup,
40 target: BackendTarget,
42 private_identity: PrivateIdentity,
44 pub(super) devices: Option<crate::device::DeviceManager>,
46}
47
48impl IdentityFolder {
49 pub async fn new(
51 target: BackendTarget,
52 name: String,
53 password: SecretString,
54 account_id: Option<AccountId>,
55 ) -> Result<Self> {
56 let account_id = account_id.unwrap_or_else(AccountId::random);
57 let target = target.with_account_id(&account_id);
58 tracing::debug!(
59 account_id = %account_id,
60 backend_target = %target,
61 "new_identity_folder");
62
63 let vault =
64 Self::create_identity_vault(name.clone(), &password).await?;
65
66 let mut folder = match &target {
67 BackendTarget::FileSystem(paths) => {
68 let buffer = encode(&vault).await?;
69 write_exclusive(paths.identity_vault(), buffer).await?;
70 Folder::from_path(
71 paths.identity_vault(),
72 &account_id,
73 EventLogType::Identity,
74 )
75 .await?
76 }
77 BackendTarget::Database(_, client) => {
78 let account_row = AccountRow::new_insert(&account_id, name)?;
79 let folder_row = FolderRow::new_insert(&vault).await?;
80 client
81 .conn_mut(move |conn| {
82 let tx = conn.transaction()?;
83 let account = AccountEntity::new(&tx);
84 let folder = FolderEntity::new(&tx);
85
86 let account_id = account.insert(&account_row)?;
87 let folder_id =
88 folder.insert_folder(account_id, &folder_row)?;
89 account.insert_login_folder(account_id, folder_id)?;
90
91 tx.commit()?;
92 Ok(())
93 })
94 .await
95 .map_err(sos_backend::database::Error::from)?;
96 Folder::new(target.clone(), &account_id, vault.id()).await?
97 }
98 };
99
100 let key: AccessKey = password.into();
101 folder.unlock(&key).await?;
102
103 let mut index: UrnLookup = Default::default();
104 let private_identity =
105 Self::create_age_identity(&account_id, &mut folder, &mut index)
106 .await?;
107
108 Ok(Self {
109 folder,
110 index,
111 target,
112 private_identity,
113 devices: None,
114 })
115 }
116
117 pub fn private_identity(&self) -> &PrivateIdentity {
119 &self.private_identity
120 }
121
122 pub fn account_id(&self) -> &AccountId {
124 self.private_identity.account_id()
125 }
126
127 pub async fn summary(&self) -> Summary {
129 let access_point = self.folder.access_point();
130 let access_point = access_point.lock().await;
131 access_point.vault().summary().clone()
132 }
133
134 pub fn folder(&self) -> &Folder {
136 &self.folder
137 }
138
139 pub fn event_log(&self) -> Arc<RwLock<FolderEventLog>> {
141 self.folder.event_log()
142 }
143
144 pub async fn verify(&self, key: &AccessKey) -> bool {
146 let access_point = self.folder.access_point();
147 let access_point = access_point.lock().await;
148 access_point.verify(key).await.ok().is_some()
149 }
150
151 pub fn device(&self) -> &DeviceSigner {
157 self.devices.as_ref().unwrap().signer()
158 }
159
160 pub fn devices(&self) -> Result<&DeviceManager> {
162 Ok(self
163 .devices
164 .as_ref()
165 .ok_or(AuthenticationError::NotAuthenticated)?)
166 }
167
168 pub async fn rename(&mut self, account_name: String) -> Result<()> {
170 let access_point = self.folder.access_point();
171 let mut access_point = access_point.lock().await;
172 access_point.set_vault_name(account_name.clone()).await?;
173
174 match &self.target {
179 BackendTarget::Database(_, client) => {
180 let account_id = *self.private_identity.account_id();
181 client
182 .conn_and_then(move |conn| {
183 let account_entity = AccountEntity::new(&conn);
184 let account = account_entity.find_one(&account_id)?;
185 account_entity
186 .rename_account(account.row_id, &account_name)?;
187 Ok::<_, Error>(())
188 })
189 .await?;
190 }
191 _ => {}
192 }
193
194 Ok(())
195 }
196
197 pub(super) async fn ensure_device_vault(
200 &mut self,
201 target: BackendTarget,
202 ) -> Result<()> {
203 let account_id = self.private_identity.account_id();
204 let device_vault = target.read_device_vault(account_id).await?;
205 let device_manager = if let Some(vault) = device_vault {
206 let device_password = self
207 .find_folder_password(vault.id())
208 .await?
209 .ok_or(Error::NoFolderPassword(*vault.id()))?;
210 let key: AccessKey = device_password.into();
211
212 let (device_manager, id) =
213 DeviceManager::open_vault(target, vault, &key).await?;
214
215 let urn = DeviceManager::device_urn()?;
217 self.index
218 .insert((*device_manager.access_point().id(), urn), id);
219
220 device_manager
221 } else {
222 self.new_device_manager(&target, DeviceSigner::random())
223 .await?
224 };
225 self.devices = Some(device_manager);
226 Ok(())
227 }
228
229 pub async fn new_device_manager(
231 &mut self,
232 target: &BackendTarget,
233 signer: DeviceSigner,
234 ) -> Result<DeviceManager> {
235 let account_id = self.private_identity.account_id();
236 let password = self.generate_folder_password()?;
237 let key = password.into();
238
239 let mut device_manager =
240 DeviceManager::new(target, account_id, signer.clone(), &key)
241 .await?;
242
243 let folder_id = *device_manager.access_point().id();
244 let device_key_urn = DeviceManager::device_urn()?;
245
246 tracing::debug!(
247 urn = %device_key_urn,
248 "create_device_manager");
249
250 self.save_folder_password(&folder_id, key.clone()).await?;
251
252 device_manager.access_point_mut().unlock(&key).await?;
254
255 let secret = Secret::Signer {
256 private_key: SecretSigner::SinglePartyEd25519(SecretBox::new(
257 signer.signing_key().to_bytes().into(),
258 )),
259 user_data: Default::default(),
260 };
261 let mut meta =
262 SecretMeta::new("Device Key".to_string(), secret.kind());
263 meta.set_urn(Some(device_key_urn.clone()));
264
265 let id = SecretId::new_v4();
266 let secret_data = SecretRow::new(id, meta, secret);
267 device_manager
268 .access_point_mut()
269 .create_secret(&secret_data)
270 .await?;
271
272 {
273 self.index.insert((folder_id, device_key_urn), id);
274 }
275
276 Ok(device_manager)
277 }
278
279 pub async fn rebuild_lookup_index(&mut self) -> Result<()> {
281 let access_point = self.folder.access_point();
282 let access_point = access_point.lock().await;
283 let (index, _) =
284 Self::lookup_identity_secrets(&*access_point).await?;
285 self.index = index;
286 Ok(())
287 }
288
289 #[cfg(feature = "files")]
290 pub(crate) async fn create_file_encryption_password(
291 &mut self,
292 ) -> Result<()> {
293 use sos_core::constants::FILE_PASSWORD_URN;
294 use sos_vault::secret::UserData;
295 let file_passphrase = self.generate_folder_password()?;
296 let secret = Secret::Password {
297 password: file_passphrase,
298 name: None,
299 user_data: UserData::new_comment(self.account_id().to_string()),
300 };
301 let mut meta =
302 SecretMeta::new("File Encryption".to_string(), secret.kind());
303 let urn: Urn = FILE_PASSWORD_URN.parse()?;
304 meta.set_urn(Some(urn.clone()));
305
306 let secret_id = SecretId::new_v4();
307 let secret_data = SecretRow::new(secret_id, meta, secret);
308 self.folder.create_secret(&secret_data).await?;
309 self.index.insert((self.folder.id().await, urn), secret_id);
310
311 Ok(())
312 }
313
314 #[cfg(feature = "files")]
316 pub(crate) async fn find_file_encryption_password(
317 &self,
318 ) -> Result<SecretString> {
319 use sos_core::constants::FILE_PASSWORD_URN;
320 let urn: Urn = FILE_PASSWORD_URN.parse()?;
321
322 let id = self
323 .index
324 .get(&(self.folder.id().await, urn.clone()))
325 .ok_or_else(|| Error::NoFileEncryptionPassword)?;
326
327 let password =
328 if let Some((_, Secret::Password { password, .. }, _)) =
329 self.folder.read_secret(id).await?
330 {
331 password
332 } else {
333 return Err(Error::VaultEntryKind(urn.to_string()));
334 };
335 Ok(password)
336 }
337
338 pub async fn sign_out(&mut self) -> Result<()> {
342 self.folder.lock().await;
344 self.index = Default::default();
345
346 if let Some(devices) = self.devices.as_mut() {
348 devices.sign_out();
349 }
350
351 Ok(())
352 }
353
354 async fn lookup_identity_secrets(
358 keeper: &AccessPoint,
359 ) -> Result<(UrnLookup, Option<Secret>)> {
360 let mut index: UrnLookup = Default::default();
361
362 let identity_urn: Urn = LOGIN_AGE_KEY_URN.parse()?;
364
365 let mut identity_secret: Option<Secret> = None;
367
368 for secret_id in keeper.vault().keys() {
369 if let Some((meta, secret, _)) =
370 keeper.read_secret(secret_id).await?
371 {
372 if let Some(urn) = meta.urn() {
373 if urn == &identity_urn {
374 identity_secret = Some(secret);
375 }
376
377 index.insert((*keeper.id(), urn.clone()), *secret_id);
379 }
380 }
381 }
382 Ok((index, identity_secret))
383 }
384
385 async fn login_private_identity(
386 account_id: AccountId,
387 keeper: &AccessPoint,
388 ) -> Result<(UrnLookup, PrivateIdentity)> {
389 let (index, identity_secret) =
390 Self::lookup_identity_secrets(keeper).await?;
391
392 let identity = identity_secret.ok_or(Error::NoIdentityKey)?;
393
394 let identity = if let Secret::Age { key, .. } = identity {
396 let identity: age::x25519::Identity =
397 key.expose_secret().parse().map_err(|s: &'static str| {
398 Error::AgeIdentityParse(s.to_string())
399 })?;
400 Some(identity)
401 } else {
402 None
403 };
404 let shared = identity.ok_or(Error::NoIdentityKey)?;
405
406 let private_identity = PrivateIdentity {
407 account_id,
408 shared_public: shared.to_public(),
409 shared_private: shared,
410 };
411
412 Ok((index, private_identity))
413 }
414
415 pub async fn folder_id(&self) -> VaultId {
417 self.folder.id().await
418 }
419
420 async fn create_identity_vault(
422 name: String,
423 password: &SecretString,
424 ) -> Result<Vault> {
425 Ok(VaultBuilder::new()
426 .public_name(name)
427 .flags(VaultFlags::IDENTITY)
428 .build(BuilderCredentials::Password(password.clone(), None))
429 .await?)
430 }
431
432 async fn create_age_identity(
433 account_id: &AccountId,
434 folder: &mut Folder,
435 index: &mut UrnLookup,
436 ) -> Result<PrivateIdentity> {
437 let identity_id = SecretId::new_v4();
439 let shared = age::x25519::Identity::generate();
440 let age_secret = Secret::Age {
441 version: Default::default(),
442 key: shared.to_string(),
443 user_data: Default::default(),
444 };
445 let identity_urn: Urn = LOGIN_AGE_KEY_URN.parse()?;
446 let mut age_meta = SecretMeta::new(
447 identity_urn.as_str().to_owned(),
448 age_secret.kind(),
449 );
450 age_meta.set_urn(Some(identity_urn.clone()));
451
452 let secret_data = SecretRow::new(identity_id, age_meta, age_secret);
453 folder.create_secret(&secret_data).await?;
454
455 let private_identity = PrivateIdentity {
456 account_id: *account_id,
457 shared_public: shared.to_public(),
458 shared_private: shared,
459 };
460
461 index.insert((folder.id().await, identity_urn), identity_id);
462
463 Ok(private_identity)
464 }
465
466 pub async fn login(
468 target: &BackendTarget,
469 account_id: &AccountId,
470 key: &AccessKey,
471 ) -> Result<Self> {
472 let target = target.clone().with_account_id(account_id);
473 let mut folder = match &target {
474 BackendTarget::FileSystem(paths) => {
475 Folder::from_path(
476 paths.identity_vault(),
477 account_id,
478 EventLogType::Identity,
479 )
480 .await?
481 }
482 BackendTarget::Database(_, client) => {
483 let (_, login_folder) =
484 AccountEntity::find_account_with_login(
485 client, account_id,
486 )
487 .await?;
488
489 Folder::new(
490 target.clone(),
491 account_id,
492 login_folder.summary.id(),
493 )
494 .await?
495 }
496 };
497
498 {
499 let access_point = folder.access_point();
500 let access_point = access_point.lock().await;
501 if !access_point.vault().flags().contains(VaultFlags::IDENTITY) {
502 return Err(Error::NotIdentityFolder);
503 }
504 }
505
506 folder.unlock(key).await?;
507
508 let access_point = folder.access_point();
509 let access_point = access_point.lock().await;
510 let (index, private_identity) =
511 Self::login_private_identity(*account_id, &*access_point).await?;
512
513 Ok(Self {
514 folder,
515 index,
516 target,
517 private_identity,
518 devices: None,
519 })
520 }
521}
522
523impl From<IdentityFolder> for Vault {
524 fn from(value: IdentityFolder) -> Self {
525 value.folder.into()
526 }
527}
528
529#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
530#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
531impl DelegatedAccess for IdentityFolder {
532 type Error = Error;
533
534 async fn find_folder_password(
535 &self,
536 folder_id: &VaultId,
537 ) -> Result<Option<AccessKey>> {
538 let urn = Vault::vault_urn(folder_id)?;
539 let login_folder_id = self.folder.id().await;
540
541 tracing::debug!(
542 login_folder_id = %login_folder_id,
543 folder_id = %folder_id,
544 urn = %urn,
545 "find_folder_password");
546
547 if let Some(id) = self.index.get(&(login_folder_id, urn.clone())) {
548 let (_, secret, _) = self
549 .folder
550 .read_secret(id)
551 .await?
552 .ok_or_else(|| Error::NoSecretId(login_folder_id, *id))?;
553
554 let key = match secret {
555 Secret::Password { password, .. } => {
556 AccessKey::Password(password)
557 }
558 Secret::Age { key, .. } => {
559 AccessKey::Identity(key.expose_secret().parse().map_err(
560 |s: &str| Error::InvalidX25519Identity(s.to_owned()),
561 )?)
562 }
563 _ => {
564 return Err(Error::VaultEntryKind(urn.to_string()));
565 }
566 };
567 Ok(Some(key))
568 } else {
569 Ok(None)
570 }
571 }
572
573 async fn remove_folder_password(
574 &mut self,
575 folder_id: &VaultId,
576 ) -> Result<()> {
577 tracing::debug!(folder = %folder_id, "remove_folder_password");
578
579 let (id, index_key) = {
580 let urn = Vault::vault_urn(folder_id)?;
581 let index_key = (self.folder.id().await, urn);
582 let id = self.index.get(&index_key);
583 (id, index_key)
584 };
585
586 if let Some(id) = &id {
587 self.folder.delete_secret(&id).await?;
588 } else {
589 tracing::warn!("remove_folder_password::secret_id_not_found");
590 }
591 self.index.remove(&index_key);
592
593 Ok(())
594 }
595
596 async fn save_folder_password(
597 &mut self,
598 folder_id: &VaultId,
599 key: AccessKey,
600 ) -> Result<()> {
601 let urn = Vault::vault_urn(folder_id)?;
602 tracing::debug!(
603 folder = %folder_id,
604 urn = %urn,
605 "save_folder_password",
606 );
607
608 let secret = match key {
609 AccessKey::Password(vault_passphrase) => Secret::Password {
610 name: None,
611 password: vault_passphrase,
612 user_data: Default::default(),
613 },
614 AccessKey::Identity(id) => Secret::Age {
615 version: Default::default(),
616 key: id.to_string(),
617 user_data: Default::default(),
618 },
619 };
620
621 let mut meta =
622 SecretMeta::new(urn.as_str().to_owned(), secret.kind());
623 meta.set_urn(Some(urn.clone()));
624
625 let id = SecretId::new_v4();
626
627 let secret_data = SecretRow::new(id, meta, secret);
628 self.folder.create_secret(&secret_data).await?;
629
630 self.index.insert((self.folder.id().await, urn), id);
631
632 Ok(())
633 }
634}