use std::collections::HashMap;
use std::sync::{MutexGuard, RwLockWriteGuard};
use std::sync::atomic::{AtomicUsize, Ordering};
use chrono::{Duration, Utc};
use super::Time;
use super::grant::Grant;
use super::generator::{TagGrant, Assertion};
pub trait Issuer {
fn issue(&mut self, Grant) -> Result<IssuedToken, ()>;
fn recover_token<'a>(&'a self, &'a str) -> Result<Option<Grant>, ()>;
fn recover_refresh<'a>(&'a self, &'a str) -> Result<Option<Grant>, ()>;
}
#[derive(Clone, Debug)]
pub struct IssuedToken {
pub token: String,
pub refresh: String,
pub until: Time,
}
pub struct TokenMap<G: TagGrant=Box<dyn TagGrant + Send + Sync + 'static>> {
duration: Option<Duration>,
generator: G,
usage: u64,
access: HashMap<String, Grant>,
refresh: HashMap<String, Grant>,
}
impl<G: TagGrant> TokenMap<G> {
pub fn new(generator: G) -> Self {
Self {
duration: None,
generator,
usage: 0,
access: HashMap::new(),
refresh: HashMap::new(),
}
}
pub fn valid_for(&mut self, duration: Duration) {
self.duration = Some(duration);
}
pub fn valid_for_default(&mut self) {
self.duration = None;
}
pub fn revoke(&mut self, token: &str) {
self.access.remove(token);
self.refresh.remove(token);
}
pub fn import_grant(&mut self, token: String, mut grant: Grant) {
self.set_duration(&mut grant);
self.access.insert(token, grant);
}
fn set_duration(&self, grant: &mut Grant) {
if let Some(duration) = &self.duration {
grant.until = Utc::now() + *duration;
}
}
}
impl<G: TagGrant> Issuer for TokenMap<G> {
fn issue(&mut self, mut grant: Grant) -> Result<IssuedToken, ()> {
self.set_duration(&mut grant);
let next_usage = self.usage.wrapping_add(2);
let (token, refresh) = {
let token = self.generator.tag(next_usage - 2, &grant)?;
let refresh = self.generator.tag(next_usage - 1, &grant)?;
(token, refresh)
};
let until = grant.until;
self.access.insert(token.clone(), grant.clone());
self.refresh.insert(refresh.clone(), grant);
self.usage = next_usage;
Ok(IssuedToken { token, refresh, until })
}
fn recover_token<'a>(&'a self, token: &'a str) -> Result<Option<Grant>, ()> {
Ok(self.access.get(token).cloned())
}
fn recover_refresh<'a>(&'a self, token: &'a str) -> Result<Option<Grant>, ()> {
Ok(self.refresh.get(token).cloned())
}
}
pub struct TokenSigner {
duration: Option<Duration>,
signer: Assertion,
counter: AtomicUsize,
}
impl TokenSigner {
pub fn new<S: Into<Assertion>>(secret: S) -> TokenSigner {
TokenSigner {
duration: None,
signer: secret.into(),
counter: AtomicUsize::new(0),
}
}
pub fn ephemeral() -> TokenSigner {
TokenSigner::new(Assertion::ephermal())
}
pub fn valid_for(&mut self, duration: Duration) {
self.duration = Some(duration);
}
pub fn valid_for_default(&mut self) {
self.duration = None;
}
fn next_counter(&self) -> usize {
self.counter.fetch_add(1, Ordering::Relaxed)
}
}
impl<'s, I: Issuer + ?Sized> Issuer for &'s mut I {
fn issue(&mut self, grant: Grant) -> Result<IssuedToken, ()> {
(**self).issue(grant)
}
fn recover_token<'a>(&'a self, token: &'a str) -> Result<Option<Grant>, ()> {
(**self).recover_token(token)
}
fn recover_refresh<'a>(&'a self, token: &'a str) -> Result<Option<Grant>, ()> {
(**self).recover_refresh(token)
}
}
impl<I: Issuer + ?Sized> Issuer for Box<I> {
fn issue(&mut self, grant: Grant) -> Result<IssuedToken, ()> {
(**self).issue(grant)
}
fn recover_token<'a>(&'a self, token: &'a str) -> Result<Option<Grant>, ()> {
(**self).recover_token(token)
}
fn recover_refresh<'a>(&'a self, token: &'a str) -> Result<Option<Grant>, ()> {
(**self).recover_refresh(token)
}
}
impl<'s, I: Issuer + ?Sized> Issuer for MutexGuard<'s, I> {
fn issue(&mut self, grant: Grant) -> Result<IssuedToken, ()> {
(**self).issue(grant)
}
fn recover_token<'a>(&'a self, token: &'a str) -> Result<Option<Grant>, ()> {
(**self).recover_token(token)
}
fn recover_refresh<'a>(&'a self, token: &'a str) -> Result<Option<Grant>, ()> {
(**self).recover_refresh(token)
}
}
impl<'s, I: Issuer + ?Sized> Issuer for RwLockWriteGuard<'s, I> {
fn issue(&mut self, grant: Grant) -> Result<IssuedToken, ()> {
(**self).issue(grant)
}
fn recover_token<'a>(&'a self, token: &'a str) -> Result<Option<Grant>, ()> {
(**self).recover_token(token)
}
fn recover_refresh<'a>(&'a self, token: &'a str) -> Result<Option<Grant>, ()> {
(**self).recover_refresh(token)
}
}
impl Issuer for TokenSigner {
fn issue(&mut self, grant: Grant) -> Result<IssuedToken, ()> {
(&mut&*self).issue(grant)
}
fn recover_token<'a>(&'a self, token: &'a str) -> Result<Option<Grant>, ()> {
(&&*self).recover_token(token)
}
fn recover_refresh<'a>(&'a self, token: &'a str) -> Result<Option<Grant>, ()> {
(&&*self).recover_refresh(token)
}
}
impl<'a> Issuer for &'a TokenSigner {
fn issue(&mut self, mut grant: Grant) -> Result<IssuedToken, ()> {
if let Some(duration) = &self.duration {
grant.until = Utc::now() + *duration;
}
let first_ctr = self.next_counter() as u64;
let second_ctr = self.next_counter() as u64;
let token = self.signer.tag("token").sign(first_ctr, &grant)?;
let refresh = self.signer.tag("refresh").sign(second_ctr, &grant)?;
Ok(IssuedToken {
token,
refresh,
until: grant.until,
})
}
fn recover_token<'t>(&'t self, token: &'t str) -> Result<Option<Grant>, ()> {
Ok(self.signer.tag("token").extract(token).ok())
}
fn recover_refresh<'t>(&'t self, token: &'t str) -> Result<Option<Grant>, ()> {
Ok(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 dyn 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.clone())
.expect("Issuing failed");
let from_token = issuer.recover_token(&issued.token)
.expect("Issuer failed during recover")
.expect("Issued token appears to be invalid");
assert_ne!(issued.token, issued.refresh);
assert_eq!(from_token.client_id, "Client");
assert_eq!(from_token.owner_id, "Owner");
assert!(Utc::now() < from_token.until);
let issued_2 = issuer.issue(request)
.expect("Issuing failed");
assert_ne!(issued.token, issued_2.token);
assert_ne!(issued.token, issued_2.refresh);
assert_ne!(issued.refresh, issued_2.refresh);
assert_ne!(issued.refresh, issued_2.token);
}
#[test]
fn signer_test_suite() {
let mut signer = TokenSigner::ephemeral();
simple_test_suite(&mut signer);
}
#[test]
fn random_test_suite() {
let mut token_map = TokenMap::new(RandomGenerator::new(16));
simple_test_suite(&mut token_map);
}
#[test]
#[should_panic]
fn bad_generator() {
struct BadGenerator;
impl TagGrant for BadGenerator {
fn tag(&mut self, _: u64, _: &Grant) -> Result<String, ()> {
Ok("YOLO.HowBadCanItBeToRepeatTokens?".into())
}
}
let mut token_map = TokenMap::new(BadGenerator);
simple_test_suite(&mut token_map);
}
}