Development Documentation (main branch) - For stable release docs, see docs.rs/eidetica

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}