1pub mod oidc;
11pub mod basic;
12
13#[cfg(test)]
14mod tests;
15
16use std::collections::HashMap;
17use std::future::Future;
18use std::pin::Pin;
19use async_trait::async_trait;
20use std::{fmt, sync::Arc};
21use once_cell::sync::Lazy;
22use serde::Deserialize;
23use std::sync::RwLock as StdRwLock;
24use crate::core::{ProxyError, ProxyRequest, ProxyResponse};
25use crate::{debug_fmt, error_fmt, trace_fmt};
26use crate::security::oidc::{OidcConfig, OidcProvider};
27use crate::security::basic::{BasicAuthConfig, BasicAuthProvider};
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum SecurityStage {
32 Pre,
33 Post,
34 Both,
35}
36
37impl SecurityStage {
38 pub const fn is_pre(self) -> bool { matches!(self, Self::Pre | Self::Both) }
39 pub const fn is_post(self) -> bool { matches!(self, Self::Post | Self::Both) }
40}
41
42#[async_trait]
44pub trait SecurityProvider: fmt::Debug + Send + Sync {
45 fn stage(&self) -> SecurityStage;
47 fn name(&self) -> &str;
49
50 async fn pre(
52 &self,
53 request: ProxyRequest,
54 ) -> Result<ProxyRequest, ProxyError> {
55 trace_fmt!("SecurityChain", "Security provider '{}' skipping pre-auth (default implementation)", self.name());
56 Ok(request)
57 }
58
59 async fn post(
61 &self,
62 _request: ProxyRequest,
63 response: ProxyResponse,
64 ) -> Result<ProxyResponse, ProxyError> {
65 trace_fmt!("SecurityChain", "Security provider '{}' skipping post-auth (default implementation)", self.name());
66 Ok(response)
67 }
68}
69
70#[derive(Debug)]
72pub struct SecurityChain {
73 providers: Vec<Arc<dyn SecurityProvider>>,
74}
75
76impl Default for SecurityChain {
77 fn default() -> Self {
78 Self::new()
79 }
80}
81
82impl SecurityChain {
83 pub fn new() -> Self {
84 Self { providers: Vec::new() }
85 }
86
87 pub async fn from_configs(cfgs: Vec<ProviderConfig>) -> Result<Self, ProxyError> {
89 let mut chain = SecurityChain::new();
90
91 debug_fmt!("SecurityChain", "Building security chain from {} provider configs", cfgs.len());
92
93 for c in cfgs {
94 let provider = SecurityProviderFactory::create_provider(&c.type_, c.config).await?;
95 chain.add(provider);
96 }
97
98 Ok(chain)
99 }
100
101 pub fn add(&mut self, p: Arc<dyn SecurityProvider>) { self.providers.push(p); }
102
103 pub async fn apply_pre(
104 &self,
105 mut req: ProxyRequest,
106 ) -> Result<ProxyRequest, ProxyError> {
107 trace_fmt!("SecurityChain", "Applying security pre-auth chain with {} providers", self.providers.len());
108
109 for p in &self.providers {
110 if p.stage().is_pre() {
111 trace_fmt!("SecurityChain", "Running pre-auth provider: {}", p.name());
112 match p.pre(req).await {
113 Ok(new_req) => {
114 req = new_req;
115 },
116 Err(e) => {
117 let err = ProxyError::SecurityError(format!("{}: {}", p.name(), e));
118 error_fmt!("SecurityChain", "Security pre-auth failed: {}", err);
119 return Err(err);
120 }
121 }
122 }
123 }
124 Ok(req)
125 }
126
127 pub async fn apply_post(
128 &self,
129 req: ProxyRequest,
130 mut resp: ProxyResponse,
131 ) -> Result<ProxyResponse, ProxyError> {
132 trace_fmt!("SecurityChain", "Applying security post-auth chain with {} providers", self.providers.len());
133
134 for p in &self.providers {
135 if p.stage().is_post() {
136 trace_fmt!("SecurityChain", "Running post-auth provider: {}", p.name());
137 match p.post(req.clone(), resp).await {
138 Ok(new_resp) => {
139 resp = new_resp;
140 },
141 Err(e) => {
142 let err = ProxyError::SecurityError(format!("{}: {}", p.name(), e));
143 error_fmt!("SecurityChain", "Security post-auth failed: {}", err);
144 return Err(err);
145 }
146 }
147 }
148 }
149 Ok(resp)
150 }
151}
152
153#[derive(Debug, Deserialize)]
154pub struct ProviderConfig {
155 #[serde(rename = "type")]
156 pub type_: String,
157 pub config: serde_json::Value,
158}
159
160
161pub type SecurityProviderConstructor =
165fn(serde_json::Value) -> Pin<Box<dyn Future<Output = Result<Arc<dyn SecurityProvider>, ProxyError>> + Send>>;
166
167
168static SECURITY_PROVIDER_REGISTRY: Lazy<StdRwLock<HashMap<String, SecurityProviderConstructor>>> =
170 Lazy::new(|| StdRwLock::new(HashMap::new()));
171
172pub fn register_security_provider(name: &str, ctor: SecurityProviderConstructor) {
174 SECURITY_PROVIDER_REGISTRY
175 .write()
176 .expect("SECURITY_PROVIDER_REGISTRY poisoned")
177 .insert(name.to_string(), ctor);
178}
179
180fn get_registered_security_provider(name: &str) -> Option<SecurityProviderConstructor> {
182 SECURITY_PROVIDER_REGISTRY
183 .read()
184 .expect("SECURITY_PROVIDER_REGISTRY poisoned")
185 .get(name)
186 .copied()
187}
188
189#[derive(Debug)]
191pub struct SecurityProviderFactory;
192
193impl SecurityProviderFactory {
194 pub async fn create_provider(
196 provider_type: &str,
197 config: serde_json::Value,
198 ) -> Result<Arc<dyn SecurityProvider>, ProxyError> {
199 debug_fmt!("SecurityProviderFactory", "Creating security provider of type '{}'", provider_type);
200 if let Some(ctor) = get_registered_security_provider(provider_type) {
201 return ctor(config).await;
202 }
203
204 match provider_type {
205 "oidc" => {
206 let oidc_config: OidcConfig = serde_json::from_value(config)
207 .map_err(|e| ProxyError::SecurityError(format!("Invalid OIDC provider config: {e}")))?;
208 let provider = OidcProvider::discover(oidc_config).await?;
209 Ok(Arc::new(provider))
210 },
211 "basic" => {
212 let basic_auth_config: BasicAuthConfig = serde_json::from_value(config)
213 .map_err(|e| ProxyError::SecurityError(format!("Invalid Basic Auth provider config: {e}")))?;
214 let provider = BasicAuthProvider::new(basic_auth_config)?;
215 Ok(Arc::new(provider))
216 },
217 _ => Err(ProxyError::SecurityError(format!("Unknown security provider type: {provider_type}"))),
218 }
219 }
220}