eva_common/
acl.rs

1use crate::value::to_value;
2use crate::{is_str_any, is_str_wildcard, EResult, Error, ItemKind, Value, OID};
3use crate::{OID_MASK_PREFIX_FORMULA, OID_MASK_PREFIX_REGEX};
4use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer};
5use std::cmp::Ordering;
6use std::collections::{hash_set, HashSet};
7use std::convert::TryFrom;
8use std::fmt;
9use std::hash::{Hash, Hasher};
10use std::str::FromStr;
11use submap::AclMap;
12
13static ERR_INVALID_OID_MASK: &str = "Invalid OID mask format";
14static ERR_PATH_MASK_EMPTY: &str = "Empty path mask";
15static ERR_INVALID_OID_MASK_OP: &str = "Invalid OID mask for this op";
16
17#[inline]
18pub fn create_acl_map() -> AclMap {
19    AclMap::new()
20        .separator('/')
21        .wildcard_multiple(crate::WILDCARD)
22        .match_any_multiple(crate::MATCH_ANY)
23        .formula_prefix(OID_MASK_PREFIX_FORMULA)
24        .regex_prefix(OID_MASK_PREFIX_REGEX)
25}
26
27#[derive(Debug, Clone, Eq)]
28pub struct PathMask {
29    chunks: Option<Vec<String>>,
30}
31
32impl PathMask {
33    #[inline]
34    fn new_any() -> Self {
35        Self { chunks: None }
36    }
37    #[inline]
38    fn is_any(&self) -> bool {
39        self.chunks.is_none()
40    }
41    fn matches_split(&self, path_split: &mut std::str::Split<'_, char>) -> bool {
42        if let Some(ref chunks) = self.chunks {
43            let mut s_m = chunks.iter();
44            loop {
45                if let Some(i_chunk) = path_split.next() {
46                    if let Some(m_chunk) = s_m.next() {
47                        if is_str_wildcard(m_chunk) {
48                            return true;
49                        }
50                        if !is_str_any(m_chunk) && i_chunk != m_chunk {
51                            return false;
52                        }
53                    } else {
54                        return false;
55                    }
56                } else {
57                    return s_m.next().is_none();
58                }
59            }
60        } else {
61            true
62        }
63    }
64}
65
66impl AsRef<PathMask> for PathMask {
67    fn as_ref(&self) -> &PathMask {
68        self
69    }
70}
71
72impl<'de> Deserialize<'de> for PathMask {
73    fn deserialize<D>(deserializer: D) -> Result<PathMask, D::Error>
74    where
75        D: Deserializer<'de>,
76    {
77        deserializer.deserialize_unit(PathMaskVisitor)
78    }
79}
80
81struct PathMaskVisitor;
82impl serde::de::Visitor<'_> for PathMaskVisitor {
83    type Value = PathMask;
84    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
85        formatter.write_str("a string-packed path mask")
86    }
87    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
88    where
89        E: serde::de::Error,
90    {
91        value
92            .parse()
93            .map_err(|e| E::custom(format!("{}: {}", e, value)))
94    }
95    fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
96    where
97        E: serde::de::Error,
98    {
99        value
100            .parse()
101            .map_err(|e| E::custom(format!("{}: {}", e, value)))
102    }
103}
104
105#[derive(Debug, Clone, Default)]
106pub struct PathMaskList {
107    acl_map: AclMap,
108}
109
110impl Serialize for PathMaskList {
111    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
112    where
113        S: Serializer,
114    {
115        let path_masks = self.acl_map.list();
116        let mut seq = serializer.serialize_seq(Some(path_masks.len()))?;
117        for el in path_masks {
118            seq.serialize_element(el)?;
119        }
120        seq.end()
121    }
122}
123
124impl<'de> Deserialize<'de> for PathMaskList {
125    fn deserialize<D>(deserializer: D) -> Result<PathMaskList, D::Error>
126    where
127        D: Deserializer<'de>,
128    {
129        let masks: Vec<String> = Deserialize::deserialize(deserializer)?;
130        Ok(PathMaskList::from_string_list(&masks))
131    }
132}
133
134impl From<PathMaskList> for Value {
135    fn from(v: PathMaskList) -> Value {
136        to_value(v).unwrap()
137    }
138}
139
140impl TryFrom<Value> for PathMaskList {
141    type Error = Error;
142    fn try_from(value: Value) -> EResult<PathMaskList> {
143        match value {
144            Value::Seq(_) => {
145                let masks: Vec<String> = value.deserialize_into()?;
146                Ok(PathMaskList::from_string_list(&masks))
147            }
148            Value::String(s) => {
149                if s.is_empty() {
150                    Ok(<_>::default())
151                } else {
152                    Ok(PathMaskList::from_str_list(
153                        &s.split(',').collect::<Vec<_>>(),
154                    ))
155                }
156            }
157            _ => Err(Error::invalid_data("Expected vec or string")),
158        }
159    }
160}
161
162impl PathMaskList {
163    pub fn from_str_list(s_masks: &[&str]) -> Self {
164        let mut acl_map = create_acl_map();
165        for s in s_masks {
166            if !s.is_empty() {
167                acl_map.insert(s);
168            }
169        }
170        Self { acl_map }
171    }
172    pub fn from_string_list(s_masks: &[String]) -> Self {
173        let mut acl_map = create_acl_map();
174        for s in s_masks {
175            if !s.is_empty() {
176                acl_map.insert(s);
177            }
178        }
179        Self { acl_map }
180    }
181    #[inline]
182    pub fn matches(&self, path: &str) -> bool {
183        self.acl_map.matches(path)
184    }
185    pub fn is_empty(&self) -> bool {
186        self.acl_map.is_empty()
187    }
188}
189
190impl AsRef<PathMaskList> for PathMaskList {
191    fn as_ref(&self) -> &PathMaskList {
192        self
193    }
194}
195
196impl PartialEq for PathMask {
197    fn eq(&self, other: &Self) -> bool {
198        self.chunks == other.chunks
199    }
200}
201
202impl Ord for PathMask {
203    fn cmp(&self, other: &Self) -> Ordering {
204        self.chunks.cmp(&other.chunks)
205    }
206}
207
208impl Hash for PathMask {
209    fn hash<H: Hasher>(&self, hasher: &mut H) {
210        self.chunks.hash(hasher);
211    }
212}
213
214impl PartialOrd for PathMask {
215    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
216        Some(self.cmp(other))
217    }
218}
219
220impl fmt::Display for PathMask {
221    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222        if let Some(ref chunks) = self.chunks {
223            write!(f, "{}", chunks.join("/"))
224        } else {
225            write!(f, "#")
226        }
227    }
228}
229
230impl FromStr for PathMask {
231    type Err = Error;
232    fn from_str(s: &str) -> Result<Self, Self::Err> {
233        if s.is_empty() {
234            Err(Error::invalid_data(ERR_PATH_MASK_EMPTY))
235        } else if is_str_wildcard(s) {
236            Ok(Self::new_any())
237        } else {
238            let mut chunks = Vec::new();
239            for chunk in s.split('/') {
240                if is_str_wildcard(chunk) {
241                    chunks.push("#".to_owned());
242                    break;
243                }
244                chunks.push(chunk.to_owned());
245            }
246            Ok(Self {
247                chunks: Some(chunks),
248            })
249        }
250    }
251}
252
253#[derive(Debug, Clone, Default)]
254pub struct OIDMaskList {
255    oid_masks: HashSet<OIDMask>,
256    acl_map: AclMap,
257}
258
259impl PartialEq for OIDMaskList {
260    fn eq(&self, other: &Self) -> bool {
261        self.oid_masks == other.oid_masks
262    }
263}
264
265impl Eq for OIDMaskList {}
266
267impl Serialize for OIDMaskList {
268    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
269    where
270        S: Serializer,
271    {
272        let mut seq = serializer.serialize_seq(Some(self.oid_masks.len()))?;
273        for element in &self.oid_masks {
274            seq.serialize_element(&element.to_string())?;
275        }
276        seq.end()
277    }
278}
279
280impl<'de> Deserialize<'de> for OIDMaskList {
281    fn deserialize<D>(deserializer: D) -> Result<OIDMaskList, D::Error>
282    where
283        D: Deserializer<'de>,
284    {
285        let masks: HashSet<OIDMask> = Deserialize::deserialize(deserializer)?;
286        Ok(OIDMaskList::new(masks))
287    }
288}
289
290impl FromIterator<OIDMask> for OIDMaskList {
291    fn from_iter<I>(masks: I) -> Self
292    where
293        I: IntoIterator<Item = OIDMask>,
294    {
295        let mut s: HashSet<OIDMask> = HashSet::new();
296        for mask in masks {
297            s.insert(mask);
298        }
299        Self::new(s)
300    }
301}
302
303impl OIDMaskList {
304    #[inline]
305    pub fn new(oid_masks: HashSet<OIDMask>) -> Self {
306        let mut acl_map = create_acl_map();
307        for mask in &oid_masks {
308            acl_map.insert(&mask.as_path());
309        }
310        Self { oid_masks, acl_map }
311    }
312    #[inline]
313    pub fn new0(oid_mask: OIDMask) -> Self {
314        let mut acl_map = create_acl_map();
315        acl_map.insert(&oid_mask.as_path());
316        let mut oid_masks = HashSet::new();
317        oid_masks.insert(oid_mask);
318        Self { oid_masks, acl_map }
319    }
320    #[inline]
321    pub fn new_any() -> Self {
322        let mut acl_map = create_acl_map();
323        acl_map.insert(crate::WILDCARD[0]);
324        let mut oid_masks = HashSet::new();
325        oid_masks.insert(OIDMask::new_any());
326        Self { oid_masks, acl_map }
327    }
328    pub fn from_str_list(s_masks: &[&str]) -> EResult<Self> {
329        let mut oid_masks = HashSet::new();
330        for s in s_masks {
331            oid_masks.insert(s.parse()?);
332        }
333        Ok(Self::new(oid_masks))
334    }
335    pub fn from_string_list(s_masks: &[String]) -> EResult<Self> {
336        let mut oid_masks = HashSet::new();
337        for s in s_masks {
338            oid_masks.insert(s.parse()?);
339        }
340        Ok(Self::new(oid_masks))
341    }
342    #[inline]
343    pub fn matches(&self, oid: &OID) -> bool {
344        self.acl_map.matches(oid.as_path())
345    }
346    #[inline]
347    pub fn matches_mask(&self, mask: &OIDMask) -> bool {
348        self.acl_map.matches(&mask.as_path())
349    }
350    #[inline]
351    pub fn is_empty(&self) -> bool {
352        self.oid_masks.is_empty()
353    }
354    #[inline]
355    pub fn oid_masks(&self) -> &HashSet<OIDMask> {
356        &self.oid_masks
357    }
358    #[inline]
359    pub fn oid_masks_mut(&mut self) -> &mut HashSet<OIDMask> {
360        &mut self.oid_masks
361    }
362    #[inline]
363    pub fn as_string_vec(&self) -> Vec<String> {
364        self.oid_masks.iter().map(ToString::to_string).collect()
365    }
366    pub fn try_from_iter<I, T>(values: I) -> EResult<Self>
367    where
368        I: IntoIterator<Item = T>,
369        T: TryInto<OIDMask, Error = Error>,
370    {
371        let mut res = HashSet::new();
372        for v in values {
373            let mask: OIDMask = v.try_into()?;
374            res.insert(mask);
375        }
376        Ok(Self::new(res))
377    }
378    pub fn iter(&self) -> hash_set::Iter<'_, OIDMask> {
379        <&Self as IntoIterator>::into_iter(self)
380    }
381}
382
383impl<'a> IntoIterator for &'a OIDMaskList {
384    type Item = &'a OIDMask;
385    type IntoIter = hash_set::Iter<'a, OIDMask>;
386
387    fn into_iter(self) -> Self::IntoIter {
388        self.oid_masks.iter()
389    }
390}
391
392impl IntoIterator for OIDMaskList {
393    type Item = OIDMask;
394    type IntoIter = hash_set::IntoIter<Self::Item>;
395
396    fn into_iter(self) -> Self::IntoIter {
397        self.oid_masks.into_iter()
398    }
399}
400
401impl AsRef<OIDMaskList> for OIDMaskList {
402    fn as_ref(&self) -> &OIDMaskList {
403        self
404    }
405}
406
407#[derive(Debug, Clone, Eq)]
408pub struct OIDMask {
409    kind: Option<ItemKind>,
410    path: PathMask,
411}
412
413impl OIDMask {
414    #[inline]
415    fn check(s: &str) -> EResult<()> {
416        if s.len() > 65000 {
417            return Err(Error::invalid_data("OID mask too long"));
418        }
419        for c in s.chars() {
420            if !(c.is_alphanumeric() || crate::OID_MASK_ALLOWED_SYMBOLS.contains(c) || c == '/') {
421                return Err(Error::invalid_data(format!(
422                    "Invalid symbol in OID mask: {}",
423                    c
424                )));
425            }
426        }
427        Ok(())
428    }
429    #[inline]
430    pub fn kind(&self) -> Option<ItemKind> {
431        self.kind
432    }
433    /// A special case, when OID mask can be converted to "wildcard OID" - an OID, where id is the
434    /// wildcard symbol. Wildcard OIDs are special types of OIDs, which are fully compatible with
435    /// majority of ACL checkers and can be used to obtain data from various database sources,
436    /// which support wildcard selections (such as like 'kind:group/%' in SQL
437    #[inline]
438    pub fn to_wildcard_oid(&self) -> EResult<OID> {
439        if let Some(kind) = self.kind {
440            if let Some(ref ch) = self.path.chunks {
441                for (i, p) in ch.iter().enumerate() {
442                    if is_str_any(p) || (is_str_wildcard(p) && i < p.len()) {
443                        return Err(Error::invalid_data(ERR_INVALID_OID_MASK_OP));
444                    }
445                }
446            }
447            Ok(OID::new0_unchecked(kind, &self.path.to_string())?)
448        } else {
449            Err(Error::invalid_data(ERR_INVALID_OID_MASK_OP))
450        }
451    }
452    fn parse_oid_mask(s: &str, c: char) -> EResult<Self> {
453        if is_str_wildcard(s) {
454            Ok(Self::new_any())
455        } else {
456            s.find(c).map_or_else(
457                || {
458                    let kind: ItemKind = s.parse()?;
459                    Ok(OIDMask {
460                        kind: Some(kind),
461                        path: PathMask::new_any(),
462                    })
463                },
464                |tpos| {
465                    if tpos == s.len() {
466                        Err(Error::invalid_data(format!(
467                            "{}: {}",
468                            ERR_INVALID_OID_MASK, s
469                        )))
470                    } else {
471                        let tp_str = &s[..tpos];
472                        let kind: Option<ItemKind> = if is_str_any(tp_str) {
473                            None
474                        } else {
475                            Some(s[..tpos].parse()?)
476                        };
477                        let p = &s[tpos + 1..];
478                        OIDMask::check(p)?;
479                        Ok(OIDMask {
480                            kind,
481                            path: p.parse()?,
482                        })
483                    }
484                },
485            )
486        }
487    }
488    #[inline]
489    pub fn from_path(s: &str) -> EResult<Self> {
490        Self::parse_oid_mask(s, '/')
491    }
492    #[inline]
493    pub fn as_path(&self) -> String {
494        if self.path.chunks.is_some() {
495            format!(
496                "{}/{}",
497                if let Some(ref kind) = self.kind {
498                    kind.as_str()
499                } else {
500                    "+"
501                },
502                self.path
503            )
504        } else if let Some(ref kind) = self.kind {
505            format!("{}/#", kind.as_str())
506        } else {
507            "#".to_owned()
508        }
509    }
510    #[inline]
511    pub fn chunks(&self) -> Option<Vec<&str>> {
512        self.path
513            .chunks
514            .as_ref()
515            .map(|v| v.iter().map(String::as_str).collect())
516    }
517    #[inline]
518    pub fn new_any() -> Self {
519        OIDMask {
520            kind: None,
521            path: PathMask::new_any(),
522        }
523    }
524    pub fn matches(&self, oid: &OID) -> bool {
525        let oid_tp = oid.kind();
526        let sp = oid.full_id().split('/');
527        if let Some(mask_tp) = self.kind {
528            if mask_tp != oid_tp {
529                return false;
530            }
531        }
532        if self.path.matches_split(&mut sp.clone()) {
533            return true;
534        }
535        false
536    }
537}
538
539impl PartialEq for OIDMask {
540    fn eq(&self, other: &Self) -> bool {
541        self.kind == other.kind && self.path == other.path
542    }
543}
544
545impl Ord for OIDMask {
546    fn cmp(&self, other: &Self) -> Ordering {
547        if self.kind == other.kind {
548            self.path.cmp(&other.path)
549        } else {
550            self.kind.cmp(&other.kind)
551        }
552    }
553}
554
555impl fmt::Display for OIDMask {
556    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
557        if let Some(kind) = self.kind {
558            write!(f, "{}:{}", kind, self.path)
559        } else if self.path.is_any() {
560            write!(f, "#")
561        } else {
562            write!(f, "+:{}", self.path)
563        }
564    }
565}
566
567impl From<Vec<OIDMask>> for OIDMaskList {
568    fn from(v: Vec<OIDMask>) -> Self {
569        Self::from_iter(v)
570    }
571}
572
573impl From<OID> for OIDMask {
574    fn from(oid: OID) -> Self {
575        OIDMask {
576            kind: Some(oid.kind()),
577            path: oid.full_id().parse().unwrap(),
578        }
579    }
580}
581
582impl From<OID> for OIDMaskList {
583    fn from(oid: OID) -> Self {
584        let mask = OIDMask {
585            kind: Some(oid.kind()),
586            path: oid.full_id().parse().unwrap(),
587        };
588        mask.into()
589    }
590}
591
592impl AsRef<OIDMask> for OIDMask {
593    fn as_ref(&self) -> &OIDMask {
594        self
595    }
596}
597
598impl AsRef<PathMask> for OIDMask {
599    fn as_ref(&self) -> &PathMask {
600        &self.path
601    }
602}
603
604impl FromStr for OIDMask {
605    type Err = Error;
606    fn from_str(s: &str) -> Result<Self, Self::Err> {
607        Self::parse_oid_mask(s, ':')
608    }
609}
610
611macro_rules! impl_oidmask_from_str {
612    ($t: ty) => {
613        impl TryFrom<$t> for OIDMask {
614            type Error = Error;
615            fn try_from(s: $t) -> EResult<Self> {
616                s.parse()
617            }
618        }
619    };
620}
621
622impl_oidmask_from_str!(String);
623impl_oidmask_from_str!(&str);
624impl_oidmask_from_str!(&&str);
625
626impl Hash for OIDMask {
627    fn hash<H: Hasher>(&self, hasher: &mut H) {
628        self.kind.map_or(0, |v| v as u16).hash(hasher);
629        self.path.hash(hasher);
630    }
631}
632
633impl PartialOrd for OIDMask {
634    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
635        Some(self.cmp(other))
636    }
637}
638
639impl<'de> Deserialize<'de> for OIDMask {
640    fn deserialize<D>(deserializer: D) -> Result<OIDMask, D::Error>
641    where
642        D: Deserializer<'de>,
643    {
644        let s: String = Deserialize::deserialize(deserializer)?;
645        s.parse().map_err(serde::de::Error::custom)
646    }
647}
648
649impl Serialize for OIDMask {
650    #[inline]
651    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
652    where
653        S: Serializer,
654    {
655        serializer.serialize_str(&self.to_string())
656    }
657}
658
659impl From<OIDMaskList> for Value {
660    fn from(v: OIDMaskList) -> Value {
661        to_value(v).unwrap()
662    }
663}
664
665impl From<OIDMask> for OIDMaskList {
666    fn from(mask: OIDMask) -> Self {
667        OIDMaskList::new0(mask)
668    }
669}
670
671impl TryFrom<Value> for OIDMaskList {
672    type Error = Error;
673    fn try_from(value: Value) -> EResult<OIDMaskList> {
674        match value {
675            Value::Seq(_) => {
676                let masks: Vec<String> = value.deserialize_into()?;
677                Ok(OIDMaskList::from_string_list(&masks)?)
678            }
679            Value::String(s) => {
680                if s.is_empty() {
681                    Ok(<_>::default())
682                } else {
683                    Ok(OIDMaskList::from_str_list(
684                        &s.split(',').collect::<Vec<_>>(),
685                    )?)
686                }
687            }
688            _ => Err(Error::invalid_data("Expected vec or string")),
689        }
690    }
691}
692
693#[derive(Serialize, Deserialize, Eq, PartialEq, Hash, Copy, Clone, Debug)]
694#[serde(rename_all = "lowercase")]
695pub enum Op {
696    Log,
697    Developer,
698    Moderator,
699    Supervisor,
700}
701
702impl fmt::Display for Op {
703    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
704        write!(
705            f,
706            "{}",
707            match self {
708                Op::Log => "log",
709                Op::Developer => "developer",
710                Op::Moderator => "moderator",
711                Op::Supervisor => "supervisor",
712            }
713        )
714    }
715}
716
717#[derive(Serialize, Deserialize, Default, Clone, Debug)]
718struct AclItemsPvt {
719    #[serde(default)]
720    items: OIDMaskList,
721    #[serde(default)]
722    pvt: PathMaskList,
723    #[serde(default)]
724    rpvt: PathMaskList,
725}
726
727//#[derive(Serialize, Deserialize, Default, Clone, Debug)]
728//struct AclItems {
729//#[serde(default)]
730//items: OIDMaskList,
731//}
732
733#[allow(clippy::trivially_copy_pass_by_ref)]
734fn is_false(val: &bool) -> bool {
735    !val
736}
737
738/// The default ACL, used by most of services. Can be overriden with a custom one
739#[derive(Serialize, Deserialize, Clone, Debug)]
740pub struct Acl {
741    id: String,
742    #[serde(default, skip_serializing_if = "is_false")]
743    admin: bool,
744    #[serde(default)]
745    read: AclItemsPvt,
746    #[serde(default)]
747    write: AclItemsPvt,
748    #[serde(default)]
749    deny_read: AclItemsPvt,
750    #[serde(default, alias = "deny")]
751    deny_write: AclItemsPvt,
752    #[serde(default)]
753    ops: HashSet<Op>,
754    #[serde(skip_serializing_if = "Option::is_none")]
755    meta: Option<Value>,
756    from: Vec<String>,
757}
758
759impl Acl {
760    #[inline]
761    pub fn id(&self) -> &str {
762        &self.id
763    }
764    pub fn get_items_allow_deny_reading(&self) -> (Vec<String>, Vec<String>) {
765        if self.admin {
766            (vec!["#".to_owned()], vec![])
767        } else {
768            let mut allow: HashSet<String> = self.read.items.as_string_vec().into_iter().collect();
769            let allow_write: HashSet<String> =
770                self.write.items.as_string_vec().into_iter().collect();
771            allow.extend(allow_write);
772            let deny: HashSet<String> = self.deny_read.items.as_string_vec().into_iter().collect();
773            (allow.into_iter().collect(), deny.into_iter().collect())
774        }
775    }
776    #[inline]
777    pub fn check_admin(&self) -> bool {
778        self.admin
779    }
780    #[inline]
781    pub fn check_op(&self, op: Op) -> bool {
782        self.admin || self.ops.contains(&op)
783    }
784    #[inline]
785    pub fn check_item_read(&self, oid: &OID) -> bool {
786        self.admin
787            || ((self.read.items.matches(oid) || self.write.items.matches(oid))
788                && !self.deny_read.items.matches(oid))
789    }
790    #[inline]
791    pub fn check_item_mask_read(&self, mask: &OIDMask) -> bool {
792        self.admin
793            || ((self.read.items.matches_mask(mask) || self.write.items.matches_mask(mask))
794                && !self.deny_read.items.matches_mask(mask))
795    }
796    #[inline]
797    pub fn check_item_write(&self, oid: &OID) -> bool {
798        self.admin
799            || (self.write.items.matches(oid)
800                && !self.deny_write.items.matches(oid)
801                && !self.deny_read.items.matches(oid))
802    }
803    #[inline]
804    pub fn check_item_mask_write(&self, mask: &OIDMask) -> bool {
805        self.admin
806            || (self.write.items.matches_mask(mask)
807                && !self.deny_write.items.matches_mask(mask)
808                && !self.deny_read.items.matches_mask(mask))
809    }
810    #[inline]
811    pub fn check_pvt_read(&self, path: &str) -> bool {
812        self.admin || (self.read.pvt.matches(path) && !self.deny_read.pvt.matches(path))
813    }
814    #[inline]
815    pub fn check_pvt_write(&self, path: &str) -> bool {
816        self.admin
817            || (self.write.pvt.matches(path)
818                && !self.deny_write.pvt.matches(path)
819                && !self.deny_read.pvt.matches(path))
820    }
821    #[inline]
822    pub fn check_rpvt_read(&self, path: &str) -> bool {
823        if self.admin {
824            true
825        } else {
826            let mut sp = path.splitn(2, '/');
827            if let Some(node) = sp.next() {
828                if let Some(uri) = sp.next() {
829                    let stripped_uri = if let Some(u) = uri.strip_prefix("https://") {
830                        u
831                    } else if let Some(u) = uri.strip_prefix("http://") {
832                        u
833                    } else {
834                        uri
835                    };
836                    let stripped_path = format!("{node}/{stripped_uri}");
837                    self.read.rpvt.matches(&stripped_path)
838                        && !self.deny_read.rpvt.matches(&stripped_path)
839                } else {
840                    false
841                }
842            } else {
843                false
844            }
845        }
846    }
847    #[inline]
848    pub fn require_admin(&self) -> EResult<()> {
849        if self.check_admin() {
850            Ok(())
851        } else {
852            Err(Error::access("admin access required"))
853        }
854    }
855    pub fn require_op(&self, op: Op) -> EResult<()> {
856        if self.check_op(op) {
857            Ok(())
858        } else {
859            Err(Error::access(format!("operation access required: {}", op)))
860        }
861    }
862    pub fn require_item_read(&self, oid: &OID) -> EResult<()> {
863        if self.check_item_read(oid) {
864            Ok(())
865        } else {
866            Err(Error::access(format!("read access required for: {}", oid)))
867        }
868    }
869    pub fn require_item_mask_read(&self, mask: &OIDMask) -> EResult<()> {
870        if self.check_item_mask_read(mask) {
871            Ok(())
872        } else {
873            Err(Error::access(format!("read access required for: {}", mask)))
874        }
875    }
876    pub fn require_item_write(&self, oid: &OID) -> EResult<()> {
877        if self.check_item_write(oid) {
878            Ok(())
879        } else {
880            Err(Error::access(format!("write access required for: {}", oid)))
881        }
882    }
883    pub fn require_item_mask_write(&self, mask: &OIDMask) -> EResult<()> {
884        if self.check_item_mask_write(mask) {
885            Ok(())
886        } else {
887            Err(Error::access(format!(
888                "write access required for: {}",
889                mask
890            )))
891        }
892    }
893    pub fn require_pvt_read(&self, path: &str) -> EResult<()> {
894        if self.check_pvt_read(path) {
895            Ok(())
896        } else {
897            Err(Error::access(format!("read access required for: {}", path)))
898        }
899    }
900    pub fn require_pvt_write(&self, path: &str) -> EResult<()> {
901        if self.check_pvt_write(path) {
902            Ok(())
903        } else {
904            Err(Error::access(format!(
905                "write access required for: {}",
906                path
907            )))
908        }
909    }
910    pub fn require_rpvt_read(&self, path: &str) -> EResult<()> {
911        if self.check_rpvt_read(path) {
912            Ok(())
913        } else {
914            Err(Error::access(format!("read access required for: {}", path)))
915        }
916    }
917    #[inline]
918    pub fn contains_acl(&self, acl_id: &str) -> bool {
919        self.from.iter().any(|v| v == acl_id)
920    }
921    #[inline]
922    pub fn meta(&self) -> Option<&Value> {
923        self.meta.as_ref()
924    }
925    #[inline]
926    pub fn from(&self) -> &[String] {
927        &self.from
928    }
929}
930
931#[cfg(test)]
932mod tests {
933    use super::{Acl, OIDMask, OIDMaskList, PathMask, PathMaskList};
934    use crate::{ItemKind, OID};
935
936    #[test]
937    fn test_path_mask() {
938        let s = "#";
939        let mask: PathMask = s.parse().unwrap();
940        assert_eq!(s, mask.to_string());
941        assert_eq!(mask.chunks, None);
942        let s = "";
943        assert!(s.parse::<PathMask>().is_err());
944        let s = "data/#";
945        let mask: PathMask = s.parse().unwrap();
946        assert_eq!(s, mask.to_string());
947        assert_eq!(mask.chunks.unwrap(), ["data", "#"]);
948        let s = "data/tests/t1";
949        let mask: PathMask = s.parse().unwrap();
950        assert_eq!(s, mask.to_string());
951        assert_eq!(mask.chunks.unwrap(), ["data", "tests", "t1"]);
952        let s = "data/tests/*";
953        let mask: PathMask = s.parse().unwrap();
954        assert_eq!(mask.to_string(), "data/tests/#");
955        assert_eq!(mask.chunks.unwrap(), ["data", "tests", "#"]);
956        let s = "data/*/t1";
957        let mask: PathMask = s.parse().unwrap();
958        assert_ne!(s, mask.to_string());
959        assert_eq!(mask.chunks.unwrap(), ["data", "#"]);
960    }
961
962    #[test]
963    fn test_oid_mask() {
964        let s = "#";
965        let mask: OIDMask = s.parse().unwrap();
966        assert_eq!(s, mask.to_string());
967        assert_eq!(mask.path.chunks, None);
968        assert_eq!(mask.as_path(), "#");
969        let s = "";
970        assert!(s.parse::<OIDMask>().is_err());
971        let s = "sensor:";
972        assert!(s.parse::<OIDMask>().is_err());
973        let s = "sensor:data/#";
974        let mask: OIDMask = s.parse().unwrap();
975        assert_eq!(mask.as_path(), "sensor/data/#");
976        assert_eq!(s, mask.to_string());
977        assert_eq!(mask.kind.unwrap(), ItemKind::Sensor);
978        assert_eq!(mask.path.chunks.unwrap(), ["data", "#"]);
979        let s = "#:data/#";
980        assert!(s.parse::<OIDMask>().is_err());
981        let s = "+:data/tests/t1";
982        let mask: OIDMask = s.parse().unwrap();
983        assert_eq!(mask.as_path(), "+/data/tests/t1");
984        assert_eq!(s, mask.to_string());
985        assert_eq!(mask.path.chunks.unwrap(), ["data", "tests", "t1"]);
986        assert_eq!(mask.kind, None);
987        let s = "unit:data/tests/*";
988        let mask: OIDMask = s.parse().unwrap();
989        assert_eq!(mask.to_string(), "unit:data/tests/#");
990        assert_eq!(mask.path.chunks.unwrap(), ["data", "tests", "#"]);
991        assert_eq!(mask.kind.unwrap(), ItemKind::Unit);
992        let s = "data/*/t1";
993        let mask: PathMask = s.parse().unwrap();
994        assert_ne!(s, mask.to_string());
995        assert_eq!(mask.chunks.unwrap(), ["data", "#"]);
996    }
997
998    #[test]
999    fn test_path_mask_list() {
1000        let p =
1001            PathMaskList::from_str_list(&["test/tests", "+/xxx", "zzz/?/222", "abc", "a/b/#/c"]);
1002        assert!(!p.matches("test"));
1003        assert!(p.matches("test/tests"));
1004        assert!(!p.matches("test/tests2"));
1005        assert!(p.matches("aaa/xxx"));
1006        assert!(!p.matches("aaa/xxx/123"));
1007        assert!(p.matches("zzz/xxx/222"));
1008        assert!(!p.matches("zzz/xxx/222/555"));
1009        assert!(!p.matches("zzz/xxx/223"));
1010        assert!(p.matches("abc"));
1011        assert!(!p.matches("abd"));
1012        assert!(p.matches("abc/xxx"));
1013        assert!(!p.matches("abc/zzz"));
1014        assert!(p.matches("a/b/zzz"));
1015        assert!(p.matches("a/b/zzz/xxx"));
1016        let p = PathMaskList::from_str_list(&["*"]);
1017        assert!(p.matches("test"));
1018        assert!(p.matches("test/tests"));
1019        assert!(p.matches("test/tests2"));
1020        assert!(p.matches("aaa/xxx"));
1021        assert!(p.matches("aaa/xxx/123"));
1022        assert!(p.matches("zzz/xxx/222"));
1023        assert!(p.matches("zzz/xxx/222/555"));
1024        assert!(p.matches("zzz/xxx/223"));
1025        assert!(p.matches("abc"));
1026        assert!(p.matches("abd"));
1027        assert!(p.matches("abc/xxx"));
1028        assert!(p.matches("abc/zzz"));
1029        assert!(p.matches("a/b/zzz"));
1030        assert!(p.matches("a/b/zzz/xxx"));
1031    }
1032
1033    #[test]
1034    fn test_oid_mask_list() {
1035        let p = OIDMaskList::from_str_list(&[
1036            "unit:test/tests",
1037            "sensor:+/xxx",
1038            "+:zzz/?/222",
1039            "lvar:abc",
1040            "+:a/b/#/c",
1041        ])
1042        .unwrap();
1043        assert!(!p.matches(&"unit:test".parse().unwrap()));
1044        assert!(p.matches(&"unit:test/tests".parse().unwrap()));
1045        assert!(!p.matches(&"sensor:test/tests".parse().unwrap()));
1046        assert!(!p.matches(&"unit:test/tests2".parse().unwrap()));
1047        assert!(p.matches(&"sensor:aaa/xxx".parse().unwrap()));
1048        assert!(!p.matches(&"lvar:aaa/xxx".parse().unwrap()));
1049        assert!(p.matches(&"unit:zzz/xxx/222".parse().unwrap()));
1050        assert!(p.matches(&"sensor:zzz/xxx/222".parse().unwrap()));
1051        assert!(!p.matches(&"sensor:zzz/xxx/222/555".parse().unwrap()));
1052        assert!(!p.matches(&"unit:zzz/xxx/223".parse().unwrap()));
1053        assert!(p.matches(&"lvar:abc".parse().unwrap()));
1054        assert!(!p.matches(&"unit:abc".parse().unwrap()));
1055        assert!(!p.matches(&"lvar:abd".parse().unwrap()));
1056        assert!(!p.matches(&"lvar:abc/xxx".parse().unwrap()));
1057        assert!(p.matches(&"sensor:abc/xxx".parse().unwrap()));
1058        assert!(!p.matches(&"sensor:abc/zzz".parse().unwrap()));
1059        assert!(p.matches(&"unit:a/b/zzz".parse().unwrap()));
1060        assert!(p.matches(&"unit:a/b/zzz/xxx".parse().unwrap()));
1061        assert!(!p.matches(&"unit:a/c/zzz/xxx".parse().unwrap()));
1062        let p = OIDMaskList::from_str_list(&["*"]).unwrap();
1063        assert!(p.matches(&"unit:test".parse().unwrap()));
1064        assert!(p.matches(&"unit:test/tests".parse().unwrap()));
1065        assert!(p.matches(&"sensor:test/tests".parse().unwrap()));
1066        assert!(p.matches(&"unit:test/tests2".parse().unwrap()));
1067        assert!(p.matches(&"sensor:aaa/xxx".parse().unwrap()));
1068        assert!(p.matches(&"lvar:aaa/xxx".parse().unwrap()));
1069        assert!(p.matches(&"unit:zzz/xxx/222".parse().unwrap()));
1070        assert!(p.matches(&"sensor:zzz/xxx/222".parse().unwrap()));
1071        assert!(p.matches(&"sensor:zzz/xxx/222/555".parse().unwrap()));
1072        assert!(p.matches(&"unit:zzz/xxx/223".parse().unwrap()));
1073        assert!(p.matches(&"lvar:abc".parse().unwrap()));
1074        assert!(p.matches(&"unit:abc".parse().unwrap()));
1075        assert!(p.matches(&"lvar:abd".parse().unwrap()));
1076        assert!(p.matches(&"lvar:abc/xxx".parse().unwrap()));
1077        assert!(p.matches(&"sensor:abc/xxx".parse().unwrap()));
1078        assert!(p.matches(&"sensor:abc/zzz".parse().unwrap()));
1079        assert!(p.matches(&"unit:a/b/zzz".parse().unwrap()));
1080        assert!(p.matches(&"unit:a/b/zzz/xxx".parse().unwrap()));
1081        assert!(p.matches(&"unit:a/c/zzz/xxx".parse().unwrap()));
1082
1083        let p = OIDMaskList::from_str_list(&["sensor:content/#"]).unwrap();
1084        assert!(p.matches(&"sensor:content/data".parse().unwrap()));
1085        let p = OIDMaskList::from_str_list(&["sensor:+"]).unwrap();
1086        assert!(!p.matches(&"sensor:content/data".parse().unwrap()));
1087    }
1088
1089    #[test]
1090    fn test_oid_wildcard_mask() {
1091        let mask: OIDMask = "sensor:tests/#".parse().unwrap();
1092        let oid_mask: OID = mask.to_wildcard_oid().unwrap();
1093        assert!(oid_mask.is_wildcard());
1094        assert_eq!(oid_mask.to_wildcard_str("%"), "sensor:tests/%");
1095        let mask: OIDMask = "sensor:#".parse().unwrap();
1096        let oid_mask: OID = mask.to_wildcard_oid().unwrap();
1097        assert!(oid_mask.is_wildcard());
1098        assert_eq!(oid_mask.to_wildcard_str("%"), "sensor:%");
1099        let mask: OIDMask = "sensor:+/#".parse().unwrap();
1100        assert!(mask.to_wildcard_oid().is_err());
1101    }
1102
1103    #[test]
1104    fn test_rpvt_acl() {
1105        let p_allow = PathMaskList::from_str_list(&["node1/res", "node2/res/#"]);
1106        let p_deny = PathMaskList::from_str_list(&["node2/res/secret"]);
1107        let mut acl: Acl = serde_json::from_str(
1108            r#"{
1109        "id": "test",
1110        "from": ["test"]
1111        }"#,
1112        )
1113        .unwrap();
1114        acl.read.rpvt = p_allow;
1115        acl.deny_read.rpvt = p_deny;
1116        for pfx in &["", "http://", "https://"] {
1117            assert!(acl.check_rpvt_read(&format!("node1/{pfx}res")));
1118            assert!(!acl.check_rpvt_read(&format!("node2/{pfx}res")));
1119            assert!(acl.check_rpvt_read(&format!("node2/{pfx}res/res1")));
1120            assert!(!acl.check_rpvt_read(&format!("node2/{pfx}res/secret")));
1121            assert!(!acl.check_rpvt_read(&format!("node3/{pfx}res")));
1122        }
1123    }
1124}