use std::collections::HashMap;
use std::sync::{MutexGuard, RwLockWriteGuard};
use super::grant::Grant;
use super::generator::TagGrant;
pub trait Authorizer {
fn authorize(&mut self, _: Grant) -> Result<String, ()>;
fn extract(&mut self, token: &str) -> Result<Option<Grant>, ()>;
}
pub struct AuthMap<I: TagGrant = Box<dyn TagGrant + Send + Sync + 'static>> {
tagger: I,
usage: u64,
tokens: HashMap<String, Grant>,
}
impl<I: TagGrant> AuthMap<I> {
pub fn new(tagger: I) -> Self {
AuthMap {
tagger,
usage: 0,
tokens: HashMap::new(),
}
}
}
impl<'a, A: Authorizer + ?Sized> Authorizer for &'a mut A {
fn authorize(&mut self, grant: Grant) -> Result<String, ()> {
(**self).authorize(grant)
}
fn extract(&mut self, code: &str) -> Result<Option<Grant>, ()> {
(**self).extract(code)
}
}
impl<A: Authorizer + ?Sized> Authorizer for Box<A> {
fn authorize(&mut self, grant: Grant) -> Result<String, ()> {
(**self).authorize(grant)
}
fn extract(&mut self, code: &str) -> Result<Option<Grant>, ()> {
(**self).extract(code)
}
}
impl<'a, A: Authorizer + ?Sized> Authorizer for MutexGuard<'a, A> {
fn authorize(&mut self, grant: Grant) -> Result<String, ()> {
(**self).authorize(grant)
}
fn extract(&mut self, code: &str) -> Result<Option<Grant>, ()> {
(**self).extract(code)
}
}
impl<'a, A: Authorizer + ?Sized> Authorizer for RwLockWriteGuard<'a, A> {
fn authorize(&mut self, grant: Grant) -> Result<String, ()> {
(**self).authorize(grant)
}
fn extract(&mut self, code: &str) -> Result<Option<Grant>, ()> {
(**self).extract(code)
}
}
impl<I: TagGrant> Authorizer for AuthMap<I> {
fn authorize(&mut self, grant: Grant) -> Result<String, ()> {
let next_usage = self.usage.wrapping_add(1);
let token = self.tagger.tag(next_usage - 1, &grant)?;
self.tokens.insert(token.clone(), grant);
self.usage = next_usage;
Ok(token)
}
fn extract<'a>(&mut self, grant: &'a str) -> Result<Option<Grant>, ()> {
Ok(self.tokens.remove(grant))
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use chrono::Utc;
use crate::primitives::grant::Extensions;
use crate::primitives::generator::{Assertion, AssertionKind, RandomGenerator};
pub fn simple_test_suite(authorizer: &mut dyn Authorizer) {
let grant = Grant {
owner_id: "Owner".to_string(),
client_id: "Client".to_string(),
scope: "One two three scopes".parse().unwrap(),
redirect_uri: "https://example.com/redirect_me".parse().unwrap(),
until: Utc::now(),
extensions: Extensions::new(),
};
let token = authorizer
.authorize(grant.clone())
.expect("Authorization should not fail here");
let recovered_grant = authorizer
.extract(&token)
.expect("Primitive failed extracting grant")
.expect("Could not extract grant for valid token");
if grant != recovered_grant {
panic!("Grant was not stored correctly");
}
if authorizer.extract(&token).unwrap().is_some() {
panic!("Token must only be usable once");
}
let token_again = authorizer
.authorize(grant.clone())
.expect("Authorization should not fail here");
assert_ne!(token, token_again);
}
#[test]
fn random_test_suite() {
let mut storage = AuthMap::new(RandomGenerator::new(16));
simple_test_suite(&mut storage);
}
#[test]
fn signing_test_suite() {
let assertion = Assertion::new(
AssertionKind::HmacSha256,
b"7EGgy8zManReq9l/ez0AyYE+xPpcTbssgW+8gBnIv3s=",
);
let mut storage = AuthMap::new(assertion);
simple_test_suite(&mut storage);
}
#[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 storage = AuthMap::new(BadGenerator);
simple_test_suite(&mut storage);
}
}