Skip to main content

wae_authentication/saml/
response.rs

1//! SAML 响应定义
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use super::assertion::SamlAssertion;
7
8/// SAML 响应状态码
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum SamlStatusCode {
11    /// 成功
12    Success,
13    /// 请求者错误
14    Requester,
15    /// 响应者错误
16    Responder,
17    /// 版本不匹配
18    VersionMismatch,
19    /// 自定义状态码
20    Custom(String),
21}
22
23impl SamlStatusCode {
24    /// 获取状态码 URI
25    pub fn uri(&self) -> &str {
26        match self {
27            SamlStatusCode::Success => "urn:oasis:names:tc:SAML:2.0:status:Success",
28            SamlStatusCode::Requester => "urn:oasis:names:tc:SAML:2.0:status:Requester",
29            SamlStatusCode::Responder => "urn:oasis:names:tc:SAML:2.0:status:Responder",
30            SamlStatusCode::VersionMismatch => "urn:oasis:names:tc:SAML:2.0:status:VersionMismatch",
31            SamlStatusCode::Custom(uri) => uri,
32        }
33    }
34
35    /// 从 URI 解析
36    pub fn from_uri(uri: &str) -> Self {
37        match uri {
38            "urn:oasis:names:tc:SAML:2.0:status:Success" => SamlStatusCode::Success,
39            "urn:oasis:names:tc:SAML:2.0:status:Requester" => SamlStatusCode::Requester,
40            "urn:oasis:names:tc:SAML:2.0:status:Responder" => SamlStatusCode::Responder,
41            "urn:oasis:names:tc:SAML:2.0:status:VersionMismatch" => SamlStatusCode::VersionMismatch,
42            _ => SamlStatusCode::Custom(uri.to_string()),
43        }
44    }
45
46    /// 检查是否成功
47    pub fn is_success(&self) -> bool {
48        matches!(self, SamlStatusCode::Success)
49    }
50}
51
52/// SAML 响应
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct SamlResponse {
55    /// 响应 ID
56    #[serde(rename = "@ID")]
57    pub id: String,
58
59    /// 版本
60    #[serde(rename = "@Version")]
61    pub version: String,
62
63    /// 发行时间
64    #[serde(rename = "@IssueInstant")]
65    pub issue_instant: DateTime<Utc>,
66
67    /// 目标
68    #[serde(rename = "@Destination")]
69    pub destination: Option<String>,
70
71    /// 响应请求 ID
72    #[serde(rename = "@InResponseTo")]
73    pub in_response_to: Option<String>,
74
75    /// 发行人
76    #[serde(rename = "saml:Issuer")]
77    pub issuer: String,
78
79    /// 状态
80    #[serde(rename = "samlp:Status")]
81    pub status: SamlStatus,
82
83    /// 断言
84    #[serde(rename = "saml:Assertion")]
85    pub assertion: Option<SamlAssertion>,
86
87    /// 加密断言
88    #[serde(rename = "saml:EncryptedAssertion")]
89    pub encrypted_assertion: Option<String>,
90
91    /// 签名
92    #[serde(rename = "ds:Signature")]
93    pub signature: Option<String>,
94}
95
96impl SamlResponse {
97    /// 检查响应是否成功
98    pub fn is_success(&self) -> bool {
99        self.status.is_success()
100    }
101
102    /// 获取断言
103    pub fn assertion(&self) -> Option<&SamlAssertion> {
104        self.assertion.as_ref()
105    }
106
107    /// 获取用户 ID (从断言中提取)
108    pub fn user_id(&self) -> Option<&str> {
109        self.assertion.as_ref().and_then(|a| a.subject.as_ref()).map(|s| s.name_id.value.as_str())
110    }
111
112    /// 获取属性
113    pub fn get_attribute(&self, name: &str) -> Option<&str> {
114        self.assertion.as_ref().and_then(|a| a.attribute_statement.as_ref()).and_then(|s| s.get_attribute_value(name))
115    }
116}
117
118/// SAML 状态
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct SamlStatus {
121    /// 状态码
122    #[serde(rename = "samlp:StatusCode")]
123    pub status_code: SamlStatusCodeElement,
124
125    /// 状态消息
126    #[serde(rename = "samlp:StatusMessage")]
127    pub status_message: Option<String>,
128
129    /// 状态详情
130    #[serde(rename = "samlp:StatusDetail")]
131    pub status_detail: Option<String>,
132}
133
134impl SamlStatus {
135    /// 检查是否成功
136    pub fn is_success(&self) -> bool {
137        self.status_code.is_success()
138    }
139
140    /// 获取状态码
141    pub fn code(&self) -> SamlStatusCode {
142        SamlStatusCode::from_uri(&self.status_code.value)
143    }
144}
145
146/// SAML 状态码元素
147#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct SamlStatusCodeElement {
149    /// 状态码值
150    #[serde(rename = "@Value")]
151    pub value: String,
152
153    /// 子状态码
154    #[serde(rename = "samlp:StatusCode")]
155    pub sub_code: Option<Box<SamlStatusCodeElement>>,
156}
157
158impl SamlStatusCodeElement {
159    /// 创建成功状态码
160    pub fn success() -> Self {
161        Self { value: SamlStatusCode::Success.uri().to_string(), sub_code: None }
162    }
163
164    /// 创建请求者错误状态码
165    pub fn requester() -> Self {
166        Self { value: SamlStatusCode::Requester.uri().to_string(), sub_code: None }
167    }
168
169    /// 创建响应者错误状态码
170    pub fn responder() -> Self {
171        Self { value: SamlStatusCode::Responder.uri().to_string(), sub_code: None }
172    }
173
174    /// 检查是否成功
175    pub fn is_success(&self) -> bool {
176        self.value == SamlStatusCode::Success.uri()
177    }
178}
179
180/// SAML 登出响应
181#[derive(Debug, Clone, Serialize, Deserialize)]
182pub struct SamlLogoutResponse {
183    /// 响应 ID
184    #[serde(rename = "@ID")]
185    pub id: String,
186
187    /// 版本
188    #[serde(rename = "@Version")]
189    pub version: String,
190
191    /// 发行时间
192    #[serde(rename = "@IssueInstant")]
193    pub issue_instant: DateTime<Utc>,
194
195    /// 目标
196    #[serde(rename = "@Destination")]
197    pub destination: Option<String>,
198
199    /// 响应请求 ID
200    #[serde(rename = "@InResponseTo")]
201    pub in_response_to: Option<String>,
202
203    /// 发行人
204    #[serde(rename = "saml:Issuer")]
205    pub issuer: String,
206
207    /// 状态
208    #[serde(rename = "samlp:Status")]
209    pub status: SamlStatus,
210
211    /// 签名
212    #[serde(rename = "ds:Signature")]
213    pub signature: Option<String>,
214}
215
216impl SamlLogoutResponse {
217    /// 检查是否成功
218    pub fn is_success(&self) -> bool {
219        self.status.is_success()
220    }
221}