1use super::identifiers::{SamlInstant, SessionIndex};
2use crate::error::{SamlError, TimeWindowField};
3use time::{format_description::well_known::Rfc3339, OffsetDateTime};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct AuthnSession {
13 session_index: Option<SessionIndex>,
14 authn_instant: Option<SamlInstant>,
15 not_on_or_after: Option<SamlInstant>,
16}
17
18pub(super) static EMPTY_AUTHN_SESSION: AuthnSession = AuthnSession {
19 session_index: None,
20 authn_instant: None,
21 not_on_or_after: None,
22};
23
24pub(crate) fn earliest_authn_session_expiration<'a>(
25 values: impl IntoIterator<Item = &'a str>,
26 error_field: TimeWindowField,
27) -> Result<Option<OffsetDateTime>, SamlError> {
28 let mut earliest = None;
29 for value in values {
30 let candidate = OffsetDateTime::parse(value, &Rfc3339)
31 .map_err(|_| SamlError::TimeWindowInvalid { field: error_field })?;
32 if earliest.is_none_or(|current| candidate < current) {
33 earliest = Some(candidate);
34 }
35 }
36 Ok(earliest)
37}
38
39impl AuthnSession {
40 pub fn new(
42 session_index: Option<SessionIndex>,
43 authn_instant: Option<SamlInstant>,
44 not_on_or_after: Option<SamlInstant>,
45 ) -> Self {
46 Self {
47 session_index,
48 authn_instant,
49 not_on_or_after,
50 }
51 }
52
53 pub fn session_index(&self) -> Option<&SessionIndex> {
55 self.session_index.as_ref()
56 }
57
58 pub fn authn_instant(&self) -> Option<&SamlInstant> {
60 self.authn_instant.as_ref()
61 }
62
63 pub fn not_on_or_after(&self) -> Option<&SamlInstant> {
65 self.not_on_or_after.as_ref()
66 }
67}