1use std::collections::HashMap;
2use std::sync::RwLock;
3
4#[derive(Debug, Clone)]
6pub struct NamespacePermission {
7 pub read: bool,
8 pub write: bool,
9 pub delete: bool,
10 pub reason: bool,
11}
12
13impl Default for NamespacePermission {
14 fn default() -> Self {
15 Self {
16 read: true,
17 write: true,
18 delete: true,
19 reason: true,
20 }
21 }
22}
23
24pub struct NamespaceAuth {
26 tokens: RwLock<HashMap<String, (Vec<String>, NamespacePermission)>>,
28 pub allow_anonymous_default: bool,
30}
31
32impl Default for NamespaceAuth {
33 fn default() -> Self {
34 Self::new()
35 }
36}
37
38impl NamespaceAuth {
39 pub fn new() -> Self {
40 Self {
41 tokens: RwLock::new(HashMap::new()),
42 allow_anonymous_default: true,
43 }
44 }
45
46 pub fn register_token(&self, token: &str, namespaces: Vec<String>, permissions: NamespacePermission) {
48 let mut tokens = self.tokens.write().unwrap();
49 tokens.insert(token.to_string(), (namespaces, permissions));
50 }
51
52 pub fn check(&self, token: Option<&str>, namespace: &str, operation: &str) -> Result<(), String> {
54 if token.is_none() && namespace == "default" && self.allow_anonymous_default {
56 return Ok(());
57 }
58
59 let token = token.ok_or("Authentication required")?;
60 let tokens = self.tokens.read().unwrap();
61
62 let (patterns, perms) = tokens.get(token)
63 .ok_or("Invalid token")?;
64
65 let ns_match = patterns.iter().any(|p| {
67 if p == "*" { true }
68 else if p.ends_with('*') { namespace.starts_with(&p[..p.len()-1]) }
69 else { p == namespace }
70 });
71
72 if !ns_match {
73 return Err(format!("Token not authorized for namespace: {}", namespace));
74 }
75
76 match operation {
78 "read" if !perms.read => Err("Read permission denied".to_string()),
79 "write" if !perms.write => Err("Write permission denied".to_string()),
80 "delete" if !perms.delete => Err("Delete permission denied".to_string()),
81 "reason" if !perms.reason => Err("Reasoning permission denied".to_string()),
82 _ => Ok(()),
83 }
84 }
85
86 pub fn load_from_env(&self) {
88 if let Ok(json) = std::env::var("SYNAPSE_AUTH_TOKENS") {
89 if let Ok(map) = serde_json::from_str::<HashMap<String, Vec<String>>>(&json) {
90 for (token, namespaces) in map {
91 self.register_token(&token, namespaces, NamespacePermission::default());
92 }
93 }
94 }
95 }
96}