Skip to main content

wae_authentication/saml/
assertion.rs

1//! SAML 断言定义
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7use super::config::NameIdFormat;
8
9/// SAML 断言
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct SamlAssertion {
12    /// 断言 ID
13    #[serde(rename = "@ID")]
14    pub id: String,
15
16    /// 发行时间
17    #[serde(rename = "@IssueInstant")]
18    pub issue_instant: DateTime<Utc>,
19
20    /// 版本
21    #[serde(rename = "@Version")]
22    pub version: String,
23
24    /// 发行人
25    #[serde(rename = "saml:Issuer")]
26    pub issuer: String,
27
28    /// 主题
29    #[serde(rename = "saml:Subject")]
30    pub subject: Option<SamlSubject>,
31
32    /// 条件
33    #[serde(rename = "saml:Conditions")]
34    pub conditions: Option<SamlConditions>,
35
36    /// 认证声明
37    #[serde(rename = "saml:AuthnStatement")]
38    pub authn_statement: Option<SamlAuthnStatement>,
39
40    /// 属性声明
41    #[serde(rename = "saml:AttributeStatement")]
42    pub attribute_statement: Option<SamlAttributeStatement>,
43
44    /// 签名
45    #[serde(rename = "ds:Signature")]
46    pub signature: Option<String>,
47}
48
49/// SAML 主题
50#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct SamlSubject {
52    /// 名称 ID
53    #[serde(rename = "saml:NameID")]
54    pub name_id: SamlNameId,
55
56    /// 主题确认
57    #[serde(rename = "saml:SubjectConfirmation")]
58    pub subject_confirmation: Option<SamlSubjectConfirmation>,
59}
60
61/// SAML 名称 ID
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct SamlNameId {
64    /// 格式
65    #[serde(rename = "@Format")]
66    pub format: Option<String>,
67
68    /// 名称限定符
69    #[serde(rename = "@NameQualifier")]
70    pub name_qualifier: Option<String>,
71
72    /// 值
73    #[serde(rename = "$value")]
74    pub value: String,
75}
76
77impl SamlNameId {
78    /// 创建新的名称 ID
79    pub fn new(value: impl Into<String>) -> Self {
80        Self { format: None, name_qualifier: None, value: value.into() }
81    }
82
83    /// 设置格式
84    pub fn with_format(mut self, format: NameIdFormat) -> Self {
85        self.format = Some(format.uri().to_string());
86        self
87    }
88
89    /// 获取名称 ID 格式
90    pub fn name_id_format(&self) -> Option<NameIdFormat> {
91        self.format.as_ref().map(|f| NameIdFormat::from_uri(f))
92    }
93}
94
95/// SAML 主题确认
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct SamlSubjectConfirmation {
98    /// 方法
99    #[serde(rename = "@Method")]
100    pub method: String,
101
102    /// 主题确认数据
103    #[serde(rename = "saml:SubjectConfirmationData")]
104    pub data: Option<SamlSubjectConfirmationData>,
105}
106
107/// SAML 主题确认数据
108#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct SamlSubjectConfirmationData {
110    /// 接收者
111    #[serde(rename = "@Recipient")]
112    pub recipient: Option<String>,
113
114    /// 响应时间
115    #[serde(rename = "@NotOnOrAfter")]
116    pub not_on_or_after: Option<DateTime<Utc>>,
117
118    /// 生效时间
119    #[serde(rename = "@NotBefore")]
120    pub not_before: Option<DateTime<Utc>>,
121
122    /// 请求 ID
123    #[serde(rename = "@InResponseTo")]
124    pub in_response_to: Option<String>,
125}
126
127/// SAML 条件
128#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct SamlConditions {
130    /// 生效时间
131    #[serde(rename = "@NotBefore")]
132    pub not_before: Option<DateTime<Utc>>,
133
134    /// 过期时间
135    #[serde(rename = "@NotOnOrAfter")]
136    pub not_on_or_after: Option<DateTime<Utc>>,
137
138    /// 受众限制
139    #[serde(rename = "saml:AudienceRestriction")]
140    pub audience_restriction: Option<SamlAudienceRestriction>,
141}
142
143/// SAML 受众限制
144#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct SamlAudienceRestriction {
146    /// 受众列表
147    #[serde(rename = "saml:Audience")]
148    pub audience: Vec<String>,
149}
150
151/// SAML 认证声明
152#[derive(Debug, Clone, Serialize, Deserialize)]
153pub struct SamlAuthnStatement {
154    /// 认证时间
155    #[serde(rename = "@AuthnInstant")]
156    pub authn_instant: DateTime<Utc>,
157
158    /// 会话索引
159    #[serde(rename = "@SessionIndex")]
160    pub session_index: Option<String>,
161
162    /// 会话过期时间
163    #[serde(rename = "@SessionNotOnOrAfter")]
164    pub session_not_on_or_after: Option<DateTime<Utc>>,
165
166    /// 认证上下文
167    #[serde(rename = "saml:AuthnContext")]
168    pub authn_context: SamlAuthnContext,
169}
170
171/// SAML 认证上下文
172#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct SamlAuthnContext {
174    /// 认证上下文类引用
175    #[serde(rename = "saml:AuthnContextClassRef")]
176    pub class_ref: Option<String>,
177}
178
179/// SAML 属性声明
180#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct SamlAttributeStatement {
182    /// 属性列表
183    #[serde(rename = "saml:Attribute")]
184    pub attributes: Vec<SamlAttribute>,
185}
186
187/// SAML 属性
188#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct SamlAttribute {
190    /// 属性名
191    #[serde(rename = "@Name")]
192    pub name: String,
193
194    /// 属性名格式
195    #[serde(rename = "@NameFormat")]
196    pub name_format: Option<String>,
197
198    /// 友好名称
199    #[serde(rename = "@FriendlyName")]
200    pub friendly_name: Option<String>,
201
202    /// 属性值
203    #[serde(rename = "saml:AttributeValue")]
204    pub values: Vec<String>,
205}
206
207impl SamlAttributeStatement {
208    /// 获取属性值
209    pub fn get_attribute(&self, name: &str) -> Option<&SamlAttribute> {
210        self.attributes.iter().find(|a| a.name == name)
211    }
212
213    /// 获取第一个属性值
214    pub fn get_attribute_value(&self, name: &str) -> Option<&str> {
215        self.get_attribute(name).and_then(|a| a.values.first().map(|s| s.as_str()))
216    }
217
218    /// 获取所有属性值
219    pub fn get_attribute_values(&self, name: &str) -> Vec<&str> {
220        self.get_attribute(name).map(|a| a.values.iter().map(|s| s.as_str()).collect()).unwrap_or_default()
221    }
222
223    /// 转换为 HashMap
224    pub fn to_map(&self) -> HashMap<String, Vec<String>> {
225        self.attributes.iter().map(|a| (a.name.clone(), a.values.clone())).collect()
226    }
227}
228
229/// 认证上下文类引用
230pub mod authn_context_class {
231    /// 未指定
232    pub const UNSPECIFIED: &str = "urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified";
233
234    /// 密码
235    pub const PASSWORD: &str = "urn:oasis:names:tc:SAML:2.0:ac:classes:Password";
236
237    /// 密码保护传输
238    pub const PASSWORD_PROTECTED_TRANSPORT: &str = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport";
239
240    /// X.509
241    pub const X509: &str = "urn:oasis:names:tc:SAML:2.0:ac:classes:X509";
242
243    /// Kerberos
244    pub const KERBEROS: &str = "urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos";
245
246    /// TLS 客户端
247    pub const TLS_CLIENT: &str = "urn:oasis:names:tc:SAML:2.0:ac:classes:TLSClient";
248
249    /// 之前会话
250    pub const PREVIOUS_SESSION: &str = "urn:oasis:names:tc:SAML:2.0:ac:classes:PreviousSession";
251}