flp_saml2/idp/
authn_response.rs1use base64::prelude::*;
18use flate2::read::DeflateDecoder;
19use openssl::x509::X509;
20use quick_xml::de::from_str as from_xml_str;
21use serde::Deserialize;
22use std::io::Read;
23
24use crate::{
25 error::{Error, Result},
26 idp::IdentityProvider,
27};
28
29#[derive(Deserialize)]
30pub struct AttributeValue {
31 #[serde(rename = "xsi:type")]
32 pub typ: String,
33 #[serde(rename = "$value")]
34 pub value: String,
35}
36
37#[derive(Deserialize)]
38pub struct Attribute {
39 #[serde(rename = "Name")]
40 pub name: String,
41 #[serde(rename = "NameFormat")]
42 pub name_format: String,
43 #[serde(rename = "AttributeValue")]
44 pub attribute_values: Vec<AttributeValue>,
45}
46
47#[derive(Deserialize)]
48pub struct AttributeStatement {
49 #[serde(rename = "Attribute")]
50 pub attributes: Vec<Attribute>,
51}
52
53#[derive(Deserialize)]
54pub struct AuthnContextClassRef {
55 #[serde(rename = "$value")]
56 pub value: String,
57}
58
59#[derive(Deserialize)]
60pub struct AuthnContext {
61 #[serde(rename = "AuthnContextClassRef")]
62 pub authn_context_class_ref: AuthnContextClassRef,
63}
64
65#[derive(Deserialize)]
66pub struct AuthnStatement {
67 #[serde(rename = "AuthnInstant")]
68 pub authn_instant: String,
69 #[serde(rename = "SessionNotOnOrAfter")]
70 pub session_not_on_or_after: String,
71 #[serde(rename = "SessionIndex")]
72 pub session_index: String,
73 #[serde(rename = "AuthnContext")]
74 pub authn_context: AuthnContext,
75}
76
77#[derive(Deserialize)]
78pub struct Audience {
79 #[serde(rename = "$value")]
80 pub value: String,
81}
82
83#[derive(Deserialize)]
84pub struct AudienceRestriction {
85 #[serde(rename = "Audience")]
86 pub audience: Audience,
87}
88
89#[derive(Deserialize)]
90pub struct Conditions {
91 #[serde(rename = "NotBefore")]
92 pub not_before: String,
93 #[serde(rename = "NotOnOrAfter")]
94 pub not_on_or_after: String,
95 #[serde(rename = "AudienceRestriction")]
96 pub audience_restriction: AudienceRestriction,
97}
98
99#[derive(Deserialize)]
100pub struct SubjectConfirmationData {
101 #[serde(rename = "NotOnOrAfter")]
102 pub not_on_or_after: String,
103 #[serde(rename = "Recipient")]
104 pub recipient: String,
105 #[serde(rename = "InResponseTo")]
106 pub in_response_to: String,
107}
108
109#[derive(Deserialize)]
110pub struct SubjectConfirmation {
111 #[serde(rename = "Method")]
112 pub method: String,
113 #[serde(rename = "SubjectConfirmationData")]
114 pub subject_confirmation_data: SubjectConfirmationData,
115}
116
117#[derive(Deserialize)]
118pub struct NameID {
119 #[serde(rename = "SPNameQualifier")]
120 pub sp_name_qualifier: Option<String>,
121 #[serde(rename = "Format")]
122 pub format: String,
123 #[serde(rename = "$value")]
124 pub value: String,
125}
126
127#[derive(Deserialize)]
128pub struct Subject {
129 #[serde(rename = "NameID")]
130 pub name_id: NameID,
131 #[serde(rename = "SubjectConfirmation")]
132 pub subject_confirmation: SubjectConfirmation,
133}
134
135#[derive(Deserialize)]
136pub struct StatusCode {
137 #[serde(rename = "Value")]
138 pub value: String,
139}
140
141#[derive(Deserialize)]
142pub struct Status {
143 #[serde(rename = "StatusCode")]
144 pub status_code: StatusCode,
145}
146
147#[derive(Deserialize)]
148pub struct X509Certificate {
149 #[serde(rename = "$value")]
150 pub value: String,
151}
152
153#[derive(Deserialize)]
154pub struct X509Data {
155 #[serde(rename = "X509Certificate")]
156 pub x509_certificate: X509Certificate,
157}
158
159#[derive(Deserialize)]
160pub struct KeyInfo {
161 #[serde(rename = "xmlns:ds")]
162 pub ds: Option<String>,
163 #[serde(rename = "X509Data")]
164 pub x509_data: X509Data,
165}
166
167#[derive(Deserialize)]
168pub struct SignatureValue {
169 #[serde(rename = "$value")]
170 pub value: String,
171}
172
173#[derive(Deserialize)]
174pub struct DigestValue {
175 #[serde(rename = "$value")]
176 pub value: String,
177}
178
179#[derive(Deserialize)]
180pub struct DigestMethod {
181 #[serde(rename = "Algorithm")]
182 pub algorithm: String,
183}
184
185#[derive(Deserialize)]
186pub struct Transform {
187 #[serde(rename = "Algorithm")]
188 pub algorithm: String,
189}
190
191#[derive(Deserialize)]
192pub struct Transforms {
193 #[serde(rename = "Transform")]
194 pub transforms: Vec<Transform>,
195}
196
197#[derive(Deserialize)]
198pub struct Reference {
199 #[serde(rename = "URI")]
200 pub uri: String,
201 #[serde(rename = "Transforms")]
202 pub transforms: Transforms,
203 #[serde(rename = "DigestMethod")]
204 pub digest_method: DigestMethod,
205 #[serde(rename = "DigestValue")]
206 pub digest_value: DigestValue,
207}
208
209#[derive(Deserialize)]
210pub struct SignatureMethod {
211 #[serde(rename = "Algorithm")]
212 pub algorithm: String,
213}
214
215#[derive(Deserialize)]
216pub struct CanonicalizationMethod {
217 #[serde(rename = "Algorithm")]
218 pub algorithm: String,
219}
220
221#[derive(Deserialize)]
222pub struct SignedInfo {
223 #[serde(rename = "CanonicalizationMethod")]
224 pub canonicalization_method: CanonicalizationMethod,
225 #[serde(rename = "SignatureMethod")]
226 pub signature_method: SignatureMethod,
227 #[serde(rename = "Reference")]
228 pub reference: Reference,
229}
230
231#[derive(Deserialize)]
232pub struct Signature {
233 #[serde(rename = "xmlns:ds")]
234 pub ds: Option<String>,
235 #[serde(rename = "SignedInfo")]
236 pub signed_info: SignedInfo,
237 #[serde(rename = "SignatureValue")]
238 pub signature_value: SignatureValue,
239 #[serde(rename = "KeyInfo")]
240 pub key_info: KeyInfo,
241}
242
243#[derive(Deserialize)]
244pub struct Issuer {
245 #[serde(rename = "$value")]
246 pub value: String,
247}
248
249#[derive(Deserialize)]
250pub struct Assertion {
251 #[serde(rename = "xmlns:xsi")]
252 pub xsi: Option<String>,
253 #[serde(rename = "xmlns:xs")]
254 pub xs: Option<String>,
255 #[serde(rename = "ID")]
256 pub id: String,
257 #[serde(rename = "Version")]
258 pub version: String,
259 #[serde(rename = "IssueInstant")]
260 pub issue_instant: String,
261 #[serde(rename = "Issuer")]
262 pub issuer: Issuer,
263 #[serde(rename = "Signature")]
264 pub signature: Option<Signature>,
265 #[serde(rename = "Subject")]
266 pub subject: Subject,
267 #[serde(rename = "Conditions")]
268 pub conditions: Conditions,
269 #[serde(rename = "AuthnStatement")]
270 pub authn_statement: AuthnStatement,
271 #[serde(rename = "AttributeStatement")]
272 pub attribute_statement: AttributeStatement,
273}
274
275#[derive(Deserialize)]
276#[serde(rename = "samlp:Response")]
277pub struct Response {
278 #[serde(rename = "xmlns:samlp")]
279 pub samlp: String,
280 #[serde(rename = "xmlns:saml")]
281 pub saml: String,
282 #[serde(rename = "ID")]
283 pub id: String,
284 #[serde(rename = "Version")]
285 pub version: String,
286 #[serde(rename = "IssueInstant")]
287 pub issue_instant: String,
288 #[serde(rename = "Destination")]
289 pub destination: String,
290 #[serde(rename = "InResponseTo")]
291 pub in_response_to: String,
292 #[serde(rename = "Issuer")]
293 pub issuer: Issuer,
294 #[serde(rename = "Signature")]
295 pub signature: Option<Signature>,
296 #[serde(rename = "Status")]
297 pub status: Status,
298 #[serde(rename = "Assertion")]
299 pub assertion: Assertion,
300}
301
302pub fn decode_authn_response(encoded: &str) -> Result<String> {
303 let deflated = BASE64_STANDARD.decode(encoded).map_err(|err| {
304 Error::InvalidResponse(format!("SAMLResponse is not encoded to base64: {}", err))
305 })?;
306 String::from_utf8(deflated).map_err(|err| {
307 Error::InvalidResponse(format!("SAMLResponse contains invalid chars: {}", err))
308 })
309}
310
311pub fn decode_inflate_authn_response(deflated_encoded: &str) -> Result<String> {
312 let deflated = BASE64_STANDARD.decode(deflated_encoded).map_err(|err| {
313 Error::InvalidResponse(format!("SAMLResponse is not encoded to base64: {}", err))
314 })?;
315 let mut inflater = DeflateDecoder::new(deflated.as_slice());
316 let mut inflated = String::new();
317 inflater.read_to_string(&mut inflated)?;
318 Ok(inflated)
319}
320
321impl IdentityProvider {
322 pub fn authn_response(&self, xml: &str) -> Result<Response> {
323 let response = from_xml_str::<Response>(xml)?;
324 if let Some(signature) = response.signature.as_ref() {
325 let cert = X509::from_pem(
326 format!(
327 "-----BEGIN CERTIFICATE-----\n{}\n-----END CERTIFICATE-----",
328 signature.key_info.x509_data.x509_certificate.value
329 )
330 .as_bytes(),
331 )?;
332 let mut is_valid = false;
333 for public_key in self.certificates.iter() {
334 if cert.verify(public_key.public_key()?.as_ref())? {
335 is_valid = true;
336 break;
337 }
338 }
339 if !is_valid {
340 return Err(Error::InvalidCert(
341 "SAML response contains invalid cert".into(),
342 ));
343 }
344 }
345 Ok(response)
346 }
347}