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(
48 &self,
49 token: &str,
50 namespaces: Vec<String>,
51 permissions: NamespacePermission,
52 ) {
53 let mut tokens = self.tokens.write().unwrap();
54 tokens.insert(token.to_string(), (namespaces, permissions));
55 }
56
57 pub fn check(
59 &self,
60 token: Option<&str>,
61 namespace: &str,
62 operation: &str,
63 ) -> Result<(), String> {
64 if token.is_none() && namespace == "default" && self.allow_anonymous_default {
66 return Ok(());
67 }
68
69 let token = token.ok_or("Authentication required")?;
70 let tokens = self.tokens.read().unwrap();
71
72 let (patterns, perms) = tokens.get(token).ok_or("Invalid token")?;
73
74 let ns_match = patterns.iter().any(|p| {
76 if p == "*" {
77 true
78 } else if p.ends_with('*') {
79 namespace.starts_with(&p[..p.len() - 1])
80 } else {
81 p == namespace
82 }
83 });
84
85 if !ns_match {
86 return Err(format!("Token not authorized for namespace: {}", namespace));
87 }
88
89 match operation {
91 "read" if !perms.read => Err("Read permission denied".to_string()),
92 "write" if !perms.write => Err("Write permission denied".to_string()),
93 "delete" if !perms.delete => Err("Delete permission denied".to_string()),
94 "reason" if !perms.reason => Err("Reasoning permission denied".to_string()),
95 _ => Ok(()),
96 }
97 }
98
99 pub fn load_from_env(&self) {
101 if let Ok(json) = std::env::var("SYNAPSE_AUTH_TOKENS") {
102 if let Ok(map) = serde_json::from_str::<HashMap<String, serde_json::Value>>(&json) {
104 for (token, value) in map {
105 if let Ok(namespaces) = serde_json::from_value::<Vec<String>>(value.clone()) {
106 self.register_token(&token, namespaces, NamespacePermission::default());
108 } else if let Some(obj) = value.as_object() {
109 let namespaces = obj
111 .get("namespaces")
112 .and_then(|v| serde_json::from_value::<Vec<String>>(v.clone()).ok())
113 .unwrap_or_default();
114
115 let permissions = if let Some(p) = obj.get("permissions") {
116 NamespacePermission {
117 read: p.get("read").and_then(|v| v.as_bool()).unwrap_or(true),
118 write: p.get("write").and_then(|v| v.as_bool()).unwrap_or(true),
119 delete: p.get("delete").and_then(|v| v.as_bool()).unwrap_or(true),
120 reason: p.get("reason").and_then(|v| v.as_bool()).unwrap_or(true),
121 }
122 } else {
123 NamespacePermission::default()
124 };
125
126 self.register_token(&token, namespaces, permissions);
127 }
128 }
129 }
130 }
131 }
132}