use std::collections::HashMap;
use std::sync::{Arc, MutexGuard, RwLockWriteGuard};
use std::sync::atomic::{AtomicUsize, Ordering};
use chrono::{Duration, Utc};
use super::Time;
use super::grant::Grant;
use super::generator::{TagGrant, TaggedAssertion, Assertion};
pub trait Issuer {
fn issue(&mut self, grant: Grant) -> Result<IssuedToken, ()>;
fn refresh(&mut self, _refresh: &str, _grant: Grant) -> Result<RefreshedToken, ()> {
Err(())
}
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,
}
#[derive(Clone, Debug)]
pub struct RefreshedToken {
pub token: String,
pub refresh: Option<String>,
pub until: Time,
}
pub struct TokenMap<G: TagGrant=Box<dyn TagGrant + Send + Sync + 'static>> {
duration: Option<Duration>,
generator: G,
usage: u64,
access: HashMap<Arc<str>, Arc<Token>>,
refresh: HashMap<Arc<str>, Arc<Token>>,
}
struct Token {
access: Arc<str>,
refresh: Option<Arc<str>>,
grant: 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);
let key: Arc<str> = Arc::from(token);
let token = Token::from_access(key.clone(), grant);
self.access.insert(key, Arc::new(token));
}
fn set_duration(&self, grant: &mut Grant) {
if let Some(duration) = &self.duration {
grant.until = Utc::now() + *duration;
}
}
}
impl Token {
fn from_access(access: Arc<str>, grant: Grant) -> Self {
Token {
access,
refresh: None,
grant,
}
}
fn from_refresh(access: Arc<str>, refresh: Arc<str>, grant: Grant) -> Self {
Token {
access,
refresh: Some(refresh),
grant,
}
}
}
impl IssuedToken {
pub fn without_refresh(token: String, until: Time) -> Self {
IssuedToken {
token,
refresh: "".into(),
until,
}
}
pub fn refreshable(&self) -> bool {
!self.refresh.is_empty()
}
}
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 (access, refresh) = {
let access = self.generator.tag(self.usage, &grant)?;
let refresh = self.generator.tag(self.usage.wrapping_add(1), &grant)?;
(access, refresh)
};
let until = grant.until.clone();
let access_key: Arc<str> = Arc::from(access.clone());
let refresh_key: Arc<str> = Arc::from(refresh.clone());
let token = Token::from_refresh(access_key.clone(), refresh_key.clone(), grant);
let token = Arc::new(token);
self.access.insert(access_key, token.clone());
self.refresh.insert(refresh_key, token);
self.usage = next_usage;
Ok(IssuedToken {
token: access,
refresh,
until,
})
}
fn refresh(&mut self, refresh: &str, mut grant: Grant) -> Result<RefreshedToken, ()> {
let (refresh_key, mut token) = self.refresh.remove_entry(refresh)
.ok_or(())?
.clone();
assert!(Arc::ptr_eq(token.refresh.as_ref().unwrap(), &refresh_key));
self.set_duration(&mut grant);
let until = grant.until.clone();
let next_usage = self.usage.wrapping_add(1);
let new_access = self.generator.tag(self.usage, &grant)?;
let new_key: Arc<str> = Arc::from(new_access.clone());
if let Some(atoken) = self.access.remove(&token.access) {
assert!(Arc::ptr_eq(&token, &atoken));
}
{
let mut_token = Arc::get_mut(&mut token).unwrap_or_else(
|| unreachable!("Grant data was only shared with access and refresh"));
mut_token.access = new_key.clone();
mut_token.grant = grant;
}
self.access.insert(new_key, token.clone());
self.refresh.insert(refresh_key, token);
self.usage = next_usage;
Ok(RefreshedToken {
token: new_access,
refresh: None,
until,
})
}
fn recover_token<'a>(&'a self, token: &'a str) -> Result<Option<Grant>, ()> {
Ok(self.access.get(token).map(|token| token.grant.clone()))
}
fn recover_refresh<'a>(&'a self, token: &'a str) -> Result<Option<Grant>, ()> {
Ok(self.refresh.get(token).map(|token| token.grant.clone()))
}
}
pub struct TokenSigner {
duration: Option<Duration>,
signer: Assertion,
counter: AtomicUsize,
have_refresh: bool,
}
impl TokenSigner {
pub fn new<S: Into<Assertion>>(secret: S) -> TokenSigner {
TokenSigner {
duration: None,
signer: secret.into(),
counter: AtomicUsize::new(0),
have_refresh: false,
}
}
pub fn ephemeral() -> TokenSigner {
TokenSigner::new(Assertion::ephemeral())
}
pub fn valid_for(&mut self, duration: Duration) {
self.duration = Some(duration);
}
pub fn valid_for_default(&mut self) {
self.duration = None;
}
pub fn generate_refresh_tokens(&mut self, refresh: bool) {
self.have_refresh = refresh;
}
fn next_counter(&self) -> usize {
self.counter.fetch_add(1, Ordering::Relaxed)
}
fn refreshable_token(&self, grant: &Grant) -> Result<IssuedToken, ()> {
let first_ctr = self.next_counter() as u64;
let second_ctr = self.next_counter() as u64;
let token = self.as_token()
.sign(first_ctr, grant)?;
let refresh = self.as_refresh()
.sign(second_ctr, grant)?;
Ok(IssuedToken {
token,
refresh,
until: grant.until,
})
}
fn unrefreshable_token(&self, grant: &Grant) -> Result<IssuedToken, ()> {
let counter = self.next_counter() as u64;
let token = self.as_token()
.sign(counter, grant)?;
Ok(IssuedToken::without_refresh(token, grant.until))
}
fn as_token(&self) -> TaggedAssertion {
self.signer.tag("token")
}
fn as_refresh(&self) -> TaggedAssertion {
self.signer.tag("refresh")
}
}
impl<'s, I: Issuer + ?Sized> Issuer for &'s mut I {
fn issue(&mut self, grant: Grant) -> Result<IssuedToken, ()> {
(**self).issue(grant)
}
fn refresh(&mut self, token: &str, grant: Grant) -> Result<RefreshedToken, ()> {
(**self).refresh(token, 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 refresh(&mut self, token: &str, grant: Grant) -> Result<RefreshedToken, ()> {
(**self).refresh(token, 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 refresh(&mut self, token: &str, grant: Grant) -> Result<RefreshedToken, ()> {
(**self).refresh(token, 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 refresh(&mut self, token: &str, grant: Grant) -> Result<RefreshedToken, ()> {
(**self).refresh(token, 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;
}
if self.have_refresh {
self.refreshable_token(&grant)
} else {
self.unrefreshable_token(&grant)
}
}
fn recover_token<'t>(&'t self, token: &'t str) -> Result<Option<Grant>, ()> {
Ok(self.as_token().extract(token).ok())
}
fn recover_refresh<'t>(&'t self, token: &'t str) -> Result<Option<Grant>, ()> {
if !self.have_refresh {
return Ok(None)
}
Ok(self.as_refresh().extract(token).ok())
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use primitives::grant::Extensions;
use primitives::generator::RandomGenerator;
use chrono::{Duration, Utc};
fn grant_template() -> Grant {
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(),
}
}
pub fn simple_test_suite(issuer: &mut dyn Issuer) {
let request = grant_template();
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();
signer.generate_refresh_tokens(true);
simple_test_suite(&mut signer);
}
#[test]
fn signer_no_default_refresh() {
let mut signer = TokenSigner::ephemeral();
let issued = signer.issue(grant_template());
let token = issued.expect("Issuing without refresh token failed");
assert!(!token.refreshable());
}
#[test]
fn random_test_suite() {
let mut token_map = TokenMap::new(RandomGenerator::new(16));
simple_test_suite(&mut token_map);
}
#[test]
fn random_has_refresh() {
let mut token_map = TokenMap::new(RandomGenerator::new(16));
let issued = token_map.issue(grant_template());
let token = issued.expect("Issuing without refresh token failed");
assert!(token.refreshable());
}
#[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);
}
}