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}