rootasrole_core/
api.rs

1use std::sync::Mutex;
2
3use capctl::CapSet;
4
5#[cfg(feature = "finder")]
6use log::debug;
7#[cfg(feature = "finder")]
8use serde_json::Value;
9use strum::EnumIs;
10
11#[cfg(feature = "finder")]
12use crate::database::finder::{ActorMatchMin, Cred, ExecSettings, TaskMatch};
13use crate::database::FilterMatcher;
14
15use crate::database::{
16    actor::SActor,
17    structs::{SConfig, SRole, STask},
18};
19use once_cell::sync::Lazy;
20static API: Lazy<Mutex<PluginManager>> = Lazy::new(|| Mutex::new(PluginManager::new()));
21
22// Define a trait for the plugin
23pub trait Plugin {
24    fn initialize(&self);
25    fn cleanup(&self);
26}
27
28#[derive(Debug, PartialEq, Eq, EnumIs)]
29pub enum PluginResultAction {
30    Override, // The result of this plugin ends the algorithm to return the plugin result
31    Edit,     // The result of this plugin modify the result, algorithm continues
32    Ignore,   // The result of this plugin is ignored, algorithm continues
33}
34
35#[derive(Debug, PartialEq, Eq, EnumIs)]
36pub enum PluginResult {
37    Deny,    // The plugin denies the action
38    Neutral, // The plugin has no opinion on the action
39}
40
41pub type ConfigLoaded = fn(config: &SConfig);
42
43#[cfg(feature = "finder")]
44pub type RoleMatcher = fn(
45    role: &SRole,
46    user: &Cred,
47    filter: &Option<FilterMatcher>,
48    command: &[String],
49    matcher: &mut TaskMatch,
50) -> PluginResultAction;
51#[cfg(feature = "finder")]
52pub type TaskMatcher = fn(
53    task: &STask,
54    user: &Cred,
55    command: &[String],
56    matcher: &mut TaskMatch,
57) -> PluginResultAction;
58#[cfg(feature = "finder")]
59pub type UserMatcher = fn(role: &SRole, user: &Cred, user_struct: &Value) -> ActorMatchMin;
60
61pub type RoleInformation = fn(role: &SRole) -> Option<String>;
62pub type ActorInformation = fn(actor: &SActor) -> Option<String>;
63pub type TaskInformation = fn(task: &STask) -> Option<String>;
64
65#[cfg(feature = "finder")]
66pub type DutySeparation = fn(role: &SRole, actor: &Cred) -> PluginResult;
67#[cfg(feature = "finder")]
68pub type TaskSeparation = fn(task: &STask, actor: &Cred) -> PluginResult;
69
70pub type CapsFilter = fn(task: &STask, capabilities: &mut CapSet) -> PluginResultAction;
71#[cfg(feature = "finder")]
72pub type ExecutionChecker = fn(user: &Cred, exec: &mut ExecSettings) -> PluginResult;
73
74pub type ComplexCommandParser =
75    fn(command: &serde_json::Value) -> Result<Vec<String>, Box<dyn std::error::Error>>;
76
77macro_rules! plugin_subscribe {
78    ($plugin:ident, $plugin_type:ident, $plugin_function:ident) => {
79        let mut api = API.lock().unwrap();
80        api.$plugin.push($plugin_function);
81    };
82}
83
84// Define a struct to hold the plugins
85pub struct PluginManager {
86    #[cfg(feature = "finder")]
87    role_matcher_plugins: Vec<RoleMatcher>,
88    #[cfg(feature = "finder")]
89    task_matcher_plugins: Vec<TaskMatcher>,
90    #[cfg(feature = "finder")]
91    user_matcher_plugins: Vec<UserMatcher>,
92    #[cfg(feature = "finder")]
93    duty_separation_plugins: Vec<DutySeparation>,
94    #[cfg(feature = "finder")]
95    task_separation_plugins: Vec<TaskSeparation>,
96    caps_filter_plugins: Vec<CapsFilter>,
97    #[cfg(feature = "finder")]
98    execution_checker_plugins: Vec<ExecutionChecker>,
99    complex_command_parsers: Vec<ComplexCommandParser>,
100}
101
102impl Default for PluginManager {
103    fn default() -> Self {
104        Self::new()
105    }
106}
107
108impl PluginManager {
109    pub fn new() -> Self {
110        PluginManager {
111            #[cfg(feature = "finder")]
112            role_matcher_plugins: Vec::new(),
113            #[cfg(feature = "finder")]
114            task_matcher_plugins: Vec::new(),
115            #[cfg(feature = "finder")]
116            user_matcher_plugins: Vec::new(),
117            #[cfg(feature = "finder")]
118            duty_separation_plugins: Vec::new(),
119            #[cfg(feature = "finder")]
120            task_separation_plugins: Vec::new(),
121            caps_filter_plugins: Vec::new(),
122            #[cfg(feature = "finder")]
123            execution_checker_plugins: Vec::new(),
124            complex_command_parsers: Vec::new(),
125        }
126    }
127
128    #[cfg(feature = "finder")]
129    pub fn subscribe_role_matcher(plugin: RoleMatcher) {
130        plugin_subscribe!(role_matcher_plugins, RoleMatcher, plugin);
131    }
132
133    #[cfg(feature = "finder")]
134    pub fn subscribe_task_matcher(plugin: TaskMatcher) {
135        plugin_subscribe!(task_matcher_plugins, TaskMatcher, plugin);
136    }
137
138    #[cfg(feature = "finder")]
139    pub fn subscribe_user_matcher(plugin: UserMatcher) {
140        plugin_subscribe!(user_matcher_plugins, UserMatcher, plugin);
141    }
142
143    #[cfg(feature = "finder")]
144    pub fn subscribe_duty_separation(plugin: DutySeparation) {
145        plugin_subscribe!(duty_separation_plugins, DutySeparation, plugin);
146    }
147
148    #[cfg(feature = "finder")]
149    pub fn subscribe_task_separation(plugin: TaskSeparation) {
150        plugin_subscribe!(task_separation_plugins, TaskSeparation, plugin);
151    }
152
153    pub fn subscribe_caps_filter(plugin: CapsFilter) {
154        plugin_subscribe!(caps_filter_plugins, CapsFilter, plugin);
155    }
156
157    #[cfg(feature = "finder")]
158    pub fn subscribe_privilege_checker(plugin: ExecutionChecker) {
159        plugin_subscribe!(execution_checker_plugins, ExecutionChecker, plugin);
160    }
161
162    pub fn subscribe_complex_command_parser(plugin: ComplexCommandParser) {
163        plugin_subscribe!(complex_command_parsers, ComplexCommandParser, plugin);
164    }
165
166    #[cfg(feature = "finder")]
167    pub fn notify_role_matcher(
168        role: &SRole,
169        user: &Cred,
170        filter: &Option<FilterMatcher>,
171        command: &[String],
172        matcher: &mut TaskMatch,
173    ) -> PluginResultAction {
174        debug!("Notifying role matchers");
175        let api = API.lock().unwrap();
176        let mut result = PluginResultAction::Ignore;
177        for plugin in api.role_matcher_plugins.iter() {
178            debug!("Calling role matcher plugin");
179            match plugin(role, user, filter, command, matcher) {
180                PluginResultAction::Override => return PluginResultAction::Override,
181                PluginResultAction::Edit => result = PluginResultAction::Edit,
182                PluginResultAction::Ignore => continue,
183            }
184        }
185        result
186    }
187
188    #[cfg(feature = "finder")]
189    pub fn notify_task_matcher(
190        task: &STask,
191        user: &Cred,
192        command: &[String],
193        matcher: &mut TaskMatch,
194    ) -> PluginResultAction {
195        let api = API.lock().unwrap();
196        for plugin in api.task_matcher_plugins.iter() {
197            match plugin(task, user, command, matcher) {
198                PluginResultAction::Override => return PluginResultAction::Override,
199                PluginResultAction::Edit => continue,
200                PluginResultAction::Ignore => continue,
201            }
202        }
203        PluginResultAction::Ignore
204    }
205
206    #[cfg(feature = "finder")]
207    pub fn notify_user_matcher(role: &SRole, user: &Cred, user_struct: &Value) -> ActorMatchMin {
208        let api = API.lock().unwrap();
209        for plugin in api.user_matcher_plugins.iter() {
210            let res = plugin(role, user, user_struct);
211            if !res.is_no_match() {
212                return res;
213            }
214        }
215        ActorMatchMin::NoMatch
216    }
217
218    #[cfg(feature = "finder")]
219    pub fn notify_duty_separation(role: &SRole, actor: &Cred) -> PluginResult {
220        let api = API.lock().unwrap();
221        for plugin in api.duty_separation_plugins.iter() {
222            match plugin(role, actor) {
223                PluginResult::Deny => return PluginResult::Deny,
224                PluginResult::Neutral => continue,
225            }
226        }
227        PluginResult::Neutral
228    }
229
230    #[cfg(feature = "finder")]
231    pub fn notify_task_separation(task: &STask, actor: &Cred) -> PluginResult {
232        let api = API.lock().unwrap();
233        for plugin in api.task_separation_plugins.iter() {
234            match plugin(task, actor) {
235                PluginResult::Deny => return PluginResult::Deny,
236                PluginResult::Neutral => continue,
237            }
238        }
239        PluginResult::Neutral
240    }
241
242    pub fn notify_caps_filter(task: &STask, capabilities: &mut CapSet) -> PluginResultAction {
243        let api = API.lock().unwrap();
244        for plugin in api.caps_filter_plugins.iter() {
245            match plugin(task, capabilities) {
246                PluginResultAction::Override => return PluginResultAction::Override,
247                PluginResultAction::Edit => continue,
248                PluginResultAction::Ignore => continue,
249            }
250        }
251        PluginResultAction::Ignore
252    }
253
254    #[cfg(feature = "finder")]
255    pub fn notify_privilege_checker(user: &Cred, exec: &mut ExecSettings) -> PluginResult {
256        let api = API.lock().unwrap();
257        for plugin in api.execution_checker_plugins.iter() {
258            match plugin(user, exec) {
259                PluginResult::Deny => return PluginResult::Deny,
260                PluginResult::Neutral => continue,
261            }
262        }
263        PluginResult::Neutral
264    }
265
266    pub fn notify_complex_command_parser(
267        command: &serde_json::Value,
268    ) -> Result<Vec<String>, Box<dyn std::error::Error>> {
269        let api = API.lock().unwrap();
270        for plugin in api.complex_command_parsers.iter() {
271            match plugin(command) {
272                Ok(result) => return Ok(result),
273                Err(_e) => {
274                    //debug!("Error parsing command {:?}", e);
275                    continue;
276                }
277            }
278        }
279        Err("No complex command parser found".into())
280    }
281}