eidetica/auth/validation/
permissions.rs1use crate::{
7 Error, Result,
8 auth::{
9 crypto::PublicKey,
10 errors::AuthError,
11 settings::AuthSettings,
12 types::{Operation, Permission, ResolvedAuth, SigKey},
13 validation::AuthValidator,
14 },
15};
16
17pub async fn resolve_identity_permission(
31 pubkey: &PublicKey,
32 identity: &SigKey,
33 auth_settings: &AuthSettings,
34 instance: Option<&crate::Instance>,
35) -> Result<Permission> {
36 match identity {
37 SigKey::Direct { hint } if hint.is_global() => {
38 if let Some(embedded_pubkey) = &hint.pubkey
39 && *embedded_pubkey != *pubkey
40 {
41 return Err(Error::Auth(Box::new(AuthError::SigningKeyMismatch {
42 reason: format!(
43 "pubkey '{pubkey}' but global identity claims '{embedded_pubkey}'"
44 ),
45 })));
46 }
47 auth_settings.get_global_permission().ok_or_else(|| {
48 Error::Auth(Box::new(AuthError::InvalidAuthConfiguration {
49 reason: "Global '*' permission not configured".to_string(),
50 }))
51 })
52 }
53 SigKey::Direct { hint } => match (&hint.pubkey, &hint.name) {
54 (Some(claimed_pubkey), _) => {
55 if *claimed_pubkey != *pubkey {
56 return Err(Error::Auth(Box::new(AuthError::SigningKeyMismatch {
57 reason: format!("pubkey '{pubkey}' but identity claims '{claimed_pubkey}'"),
58 })));
59 }
60 if let Ok(auth_key) = auth_settings.get_key_by_pubkey(pubkey) {
69 return Ok(*auth_key.permissions());
70 }
71 if let Some(global) = auth_settings.get_global_permission() {
72 return Ok(global);
73 }
74 Err(Error::Auth(Box::new(AuthError::KeyNotFound {
75 key_name: pubkey.to_string(),
76 })))
77 }
78 (_, Some(name)) => {
79 let matches = auth_settings.find_keys_by_name(name);
80 if matches.is_empty() {
81 if let Some(global) = auth_settings.get_global_permission() {
85 return Ok(global);
86 }
87 return Err(Error::Auth(Box::new(AuthError::KeyNotFound {
88 key_name: name.clone(),
89 })));
90 }
91 let pubkey_str = pubkey.to_string();
92 let (_, auth_key) = matches
93 .iter()
94 .find(|(pk, _)| *pk == pubkey_str)
95 .ok_or_else(|| {
96 Error::Auth(Box::new(AuthError::SigningKeyMismatch {
97 reason: format!(
98 "pubkey '{pubkey}' but no key named '{name}' has that pubkey"
99 ),
100 }))
101 })?;
102 Ok(*auth_key.permissions())
103 }
104 _ => Err(Error::Auth(Box::new(AuthError::InvalidAuthConfiguration {
105 reason: "identity has empty hint".to_string(),
106 }))),
107 },
108 SigKey::Delegation { .. } => {
109 let mut validator = AuthValidator::new();
110 let resolved_auths = validator
111 .resolve_sig_key(identity, auth_settings, instance)
112 .await
113 .map_err(|e| {
114 Error::Auth(Box::new(AuthError::InvalidAuthConfiguration {
115 reason: format!("Delegation resolution failed: {e}"),
116 }))
117 })?;
118
119 resolved_auths
120 .into_iter()
121 .find(|ra| ra.public_key == *pubkey)
122 .map(|ra| ra.effective_permission)
123 .ok_or_else(|| {
124 Error::Auth(Box::new(AuthError::SigningKeyMismatch {
125 reason: format!("no resolved delegation key matches pubkey '{pubkey}'"),
126 }))
127 })
128 }
129 }
130}
131
132pub fn check_permissions(resolved: &ResolvedAuth, operation: &Operation) -> Result<bool> {
134 match operation {
135 Operation::WriteData => {
136 Ok(resolved.effective_permission.can_write()
137 || resolved.effective_permission.can_admin())
138 }
139 Operation::WriteSettings => Ok(resolved.effective_permission.can_admin()),
140 }
141}