wae_authentication/saml/
response.rs1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use super::assertion::SamlAssertion;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum SamlStatusCode {
11 Success,
13 Requester,
15 Responder,
17 VersionMismatch,
19 Custom(String),
21}
22
23impl SamlStatusCode {
24 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 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 pub fn is_success(&self) -> bool {
48 matches!(self, SamlStatusCode::Success)
49 }
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct SamlResponse {
55 #[serde(rename = "@ID")]
57 pub id: String,
58
59 #[serde(rename = "@Version")]
61 pub version: String,
62
63 #[serde(rename = "@IssueInstant")]
65 pub issue_instant: DateTime<Utc>,
66
67 #[serde(rename = "@Destination")]
69 pub destination: Option<String>,
70
71 #[serde(rename = "@InResponseTo")]
73 pub in_response_to: Option<String>,
74
75 #[serde(rename = "saml:Issuer")]
77 pub issuer: String,
78
79 #[serde(rename = "samlp:Status")]
81 pub status: SamlStatus,
82
83 #[serde(rename = "saml:Assertion")]
85 pub assertion: Option<SamlAssertion>,
86
87 #[serde(rename = "saml:EncryptedAssertion")]
89 pub encrypted_assertion: Option<String>,
90
91 #[serde(rename = "ds:Signature")]
93 pub signature: Option<String>,
94}
95
96impl SamlResponse {
97 pub fn is_success(&self) -> bool {
99 self.status.is_success()
100 }
101
102 pub fn assertion(&self) -> Option<&SamlAssertion> {
104 self.assertion.as_ref()
105 }
106
107 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 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#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct SamlStatus {
121 #[serde(rename = "samlp:StatusCode")]
123 pub status_code: SamlStatusCodeElement,
124
125 #[serde(rename = "samlp:StatusMessage")]
127 pub status_message: Option<String>,
128
129 #[serde(rename = "samlp:StatusDetail")]
131 pub status_detail: Option<String>,
132}
133
134impl SamlStatus {
135 pub fn is_success(&self) -> bool {
137 self.status_code.is_success()
138 }
139
140 pub fn code(&self) -> SamlStatusCode {
142 SamlStatusCode::from_uri(&self.status_code.value)
143 }
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct SamlStatusCodeElement {
149 #[serde(rename = "@Value")]
151 pub value: String,
152
153 #[serde(rename = "samlp:StatusCode")]
155 pub sub_code: Option<Box<SamlStatusCodeElement>>,
156}
157
158impl SamlStatusCodeElement {
159 pub fn success() -> Self {
161 Self { value: SamlStatusCode::Success.uri().to_string(), sub_code: None }
162 }
163
164 pub fn requester() -> Self {
166 Self { value: SamlStatusCode::Requester.uri().to_string(), sub_code: None }
167 }
168
169 pub fn responder() -> Self {
171 Self { value: SamlStatusCode::Responder.uri().to_string(), sub_code: None }
172 }
173
174 pub fn is_success(&self) -> bool {
176 self.value == SamlStatusCode::Success.uri()
177 }
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
182pub struct SamlLogoutResponse {
183 #[serde(rename = "@ID")]
185 pub id: String,
186
187 #[serde(rename = "@Version")]
189 pub version: String,
190
191 #[serde(rename = "@IssueInstant")]
193 pub issue_instant: DateTime<Utc>,
194
195 #[serde(rename = "@Destination")]
197 pub destination: Option<String>,
198
199 #[serde(rename = "@InResponseTo")]
201 pub in_response_to: Option<String>,
202
203 #[serde(rename = "saml:Issuer")]
205 pub issuer: String,
206
207 #[serde(rename = "samlp:Status")]
209 pub status: SamlStatus,
210
211 #[serde(rename = "ds:Signature")]
213 pub signature: Option<String>,
214}
215
216impl SamlLogoutResponse {
217 pub fn is_success(&self) -> bool {
219 self.status.is_success()
220 }
221}