1use core::fmt;
5
6use crate::binding::MAX_DEFLATE_RAW_DECODE_BYTES;
7use crate::constants::{
8 data_encryption_algorithm, key_encryption_algorithm, signature_algorithm, transform_algorithm,
9 MessageSignatureOrder,
10};
11use crate::xml::XmlLimits;
12
13#[non_exhaustive]
17#[derive(Clone)]
18pub struct EntitySetting {
19 pub entity_id: Option<String>,
21 pub request_signature_algorithm: String,
23 pub data_encryption_algorithm: String,
25 pub key_encryption_algorithm: String,
27 pub message_signing_order: MessageSignatureOrder,
29 pub allow_create: bool,
31 pub is_assertion_encrypted: bool,
33 pub allow_insecure_software_rsa_key_transport_decryption: bool,
41 pub relay_state: String,
43 pub authn_requests_signed: bool,
45 pub want_assertions_signed: bool,
47 pub validate_audience: bool,
49 pub want_message_signed: bool,
51 pub want_authn_requests_signed: bool,
53 pub want_logout_request_signed: bool,
55 pub want_logout_response_signed: bool,
57 pub name_id_format: Vec<String>,
59 pub private_key: Option<String>,
61 pub private_key_pass: Option<String>,
63 pub signing_cert: Option<String>,
65 pub encrypt_cert: Option<String>,
67 pub enc_private_key: Option<String>,
69 pub enc_private_key_pass: Option<String>,
71 pub clock_drifts: (i64, i64),
73 pub redirect_inflate_max_bytes: usize,
79 pub xml_limits: XmlLimits,
81 pub tag_prefix_protocol: String,
83 pub tag_prefix_assertion: String,
85 pub tag_prefix_encrypted_assertion: String,
87 pub login_response_template: Option<crate::template::LoginResponseTemplate>,
89 pub login_request_template: Option<String>,
91 pub logout_request_template: Option<String>,
93 pub logout_response_template: Option<String>,
95 pub signature_config: Option<SignatureConfig>,
97 pub transformation_algorithms: Vec<String>,
100}
101
102fn redacted_option(value: &Option<String>) -> Option<&'static str> {
103 value.as_ref().map(|_| "<redacted>")
104}
105
106impl fmt::Debug for EntitySetting {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 f.debug_struct("EntitySetting")
109 .field("entity_id", &self.entity_id)
110 .field(
111 "request_signature_algorithm",
112 &self.request_signature_algorithm,
113 )
114 .field("data_encryption_algorithm", &self.data_encryption_algorithm)
115 .field("key_encryption_algorithm", &self.key_encryption_algorithm)
116 .field("message_signing_order", &self.message_signing_order)
117 .field("allow_create", &self.allow_create)
118 .field("is_assertion_encrypted", &self.is_assertion_encrypted)
119 .field(
120 "allow_insecure_software_rsa_key_transport_decryption",
121 &self.allow_insecure_software_rsa_key_transport_decryption,
122 )
123 .field("relay_state", &self.relay_state)
124 .field("authn_requests_signed", &self.authn_requests_signed)
125 .field("want_assertions_signed", &self.want_assertions_signed)
126 .field("validate_audience", &self.validate_audience)
127 .field("want_message_signed", &self.want_message_signed)
128 .field(
129 "want_authn_requests_signed",
130 &self.want_authn_requests_signed,
131 )
132 .field(
133 "want_logout_request_signed",
134 &self.want_logout_request_signed,
135 )
136 .field(
137 "want_logout_response_signed",
138 &self.want_logout_response_signed,
139 )
140 .field("name_id_format", &self.name_id_format)
141 .field("private_key", &redacted_option(&self.private_key))
142 .field("private_key_pass", &redacted_option(&self.private_key_pass))
143 .field("signing_cert", &redacted_option(&self.signing_cert))
144 .field("encrypt_cert", &redacted_option(&self.encrypt_cert))
145 .field("enc_private_key", &redacted_option(&self.enc_private_key))
146 .field(
147 "enc_private_key_pass",
148 &redacted_option(&self.enc_private_key_pass),
149 )
150 .field("clock_drifts", &self.clock_drifts)
151 .field(
152 "redirect_inflate_max_bytes",
153 &self.redirect_inflate_max_bytes,
154 )
155 .field("xml_limits", &self.xml_limits)
156 .field("tag_prefix_protocol", &self.tag_prefix_protocol)
157 .field("tag_prefix_assertion", &self.tag_prefix_assertion)
158 .field(
159 "tag_prefix_encrypted_assertion",
160 &self.tag_prefix_encrypted_assertion,
161 )
162 .field("login_response_template", &self.login_response_template)
163 .field("login_request_template", &self.login_request_template)
164 .field("logout_request_template", &self.logout_request_template)
165 .field("logout_response_template", &self.logout_response_template)
166 .field("signature_config", &self.signature_config)
167 .field("transformation_algorithms", &self.transformation_algorithms)
168 .finish()
169 }
170}
171
172pub type CustomTagReplacement<'a> = &'a dyn Fn(&str) -> (String, String);
175
176#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
178pub enum SignatureAction {
179 #[default]
181 After,
182 Before,
184 Prepend,
186 Append,
188}
189
190#[derive(Debug, Clone)]
192pub struct SignatureConfig {
193 pub prefix: String,
195 pub reference: Option<String>,
198 pub action: SignatureAction,
200}
201
202impl Default for SignatureConfig {
203 fn default() -> Self {
204 Self {
205 prefix: "ds".to_string(),
206 reference: None,
207 action: SignatureAction::After,
208 }
209 }
210}
211
212impl Default for EntitySetting {
213 fn default() -> Self {
214 Self {
215 entity_id: None,
216 request_signature_algorithm: signature_algorithm::RSA_SHA256.to_string(),
217 data_encryption_algorithm: data_encryption_algorithm::AES_256.to_string(),
218 key_encryption_algorithm: key_encryption_algorithm::RSA_OAEP_MGF1P.to_string(),
219 message_signing_order: MessageSignatureOrder::SignThenEncrypt,
220 allow_create: false,
221 is_assertion_encrypted: false,
222 allow_insecure_software_rsa_key_transport_decryption: false,
223 relay_state: String::new(),
224 authn_requests_signed: false,
225 want_assertions_signed: false,
226 validate_audience: true,
227 want_message_signed: false,
228 want_authn_requests_signed: false,
229 want_logout_request_signed: true,
230 want_logout_response_signed: true,
231 name_id_format: Vec::new(),
232 private_key: None,
233 private_key_pass: None,
234 signing_cert: None,
235 encrypt_cert: None,
236 enc_private_key: None,
237 enc_private_key_pass: None,
238 clock_drifts: (0, 0),
239 redirect_inflate_max_bytes: MAX_DEFLATE_RAW_DECODE_BYTES,
240 xml_limits: XmlLimits::default(),
241 tag_prefix_protocol: "samlp".to_string(),
242 tag_prefix_assertion: "saml".to_string(),
243 tag_prefix_encrypted_assertion: "saml".to_string(),
244 login_response_template: None,
245 login_request_template: None,
246 logout_request_template: None,
247 logout_response_template: None,
248 signature_config: None,
249 transformation_algorithms: vec![
250 transform_algorithm::ENVELOPED_SIGNATURE.to_string(),
251 transform_algorithm::EXC_C14N.to_string(),
252 ],
253 }
254 }
255}
256
257pub fn generate_id() -> String {
259 format!("_{}", uuid::Uuid::new_v4())
260}
261
262#[derive(Debug, Clone, Default)]
264pub struct User {
265 pub name_id: String,
267 pub attributes: Vec<(String, String)>,
270 pub session_index: Option<String>,
272}
273
274impl User {
275 pub fn new(name_id: impl Into<String>) -> Self {
277 Self {
278 name_id: name_id.into(),
279 ..Default::default()
280 }
281 }
282}
283
284pub fn now_iso8601() -> String {
286 iso8601_offset(0)
287}
288
289pub fn iso8601_offset(seconds: i64) -> String {
291 let t = time::OffsetDateTime::now_utc() + time::Duration::seconds(seconds);
292 format!(
293 "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
294 t.year(),
295 u8::from(t.month()),
296 t.day(),
297 t.hour(),
298 t.minute(),
299 t.second(),
300 )
301}
302
303#[derive(Debug, Clone)]
305pub struct BindingContext {
306 pub id: String,
308 pub context: String,
310 pub relay_state: Option<String>,
312 pub entity_endpoint: String,
314 pub binding: crate::constants::Binding,
316 pub request_type: &'static str,
318 pub signature: Option<String>,
320 pub sig_alg: Option<String>,
322}
323
324impl BindingContext {
325 pub fn post_form(&self) -> String {
331 crate::binding::saml_post_binding_form_with_signature(
332 &self.entity_endpoint,
333 self.request_type,
334 &self.context,
335 self.relay_state.as_deref(),
336 self.sig_alg.as_deref(),
337 self.signature.as_deref(),
338 )
339 }
340
341 pub fn try_post_form(&self) -> Result<String, crate::error::SamlError> {
349 crate::binding::try_saml_post_binding_form_with_signature(
350 &self.entity_endpoint,
351 self.request_type,
352 &self.context,
353 self.relay_state.as_deref(),
354 self.sig_alg.as_deref(),
355 self.signature.as_deref(),
356 )
357 }
358}