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