Skip to main content

wae_authentication/saml/
config.rs

1//! SAML 配置模块
2
3use std::collections::HashMap;
4
5/// SAML 绑定类型
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum SamlBinding {
8    /// HTTP Redirect 绑定
9    HttpRedirect,
10    /// HTTP POST 绑定
11    HttpPost,
12    /// HTTP Artifact 绑定
13    HttpArtifact,
14    /// SOAP 绑定
15    Soap,
16}
17
18impl SamlBinding {
19    /// 获取绑定 URI
20    pub fn uri(&self) -> &'static str {
21        match self {
22            SamlBinding::HttpRedirect => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
23            SamlBinding::HttpPost => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
24            SamlBinding::HttpArtifact => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact",
25            SamlBinding::Soap => "urn:oasis:names:tc:SAML:2.0:bindings:SOAP",
26        }
27    }
28
29    /// 从 URI 解析
30    pub fn from_uri(uri: &str) -> Option<Self> {
31        match uri {
32            "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" => Some(SamlBinding::HttpRedirect),
33            "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" => Some(SamlBinding::HttpPost),
34            "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" => Some(SamlBinding::HttpArtifact),
35            "urn:oasis:names:tc:SAML:2.0:bindings:SOAP" => Some(SamlBinding::Soap),
36            _ => None,
37        }
38    }
39}
40
41/// 名称 ID 格式
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub enum NameIdFormat {
44    /// 未指定
45    Unspecified,
46    /// 电子邮箱
47    EmailAddress,
48    /// X.509 主题名称
49    X509SubjectName,
50    /// Windows 域限定名
51    WindowsDomainQualifiedName,
52    /// Kerberos 主体名称
53    KerberosPrincipalName,
54    /// 实体标识符
55    EntityIdentifier,
56    /// 持久化标识符
57    Persistent,
58    /// 临时标识符
59    Transient,
60    /// 自定义格式
61    Custom(String),
62}
63
64impl NameIdFormat {
65    /// 获取格式 URI
66    pub fn uri(&self) -> &str {
67        match self {
68            NameIdFormat::Unspecified => "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
69            NameIdFormat::EmailAddress => "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
70            NameIdFormat::X509SubjectName => "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName",
71            NameIdFormat::WindowsDomainQualifiedName => "urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName",
72            NameIdFormat::KerberosPrincipalName => "urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos",
73            NameIdFormat::EntityIdentifier => "urn:oasis:names:tc:SAML:2.0:nameid-format:entity",
74            NameIdFormat::Persistent => "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
75            NameIdFormat::Transient => "urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
76            NameIdFormat::Custom(uri) => uri,
77        }
78    }
79
80    /// 从 URI 解析
81    pub fn from_uri(uri: &str) -> Self {
82        match uri {
83            "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" => NameIdFormat::Unspecified,
84            "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" => NameIdFormat::EmailAddress,
85            "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName" => NameIdFormat::X509SubjectName,
86            "urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName" => NameIdFormat::WindowsDomainQualifiedName,
87            "urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos" => NameIdFormat::KerberosPrincipalName,
88            "urn:oasis:names:tc:SAML:2.0:nameid-format:entity" => NameIdFormat::EntityIdentifier,
89            "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" => NameIdFormat::Persistent,
90            "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" => NameIdFormat::Transient,
91            _ => NameIdFormat::Custom(uri.to_string()),
92        }
93    }
94}
95
96/// SAML 服务提供者 (SP) 配置
97#[derive(Debug, Clone)]
98pub struct ServiceProviderConfig {
99    /// 实体 ID
100    pub entity_id: String,
101
102    /// ACS (Assertion Consumer Service) URL
103    pub acs_url: String,
104
105    /// SLO (Single Logout) URL
106    pub slo_url: Option<String>,
107
108    /// 名称 ID 格式
109    pub name_id_format: NameIdFormat,
110
111    /// 需要的属性
112    pub required_attributes: Vec<String>,
113
114    /// 可选属性
115    pub optional_attributes: Vec<String>,
116
117    /// 是否需要签名
118    pub want_assertions_signed: bool,
119
120    /// 是否需要响应签名
121    pub want_response_signed: bool,
122
123    /// 私钥 (用于签名请求)
124    pub private_key: Option<String>,
125
126    /// 证书
127    pub certificate: Option<String>,
128}
129
130impl ServiceProviderConfig {
131    /// 创建新的 SP 配置
132    pub fn new(entity_id: impl Into<String>, acs_url: impl Into<String>) -> Self {
133        Self {
134            entity_id: entity_id.into(),
135            acs_url: acs_url.into(),
136            slo_url: None,
137            name_id_format: NameIdFormat::EmailAddress,
138            required_attributes: Vec::new(),
139            optional_attributes: Vec::new(),
140            want_assertions_signed: true,
141            want_response_signed: true,
142            private_key: None,
143            certificate: None,
144        }
145    }
146
147    /// 设置 SLO URL
148    pub fn with_slo_url(mut self, url: impl Into<String>) -> Self {
149        self.slo_url = Some(url.into());
150        self
151    }
152
153    /// 设置名称 ID 格式
154    pub fn with_name_id_format(mut self, format: NameIdFormat) -> Self {
155        self.name_id_format = format;
156        self
157    }
158
159    /// 添加需要的属性
160    pub fn add_required_attribute(mut self, attr: impl Into<String>) -> Self {
161        self.required_attributes.push(attr.into());
162        self
163    }
164
165    /// 添加可选属性
166    pub fn add_optional_attribute(mut self, attr: impl Into<String>) -> Self {
167        self.optional_attributes.push(attr.into());
168        self
169    }
170
171    /// 设置签名密钥
172    pub fn with_signing_key(mut self, private_key: impl Into<String>, certificate: impl Into<String>) -> Self {
173        self.private_key = Some(private_key.into());
174        self.certificate = Some(certificate.into());
175        self
176    }
177}
178
179/// SAML 身份提供者 (IdP) 配置
180#[derive(Debug, Clone)]
181pub struct IdentityProviderConfig {
182    /// 实体 ID
183    pub entity_id: String,
184
185    /// SSO URL
186    pub sso_url: String,
187
188    /// SLO URL
189    pub slo_url: Option<String>,
190
191    /// SSO 绑定方式
192    pub sso_binding: SamlBinding,
193
194    /// SLO 绑定方式
195    pub slo_binding: SamlBinding,
196
197    /// 签名证书
198    pub signing_certificate: String,
199
200    /// 加密证书
201    pub encryption_certificate: Option<String>,
202
203    /// 额外配置
204    pub extra: HashMap<String, String>,
205}
206
207impl IdentityProviderConfig {
208    /// 创建新的 IdP 配置
209    pub fn new(entity_id: impl Into<String>, sso_url: impl Into<String>, certificate: impl Into<String>) -> Self {
210        Self {
211            entity_id: entity_id.into(),
212            sso_url: sso_url.into(),
213            slo_url: None,
214            sso_binding: SamlBinding::HttpRedirect,
215            slo_binding: SamlBinding::HttpRedirect,
216            signing_certificate: certificate.into(),
217            encryption_certificate: None,
218            extra: HashMap::new(),
219        }
220    }
221
222    /// 设置 SLO URL
223    pub fn with_slo_url(mut self, url: impl Into<String>) -> Self {
224        self.slo_url = Some(url.into());
225        self
226    }
227
228    /// 设置 SSO 绑定方式
229    pub fn with_sso_binding(mut self, binding: SamlBinding) -> Self {
230        self.sso_binding = binding;
231        self
232    }
233
234    /// 设置 SLO 绑定方式
235    pub fn with_slo_binding(mut self, binding: SamlBinding) -> Self {
236        self.slo_binding = binding;
237        self
238    }
239
240    /// 设置加密证书
241    pub fn with_encryption_certificate(mut self, cert: impl Into<String>) -> Self {
242        self.encryption_certificate = Some(cert.into());
243        self
244    }
245}
246
247/// SAML 配置
248#[derive(Debug, Clone)]
249pub struct SamlConfig {
250    /// 服务提供者配置
251    pub sp: ServiceProviderConfig,
252
253    /// 身份提供者配置
254    pub idp: IdentityProviderConfig,
255
256    /// 时钟偏移容忍度 (秒)
257    pub clock_skew_seconds: i64,
258
259    /// 断言有效期 (秒)
260    pub assertion_validity_seconds: i64,
261
262    /// 是否验证受众
263    pub validate_audience: bool,
264
265    /// 是否验证发行人
266    pub validate_issuer: bool,
267
268    /// 是否验证目标
269    pub validate_destination: bool,
270
271    /// 是否检测重放攻击
272    pub detect_replay: bool,
273}
274
275impl SamlConfig {
276    /// 创建新的 SAML 配置
277    pub fn new(sp: ServiceProviderConfig, idp: IdentityProviderConfig) -> Self {
278        Self {
279            sp,
280            idp,
281            clock_skew_seconds: 60,
282            assertion_validity_seconds: 300,
283            validate_audience: true,
284            validate_issuer: true,
285            validate_destination: true,
286            detect_replay: true,
287        }
288    }
289
290    /// 设置时钟偏移容忍度
291    pub fn with_clock_skew(mut self, seconds: i64) -> Self {
292        self.clock_skew_seconds = seconds;
293        self
294    }
295
296    /// 设置断言有效期
297    pub fn with_assertion_validity(mut self, seconds: i64) -> Self {
298        self.assertion_validity_seconds = seconds;
299        self
300    }
301}