diff --git a/CHANGELOG b/CHANGELOG index a25edfb..f761bc1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ # Changelog +### Unreleased +- Add ability to serialize and deserialize `Extensions` in an `AuthnRequest` + ### 0.0.15 - Updates dependencies diff --git a/src/schema/authn_request.rs b/src/schema/authn_request.rs index f7e670b..1e2c459 100644 --- a/src/schema/authn_request.rs +++ b/src/schema/authn_request.rs @@ -1,4 +1,4 @@ -use crate::schema::{Conditions, Issuer, NameIdPolicy, Subject}; +use crate::schema::{Conditions, Extensions, Issuer, NameIdPolicy, Subject}; use crate::signature::Signature; use chrono::prelude::*; use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, BytesText, Event}; @@ -30,6 +30,9 @@ pub struct AuthnRequest { pub issuer: Option, #[serde(rename = "Signature")] pub signature: Option, + #[serde(rename = "Extensions")] + #[serde(skip_deserializing)] + pub extensions: Option, #[serde(rename = "Subject")] pub subject: Option, #[serde(rename = "NameIDPolicy")] @@ -62,6 +65,7 @@ impl Default for AuthnRequest { consent: None, issuer: None, signature: None, + extensions: None, subject: None, name_id_policy: None, conditions: None, @@ -207,6 +211,10 @@ impl TryFrom<&AuthnRequest> for Event<'_> { let event: Event<'_> = signature.try_into()?; writer.write_event(event)?; } + if let Some(extensions) = &value.extensions { + let event: Event<'_> = extensions.try_into()?; + writer.write_event(event)?; + } if let Some(subject) = &value.subject { let event: Event<'_> = subject.try_into()?; writer.write_event(event)?; diff --git a/src/schema/extensions.rs b/src/schema/extensions.rs new file mode 100644 index 0000000..38fde96 --- /dev/null +++ b/src/schema/extensions.rs @@ -0,0 +1,56 @@ +use std::io::{Cursor, Write}; + +use quick_xml::{ + events::{BytesEnd, BytesStart, BytesText, Event}, + Writer, +}; +use serde::Deserialize; + +const NAME: &str = "saml2p:Extensions"; + +#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Ord, PartialOrd)] +pub struct Extensions(pub Vec); + +impl TryFrom for Event<'_> { + type Error = Box; + + fn try_from(value: Extensions) -> Result { + (&value).try_into() + } +} + +impl TryFrom<&Extensions> for Event<'_> { + type Error = Box; + + fn try_from(value: &Extensions) -> Result { + let mut write_buf = Vec::new(); + let mut writer = Writer::new(Cursor::new(&mut write_buf)); + let root = BytesStart::from_content(NAME, NAME.len()); + writer.write_event(Event::Start(root))?; + + for extension in &value.0 { + writer.get_mut().write_all(extension.as_bytes())?; + } + + writer.write_event(Event::End(BytesEnd::new(NAME)))?; + Ok(Event::Text(BytesText::from_escaped(String::from_utf8( + write_buf, + )?))) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::traits::ToXml; + + #[test] + fn extensions_xml_serialization() { + assert_eq!( + r#""#, + Extensions(vec![r#""#.to_string()]) + .to_xml() + .unwrap(), + ) + } +} diff --git a/src/schema/mod.rs b/src/schema/mod.rs index 5a30181..5d18cc8 100644 --- a/src/schema/mod.rs +++ b/src/schema/mod.rs @@ -1,5 +1,6 @@ pub mod authn_request; mod conditions; +mod extensions; mod issuer; mod name_id_policy; mod response; @@ -7,6 +8,7 @@ mod subject; pub use authn_request::AuthnRequest; pub use conditions::*; +pub use extensions::Extensions; pub use issuer::Issuer; pub use name_id_policy::NameIdPolicy; pub use response::Response;