casbin/adapter/
string_adapter.rs

1// Copyright 2024 The casbin Authors. All Rights Reserved.
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 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    /// # Examples
31    ///
32    ///```rust
33    /// # use casbin::{CoreApi, DefaultModel, Enforcer, Result};
34    /// # use casbin::prelude::*;
35    /// # #[tokio::main]
36    /// # async fn main() -> Result<()> {
37    /// # let m = DefaultModel::from_file("examples/rbac_model.conf").await?;
38    /// let a = StringAdapter::new(
39    ///     r#"
40    ///     p, alice, data1, read
41    ///     p, bob, data2, write
42    ///     p, data2_admin, data2, read
43    ///     p, data2_admin, data2, write
44    ///     g, alice, data2_admin
45    ///     "#,
46    /// );
47    /// let e = Enforcer::new(m, a).await?;
48    ///
49    /// assert_eq!(true, e.enforce(("alice", "data1", "read"))?);
50    /// assert_eq!(true, e.enforce(("alice", "data2", "read"))?);
51    /// assert_eq!(true, e.enforce(("bob", "data2", "write"))?);
52    /// assert_eq!(false, e.enforce(("bob", "data1", "write"))?);
53    /// #   Ok(())
54    /// # }
55    ///
56    /// ```
57    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        // not implemented
155        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        // not implemented
167        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        // not implemented
179        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        // not implemented
191        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        // not implemented
204        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}