use std::collections::HashMap;
use super::Time;
use super::grant::Grant;
use super::generator::{TokenGenerator, Assertion};
use ring::digest::SHA256;
use ring::pbkdf2::derive as key_derive;
use ring::hmac::SigningKey;
pub trait Issuer {
fn issue(&mut self, Grant) -> Result<IssuedToken, ()>;
fn recover_token<'a>(&'a self, &'a str) -> Option<Grant>;
fn recover_refresh<'a>(&'a self, &'a str) -> Option<Grant>;
}
#[derive(Clone, Debug)]
pub struct IssuedToken {
pub token: String,
pub refresh: String,
pub until: Time,
}
pub struct TokenMap<G: TokenGenerator> {
generator: G,
access: HashMap<String, Grant>,
refresh: HashMap<String, Grant>,
}
impl<G: TokenGenerator> TokenMap<G> {
pub fn new(generator: G) -> Self {
Self {
generator: generator,
access: HashMap::new(),
refresh: HashMap::new(),
}
}
}
impl<G: TokenGenerator> Issuer for TokenMap<G> {
fn issue(&mut self, grant: Grant) -> Result<IssuedToken, ()> {
let (token, refresh) = {
let token = self.generator.generate(&grant)?;
let refresh = self.generator.generate(&grant)?;
(token, refresh)
};
let until = grant.until.clone();
self.access.insert(token.clone(), grant.clone());
self.refresh.insert(refresh.clone(), grant);
Ok(IssuedToken { token, refresh, until })
}
fn recover_token<'a>(&'a self, token: &'a str) -> Option<Grant> {
self.access.get(token).cloned()
}
fn recover_refresh<'a>(&'a self, token: &'a str) -> Option<Grant> {
self.refresh.get(token).cloned()
}
}
pub struct TokenSigner {
signer: Assertion,
}
impl TokenSigner {
pub fn new(key: SigningKey) -> TokenSigner {
TokenSigner { signer: Assertion::new(key) }
}
pub fn new_from_passphrase(passwd: &str, salt: Option<&[u8]>) -> TokenSigner {
let salt = salt.unwrap_or(
b"\xdf\xcf\xddt\n\xd08a*\xc3\x96\xafj<\x8c\xa7\xaa\x15\xce$\x83ND\xb4\xdf\x98%\xcb\xde\x1f\xf0\x9a"
);
let mut out = Vec::new();
out.resize(SHA256.block_len, 0);
key_derive(
&SHA256,
2 << 16,
salt,
passwd.as_bytes(),
out.as_mut_slice());
let key = SigningKey::new(&SHA256, out.as_slice());
TokenSigner { signer: Assertion::new(key) }
}
}
impl Issuer for TokenSigner {
fn issue(&mut self, grant: Grant) -> Result<IssuedToken, ()> {
let token = self.signer.tag("token").generate(&grant)?;
let refresh = self.signer.tag("refresh").generate(&grant)?;
Ok(IssuedToken {token, refresh, until: grant.until})
}
fn recover_token<'a>(&'a self, token: &'a str) -> Option<Grant> {
self.signer.tag("token").extract(token).ok()
}
fn recover_refresh<'a>(&'a self, token: &'a str) -> Option<Grant> {
self.signer.tag("refresh").extract(token).ok()
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use primitives::grant::Extensions;
use primitives::generator::RandomGenerator;
use chrono::{Duration, Utc};
pub fn simple_test_suite(issuer: &mut Issuer) {
let request = Grant {
client_id: "Client".to_string(),
owner_id: "Owner".to_string(),
redirect_uri: "https://example.com".parse().unwrap(),
scope: "default".parse().unwrap(),
until: Utc::now() + Duration::hours(1),
extensions: Extensions::new(),
};
let issued = issuer.issue(request)
.expect("Issuing failed");
let from_token = issuer.recover_token(&issued.token)
.expect("Could not recover the issued token");
assert_eq!(from_token.client_id, "Client");
assert_eq!(from_token.owner_id, "Owner");
assert!(Utc::now() < from_token.until);
}
#[test]
fn signer_test_suite() {
let passwd = "Some secret password";
let mut signer = TokenSigner::new_from_passphrase(passwd, None);
simple_test_suite(&mut signer);
}
#[test]
fn token_map_test_suite() {
let mut token_map = TokenMap::new(RandomGenerator::new(16));
simple_test_suite(&mut token_map);
}
}