1use super::pw_generator::{generate_chars, generate_words};
2use super::synchronizer::Synchronizer;
3use crate::api::{ClipboardProviding, Event, EventData, EventHub, PasswordGeneratorParam, StoreConfig};
4use crate::block_store::StoreError;
5use crate::clipboard::{Clipboard, ClipboardCommon};
6use crate::secrets_store::{open_secrets_store, SecretStoreResult, SecretsStore};
7use crate::service::config::{read_config, write_config, Config};
8use crate::service::error::{ServiceError, ServiceResult};
9#[cfg(any(unix, windows))]
10use crate::service::secrets_provider::SecretsProvider;
11use crate::service::{ClipboardControl, TrustlessService};
12use chrono::{DateTime, Utc};
13use log::{error, info};
14use rand::{distributions, thread_rng, Rng};
15use std::collections::{HashMap, VecDeque};
16use std::sync::{Arc, Mutex, RwLock};
17use std::time::Duration;
18
19enum ClipboardHolder {
20 Empty,
21 Providing(Clipboard),
22}
23
24impl ClipboardControl for ClipboardHolder {
25 fn is_done(&self) -> ServiceResult<bool> {
26 match self {
27 ClipboardHolder::Empty => Ok(true),
28 ClipboardHolder::Providing(clipboard) => Ok(!clipboard.is_open()),
29 }
30 }
31
32 fn currently_providing(&self) -> ServiceResult<Option<ClipboardProviding>> {
33 match self {
34 ClipboardHolder::Empty => Ok(None),
35 ClipboardHolder::Providing(clipboard) => Ok(clipboard.currently_providing()),
36 }
37 }
38
39 fn provide_next(&self) -> ServiceResult<()> {
40 if let ClipboardHolder::Providing(clipboard) = &self {
41 clipboard.provide_next();
42 }
43 Ok(())
44 }
45
46 fn destroy(&self) -> ServiceResult<()> {
47 if let ClipboardHolder::Providing(clipboard) = &self {
48 clipboard.destroy();
49 }
50 Ok(())
51 }
52}
53
54struct LocalEventQueue {
55 last_id: u64,
56 limit: usize,
57 queue: VecDeque<Event>,
58}
59
60impl LocalEventQueue {
61 fn new(limit: usize) -> Self {
62 LocalEventQueue {
63 last_id: 0,
64 limit,
65 queue: VecDeque::with_capacity(limit),
66 }
67 }
68
69 fn queue(&mut self, data: EventData) {
70 if self.queue.len() >= self.limit {
71 self.queue.pop_front();
72 }
73 self.last_id += 1;
74 self.queue.push_back(Event { id: self.last_id, data });
75 }
76
77 fn poll(&self, last_id: u64) -> Vec<Event> {
78 match self.queue.iter().position(|e| e.id > last_id) {
79 Some(start) => self.queue.iter().skip(start).cloned().collect(),
80 None => vec![],
81 }
82 }
83}
84
85struct LocalEventHub {
86 event_queue: RwLock<LocalEventQueue>,
87}
88
89impl LocalEventHub {
90 fn new(limit: usize) -> Self {
91 LocalEventHub {
92 event_queue: RwLock::new(LocalEventQueue::new(limit)),
93 }
94 }
95
96 fn poll_events(&self, last_id: u64) -> ServiceResult<Vec<Event>> {
97 let event_queue = self.event_queue.read()?;
98
99 Ok(event_queue.poll(last_id))
100 }
101}
102
103impl EventHub for LocalEventHub {
104 fn send(&self, event: EventData) {
105 match self.event_queue.write() {
106 Ok(mut event_queue) => event_queue.queue(event),
107 Err(e) => {
108 error!("Queue event failed: {e}");
109 }
110 };
111 }
112}
113
114pub struct LocalTrustlessService {
115 config: RwLock<Config>,
116 opened_stores: RwLock<HashMap<String, Arc<dyn SecretsStore>>>,
117 synchronizers: Mutex<Vec<Synchronizer>>,
118 clipboard: RwLock<Arc<ClipboardHolder>>,
119 event_hub: Arc<LocalEventHub>,
120}
121
122impl LocalTrustlessService {
123 pub fn new() -> ServiceResult<LocalTrustlessService> {
124 let config = read_config()?.unwrap_or_default();
125
126 Ok(LocalTrustlessService {
127 config: RwLock::new(config),
128 opened_stores: RwLock::new(HashMap::new()),
129 synchronizers: Mutex::new(vec![]),
130 clipboard: RwLock::new(Arc::new(ClipboardHolder::Empty)),
131 event_hub: Arc::new(LocalEventHub::new(100)),
132 })
133 }
134}
135
136impl TrustlessService for LocalTrustlessService {
137 fn list_stores(&self) -> ServiceResult<Vec<StoreConfig>> {
138 let config = self.config.read()?;
139
140 Ok(config.stores.values().cloned().collect())
141 }
142
143 fn upsert_store_config(&self, mut store_config: StoreConfig) -> ServiceResult<()> {
144 let mut config = self.config.write()?;
145
146 if store_config.client_id.is_empty() {
147 store_config.client_id = self.generate_id()?;
148 }
149
150 if config.default_store.is_none() {
151 config.default_store = Some(store_config.name.to_string());
152 }
153 config.stores.insert(store_config.name.to_string(), store_config);
154 write_config(&config)?;
155
156 Ok(())
157 }
158
159 fn delete_store_config(&self, name: &str) -> ServiceResult<()> {
160 let mut config = self.config.write()?;
161
162 if config.stores.remove(name).is_some() {
163 write_config(&config)?;
164 }
165
166 Ok(())
167 }
168
169 fn open_store(&self, name: &str) -> SecretStoreResult<Arc<dyn SecretsStore>> {
170 {
171 let opened_stores = self.opened_stores.read()?;
172
173 if let Some(store) = opened_stores.get(name) {
174 return Ok(store.clone());
175 }
176 }
177 let mut opened_stores = self.opened_stores.write()?;
178 let config = self.config.read()?;
179 let store_config = config
180 .stores
181 .get(name)
182 .ok_or_else(|| StoreError::StoreNotFound(name.to_string()))?;
183 let (store, maybe_sync_block_store) = open_secrets_store(
184 name,
185 &store_config.store_url,
186 store_config.remote_url.as_deref(),
187 &store_config.client_id,
188 Duration::from_secs(store_config.autolock_timeout_secs),
189 self.event_hub.clone(),
190 )?;
191
192 if let Some(sync_block_store) = maybe_sync_block_store {
193 self.synchronizers.lock()?.push(Synchronizer::new(
194 store.clone(),
195 sync_block_store,
196 chrono::Duration::seconds(store_config.sync_interval_sec as i64),
197 ));
198 }
199
200 opened_stores.insert(name.to_string(), store.clone());
201
202 Ok(store)
203 }
204
205 fn get_default_store(&self) -> ServiceResult<Option<String>> {
206 let config = self.config.read()?;
207
208 Ok(config.default_store.to_owned())
209 }
210
211 fn set_default_store(&self, name: &str) -> ServiceResult<()> {
212 let mut config = self.config.write()?;
213
214 if !config.stores.contains_key(name) {
215 return Err(ServiceError::StoreNotFound(name.to_string()));
216 }
217
218 config.default_store = Some(name.to_string());
219 write_config(&config)?;
220
221 Ok(())
222 }
223
224 fn secret_to_clipboard(
225 &self,
226 store_name: &str,
227 block_id: &str,
228 properties: &[&str],
229 ) -> ServiceResult<Arc<dyn ClipboardControl>> {
230 #[cfg(any(unix, windows))]
231 {
232 let store = self.open_store(store_name)?;
233 let secret_version = store.get_version(block_id)?;
234 let secret_provider =
235 SecretsProvider::new(store_name.to_string(), block_id.to_string(), secret_version, properties);
236 let mut clipboard = self.clipboard.write()?;
237
238 clipboard.destroy()?;
239
240 info!("Providing {} for {} in {}", properties.join(","), block_id, store_name);
241
242 let next_clipboard = Arc::new(ClipboardHolder::Providing(Clipboard::new(
243 secret_provider,
244 self.event_hub.clone(),
245 )?));
246 *clipboard = next_clipboard.clone();
247
248 Ok(next_clipboard)
249 }
250 #[cfg(not(any(unix, windows)))]
251 {
252 Err(ServiceError::NotAvailable)
253 }
254 }
255
256 fn poll_events(&self, last_id: u64) -> ServiceResult<Vec<Event>> {
257 self.event_hub.poll_events(last_id)
258 }
259
260 fn generate_id(&self) -> ServiceResult<String> {
261 let rng = thread_rng();
262
263 Ok(
264 rng
265 .sample_iter(distributions::Alphanumeric)
266 .map(char::from)
267 .take(64)
268 .collect::<String>(),
269 )
270 }
271
272 fn generate_password(&self, param: PasswordGeneratorParam) -> ServiceResult<String> {
273 match ¶m {
274 PasswordGeneratorParam::Chars(params) => Ok(generate_chars(params)),
275 PasswordGeneratorParam::Words(params) => Ok(generate_words(params)),
276 }
277 }
278
279 fn check_autolock(&self) {
280 let opened_stores = match self.opened_stores.read() {
281 Ok(opened_stores) => opened_stores,
282 Err(err) => {
283 error!("Failed locking opened stores: {err}");
284 return;
285 }
286 };
287
288 for (name, secrets_store) in opened_stores.iter() {
289 let status = match secrets_store.status() {
290 Ok(status) => status,
291 Err(error) => {
292 error!("Autolocker was unable to query status: {error}");
293 continue;
294 }
295 };
296
297 if let Some(autolock_at) = status.autolock_at {
298 if autolock_at < Utc::now().into() {
299 info!("Autolocking {name}");
300 if let Err(error) = secrets_store.lock() {
301 error!("Autolocker was unable to lock store: {error}");
302 }
303 }
304 }
305 }
306 }
307
308 fn needs_synchronization(&self) -> bool {
309 if let Ok(config) = self.config.read() {
310 config
311 .stores
312 .iter()
313 .any(|(_, store_config)| store_config.remote_url.is_some())
314 } else {
315 false
316 }
317 }
318
319 fn synchronize(&self) -> Option<DateTime<Utc>> {
320 match self.synchronizers.lock() {
321 Ok(mut synchronizers) => {
322 let mut result = None;
323 for synchronizer in synchronizers.iter_mut() {
324 if let Err(err) = synchronizer.synchronize() {
325 error!("Synchronization failed: {err}");
326 }
327 let next = synchronizer.next_run();
328 result = match result {
329 Some(prev) if prev > next => Some(next),
330 Some(prev) => Some(prev),
331 None => Some(next),
332 };
333 }
334 result
335 }
336 Err(err) => {
337 error!("Synchronization lock failed: {err}");
338 None
339 }
340 }
341 }
342}
343
344impl std::fmt::Debug for LocalTrustlessService {
345 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
346 write!(f, "Local Trustless service")
347 }
348}