1use std::collections::HashMap;
8
9use serde::{Deserialize, Serialize};
10
11use super::errors::AuthError;
12use crate::{
13 Result,
14 auth::{
15 crypto::PublicKey,
16 types::{AuthKey, DelegatedTreeRef, KeyHint, KeyStatus, Permission, ResolvedAuth, SigKey},
17 },
18 crdt::{Doc, doc::Value},
19 entry::ID,
20};
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct AuthSettings {
29 inner: Doc,
31}
32
33impl From<Doc> for AuthSettings {
34 fn from(doc: Doc) -> Self {
35 Self { inner: doc }
36 }
37}
38
39impl From<AuthSettings> for Doc {
40 fn from(settings: AuthSettings) -> Doc {
41 settings.inner
42 }
43}
44
45impl AuthSettings {
46 pub fn new() -> Self {
48 Self { inner: Doc::new() }
49 }
50
51 pub fn as_doc(&self) -> &Doc {
53 &self.inner
54 }
55
56 pub fn as_doc_mut(&mut self) -> &mut Doc {
58 &mut self.inner
59 }
60
61 pub fn add_key(&mut self, pubkey: &PublicKey, key: AuthKey) -> Result<()> {
69 let pubkey_str = pubkey.to_string();
70
71 if self.get_key_by_str(&pubkey_str).is_ok() {
73 return Err(AuthError::KeyAlreadyExists {
74 key_name: pubkey_str,
75 }
76 .into());
77 }
78
79 self.inner.set(format!("keys.{pubkey_str}"), key);
80 Ok(())
81 }
82
83 pub fn overwrite_key(&mut self, pubkey: &PublicKey, key: AuthKey) -> Result<()> {
85 let pubkey_str = pubkey.to_string();
86 self.inner.set(format!("keys.{pubkey_str}"), key);
87 Ok(())
88 }
89
90 pub fn get_key_by_pubkey(&self, pubkey: &PublicKey) -> Result<AuthKey> {
92 self.get_key_by_str(&pubkey.to_string())
93 }
94
95 fn get_key_by_str(&self, pubkey: &str) -> Result<AuthKey> {
100 match self.inner.get(format!("keys.{pubkey}")) {
101 Some(Value::Doc(doc)) => AuthKey::try_from(doc).map_err(|e| {
102 AuthError::InvalidKeyFormat {
103 reason: e.to_string(),
104 }
105 .into()
106 }),
107 Some(_) => Err(AuthError::InvalidKeyFormat {
108 reason: format!("key '{pubkey}' is not a Doc"),
109 }
110 .into()),
111 None => Err(AuthError::KeyNotFound {
112 key_name: pubkey.to_string(),
113 }
114 .into()),
115 }
116 }
117
118 pub fn find_keys_by_name(&self, name: &str) -> Vec<(String, AuthKey)> {
122 let mut matches = Vec::new();
123
124 if let Ok(all_keys) = self.get_all_keys() {
126 for (pubkey, auth_key) in all_keys {
127 if auth_key.name() == Some(name) {
128 matches.push((pubkey, auth_key));
129 }
130 }
131 }
132
133 matches.sort_by(|a, b| a.0.cmp(&b.0));
135 matches
136 }
137
138 pub fn get_all_keys(&self) -> Result<HashMap<String, AuthKey>> {
140 let mut result: HashMap<String, AuthKey> = HashMap::new();
141
142 if let Some(Value::Doc(keys_doc)) = self.inner.get("keys") {
144 for (pubkey, value) in keys_doc.iter() {
145 if let Value::Doc(key_doc) = value
146 && let Ok(auth_key) = AuthKey::try_from(key_doc)
147 {
148 result.insert(pubkey.clone(), auth_key);
149 }
150 }
151 }
152
153 Ok(result)
154 }
155
156 pub fn rename_key(&mut self, pubkey: &PublicKey, name: Option<&str>) -> Result<()> {
161 let pubkey_str = pubkey.to_string();
162 let mut auth_key = self.get_key_by_str(&pubkey_str)?;
163 auth_key.set_name(name);
164 self.inner.set(format!("keys.{pubkey_str}"), auth_key);
165 Ok(())
166 }
167
168 pub fn revoke_key(&mut self, pubkey: &PublicKey) -> Result<()> {
170 let pubkey_str = pubkey.to_string();
171 let mut auth_key = self.get_key_by_str(&pubkey_str)?;
172 auth_key.set_status(KeyStatus::Revoked);
173 self.inner.set(format!("keys.{pubkey_str}"), auth_key);
174 Ok(())
175 }
176
177 pub fn add_delegated_tree(&mut self, tree_ref: DelegatedTreeRef) -> Result<()> {
184 let root_id = tree_ref.tree.root.as_str().to_string();
185 self.inner.set(format!("delegations.{root_id}"), tree_ref);
186 Ok(())
187 }
188
189 pub fn get_delegated_tree(&self, root_id: &ID) -> Result<DelegatedTreeRef> {
191 self.get_delegated_tree_by_str(root_id.as_str())
192 }
193
194 pub fn get_delegated_tree_by_str(&self, root_id: &str) -> Result<DelegatedTreeRef> {
199 match self.inner.get(format!("delegations.{root_id}")) {
200 Some(Value::Doc(doc)) => DelegatedTreeRef::try_from(doc).map_err(|e| {
201 AuthError::InvalidAuthConfiguration {
202 reason: format!("Invalid delegated tree format: {e}"),
203 }
204 .into()
205 }),
206 Some(_) => Err(AuthError::InvalidAuthConfiguration {
207 reason: format!("delegation '{root_id}' is not a Doc"),
208 }
209 .into()),
210 None => Err(AuthError::DelegationNotFound {
211 tree_id: root_id.to_string(),
212 }
213 .into()),
214 }
215 }
216
217 pub fn get_all_delegated_trees(&self) -> Result<HashMap<ID, DelegatedTreeRef>> {
221 let mut result: HashMap<ID, DelegatedTreeRef> = HashMap::new();
222
223 if let Some(Value::Doc(delegations_doc)) = self.inner.get("delegations") {
225 for (root_id_str, value) in delegations_doc.iter() {
226 if let Value::Doc(doc) = value
227 && let Ok(tree_ref) = DelegatedTreeRef::try_from(doc)
228 {
229 let root_id = ID::new(root_id_str);
230 result.insert(root_id, tree_ref);
231 }
232 }
233 }
234
235 Ok(result)
236 }
237
238 pub fn set_global_permission(&mut self, key: AuthKey) {
245 self.inner.set("global", key);
246 }
247
248 pub fn get_global_key(&self) -> Result<AuthKey> {
252 match self.inner.get("global") {
253 Some(Value::Doc(doc)) => AuthKey::try_from(doc).map_err(|e| {
254 AuthError::InvalidKeyFormat {
255 reason: e.to_string(),
256 }
257 .into()
258 }),
259 Some(_) => Err(AuthError::InvalidKeyFormat {
260 reason: "global key is not a Doc".to_string(),
261 }
262 .into()),
263 None => Err(AuthError::KeyNotFound {
264 key_name: "global".to_string(),
265 }
266 .into()),
267 }
268 }
269
270 pub fn resolve_hint(&self, hint: &KeyHint) -> Result<Vec<ResolvedAuth>> {
284 if hint.is_global() {
286 let global_key =
288 self.get_global_key()
289 .map_err(|_| AuthError::InvalidAuthConfiguration {
290 reason: "Global hint used but no global permission configured".to_string(),
291 })?;
292
293 let actual_pubkey =
295 hint.pubkey
296 .as_ref()
297 .ok_or_else(|| AuthError::InvalidAuthConfiguration {
298 reason: "Global hint has no pubkey".to_string(),
299 })?;
300
301 return Ok(vec![ResolvedAuth {
304 public_key: actual_pubkey.clone(),
305 effective_permission: *global_key.permissions(),
306 key_status: global_key.status().clone(),
307 }]);
308 }
309
310 if let Some(pubkey) = &hint.pubkey {
312 return match self.get_key_by_pubkey(pubkey) {
313 Ok(key) => Ok(vec![ResolvedAuth {
314 public_key: pubkey.clone(),
315 effective_permission: *key.permissions(),
316 key_status: key.status().clone(),
317 }]),
318 Err(e) => Err(e),
319 };
320 }
321
322 if let Some(name) = &hint.name {
324 let matches = self.find_keys_by_name(name);
325 if matches.is_empty() {
326 return Err(AuthError::KeyNotFound {
327 key_name: name.clone(),
328 }
329 .into());
330 }
331 let mut results = Vec::with_capacity(matches.len());
333 for (pubkey, auth_key) in matches {
334 results.push(ResolvedAuth {
335 public_key: PublicKey::from_prefixed_string(&pubkey)?,
336 effective_permission: *auth_key.permissions(),
337 key_status: auth_key.status().clone(),
338 });
339 }
340 return Ok(results);
341 }
342
343 Ok(vec![])
345 }
346
347 pub fn has_global_permission(&self) -> bool {
351 self.get_global_permission().is_some()
352 }
353
354 pub fn get_global_permission(&self) -> Option<Permission> {
356 if let Ok(key) = self.get_global_key()
357 && *key.status() == KeyStatus::Active
358 {
359 Some(*key.permissions())
360 } else {
361 None
362 }
363 }
364
365 pub fn global_permission_grants_access(&self, requested_permission: &Permission) -> bool {
367 if let Some(global_perm) = self.get_global_permission() {
368 global_perm >= *requested_permission
369 } else {
370 false
371 }
372 }
373
374 pub fn can_access(&self, pubkey: &PublicKey, requested_permission: &Permission) -> bool {
378 if let Ok(auth_key) = self.get_key_by_pubkey(pubkey)
380 && *auth_key.status() == KeyStatus::Active
381 && *auth_key.permissions() >= *requested_permission
382 {
383 return true;
384 }
385
386 self.global_permission_grants_access(requested_permission)
388 }
389
390 pub fn find_all_sigkeys_for_pubkey(&self, pubkey: &PublicKey) -> Vec<(SigKey, Permission)> {
394 let mut results = Vec::new();
395
396 if let Ok(auth_key) = self.get_key_by_pubkey(pubkey) {
398 results.push((SigKey::from_pubkey(pubkey), *auth_key.permissions()));
399 }
400
401 if let Some(global_perm) = self.get_global_permission() {
403 results.push((SigKey::global(pubkey), global_perm));
404 }
405
406 results.sort_by(|a, b| b.1.cmp(&a.1));
411 results
412 }
413
414 pub fn resolve_sig_key_for_operation(
418 &self,
419 pubkey: &PublicKey,
420 ) -> Result<(SigKey, Permission)> {
421 let matches = self.find_all_sigkeys_for_pubkey(pubkey);
422
423 matches.into_iter().next().ok_or_else(|| {
424 AuthError::PermissionDenied {
425 reason: format!("No active key found for pubkey: {pubkey}"),
426 }
427 .into()
428 })
429 }
430
431 pub fn can_modify_key(
435 &self,
436 signing_key: &ResolvedAuth,
437 target_pubkey: &PublicKey,
438 ) -> Result<bool> {
439 if !signing_key.effective_permission.can_admin() {
441 return Ok(false);
442 }
443
444 let target_key = self.get_key_by_pubkey(target_pubkey)?;
446
447 Ok(signing_key.effective_permission >= *target_key.permissions())
449 }
450
451 pub fn can_create_key(
453 &self,
454 signing_key: &ResolvedAuth,
455 new_key_permissions: &Permission,
456 ) -> Result<bool> {
457 if !signing_key.effective_permission.can_admin() {
459 return Ok(false);
460 }
461
462 Ok(signing_key.effective_permission >= *new_key_permissions)
464 }
465}
466
467impl Default for AuthSettings {
468 fn default() -> Self {
469 Self::new()
470 }
471}
472
473#[cfg(test)]
474mod tests {
475 use super::*;
476 use crate::crdt::CRDT;
477
478 #[test]
479 fn test_auth_settings_basic_operations() {
480 let mut settings = AuthSettings::new();
481
482 let pubkey = PublicKey::random();
483 let auth_key = AuthKey::active(Some("laptop"), Permission::Write(10));
484
485 settings.add_key(&pubkey, auth_key.clone()).unwrap();
486
487 let retrieved = settings.get_key_by_pubkey(&pubkey).unwrap();
489 assert_eq!(retrieved.name(), Some("laptop"));
490 assert_eq!(retrieved.permissions(), auth_key.permissions());
491 assert_eq!(retrieved.status(), auth_key.status());
492 }
493
494 #[test]
495 fn test_find_keys_by_name() {
496 let mut settings = AuthSettings::new();
497
498 let pubkey1 = PublicKey::random();
499 let pubkey2 = PublicKey::random();
500
501 settings
503 .add_key(
504 &pubkey1,
505 AuthKey::active(Some("device"), Permission::Write(10)),
506 )
507 .unwrap();
508 settings
509 .add_key(
510 &pubkey2,
511 AuthKey::active(Some("device"), Permission::Admin(1)),
512 )
513 .unwrap();
514
515 let matches = settings.find_keys_by_name("device");
517 assert_eq!(matches.len(), 2);
518 }
519
520 #[test]
521 fn test_revoke_key() {
522 let mut settings = AuthSettings::new();
523
524 let pubkey = PublicKey::random();
525 let auth_key = AuthKey::active(Some("laptop"), Permission::Admin(5));
526
527 settings.add_key(&pubkey, auth_key).unwrap();
528
529 settings.revoke_key(&pubkey).unwrap();
531
532 let retrieved = settings.get_key_by_pubkey(&pubkey).unwrap();
534 assert_eq!(retrieved.status(), &KeyStatus::Revoked);
535 }
536
537 #[test]
538 fn test_global_permission() {
539 let mut settings = AuthSettings::new();
540
541 assert!(!settings.has_global_permission());
543 assert_eq!(settings.get_global_permission(), None);
544
545 let global_key = AuthKey::active(None, Permission::Write(10));
547 settings.set_global_permission(global_key);
548
549 assert!(settings.has_global_permission());
551 assert_eq!(
552 settings.get_global_permission(),
553 Some(Permission::Write(10))
554 );
555
556 assert!(settings.global_permission_grants_access(&Permission::Read));
558 assert!(settings.global_permission_grants_access(&Permission::Write(10)));
559 assert!(!settings.global_permission_grants_access(&Permission::Write(5)));
560 assert!(!settings.global_permission_grants_access(&Permission::Admin(10)));
561 }
562
563 #[test]
564 fn test_resolve_hint_pubkey() {
565 let mut settings = AuthSettings::new();
566
567 let pubkey = PublicKey::random();
568 settings
569 .add_key(
570 &pubkey,
571 AuthKey::active(Some("laptop"), Permission::Write(10)),
572 )
573 .unwrap();
574
575 let hint = KeyHint::from_pubkey(&pubkey);
577 let matches = settings.resolve_hint(&hint).unwrap();
578 assert_eq!(matches.len(), 1);
579 assert_eq!(matches[0].public_key, pubkey);
580 assert_eq!(matches[0].effective_permission, Permission::Write(10));
581 }
582
583 #[test]
584 fn test_resolve_hint_name() {
585 let mut settings = AuthSettings::new();
586
587 let pubkey = PublicKey::random();
588 settings
589 .add_key(
590 &pubkey,
591 AuthKey::active(Some("laptop"), Permission::Write(10)),
592 )
593 .unwrap();
594
595 let hint = KeyHint::from_name("laptop");
597 let matches = settings.resolve_hint(&hint).unwrap();
598 assert_eq!(matches.len(), 1);
599 assert_eq!(matches[0].public_key, pubkey);
600 assert_eq!(matches[0].effective_permission, Permission::Write(10));
601 }
602
603 #[test]
604 fn test_resolve_hint_global() {
605 let mut settings = AuthSettings::new();
606
607 settings.set_global_permission(AuthKey::active(None, Permission::Write(10)));
609
610 let actual_pubkey = PublicKey::random();
611 let hint = KeyHint::global(&actual_pubkey);
612 let matches = settings.resolve_hint(&hint).unwrap();
613
614 assert_eq!(matches.len(), 1);
615 assert_eq!(matches[0].public_key, actual_pubkey);
616 assert_eq!(matches[0].effective_permission, Permission::Write(10));
617 }
618
619 #[test]
620 fn test_find_all_sigkeys_for_pubkey() {
621 let mut settings = AuthSettings::new();
622
623 let pubkey = PublicKey::random();
624
625 let results = settings.find_all_sigkeys_for_pubkey(&pubkey);
627 assert_eq!(results.len(), 0);
628
629 settings
631 .add_key(
632 &pubkey,
633 AuthKey::active(Some("device1"), Permission::Write(5)),
634 )
635 .unwrap();
636
637 let results = settings.find_all_sigkeys_for_pubkey(&pubkey);
638 assert_eq!(results.len(), 1);
639
640 settings.set_global_permission(AuthKey::active(None, Permission::Write(10)));
642
643 let results = settings.find_all_sigkeys_for_pubkey(&pubkey);
644 assert_eq!(results.len(), 2);
645 }
646
647 #[test]
648 fn test_resolve_sig_key_for_operation() {
649 let mut settings = AuthSettings::new();
650
651 let pubkey = PublicKey::random();
652
653 let result = settings.resolve_sig_key_for_operation(&pubkey);
655 assert!(result.is_err());
656
657 settings
659 .add_key(
660 &pubkey,
661 AuthKey::active(Some("device"), Permission::Write(5)),
662 )
663 .unwrap();
664
665 let (sig_key, granted_perm) = settings.resolve_sig_key_for_operation(&pubkey).unwrap();
667 assert!(sig_key.has_pubkey_hint(&pubkey));
668 assert_eq!(granted_perm, Permission::Write(5));
669 }
670
671 #[test]
672 fn test_can_access() {
673 let mut settings = AuthSettings::new();
674
675 let pubkey = PublicKey::random();
676 let other_pubkey = PublicKey::random();
677
678 assert!(!settings.can_access(&pubkey, &Permission::Read));
680
681 settings
683 .add_key(
684 &pubkey,
685 AuthKey::active(Some("device"), Permission::Write(5)),
686 )
687 .unwrap();
688
689 assert!(settings.can_access(&pubkey, &Permission::Read));
691 assert!(settings.can_access(&pubkey, &Permission::Write(5)));
692 assert!(!settings.can_access(&pubkey, &Permission::Admin(1)));
693
694 assert!(!settings.can_access(&other_pubkey, &Permission::Read));
696
697 settings.set_global_permission(AuthKey::active(None, Permission::Read));
699
700 assert!(settings.can_access(&other_pubkey, &Permission::Read));
702 assert!(!settings.can_access(&other_pubkey, &Permission::Write(10)));
703 }
704
705 #[test]
706 fn test_auth_settings_merge() {
707 let mut settings1 = AuthSettings::new();
708 let mut settings2 = AuthSettings::new();
709
710 let pubkey1 = PublicKey::random();
711 let pubkey2 = PublicKey::random();
712
713 settings1
714 .add_key(
715 &pubkey1,
716 AuthKey::active(Some("key1"), Permission::Write(10)),
717 )
718 .unwrap();
719 settings2
720 .add_key(
721 &pubkey2,
722 AuthKey::active(Some("key2"), Permission::Admin(5)),
723 )
724 .unwrap();
725
726 let merged_doc = settings1.as_doc().merge(settings2.as_doc()).unwrap();
728 let merged_settings: AuthSettings = merged_doc.into();
729
730 assert!(merged_settings.get_key_by_pubkey(&pubkey1).is_ok());
732 assert!(merged_settings.get_key_by_pubkey(&pubkey2).is_ok());
733 }
734}