veloren_voxygen_credentialstore/
linux.rs1use super::{CredentialError, CredentialStatus, CredentialStore, Credentials};
2use secret_service::{EncryptionType, SecretService as Provider};
3use std::{collections::HashMap, sync::Arc};
4use tokio::sync::{Mutex, MutexGuard};
5
6const TITLE: &str = "Veloren";
7const APPLICATION: &str = "application";
8const AUTH_SERVER: &str = "auth_server";
9const USERNAME: &str = "username";
10const CONTENT_TYPE: &str = "text/plain";
11
12#[derive(Clone, Default)]
13pub struct SecretService {
14 provider: Arc<Mutex<Option<Provider<'static>>>>,
15}
16
17impl SecretService {
18 async fn get_or_create_provider(&self) -> MutexGuard<'_, Option<Provider<'static>>> {
22 let mut provider = self.provider.lock().await;
23
24 if provider.is_none() {
25 *provider = Provider::connect(EncryptionType::Dh).await.ok();
27 }
28
29 provider
30 }
31}
32
33impl CredentialStore for SecretService {
34 fn is_supported(&self) -> bool { true }
35
36 async fn status(&self) -> CredentialStatus {
37 let mut provider = self.get_or_create_provider().await;
38
39 if let Some(provider_ref) = provider.as_ref()
40 && let Ok(collection) = provider_ref.get_default_collection().await
41 && let Ok(locked) = collection.is_locked().await
42 {
43 if locked {
44 return CredentialStatus::Locked;
45 } else {
46 return CredentialStatus::Ready;
47 }
48 }
49
50 *provider = None;
51 CredentialStatus::Disabled
52 }
53
54 async fn load(&self, auth_server: String, username: String) -> Result<String, CredentialError> {
55 let mut provider = self.get_or_create_provider().await;
56 let mut error = CredentialError::Connection;
57 let attributes = HashMap::from([
58 (APPLICATION, TITLE),
59 (AUTH_SERVER, auth_server.as_str()),
60 (USERNAME, username.as_str()),
61 ]);
62
63 if let Some(provider_ref) = provider.as_ref()
64 && let Ok(search_items) = provider_ref
65 .search_items(attributes)
66 .await
67 .map_err(|_| error = CredentialError::Search)
68 && let Some(item) = match search_items.unlocked.first() {
69 Some(item) => Some(item),
70 None => match search_items.locked.first() {
71 Some(item) => {
72 if item.unlock().await.is_ok() {
73 Some(item)
74 } else {
75 error = CredentialError::Unlock;
76 None
77 }
78 },
79 None => {
80 return Err(CredentialError::Empty);
82 },
83 },
84 }
85 && let Ok(secret) = item
86 .get_secret()
87 .await
88 .map_err(|_| error = CredentialError::Secret)
89 && let Ok(password) =
90 String::from_utf8(secret).map_err(|_| error = CredentialError::Utf8)
91 {
92 return Ok(password);
93 }
94
95 *provider = None;
96 Err(error)
97 }
98
99 async fn save(&self, credentials: Credentials) -> Result<(), CredentialError> {
100 let mut provider = self.get_or_create_provider().await;
101 let mut error = CredentialError::Connection;
102 let attributes = HashMap::from([
103 (APPLICATION, TITLE),
104 (AUTH_SERVER, credentials.auth_server.as_str()),
105 (USERNAME, credentials.username.as_str()),
106 ]);
107 let formatted_title = format!("{} ({})", TITLE, credentials.username);
108
109 if let Some(provider_ref) = provider.as_ref()
110 && let Ok(collection) = provider_ref
111 .get_default_collection()
112 .await
113 .map_err(|_| error = CredentialError::Collection)
114 && let Ok(_) = collection
115 .create_item(
116 &formatted_title,
117 attributes,
118 credentials.password.as_bytes(),
119 true,
120 CONTENT_TYPE,
121 )
122 .await
123 .map_err(|_| error = CredentialError::Save)
124 {
125 return Ok(());
126 }
127
128 *provider = None;
129 Err(error)
130 }
131}