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
22pub trait Plugin {
24 fn initialize(&self);
25 fn cleanup(&self);
26}
27
28#[derive(Debug, PartialEq, Eq, EnumIs)]
29pub enum PluginResultAction {
30 Override, Edit, Ignore, }
34
35#[derive(Debug, PartialEq, Eq, EnumIs)]
36pub enum PluginResult {
37 Deny, Neutral, }
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
84pub 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 continue;
276 }
277 }
278 }
279 Err("No complex command parser found".into())
280 }
281}