1pub mod oidc;
11
12#[cfg(test)]
13mod tests;
14
15use async_trait::async_trait;
16use std::{fmt, sync::Arc};
17use serde::Deserialize;
18use crate::core::{ProxyError, ProxyRequest, ProxyResponse};
19use crate::security::oidc::{OidcConfig, OidcProvider};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum SecurityStage {
24 Pre,
25 Post,
26 Both,
27}
28
29impl SecurityStage {
30 pub const fn is_pre(self) -> bool { matches!(self, Self::Pre | Self::Both) }
31 pub const fn is_post(self) -> bool { matches!(self, Self::Post | Self::Both) }
32}
33
34#[async_trait]
36pub trait SecurityProvider: fmt::Debug + Send + Sync {
37 fn stage(&self) -> SecurityStage;
39 fn name(&self) -> &str;
41
42 async fn pre(
44 &self,
45 request: ProxyRequest,
46 ) -> Result<ProxyRequest, ProxyError> {
47 Ok(request)
48 }
49
50 async fn post(
52 &self,
53 _request: ProxyRequest,
54 response: ProxyResponse,
55 ) -> Result<ProxyResponse, ProxyError> {
56 Ok(response)
57 }
58}
59
60#[derive(Debug)]
62pub struct SecurityChain {
63 providers: Vec<Arc<dyn SecurityProvider>>,
64 bypass_routes: Vec<String>,
65}
66
67impl SecurityChain {
68 pub fn new(bypass_routes: Vec<String>) -> Self {
69 Self { providers: Vec::new(), bypass_routes }
70 }
71
72 pub async fn from_configs(cfgs: Vec<ProviderConfig>) -> Result<Self, ProxyError> {
74 let mut chain = SecurityChain { providers: Vec::new(), bypass_routes: Vec::new() };
75
76 for c in cfgs {
77 match c {
78 ProviderConfig::Oidc { config } => {
79 let p = OidcProvider::discover(config).await?;
80 chain.add(Arc::new(p));
81 }
82 }
83 }
84
85 Ok(chain)
86 }
87
88 pub fn add(&mut self, p: Arc<dyn SecurityProvider>) { self.providers.push(p); }
89
90 fn is_bypassed(&self, path: &str) -> bool {
91 self.bypass_routes.iter().any(|p| path.starts_with(p))
92 }
93
94 pub async fn apply_pre(
95 &self,
96 mut req: ProxyRequest,
97 ) -> Result<ProxyRequest, ProxyError> {
98 if self.is_bypassed(&req.path) { return Ok(req); }
99 for p in &self.providers {
100 if p.stage().is_pre() {
101 req = p.pre(req).await
102 .map_err(|e| ProxyError::SecurityError(format!("{}: {e}", p.name())))?;
103 }
104 }
105 Ok(req)
106 }
107
108 pub async fn apply_post(
109 &self,
110 req: ProxyRequest,
111 mut resp: ProxyResponse,
112 ) -> Result<ProxyResponse, ProxyError> {
113 if self.is_bypassed(&req.path) { return Ok(resp); }
114 for p in &self.providers {
115 if p.stage().is_post() {
116 resp = p.post(req.clone(), resp).await
117 .map_err(|e| ProxyError::SecurityError(format!("{}: {e}", p.name())))?;
118 }
119 }
120 Ok(resp)
121 }
122}
123
124#[derive(Debug, Deserialize)]
125#[serde(tag = "type", rename_all = "lowercase")]
126pub enum ProviderConfig {
127 Oidc { config: OidcConfig },
128}