eidetica/auth/validation/resolver.rs
1//! Key resolution for authentication
2//!
3//! This module handles resolving authentication keys, both direct keys
4//! and delegation paths.
5
6use std::collections::HashMap;
7
8use super::delegation::DelegationResolver;
9use crate::{
10 Instance, Result,
11 auth::{
12 errors::AuthError,
13 settings::AuthSettings,
14 types::{KeyHint, ResolvedAuth, SigKey},
15 },
16};
17
18/// Key resolver for handling both direct and delegated key resolution
19pub struct KeyResolver {
20 /// Cache for resolved authentication data to improve performance
21 auth_cache: HashMap<String, ResolvedAuth>,
22 /// Delegation resolver for handling complex delegation paths
23 delegation_resolver: DelegationResolver,
24}
25
26impl KeyResolver {
27 /// Create a new key resolver
28 pub fn new() -> Self {
29 Self {
30 auth_cache: HashMap::new(),
31 delegation_resolver: DelegationResolver::new(),
32 }
33 }
34
35 /// Resolve authentication identifier to concrete authentication information
36 ///
37 /// Returns all matching ResolvedAuth entries. For name hints that match
38 /// multiple keys, all matches are returned so the caller can try signature
39 /// verification against each.
40 ///
41 /// # Arguments
42 /// * `sig_key` - The signature key identifier to resolve
43 /// * `auth_settings` - Authentication settings containing auth configuration
44 /// * `instance` - Instance for loading delegated trees (required for Delegation sig_key)
45 pub async fn resolve_sig_key(
46 &mut self,
47 sig_key: &SigKey,
48 auth_settings: &AuthSettings,
49 instance: Option<&Instance>,
50 ) -> Result<Vec<ResolvedAuth>> {
51 self.resolve_sig_key_with_depth(sig_key, auth_settings, instance, 0)
52 .await
53 }
54
55 /// Resolve authentication identifier with recursion depth tracking
56 ///
57 /// This internal method tracks delegation depth to prevent infinite loops
58 /// and ensures that delegation chains don't exceed reasonable limits.
59 ///
60 /// Returns all matching ResolvedAuth entries.
61 pub async fn resolve_sig_key_with_depth(
62 &mut self,
63 sig_key: &SigKey,
64 auth_settings: &AuthSettings,
65 instance: Option<&Instance>,
66 depth: usize,
67 ) -> Result<Vec<ResolvedAuth>> {
68 // Prevent infinite recursion and overly deep delegation chains
69 const MAX_DELEGATION_DEPTH: usize = 10;
70 if depth >= MAX_DELEGATION_DEPTH {
71 return Err(AuthError::DelegationDepthExceeded {
72 depth: MAX_DELEGATION_DEPTH,
73 }
74 .into());
75 }
76
77 match sig_key {
78 SigKey::Direct(hint) => self.resolve_direct_key(hint, auth_settings),
79 SigKey::Delegation { path, hint } => {
80 let instance = instance.ok_or_else(|| AuthError::DatabaseRequired {
81 operation: "delegated tree resolution".to_string(),
82 })?;
83 self.delegation_resolver
84 .resolve_delegation_path_with_depth(path, hint, auth_settings, instance, depth)
85 .await
86 }
87 }
88 }
89
90 /// Resolve a direct key reference from the main tree's auth settings
91 ///
92 /// Returns all matching ResolvedAuth entries. For name hints that match
93 /// multiple keys, all matches are returned so the caller can try signature
94 /// verification against each.
95 pub fn resolve_direct_key(
96 &mut self,
97 hint: &KeyHint,
98 auth_settings: &AuthSettings,
99 ) -> Result<Vec<ResolvedAuth>> {
100 // Use AuthSettings.resolve_hint which handles:
101 // - Global permission (returns single match with actual pubkey)
102 // - Direct pubkey lookup (returns single match)
103 // - Name lookup (may return multiple matches)
104 let matches = auth_settings.resolve_hint(hint)?;
105 if matches.is_empty() {
106 return Err(AuthError::KeyNotFound {
107 key_name: format!("hint({:?})", hint.hint_type()),
108 }
109 .into());
110 }
111
112 Ok(matches)
113 }
114
115 /// Clear the authentication cache
116 pub fn clear_cache(&mut self) {
117 self.auth_cache.clear();
118 }
119}
120
121impl Default for KeyResolver {
122 fn default() -> Self {
123 Self::new()
124 }
125}