wae_authentication/saml/
assertion.rs1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7use super::config::NameIdFormat;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct SamlAssertion {
12 #[serde(rename = "@ID")]
14 pub id: String,
15
16 #[serde(rename = "@IssueInstant")]
18 pub issue_instant: DateTime<Utc>,
19
20 #[serde(rename = "@Version")]
22 pub version: String,
23
24 #[serde(rename = "saml:Issuer")]
26 pub issuer: String,
27
28 #[serde(rename = "saml:Subject")]
30 pub subject: Option<SamlSubject>,
31
32 #[serde(rename = "saml:Conditions")]
34 pub conditions: Option<SamlConditions>,
35
36 #[serde(rename = "saml:AuthnStatement")]
38 pub authn_statement: Option<SamlAuthnStatement>,
39
40 #[serde(rename = "saml:AttributeStatement")]
42 pub attribute_statement: Option<SamlAttributeStatement>,
43
44 #[serde(rename = "ds:Signature")]
46 pub signature: Option<String>,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct SamlSubject {
52 #[serde(rename = "saml:NameID")]
54 pub name_id: SamlNameId,
55
56 #[serde(rename = "saml:SubjectConfirmation")]
58 pub subject_confirmation: Option<SamlSubjectConfirmation>,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct SamlNameId {
64 #[serde(rename = "@Format")]
66 pub format: Option<String>,
67
68 #[serde(rename = "@NameQualifier")]
70 pub name_qualifier: Option<String>,
71
72 #[serde(rename = "$value")]
74 pub value: String,
75}
76
77impl SamlNameId {
78 pub fn new(value: impl Into<String>) -> Self {
80 Self { format: None, name_qualifier: None, value: value.into() }
81 }
82
83 pub fn with_format(mut self, format: NameIdFormat) -> Self {
85 self.format = Some(format.uri().to_string());
86 self
87 }
88
89 pub fn name_id_format(&self) -> Option<NameIdFormat> {
91 self.format.as_ref().map(|f| NameIdFormat::from_uri(f))
92 }
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct SamlSubjectConfirmation {
98 #[serde(rename = "@Method")]
100 pub method: String,
101
102 #[serde(rename = "saml:SubjectConfirmationData")]
104 pub data: Option<SamlSubjectConfirmationData>,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct SamlSubjectConfirmationData {
110 #[serde(rename = "@Recipient")]
112 pub recipient: Option<String>,
113
114 #[serde(rename = "@NotOnOrAfter")]
116 pub not_on_or_after: Option<DateTime<Utc>>,
117
118 #[serde(rename = "@NotBefore")]
120 pub not_before: Option<DateTime<Utc>>,
121
122 #[serde(rename = "@InResponseTo")]
124 pub in_response_to: Option<String>,
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct SamlConditions {
130 #[serde(rename = "@NotBefore")]
132 pub not_before: Option<DateTime<Utc>>,
133
134 #[serde(rename = "@NotOnOrAfter")]
136 pub not_on_or_after: Option<DateTime<Utc>>,
137
138 #[serde(rename = "saml:AudienceRestriction")]
140 pub audience_restriction: Option<SamlAudienceRestriction>,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct SamlAudienceRestriction {
146 #[serde(rename = "saml:Audience")]
148 pub audience: Vec<String>,
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize)]
153pub struct SamlAuthnStatement {
154 #[serde(rename = "@AuthnInstant")]
156 pub authn_instant: DateTime<Utc>,
157
158 #[serde(rename = "@SessionIndex")]
160 pub session_index: Option<String>,
161
162 #[serde(rename = "@SessionNotOnOrAfter")]
164 pub session_not_on_or_after: Option<DateTime<Utc>>,
165
166 #[serde(rename = "saml:AuthnContext")]
168 pub authn_context: SamlAuthnContext,
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct SamlAuthnContext {
174 #[serde(rename = "saml:AuthnContextClassRef")]
176 pub class_ref: Option<String>,
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct SamlAttributeStatement {
182 #[serde(rename = "saml:Attribute")]
184 pub attributes: Vec<SamlAttribute>,
185}
186
187#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct SamlAttribute {
190 #[serde(rename = "@Name")]
192 pub name: String,
193
194 #[serde(rename = "@NameFormat")]
196 pub name_format: Option<String>,
197
198 #[serde(rename = "@FriendlyName")]
200 pub friendly_name: Option<String>,
201
202 #[serde(rename = "saml:AttributeValue")]
204 pub values: Vec<String>,
205}
206
207impl SamlAttributeStatement {
208 pub fn get_attribute(&self, name: &str) -> Option<&SamlAttribute> {
210 self.attributes.iter().find(|a| a.name == name)
211 }
212
213 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 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 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
229pub mod authn_context_class {
231 pub const UNSPECIFIED: &str = "urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified";
233
234 pub const PASSWORD: &str = "urn:oasis:names:tc:SAML:2.0:ac:classes:Password";
236
237 pub const PASSWORD_PROTECTED_TRANSPORT: &str = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport";
239
240 pub const X509: &str = "urn:oasis:names:tc:SAML:2.0:ac:classes:X509";
242
243 pub const KERBEROS: &str = "urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos";
245
246 pub const TLS_CLIENT: &str = "urn:oasis:names:tc:SAML:2.0:ac:classes:TLSClient";
248
249 pub const PREVIOUS_SESSION: &str = "urn:oasis:names:tc:SAML:2.0:ac:classes:PreviousSession";
251}