1use crate::{
8 Result, Transaction,
9 auth::{
10 crypto::PublicKey,
11 settings::AuthSettings,
12 types::{AuthKey, DelegatedTreeRef, KeyStatus},
13 },
14 crdt::{CRDTError, Doc, doc},
15 height::HeightStrategy,
16 store::DocStore,
17};
18
19pub struct SettingsStore {
26 inner: DocStore,
28}
29
30impl SettingsStore {
31 pub(crate) fn new(transaction: &Transaction) -> Result<Self> {
44 let inner = DocStore {
48 name: "_settings".to_string(),
49 txn: transaction.clone(),
50 };
51 Ok(Self { inner })
52 }
53
54 pub async fn get_name(&self) -> Result<String> {
59 self.inner.get_string("name").await
60 }
61
62 pub async fn set_name(&self, name: &str) -> Result<()> {
70 self.inner.set_result("name", name).await
71 }
72
73 pub async fn get(&self, key: impl AsRef<str>) -> Result<doc::Value> {
81 self.inner.get(key).await
82 }
83
84 pub async fn get_string(&self, key: impl AsRef<str>) -> Result<String> {
92 self.inner.get_string(key).await
93 }
94
95 pub async fn get_all(&self) -> Result<Doc> {
102 self.inner.get_all().await
103 }
104
105 pub async fn get_height_strategy(&self) -> Result<HeightStrategy> {
113 match self.inner.get("height_strategy").await {
114 Ok(value) => {
115 let json = match value {
117 doc::Value::Text(s) => s,
118 _ => return Ok(HeightStrategy::default()),
119 };
120 serde_json::from_str(&json).map_err(|e| {
121 CRDTError::DeserializationFailed {
122 reason: e.to_string(),
123 }
124 .into()
125 })
126 }
127 Err(e) if e.is_not_found() => Ok(HeightStrategy::default()),
128 Err(e) => Err(e),
129 }
130 }
131
132 pub async fn set_height_strategy(&self, strategy: HeightStrategy) -> Result<()> {
137 let json =
138 serde_json::to_string(&strategy).map_err(|e| CRDTError::SerializationFailed {
139 reason: e.to_string(),
140 })?;
141 self.inner
142 .set("height_strategy", doc::Value::Text(json))
143 .await
144 }
145
146 pub async fn auth_snapshot(&self) -> Result<AuthSettings> {
156 let all = self.inner.get_all().await?;
157 match all.get("auth") {
158 Some(doc::Value::Doc(auth_doc)) => Ok(auth_doc.clone().into()),
159 _ => Ok(AuthSettings::new()),
160 }
161 }
162
163 pub async fn set_auth_key(&self, pubkey: &PublicKey, key: AuthKey) -> Result<()> {
183 let pubkey_str = pubkey.to_string();
184 self.inner.set(format!("auth.keys.{pubkey_str}"), key).await
185 }
186
187 pub async fn set_global_auth_key(&self, key: AuthKey) -> Result<()> {
198 self.inner.set("auth.global", key).await
199 }
200
201 pub async fn get_global_auth_key(&self) -> Result<AuthKey> {
208 let auth_settings = self.auth_snapshot().await?;
209 auth_settings.get_global_key()
210 }
211
212 pub async fn get_auth_key(&self, pubkey: &PublicKey) -> Result<AuthKey> {
220 let auth_settings = self.auth_snapshot().await?;
221 auth_settings.get_key_by_pubkey(pubkey)
222 }
223
224 pub async fn rename_auth_key(&self, pubkey: &PublicKey, name: Option<&str>) -> Result<()> {
236 let auth = self.auth_snapshot().await?;
237 let mut key = auth.get_key_by_pubkey(pubkey)?;
238 key.set_name(name);
239 self.set_auth_key(pubkey, key).await
240 }
241
242 pub async fn revoke_auth_key(&self, pubkey: &PublicKey) -> Result<()> {
250 let auth = self.auth_snapshot().await?;
251 let mut key = auth.get_key_by_pubkey(pubkey)?;
252 key.set_status(KeyStatus::Revoked);
253 self.set_auth_key(pubkey, key).await
254 }
255
256 pub async fn add_delegated_tree(&self, tree_ref: DelegatedTreeRef) -> Result<()> {
266 let root_id = tree_ref.tree.root.to_string();
267 self.inner
268 .set(format!("auth.delegations.{root_id}"), tree_ref)
269 .await
270 }
271
272 pub async fn get_auth_doc_for_validation(&self) -> Result<Doc> {
281 let auth_settings = self.auth_snapshot().await?;
282 Ok(auth_settings.as_doc().clone())
283 }
284
285 pub fn as_doc_store(&self) -> &DocStore {
293 &self.inner
294 }
295}
296
297#[cfg(test)]
298mod tests {
299 use super::*;
300 use crate::{
301 Database, Error, Instance,
302 auth::{
303 crypto::PublicKey,
304 types::{KeyStatus, Permission},
305 },
306 backend::database::InMemory,
307 crdt::Doc,
308 store::Store,
309 };
310
311 async fn create_test_database() -> (Instance, Database) {
312 let (instance, mut user) = Instance::create_backend(
314 Box::new(InMemory::new()),
315 crate::NewUser::passwordless("test"),
316 )
317 .await
318 .expect("Failed to create test instance");
319
320 let (database, _) = user.new_database().build().await.unwrap();
321
322 let transaction = database.new_transaction().await.unwrap();
324 let settings_store = SettingsStore::new(&transaction).unwrap();
325 settings_store.set_name("test_db").await.unwrap();
326 transaction.commit().await.unwrap();
327
328 (instance, database)
329 }
330
331 #[tokio::test]
332 async fn test_settings_store_creation() {
333 let (_instance, database) = create_test_database().await;
334 let transaction = database.new_transaction().await.unwrap();
335 let settings_store = SettingsStore::new(&transaction).unwrap();
336
337 assert!(settings_store.as_doc_store().name() == "_settings");
339 }
340
341 #[tokio::test]
342 async fn test_name_operations() {
343 let (_instance, database) = create_test_database().await;
344 let transaction = database.new_transaction().await.unwrap();
345 let settings_store = SettingsStore::new(&transaction).unwrap();
346
347 let name = settings_store.get_name().await.unwrap();
349 assert_eq!(name, "test_db");
350
351 settings_store.set_name("updated_name").await.unwrap();
353 let updated_name = settings_store.get_name().await.unwrap();
354 assert_eq!(updated_name, "updated_name");
355 }
356
357 #[tokio::test]
358 async fn test_auth_settings_integration() {
359 let (_instance, database) = create_test_database().await;
360 let transaction = database.new_transaction().await.unwrap();
361 let settings_store = SettingsStore::new(&transaction).unwrap();
362
363 let initial_auth_settings = settings_store.auth_snapshot().await.unwrap();
365 let initial_key_count = initial_auth_settings.get_all_keys().unwrap().len();
366
367 let pubkey = PublicKey::random();
369 let auth_key = AuthKey::active(Some("new_test_key"), Permission::Admin(1));
370
371 settings_store
372 .set_auth_key(&pubkey, auth_key.clone())
373 .await
374 .unwrap();
375
376 let retrieved_key = settings_store.get_auth_key(&pubkey).await.unwrap();
378 assert_eq!(retrieved_key.name(), auth_key.name());
379 assert_eq!(retrieved_key.permissions(), auth_key.permissions());
380 assert_eq!(retrieved_key.status(), auth_key.status());
381
382 let final_auth_settings = settings_store.auth_snapshot().await.unwrap();
384 let final_key_count = final_auth_settings.get_all_keys().unwrap().len();
385 assert_eq!(final_key_count, initial_key_count + 1);
386 }
387
388 #[tokio::test]
389 async fn test_auth_key_operations() {
390 let (_instance, database) = create_test_database().await;
391 let transaction = database.new_transaction().await.unwrap();
392 let settings_store = SettingsStore::new(&transaction).unwrap();
393
394 let pubkey = PublicKey::random();
395 let auth_key = AuthKey::active(Some("laptop"), Permission::Write(5));
396
397 settings_store
399 .set_auth_key(&pubkey, auth_key.clone())
400 .await
401 .unwrap();
402
403 let retrieved = settings_store.get_auth_key(&pubkey).await.unwrap();
405 assert_eq!(retrieved.name(), Some("laptop"));
406 assert_eq!(retrieved.status(), &KeyStatus::Active);
407
408 settings_store.revoke_auth_key(&pubkey).await.unwrap();
410
411 let revoked_key = settings_store.get_auth_key(&pubkey).await.unwrap();
413 assert_eq!(revoked_key.status(), &KeyStatus::Revoked);
414 }
415
416 #[tokio::test]
417 async fn test_rename_auth_key() {
418 let (_instance, database) = create_test_database().await;
419 let transaction = database.new_transaction().await.unwrap();
420 let settings_store = SettingsStore::new(&transaction).unwrap();
421
422 let pubkey = PublicKey::random();
423 let auth_key = AuthKey::active(Some("laptop"), Permission::Write(5));
424
425 settings_store
426 .set_auth_key(&pubkey, auth_key)
427 .await
428 .unwrap();
429
430 settings_store
432 .rename_auth_key(&pubkey, Some("desktop"))
433 .await
434 .unwrap();
435
436 let renamed = settings_store.get_auth_key(&pubkey).await.unwrap();
438 assert_eq!(renamed.name(), Some("desktop"));
439 assert_eq!(renamed.permissions(), &Permission::Write(5));
440 assert_eq!(renamed.status(), &KeyStatus::Active);
441
442 settings_store.rename_auth_key(&pubkey, None).await.unwrap();
444
445 let unnamed = settings_store.get_auth_key(&pubkey).await.unwrap();
446 assert_eq!(unnamed.name(), None);
447 assert_eq!(unnamed.permissions(), &Permission::Write(5));
448 }
449
450 #[tokio::test]
451 async fn test_multiple_auth_key_writes() {
452 let (_instance, database) = create_test_database().await;
453 let transaction = database.new_transaction().await.unwrap();
454 let settings_store = SettingsStore::new(&transaction).unwrap();
455
456 let pubkey1 = PublicKey::random();
458 let pubkey2 = PublicKey::random();
459
460 settings_store
462 .set_auth_key(
463 &pubkey1,
464 AuthKey::active(Some("admin"), Permission::Admin(1)),
465 )
466 .await
467 .unwrap();
468 settings_store
469 .set_auth_key(
470 &pubkey2,
471 AuthKey::active(Some("writer"), Permission::Write(5)),
472 )
473 .await
474 .unwrap();
475
476 let auth_settings = settings_store.auth_snapshot().await.unwrap();
478 let all_keys = auth_settings.get_all_keys().unwrap();
479 assert!(all_keys.len() >= 2); assert!(all_keys.contains_key(&pubkey1.to_string()));
481 assert!(all_keys.contains_key(&pubkey2.to_string()));
482 }
483
484 #[tokio::test]
485 async fn test_auth_doc_for_validation() {
486 let (_instance, database) = create_test_database().await;
487 let transaction = database.new_transaction().await.unwrap();
488 let settings_store = SettingsStore::new(&transaction).unwrap();
489
490 let pubkey = PublicKey::random();
492 let auth_key = AuthKey::active(Some("validator"), Permission::Read);
493 settings_store
494 .set_auth_key(&pubkey, auth_key)
495 .await
496 .unwrap();
497
498 let auth_doc = settings_store.get_auth_doc_for_validation().await.unwrap();
500
501 let auth_settings: AuthSettings = auth_doc.into();
503 let validator_key = auth_settings.get_key_by_pubkey(&pubkey).unwrap();
504 assert_eq!(validator_key.name(), Some("validator"));
505 }
506
507 #[tokio::test]
511 async fn test_auth_settings_entries_are_incremental() {
512 let (_instance, database) = create_test_database().await;
513
514 let pubkey_a = PublicKey::random();
515 let pubkey_b = PublicKey::random();
516
517 let txn1 = database.new_transaction().await.unwrap();
519 let settings1 = SettingsStore::new(&txn1).unwrap();
520 settings1
521 .set_auth_key(
522 &pubkey_a,
523 AuthKey::active(Some("key_a"), Permission::Write(5)),
524 )
525 .await
526 .unwrap();
527 txn1.commit().await.unwrap();
528
529 let txn2 = database.new_transaction().await.unwrap();
531 let settings2 = SettingsStore::new(&txn2).unwrap();
532 settings2
533 .set_auth_key(
534 &pubkey_b,
535 AuthKey::active(Some("key_b"), Permission::Admin(1)),
536 )
537 .await
538 .unwrap();
539 let entry_id_2 = txn2.commit().await.unwrap();
540
541 let entry2 = database.get_entry(&entry_id_2).await.unwrap();
543 let raw_2: Doc = serde_json::from_slice(entry2.data("_settings").unwrap()).unwrap();
544
545 assert!(
546 raw_2.get(format!("auth.keys.{pubkey_a}")).is_none(),
547 "SettingsStore entry should NOT contain key A from prior entry (incremental)"
548 );
549 assert!(
550 raw_2.get(format!("auth.keys.{pubkey_b}")).is_some(),
551 "SettingsStore entry should contain key B"
552 );
553
554 let txn3 = database.new_transaction().await.unwrap();
556 let settings3 = SettingsStore::new(&txn3).unwrap();
557 let auth = settings3.auth_snapshot().await.unwrap();
558 assert!(
559 auth.get_key_by_pubkey(&pubkey_a).is_ok(),
560 "Merged view should contain key A"
561 );
562 assert!(
563 auth.get_key_by_pubkey(&pubkey_b).is_ok(),
564 "Merged view should contain key B"
565 );
566 }
567
568 #[tokio::test]
569 async fn test_error_handling() {
570 let (_instance, database) = create_test_database().await;
571 let transaction = database.new_transaction().await.unwrap();
572 let settings_store = SettingsStore::new(&transaction).unwrap();
573
574 let nonexistent_pubkey = PublicKey::random();
576 let result = settings_store.get_auth_key(&nonexistent_pubkey).await;
577 assert!(result.is_err());
578 if let Err(Error::Auth(ref auth_err)) = result {
579 assert!(auth_err.is_not_found());
580 } else {
581 panic!("Expected Auth(KeyNotFound) error");
582 }
583
584 let revoke_result = settings_store.revoke_auth_key(&nonexistent_pubkey).await;
586 assert!(revoke_result.is_err());
587 }
588}