1use super::resolver::AuthResolver;
2use crate::security::AuthIdentity;
3use macp_core::error::MacpError;
4use tonic::metadata::MetadataMap;
5
6pub struct AuthResolverChain {
7 resolvers: Vec<Box<dyn AuthResolver>>,
8}
9
10impl AuthResolverChain {
11 pub fn new(resolvers: Vec<Box<dyn AuthResolver>>) -> Self {
12 let names: Vec<&str> = resolvers.iter().map(|r| r.name()).collect();
13 tracing::info!(chain = ?names, "auth resolver chain initialized");
14 Self { resolvers }
15 }
16
17 pub async fn authenticate(&self, metadata: &MetadataMap) -> Result<AuthIdentity, MacpError> {
18 for resolver in &self.resolvers {
19 match resolver.resolve(metadata).await {
20 Ok(Some(identity)) => {
21 tracing::debug!(
22 resolver = resolver.name(),
23 sender = %identity.sender,
24 "authenticated"
25 );
26 return Ok(identity.into());
27 }
28 Ok(None) => continue,
29 Err(e) => {
30 tracing::warn!(
31 resolver = resolver.name(),
32 error = %e,
33 "auth resolver rejected credential"
34 );
35 return Err(MacpError::Unauthenticated);
36 }
37 }
38 }
39 Err(MacpError::Unauthenticated)
40 }
41}