2019-09-02 16:49:02 -04:00
|
|
|
use crate::sealed_state::SealedState;
|
2019-04-02 08:54:40 -04:00
|
|
|
|
2019-09-02 16:49:02 -04:00
|
|
|
use crate::Result;
|
2022-02-26 10:54:07 -05:00
|
|
|
use serde::{de::DeserializeOwned, Serialize};
|
2019-04-02 08:54:40 -04:00
|
|
|
|
2022-02-26 10:54:07 -05:00
|
|
|
pub trait StatelessSerializable: Serialize + DeserializeOwned {}
|
2019-05-02 11:25:48 -04:00
|
|
|
|
2019-04-02 08:54:40 -04:00
|
|
|
pub struct Service {
|
|
|
|
sealed_state: SealedState,
|
|
|
|
validity: u64,
|
|
|
|
}
|
|
|
|
|
2022-02-26 10:54:07 -05:00
|
|
|
#[derive(Serialize, Deserialize)]
|
2019-04-02 08:54:40 -04:00
|
|
|
struct Token {
|
|
|
|
#[serde(rename = "c")]
|
|
|
|
creation: u64,
|
2019-05-02 10:56:47 -04:00
|
|
|
#[serde(rename = "p")]
|
|
|
|
payload: String,
|
2019-04-02 08:54:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Service {
|
|
|
|
pub fn init(secret: &str, validity: u64) -> Self {
|
|
|
|
let sealed_state = SealedState::new(secret);
|
2022-02-26 10:54:07 -05:00
|
|
|
Service {
|
|
|
|
sealed_state,
|
|
|
|
validity,
|
|
|
|
}
|
2019-04-02 08:54:40 -04:00
|
|
|
}
|
|
|
|
|
2019-05-20 17:17:50 -04:00
|
|
|
pub fn create(&self, payload_content: &impl StatelessSerializable) -> String {
|
|
|
|
let payload = serde_json::to_string(payload_content).unwrap();
|
2019-04-02 08:54:40 -04:00
|
|
|
let creation = current_time();
|
2019-05-02 10:56:47 -04:00
|
|
|
let token = Token { creation, payload };
|
2019-04-02 08:54:40 -04:00
|
|
|
let token_serialized = serde_json::to_string(&token).unwrap();
|
|
|
|
|
|
|
|
let token_sealed = self.sealed_state.seal(&token_serialized);
|
|
|
|
|
|
|
|
base64::encode_config(&token_sealed, base64::URL_SAFE_NO_PAD)
|
|
|
|
}
|
|
|
|
|
2019-05-02 10:56:47 -04:00
|
|
|
pub fn check<T>(&self, token_encoded: &str) -> Result<T>
|
2022-02-26 10:54:07 -05:00
|
|
|
where
|
|
|
|
T: StatelessSerializable,
|
|
|
|
{
|
2019-04-05 11:07:40 -04:00
|
|
|
let token_sealed = base64::decode_config(&token_encoded, base64::URL_SAFE_NO_PAD)
|
2020-11-04 16:21:00 -05:00
|
|
|
.map_err(|_| anyhow!("invalid b64"))?;
|
2022-02-26 10:54:07 -05:00
|
|
|
let token_str = self
|
|
|
|
.sealed_state
|
|
|
|
.unseal(token_sealed)
|
2020-11-04 16:21:00 -05:00
|
|
|
.map_err(|_| anyhow!("failed to validate"))?;
|
2022-02-26 10:54:07 -05:00
|
|
|
let token: Token =
|
|
|
|
serde_json::from_str(&token_str).map_err(|_| anyhow!("failed to deserialize"))?;
|
2019-04-02 08:54:40 -04:00
|
|
|
|
|
|
|
let elapsed = current_time() - token.creation;
|
|
|
|
if elapsed > self.validity {
|
2022-02-18 05:14:58 -05:00
|
|
|
return Err(anyhow!("Token has expired!"));
|
2019-04-02 08:54:40 -04:00
|
|
|
}
|
|
|
|
|
2019-05-02 10:56:47 -04:00
|
|
|
let payload: T = serde_json::from_str(&token.payload)
|
2020-11-04 16:21:00 -05:00
|
|
|
.map_err(|_| anyhow!("failed to deserialize payload"))?;
|
2019-05-02 10:56:47 -04:00
|
|
|
|
|
|
|
Ok(payload)
|
2019-04-02 08:54:40 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(test))]
|
|
|
|
fn current_time() -> u64 {
|
2019-04-16 09:51:13 -04:00
|
|
|
use std::time::SystemTime;
|
2022-02-26 10:54:07 -05:00
|
|
|
SystemTime::now()
|
|
|
|
.duration_since(SystemTime::UNIX_EPOCH)
|
|
|
|
.unwrap()
|
|
|
|
.as_secs()
|
2019-04-02 08:54:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
fn current_time() -> u64 {
|
|
|
|
12345678
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
2022-02-26 10:54:07 -05:00
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
2019-05-02 11:25:48 -04:00
|
|
|
struct TestStruct1 {
|
|
|
|
payload: String,
|
|
|
|
}
|
2022-02-26 10:54:07 -05:00
|
|
|
impl StatelessSerializable for TestStruct1 {}
|
2019-05-02 11:25:48 -04:00
|
|
|
|
2022-02-26 10:54:07 -05:00
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
2019-05-02 11:25:48 -04:00
|
|
|
struct TestStruct2 {
|
|
|
|
something: String,
|
|
|
|
}
|
2022-02-26 10:54:07 -05:00
|
|
|
impl StatelessSerializable for TestStruct2 {}
|
2019-05-02 11:25:48 -04:00
|
|
|
|
2019-04-02 08:54:40 -04:00
|
|
|
#[test]
|
|
|
|
fn test_create_check() {
|
2022-02-26 10:54:07 -05:00
|
|
|
let payload = TestStruct1 {
|
|
|
|
payload: "hello".to_owned(),
|
|
|
|
};
|
2019-04-02 08:54:40 -04:00
|
|
|
let mt = Service::init("secret", 60);
|
2019-05-20 17:17:50 -04:00
|
|
|
let token = mt.create(&payload);
|
2019-04-02 08:54:40 -04:00
|
|
|
// println!("{}", &token);
|
|
|
|
// assert!(false);
|
|
|
|
|
|
|
|
let check_result = mt.check(&token);
|
|
|
|
|
2019-05-02 11:25:48 -04:00
|
|
|
assert_eq!(payload, check_result.unwrap());
|
2019-04-02 08:54:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_ok() {
|
2022-02-26 10:54:07 -05:00
|
|
|
let payload = TestStruct1 {
|
|
|
|
payload: "hello".to_owned(),
|
|
|
|
};
|
2019-05-02 11:25:48 -04:00
|
|
|
let token = "rwM_S9gZaRQaf6DLvmWtZSipQhH_G5ronSIJv2FrMdwGBPSYYQ-1jaP58dTHU5WuC14vb8jxmz2Xf_b3pqzpCGTEJj9drm4t";
|
2019-04-02 08:54:40 -04:00
|
|
|
let mt = Service::init("secret", 60);
|
|
|
|
|
|
|
|
let check_result = mt.check(token);
|
|
|
|
|
2019-05-02 11:25:48 -04:00
|
|
|
assert_eq!(payload, check_result.unwrap());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bad_type() {
|
2022-02-26 10:54:07 -05:00
|
|
|
let payload = TestStruct1 {
|
|
|
|
payload: "hello".to_owned(),
|
|
|
|
};
|
2019-05-02 11:25:48 -04:00
|
|
|
let mt = Service::init("secret", 60);
|
|
|
|
|
2019-05-20 17:17:50 -04:00
|
|
|
let token = mt.create(&payload);
|
2019-05-02 11:25:48 -04:00
|
|
|
let check_result = mt.check::<TestStruct2>(&token);
|
|
|
|
|
|
|
|
assert!(check_result.is_err());
|
2019-04-02 08:54:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_expired() {
|
2019-05-02 11:25:48 -04:00
|
|
|
// {"c":12345078,"f":"D4AB192964F76A7F8F8A9B357BD18320DEADFA11"}
|
2019-04-02 10:06:42 -04:00
|
|
|
let token = "tqDOpM5mdNSTCDzyyy6El_Chpj1k-ozzw4AHy-3KJhxkXs8A17GJYVq7CHbgsYMc7n5irdzOJ-IvForV_HiVSnZYpnS_BiORWN6FISVmnwlMxDBIGUqa1XDiBLD7UW8";
|
2019-04-02 08:54:40 -04:00
|
|
|
let mt = Service::init("secret", 60);
|
|
|
|
|
2019-05-02 11:25:48 -04:00
|
|
|
let check_result = mt.check::<TestStruct1>(token);
|
2019-04-02 08:54:40 -04:00
|
|
|
|
2019-04-16 09:51:13 -04:00
|
|
|
assert!(check_result.is_err());
|
2019-04-02 08:54:40 -04:00
|
|
|
}
|
|
|
|
}
|