arcly_http_identity/
consent.rs1use std::collections::HashMap;
6use std::sync::RwLock;
7use std::time::{SystemTime, UNIX_EPOCH};
8
9use async_trait::async_trait;
10use serde::{Deserialize, Serialize};
11
12use crate::error::Result;
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct ConsentGrant {
17 pub purpose: String,
18 pub version: String,
20 pub granted: bool,
21 pub timestamp: u64,
22}
23
24#[async_trait]
27pub trait ConsentStore: Send + Sync + 'static {
28 async fn record(&self, user_id: &str, grant: ConsentGrant) -> Result<()>;
29 async fn current(&self, user_id: &str) -> Result<Vec<ConsentGrant>>;
31 async fn has_consent(&self, user_id: &str, purpose: &str) -> Result<bool> {
33 Ok(self
34 .current(user_id)
35 .await?
36 .into_iter()
37 .find(|g| g.purpose == purpose)
38 .map(|g| g.granted)
39 .unwrap_or(false))
40 }
41}
42
43pub fn now() -> u64 {
44 SystemTime::now()
45 .duration_since(UNIX_EPOCH)
46 .map(|d| d.as_secs())
47 .unwrap_or(0)
48}
49
50#[derive(Default)]
52pub struct MemoryConsentStore {
53 inner: RwLock<HashMap<String, Vec<ConsentGrant>>>,
54}
55impl MemoryConsentStore {
56 pub fn new() -> Self {
57 Self::default()
58 }
59}
60
61#[async_trait]
62impl ConsentStore for MemoryConsentStore {
63 async fn record(&self, user_id: &str, grant: ConsentGrant) -> Result<()> {
64 self.inner
65 .write()
66 .unwrap()
67 .entry(user_id.to_owned())
68 .or_default()
69 .push(grant);
70 Ok(())
71 }
72
73 async fn current(&self, user_id: &str) -> Result<Vec<ConsentGrant>> {
74 let guard = self.inner.read().unwrap();
75 let Some(history) = guard.get(user_id) else {
76 return Ok(Vec::new());
77 };
78 let mut latest: HashMap<&str, &ConsentGrant> = HashMap::new();
80 for g in history {
81 latest
82 .entry(g.purpose.as_str())
83 .and_modify(|cur| {
84 if g.timestamp >= cur.timestamp {
85 *cur = g;
86 }
87 })
88 .or_insert(g);
89 }
90 Ok(latest.into_values().cloned().collect())
91 }
92}