rovenue/sessions/
account_token.rs

1use std::sync::Arc;
2
3use uuid::Uuid;
4
5use crate::cache::CacheStore;
6use crate::error::{RovenueError, RovenueResult};
7
8pub struct AccountTokenStore {
9    store: Arc<CacheStore>,
10}
11
12impl AccountTokenStore {
13    pub fn new(store: Arc<CacheStore>) -> Self {
14        Self { store }
15    }
16
17    /// Returns the stable token for the given scope (typically the
18    /// subscriber's rovenue_id or app_user_id). Generates + persists on
19    /// first call; subsequent calls return the same UUID.
20    pub fn get_or_create(&self, scope: &str) -> RovenueResult<String> {
21        if scope.trim().is_empty() {
22            return Err(RovenueError::Internal());
23        }
24        if let Some(existing) = self.store.get_app_account_token(scope)? {
25            return Ok(existing);
26        }
27        let new_token = Uuid::new_v4().to_string();
28        self.store.put_app_account_token(scope, &new_token)?;
29        // Re-read to handle race: another caller may have inserted first.
30        self.store
31            .get_app_account_token(scope)?
32            .ok_or(RovenueError::Storage())
33    }
34
35    /// Drops every stored token. Called on log_out so the next identity does not
36    /// reuse the previous user's account token.
37    pub fn clear(&self) -> RovenueResult<()> {
38        self.store.clear_app_account_tokens()
39    }
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45
46    #[test]
47    fn returns_same_token_on_repeat_calls() {
48        let store = Arc::new(CacheStore::open_in_memory().unwrap());
49        let svc = AccountTokenStore::new(Arc::clone(&store));
50        let t1 = svc.get_or_create("user-a").unwrap();
51        let t2 = svc.get_or_create("user-a").unwrap();
52        assert_eq!(t1, t2);
53    }
54
55    #[test]
56    fn different_scopes_get_different_tokens() {
57        let store = Arc::new(CacheStore::open_in_memory().unwrap());
58        let svc = AccountTokenStore::new(Arc::clone(&store));
59        assert_ne!(
60            svc.get_or_create("user-a").unwrap(),
61            svc.get_or_create("user-b").unwrap(),
62        );
63    }
64
65    #[test]
66    fn token_is_valid_uuid_v4() {
67        let store = Arc::new(CacheStore::open_in_memory().unwrap());
68        let svc = AccountTokenStore::new(Arc::clone(&store));
69        let t = svc.get_or_create("user-a").unwrap();
70        let parsed = Uuid::parse_str(&t).expect("valid UUID");
71        assert_eq!(parsed.get_version_num(), 4);
72    }
73
74    #[test]
75    fn clear_removes_all_tokens() {
76        let store = Arc::new(CacheStore::open_in_memory().unwrap());
77        let svc = AccountTokenStore::new(Arc::clone(&store));
78        svc.get_or_create("user-a").unwrap();
79        svc.get_or_create("user-b").unwrap();
80        assert_eq!(store.count_app_account_tokens().unwrap(), 2);
81        svc.clear().unwrap();
82        assert_eq!(store.count_app_account_tokens().unwrap(), 0);
83    }
84
85    #[test]
86    fn empty_scope_returns_internal_error() {
87        let store = Arc::new(CacheStore::open_in_memory().unwrap());
88        let svc = AccountTokenStore::new(Arc::clone(&store));
89        assert!(svc.get_or_create("").is_err());
90    }
91}