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.as_str().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 backend = Box::new(InMemory::new());
313 let instance = Instance::open(backend)
314 .await
315 .expect("Failed to create test instance");
316
317 instance.create_user("test", None).await.unwrap();
319 let mut user = instance.login_user("test", None).await.unwrap();
320 let key_id = user.add_private_key(None).await.unwrap();
321 let database = user.create_database(Doc::new(), &key_id).await.unwrap();
322
323 let transaction = database.new_transaction().await.unwrap();
325 let settings_store = SettingsStore::new(&transaction).unwrap();
326 settings_store.set_name("test_db").await.unwrap();
327 transaction.commit().await.unwrap();
328
329 (instance, database)
330 }
331
332 #[tokio::test]
333 async fn test_settings_store_creation() {
334 let (_instance, database) = create_test_database().await;
335 let transaction = database.new_transaction().await.unwrap();
336 let settings_store = SettingsStore::new(&transaction).unwrap();
337
338 assert!(settings_store.as_doc_store().name() == "_settings");
340 }
341
342 #[tokio::test]
343 async fn test_name_operations() {
344 let (_instance, database) = create_test_database().await;
345 let transaction = database.new_transaction().await.unwrap();
346 let settings_store = SettingsStore::new(&transaction).unwrap();
347
348 let name = settings_store.get_name().await.unwrap();
350 assert_eq!(name, "test_db");
351
352 settings_store.set_name("updated_name").await.unwrap();
354 let updated_name = settings_store.get_name().await.unwrap();
355 assert_eq!(updated_name, "updated_name");
356 }
357
358 #[tokio::test]
359 async fn test_auth_settings_integration() {
360 let (_instance, database) = create_test_database().await;
361 let transaction = database.new_transaction().await.unwrap();
362 let settings_store = SettingsStore::new(&transaction).unwrap();
363
364 let initial_auth_settings = settings_store.auth_snapshot().await.unwrap();
366 let initial_key_count = initial_auth_settings.get_all_keys().unwrap().len();
367
368 let pubkey = PublicKey::random();
370 let auth_key = AuthKey::active(Some("new_test_key"), Permission::Admin(1));
371
372 settings_store
373 .set_auth_key(&pubkey, auth_key.clone())
374 .await
375 .unwrap();
376
377 let retrieved_key = settings_store.get_auth_key(&pubkey).await.unwrap();
379 assert_eq!(retrieved_key.name(), auth_key.name());
380 assert_eq!(retrieved_key.permissions(), auth_key.permissions());
381 assert_eq!(retrieved_key.status(), auth_key.status());
382
383 let final_auth_settings = settings_store.auth_snapshot().await.unwrap();
385 let final_key_count = final_auth_settings.get_all_keys().unwrap().len();
386 assert_eq!(final_key_count, initial_key_count + 1);
387 }
388
389 #[tokio::test]
390 async fn test_auth_key_operations() {
391 let (_instance, database) = create_test_database().await;
392 let transaction = database.new_transaction().await.unwrap();
393 let settings_store = SettingsStore::new(&transaction).unwrap();
394
395 let pubkey = PublicKey::random();
396 let auth_key = AuthKey::active(Some("laptop"), Permission::Write(5));
397
398 settings_store
400 .set_auth_key(&pubkey, auth_key.clone())
401 .await
402 .unwrap();
403
404 let retrieved = settings_store.get_auth_key(&pubkey).await.unwrap();
406 assert_eq!(retrieved.name(), Some("laptop"));
407 assert_eq!(retrieved.status(), &KeyStatus::Active);
408
409 settings_store.revoke_auth_key(&pubkey).await.unwrap();
411
412 let revoked_key = settings_store.get_auth_key(&pubkey).await.unwrap();
414 assert_eq!(revoked_key.status(), &KeyStatus::Revoked);
415 }
416
417 #[tokio::test]
418 async fn test_rename_auth_key() {
419 let (_instance, database) = create_test_database().await;
420 let transaction = database.new_transaction().await.unwrap();
421 let settings_store = SettingsStore::new(&transaction).unwrap();
422
423 let pubkey = PublicKey::random();
424 let auth_key = AuthKey::active(Some("laptop"), Permission::Write(5));
425
426 settings_store
427 .set_auth_key(&pubkey, auth_key)
428 .await
429 .unwrap();
430
431 settings_store
433 .rename_auth_key(&pubkey, Some("desktop"))
434 .await
435 .unwrap();
436
437 let renamed = settings_store.get_auth_key(&pubkey).await.unwrap();
439 assert_eq!(renamed.name(), Some("desktop"));
440 assert_eq!(renamed.permissions(), &Permission::Write(5));
441 assert_eq!(renamed.status(), &KeyStatus::Active);
442
443 settings_store.rename_auth_key(&pubkey, None).await.unwrap();
445
446 let unnamed = settings_store.get_auth_key(&pubkey).await.unwrap();
447 assert_eq!(unnamed.name(), None);
448 assert_eq!(unnamed.permissions(), &Permission::Write(5));
449 }
450
451 #[tokio::test]
452 async fn test_multiple_auth_key_writes() {
453 let (_instance, database) = create_test_database().await;
454 let transaction = database.new_transaction().await.unwrap();
455 let settings_store = SettingsStore::new(&transaction).unwrap();
456
457 let pubkey1 = PublicKey::random();
459 let pubkey2 = PublicKey::random();
460
461 settings_store
463 .set_auth_key(
464 &pubkey1,
465 AuthKey::active(Some("admin"), Permission::Admin(1)),
466 )
467 .await
468 .unwrap();
469 settings_store
470 .set_auth_key(
471 &pubkey2,
472 AuthKey::active(Some("writer"), Permission::Write(5)),
473 )
474 .await
475 .unwrap();
476
477 let auth_settings = settings_store.auth_snapshot().await.unwrap();
479 let all_keys = auth_settings.get_all_keys().unwrap();
480 assert!(all_keys.len() >= 2); assert!(all_keys.contains_key(&pubkey1.to_string()));
482 assert!(all_keys.contains_key(&pubkey2.to_string()));
483 }
484
485 #[tokio::test]
486 async fn test_auth_doc_for_validation() {
487 let (_instance, database) = create_test_database().await;
488 let transaction = database.new_transaction().await.unwrap();
489 let settings_store = SettingsStore::new(&transaction).unwrap();
490
491 let pubkey = PublicKey::random();
493 let auth_key = AuthKey::active(Some("validator"), Permission::Read);
494 settings_store
495 .set_auth_key(&pubkey, auth_key)
496 .await
497 .unwrap();
498
499 let auth_doc = settings_store.get_auth_doc_for_validation().await.unwrap();
501
502 let auth_settings: AuthSettings = auth_doc.into();
504 let validator_key = auth_settings.get_key_by_pubkey(&pubkey).unwrap();
505 assert_eq!(validator_key.name(), Some("validator"));
506 }
507
508 #[tokio::test]
512 async fn test_auth_settings_entries_are_incremental() {
513 let (_instance, database) = create_test_database().await;
514
515 let pubkey_a = PublicKey::random();
516 let pubkey_b = PublicKey::random();
517
518 let txn1 = database.new_transaction().await.unwrap();
520 let settings1 = SettingsStore::new(&txn1).unwrap();
521 settings1
522 .set_auth_key(
523 &pubkey_a,
524 AuthKey::active(Some("key_a"), Permission::Write(5)),
525 )
526 .await
527 .unwrap();
528 txn1.commit().await.unwrap();
529
530 let txn2 = database.new_transaction().await.unwrap();
532 let settings2 = SettingsStore::new(&txn2).unwrap();
533 settings2
534 .set_auth_key(
535 &pubkey_b,
536 AuthKey::active(Some("key_b"), Permission::Admin(1)),
537 )
538 .await
539 .unwrap();
540 let entry_id_2 = txn2.commit().await.unwrap();
541
542 let entry2 = database.get_entry(&entry_id_2).await.unwrap();
544 let raw_2: Doc = serde_json::from_str(entry2.data("_settings").unwrap()).unwrap();
545
546 assert!(
547 raw_2.get(format!("auth.keys.{pubkey_a}")).is_none(),
548 "SettingsStore entry should NOT contain key A from prior entry (incremental)"
549 );
550 assert!(
551 raw_2.get(format!("auth.keys.{pubkey_b}")).is_some(),
552 "SettingsStore entry should contain key B"
553 );
554
555 let txn3 = database.new_transaction().await.unwrap();
557 let settings3 = SettingsStore::new(&txn3).unwrap();
558 let auth = settings3.auth_snapshot().await.unwrap();
559 assert!(
560 auth.get_key_by_pubkey(&pubkey_a).is_ok(),
561 "Merged view should contain key A"
562 );
563 assert!(
564 auth.get_key_by_pubkey(&pubkey_b).is_ok(),
565 "Merged view should contain key B"
566 );
567 }
568
569 #[tokio::test]
570 async fn test_error_handling() {
571 let (_instance, database) = create_test_database().await;
572 let transaction = database.new_transaction().await.unwrap();
573 let settings_store = SettingsStore::new(&transaction).unwrap();
574
575 let nonexistent_pubkey = PublicKey::random();
577 let result = settings_store.get_auth_key(&nonexistent_pubkey).await;
578 assert!(result.is_err());
579 if let Err(Error::Auth(auth_err)) = result {
580 assert!(auth_err.is_not_found());
581 } else {
582 panic!("Expected Auth(KeyNotFound) error");
583 }
584
585 let revoke_result = settings_store.revoke_auth_key(&nonexistent_pubkey).await;
587 assert!(revoke_result.is_err());
588 }
589}