1use crate::{
10 device::DeviceManager, DelegatedAccess, Error, IdentityFolder,
11 PublicIdentity, Result,
12};
13use async_trait::async_trait;
14use sos_backend::BackendTarget;
15use sos_core::{
16 crypto::AccessKey, AccountId, AuthenticationError, SecretId, VaultId,
17};
18use std::collections::HashMap;
19use urn::Urn;
20
21#[cfg(feature = "files")]
22use secrecy::SecretString;
23
24pub struct FolderKeys(pub HashMap<VaultId, AccessKey>);
26
27impl FolderKeys {
28 pub fn find(&self, id: &VaultId) -> Option<&AccessKey> {
30 self.0
31 .iter()
32 .find_map(|(k, v)| if k == id { Some(v) } else { None })
33 }
34}
35
36#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
37#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
38impl DelegatedAccess for FolderKeys {
39 type Error = Error;
40
41 async fn find_folder_password(
42 &self,
43 folder_id: &VaultId,
44 ) -> Result<Option<AccessKey>> {
45 Ok(self.find(folder_id).cloned())
46 }
47
48 async fn remove_folder_password(
49 &mut self,
50 folder_id: &VaultId,
51 ) -> Result<()> {
52 self.0.remove(folder_id);
53 Ok(())
54 }
55
56 async fn save_folder_password(
57 &mut self,
58 folder_id: &VaultId,
59 key: AccessKey,
60 ) -> Result<()> {
61 self.0.insert(*folder_id, key);
62 Ok(())
63 }
64}
65
66pub type UrnLookup = HashMap<(VaultId, Urn), SecretId>;
70
71pub struct Identity {
74 target: BackendTarget,
75 account: Option<PublicIdentity>,
76 identity: Option<IdentityFolder>,
77}
78
79impl Identity {
80 pub fn new(target: BackendTarget) -> Self {
82 Self {
83 target,
84 identity: None,
85 account: None,
86 }
87 }
88
89 pub fn devices(&self) -> Result<&DeviceManager> {
91 Ok(self
92 .identity
93 .as_ref()
94 .ok_or(AuthenticationError::NotAuthenticated)?
95 .devices()?)
96 }
97
98 pub fn account(&self) -> Result<&PublicIdentity> {
100 Ok(self
101 .account
102 .as_ref()
103 .ok_or(AuthenticationError::NotAuthenticated)?)
104 }
105
106 fn account_mut(&mut self) -> Result<&mut PublicIdentity> {
107 Ok(self
108 .account
109 .as_mut()
110 .ok_or(AuthenticationError::NotAuthenticated)?)
111 }
112
113 pub fn identity(&self) -> Result<&IdentityFolder> {
115 Ok(self
116 .identity
117 .as_ref()
118 .ok_or(AuthenticationError::NotAuthenticated)?)
119 }
120
121 #[doc(hidden)]
122 pub fn identity_mut(&mut self) -> Result<&mut IdentityFolder> {
123 Ok(self
124 .identity
125 .as_mut()
126 .ok_or(AuthenticationError::NotAuthenticated)?)
127 }
128
129 pub async fn verify(&self, key: &AccessKey) -> bool {
131 if let Some(identity) = &self.identity {
132 identity.verify(key).await
133 } else {
134 false
135 }
136 }
137
138 pub async fn rename_account(
140 &mut self,
141 account_name: String,
142 ) -> Result<()> {
143 self.identity_mut()?.rename(account_name.clone()).await?;
145
146 self.account_mut()?.set_label(account_name);
148
149 Ok(())
150 }
151
152 #[cfg(feature = "files")]
154 pub async fn create_file_encryption_password(&mut self) -> Result<()> {
155 self.identity_mut()?.create_file_encryption_password().await
156 }
157
158 #[cfg(feature = "files")]
160 pub async fn find_file_encryption_password(
161 &self,
162 ) -> Result<SecretString> {
163 self.identity()?.find_file_encryption_password().await
164 }
165
166 pub async fn login(
168 &mut self,
169 account_id: &AccountId,
170 key: &AccessKey,
171 ) -> Result<()> {
172 let target = self.target.clone().with_account_id(account_id);
173 let mut identity =
174 IdentityFolder::login(&target, account_id, key).await?;
175 identity.ensure_device_vault(target).await?;
176 self.identity = Some(identity);
177 Ok(())
178 }
179
180 pub async fn sign_in(
182 &mut self,
183 account_id: &AccountId,
184 key: &AccessKey,
185 ) -> Result<()> {
186 let accounts = self.target.list_accounts().await?;
187 let account = accounts
188 .into_iter()
189 .find(|a| a.account_id() == account_id)
190 .ok_or_else(|| Error::NoAccount(account_id.to_string()))?;
191
192 tracing::debug!("identity::sign_in");
193 self.login(account_id, key).await?;
194 tracing::debug!("identity::verified");
195
196 self.account = Some(account);
197 Ok(())
198 }
199
200 pub async fn sign_out(&mut self) -> Result<()> {
202 tracing::debug!("identity::sign_out");
203
204 self.identity_mut()?.sign_out().await?;
206
207 self.account = None;
208 self.identity = None;
209 Ok(())
210 }
211}
212
213impl From<Identity> for BackendTarget {
214 fn from(value: Identity) -> Self {
215 value.target
216 }
217}
218
219#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
220#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
221impl DelegatedAccess for Identity {
222 type Error = Error;
223
224 async fn find_folder_password(
225 &self,
226 folder_id: &VaultId,
227 ) -> Result<Option<AccessKey>> {
228 self.identity()?.find_folder_password(folder_id).await
229 }
230
231 async fn remove_folder_password(
232 &mut self,
233 folder_id: &VaultId,
234 ) -> Result<()> {
235 self.identity_mut()?.remove_folder_password(folder_id).await
236 }
237
238 async fn save_folder_password(
239 &mut self,
240 folder_id: &VaultId,
241 key: AccessKey,
242 ) -> Result<()> {
243 self.identity_mut()?
244 .save_folder_password(folder_id, key)
245 .await
246 }
247}