ash_core/config/
scope_policies.rs1use regex::Regex;
25use std::collections::HashMap;
26use std::sync::RwLock;
27
28#[derive(Debug, Default)]
33pub struct ScopePolicyRegistry {
34 policies: HashMap<String, Vec<String>>,
35}
36
37impl ScopePolicyRegistry {
38 pub fn new() -> Self {
40 Self {
41 policies: HashMap::new(),
42 }
43 }
44
45 pub fn register(&mut self, binding: &str, fields: &[&str]) {
47 self.policies.insert(
48 binding.to_string(),
49 fields.iter().map(|s| s.to_string()).collect(),
50 );
51 }
52
53 pub fn register_many(&mut self, policies_map: &HashMap<&str, Vec<&str>>) {
55 for (binding, fields) in policies_map {
56 self.policies.insert(
57 binding.to_string(),
58 fields.iter().map(|s| s.to_string()).collect(),
59 );
60 }
61 }
62
63 pub fn get(&self, binding: &str) -> Vec<String> {
65 if let Some(fields) = self.policies.get(binding) {
67 return fields.clone();
68 }
69
70 for (pattern, fields) in self.policies.iter() {
72 if matches_pattern(binding, pattern) {
73 return fields.clone();
74 }
75 }
76
77 Vec::new()
79 }
80
81 pub fn has(&self, binding: &str) -> bool {
83 if self.policies.contains_key(binding) {
84 return true;
85 }
86
87 for pattern in self.policies.keys() {
88 if matches_pattern(binding, pattern) {
89 return true;
90 }
91 }
92
93 false
94 }
95
96 pub fn get_all(&self) -> HashMap<String, Vec<String>> {
98 self.policies.clone()
99 }
100
101 pub fn clear(&mut self) {
103 self.policies.clear();
104 }
105}
106
107lazy_static::lazy_static! {
108 static ref GLOBAL_REGISTRY: RwLock<ScopePolicyRegistry> = RwLock::new(ScopePolicyRegistry::new());
110}
111
112pub fn register_scope_policy(binding: &str, fields: &[&str]) {
129 let mut registry = GLOBAL_REGISTRY.write().unwrap();
130 registry.register(binding, fields);
131}
132
133pub fn register_scope_policies(policies_map: &HashMap<&str, Vec<&str>>) {
152 let mut registry = GLOBAL_REGISTRY.write().unwrap();
153 registry.register_many(policies_map);
154}
155
156pub fn get_scope_policy(binding: &str) -> Vec<String> {
183 let registry = GLOBAL_REGISTRY.read().unwrap();
184 registry.get(binding)
185}
186
187pub fn has_scope_policy(binding: &str) -> bool {
197 let registry = GLOBAL_REGISTRY.read().unwrap();
198 registry.has(binding)
199}
200
201pub fn get_all_scope_policies() -> HashMap<String, Vec<String>> {
207 let registry = GLOBAL_REGISTRY.read().unwrap();
208 registry.get_all()
209}
210
211pub fn clear_scope_policies() {
215 let mut registry = GLOBAL_REGISTRY.write().unwrap();
216 registry.clear();
217}
218
219fn matches_pattern(binding: &str, pattern: &str) -> bool {
228 if !pattern.contains('*')
230 && !pattern.contains('<')
231 && !pattern.contains(':')
232 && !pattern.contains('{')
233 {
234 return binding == pattern;
235 }
236
237 let mut regex_str = regex::escape(pattern);
239
240 regex_str = regex_str.replace(r"\*\*", ".*");
242
243 regex_str = regex_str.replace(r"\*", "[^|/]*");
245
246 let flask_re = Regex::new(r"<[a-zA-Z_][a-zA-Z0-9_]*>").unwrap();
249 regex_str = flask_re.replace_all(®ex_str, "[^|/]+").to_string();
250
251 let express_re = Regex::new(r":[a-zA-Z_][a-zA-Z0-9_]*").unwrap();
253 regex_str = express_re.replace_all(®ex_str, "[^|/]+").to_string();
254
255 let laravel_re = Regex::new(r"\\[{][a-zA-Z_][a-zA-Z0-9_]*\\[}]").unwrap();
259 regex_str = laravel_re.replace_all(®ex_str, "[^|/]+").to_string();
260
261 if let Ok(re) = Regex::new(&format!("^{}$", regex_str)) {
263 re.is_match(binding)
264 } else {
265 false
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272
273 #[test]
276 fn test_registry_register_and_get() {
277 let mut registry = ScopePolicyRegistry::new();
278 registry.register("POST|/api/transfer|", &["amount", "recipient"]);
279
280 let scope = registry.get("POST|/api/transfer|");
281 assert_eq!(scope, vec!["amount", "recipient"]);
282 }
283
284 #[test]
285 fn test_registry_get_no_match() {
286 let registry = ScopePolicyRegistry::new();
287
288 let scope = registry.get("GET|/api/users|");
289 assert!(scope.is_empty());
290 }
291
292 #[test]
293 fn test_registry_has() {
294 let mut registry = ScopePolicyRegistry::new();
295 registry.register("POST|/api/transfer|", &["amount"]);
296
297 assert!(registry.has("POST|/api/transfer|"));
298 assert!(!registry.has("GET|/api/users|"));
299 }
300
301 #[test]
302 fn test_registry_pattern_matching_flask_style() {
303 let mut registry = ScopePolicyRegistry::new();
304 registry.register("PUT|/api/users/<id>|", &["role", "permissions"]);
305
306 let scope = registry.get("PUT|/api/users/123|");
307 assert_eq!(scope, vec!["role", "permissions"]);
308 }
309
310 #[test]
311 fn test_registry_pattern_matching_express_style() {
312 let mut registry = ScopePolicyRegistry::new();
313 registry.register("PUT|/api/users/:id|", &["role"]);
314
315 let scope = registry.get("PUT|/api/users/456|");
316 assert_eq!(scope, vec!["role"]);
317 }
318
319 #[test]
320 fn test_registry_pattern_matching_laravel_style() {
321 let mut registry = ScopePolicyRegistry::new();
322 registry.register("PUT|/api/users/{id}|", &["email"]);
323
324 let scope = registry.get("PUT|/api/users/789|");
325 assert_eq!(scope, vec!["email"]);
326 }
327
328 #[test]
329 fn test_registry_pattern_matching_wildcard() {
330 let mut registry = ScopePolicyRegistry::new();
331 registry.register("POST|/api/*/transfer|", &["amount"]);
332
333 let scope = registry.get("POST|/api/v1/transfer|");
334 assert_eq!(scope, vec!["amount"]);
335 }
336
337 #[test]
338 fn test_registry_pattern_matching_double_wildcard() {
339 let mut registry = ScopePolicyRegistry::new();
340 registry.register("POST|/api/**/transfer|", &["amount"]);
341
342 let scope = registry.get("POST|/api/v1/users/transfer|");
343 assert_eq!(scope, vec!["amount"]);
344 }
345
346 #[test]
347 fn test_registry_clear() {
348 let mut registry = ScopePolicyRegistry::new();
349 registry.register("POST|/api/transfer|", &["amount"]);
350
351 assert!(registry.has("POST|/api/transfer|"));
352
353 registry.clear();
354
355 assert!(!registry.has("POST|/api/transfer|"));
356 }
357
358 #[test]
359 fn test_registry_register_many() {
360 let mut registry = ScopePolicyRegistry::new();
361 let mut policies = HashMap::new();
362 policies.insert("POST|/api/transfer|", vec!["amount"]);
363 policies.insert("POST|/api/payment|", vec!["card"]);
364
365 registry.register_many(&policies);
366
367 assert!(registry.has("POST|/api/transfer|"));
368 assert!(registry.has("POST|/api/payment|"));
369 }
370
371 #[test]
372 fn test_registry_get_all() {
373 let mut registry = ScopePolicyRegistry::new();
374 registry.register("POST|/api/transfer|", &["amount"]);
375 registry.register("POST|/api/payment|", &["card"]);
376
377 let all = registry.get_all();
378 assert_eq!(all.len(), 2);
379 }
380}