Skip to content

Commit 72e8f6f

Browse files
authored
Merge pull request #18 from CoLearn-Dev/policy_module
- policy module extension
2 parents c014202 + 2ba76c2 commit 72e8f6f

File tree

11 files changed

+216
-11
lines changed

11 files changed

+216
-11
lines changed

Cargo.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "colink"
3-
version = "0.1.20"
3+
version = "0.1.21"
44
edition = "2021"
55
description = "CoLink Rust SDK"
66
license = "MIT"
@@ -16,25 +16,27 @@ chrono = "0.4"
1616
ctrlc = { version = "3.2", features = ["termination"] }
1717
futures-lite = "1.12"
1818
lapin = "2.1"
19-
sha2 = "0.10"
2019
prost = "0.10"
2120
rand = { version = "0.8.4", features = ["std_rng"] }
2221
secp256k1 = { version = "0.21.2", features = ["rand-std"] }
2322
serde = { version = "1.0", features = ["derive"] }
2423
serde_json = "1.0"
24+
sha2 = "0.10"
2525
structopt = "0.3"
2626
tokio = { version = "1.18", features = ["macros", "rt-multi-thread", "rt", "fs"] }
2727
tonic = { version = "0.7", features = ["tls", "tls-roots"] }
2828
tracing = "0.1"
2929
tracing-subscriber = "0.2"
30+
uuid = { version = "0.8", features = ["v4"] }
3031

3132
[build-dependencies]
3233
prost-build = "0.10"
3334
tonic-build = "0.7"
3435

3536
[features]
36-
default = ["extensions", "remote_storage", "variable_transfer", "registry"]
37+
default = ["extensions", "remote_storage", "variable_transfer", "registry", "policy_module"]
3738
extensions = []
3839
remote_storage = ["extensions"]
3940
variable_transfer = ["extensions", "remote_storage"]
4041
registry = []
42+
policy_module = []

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ CoLink SDK helps both application adnd protocol developers access the functional
99
Add this to your Cargo.toml:
1010
```toml
1111
[dependencies]
12-
colink = "0.1.20"
12+
colink = "0.1.21"
1313
```
1414

1515
## Getting Started
@@ -74,6 +74,9 @@ cargo run --example mtls_request_core_info <address> <ca_certificate> <client_ce
7474
cargo run --example user_lock <address> <user_jwt>
7575
```
7676
```
77+
cargo run --example user_policy_module <address> <user_jwt>
78+
```
79+
```
7780
cargo run --example user_remote_storage <address> <user_jwt A> <user_jwt B> <message> # <message> is optional
7881
```
7982
```

build.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,7 @@ fn main() {
77
#[cfg(feature = "registry")]
88
prost_build::compile_protos(&["proto/colink_registry.proto"], &["proto/"])
99
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
10+
#[cfg(feature = "policy_module")]
11+
prost_build::compile_protos(&["proto/colink_policy_module.proto"], &["proto/"])
12+
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
1013
}

examples/host_import_users_and_set_registry.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use colink::extensions::registry::{Registries, Registry};
12
use colink::*;
23
use std::env;
34

examples/user_policy_module.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use colink::extensions::policy_module::{Rule, TaskFilter};
2+
use colink::CoLink;
3+
use std::env;
4+
5+
#[tokio::main]
6+
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
7+
let args = env::args().skip(1).collect::<Vec<_>>();
8+
let addr = &args[0];
9+
let jwt = &args[1];
10+
11+
let cl = CoLink::new(addr, jwt);
12+
let res = cl.policy_module_get_rules().await?;
13+
println!("{:?}", res);
14+
15+
let rule_id = cl
16+
.policy_module_add_rule(&Rule {
17+
task_filter: Some(TaskFilter {
18+
protocol_name: "greetings".to_string(),
19+
..Default::default()
20+
}),
21+
action: "approve".to_string(),
22+
priority: 1,
23+
..Default::default()
24+
})
25+
.await?;
26+
println!("rule_id: {}", rule_id);
27+
let res = cl.policy_module_get_rules().await?;
28+
println!("{:?}", res);
29+
30+
cl.policy_module_remove_rule(&rule_id).await?;
31+
let res = cl.policy_module_get_rules().await?;
32+
println!("{:?}", res);
33+
34+
Ok(())
35+
}

src/extensions.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
mod get_participant_id;
33
#[cfg(feature = "extensions")]
44
mod lock;
5+
#[cfg(feature = "policy_module")]
6+
pub mod policy_module;
57
#[cfg(feature = "extensions")]
68
mod read_or_wait;
79
#[cfg(feature = "registry")]

src/extensions/policy_module.rs

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
use crate::colink_proto::*;
2+
pub use colink_policy_module_proto::*;
3+
use prost::Message;
4+
mod colink_policy_module_proto {
5+
include!(concat!(env!("OUT_DIR"), "/colink_policy_module.rs"));
6+
}
7+
8+
type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
9+
10+
impl crate::application::CoLink {
11+
pub async fn policy_module_start(&self) -> Result<(), Error> {
12+
let lock = self.lock("_policy_module:settings").await?;
13+
let (mut settings, timestamp): (Settings, i64) = match self
14+
.read_entries(&[StorageEntry {
15+
key_name: "_policy_module:settings".to_string(),
16+
..Default::default()
17+
}])
18+
.await
19+
{
20+
Ok(res) => (
21+
prost::Message::decode(&*res[0].payload)?,
22+
get_timestamp(&res[0].key_path),
23+
),
24+
Err(_) => (Default::default(), 0),
25+
};
26+
if settings.enable {
27+
self.unlock(lock).await?;
28+
return self.wait_for_applying(timestamp).await; // Wait for the current timestamp to be applied.
29+
}
30+
settings.enable = true;
31+
let mut payload = vec![];
32+
settings.encode(&mut payload).unwrap();
33+
let timestamp = get_timestamp(
34+
&self
35+
.update_entry("_policy_module:settings", &payload)
36+
.await?,
37+
);
38+
self.unlock(lock).await?;
39+
let participants = vec![Participant {
40+
user_id: self.get_user_id()?,
41+
role: "local".to_string(),
42+
}];
43+
self.run_task("policy_module", Default::default(), &participants, false)
44+
.await?;
45+
self.wait_for_applying(timestamp).await
46+
}
47+
48+
pub async fn policy_module_stop(&self) -> Result<(), Error> {
49+
let lock = self.lock("_policy_module:settings").await?;
50+
let mut settings: Settings = match self.read_entry("_policy_module:settings").await {
51+
Ok(res) => prost::Message::decode(&*res)?,
52+
Err(_) => Default::default(),
53+
};
54+
if !settings.enable {
55+
self.unlock(lock).await?;
56+
return Ok(()); // Return directly here because we only release the lock after the policy module truly stopped.
57+
}
58+
settings.enable = false;
59+
let mut payload = vec![];
60+
settings.encode(&mut payload).unwrap();
61+
let timestamp = get_timestamp(
62+
&self
63+
.update_entry("_policy_module:settings", &payload)
64+
.await?,
65+
);
66+
let res = self.wait_for_applying(timestamp).await;
67+
self.unlock(lock).await?; // Unlock after the policy module truly stopped.
68+
res
69+
}
70+
71+
pub async fn policy_module_get_rules(&self) -> Result<Vec<Rule>, Error> {
72+
let settings: Settings = match self.read_entry("_policy_module:settings").await {
73+
Ok(res) => prost::Message::decode(&*res)?,
74+
Err(_) => Default::default(),
75+
};
76+
Ok(settings.rules)
77+
}
78+
79+
pub async fn policy_module_add_rule(&self, rule: &Rule) -> Result<String, Error> {
80+
let lock = self.lock("_policy_module:settings").await?;
81+
let mut settings: Settings = match self.read_entry("_policy_module:settings").await {
82+
Ok(res) => prost::Message::decode(&*res)?,
83+
Err(_) => Default::default(),
84+
};
85+
let rule_id = uuid::Uuid::new_v4().to_string();
86+
let mut rule = rule.clone();
87+
rule.rule_id = rule_id.clone();
88+
settings.rules.push(rule);
89+
let mut payload = vec![];
90+
settings.encode(&mut payload).unwrap();
91+
let timestamp = get_timestamp(
92+
&self
93+
.update_entry("_policy_module:settings", &payload)
94+
.await?,
95+
);
96+
self.unlock(lock).await?;
97+
if settings.enable {
98+
self.wait_for_applying(timestamp).await?;
99+
}
100+
Ok(rule_id)
101+
}
102+
103+
pub async fn policy_module_remove_rule(&self, rule_id: &str) -> Result<(), Error> {
104+
let lock = self.lock("_policy_module:settings").await?;
105+
let mut settings: Settings = match self.read_entry("_policy_module:settings").await {
106+
Ok(res) => prost::Message::decode(&*res)?,
107+
Err(_) => Default::default(),
108+
};
109+
settings.rules.retain(|x| x.rule_id != rule_id);
110+
let mut payload = vec![];
111+
settings.encode(&mut payload).unwrap();
112+
let timestamp = get_timestamp(
113+
&self
114+
.update_entry("_policy_module:settings", &payload)
115+
.await?,
116+
);
117+
self.unlock(lock).await?;
118+
if settings.enable {
119+
self.wait_for_applying(timestamp).await?;
120+
}
121+
Ok(())
122+
}
123+
124+
async fn wait_for_applying(&self, timestamp: i64) -> Result<(), Error> {
125+
let key = "_policy_module:applied_settings_timestamp";
126+
let start_timestamp = match self
127+
.read_entries(&[StorageEntry {
128+
key_name: key.to_string(),
129+
..Default::default()
130+
}])
131+
.await
132+
{
133+
Ok(res) => {
134+
let applied_settings_timestamp =
135+
i64::from_le_bytes(<[u8; 8]>::try_from(&*res[0].payload).unwrap());
136+
if applied_settings_timestamp >= timestamp {
137+
return Ok(());
138+
}
139+
get_timestamp(&res[0].key_path) + 1
140+
}
141+
Err(_) => 0,
142+
};
143+
let queue_name = self.subscribe(key, Some(start_timestamp)).await?;
144+
let mut subscriber = self.new_subscriber(&queue_name).await?;
145+
loop {
146+
let data = subscriber.get_next().await?;
147+
let message: SubscriptionMessage = Message::decode(&*data).unwrap();
148+
if message.change_type != "delete" {
149+
let applied_settings_timestamp =
150+
i64::from_le_bytes(<[u8; 8]>::try_from(&*message.payload).unwrap());
151+
if applied_settings_timestamp >= timestamp {
152+
break;
153+
}
154+
}
155+
}
156+
self.unsubscribe(&queue_name).await?;
157+
Ok(())
158+
}
159+
}
160+
161+
fn get_timestamp(key_path: &str) -> i64 {
162+
let pos = key_path.rfind('@').unwrap();
163+
key_path[pos + 1..].parse().unwrap()
164+
}

src/extensions/registry.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use crate::colink_proto::*;
22
pub use colink_registry_proto::{Registries, Registry};
33
use prost::Message;
44
mod colink_registry_proto {
5-
#![allow(clippy::derive_partial_eq_without_eq)]
65
include!(concat!(env!("OUT_DIR"), "/colink_registry.rs"));
76
}
87

src/extensions/remote_storage.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use colink_remote_storage_proto::*;
33
use prost::Message;
44

55
mod colink_remote_storage_proto {
6-
#![allow(clippy::derive_partial_eq_without_eq)]
76
include!(concat!(env!("OUT_DIR"), "/colink_remote_storage.rs"));
87
}
98

src/extensions/variable_transfer.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use crate::colink_proto::*;
22
use colink_remote_storage::*;
33
use prost::Message;
44
mod colink_remote_storage {
5-
#![allow(clippy::derive_partial_eq_without_eq)]
65
include!(concat!(env!("OUT_DIR"), "/colink_remote_storage.rs"));
76
}
87

0 commit comments

Comments
 (0)