Skip to main content

wae_authentication/saml/
request.rs

1//! SAML 请求定义
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use super::config::{NameIdFormat, SamlBinding};
7
8/// SAML 认证请求
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct SamlAuthnRequest {
11    /// 请求 ID
12    #[serde(rename = "@ID")]
13    pub id: String,
14
15    /// 版本
16    #[serde(rename = "@Version")]
17    pub version: String,
18
19    /// 发行时间
20    #[serde(rename = "@IssueInstant")]
21    pub issue_instant: DateTime<Utc>,
22
23    /// 目标
24    #[serde(rename = "@Destination")]
25    pub destination: Option<String>,
26
27    /// 协议绑定
28    #[serde(rename = "@ProtocolBinding")]
29    pub protocol_binding: Option<String>,
30
31    /// 断言消费者服务 URL
32    #[serde(rename = "@AssertionConsumerServiceURL")]
33    pub assertion_consumer_service_url: Option<String>,
34
35    /// 发行人
36    #[serde(rename = "saml:Issuer")]
37    pub issuer: String,
38
39    /// 名称 ID 策略
40    #[serde(rename = "samlp:NameIDPolicy")]
41    pub name_id_policy: Option<SamlNameIdPolicy>,
42
43    /// 请求认证上下文
44    #[serde(rename = "samlp:RequestedAuthnContext")]
45    pub requested_authn_context: Option<SamlRequestedAuthnContext>,
46
47    /// 签名
48    #[serde(rename = "ds:Signature")]
49    pub signature: Option<String>,
50}
51
52impl SamlAuthnRequest {
53    /// 创建新的认证请求
54    pub fn new(id: impl Into<String>, issuer: impl Into<String>) -> Self {
55        Self {
56            id: id.into(),
57            version: "2.0".to_string(),
58            issue_instant: Utc::now(),
59            destination: None,
60            protocol_binding: None,
61            assertion_consumer_service_url: None,
62            issuer: issuer.into(),
63            name_id_policy: None,
64            requested_authn_context: None,
65            signature: None,
66        }
67    }
68
69    /// 设置目标
70    pub fn with_destination(mut self, destination: impl Into<String>) -> Self {
71        self.destination = Some(destination.into());
72        self
73    }
74
75    /// 设置协议绑定
76    pub fn with_protocol_binding(mut self, binding: SamlBinding) -> Self {
77        self.protocol_binding = Some(binding.uri().to_string());
78        self
79    }
80
81    /// 设置 ACS URL
82    pub fn with_acs_url(mut self, url: impl Into<String>) -> Self {
83        self.assertion_consumer_service_url = Some(url.into());
84        self
85    }
86
87    /// 设置名称 ID 策略
88    pub fn with_name_id_policy(mut self, policy: SamlNameIdPolicy) -> Self {
89        self.name_id_policy = Some(policy);
90        self
91    }
92
93    /// 设置请求认证上下文
94    pub fn with_authn_context(mut self, context: SamlRequestedAuthnContext) -> Self {
95        self.requested_authn_context = Some(context);
96        self
97    }
98}
99
100/// SAML 名称 ID 策略
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct SamlNameIdPolicy {
103    /// 格式
104    #[serde(rename = "@Format")]
105    pub format: Option<String>,
106
107    /// 允许创建
108    #[serde(rename = "@AllowCreate")]
109    pub allow_create: Option<bool>,
110
111    /// SP 名称限定符
112    #[serde(rename = "@SPNameQualifier")]
113    pub sp_name_qualifier: Option<String>,
114}
115
116impl SamlNameIdPolicy {
117    /// 创建新的名称 ID 策略
118    pub fn new() -> Self {
119        Self { format: None, allow_create: None, sp_name_qualifier: None }
120    }
121
122    /// 设置格式
123    pub fn with_format(mut self, format: NameIdFormat) -> Self {
124        self.format = Some(format.uri().to_string());
125        self
126    }
127
128    /// 设置允许创建
129    pub fn with_allow_create(mut self, allow: bool) -> Self {
130        self.allow_create = Some(allow);
131        self
132    }
133}
134
135impl Default for SamlNameIdPolicy {
136    fn default() -> Self {
137        Self::new()
138    }
139}
140
141/// SAML 请求认证上下文
142#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct SamlRequestedAuthnContext {
144    /// 比较方式
145    #[serde(rename = "@Comparison")]
146    pub comparison: Option<String>,
147
148    /// 认证上下文类引用
149    #[serde(rename = "saml:AuthnContextClassRef")]
150    pub class_ref: Vec<String>,
151}
152
153impl SamlRequestedAuthnContext {
154    /// 创建新的请求认证上下文
155    pub fn new(class_refs: Vec<String>) -> Self {
156        Self { comparison: None, class_ref: class_refs }
157    }
158
159    /// 设置比较方式
160    pub fn with_comparison(mut self, comparison: AuthnContextComparison) -> Self {
161        self.comparison = Some(comparison.as_str().to_string());
162        self
163    }
164}
165
166/// 认证上下文比较方式
167#[derive(Debug, Clone, Copy, PartialEq, Eq)]
168pub enum AuthnContextComparison {
169    /// 精确匹配
170    Exact,
171    /// 更强
172    Better,
173    /// 更强或相等
174    Maximum,
175    /// 更弱或相等
176    Minimum,
177}
178
179impl AuthnContextComparison {
180    /// 获取比较方式字符串
181    pub fn as_str(&self) -> &'static str {
182        match self {
183            AuthnContextComparison::Exact => "exact",
184            AuthnContextComparison::Better => "better",
185            AuthnContextComparison::Maximum => "maximum",
186            AuthnContextComparison::Minimum => "minimum",
187        }
188    }
189}
190
191/// SAML 登出请求
192#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct SamlLogoutRequest {
194    /// 请求 ID
195    #[serde(rename = "@ID")]
196    pub id: String,
197
198    /// 版本
199    #[serde(rename = "@Version")]
200    pub version: String,
201
202    /// 发行时间
203    #[serde(rename = "@IssueInstant")]
204    pub issue_instant: DateTime<Utc>,
205
206    /// 目标
207    #[serde(rename = "@Destination")]
208    pub destination: Option<String>,
209
210    /// 发行人
211    #[serde(rename = "saml:Issuer")]
212    pub issuer: String,
213
214    /// 名称 ID
215    #[serde(rename = "saml:NameID")]
216    pub name_id: Option<super::assertion::SamlNameId>,
217
218    /// 会话索引
219    #[serde(rename = "samlp:SessionIndex")]
220    pub session_index: Option<String>,
221
222    /// 签名
223    #[serde(rename = "ds:Signature")]
224    pub signature: Option<String>,
225}
226
227impl SamlLogoutRequest {
228    /// 创建新的登出请求
229    pub fn new(id: impl Into<String>, issuer: impl Into<String>) -> Self {
230        Self {
231            id: id.into(),
232            version: "2.0".to_string(),
233            issue_instant: Utc::now(),
234            destination: None,
235            issuer: issuer.into(),
236            name_id: None,
237            session_index: None,
238            signature: None,
239        }
240    }
241
242    /// 设置目标
243    pub fn with_destination(mut self, destination: impl Into<String>) -> Self {
244        self.destination = Some(destination.into());
245        self
246    }
247
248    /// 设置名称 ID
249    pub fn with_name_id(mut self, name_id: super::assertion::SamlNameId) -> Self {
250        self.name_id = Some(name_id);
251        self
252    }
253
254    /// 设置会话索引
255    pub fn with_session_index(mut self, index: impl Into<String>) -> Self {
256        self.session_index = Some(index.into());
257        self
258    }
259}