casbin/adapter/
file_adapter.rs

1use crate::{
2    adapter::{Adapter, Filter},
3    error::{AdapterError, ModelError},
4    model::Model,
5    util::parse_csv_line,
6    Result,
7};
8
9#[cfg(feature = "runtime-async-std")]
10use async_std::{
11    fs::File as file,
12    io::prelude::*,
13    io::{
14        BufReader as ioBufReader, Error as ioError, ErrorKind as ioErrorKind,
15    },
16    path::Path as ioPath,
17    prelude::*,
18};
19
20#[cfg(feature = "runtime-tokio")]
21use std::path::Path as ioPath;
22#[cfg(feature = "runtime-tokio")]
23use tokio::{
24    fs::File as file,
25    io::{AsyncBufReadExt, AsyncWriteExt, BufReader as ioBufReader},
26};
27
28use async_trait::async_trait;
29use std::fmt::Write;
30
31pub struct FileAdapter<P> {
32    file_path: P,
33    is_filtered: bool,
34}
35
36type LoadPolicyFileHandler = fn(String, &mut dyn Model);
37type LoadFilteredPolicyFileHandler<'a> =
38    fn(String, &mut dyn Model, f: &Filter<'a>) -> bool;
39
40impl<P> FileAdapter<P>
41where
42    P: AsRef<ioPath> + Send + Sync,
43{
44    pub fn new(p: P) -> FileAdapter<P> {
45        FileAdapter {
46            file_path: p,
47            is_filtered: false,
48        }
49    }
50
51    pub fn new_filtered_adapter(p: P) -> FileAdapter<P> {
52        FileAdapter {
53            file_path: p,
54            is_filtered: true,
55        }
56    }
57
58    async fn load_policy_file(
59        &mut self,
60        m: &mut dyn Model,
61        handler: LoadPolicyFileHandler,
62    ) -> Result<()> {
63        let f = file::open(&self.file_path).await?;
64        let mut lines = ioBufReader::new(f).lines();
65        #[cfg(feature = "runtime-async-std")]
66        while let Some(line) = lines.next().await {
67            handler(line?, m)
68        }
69
70        #[cfg(feature = "runtime-tokio")]
71        while let Some(line) = lines.next_line().await? {
72            handler(line, m)
73        }
74
75        Ok(())
76    }
77
78    async fn load_filtered_policy_file<'a>(
79        &self,
80        m: &mut dyn Model,
81        filter: Filter<'a>,
82        handler: LoadFilteredPolicyFileHandler<'a>,
83    ) -> Result<bool> {
84        let f = file::open(&self.file_path).await?;
85        let mut lines = ioBufReader::new(f).lines();
86
87        let mut is_filtered = false;
88        #[cfg(feature = "runtime-async-std")]
89        while let Some(line) = lines.next().await {
90            if handler(line?, m, &filter) {
91                is_filtered = true;
92            }
93        }
94
95        #[cfg(feature = "runtime-tokio")]
96        while let Some(line) = lines.next_line().await? {
97            if handler(line, m, &filter) {
98                is_filtered = true;
99            }
100        }
101
102        Ok(is_filtered)
103    }
104
105    async fn save_policy_file(&self, text: String) -> Result<()> {
106        let mut file = file::create(&self.file_path).await?;
107        file.write_all(text.as_bytes()).await?;
108        Ok(())
109    }
110}
111
112#[async_trait]
113impl<P> Adapter for FileAdapter<P>
114where
115    P: AsRef<ioPath> + Send + Sync,
116{
117    async fn load_policy(&mut self, m: &mut dyn Model) -> Result<()> {
118        self.is_filtered = false;
119        self.load_policy_file(m, load_policy_line).await?;
120        Ok(())
121    }
122
123    async fn load_filtered_policy<'a>(
124        &mut self,
125        m: &mut dyn Model,
126        f: Filter<'a>,
127    ) -> Result<()> {
128        self.is_filtered = self
129            .load_filtered_policy_file(m, f, load_filtered_policy_line)
130            .await?;
131
132        Ok(())
133    }
134
135    async fn save_policy(&mut self, m: &mut dyn Model) -> Result<()> {
136        if self.file_path.as_ref().as_os_str().is_empty() {
137            return Err(std::io::Error::other(
138                "save policy failed, file path is empty",
139            )
140            .into());
141        }
142
143        let mut policies = String::new();
144        let ast_map = m.get_model().get("p").ok_or_else(|| {
145            ModelError::P("Missing policy definition in conf file".to_owned())
146        })?;
147
148        for (ptype, ast) in ast_map {
149            for rule in ast.get_policy() {
150                writeln!(policies, "{}, {}", ptype, rule.join(","))
151                    .map_err(|e| AdapterError(e.into()))?;
152            }
153        }
154
155        if let Some(ast_map) = m.get_model().get("g") {
156            for (ptype, ast) in ast_map {
157                for rule in ast.get_policy() {
158                    writeln!(policies, "{}, {}", ptype, rule.join(","))
159                        .map_err(|e| AdapterError(e.into()))?;
160                }
161            }
162        }
163
164        self.save_policy_file(policies).await?;
165        Ok(())
166    }
167
168    async fn clear_policy(&mut self) -> Result<()> {
169        self.save_policy_file(String::new()).await?;
170        Ok(())
171    }
172
173    async fn add_policy(
174        &mut self,
175        _sec: &str,
176        _ptype: &str,
177        _rule: Vec<String>,
178    ) -> Result<bool> {
179        // this api shouldn't implement, just for convenience
180        Ok(true)
181    }
182
183    async fn add_policies(
184        &mut self,
185        _sec: &str,
186        _ptype: &str,
187        _rules: Vec<Vec<String>>,
188    ) -> Result<bool> {
189        // this api shouldn't implement, just for convenience
190        Ok(true)
191    }
192
193    async fn remove_policy(
194        &mut self,
195        _sec: &str,
196        _ptype: &str,
197        _rule: Vec<String>,
198    ) -> Result<bool> {
199        // this api shouldn't implement, just for convenience
200        Ok(true)
201    }
202
203    async fn remove_policies(
204        &mut self,
205        _sec: &str,
206        _ptype: &str,
207        _rule: Vec<Vec<String>>,
208    ) -> Result<bool> {
209        // this api shouldn't implement, just for convenience
210        Ok(true)
211    }
212
213    async fn remove_filtered_policy(
214        &mut self,
215        _sec: &str,
216        _ptype: &str,
217        _field_index: usize,
218        _field_values: Vec<String>,
219    ) -> Result<bool> {
220        // this api shouldn't implement, just for convenience
221        Ok(true)
222    }
223
224    fn is_filtered(&self) -> bool {
225        self.is_filtered
226    }
227}
228
229fn load_policy_line(line: String, m: &mut dyn Model) {
230    if line.is_empty() || line.starts_with('#') {
231        return;
232    }
233
234    if let Some(tokens) = parse_csv_line(line) {
235        let key = &tokens[0];
236
237        if let Some(ref sec) = key.chars().next().map(|x| x.to_string()) {
238            if let Some(ast_map) = m.get_mut_model().get_mut(sec) {
239                if let Some(ast) = ast_map.get_mut(key) {
240                    ast.policy.insert(tokens[1..].to_vec());
241                }
242            }
243        }
244    }
245}
246
247fn load_filtered_policy_line(
248    line: String,
249    m: &mut dyn Model,
250    f: &Filter<'_>,
251) -> bool {
252    if line.is_empty() || line.starts_with('#') {
253        return false;
254    }
255
256    if let Some(tokens) = parse_csv_line(line) {
257        let key = &tokens[0];
258
259        let mut is_filtered = false;
260        if let Some(ref sec) = key.chars().next().map(|x| x.to_string()) {
261            if sec == "p" {
262                for (i, rule) in f.p.iter().enumerate() {
263                    if !rule.is_empty() && rule != &tokens[i + 1] {
264                        is_filtered = true;
265                    }
266                }
267            }
268            if sec == "g" {
269                for (i, rule) in f.g.iter().enumerate() {
270                    if !rule.is_empty() && rule != &tokens[i + 1] {
271                        is_filtered = true;
272                    }
273                }
274            }
275            if !is_filtered {
276                if let Some(ast_map) = m.get_mut_model().get_mut(sec) {
277                    if let Some(ast) = ast_map.get_mut(key) {
278                        ast.policy.insert(tokens[1..].to_vec());
279                    }
280                }
281            }
282        }
283
284        is_filtered
285    } else {
286        false
287    }
288}