Skip to main content

framework_web/
auth.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Clone, Debug, Default, Deserialize, Serialize)]
4#[serde(rename_all = "camelCase")]
5pub struct AuthRule {
6    pub anonymous: Option<bool>,
7    pub organizations: Option<Vec<String>>,
8    pub organizations_any: Option<Vec<String>>,
9    pub permissions: Option<Vec<String>>,
10    pub permissions_any: Option<Vec<String>>,
11    pub roles: Option<Vec<String>>,
12    pub roles_any: Option<Vec<String>>,
13    pub rules: Option<Vec<AuthRule>>,
14    pub rules_any: Option<Vec<AuthRule>>,
15}
16
17impl AuthRule {
18    pub fn anonymous() -> Self {
19        Self {
20            anonymous: Some(true),
21            ..Default::default()
22        }
23    }
24
25    pub fn login() -> Self {
26        Self {
27            anonymous: Some(false),
28            ..Default::default()
29        }
30    }
31
32    pub fn check(
33        &self,
34        organizations: Option<&[String]>,
35        permissions: Option<&[String]>,
36        roles: Option<&[String]>,
37    ) -> bool {
38        if self.anonymous == Some(true) {
39            return true;
40        }
41        self.check_organization(organizations)
42            && self.check_permission(permissions)
43            && self.check_role(roles)
44            && self.rules.as_ref().is_none_or(|rules| {
45                rules
46                    .iter()
47                    .all(|rule| rule.check(organizations, permissions, roles))
48            })
49            && self.rules_any.as_ref().is_none_or(|rules| {
50                rules
51                    .iter()
52                    .any(|rule| rule.check(organizations, permissions, roles))
53            })
54    }
55
56    pub fn check_organization(&self, value: Option<&[String]>) -> bool {
57        contains_none(&self.organizations, &self.organizations_any, value)
58    }
59
60    pub fn check_permission(&self, value: Option<&[String]>) -> bool {
61        contains_none(&self.permissions, &self.permissions_any, value)
62    }
63
64    pub fn check_role(&self, value: Option<&[String]>) -> bool {
65        contains_none(&self.roles, &self.roles_any, value)
66    }
67}
68
69fn contains_none(
70    all: &Option<Vec<String>>,
71    any: &Option<Vec<String>>,
72    value: Option<&[String]>,
73) -> bool {
74    let has = all.is_some() || any.is_some();
75    let value = match value {
76        Some(v) => v,
77        None => {
78            return !has;
79        }
80    };
81
82    if value.is_empty() && has {
83        return false;
84    }
85
86    contains_all(all, value) && contains_any(any, value)
87}
88
89fn contains_all(required: &Option<Vec<String>>, actual: &[String]) -> bool {
90    required
91        .as_ref()
92        .is_none_or(|items| items.iter().all(|item| actual.contains(item)))
93}
94
95fn contains_any(required: &Option<Vec<String>>, actual: &[String]) -> bool {
96    required
97        .as_ref()
98        .is_none_or(|items| items.iter().any(|item| actual.contains(item)))
99}