use std::collections::HashMap;
use std::sync::Mutex;
use std::time::{Duration, Instant};
use sha2::{Digest, Sha256};
use super::Permission;
use crate::error::Error;
use crate::namespace::Namespace;
type Key = ([u8; 32], String);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Decision {
Granted(Permission),
Unauthenticated,
Forbidden,
}
impl Decision {
pub fn of(outcome: &Result<Permission, Error>) -> Option<Self> {
match outcome {
Ok(permission) => Some(Self::Granted(*permission)),
Err(Error::Unauthenticated) => Some(Self::Unauthenticated),
Err(Error::Forbidden) => Some(Self::Forbidden),
Err(_) => None,
}
}
}
impl From<Decision> for Result<Permission, Error> {
fn from(decision: Decision) -> Self {
match decision {
Decision::Granted(permission) => Ok(permission),
Decision::Unauthenticated => Err(Error::Unauthenticated),
Decision::Forbidden => Err(Error::Forbidden),
}
}
}
struct Entry {
decision: Decision,
expires_at: Instant,
}
pub struct Cache {
granted_ttl: Duration,
rejected_ttl: Duration,
entries: Mutex<HashMap<Key, Entry>>,
}
impl Cache {
pub fn new(granted_ttl: Duration, rejected_ttl: Duration) -> Self {
Self {
granted_ttl,
rejected_ttl,
entries: Mutex::new(HashMap::new()),
}
}
pub fn get(&self, token: &str, ns: &Namespace) -> Option<Decision> {
let entries = self.entries.lock().expect("permission cache");
let entry = entries.get(&key(token, ns))?;
(entry.expires_at > Instant::now()).then_some(entry.decision)
}
pub fn insert(&self, token: &str, ns: &Namespace, decision: Decision) {
let now = Instant::now();
let ttl = match decision {
Decision::Granted(_) => self.granted_ttl,
_ => self.rejected_ttl,
};
let mut entries = self.entries.lock().expect("permission cache");
entries.retain(|_, entry| entry.expires_at > now);
entries.insert(
key(token, ns),
Entry {
decision,
expires_at: now + ttl,
},
);
}
}
fn key(token: &str, ns: &Namespace) -> Key {
(fingerprint(token), ns.to_string())
}
pub struct IdentityCache {
ttl: Duration,
entries: Mutex<HashMap<[u8; 32], (String, Instant)>>,
}
impl IdentityCache {
pub fn new(ttl: Duration) -> Self {
Self {
ttl,
entries: Mutex::new(HashMap::new()),
}
}
pub fn get(&self, token: &str) -> Option<String> {
let entries = self.entries.lock().expect("identity cache");
let (login, expires_at) = entries.get(&fingerprint(token))?;
(*expires_at > Instant::now()).then(|| login.clone())
}
pub fn insert(&self, token: &str, login: &str) {
let now = Instant::now();
let mut entries = self.entries.lock().expect("identity cache");
entries.retain(|_, (_, expires_at)| *expires_at > now);
entries.insert(fingerprint(token), (login.to_owned(), now + self.ttl));
}
}
fn fingerprint(token: &str) -> [u8; 32] {
Sha256::digest(token.as_bytes()).into()
}