1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use super::config::{NameIdFormat, SamlBinding};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct EntityDescriptor {
11 #[serde(rename = "@entityID")]
13 pub entity_id: String,
14
15 #[serde(rename = "@validUntil")]
17 pub valid_until: Option<DateTime<Utc>>,
18
19 #[serde(rename = "@cacheDuration")]
21 pub cache_duration: Option<String>,
22
23 #[serde(rename = "md:SPSSODescriptor")]
25 pub sp_sso_descriptor: Option<SPSSODescriptor>,
26
27 #[serde(rename = "md:IDPSSODescriptor")]
29 pub idp_sso_descriptor: Option<IDPSSODescriptor>,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct SPSSODescriptor {
35 #[serde(rename = "@WantAssertionsSigned")]
37 pub want_assertions_signed: Option<bool>,
38
39 #[serde(rename = "@AuthnRequestsSigned")]
41 pub authn_requests_signed: Option<bool>,
42
43 #[serde(rename = "@protocolSupportEnumeration")]
45 pub protocol_support_enumeration: String,
46
47 #[serde(rename = "md:NameIDFormat")]
49 pub name_id_format: Vec<String>,
50
51 #[serde(rename = "md:AssertionConsumerService")]
53 pub assertion_consumer_service: Vec<AssertionConsumerService>,
54
55 #[serde(rename = "md:SingleLogoutService")]
57 pub single_logout_service: Option<Vec<SingleLogoutService>>,
58
59 #[serde(rename = "md:KeyDescriptor")]
61 pub key_descriptor: Option<Vec<KeyDescriptor>>,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct IDPSSODescriptor {
67 #[serde(rename = "@WantAuthnRequestsSigned")]
69 pub want_authn_requests_signed: Option<bool>,
70
71 #[serde(rename = "@protocolSupportEnumeration")]
73 pub protocol_support_enumeration: String,
74
75 #[serde(rename = "md:NameIDFormat")]
77 pub name_id_format: Vec<String>,
78
79 #[serde(rename = "md:SingleSignOnService")]
81 pub single_sign_on_service: Vec<SingleSignOnService>,
82
83 #[serde(rename = "md:SingleLogoutService")]
85 pub single_logout_service: Option<Vec<SingleLogoutService>>,
86
87 #[serde(rename = "md:KeyDescriptor")]
89 pub key_descriptor: Option<Vec<KeyDescriptor>>,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct AssertionConsumerService {
95 #[serde(rename = "@index")]
97 pub index: u32,
98
99 #[serde(rename = "@isDefault")]
101 pub is_default: Option<bool>,
102
103 #[serde(rename = "@Binding")]
105 pub binding: String,
106
107 #[serde(rename = "@Location")]
109 pub location: String,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct SingleSignOnService {
115 #[serde(rename = "@Binding")]
117 pub binding: String,
118
119 #[serde(rename = "@Location")]
121 pub location: String,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct SingleLogoutService {
127 #[serde(rename = "@Binding")]
129 pub binding: String,
130
131 #[serde(rename = "@Location")]
133 pub location: String,
134
135 #[serde(rename = "@ResponseLocation")]
137 pub response_location: Option<String>,
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct KeyDescriptor {
143 #[serde(rename = "@use")]
145 pub use_: Option<String>,
146
147 #[serde(rename = "ds:KeyInfo")]
149 pub key_info: KeyInfo,
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct KeyInfo {
155 #[serde(rename = "ds:X509Data")]
157 pub x509_data: X509Data,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct X509Data {
163 #[serde(rename = "ds:X509Certificate")]
165 pub certificate: String,
166}
167
168#[derive(Debug, Clone)]
170pub struct SpMetadataBuilder {
171 entity_id: String,
172 acs_url: String,
173 slo_url: Option<String>,
174 name_id_formats: Vec<NameIdFormat>,
175 want_assertions_signed: bool,
176 authn_requests_signed: bool,
177 certificate: Option<String>,
178}
179
180impl SpMetadataBuilder {
181 pub fn new(entity_id: impl Into<String>, acs_url: impl Into<String>) -> Self {
183 Self {
184 entity_id: entity_id.into(),
185 acs_url: acs_url.into(),
186 slo_url: None,
187 name_id_formats: vec![NameIdFormat::EmailAddress],
188 want_assertions_signed: true,
189 authn_requests_signed: false,
190 certificate: None,
191 }
192 }
193
194 pub fn with_slo_url(mut self, url: impl Into<String>) -> Self {
196 self.slo_url = Some(url.into());
197 self
198 }
199
200 pub fn with_name_id_formats(mut self, formats: Vec<NameIdFormat>) -> Self {
202 self.name_id_formats = formats;
203 self
204 }
205
206 pub fn with_want_assertions_signed(mut self, want: bool) -> Self {
208 self.want_assertions_signed = want;
209 self
210 }
211
212 pub fn with_authn_requests_signed(mut self, signed: bool) -> Self {
214 self.authn_requests_signed = signed;
215 self
216 }
217
218 pub fn with_certificate(mut self, cert: impl Into<String>) -> Self {
220 self.certificate = Some(cert.into());
221 self
222 }
223
224 pub fn build(self) -> EntityDescriptor {
226 let acs = AssertionConsumerService {
227 index: 0,
228 is_default: Some(true),
229 binding: SamlBinding::HttpPost.uri().to_string(),
230 location: self.acs_url,
231 };
232
233 let slo_services = self.slo_url.map(|url| {
234 vec![SingleLogoutService {
235 binding: SamlBinding::HttpRedirect.uri().to_string(),
236 location: url,
237 response_location: None,
238 }]
239 });
240
241 let key_descriptors = self.certificate.map(|cert| {
242 vec![KeyDescriptor {
243 use_: Some("signing".to_string()),
244 key_info: KeyInfo { x509_data: X509Data { certificate: cert } },
245 }]
246 });
247
248 EntityDescriptor {
249 entity_id: self.entity_id,
250 valid_until: None,
251 cache_duration: None,
252 sp_sso_descriptor: Some(SPSSODescriptor {
253 want_assertions_signed: Some(self.want_assertions_signed),
254 authn_requests_signed: Some(self.authn_requests_signed),
255 protocol_support_enumeration: "urn:oasis:names:tc:SAML:2.0:protocol".to_string(),
256 name_id_format: self.name_id_formats.iter().map(|f| f.uri().to_string()).collect(),
257 assertion_consumer_service: vec![acs],
258 single_logout_service: slo_services,
259 key_descriptor: key_descriptors,
260 }),
261 idp_sso_descriptor: None,
262 }
263 }
264}