use chrono::{DateTime, Utc};
use crate::core::assertion::conditions::{Conditions, ConditionsRef};
use crate::security::clock::{is_not_before_valid, is_not_on_or_after_valid};
use crate::security::error::ValidationCheck;
pub fn validate_time_conditions(
conditions: &Conditions,
now: DateTime<Utc>,
clock_skew_seconds: u64,
) -> Vec<ValidationCheck> {
let mut checks = Vec::new();
if let Some(not_before) = conditions.not_before {
if is_not_before_valid(now, not_before, clock_skew_seconds) {
checks.push(ValidationCheck::pass(9, "NotBefore valid"));
} else {
checks.push(ValidationCheck::fail(
9,
"NotBefore valid",
format!(
"Current time {} is before NotBefore {} (skew: {}s)",
now, not_before, clock_skew_seconds
),
));
}
} else {
checks.push(ValidationCheck::pass(9, "NotBefore valid"));
}
if let Some(not_on_or_after) = conditions.not_on_or_after {
if is_not_on_or_after_valid(now, not_on_or_after, clock_skew_seconds) {
checks.push(ValidationCheck::pass(10, "NotOnOrAfter valid"));
} else {
checks.push(ValidationCheck::fail(
10,
"NotOnOrAfter valid",
format!(
"Current time {} is at or after NotOnOrAfter {} (skew: {}s)",
now, not_on_or_after, clock_skew_seconds
),
));
}
} else {
checks.push(ValidationCheck::pass(10, "NotOnOrAfter valid"));
}
checks
}
pub fn validate_time_conditions_ref(
conditions: &ConditionsRef<'_>,
now: DateTime<Utc>,
clock_skew_seconds: u64,
) -> Vec<ValidationCheck> {
let mut checks = Vec::new();
if let Some(not_before) = conditions.not_before {
if is_not_before_valid(now, not_before, clock_skew_seconds) {
checks.push(ValidationCheck::pass(9, "NotBefore valid"));
} else {
checks.push(ValidationCheck::fail(
9,
"NotBefore valid",
format!(
"Current time {} is before NotBefore {} (skew: {}s)",
now, not_before, clock_skew_seconds
),
));
}
} else {
checks.push(ValidationCheck::pass(9, "NotBefore valid"));
}
if let Some(not_on_or_after) = conditions.not_on_or_after {
if is_not_on_or_after_valid(now, not_on_or_after, clock_skew_seconds) {
checks.push(ValidationCheck::pass(10, "NotOnOrAfter valid"));
} else {
checks.push(ValidationCheck::fail(
10,
"NotOnOrAfter valid",
format!(
"Current time {} is at or after NotOnOrAfter {} (skew: {}s)",
now, not_on_or_after, clock_skew_seconds
),
));
}
} else {
checks.push(ValidationCheck::pass(10, "NotOnOrAfter valid"));
}
checks
}
pub fn check_one_time_use(one_time_use: bool) -> ValidationCheck {
if one_time_use {
ValidationCheck::pass(12, "OneTimeUse condition noted")
} else {
ValidationCheck::pass(12, "OneTimeUse condition")
}
}
pub fn check_proxy_restriction(
proxy_restriction_count: Option<u32>,
current_proxy_depth: u32,
) -> ValidationCheck {
match proxy_restriction_count {
Some(limit) => {
if current_proxy_depth <= limit {
ValidationCheck::pass(13, "ProxyRestriction count")
} else {
ValidationCheck::fail(
13,
"ProxyRestriction count",
format!(
"Proxy depth {} exceeds restriction limit {}",
current_proxy_depth, limit
),
)
}
}
None => ValidationCheck::pass(13, "ProxyRestriction count"),
}
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::TimeDelta;
#[test]
fn test_valid_time_conditions() {
let now = Utc::now();
let conditions = Conditions {
not_before: Some(now - TimeDelta::seconds(60)),
not_on_or_after: Some(now + TimeDelta::seconds(300)),
audience_restrictions: vec![],
one_time_use: false,
proxy_restriction: None,
};
let checks = validate_time_conditions(&conditions, now, 180);
assert!(checks.iter().all(|c| c.passed));
}
#[test]
fn test_not_before_failed() {
let now = Utc::now();
let conditions = Conditions {
not_before: Some(now + TimeDelta::seconds(300)), not_on_or_after: None,
audience_restrictions: vec![],
one_time_use: false,
proxy_restriction: None,
};
let checks = validate_time_conditions(&conditions, now, 180); let not_before_check = checks.iter().find(|c| c.check_number == 9).unwrap();
assert!(!not_before_check.passed);
}
#[test]
fn test_not_on_or_after_expired() {
let now = Utc::now();
let conditions = Conditions {
not_before: None,
not_on_or_after: Some(now - TimeDelta::seconds(300)), audience_restrictions: vec![],
one_time_use: false,
proxy_restriction: None,
};
let checks = validate_time_conditions(&conditions, now, 180); let noa_check = checks.iter().find(|c| c.check_number == 10).unwrap();
assert!(!noa_check.passed);
}
#[test]
fn test_no_time_conditions() {
let now = Utc::now();
let conditions = Conditions {
not_before: None,
not_on_or_after: None,
audience_restrictions: vec![],
one_time_use: false,
proxy_restriction: None,
};
let checks = validate_time_conditions(&conditions, now, 180);
assert!(checks.iter().all(|c| c.passed));
}
#[test]
fn test_proxy_restriction_within_limit() {
let check = check_proxy_restriction(Some(5), 3);
assert!(check.passed);
}
#[test]
fn test_proxy_restriction_exceeded() {
let check = check_proxy_restriction(Some(2), 5);
assert!(!check.passed);
}
#[test]
fn test_proxy_restriction_no_limit() {
let check = check_proxy_restriction(None, 100);
assert!(check.passed);
}
#[test]
fn test_one_time_use_present() {
let check = check_one_time_use(true);
assert!(check.passed);
}
#[test]
fn test_ref_time_conditions() {
let now = Utc::now();
let conditions = ConditionsRef {
not_before: Some(now - TimeDelta::seconds(60)),
not_on_or_after: Some(now + TimeDelta::seconds(300)),
audience_restrictions: vec![],
one_time_use: false,
proxy_restriction: None,
};
let checks = validate_time_conditions_ref(&conditions, now, 180);
assert!(checks.iter().all(|c| c.passed));
}
}