cloudillo_core/
proxy_token_cache.rs1use std::num::NonZeroUsize;
18use std::sync::Arc;
19
20use lru::LruCache;
21use serde::Deserialize;
22
23use crate::prelude::*;
24
25const SAFETY_MARGIN_SECS: i64 = 60;
28
29const DEFAULT_CAPACITY: NonZeroUsize = match NonZeroUsize::new(256) {
34 Some(n) => n,
35 None => NonZeroUsize::MIN,
36};
37
38#[derive(Debug, Clone)]
39struct CachedAccessToken {
40 token: Box<str>,
41 valid_until: Timestamp,
42}
43
44type TokenCacheKey = (TnId, Box<str>);
45type TokenCacheInner = LruCache<TokenCacheKey, CachedAccessToken>;
46
47#[derive(Debug)]
52pub struct ProxyTokenCache {
53 entries: Arc<parking_lot::Mutex<TokenCacheInner>>,
54}
55
56impl ProxyTokenCache {
57 pub fn new() -> Self {
58 Self::with_capacity(DEFAULT_CAPACITY)
59 }
60
61 pub fn with_capacity(capacity: NonZeroUsize) -> Self {
62 Self { entries: Arc::new(parking_lot::Mutex::new(LruCache::new(capacity))) }
63 }
64
65 pub fn get(&self, tn_id: TnId, id_tag: &str) -> Option<Box<str>> {
67 let mut cache = self.entries.lock();
68 let now = Timestamp::now();
69 cache
70 .get(&(tn_id, Box::<str>::from(id_tag)))
71 .filter(|e| e.valid_until.0 > now.0)
72 .map(|e| e.token.clone())
73 }
74
75 pub fn insert(&self, tn_id: TnId, id_tag: &str, token: Box<str>) {
78 let valid_until = match read_jwt_exp(&token) {
79 Ok(exp) => Timestamp(exp.0 - SAFETY_MARGIN_SECS),
80 Err(e) => {
81 warn!(id_tag = %id_tag, error = %e,
82 "failed to read access-token exp; using minimal cache TTL");
83 Timestamp::from_now(60)
84 }
85 };
86 let mut cache = self.entries.lock();
87 cache.put((tn_id, Box::<str>::from(id_tag)), CachedAccessToken { token, valid_until });
88 }
89
90 pub fn invalidate(&self, tn_id: TnId, id_tag: &str) {
93 let mut cache = self.entries.lock();
94 cache.pop(&(tn_id, Box::<str>::from(id_tag)));
95 }
96}
97
98impl Default for ProxyTokenCache {
99 fn default() -> Self {
100 Self::new()
101 }
102}
103
104#[derive(Deserialize)]
105struct AccessTokenExp {
106 exp: i64,
107}
108
109fn read_jwt_exp(jwt: &str) -> ClResult<Timestamp> {
110 let claim: AccessTokenExp = cloudillo_types::utils::decode_jwt_no_verify(jwt)?;
111 Ok(Timestamp(claim.exp))
112}
113
114