1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! Usage example:
//!
//! Here we declare a policy that allows "alice" to create a host if and only if the following conditions are met:
//! - The host's nameLabel set contains "example_domain". This is created via regular expressions on the name from
//! the `initialize_host_patterns` function.
//! - The host's IP address is within the network "10.0.0.0/24".
//! - The host's name contains the letter 'n'
//!
//! Note that we do not require the host to have the nameLabel "webserver" for "alice" to create it.
//!
//! ```rust
//! use regex::Regex;
//! use std::sync::Arc;
//! use treetop_core::{Action, AttrValue, PolicyEngine, Request, Decision, User, Principal, Resource, RegexLabeler, LabelRegistryBuilder};
//! use sha2::{Digest, Sha256};
//!
//! let policies = r#"
//! permit (
//! principal == User::"alice",
//! action == Action::"create_host",
//! resource is Host
//! ) when {
//! resource.nameLabels.contains("in_domain") &&
//! resource.ip.isInRange(ip("10.0.0.0/24")) &&
//! resource.name like "*n*"
//! };
//! "#;
//!
//! // Used to create attributes for hosts based on their names.
//! let patterns = vec![
//! ("in_domain".to_string(), Regex::new(r"example\.com$").unwrap()),
//! ("webserver".to_string(), Regex::new(r"^web-\d+").unwrap()),
//! ];
//! let label_registry = LabelRegistryBuilder::new()
//! .add_labeler(Arc::new(RegexLabeler::new(
//! "Host",
//! "name",
//! "nameLabels",
//! patterns.into_iter().collect(),
//! )))
//! .build();
//!
//! let engine = PolicyEngine::new_from_str(&policies).unwrap()
//! .with_label_registry(label_registry);
//!
//! let request = Request {
//! principal: Principal::User(User::new("alice", None, None)), // No groups, no namespace
//! action: Action::new("create_host", None), // Action is not in a namespace
//! resource: Resource::new("Host", "hostname.example.com")
//! .with_attr("name", AttrValue::String("hostname.example.com".into()))
//! .with_attr("ip", AttrValue::Ip("10.0.0.1".into()))
//! };
//!
//! let decision = engine.evaluate(&request).unwrap();
//! assert!(matches!(decision, Decision::Allow { .. }));
//!
//! // List all of alice's policies
//! let alice_policies = engine.list_policies_for_user("alice", &[], &[]).unwrap();
//! // This value is also seralizable to JSON
//! let json = serde_json::to_string(&alice_policies).unwrap();
//!
//! // Check that the policy running is the expected version
//! let expected_hash = Sha256::digest(policies)
//! .iter()
//! .fold(String::with_capacity(64), |mut s, b| {
//! use std::fmt::Write;
//! write!(s, "{b:02x}").unwrap();
//! s
//! });
//! assert_eq!(engine.current_version().hash, expected_hash);
//!
//! ```
//!
//! ## Thread-Safe Sharing
//!
//! For multithreaded applications, wrap `PolicyEngine` in `Arc` to share it across threads:
//!
//! ```rust,no_run
//! use std::sync::Arc;
//! use std::thread;
//! # use treetop_core::{PolicyEngine, Request, Principal, User, Action, Resource, Decision};
//! # let engine_base = PolicyEngine::new_from_str("permit(principal,action,resource);").unwrap();
//!
//! let engine = Arc::new(engine_base);
//! let engine_clone = Arc::clone(&engine);
//!
//! let handle = thread::spawn(move || {
//! // Evaluate policies in a background thread
//! let request = Request {
//! principal: Principal::User(User::new("user", None, None)),
//! action: Action::new("read", None),
//! resource: Resource::new("Document", "doc1"),
//! };
//! let _decision = engine_clone.evaluate(&request);
//! });
//!
//! handle.join().unwrap();
//! ```
//!
pub use build_info;
pub use Schema;
pub use PolicyEngine;
pub use PolicyError;
pub use ;
pub use ;
pub use ;
pub use ;