Skip to main content

rocketmq_auth/authorization/model/
policy_entry.rs

1// Copyright 2023 The RocketMQ Rust Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use rocketmq_security_api::Action;
16
17use super::environment::Environment;
18use super::resource::Resource;
19use crate::authorization::enums::decision::Decision;
20
21#[derive(Clone, Debug, PartialEq, Eq)]
22pub struct PolicyEntry {
23    resource: Resource,
24    actions: Vec<Action>,
25    environment: Option<Environment>,
26    decision: Decision,
27}
28
29impl PolicyEntry {
30    pub fn of(resource: Resource, actions: Vec<Action>, environment: Option<Environment>, decision: Decision) -> Self {
31        Self {
32            resource,
33            actions,
34            environment,
35            decision,
36        }
37    }
38
39    pub fn update_entry(&mut self, actions: Vec<Action>, environment: Option<Environment>, decision: Decision) {
40        self.actions = actions;
41        self.environment = environment;
42        self.decision = decision;
43    }
44
45    pub fn is_match_resource(&self, resource: &Resource) -> bool {
46        self.resource.is_match(resource)
47    }
48
49    /// Check whether the provided actions match this policy's actions.
50    /// Returns false if this policy has no actions configured.
51    pub fn is_match_action(&self, actions: &[Action]) -> bool {
52        if self.actions.is_empty() {
53            return false;
54        }
55        if actions.contains(&Action::Any) {
56            return true;
57        }
58        // any action in `actions` present in self.actions OR self.actions contains ALL
59        let contains_all = self.actions.contains(&Action::All);
60        actions
61            .iter()
62            .any(|a| self.actions.iter().any(|sa| sa == a) || contains_all)
63    }
64
65    pub fn is_match_environment(&self, environment: &Environment) -> bool {
66        match &self.environment {
67            None => true,
68            Some(env) => env.is_match(environment),
69        }
70    }
71
72    pub fn to_resource_str(&self) -> Option<String> {
73        self.resource.resource_key()
74    }
75
76    pub fn to_actions_str(&self) -> Option<Vec<String>> {
77        if self.actions.is_empty() {
78            return None;
79        }
80        Some(
81            self.actions
82                .iter()
83                .map(|a| a.name().to_string())
84                .collect::<Vec<String>>(),
85        )
86    }
87
88    // getters/setters
89    pub fn resource(&self) -> &Resource {
90        &self.resource
91    }
92
93    pub fn set_resource(&mut self, r: Resource) {
94        self.resource = r;
95    }
96
97    pub fn actions(&self) -> &Vec<Action> {
98        &self.actions
99    }
100
101    pub fn set_actions(&mut self, a: Vec<Action>) {
102        self.actions = a;
103    }
104
105    pub fn environment(&self) -> Option<&Environment> {
106        self.environment.as_ref()
107    }
108
109    pub fn set_environment(&mut self, e: Option<Environment>) {
110        self.environment = e;
111    }
112
113    pub fn decision(&self) -> Decision {
114        self.decision
115    }
116
117    pub fn set_decision(&mut self, d: Decision) {
118        self.decision = d;
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use rocketmq_security_api::Action;
125    use rocketmq_security_api::ResourceType;
126
127    use super::*;
128    use crate::authorization::enums::decision::Decision;
129    use crate::authorization::model::resource::Resource;
130
131    #[test]
132    fn test_of_and_to_strings() {
133        let r = Resource::of(
134            ResourceType::Topic,
135            Some("foo".to_string()),
136            rocketmq_security_api::ResourcePattern::Literal,
137        );
138        let p = PolicyEntry::of(r.clone(), vec![Action::Pub, Action::Sub], None, Decision::Allow);
139        assert_eq!(p.to_resource_str(), Some("Topic:foo".to_string()));
140        assert_eq!(p.to_actions_str().unwrap(), vec!["Pub".to_string(), "Sub".to_string()]);
141        assert_eq!(p.decision(), Decision::Allow);
142    }
143
144    #[test]
145    fn test_is_match_action_any_and_all() {
146        let r = Resource::of(
147            ResourceType::Topic,
148            Some("x".to_string()),
149            rocketmq_security_api::ResourcePattern::Literal,
150        );
151        let p = PolicyEntry::of(r, vec![Action::Pub, Action::Sub], None, Decision::Allow);
152        assert!(p.is_match_action(&[Action::Pub]));
153        assert!(!p.is_match_action(&[Action::Create]));
154
155        // ANY in incoming actions -> true
156        assert!(p.is_match_action(&[Action::Any]));
157
158        // ALL in policy -> matches any
159        let r2 = Resource::of(
160            ResourceType::Topic,
161            Some("x".to_string()),
162            rocketmq_security_api::ResourcePattern::Literal,
163        );
164        let p2 = PolicyEntry::of(r2, vec![Action::All], None, Decision::Allow);
165        assert!(p2.is_match_action(&[Action::Create]));
166    }
167
168    #[test]
169    fn test_is_match_resource_and_environment() {
170        let rule = PolicyEntry::of(
171            Resource::of(
172                ResourceType::Topic,
173                Some("pre".to_string()),
174                rocketmq_security_api::ResourcePattern::Prefixed,
175            ),
176            vec![Action::Pub],
177            Some(crate::authorization::model::environment::Environment::of("1.2.3.4").unwrap()),
178            Decision::Allow,
179        );
180
181        let res = Resource::of(
182            ResourceType::Topic,
183            Some("prefix_val".to_string()),
184            rocketmq_security_api::ResourcePattern::Literal,
185        );
186        assert!(rule.is_match_resource(&res));
187        assert!(
188            rule.is_match_environment(&crate::authorization::model::environment::Environment::of("1.2.3.4").unwrap())
189        );
190        assert!(
191            !rule.is_match_environment(&crate::authorization::model::environment::Environment::of("8.8.8.8").unwrap())
192        );
193    }
194}