rootasrole_core/database/
structs.rs

1use bon::{bon, builder, Builder};
2use capctl::{Cap, CapSet};
3use derivative::Derivative;
4use serde::{
5    de::{self, MapAccess, SeqAccess, Visitor},
6    ser::SerializeMap,
7    Deserialize, Deserializer, Serialize,
8};
9use serde_json::{Map, Value};
10use strum::{Display, EnumIs};
11
12use std::{
13    cell::RefCell,
14    error::Error,
15    fmt,
16    ops::{Index, Not},
17    rc::{Rc, Weak},
18};
19
20use super::{
21    actor::{SActor, SGroups, SUserType},
22    is_default,
23    options::{Level, Opt, OptBuilder},
24};
25
26#[derive(Deserialize, Serialize, PartialEq, Eq, Debug)]
27pub struct SConfig {
28    #[serde(
29        default,
30        skip_serializing_if = "Option::is_none",
31        deserialize_with = "sconfig_opt"
32    )]
33    pub options: Option<Rc<RefCell<Opt>>>,
34    #[serde(default, skip_serializing_if = "Vec::is_empty")]
35    pub roles: Vec<Rc<RefCell<SRole>>>,
36    #[serde(default)]
37    #[serde(flatten, skip_serializing_if = "Map::is_empty")]
38    pub _extra_fields: Map<String, Value>,
39}
40
41fn sconfig_opt<'de, D>(deserializer: D) -> Result<Option<Rc<RefCell<Opt>>>, D::Error>
42where
43    D: Deserializer<'de>,
44{
45    let mut opt = Opt::deserialize(deserializer)?;
46    opt.level = Level::Global;
47    Ok(Some(Rc::new(RefCell::new(opt))))
48}
49
50#[derive(Serialize, Deserialize, Debug, Derivative)]
51#[serde(rename_all = "kebab-case")]
52#[derivative(PartialEq, Eq)]
53pub struct SRole {
54    pub name: String,
55    #[serde(default, skip_serializing_if = "Vec::is_empty")]
56    pub actors: Vec<SActor>,
57    #[serde(default, skip_serializing_if = "Vec::is_empty")]
58    pub tasks: Vec<Rc<RefCell<STask>>>,
59    #[serde(
60        default,
61        skip_serializing_if = "Option::is_none",
62        deserialize_with = "srole_opt"
63    )]
64    pub options: Option<Rc<RefCell<Opt>>>,
65    #[serde(default, flatten, skip_serializing_if = "Map::is_empty")]
66    pub _extra_fields: Map<String, Value>,
67    #[serde(skip)]
68    #[derivative(PartialEq = "ignore")]
69    pub _config: Option<Weak<RefCell<SConfig>>>,
70}
71
72fn srole_opt<'de, D>(deserializer: D) -> Result<Option<Rc<RefCell<Opt>>>, D::Error>
73where
74    D: Deserializer<'de>,
75{
76    let mut opt = Opt::deserialize(deserializer)?;
77    opt.level = Level::Role;
78    Ok(Some(Rc::new(RefCell::new(opt))))
79}
80
81#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, EnumIs)]
82#[serde(untagged)]
83pub enum IdTask {
84    Name(String),
85    Number(usize),
86}
87
88impl std::fmt::Display for IdTask {
89    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
90        match self {
91            IdTask::Name(name) => write!(f, "{}", name),
92            IdTask::Number(id) => write!(f, "{}", id),
93        }
94    }
95}
96
97#[derive(Serialize, Deserialize, Debug, Derivative)]
98#[derivative(PartialEq, Eq)]
99pub struct STask {
100    #[serde(default, skip_serializing_if = "IdTask::is_number")]
101    pub name: IdTask,
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub purpose: Option<String>,
104    #[serde(default, skip_serializing_if = "is_default")]
105    pub cred: SCredentials,
106    #[serde(default, skip_serializing_if = "is_default")]
107    pub commands: SCommands,
108    #[serde(
109        default,
110        skip_serializing_if = "Option::is_none",
111        deserialize_with = "stask_opt"
112    )]
113    pub options: Option<Rc<RefCell<Opt>>>,
114    #[serde(default, flatten, skip_serializing_if = "Map::is_empty")]
115    pub _extra_fields: Map<String, Value>,
116    #[serde(skip)]
117    #[derivative(PartialEq = "ignore")]
118    pub _role: Option<Weak<RefCell<SRole>>>,
119}
120
121fn stask_opt<'de, D>(deserializer: D) -> Result<Option<Rc<RefCell<Opt>>>, D::Error>
122where
123    D: Deserializer<'de>,
124{
125    let mut opt = Opt::deserialize(deserializer)?;
126    opt.level = Level::Task;
127    Ok(Some(Rc::new(RefCell::new(opt))))
128}
129
130#[derive(Serialize, Deserialize, Debug, Builder, PartialEq, Eq)]
131#[serde(rename_all = "kebab-case")]
132pub struct SCredentials {
133    #[serde(skip_serializing_if = "Option::is_none")]
134    #[builder(into)]
135    pub setuid: Option<SUserChooser>,
136    #[serde(skip_serializing_if = "Option::is_none")]
137    #[builder(into)]
138    pub setgid: Option<SGroups>,
139    #[serde(default, skip_serializing_if = "Option::is_none")]
140    pub capabilities: Option<SCapabilities>,
141    #[serde(default, skip_serializing_if = "Option::is_none")]
142    #[builder(into)]
143    pub additional_auth: Option<String>, // TODO: to extract as plugin
144    #[serde(default, flatten, skip_serializing_if = "Map::is_empty")]
145    #[builder(default)]
146    pub _extra_fields: Map<String, Value>,
147}
148
149#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
150#[serde(untagged)]
151pub enum SUserChooser {
152    Actor(SUserType),
153    ChooserStruct(SSetuidSet),
154}
155
156impl From<SUserType> for SUserChooser {
157    fn from(actor: SUserType) -> Self {
158        SUserChooser::Actor(actor)
159    }
160}
161
162impl From<SSetuidSet> for SUserChooser {
163    fn from(set: SSetuidSet) -> Self {
164        SUserChooser::ChooserStruct(set)
165    }
166}
167
168impl From<&str> for SUserChooser {
169    fn from(name: &str) -> Self {
170        SUserChooser::Actor(name.into())
171    }
172}
173
174impl From<u32> for SUserChooser {
175    fn from(id: u32) -> Self {
176        SUserChooser::Actor(id.into())
177    }
178}
179
180#[derive(Serialize, Deserialize, Debug, Clone, Builder, PartialEq, Eq)]
181
182pub struct SSetuidSet {
183    #[builder(start_fn, into)]
184    pub fallback: SUserType,
185    #[serde(rename = "default", default, skip_serializing_if = "is_default")]
186    #[builder(start_fn)]
187    pub default: SetBehavior,
188    #[serde(default, skip_serializing_if = "Vec::is_empty")]
189    #[builder(default, with = FromIterator::from_iter)]
190    pub add: Vec<SUserType>,
191    #[serde(default, skip_serializing_if = "Vec::is_empty")]
192    #[builder(default, with = FromIterator::from_iter)]
193    pub sub: Vec<SUserType>,
194}
195
196#[derive(Serialize, Deserialize, PartialEq, Eq, Display, Debug, EnumIs, Clone)]
197#[serde(rename_all = "lowercase")]
198#[derive(Default)]
199pub enum SetBehavior {
200    All,
201    #[default]
202    None,
203}
204
205#[derive(PartialEq, Eq, Debug, Builder)]
206pub struct SCapabilities {
207    #[builder(start_fn)]
208    pub default_behavior: SetBehavior,
209    #[builder(field)]
210    pub add: CapSet,
211    #[builder(field)]
212    pub sub: CapSet,
213    #[builder(default, with = <_>::from_iter)]
214    pub _extra_fields: Map<String, Value>,
215}
216
217impl<S: s_capabilities_builder::State> SCapabilitiesBuilder<S> {
218    pub fn add_cap(mut self, cap: Cap) -> Self {
219        self.add.add(cap);
220        self
221    }
222    pub fn add_all(mut self, set: CapSet) -> Self {
223        self.add = set;
224        self
225    }
226    pub fn sub_cap(mut self, cap: Cap) -> Self {
227        self.sub.add(cap);
228        self
229    }
230    pub fn sub_all(mut self, set: CapSet) -> Self {
231        self.sub = set;
232        self
233    }
234}
235
236impl Serialize for SCapabilities {
237    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
238    where
239        S: serde::Serializer,
240    {
241        if self.default_behavior.is_none() && self.sub.is_empty() && self._extra_fields.is_empty() {
242            super::serialize_capset(&self.add, serializer)
243        } else {
244            let mut map = serializer.serialize_map(Some(3))?;
245            if self.default_behavior.is_none() {
246                map.serialize_entry("default", &self.default_behavior)?;
247            }
248            if !self.add.is_empty() {
249                let v: Vec<String> = self.add.iter().map(|cap| cap.to_string()).collect();
250                map.serialize_entry("add", &v)?;
251            }
252            if !self.sub.is_empty() {
253                let v: Vec<String> = self.sub.iter().map(|cap| cap.to_string()).collect();
254                map.serialize_entry("del", &v)?;
255            }
256            for (key, value) in &self._extra_fields {
257                map.serialize_entry(key, value)?;
258            }
259            map.end()
260        }
261    }
262}
263impl<'de> Deserialize<'de> for SCapabilities {
264    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
265    where
266        D: Deserializer<'de>,
267    {
268        struct SCapabilitiesVisitor;
269
270        impl<'de> Visitor<'de> for SCapabilitiesVisitor {
271            type Value = SCapabilities;
272
273            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
274                formatter.write_str("an array of strings or a map with SCapabilities fields")
275            }
276
277            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
278            where
279                A: SeqAccess<'de>,
280            {
281                let mut add = CapSet::default();
282                while let Some(cap) = seq.next_element::<String>()? {
283                    add.add(cap.parse().map_err(de::Error::custom)?);
284                }
285
286                Ok(SCapabilities {
287                    default_behavior: SetBehavior::None,
288                    add,
289                    sub: CapSet::default(),
290                    _extra_fields: Map::new(),
291                })
292            }
293
294            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
295            where
296                A: MapAccess<'de>,
297            {
298                let mut default_behavior = SetBehavior::None;
299                let mut add = CapSet::default();
300                let mut sub = CapSet::default();
301                let mut _extra_fields = Map::new();
302
303                while let Some(key) = map.next_key::<String>()? {
304                    match key.as_str() {
305                        "default" => {
306                            default_behavior = map
307                                .next_value()
308                                .expect("default entry must be either 'all' or 'none'");
309                        }
310                        "add" => {
311                            let values: Vec<String> =
312                                map.next_value().expect("add entry must be a list");
313                            for value in values {
314                                add.add(value.parse().map_err(|_| {
315                                    de::Error::custom(format!("Invalid capability: {}", value))
316                                })?);
317                            }
318                        }
319                        "sub" | "del" => {
320                            let values: Vec<String> =
321                                map.next_value().expect("sub entry must be a list");
322                            for value in values {
323                                sub.add(value.parse().map_err(|_| {
324                                    de::Error::custom(format!("Invalid capability: {}", value))
325                                })?);
326                            }
327                        }
328                        other => {
329                            _extra_fields.insert(other.to_string(), map.next_value()?);
330                        }
331                    }
332                }
333
334                Ok(SCapabilities {
335                    default_behavior,
336                    add,
337                    sub,
338                    _extra_fields,
339                })
340            }
341        }
342
343        deserializer.deserialize_any(SCapabilitiesVisitor)
344    }
345}
346
347#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, EnumIs, Clone)]
348#[serde(untagged)]
349pub enum SCommand {
350    Simple(String),
351    Complex(Value),
352}
353
354#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
355pub struct SCommands {
356    #[serde(rename = "default")]
357    pub default_behavior: Option<SetBehavior>,
358    #[serde(default, skip_serializing_if = "Vec::is_empty")]
359    pub add: Vec<SCommand>,
360    #[serde(default, alias = "del", skip_serializing_if = "Vec::is_empty")]
361    pub sub: Vec<SCommand>,
362    #[serde(default, flatten, skip_serializing_if = "Map::is_empty")]
363    pub _extra_fields: Map<String, Value>,
364}
365
366// ------------------------
367// Default implementations
368// ------------------------
369
370impl Default for SConfig {
371    fn default() -> Self {
372        SConfig {
373            options: Some(Rc::new(RefCell::new(Opt::default()))),
374            roles: Vec::new(),
375            _extra_fields: Map::default(),
376        }
377    }
378}
379
380impl Default for SRole {
381    fn default() -> Self {
382        SRole {
383            name: "".to_string(),
384            actors: Vec::new(),
385            tasks: Vec::new(),
386            options: None,
387            _extra_fields: Map::default(),
388            _config: None,
389        }
390    }
391}
392
393impl Default for STask {
394    fn default() -> Self {
395        STask {
396            name: IdTask::Number(0),
397            purpose: None,
398            cred: SCredentials::default(),
399            commands: SCommands::default(),
400            options: None,
401            _extra_fields: Map::default(),
402            _role: None,
403        }
404    }
405}
406
407impl Default for SCredentials {
408    fn default() -> Self {
409        SCredentials {
410            setuid: None,
411            setgid: None,
412            capabilities: Some(SCapabilities::default()),
413            additional_auth: None,
414            _extra_fields: Map::default(),
415        }
416    }
417}
418
419impl Default for SCommands {
420    fn default() -> Self {
421        SCommands {
422            default_behavior: Some(SetBehavior::default()),
423            add: Vec::new(),
424            sub: Vec::new(),
425            _extra_fields: Map::default(),
426        }
427    }
428}
429
430impl Default for SCapabilities {
431    fn default() -> Self {
432        SCapabilities {
433            default_behavior: SetBehavior::default(),
434            add: CapSet::empty(),
435            sub: CapSet::empty(),
436            _extra_fields: Map::default(),
437        }
438    }
439}
440
441impl Default for SSetuidSet {
442    fn default() -> Self {
443        SSetuidSet::builder(0, SetBehavior::None).build()
444    }
445}
446
447impl Default for IdTask {
448    fn default() -> Self {
449        IdTask::Number(0)
450    }
451}
452
453// ------------------------
454// From implementations
455// ------------------------
456
457impl From<usize> for IdTask {
458    fn from(id: usize) -> Self {
459        IdTask::Number(id)
460    }
461}
462
463impl From<String> for IdTask {
464    fn from(name: String) -> Self {
465        IdTask::Name(name)
466    }
467}
468
469impl From<&str> for IdTask {
470    fn from(name: &str) -> Self {
471        IdTask::Name(name.to_string())
472    }
473}
474
475impl From<&str> for SCommand {
476    fn from(name: &str) -> Self {
477        SCommand::Simple(name.to_string())
478    }
479}
480
481impl From<CapSet> for SCapabilities {
482    fn from(capset: CapSet) -> Self {
483        SCapabilities {
484            add: capset,
485            ..Default::default()
486        }
487    }
488}
489
490// ------------------------
491// Deserialize
492// ------------------------
493
494// This try to deserialize a number as an ID and a string as a name
495
496// ========================
497// Implementations for Struct navigation
498// ========================
499#[bon]
500impl SConfig {
501    #[builder]
502    pub fn new(
503        #[builder(field)] roles: Vec<Rc<RefCell<SRole>>>,
504        #[builder(with = |f : fn(OptBuilder) -> Rc<RefCell<Opt>> | f(Opt::builder(Level::Global)))]
505        options: Option<Rc<RefCell<Opt>>>,
506        _extra_fields: Option<Map<String, Value>>,
507    ) -> Rc<RefCell<Self>> {
508        let c = Rc::new(RefCell::new(SConfig {
509            roles: roles.clone(),
510            options: options.clone(),
511            _extra_fields: _extra_fields.unwrap_or_default().clone(),
512        }));
513        for role in &roles {
514            role.borrow_mut()._config = Some(Rc::downgrade(&c));
515        }
516        c
517    }
518}
519
520pub trait RoleGetter {
521    fn role(&self, name: &str) -> Option<Rc<RefCell<SRole>>>;
522    fn task<T: Into<IdTask>>(
523        &self,
524        role: &str,
525        name: T,
526    ) -> Result<Rc<RefCell<STask>>, Box<dyn Error>>;
527}
528
529pub trait TaskGetter {
530    fn task(&self, name: &IdTask) -> Option<Rc<RefCell<STask>>>;
531}
532
533impl RoleGetter for Rc<RefCell<SConfig>> {
534    fn role(&self, name: &str) -> Option<Rc<RefCell<SRole>>> {
535        self.as_ref()
536            .borrow()
537            .roles
538            .iter()
539            .find(|role| role.borrow().name == name)
540            .cloned()
541    }
542    fn task<T: Into<IdTask>>(
543        &self,
544        role: &str,
545        name: T,
546    ) -> Result<Rc<RefCell<STask>>, Box<dyn Error>> {
547        let name = name.into();
548        self.role(role)
549            .and_then(|role| role.as_ref().borrow().task(&name).cloned())
550            .ok_or_else(|| format!("Task {} not found in role {}", name, role).into())
551    }
552}
553
554impl TaskGetter for Rc<RefCell<SRole>> {
555    fn task(&self, name: &IdTask) -> Option<Rc<RefCell<STask>>> {
556        self.as_ref()
557            .borrow()
558            .tasks
559            .iter()
560            .find(|task| task.borrow().name == *name)
561            .cloned()
562    }
563}
564
565impl<S: s_config_builder::State> SConfigBuilder<S> {
566    pub fn role(mut self, role: Rc<RefCell<SRole>>) -> Self {
567        self.roles.push(role);
568        self
569    }
570    pub fn roles(mut self, roles: impl IntoIterator<Item = Rc<RefCell<SRole>>>) -> Self {
571        self.roles.extend(roles);
572        self
573    }
574}
575
576impl<S: s_role_builder::State> SRoleBuilder<S> {
577    pub fn task(mut self, task: Rc<RefCell<STask>>) -> Self {
578        self.tasks.push(task);
579        self
580    }
581    pub fn actor(mut self, actor: SActor) -> Self {
582        self.actors.push(actor);
583        self
584    }
585}
586
587#[bon]
588impl SRole {
589    #[builder]
590    pub fn new(
591        #[builder(start_fn, into)] name: String,
592        #[builder(field)] tasks: Vec<Rc<RefCell<STask>>>,
593        #[builder(field)] actors: Vec<SActor>,
594        #[builder(with = |f : fn(OptBuilder) -> Rc<RefCell<Opt>> | f(Opt::builder(Level::Role)))]
595        options: Option<Rc<RefCell<Opt>>>,
596        #[builder(default)] _extra_fields: Map<String, Value>,
597    ) -> Rc<RefCell<Self>> {
598        let s = Rc::new(RefCell::new(SRole {
599            name,
600            actors,
601            tasks,
602            options,
603            _extra_fields,
604            _config: None,
605        }));
606        for task in s.as_ref().borrow_mut().tasks.iter() {
607            task.borrow_mut()._role = Some(Rc::downgrade(&s));
608        }
609        s
610    }
611    pub fn config(&self) -> Option<Rc<RefCell<SConfig>>> {
612        self._config.as_ref()?.upgrade()
613    }
614    pub fn task(&self, name: &IdTask) -> Option<&Rc<RefCell<STask>>> {
615        self.tasks
616            .iter()
617            .find(|task| task.as_ref().borrow().name == *name)
618    }
619}
620
621#[bon]
622impl STask {
623    #[builder]
624    pub fn new(
625        #[builder(start_fn, into)] name: IdTask,
626        purpose: Option<String>,
627        #[builder(default)] cred: SCredentials,
628        #[builder(default)] commands: SCommands,
629        #[builder(with = |f : fn(OptBuilder) -> Rc<RefCell<Opt>> | f(Opt::builder(Level::Task)))]
630        options: Option<Rc<RefCell<Opt>>>,
631        #[builder(default)] _extra_fields: Map<String, Value>,
632        _role: Option<Weak<RefCell<SRole>>>,
633    ) -> Rc<RefCell<Self>> {
634        Rc::new(RefCell::new(STask {
635            name,
636            purpose,
637            cred,
638            commands,
639            options,
640            _extra_fields,
641            _role,
642        }))
643    }
644    pub fn role(&self) -> Option<Rc<RefCell<SRole>>> {
645        self._role.as_ref()?.upgrade()
646    }
647}
648
649impl Index<usize> for SConfig {
650    type Output = Rc<RefCell<SRole>>;
651
652    fn index(&self, index: usize) -> &Self::Output {
653        &self.roles[index]
654    }
655}
656
657impl Index<usize> for SRole {
658    type Output = Rc<RefCell<STask>>;
659
660    fn index(&self, index: usize) -> &Self::Output {
661        &self.tasks[index]
662    }
663}
664
665#[bon]
666impl SCommands {
667    #[builder]
668    pub fn new(
669        #[builder(start_fn)] default_behavior: SetBehavior,
670        #[builder(default, with = FromIterator::from_iter)] add: Vec<SCommand>,
671        #[builder(default, with = FromIterator::from_iter)] sub: Vec<SCommand>,
672        #[builder(default, with = <_>::from_iter)] _extra_fields: Map<String, Value>,
673    ) -> Self {
674        SCommands {
675            default_behavior: Some(default_behavior),
676            add,
677            sub,
678            _extra_fields,
679        }
680    }
681}
682
683impl SCapabilities {
684    pub fn to_capset(&self) -> CapSet {
685        let mut capset = match self.default_behavior {
686            SetBehavior::All => capctl::bounding::probe() & CapSet::not(CapSet::empty()),
687            SetBehavior::None => CapSet::empty(),
688        };
689        capset = capset.union(self.add);
690        capset.drop_all(self.sub);
691        capset
692    }
693}
694
695impl PartialEq<str> for SUserChooser {
696    fn eq(&self, other: &str) -> bool {
697        match self {
698            SUserChooser::Actor(actor) => actor == &SUserType::from(other),
699            SUserChooser::ChooserStruct(chooser) => chooser.fallback == *other,
700        }
701    }
702}
703
704#[cfg(test)]
705mod tests {
706
707    use capctl::Cap;
708    use chrono::Duration;
709    use linked_hash_set::LinkedHashSet;
710
711    use crate::{
712        as_borrow,
713        database::{
714            actor::SGroupType,
715            options::{
716                EnvBehavior, PathBehavior, SAuthentication, SBounding, SEnvOptions, SPathOptions,
717                SPrivileged, STimeout, TimestampType,
718            },
719        },
720    };
721
722    use super::*;
723
724    #[test]
725    fn test_deserialize() {
726        println!("START");
727        let config = r#"
728        {
729            "options": {
730                "path": {
731                    "default": "delete",
732                    "add": ["path_add"],
733                    "sub": ["path_sub"]
734                },
735                "env": {
736                    "default": "delete",
737                    "override_behavior": true,
738                    "keep": ["keep_env"],
739                    "check": ["check_env"]
740                },
741                "root": "privileged",
742                "bounding": "ignore",
743                "authentication": "skip",
744                "wildcard-denied": "wildcards",
745                "timeout": {
746                    "type": "ppid",
747                    "duration": "00:05:00"
748                }
749            },
750            "roles": [
751                {
752                    "name": "role1",
753                    "actors": [
754                        {
755                            "type": "user",
756                            "name": "user1"
757                        },
758                        {
759                            "type":"group",
760                            "groups": ["group1","1000"]
761                        }
762                    ],
763                    "tasks": [
764                        {
765                            "name": "task1",
766                            "purpose": "purpose1",
767                            "cred": {
768                                "setuid": {
769                                    "fallback": "user1",
770                                    "default": "all",
771                                    "add": ["user2"],
772                                    "sub": ["user3"]
773                                },
774                                "setgid": "setgid1",
775                                "capabilities": {
776                                    "default": "all",
777                                    "add": ["cap_net_bind_service"],
778                                    "sub": ["cap_sys_admin"]
779                                }
780                            },
781                            "commands": {
782                                "default": "all",
783                                "add": ["cmd1"],
784                                "sub": ["cmd2"]
785                            }
786                        }
787                    ]
788                }
789            ]
790        }
791        "#;
792        println!("STEP 1");
793        let config: SConfig = serde_json::from_str(config).unwrap();
794        let options = config.options.as_ref().unwrap().as_ref().borrow();
795        let path = options.path.as_ref().unwrap();
796        assert_eq!(path.default_behavior, PathBehavior::Delete);
797        let default = LinkedHashSet::new();
798        assert!(path
799            .add
800            .as_ref()
801            .unwrap_or(&default)
802            .front()
803            .is_some_and(|s| s == "path_add"));
804        let env = options.env.as_ref().unwrap();
805        assert_eq!(env.default_behavior, EnvBehavior::Delete);
806        assert!(env.override_behavior.is_some_and(|b| b));
807        assert!(env
808            .keep
809            .as_ref()
810            .unwrap_or(&LinkedHashSet::new())
811            .front()
812            .is_some_and(|s| s == "keep_env"));
813        assert!(env
814            .check
815            .as_ref()
816            .unwrap_or(&LinkedHashSet::new())
817            .front()
818            .is_some_and(|s| s == "check_env"));
819        assert!(options.root.as_ref().unwrap().is_privileged());
820        assert!(options.bounding.as_ref().unwrap().is_ignore());
821        assert_eq!(options.authentication, Some(SAuthentication::Skip));
822        assert_eq!(options.wildcard_denied.as_ref().unwrap(), "wildcards");
823
824        let timeout = options.timeout.as_ref().unwrap();
825        assert_eq!(timeout.type_field, Some(TimestampType::PPID));
826        assert_eq!(timeout.duration, Some(Duration::minutes(5)));
827        assert_eq!(config.roles[0].as_ref().borrow().name, "role1");
828        let actor0 = &config.roles[0].as_ref().borrow().actors[0];
829        assert_eq!(
830            actor0,
831            &SActor::User {
832                id: Some("user1".into()),
833                _extra_fields: Map::default()
834            }
835        );
836        let actor1 = &config.roles[0].as_ref().borrow().actors[1];
837        match actor1 {
838            SActor::Group { groups, .. } => match groups.as_ref().unwrap() {
839                SGroups::Multiple(groups) => {
840                    assert_eq!(&groups[0], "group1");
841                    assert_eq!(groups[1], 1000);
842                }
843                _ => panic!("unexpected actor group type"),
844            },
845            _ => panic!("unexpected actor {:?}", actor1),
846        }
847        let role = config.roles[0].as_ref().borrow();
848        assert_eq!(as_borrow!(role[0]).purpose.as_ref().unwrap(), "purpose1");
849        let cred = &as_borrow!(&role[0]).cred;
850        let setuidstruct = SSetuidSet {
851            fallback: "user1".into(),
852            default: SetBehavior::All,
853            add: ["user2".into()].into(),
854            sub: ["user3".into()].into(),
855        };
856        assert!(
857            matches!(cred.setuid.as_ref().unwrap(), SUserChooser::ChooserStruct(set) if set == &setuidstruct)
858        );
859        assert_eq!(*cred.setgid.as_ref().unwrap(), ["setgid1".into()]);
860        let capabilities = cred.capabilities.as_ref().unwrap();
861        assert_eq!(capabilities.default_behavior, SetBehavior::All);
862        assert!(capabilities.add.has(Cap::NET_BIND_SERVICE));
863        assert!(capabilities.sub.has(Cap::SYS_ADMIN));
864        let commands = &as_borrow!(&role[0]).commands;
865        assert_eq!(
866            *commands.default_behavior.as_ref().unwrap(),
867            SetBehavior::All
868        );
869        assert_eq!(commands.add[0], SCommand::Simple("cmd1".into()));
870        assert_eq!(commands.sub[0], SCommand::Simple("cmd2".into()));
871    }
872    #[test]
873    fn test_unknown_fields() {
874        let config = r#"
875        {
876            "options": {
877                "path": {
878                    "default": "delete",
879                    "add": ["path_add"],
880                    "sub": ["path_sub"],
881                    "unknown": "unknown"
882                },
883                "env": {
884                    "default": "delete",
885                    "keep": ["keep_env"],
886                    "check": ["check_env"],
887                    "unknown": "unknown"
888                },
889                "allow-root": false,
890                "allow-bounding": false,
891                "wildcard-denied": "wildcards",
892                "timeout": {
893                    "type": "ppid",
894                    "duration": "00:05:00",
895                    "unknown": "unknown"
896                },
897                "unknown": "unknown"
898            },
899            "roles": [
900                {
901                    "name": "role1",
902                    "actors": [
903                        {
904                            "type": "user",
905                            "name": "user1",
906                            "unknown": "unknown"
907                        },
908                        {
909                            "type":"bla",
910                            "unknown": "unknown"
911                        }
912                    ],
913                    "tasks": [
914                        {
915                            "name": "task1",
916                            "purpose": "purpose1",
917                            "cred": {
918                                "setuid": "setuid1",
919                                "setgid": "setgid1",
920                                "capabilities": {
921                                    "default": "all",
922                                    "add": ["cap_dac_override"],
923                                    "sub": ["cap_dac_override"],
924                                    "unknown": "unknown"
925                                },
926                                "unknown": "unknown"
927                            },
928                            "commands": {
929                                "default": "all",
930                                "add": ["cmd1"],
931                                "sub": ["cmd2"],
932                                "unknown": "unknown"
933                            },
934                            "unknown": "unknown"
935                        }
936                    ],
937                    "unknown": "unknown"
938                }
939            ],
940            "unknown": "unknown"
941        }
942        "#;
943        let config: SConfig = serde_json::from_str(config).unwrap();
944        assert_eq!(config._extra_fields.get("unknown").unwrap(), "unknown");
945
946        let binding = config.options.unwrap();
947        let options = binding.as_ref().borrow();
948        let path = options.path.as_ref().unwrap();
949        assert_eq!(path._extra_fields.get("unknown").unwrap(), "unknown");
950        let env = &options.env.as_ref().unwrap();
951        assert_eq!(env._extra_fields.get("unknown").unwrap(), "unknown");
952        assert_eq!(options._extra_fields.get("unknown").unwrap(), "unknown");
953        let timeout = options.timeout.as_ref().unwrap();
954        assert_eq!(timeout._extra_fields.get("unknown").unwrap(), "unknown");
955        assert_eq!(config._extra_fields.get("unknown").unwrap(), "unknown");
956        let actor0 = &as_borrow!(config.roles[0]).actors[0];
957        match actor0 {
958            SActor::User { id, _extra_fields } => {
959                assert_eq!(id.as_ref().unwrap(), "user1");
960                assert_eq!(_extra_fields.get("unknown").unwrap(), "unknown");
961            }
962            _ => panic!("unexpected actor type"),
963        }
964        let actor1 = &as_borrow!(config.roles[0]).actors[1];
965        match actor1 {
966            SActor::Unknown(unknown) => {
967                let obj = unknown.as_object().unwrap();
968                assert_eq!(obj.get("type").unwrap().as_str().unwrap(), "bla");
969                assert_eq!(obj.get("unknown").unwrap().as_str().unwrap(), "unknown");
970            }
971            _ => panic!("unexpected actor type"),
972        }
973        assert_eq!(
974            config.roles[0].as_ref().borrow()[0]
975                .as_ref()
976                .borrow()
977                ._extra_fields
978                .get("unknown")
979                .as_ref()
980                .unwrap()
981                .as_str()
982                .unwrap(),
983            "unknown"
984        );
985        let role = config.roles[0].as_ref().borrow();
986        let cred = &role[0].as_ref().borrow().cred;
987        assert_eq!(cred._extra_fields.get("unknown").unwrap(), "unknown");
988        let capabilities = cred.capabilities.as_ref().unwrap();
989        assert_eq!(
990            capabilities._extra_fields.get("unknown").unwrap(),
991            "unknown"
992        );
993        let commands = &as_borrow!(role[0]).commands;
994        assert_eq!(commands._extra_fields.get("unknown").unwrap(), "unknown");
995    }
996
997    #[test]
998    fn test_deserialize_alias() {
999        let config = r#"
1000        {
1001            "options": {
1002                "path": {
1003                    "default": "delete",
1004                    "add": ["path_add"],
1005                    "del": ["path_sub"]
1006                },
1007                "env": {
1008                    "default": "delete",
1009                    "keep": ["keep_env"],
1010                    "check": ["check_env"]
1011                },
1012                "root": "privileged",
1013                "bounding": "ignore",
1014                "authentication": "skip",
1015                "wildcard-denied": "wildcards",
1016                "timeout": {
1017                    "type": "ppid",
1018                    "duration": "00:05:00"
1019                }
1020            },
1021            "roles": [
1022                {
1023                    "name": "role1",
1024                    "actors": [
1025                        {
1026                            "type": "user",
1027                            "name": "user1"
1028                        },
1029                        {
1030                            "type":"group",
1031                            "groups": ["group1","1000"]
1032                        }
1033                    ],
1034                    "tasks": [
1035                        {
1036                            "name": "task1",
1037                            "purpose": "purpose1",
1038                            "cred": {
1039                                "setuid": "setuid1",
1040                                "setgid": "setgid1",
1041                                "capabilities": ["cap_net_bind_service"]
1042                            },
1043                            "commands": {
1044                                "default": "all",
1045                                "add": ["cmd1"],
1046                                "del": ["cmd2"]
1047                            }
1048                        }
1049                    ]
1050                }
1051            ]
1052        }
1053        "#;
1054        let config: SConfig = serde_json::from_str(config).unwrap();
1055        let options = config.options.as_ref().unwrap().as_ref().borrow();
1056        let path = options.path.as_ref().unwrap();
1057        assert_eq!(path.default_behavior, PathBehavior::Delete);
1058        let default = LinkedHashSet::new();
1059        assert!(path
1060            .add
1061            .as_ref()
1062            .unwrap_or(&default)
1063            .front()
1064            .is_some_and(|s| s == "path_add"));
1065        let env = options.env.as_ref().unwrap();
1066        assert_eq!(env.default_behavior, EnvBehavior::Delete);
1067        assert!(env
1068            .keep
1069            .as_ref()
1070            .unwrap()
1071            .front()
1072            .is_some_and(|s| s == "keep_env"));
1073        assert!(env
1074            .check
1075            .as_ref()
1076            .unwrap()
1077            .front()
1078            .is_some_and(|s| s == "check_env"));
1079        assert!(options.root.as_ref().unwrap().is_privileged());
1080        assert!(options.bounding.as_ref().unwrap().is_ignore());
1081        assert_eq!(options.authentication, Some(SAuthentication::Skip));
1082        assert_eq!(options.wildcard_denied.as_ref().unwrap(), "wildcards");
1083
1084        let timeout = options.timeout.as_ref().unwrap();
1085        assert_eq!(timeout.type_field, Some(TimestampType::PPID));
1086        assert_eq!(timeout.duration, Some(Duration::minutes(5)));
1087        assert_eq!(config.roles[0].as_ref().borrow().name, "role1");
1088        let actor0 = &config.roles[0].as_ref().borrow().actors[0];
1089        match actor0 {
1090            SActor::User { id, .. } => {
1091                assert_eq!(id.as_ref().unwrap(), "user1");
1092            }
1093            _ => panic!("unexpected actor type"),
1094        }
1095        let actor1 = &config.roles[0].as_ref().borrow().actors[1];
1096        match actor1 {
1097            SActor::Group { groups, .. } => match groups.as_ref().unwrap() {
1098                SGroups::Multiple(groups) => {
1099                    assert_eq!(groups[0], SGroupType::from("group1"));
1100                    assert_eq!(groups[1], SGroupType::from(1000));
1101                }
1102                _ => panic!("unexpected actor group type"),
1103            },
1104            _ => panic!("unexpected actor {:?}", actor1),
1105        }
1106        let role = config.roles[0].as_ref().borrow();
1107        assert_eq!(as_borrow!(role[0]).purpose.as_ref().unwrap(), "purpose1");
1108        let cred = &as_borrow!(&role[0]).cred;
1109        assert_eq!(
1110            cred.setuid.as_ref().unwrap(),
1111            &SUserChooser::from(SUserType::from("setuid1"))
1112        );
1113        assert_eq!(cred.setgid.as_ref().unwrap(), &SGroups::from(["setgid1"]));
1114        let capabilities = cred.capabilities.as_ref().unwrap();
1115        assert_eq!(capabilities.default_behavior, SetBehavior::None);
1116        assert!(capabilities.add.has(Cap::NET_BIND_SERVICE));
1117        assert!(capabilities.sub.is_empty());
1118        let commands = &as_borrow!(&role[0]).commands;
1119        assert_eq!(
1120            *commands.default_behavior.as_ref().unwrap(),
1121            SetBehavior::All
1122        );
1123        assert_eq!(commands.add[0], SCommand::Simple("cmd1".into()));
1124        assert_eq!(commands.sub[0], SCommand::Simple("cmd2".into()));
1125    }
1126
1127    #[test]
1128    fn test_serialize() {
1129        let config = SConfig::builder()
1130            .role(
1131                SRole::builder("role1")
1132                    .actor(SActor::user("user1").build())
1133                    .actor(
1134                        SActor::group([SGroupType::from("group1"), SGroupType::from(1000)]).build(),
1135                    )
1136                    .task(
1137                        STask::builder("task1")
1138                            .purpose("purpose1".into())
1139                            .cred(
1140                                SCredentials::builder()
1141                                    .setuid(SUserChooser::ChooserStruct(
1142                                        SSetuidSet::builder("user1", SetBehavior::All)
1143                                            .add(["user2".into()])
1144                                            .sub(["user3".into()])
1145                                            .build(),
1146                                    ))
1147                                    .setgid(["setgid1"])
1148                                    .capabilities(
1149                                        SCapabilities::builder(SetBehavior::All)
1150                                            .add_cap(Cap::NET_BIND_SERVICE)
1151                                            .sub_cap(Cap::SYS_ADMIN)
1152                                            .build(),
1153                                    )
1154                                    .build(),
1155                            )
1156                            .commands(
1157                                SCommands::builder(SetBehavior::All)
1158                                    .add(["cmd1".into()])
1159                                    .sub(["cmd2".into()])
1160                                    .build(),
1161                            )
1162                            .build(),
1163                    )
1164                    .build(),
1165            )
1166            .options(|opt| {
1167                opt.path(
1168                    SPathOptions::builder(PathBehavior::Delete)
1169                        .add(["path_add"])
1170                        .sub(["path_sub"])
1171                        .build(),
1172                )
1173                .env(
1174                    SEnvOptions::builder(EnvBehavior::Delete)
1175                        .override_behavior(true)
1176                        .keep(["keep_env"])
1177                        .unwrap()
1178                        .check(["check_env"])
1179                        .unwrap()
1180                        .build(),
1181                )
1182                .root(SPrivileged::Privileged)
1183                .bounding(SBounding::Ignore)
1184                .authentication(SAuthentication::Skip)
1185                .wildcard_denied("wildcards")
1186                .timeout(
1187                    STimeout::builder()
1188                        .type_field(TimestampType::PPID)
1189                        .duration(Duration::minutes(5))
1190                        .build(),
1191                )
1192                .build()
1193            })
1194            .build();
1195        let config = serde_json::to_string_pretty(&config).unwrap();
1196        println!("{}", config);
1197    }
1198
1199    #[test]
1200    fn test_serialize_operride_behavior_option() {
1201        let config = SConfig::builder()
1202            .options(|opt| {
1203                opt.env(
1204                    SEnvOptions::builder(EnvBehavior::Inherit)
1205                        .override_behavior(true)
1206                        .build(),
1207                )
1208                .build()
1209            })
1210            .build();
1211        let config = serde_json::to_string(&config).unwrap();
1212        assert_eq!(
1213            config,
1214            "{\"options\":{\"env\":{\"override_behavior\":true}}}"
1215        );
1216    }
1217}