Skip to content

Commit c48bd06

Browse files
authored
feat: add some subcommands like enforce_ex() & disable semantic-release-action at forks (#6)
* feat: implement completion and enforceEx subcommand * chore: cargo clippy * feat: disable semantic-release-action at forks
1 parent ebc782e commit c48bd06

File tree

5 files changed

+171
-61
lines changed

5 files changed

+171
-61
lines changed

.github/workflows/release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ on:
1313

1414
jobs:
1515
release:
16+
if: github.repository == 'casbin-rs/casbin-rust-cli'
1617
uses: semantic-release-action/rust/.github/workflows/release-binary.yml@v5
1718
secrets:
1819
cargo-registry-token: ${{ secrets.CARGO_TOKEN }}

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@ target/
1414
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
1515
# and can be added to the global gitignore or merged into this file. For a more nuclear
1616
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
17-
#.idea/
17+
#.idea/
18+
19+
.vscode

Cargo.lock

Lines changed: 45 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ repository = "https://github.com/casbin-rs/casbin-rust-cli"
1212
readme= "README.md"
1313

1414
[dependencies]
15-
casbin = { version = "2.7.0", features = ["explain", "logging"] }
15+
casbin = { version = "2.8.0", features = ["explain", "logging"] }
1616
clap = { version = "4.5.27", features = ["derive"] }
17-
serde_json = "1.0.137"
17+
serde_json = "1.0.138"
1818
tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread"] }
19+
clap_complete_command = "0.6.1"

src/main.rs

Lines changed: 119 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,168 @@
11
use casbin::prelude::*;
2-
use clap::Parser;
2+
use clap::{CommandFactory, Parser, Subcommand};
33
use serde_json::json;
44

5-
#[derive(Parser, Debug)]
5+
#[derive(Parser, Debug, Clone)]
66
#[command(author, version, about, long_about)]
77
struct Args {
88
/// The command to execute
9-
#[arg(value_enum)]
9+
#[command(subcommand)]
1010
command: Cmd,
11-
12-
/// The path of the model file or model text
13-
#[arg(short, long)]
14-
model: String,
15-
16-
/// The path of the policy file or policy text
17-
#[arg(short, long)]
18-
policy: String,
19-
20-
/// The arguments for the enforcer
21-
command_args: Vec<String>,
2211
}
2312

24-
#[derive(Debug, Clone, clap::ValueEnum)]
13+
#[derive(Subcommand, Debug, Clone)]
2514
#[clap(rename_all = "camelCase")]
2615
pub enum Cmd {
16+
/// Generate the autocompletion script for the specified shell
17+
Completion {
18+
/// The shell to generate the completions for
19+
#[arg(value_enum)]
20+
shell: clap_complete_command::Shell,
21+
},
2722
/// Check permissions
28-
Enforce,
29-
// /// Check permissions and get which policy it is
30-
// EnforceEx,
23+
Enforce {
24+
/// The path of the model file or model text
25+
#[arg(short, long)]
26+
model: String,
27+
28+
/// The path of the policy file or policy text
29+
#[arg(short, long)]
30+
policy: String,
31+
32+
/// The arguments for the enforcer
33+
command_args: Vec<String>,
34+
},
35+
/// Check permissions and get which policy it is
36+
EnforceEx {
37+
/// The path of the model file or model text
38+
#[arg(short, long)]
39+
model: String,
40+
41+
/// The path of the policy file or policy text
42+
#[arg(short, long)]
43+
policy: String,
44+
45+
/// The arguments for the enforcer
46+
command_args: Vec<String>,
47+
},
3148
}
3249

3350
#[tokio::main]
3451
async fn main() {
3552
let args = Args::parse();
3653

37-
let model = DefaultModel::from_file(args.model)
54+
match args.command {
55+
Cmd::Enforce {
56+
model,
57+
policy,
58+
command_args,
59+
} => {
60+
let model = DefaultModel::from_file(model)
61+
.await
62+
.expect("failed to load model");
63+
let adapter = FileAdapter::new(policy);
64+
65+
let e = Enforcer::new(model, adapter)
66+
.await
67+
.expect("failed to create enforcer");
68+
69+
let allow = e.enforce(command_args).expect("failed to enforce");
70+
71+
let response = json!({
72+
"allow": allow,
73+
"explain": Vec::<String>::new(),
74+
});
75+
76+
println!("{}", response);
77+
}
78+
Cmd::EnforceEx {
79+
model,
80+
policy,
81+
command_args,
82+
} => {
83+
let model = DefaultModel::from_file(model)
84+
.await
85+
.expect("failed to load model");
86+
let adapter = FileAdapter::new(policy);
87+
88+
let e = Enforcer::new(model, adapter)
89+
.await
90+
.expect("failed to create enforcer");
91+
92+
let (allow, explain) = e.enforce_ex(command_args).expect("failed to enforce");
93+
94+
let response = json!({
95+
"allow": allow,
96+
"explain": explain.first().unwrap_or(&Vec::<String>::new()),
97+
});
98+
99+
println!("{}", response);
100+
}
101+
Cmd::Completion { shell } => {
102+
shell.generate(&mut Args::command(), &mut std::io::stdout());
103+
}
104+
};
105+
}
106+
107+
#[tokio::test]
108+
async fn test_enforce() {
109+
let model = DefaultModel::from_file("examples/basic_model.conf".to_owned())
38110
.await
39111
.expect("failed to load model");
40-
let adapter = FileAdapter::new(args.policy);
112+
let adapter = FileAdapter::new("examples/basic_policy.csv".to_owned());
41113

42114
let e = Enforcer::new(model, adapter)
43115
.await
44116
.expect("failed to create enforcer");
45-
let allow = e.enforce(args.command_args).expect("failed to enforce");
117+
118+
let allow = e
119+
.enforce(vec![
120+
"alice".to_owned(),
121+
"data1".to_owned(),
122+
"read".to_owned(),
123+
])
124+
.expect("failed to enforce");
46125

47126
let response = json!({
48127
"allow": allow,
49128
"explain": Vec::<String>::new(),
50129
});
51130

52-
println!("{}", response);
131+
let expected = json!({
132+
"allow": true,
133+
"explain": [],
134+
});
135+
136+
assert_eq!(response, expected);
53137
}
54138

55139
#[tokio::test]
56-
async fn test_enforce() {
57-
let args = Args {
58-
command: Cmd::Enforce,
59-
model: "examples/basic_model.conf".to_owned(),
60-
policy: "examples/basic_policy.csv".to_owned(),
61-
command_args: vec!["alice".to_owned(), "data1".to_owned(), "read".to_owned()],
62-
};
63-
64-
let model = DefaultModel::from_file(args.model)
140+
async fn test_enforce_explain() {
141+
let model = DefaultModel::from_file("examples/basic_model.conf".to_owned())
65142
.await
66143
.expect("failed to load model");
67-
let adapter = FileAdapter::new(args.policy);
144+
let adapter = FileAdapter::new("examples/basic_policy.csv".to_owned());
68145

69146
let e = Enforcer::new(model, adapter)
70147
.await
71148
.expect("failed to create enforcer");
72-
let allow = e.enforce(args.command_args).expect("failed to enforce");
149+
150+
let (allow, explain) = e
151+
.enforce_ex(vec![
152+
"alice".to_owned(),
153+
"data1".to_owned(),
154+
"read".to_owned(),
155+
])
156+
.expect("failed to enforce");
73157

74158
let response = json!({
75159
"allow": allow,
76-
"explain": Vec::<String>::new(),
160+
"explain": explain.first().unwrap_or(&Vec::<String>::new()),
77161
});
78162

79163
let expected = json!({
80164
"allow": true,
81-
"explain": [],
165+
"explain": ["alice", "data1", "read"],
82166
});
83167

84168
assert_eq!(response, expected);

0 commit comments

Comments
 (0)