aboutsummaryrefslogtreecommitdiff
path: root/src/security.rs
blob: cd9d7bdb6f53e2ec2efb3155eceb13d800fd9857 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//! Provides [`Key`] and functions to encode & decode expiring claims.

pub use signed::Key;
pub use std::time::{SystemTime, UNIX_EPOCH};

mod signed;

/// Join a string and an expiry date together into a string.
pub fn encode_expiring_claim(claim: &str, expiry_date: SystemTime) -> String {
    format!("{}:{}", claim, expiry_date.duration_since(UNIX_EPOCH).unwrap().as_secs())
}

/// Extract the string, failing if the expiry date is in the past.
pub fn decode_expiring_claim(value: &str) -> Result<&str, &'static str> {
    let mut parts = value.rsplitn(2, ':');
    let expiry_date = parts.next().expect("first .rsplitn().next() is expected to return Some");
    let claim = parts.next().ok_or("expected colon")?;
    let expiry_date: u64 = expiry_date.parse().map_err(|_| "failed to parse timestamp")?;

    if expiry_date > SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() {
        Ok(claim)
    } else {
        Err("token is expired")
    }
}

#[cfg(test)]
mod tests {
    use std::time::{SystemTime, Duration};

    #[test]
    fn test_expiring_claim() {
        for claim in vec!["test", "", "foo:bar"] {
            let encoded_claim = super::encode_expiring_claim(claim, SystemTime::now() + Duration::from_secs(60));
            assert_eq!(super::decode_expiring_claim(&encoded_claim).unwrap(), claim);

            let encoded_claim = super::encode_expiring_claim(claim, SystemTime::now() - Duration::from_secs(60));
            assert!(super::decode_expiring_claim(&encoded_claim).is_err());
        }
        assert!(super::decode_expiring_claim("test".into()).is_err());
    }
}