1use chrono::{Duration, SecondsFormat, Utc};
18use quick_xml::se::to_string as to_xml_string;
19use serde::Serialize;
20
21use crate::{get_cert_data, sp::ServiceProvider};
22
23#[derive(Serialize)]
24pub struct AssertionConsumerService {
25 #[serde(rename = "Binding")]
26 binding: String,
27 #[serde(rename = "Location")]
28 location: String,
29 index: String,
30}
31
32#[derive(Serialize)]
33pub struct SingleLogoutService {
34 #[serde(rename = "Binding")]
35 binding: String,
36 #[serde(rename = "Location")]
37 location: String,
38}
39
40#[derive(Serialize)]
41pub struct X509Certificate {
42 #[serde(rename = "$value")]
43 value: String,
44}
45
46#[derive(Serialize)]
47pub struct X509Data {
48 #[serde(rename = "ds:X509Certificate")]
49 x509_certificate: X509Certificate,
50}
51
52#[derive(Serialize)]
53pub struct KeyInfo {
54 #[serde(rename = "xmlns:ds")]
55 ds: String,
56 #[serde(rename = "ds:X509Data")]
57 x509_data: X509Data,
58}
59
60#[derive(Serialize)]
61pub struct KeyDescriptor {
62 #[serde(rename = "use")]
63 us: String,
64 #[serde(rename = "ds:KeyInfo")]
65 key_info: KeyInfo,
66}
67
68#[derive(Serialize)]
69pub struct SPSSODescriptor {
70 #[serde(rename = "protocolSupportEnumeration")]
71 protocol_support_enumeration: String,
72 #[serde(rename = "md:KeyDescriptor")]
73 key_descriptor: Vec<KeyDescriptor>,
74 #[serde(rename = "md:SingleLogoutService")]
75 single_logout_service: SingleLogoutService,
76 #[serde(rename = "md:AssertionConsumerService")]
77 assertion_consumer_service: AssertionConsumerService,
78}
79
80#[derive(Serialize)]
81#[serde(rename = "md:EntityDescriptor")]
82pub struct EntityDescriptor {
83 #[serde(rename = "xmlns:md")]
84 md: String,
85 #[serde(rename = "xmlns:ds")]
86 ds: String,
87 #[serde(rename = "entityID")]
88 entity_id: String,
89 #[serde(rename = "validUntil")]
90 valid_util: String,
91 #[serde(rename = "md:SPSSODescriptor")]
92 sp_sso_descriptor: SPSSODescriptor,
93}
94
95impl ServiceProvider {
96 pub fn metadata(&self) -> String {
97 let now = Utc::now();
98 let tomorrow = now + Duration::days(1);
99
100 let cert_data = get_cert_data(&self.certificate);
101
102 to_xml_string(&EntityDescriptor {
103 md: "urn:oasis:names:tc:SAML:2.0:metadata".into(),
104 ds: "http://www.w3.org/2000/09/xmldsig#".into(),
105 entity_id: self.entity_id.to_string(),
106 valid_util: tomorrow.to_rfc3339_opts(SecondsFormat::Millis, true),
107 sp_sso_descriptor: SPSSODescriptor {
108 protocol_support_enumeration:
109 "urn:oasis:names:tc:SAML:1.1:protocol urn:oasis:names:tc:SAML:2.0:protocol"
110 .into(),
111 key_descriptor: vec![
112 KeyDescriptor {
113 us: "signing".into(),
114 key_info: KeyInfo {
115 ds: "http://www.w3.org/2000/09/xmldsig#".into(),
116 x509_data: X509Data {
117 x509_certificate: X509Certificate {
118 value: cert_data.clone(),
119 },
120 },
121 },
122 },
123 KeyDescriptor {
124 us: "encryption".into(),
125 key_info: KeyInfo {
126 ds: "http://www.w3.org/2000/09/xmldsig#".into(),
127 x509_data: X509Data {
128 x509_certificate: X509Certificate { value: cert_data },
129 },
130 },
131 },
132 ],
133 single_logout_service: SingleLogoutService {
134 binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect".into(),
135 location: self.assert_logout.to_string(),
136 },
137 assertion_consumer_service: AssertionConsumerService {
138 binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST".into(),
139 location: self.assert_login.to_string(),
140 index: "0".into(),
141 },
142 },
143 })
144 .unwrap()
145 }
146}