1use async_trait::async_trait;
19use casbin::{error::AdapterError, Adapter, Error as CasbinError, Filter, Model, Result};
20use std::sync::{
21 atomic::{AtomicBool, Ordering},
22 Arc,
23};
24
25use crate::{error::*, models::*};
26
27use crate::actions as adapter;
28
29#[cfg(feature = "mysql")]
30use sqlx::mysql::MySqlPoolOptions;
31#[cfg(feature = "postgres")]
32use sqlx::postgres::PgPoolOptions;
33#[cfg(feature = "sqlite")]
34use sqlx::sqlite::SqlitePoolOptions;
35
36#[derive(Clone)]
37pub struct SqlxAdapter {
38 pool: adapter::ConnectionPool,
39 is_filtered: Arc<AtomicBool>,
40}
41
42impl<'a> SqlxAdapter {
45 pub async fn new<U: Into<String>>(url: U, pool_size: u32) -> Result<Self> {
46 #[cfg(feature = "postgres")]
47 let pool = PgPoolOptions::new()
48 .max_connections(pool_size)
49 .connect(&url.into())
50 .await
51 .map_err(|err| CasbinError::from(AdapterError(Box::new(Error::SqlxError(err)))))?;
52
53 #[cfg(feature = "mysql")]
54 let pool = MySqlPoolOptions::new()
55 .max_connections(pool_size)
56 .connect(&url.into())
57 .await
58 .map_err(|err| CasbinError::from(AdapterError(Box::new(Error::SqlxError(err)))))?;
59
60 #[cfg(feature = "sqlite")]
61 let pool = SqlitePoolOptions::new()
62 .max_connections(pool_size)
63 .connect(&url.into())
64 .await
65 .map_err(|err| CasbinError::from(AdapterError(Box::new(Error::SqlxError(err)))))?;
66
67 adapter::new(&pool).await.map(|_| Self {
68 pool,
69 is_filtered: Arc::new(AtomicBool::new(false)),
70 })
71 }
72
73 pub async fn new_with_pool(pool: adapter::ConnectionPool) -> Result<Self> {
74 adapter::new(&pool).await.map(|_| Self {
75 pool,
76 is_filtered: Arc::new(AtomicBool::new(false)),
77 })
78 }
79
80 pub(crate) fn save_policy_line(
81 &self,
82 ptype: &'a str,
83 rule: &'a [String],
84 ) -> Option<NewCasbinRule<'a>> {
85 if ptype.trim().is_empty() || rule.is_empty() {
86 return None;
87 }
88
89 let mut new_rule = NewCasbinRule {
90 ptype,
91 v0: "",
92 v1: "",
93 v2: "",
94 v3: "",
95 v4: "",
96 v5: "",
97 };
98
99 new_rule.v0 = &rule[0];
100
101 if rule.len() > 1 {
102 new_rule.v1 = &rule[1];
103 }
104
105 if rule.len() > 2 {
106 new_rule.v2 = &rule[2];
107 }
108
109 if rule.len() > 3 {
110 new_rule.v3 = &rule[3];
111 }
112
113 if rule.len() > 4 {
114 new_rule.v4 = &rule[4];
115 }
116
117 if rule.len() > 5 {
118 new_rule.v5 = &rule[5];
119 }
120
121 Some(new_rule)
122 }
123
124 pub(crate) fn load_policy_line(&self, casbin_rule: &CasbinRule) -> Option<Vec<String>> {
125 if casbin_rule.ptype.chars().next().is_some() {
126 return self.normalize_policy(casbin_rule);
127 }
128
129 None
130 }
131
132 fn normalize_policy(&self, casbin_rule: &CasbinRule) -> Option<Vec<String>> {
133 let mut result = vec![
134 &casbin_rule.v0,
135 &casbin_rule.v1,
136 &casbin_rule.v2,
137 &casbin_rule.v3,
138 &casbin_rule.v4,
139 &casbin_rule.v5,
140 ];
141
142 while let Some(last) = result.last() {
143 if last.is_empty() {
144 result.pop();
145 } else {
146 break;
147 }
148 }
149
150 if !result.is_empty() {
151 return Some(result.iter().map(|&x| x.to_owned()).collect());
152 }
153
154 None
155 }
156}
157
158#[async_trait]
159impl Adapter for SqlxAdapter {
160 async fn load_policy(&mut self, m: &mut dyn Model) -> Result<()> {
161 let rules = adapter::load_policy(&self.pool).await?;
162
163 for casbin_rule in &rules {
164 let rule = self.load_policy_line(casbin_rule);
165
166 if let Some(ref sec) = casbin_rule.ptype.chars().next().map(|x| x.to_string()) {
167 if let Some(t1) = m.get_mut_model().get_mut(sec) {
168 if let Some(t2) = t1.get_mut(&casbin_rule.ptype) {
169 if let Some(rule) = rule {
170 t2.get_mut_policy().insert(rule);
171 }
172 }
173 }
174 }
175 }
176
177 Ok(())
178 }
179
180 async fn load_filtered_policy<'a>(&mut self, m: &mut dyn Model, f: Filter<'a>) -> Result<()> {
181 let rules = adapter::load_filtered_policy(&self.pool, &f).await?;
182 self.is_filtered.store(true, Ordering::SeqCst);
183
184 for casbin_rule in &rules {
185 if let Some(policy) = self.normalize_policy(casbin_rule) {
186 if let Some(ref sec) = casbin_rule.ptype.chars().next().map(|x| x.to_string()) {
187 if let Some(t1) = m.get_mut_model().get_mut(sec) {
188 if let Some(t2) = t1.get_mut(&casbin_rule.ptype) {
189 t2.get_mut_policy().insert(policy);
190 }
191 }
192 }
193 }
194 }
195
196 Ok(())
197 }
198
199 async fn save_policy(&mut self, m: &mut dyn Model) -> Result<()> {
200 let mut rules = vec![];
201
202 if let Some(ast_map) = m.get_model().get("p") {
203 for (ptype, ast) in ast_map {
204 let new_rules = ast
205 .get_policy()
206 .into_iter()
207 .filter_map(|x| self.save_policy_line(ptype, x));
208
209 rules.extend(new_rules);
210 }
211 }
212
213 if let Some(ast_map) = m.get_model().get("g") {
214 for (ptype, ast) in ast_map {
215 let new_rules = ast
216 .get_policy()
217 .into_iter()
218 .filter_map(|x| self.save_policy_line(ptype, x));
219
220 rules.extend(new_rules);
221 }
222 }
223 adapter::save_policy(&self.pool, rules).await
224 }
225
226 async fn add_policy(&mut self, _sec: &str, ptype: &str, rule: Vec<String>) -> Result<bool> {
227 if let Some(new_rule) = self.save_policy_line(ptype, rule.as_slice()) {
228 return adapter::add_policy(&self.pool, new_rule).await;
229 }
230
231 Ok(false)
232 }
233
234 async fn add_policies(
235 &mut self,
236 _sec: &str,
237 ptype: &str,
238 rules: Vec<Vec<String>>,
239 ) -> Result<bool> {
240 let new_rules = rules
241 .iter()
242 .filter_map(|x| self.save_policy_line(ptype, x))
243 .collect::<Vec<NewCasbinRule>>();
244
245 adapter::add_policies(&self.pool, new_rules).await
246 }
247
248 async fn remove_policy(&mut self, _sec: &str, pt: &str, rule: Vec<String>) -> Result<bool> {
249 adapter::remove_policy(&self.pool, pt, rule).await
250 }
251
252 async fn remove_policies(
253 &mut self,
254 _sec: &str,
255 pt: &str,
256 rules: Vec<Vec<String>>,
257 ) -> Result<bool> {
258 adapter::remove_policies(&self.pool, pt, rules).await
259 }
260
261 async fn remove_filtered_policy(
262 &mut self,
263 _sec: &str,
264 pt: &str,
265 field_index: usize,
266 field_values: Vec<String>,
267 ) -> Result<bool> {
268 if field_index <= 5 && !field_values.is_empty() && field_values.len() + field_index <= 6 {
269 adapter::remove_filtered_policy(&self.pool, pt, field_index, field_values).await
270 } else {
271 Ok(false)
272 }
273 }
274
275 async fn clear_policy(&mut self) -> Result<()> {
276 adapter::clear_policy(&self.pool).await
277 }
278
279 fn is_filtered(&self) -> bool {
280 self.is_filtered.load(Ordering::SeqCst)
281 }
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287
288 fn to_owned(v: Vec<&str>) -> Vec<String> {
289 v.into_iter().map(|x| x.to_owned()).collect()
290 }
291
292 fn test_database_url() -> String {
293 #[cfg(feature = "postgres")]
294 let default_url = "postgres://casbin_rs:casbin_rs@localhost:5432/casbin";
295
296 #[cfg(feature = "mysql")]
297 let default_url = "mysql://casbin_rs:casbin_rs@localhost:3306/casbin";
298
299 #[cfg(feature = "sqlite")]
300 let default_url = "sqlite:casbin.db";
301
302 std::env::var("DATABASE_URL").unwrap_or_else(|_| default_url.to_owned())
303 }
304
305 #[cfg_attr(feature = "runtime-async-std", async_std::test)]
306 #[cfg_attr(feature = "runtime-tokio", tokio::test(flavor = "multi_thread"))]
307 async fn test_create() {
308 use casbin::prelude::*;
309
310 let m = DefaultModel::from_file("examples/rbac_model.conf")
311 .await
312 .unwrap();
313
314 let adapter = {
315 #[cfg(feature = "postgres")]
316 {
317 SqlxAdapter::new(test_database_url(), 8).await.unwrap()
318 }
319
320 #[cfg(feature = "mysql")]
321 {
322 SqlxAdapter::new(test_database_url(), 8).await.unwrap()
323 }
324
325 #[cfg(feature = "sqlite")]
326 {
327 SqlxAdapter::new(test_database_url(), 8).await.unwrap()
328 }
329 };
330
331 assert!(Enforcer::new(m, adapter).await.is_ok());
332 }
333
334 #[cfg_attr(feature = "runtime-async-std", async_std::test)]
335 #[cfg_attr(feature = "runtime-tokio", tokio::test(flavor = "multi_thread"))]
336 async fn test_create_with_pool() {
337 use casbin::prelude::*;
338
339 let m = DefaultModel::from_file("examples/rbac_model.conf")
340 .await
341 .unwrap();
342 let pool = {
343 #[cfg(feature = "postgres")]
344 {
345 let url = test_database_url();
346 PgPoolOptions::new()
347 .max_connections(8)
348 .connect(&url)
349 .await
350 .unwrap()
351 }
352
353 #[cfg(feature = "mysql")]
354 {
355 let url = test_database_url();
356 MySqlPoolOptions::new()
357 .max_connections(8)
358 .connect(&url)
359 .await
360 .unwrap()
361 }
362
363 #[cfg(feature = "sqlite")]
364 {
365 let url = test_database_url();
366 SqlitePoolOptions::new()
367 .max_connections(8)
368 .connect(&url)
369 .await
370 .unwrap()
371 }
372 };
373
374 let adapter = SqlxAdapter::new_with_pool(pool).await.unwrap();
375
376 assert!(Enforcer::new(m, adapter).await.is_ok());
377 }
378
379 #[cfg_attr(feature = "runtime-async-std", async_std::test)]
380 #[cfg_attr(feature = "runtime-tokio", tokio::test(flavor = "multi_thread"))]
381 async fn test_adapter() {
382 use casbin::prelude::*;
383
384 let file_adapter = FileAdapter::new("examples/rbac_policy.csv");
385
386 let m = DefaultModel::from_file("examples/rbac_model.conf")
387 .await
388 .unwrap();
389
390 let mut e = Enforcer::new(m, file_adapter).await.unwrap();
391 #[cfg(feature = "postgres")]
392 let postgres_pool = {
393 let url = test_database_url();
394 PgPoolOptions::new()
395 .max_connections(8)
396 .connect(&url)
397 .await
398 .unwrap()
399 };
400 let mut adapter = {
401 #[cfg(feature = "postgres")]
402 {
403 SqlxAdapter::new_with_pool(postgres_pool.clone())
404 .await
405 .unwrap()
406 }
407
408 #[cfg(feature = "mysql")]
409 {
410 SqlxAdapter::new(test_database_url(), 8).await.unwrap()
411 }
412
413 #[cfg(feature = "sqlite")]
414 {
415 SqlxAdapter::new(test_database_url(), 8).await.unwrap()
416 }
417 };
418
419 assert!(adapter.save_policy(e.get_mut_model()).await.is_ok());
420
421 #[cfg(feature = "postgres")]
422 {
423 let model = DefaultModel::from_file("examples/rbac_model.conf")
424 .await
425 .unwrap();
426 let reloaded_adapter = SqlxAdapter::new_with_pool(postgres_pool).await.unwrap();
427 let reloaded = Enforcer::new(model, reloaded_adapter).await.unwrap();
428
429 assert!(reloaded.enforce(("alice", "data1", "read")).unwrap());
430 }
431
432 assert!(adapter
433 .remove_policy("", "p", to_owned(vec!["alice", "data1", "read"]))
434 .await
435 .unwrap());
436 assert!(adapter
437 .remove_policy("", "p", to_owned(vec!["bob", "data2", "write"]))
438 .await
439 .is_ok());
440 assert!(adapter
441 .remove_policy("", "p", to_owned(vec!["data2_admin", "data2", "read"]))
442 .await
443 .is_ok());
444 assert!(adapter
445 .remove_policy("", "p", to_owned(vec!["data2_admin", "data2", "write"]))
446 .await
447 .is_ok());
448 assert!(adapter
449 .remove_policy("", "g", to_owned(vec!["alice", "data2_admin"]))
450 .await
451 .is_ok());
452
453 assert!(adapter
454 .add_policy("", "p", to_owned(vec!["alice", "data1", "read"]))
455 .await
456 .is_ok());
457 assert!(adapter
458 .add_policy("", "p", to_owned(vec!["bob", "data2", "write"]))
459 .await
460 .is_ok());
461 assert!(adapter
462 .add_policy("", "p", to_owned(vec!["data2_admin", "data2", "read"]))
463 .await
464 .is_ok());
465 assert!(adapter
466 .add_policy("", "p", to_owned(vec!["data2_admin", "data2", "write"]))
467 .await
468 .is_ok());
469
470 assert!(adapter
471 .remove_policies(
472 "",
473 "p",
474 vec![
475 to_owned(vec!["alice", "data1", "read"]),
476 to_owned(vec!["bob", "data2", "write"]),
477 to_owned(vec!["data2_admin", "data2", "read"]),
478 to_owned(vec!["data2_admin", "data2", "write"]),
479 ]
480 )
481 .await
482 .is_ok());
483
484 assert!(adapter
485 .add_policies(
486 "",
487 "p",
488 vec![
489 to_owned(vec!["alice", "data1", "read"]),
490 to_owned(vec!["bob", "data2", "write"]),
491 to_owned(vec!["data2_admin", "data2", "read"]),
492 to_owned(vec!["data2_admin", "data2", "write"]),
493 ]
494 )
495 .await
496 .is_ok());
497
498 assert!(adapter
499 .add_policy("", "g", to_owned(vec!["alice", "data2_admin"]))
500 .await
501 .is_ok());
502
503 assert!(adapter
504 .remove_policy("", "p", to_owned(vec!["alice", "data1", "read"]))
505 .await
506 .is_ok());
507 assert!(adapter
508 .remove_policy("", "p", to_owned(vec!["bob", "data2", "write"]))
509 .await
510 .is_ok());
511 assert!(adapter
512 .remove_policy("", "p", to_owned(vec!["data2_admin", "data2", "read"]))
513 .await
514 .is_ok());
515 assert!(adapter
516 .remove_policy("", "p", to_owned(vec!["data2_admin", "data2", "write"]))
517 .await
518 .is_ok());
519 assert!(adapter
520 .remove_policy("", "g", to_owned(vec!["alice", "data2_admin"]))
521 .await
522 .is_ok());
523
524 assert!(!adapter
525 .remove_policy(
526 "",
527 "g",
528 to_owned(vec!["alice", "data2_admin", "not_exists"])
529 )
530 .await
531 .unwrap());
532
533 assert!(adapter
534 .add_policy("", "g", to_owned(vec!["alice", "data2_admin"]))
535 .await
536 .is_ok());
537 assert!(adapter
538 .add_policy("", "g", to_owned(vec!["alice", "data2_admin"]))
539 .await
540 .is_err());
541
542 assert!(!adapter
543 .remove_filtered_policy(
544 "",
545 "g",
546 0,
547 to_owned(vec!["alice", "data2_admin", "not_exists"]),
548 )
549 .await
550 .unwrap());
551
552 assert!(adapter
553 .remove_filtered_policy("", "g", 0, to_owned(vec!["alice", "data2_admin"]))
554 .await
555 .unwrap());
556
557 assert!(adapter
558 .add_policy(
559 "",
560 "g",
561 to_owned(vec!["alice", "data2_admin", "domain1", "domain2"]),
562 )
563 .await
564 .is_ok());
565 assert!(adapter
566 .remove_filtered_policy(
567 "",
568 "g",
569 1,
570 to_owned(vec!["data2_admin", "domain1", "domain2"]),
571 )
572 .await
573 .unwrap());
574
575 assert!(adapter
577 .add_policy("", "g", to_owned(vec!["carol", "data1_admin"]),)
578 .await
579 .is_ok());
580 assert!(adapter
581 .remove_filtered_policy("", "g", 0, to_owned(vec!["carol"]),)
582 .await
583 .unwrap());
584 assert_eq!(Vec::<String>::new(), e.get_roles_for_user("carol", None));
585
586 assert!(adapter
595 .add_policy("", "p", to_owned(vec!["alice_rfp", "book_rfp", "read_rfp"]),)
596 .await
597 .is_ok());
598 assert!(adapter
599 .add_policy("", "p", to_owned(vec!["bob_rfp", "book_rfp", "read_rfp"]),)
600 .await
601 .is_ok());
602 assert!(adapter
603 .add_policy("", "p", to_owned(vec!["bob_rfp", "book_rfp", "write_rfp"]),)
604 .await
605 .is_ok());
606 assert!(adapter
607 .add_policy("", "p", to_owned(vec!["alice_rfp", "pen_rfp", "get_rfp"]),)
608 .await
609 .is_ok());
610 assert!(adapter
611 .add_policy("", "p", to_owned(vec!["bob_rfp", "pen_rfp", "get_rfp"]),)
612 .await
613 .is_ok());
614 assert!(adapter
615 .add_policy(
616 "",
617 "p",
618 to_owned(vec!["alice_rfp", "pencil_rfp", "get_rfp"]),
619 )
620 .await
621 .is_ok());
622
623 assert!(adapter
625 .remove_filtered_policy("", "p", 1, to_owned(vec!["book_rfp"]),)
626 .await
627 .unwrap());
628
629 assert!(adapter
632 .remove_filtered_policy("", "p", 0, to_owned(vec!["alice_rfp", "", "get_rfp"]),)
633 .await
634 .unwrap());
635
636 let mut e = Enforcer::new(
638 "examples/rbac_with_domains_model.conf",
639 "examples/rbac_with_domains_policy.csv",
640 )
641 .await
642 .unwrap();
643
644 assert!(adapter.save_policy(e.get_mut_model()).await.is_ok());
645 e.set_adapter(adapter).await.unwrap();
646
647 let filter = Filter {
648 p: vec!["", "domain1"],
649 g: vec!["", "", "domain1"],
650 };
651
652 e.load_filtered_policy(filter).await.unwrap();
653 assert!(e.enforce(("alice", "domain1", "data1", "read")).unwrap());
654 assert!(e.enforce(("alice", "domain1", "data1", "write")).unwrap());
655 assert!(!e.enforce(("alice", "domain1", "data2", "read")).unwrap());
656 assert!(!e.enforce(("alice", "domain1", "data2", "write")).unwrap());
657 assert!(!e.enforce(("bob", "domain2", "data2", "read")).unwrap());
658 assert!(!e.enforce(("bob", "domain2", "data2", "write")).unwrap());
659 }
660}