string_adapter/
lib.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
15mod utils;
16use async_trait::async_trait;
17use casbin::{
18    error::{AdapterError, ModelError},
19    Adapter, Filter, Model, Result,
20};
21use std::fmt::Write;
22use utils::*;
23
24#[derive(Default)]
25pub struct StringAdapter {
26    policy: String,
27    is_filtered: bool,
28}
29
30impl StringAdapter {
31    pub fn new(s: impl ToString) -> Self {
32        Self {
33            policy: s.to_string(),
34            is_filtered: false,
35        }
36    }
37}
38
39#[async_trait]
40impl Adapter for StringAdapter {
41    async fn load_policy(&mut self, m: &mut dyn Model) -> Result<()> {
42        let policies = self.policy.split("\n");
43        for line in policies {
44            load_policy_line(line, m);
45        }
46        Ok(())
47    }
48
49    async fn load_filtered_policy<'a>(&mut self, m: &mut dyn Model, f: Filter<'a>) -> Result<()> {
50        let policies = self.policy.split("\n");
51        for line in policies {
52            if let Some(tokens) = parse_csv_line(line) {
53                let sec = &tokens[0];
54                let ptype = &tokens[1];
55                let rule = tokens[1..].to_vec().clone();
56                let mut is_filtered = false;
57
58                if sec == "p" {
59                    for (i, r) in f.p.iter().enumerate() {
60                        if !r.is_empty() && r != &rule[i + 1] {
61                            is_filtered = true;
62                        }
63                    }
64                }
65                if sec == "g" {
66                    for (i, r) in f.g.iter().enumerate() {
67                        if !r.is_empty() && r != &rule[i + 1] {
68                            is_filtered = true;
69                        }
70                    }
71                }
72                if !is_filtered {
73                    if let Some(ast_map) = m.get_mut_model().get_mut(sec) {
74                        if let Some(ast) = ast_map.get_mut(ptype) {
75                            ast.get_mut_policy().insert(rule);
76                        }
77                    }
78                } else {
79                    self.is_filtered = true;
80                }
81            }
82        }
83        Ok(())
84    }
85
86    async fn save_policy(&mut self, m: &mut dyn Model) -> Result<()> {
87        let mut policies = String::new();
88        let ast_map = m
89            .get_model()
90            .get("p")
91            .ok_or_else(|| ModelError::P("Missing policy definition in conf file".to_owned()))?;
92
93        for (ptype, ast) in ast_map {
94            for rule in ast.get_policy() {
95                writeln!(policies, "{}, {}", ptype, rule.join(", "))
96                    .map_err(|e| AdapterError(e.into()))?;
97            }
98        }
99
100        if let Some(ast_map) = m.get_model().get("g") {
101            for (ptype, ast) in ast_map {
102                for rule in ast.get_policy() {
103                    writeln!(policies, "{}, {}", ptype, rule.join(", "))
104                        .map_err(|e| AdapterError(e.into()))?;
105                }
106            }
107        }
108
109        self.policy = policies;
110        Ok(())
111    }
112
113    async fn clear_policy(&mut self) -> Result<()> {
114        self.policy.clear();
115        self.is_filtered = false;
116        Ok(())
117    }
118
119    async fn add_policy(&mut self, _sec: &str, _ptype: &str, _rule: Vec<String>) -> Result<bool> {
120        // not implemented
121        Err(casbin::Error::AdapterError(AdapterError(
122            "not implemented".to_string().into(),
123        )))
124    }
125
126    async fn add_policies(
127        &mut self,
128        _sec: &str,
129        _ptype: &str,
130        _rules: Vec<Vec<String>>,
131    ) -> Result<bool> {
132        // not implemented
133        Err(casbin::Error::AdapterError(AdapterError(
134            "not implemented".to_string().into(),
135        )))
136    }
137
138    async fn remove_policy(
139        &mut self,
140        _sec: &str,
141        _ptype: &str,
142        _rule: Vec<String>,
143    ) -> Result<bool> {
144        // not implemented
145        Err(casbin::Error::AdapterError(AdapterError(
146            "not implemented".to_string().into(),
147        )))
148    }
149
150    async fn remove_policies(
151        &mut self,
152        _sec: &str,
153        _ptype: &str,
154        _rule: Vec<Vec<String>>,
155    ) -> Result<bool> {
156        // not implemented
157        Err(casbin::Error::AdapterError(AdapterError(
158            "not implemented".to_string().into(),
159        )))
160    }
161
162    async fn remove_filtered_policy(
163        &mut self,
164        _sec: &str,
165        _ptype: &str,
166        _field_index: usize,
167        _field_values: Vec<String>,
168    ) -> Result<bool> {
169        // not implemented
170        Err(casbin::Error::AdapterError(AdapterError(
171            "not implemented".to_string().into(),
172        )))
173    }
174
175    fn is_filtered(&self) -> bool {
176        self.is_filtered
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use crate::StringAdapter;
183    use casbin::{Adapter, CoreApi, Filter};
184    use casbin::{DefaultModel, Enforcer};
185
186    #[cfg_attr(feature = "runtime-async-std", async_std::test)]
187    #[cfg_attr(feature = "runtime-tokio", tokio::test)]
188    async fn test_load_policy() {
189        let policy = "p, alice, data1, read\np, bob, data2, write";
190        let mut adapter = StringAdapter::new(policy);
191        let mut model = DefaultModel::from_file("tests/rbac_model.conf")
192            .await
193            .unwrap();
194
195        adapter.load_policy(&mut model).await.unwrap();
196        let enforcer = Enforcer::new(model, adapter).await.unwrap();
197
198        assert!(enforcer.enforce(("alice", "data1", "read")).unwrap());
199        assert!(enforcer.enforce(("bob", "data2", "write")).unwrap());
200        assert!(!enforcer.enforce(("alice", "data2", "read")).unwrap());
201    }
202
203    #[cfg_attr(feature = "runtime-async-std", async_std::test)]
204    #[cfg_attr(feature = "runtime-tokio", tokio::test)]
205    async fn test_save_policy() {
206        let policy = "p, alice, data1, read\np, bob, data2, write";
207        let mut adapter = StringAdapter::new(policy);
208        let mut model = DefaultModel::from_file("tests/rbac_model.conf")
209            .await
210            .unwrap();
211
212        adapter.load_policy(&mut model).await.unwrap();
213        adapter.save_policy(&mut model).await.unwrap();
214
215        assert_eq!(
216            adapter.policy,
217            "p, alice, data1, read\np, bob, data2, write\n"
218        );
219    }
220
221    #[cfg_attr(feature = "runtime-async-std", async_std::test)]
222    #[cfg_attr(feature = "runtime-tokio", tokio::test)]
223    async fn test_clear_policy() {
224        let policy = "p, alice, data1, read\np, bob, data2, write";
225        let mut adapter = StringAdapter::new(policy);
226        let mut model = DefaultModel::from_file("tests/rbac_model.conf")
227            .await
228            .unwrap();
229
230        adapter.load_policy(&mut model).await.unwrap();
231        adapter.clear_policy().await.unwrap();
232
233        assert_eq!(adapter.policy, "");
234        assert!(!adapter.is_filtered);
235    }
236
237    #[cfg_attr(feature = "runtime-async-std", async_std::test)]
238    #[cfg_attr(feature = "runtime-tokio", tokio::test)]
239    async fn test_is_filtered() {
240        let policy = "p, alice, data1, read\np, bob, data2, write";
241        let mut adapter = StringAdapter::new(policy);
242        let mut model = DefaultModel::from_file("tests/rbac_model.conf")
243            .await
244            .unwrap();
245
246        let filter = Filter {
247            p: vec!["alice"],
248            g: vec![],
249        };
250
251        adapter
252            .load_filtered_policy(&mut model, filter)
253            .await
254            .unwrap();
255
256        assert!(adapter.is_filtered());
257    }
258}