Skip to main content

a3s_code_core/permissions/
manager.rs

1use std::collections::HashMap;
2
3use super::{PermissionDecision, PermissionPolicy};
4
5/// Matching rules for debugging
6#[derive(Debug, Default, Clone)]
7pub struct MatchingRules {
8    pub deny: Vec<String>,
9    pub allow: Vec<String>,
10    pub ask: Vec<String>,
11}
12
13impl MatchingRules {
14    pub fn is_empty(&self) -> bool {
15        self.deny.is_empty() && self.allow.is_empty() && self.ask.is_empty()
16    }
17}
18
19/// Permission manager that handles per-session permissions
20#[derive(Debug)]
21pub struct PermissionManager {
22    /// Global policy applied to all sessions
23    global_policy: PermissionPolicy,
24    /// Per-session policy overrides
25    session_policies: HashMap<String, PermissionPolicy>,
26}
27
28impl Default for PermissionManager {
29    fn default() -> Self {
30        Self::new()
31    }
32}
33
34impl PermissionManager {
35    /// Create a new permission manager with default global policy
36    pub fn new() -> Self {
37        Self {
38            global_policy: PermissionPolicy::default(),
39            session_policies: HashMap::new(),
40        }
41    }
42
43    /// Create with a custom global policy
44    pub fn with_global_policy(policy: PermissionPolicy) -> Self {
45        Self {
46            global_policy: policy,
47            session_policies: HashMap::new(),
48        }
49    }
50
51    /// Set the global policy
52    pub fn set_global_policy(&mut self, policy: PermissionPolicy) {
53        self.global_policy = policy;
54    }
55
56    /// Get the global policy
57    pub fn global_policy(&self) -> &PermissionPolicy {
58        &self.global_policy
59    }
60
61    /// Set a session-specific policy
62    pub fn set_session_policy(&mut self, session_id: &str, policy: PermissionPolicy) {
63        self.session_policies.insert(session_id.to_string(), policy);
64    }
65
66    /// Remove a session-specific policy
67    pub fn remove_session_policy(&mut self, session_id: &str) {
68        self.session_policies.remove(session_id);
69    }
70
71    /// Get the effective policy for a session
72    ///
73    /// Session policy takes precedence over global policy for matching rules.
74    /// If no session policy exists, uses global policy.
75    pub fn get_effective_policy(&self, session_id: &str) -> &PermissionPolicy {
76        self.session_policies
77            .get(session_id)
78            .unwrap_or(&self.global_policy)
79    }
80
81    /// Check permission for a tool invocation in a session
82    pub fn check(
83        &self,
84        session_id: &str,
85        tool_name: &str,
86        args: &serde_json::Value,
87    ) -> PermissionDecision {
88        // Get session policy or fall back to global
89        let policy = self.get_effective_policy(session_id);
90
91        // Session deny rules
92        for rule in &policy.deny {
93            if rule.matches(tool_name, args) {
94                return PermissionDecision::Deny;
95            }
96        }
97
98        // Global deny rules (if different policy)
99        if !self.session_policies.contains_key(session_id) {
100            // Already checked global
101        } else {
102            for rule in &self.global_policy.deny {
103                if rule.matches(tool_name, args) {
104                    return PermissionDecision::Deny;
105                }
106            }
107        }
108
109        // Session allow rules
110        for rule in &policy.allow {
111            if rule.matches(tool_name, args) {
112                return PermissionDecision::Allow;
113            }
114        }
115
116        // Session ask rules
117        for rule in &policy.ask {
118            if rule.matches(tool_name, args) {
119                return PermissionDecision::Ask;
120            }
121        }
122
123        // Fall back to policy default
124        policy.default_decision
125    }
126}