sen_plugin_host/permission/
trust.rs1#[derive(Debug, Clone)]
25pub struct TrustFlagConfig {
26 pub enabled: bool,
28
29 pub flag_template: String,
32
33 pub value_template: String,
36
37 pub aliases: Vec<TrustFlagAlias>,
39
40 pub show_in_help: bool,
42
43 pub help_template: String,
45}
46
47impl Default for TrustFlagConfig {
48 fn default() -> Self {
49 Self {
50 enabled: true,
51 flag_template: "--trust-{target}".into(),
52 value_template: "{name}".into(),
53 aliases: vec![],
54 show_in_help: true,
55 help_template: "Trust {target} '{name}' for this execution".into(),
56 }
57 }
58}
59
60impl TrustFlagConfig {
61 pub fn new() -> Self {
63 Self::default()
64 }
65
66 pub fn disabled() -> Self {
68 Self {
69 enabled: false,
70 ..Self::default()
71 }
72 }
73
74 pub fn with_flag_template(mut self, template: impl Into<String>) -> Self {
79 self.flag_template = template.into();
80 self
81 }
82
83 pub fn with_value_template(mut self, template: impl Into<String>) -> Self {
88 self.value_template = template.into();
89 self
90 }
91
92 pub fn with_alias(mut self, flag: impl Into<String>, effect: TrustEffect) -> Self {
94 self.aliases.push(TrustFlagAlias {
95 flag: flag.into(),
96 description: effect.default_description(),
97 effect,
98 });
99 self
100 }
101
102 pub fn with_alias_desc(
104 mut self,
105 flag: impl Into<String>,
106 description: impl Into<String>,
107 effect: TrustEffect,
108 ) -> Self {
109 self.aliases.push(TrustFlagAlias {
110 flag: flag.into(),
111 description: description.into(),
112 effect,
113 });
114 self
115 }
116
117 pub fn hidden(mut self) -> Self {
119 self.show_in_help = false;
120 self
121 }
122
123 pub fn generate_flag(&self, target: TrustTarget) -> String {
125 self.flag_template.replace("{target}", target.as_str())
126 }
127
128 pub fn generate_value(&self, name: &str) -> String {
130 self.value_template.replace("{name}", name)
131 }
132
133 pub fn generate_help(&self, target: TrustTarget, name: &str) -> String {
135 self.help_template
136 .replace("{target}", target.as_str())
137 .replace("{name}", name)
138 }
139
140 pub fn parse_args(&self, args: &[String]) -> TrustDirectives {
142 if !self.enabled {
143 return TrustDirectives::default();
144 }
145
146 let mut directives = TrustDirectives::default();
147
148 for arg in args {
149 for alias in &self.aliases {
151 if arg == &alias.flag {
152 match &alias.effect {
153 TrustEffect::TrustAll => directives.trust_all = true,
154 TrustEffect::TrustSession => directives.trust_session = true,
155 TrustEffect::TrustNamed { target, name } => match target {
156 TrustTarget::Plugin => {
157 directives.trusted_plugins.push(name.clone());
158 }
159 TrustTarget::Command => {
160 directives.trusted_commands.push(name.clone());
161 }
162 },
163 }
164 }
165 }
166
167 let plugin_flag = self.generate_flag(TrustTarget::Plugin);
169 let command_flag = self.generate_flag(TrustTarget::Command);
170
171 if let Some(value) = arg.strip_prefix(&format!("{}=", plugin_flag)) {
172 directives.trusted_plugins.push(value.to_string());
173 } else if let Some(value) = arg.strip_prefix(&format!("{}=", command_flag)) {
174 directives.trusted_commands.push(value.to_string());
175 }
176 }
177
178 directives
179 }
180}
181
182#[derive(Debug, Clone, Copy, PartialEq, Eq)]
184pub enum TrustTarget {
185 Plugin,
186 Command,
187}
188
189impl TrustTarget {
190 fn as_str(&self) -> &'static str {
191 match self {
192 Self::Plugin => "plugin",
193 Self::Command => "command",
194 }
195 }
196}
197
198#[derive(Debug, Clone)]
200pub struct TrustFlagAlias {
201 pub flag: String,
203 pub description: String,
205 pub effect: TrustEffect,
207}
208
209#[derive(Debug, Clone, PartialEq, Eq)]
211pub enum TrustEffect {
212 TrustNamed { target: TrustTarget, name: String },
214 TrustAll,
216 TrustSession,
218}
219
220impl TrustEffect {
221 fn default_description(&self) -> String {
223 match self {
224 Self::TrustNamed { target, name } => {
225 format!("Trust {} '{}'", target.as_str(), name)
226 }
227 Self::TrustAll => "Trust all plugins (dangerous)".into(),
228 Self::TrustSession => "Trust permissions for this session only".into(),
229 }
230 }
231}
232
233#[derive(Debug, Clone, Default)]
235pub struct TrustDirectives {
236 pub trusted_plugins: Vec<String>,
238 pub trusted_commands: Vec<String>,
240 pub trust_all: bool,
242 pub trust_session: bool,
244}
245
246impl TrustDirectives {
247 pub fn is_plugin_trusted(&self, name: &str) -> bool {
249 self.trust_all || self.trusted_plugins.iter().any(|p| p == name)
250 }
251
252 pub fn is_command_trusted(&self, name: &str) -> bool {
254 self.trust_all || self.trusted_commands.iter().any(|c| c == name)
255 }
256
257 pub fn has_any(&self) -> bool {
259 self.trust_all
260 || self.trust_session
261 || !self.trusted_plugins.is_empty()
262 || !self.trusted_commands.is_empty()
263 }
264}
265
266pub struct TrustFlagPresets;
268
269impl TrustFlagPresets {
270 pub fn standard() -> TrustFlagConfig {
272 TrustFlagConfig::default()
273 }
274
275 pub fn allow_style() -> TrustFlagConfig {
277 TrustFlagConfig::new().with_flag_template("--allow-{target}")
278 }
279
280 pub fn short_style() -> TrustFlagConfig {
282 TrustFlagConfig::new()
283 .with_flag_template("-t{target}")
284 .with_alias("-ta", TrustEffect::TrustAll)
285 }
286
287 pub fn k8s_style() -> TrustFlagConfig {
289 TrustFlagConfig::new()
290 .with_flag_template("--trust")
291 .with_value_template("{target}={name}")
292 }
293
294 pub fn disabled() -> TrustFlagConfig {
296 TrustFlagConfig::disabled()
297 }
298}
299
300#[cfg(test)]
301mod tests {
302 use super::*;
303
304 #[test]
305 fn test_default_config() {
306 let config = TrustFlagConfig::default();
307 assert!(config.enabled);
308 assert_eq!(config.generate_flag(TrustTarget::Plugin), "--trust-plugin");
309 assert_eq!(
310 config.generate_flag(TrustTarget::Command),
311 "--trust-command"
312 );
313 }
314
315 #[test]
316 fn test_custom_template() {
317 let config = TrustFlagConfig::new().with_flag_template("--allow-{target}");
318
319 assert_eq!(config.generate_flag(TrustTarget::Plugin), "--allow-plugin");
320 }
321
322 #[test]
323 fn test_parse_args() {
324 let config = TrustFlagConfig::default();
325 let args = vec![
326 "--trust-plugin=hello".into(),
327 "--trust-command=db:migrate".into(),
328 "other-arg".into(),
329 ];
330
331 let directives = config.parse_args(&args);
332 assert!(directives.is_plugin_trusted("hello"));
333 assert!(directives.is_command_trusted("db:migrate"));
334 assert!(!directives.is_plugin_trusted("other"));
335 }
336
337 #[test]
338 fn test_alias() {
339 let config = TrustFlagConfig::new().with_alias("--yolo", TrustEffect::TrustAll);
340
341 let args = vec!["--yolo".into()];
342 let directives = config.parse_args(&args);
343
344 assert!(directives.trust_all);
345 assert!(directives.is_plugin_trusted("any-plugin"));
346 }
347
348 #[test]
349 fn test_disabled_config() {
350 let config = TrustFlagConfig::disabled();
351 let args = vec!["--trust-plugin=hello".into()];
352 let directives = config.parse_args(&args);
353
354 assert!(!directives.is_plugin_trusted("hello"));
355 }
356
357 #[test]
358 fn test_presets() {
359 let allow = TrustFlagPresets::allow_style();
360 assert_eq!(allow.generate_flag(TrustTarget::Plugin), "--allow-plugin");
361
362 let short = TrustFlagPresets::short_style();
363 assert_eq!(short.generate_flag(TrustTarget::Plugin), "-tplugin");
364 }
365}