1use crate::{
16 error::{AdapterError, ModelError},
17 util::parse_csv_line,
18 Adapter, Filter, Model, Result,
19};
20use async_trait::async_trait;
21use std::fmt::Write;
22
23#[derive(Default)]
24pub struct StringAdapter {
25 pub(crate) policy: String,
26 is_filtered: bool,
27}
28
29impl StringAdapter {
30 pub fn new(s: impl ToString) -> Self {
58 Self {
59 policy: s.to_string(),
60 is_filtered: false,
61 }
62 }
63}
64
65#[async_trait]
66impl Adapter for StringAdapter {
67 async fn load_policy(&mut self, m: &mut dyn Model) -> Result<()> {
68 let policies = self.policy.split("\n");
69 for line in policies {
70 load_policy_line(line, m);
71 }
72 Ok(())
73 }
74
75 async fn load_filtered_policy<'a>(
76 &mut self,
77 m: &mut dyn Model,
78 f: Filter<'a>,
79 ) -> Result<()> {
80 let policies = self.policy.split("\n");
81 for line in policies {
82 if let Some(tokens) = parse_csv_line(line) {
83 let sec = &tokens[0];
84 let ptype = &tokens[1];
85 let rule = tokens[1..].to_vec().clone();
86 let mut is_filtered = false;
87
88 if sec == "p" {
89 for (i, r) in f.p.iter().enumerate() {
90 if !r.is_empty() && r != &rule[i + 1] {
91 is_filtered = true;
92 }
93 }
94 }
95 if sec == "g" {
96 for (i, r) in f.g.iter().enumerate() {
97 if !r.is_empty() && r != &rule[i + 1] {
98 is_filtered = true;
99 }
100 }
101 }
102 if !is_filtered {
103 if let Some(ast_map) = m.get_mut_model().get_mut(sec) {
104 if let Some(ast) = ast_map.get_mut(ptype) {
105 ast.get_mut_policy().insert(rule);
106 }
107 }
108 } else {
109 self.is_filtered = true;
110 }
111 }
112 }
113 Ok(())
114 }
115
116 async fn save_policy(&mut self, m: &mut dyn Model) -> Result<()> {
117 let mut policies = String::new();
118 let ast_map = m.get_model().get("p").ok_or_else(|| {
119 ModelError::P("Missing policy definition in conf file".to_owned())
120 })?;
121
122 for (ptype, ast) in ast_map {
123 for rule in ast.get_policy() {
124 writeln!(policies, "{}, {}", ptype, rule.join(", "))
125 .map_err(|e| AdapterError(e.into()))?;
126 }
127 }
128
129 if let Some(ast_map) = m.get_model().get("g") {
130 for (ptype, ast) in ast_map {
131 for rule in ast.get_policy() {
132 writeln!(policies, "{}, {}", ptype, rule.join(", "))
133 .map_err(|e| AdapterError(e.into()))?;
134 }
135 }
136 }
137
138 self.policy = policies;
139 Ok(())
140 }
141
142 async fn clear_policy(&mut self) -> Result<()> {
143 self.policy.clear();
144 self.is_filtered = false;
145 Ok(())
146 }
147
148 async fn add_policy(
149 &mut self,
150 _sec: &str,
151 _ptype: &str,
152 _rule: Vec<String>,
153 ) -> Result<bool> {
154 Err(crate::Error::AdapterError(AdapterError(Box::new(
156 AdapterError("not implemented".to_string().into()),
157 ))))
158 }
159
160 async fn add_policies(
161 &mut self,
162 _sec: &str,
163 _ptype: &str,
164 _rules: Vec<Vec<String>>,
165 ) -> Result<bool> {
166 Err(crate::Error::AdapterError(AdapterError(Box::new(
168 AdapterError("not implemented".to_string().into()),
169 ))))
170 }
171
172 async fn remove_policy(
173 &mut self,
174 _sec: &str,
175 _ptype: &str,
176 _rule: Vec<String>,
177 ) -> Result<bool> {
178 Err(crate::Error::AdapterError(AdapterError(Box::new(
180 AdapterError("not implemented".to_string().into()),
181 ))))
182 }
183
184 async fn remove_policies(
185 &mut self,
186 _sec: &str,
187 _ptype: &str,
188 _rule: Vec<Vec<String>>,
189 ) -> Result<bool> {
190 Err(crate::Error::AdapterError(AdapterError(Box::new(
192 AdapterError("not implemented".to_string().into()),
193 ))))
194 }
195
196 async fn remove_filtered_policy(
197 &mut self,
198 _sec: &str,
199 _ptype: &str,
200 _field_index: usize,
201 _field_values: Vec<String>,
202 ) -> Result<bool> {
203 Err(crate::Error::AdapterError(AdapterError(Box::new(
205 AdapterError("not implemented".to_string().into()),
206 ))))
207 }
208
209 fn is_filtered(&self) -> bool {
210 self.is_filtered
211 }
212}
213
214fn load_policy_line(line: &str, m: &mut dyn Model) {
215 if line.is_empty() || line.starts_with('#') {
216 return;
217 }
218
219 if let Some(tokens) = parse_csv_line(line) {
220 let key = &tokens[0];
221
222 if let Some(ref sec) = key.chars().next().map(|x| x.to_string()) {
223 if let Some(ast_map) = m.get_mut_model().get_mut(sec) {
224 if let Some(ast) = ast_map.get_mut(key) {
225 ast.policy.insert(tokens[1..].to_vec());
226 }
227 }
228 }
229 }
230}
231
232#[cfg(test)]
233mod tests {
234 use super::StringAdapter;
235 use crate::{Adapter, CoreApi, Filter};
236 use crate::{DefaultModel, Enforcer};
237
238 #[cfg(target_arch = "wasm32")]
239 use wasm_bindgen_test::*;
240
241 #[cfg_attr(
242 all(not(target_arch = "wasm32"), feature = "runtime-async-std"),
243 async_std::test
244 )]
245 #[cfg_attr(
246 all(not(target_arch = "wasm32"), feature = "runtime-tokio"),
247 tokio::test
248 )]
249 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
250 async fn test_load_policy() {
251 let policy = "p, alice, data1, read\np, bob, data2, write";
252 let mut adapter = StringAdapter::new(policy);
253 let mut model = DefaultModel::from_str(include_str!(
254 "../../examples/rbac_model.conf"
255 ))
256 .await
257 .unwrap();
258
259 adapter.load_policy(&mut model).await.unwrap();
260 let enforcer = Enforcer::new(model, adapter).await.unwrap();
261
262 assert!(enforcer.enforce(("alice", "data1", "read")).unwrap());
263 assert!(enforcer.enforce(("bob", "data2", "write")).unwrap());
264 assert!(!enforcer.enforce(("alice", "data2", "read")).unwrap());
265 }
266
267 #[cfg_attr(
268 all(not(target_arch = "wasm32"), feature = "runtime-async-std"),
269 async_std::test
270 )]
271 #[cfg_attr(
272 all(not(target_arch = "wasm32"), feature = "runtime-tokio"),
273 tokio::test
274 )]
275 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
276 async fn test_save_policy() {
277 let policy = "p, alice, data1, read\np, bob, data2, write";
278 let mut adapter = StringAdapter::new(policy);
279 let mut model = DefaultModel::from_str(include_str!(
280 "../../examples/rbac_model.conf"
281 ))
282 .await
283 .unwrap();
284
285 adapter.load_policy(&mut model).await.unwrap();
286 adapter.save_policy(&mut model).await.unwrap();
287
288 assert_eq!(
289 adapter.policy,
290 "p, alice, data1, read\np, bob, data2, write\n"
291 );
292 }
293
294 #[cfg_attr(
295 all(not(target_arch = "wasm32"), feature = "runtime-async-std"),
296 async_std::test
297 )]
298 #[cfg_attr(
299 all(not(target_arch = "wasm32"), feature = "runtime-tokio"),
300 tokio::test
301 )]
302 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
303 async fn test_clear_policy() {
304 let policy = "p, alice, data1, read\np, bob, data2, write";
305 let mut adapter = StringAdapter::new(policy);
306 let mut model = DefaultModel::from_str(include_str!(
307 "../../examples/rbac_model.conf"
308 ))
309 .await
310 .unwrap();
311
312 adapter.load_policy(&mut model).await.unwrap();
313 adapter.clear_policy().await.unwrap();
314
315 assert_eq!(adapter.policy, "");
316 assert!(!adapter.is_filtered);
317 }
318
319 #[cfg_attr(
320 all(not(target_arch = "wasm32"), feature = "runtime-async-std"),
321 async_std::test
322 )]
323 #[cfg_attr(
324 all(not(target_arch = "wasm32"), feature = "runtime-tokio"),
325 tokio::test
326 )]
327 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
328 async fn test_is_filtered() {
329 let policy = "p, alice, data1, read\np, bob, data2, write";
330 let mut adapter = StringAdapter::new(policy);
331 let mut model = DefaultModel::from_str(include_str!(
332 "../../examples/rbac_model.conf"
333 ))
334 .await
335 .unwrap();
336
337 let filter = Filter {
338 p: vec!["alice"],
339 g: vec![],
340 };
341
342 adapter
343 .load_filtered_policy(&mut model, filter)
344 .await
345 .unwrap();
346
347 assert!(adapter.is_filtered());
348 }
349}