1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3
4pub use ipnet::IpNet;
5
6pub mod acl;
7pub mod error;
8pub mod utils;
9
10pub use acl::{AclClassification, HttpAcl, HttpAclBuilder, HttpRequestMethod};
11pub use utils::IntoIpRange;
12
13#[cfg(test)]
14mod tests {
15 use std::net::IpAddr;
16 use std::sync::Arc;
17
18 use ipnet::IpNet;
19
20 use super::{AclClassification, HttpAclBuilder};
21
22 #[test]
23 fn acl() {
24 let acl = HttpAclBuilder::new()
25 .add_allowed_host("example.com".to_string())
26 .unwrap()
27 .add_allowed_host("example.org".to_string())
28 .unwrap()
29 .add_denied_host("example.net".to_string())
30 .unwrap()
31 .add_allowed_port_range(8080..=8080)
32 .unwrap()
33 .add_denied_port_range(8443..=8443)
34 .unwrap()
35 .add_allowed_ip_range("1.0.0.0/8".parse::<IpNet>().unwrap())
36 .unwrap()
37 .add_denied_ip_range("9.0.0.0/8".parse::<IpNet>().unwrap())
38 .unwrap()
39 .try_build()
40 .unwrap();
41
42 assert!(acl.is_host_allowed("example.com").is_allowed());
43 assert!(acl.is_host_allowed("example.org").is_allowed());
44 assert!(!acl.is_host_allowed("example.net").is_allowed());
45 assert!(acl.is_port_allowed(8080).is_allowed());
46 assert!(!acl.is_port_allowed(8443).is_allowed());
47 assert!(acl.is_ip_allowed(&"1.1.1.1".parse().unwrap()).is_allowed());
48 assert!(acl.is_ip_allowed(&"9.9.9.9".parse().unwrap()).is_denied());
49 assert!(
50 acl.is_ip_allowed(&"192.168.1.1".parse().unwrap())
51 .is_denied()
52 );
53 }
54
55 #[test]
56 fn host_acl() {
57 let acl = HttpAclBuilder::new()
58 .add_allowed_host("example.com".to_string())
59 .unwrap()
60 .add_allowed_host("example.org".to_string())
61 .unwrap()
62 .add_denied_host("example.net".to_string())
63 .unwrap()
64 .try_build()
65 .unwrap();
66
67 assert!(acl.is_host_allowed("example.com").is_allowed());
68 assert!(acl.is_host_allowed("example.org").is_allowed());
69 assert!(!acl.is_host_allowed("example.net").is_allowed());
70 }
71
72 #[test]
73 fn port_acl() {
74 let acl = HttpAclBuilder::new()
75 .clear_allowed_port_ranges()
76 .add_allowed_port_range(8080..=8080)
77 .unwrap()
78 .add_denied_port_range(8441..=8443)
79 .unwrap()
80 .try_build()
81 .unwrap();
82
83 assert!(acl.is_port_allowed(80).is_denied());
84 assert!(acl.is_port_allowed(8080).is_allowed());
85 assert!(acl.is_port_allowed(8440).is_denied());
86 assert!(!acl.is_port_allowed(8441).is_allowed());
87 assert!(!acl.is_port_allowed(8442).is_allowed());
88 assert!(!acl.is_port_allowed(8443).is_allowed());
89 assert!(acl.is_port_allowed(8444).is_denied());
90 }
91
92 #[test]
93 fn ip_acl() {
94 let acl = HttpAclBuilder::new()
95 .clear_allowed_ip_ranges()
96 .add_allowed_ip_range("1.0.0.0/8".parse::<IpNet>().unwrap())
97 .unwrap()
98 .add_denied_ip_range("9.0.0.0/8".parse::<IpNet>().unwrap())
99 .unwrap()
100 .try_build()
101 .unwrap();
102
103 assert!(acl.is_ip_allowed(&"1.1.1.1".parse().unwrap()).is_allowed());
104 assert!(acl.is_ip_allowed(&"9.9.9.9".parse().unwrap()).is_denied());
105 assert!(
106 acl.is_ip_allowed(&"192.168.1.1".parse().unwrap())
107 .is_denied()
108 );
109 }
110
111 #[test]
112 fn non_global_ip_acl() {
113 let acl = HttpAclBuilder::new()
114 .non_global_ip_ranges(true)
115 .ip_acl_default(true)
116 .try_build()
117 .unwrap();
118
119 assert!(
120 acl.is_ip_allowed(&"192.168.1.1".parse().unwrap())
121 .is_allowed()
122 );
123 assert!(
124 acl.is_ip_allowed(&"203.0.113.12".parse().unwrap())
125 .is_allowed()
126 );
127
128 let acl = HttpAclBuilder::new()
129 .ip_acl_default(true)
130 .try_build()
131 .unwrap();
132
133 assert!(
134 acl.is_ip_allowed(&"192.168.1.1".parse().unwrap())
135 .is_denied()
136 );
137 assert!(
138 acl.is_ip_allowed(&"203.0.113.12".parse().unwrap())
139 .is_denied()
140 );
141 }
142
143 #[test]
144 fn default_ip_acl() {
145 let acl = HttpAclBuilder::new().try_build().unwrap();
146
147 assert!(
148 acl.is_ip_allowed(&"192.168.1.1".parse().unwrap())
149 .is_denied()
150 );
151 assert!(acl.is_ip_allowed(&"1.1.1.1".parse().unwrap()).is_denied());
152 assert!(!acl.is_port_allowed(8080).is_allowed());
153 }
154
155 #[test]
156 fn url_path_acl() {
157 let acl = HttpAclBuilder::new()
158 .add_allowed_url_path("/allowed".to_string())
159 .unwrap()
160 .add_allowed_url_path("/allowed/:id".to_string())
161 .unwrap()
162 .add_denied_url_path("/denied".to_string())
163 .unwrap()
164 .add_denied_url_path("/denied/{*path}".to_string())
165 .unwrap()
166 .try_build()
167 .unwrap();
168
169 assert!(acl.is_url_path_allowed("/allowed").is_allowed());
170 assert!(acl.is_url_path_allowed("/allowed/allowed").is_allowed());
171 assert!(acl.is_url_path_allowed("/denied").is_denied());
172 assert!(acl.is_url_path_allowed("/denied/denied").is_denied());
173 assert!(acl.is_url_path_allowed("/denied/denied/denied").is_denied());
174 }
175
176 #[test]
177 fn header_acl() {
178 let acl = HttpAclBuilder::new()
179 .add_allowed_header("X-Allowed".to_string(), Some("true".to_string()))
180 .unwrap()
181 .add_allowed_header("X-Allowed2".to_string(), None)
182 .unwrap()
183 .add_denied_header("X-Denied".to_string(), Some("true".to_string()))
184 .unwrap()
185 .add_denied_header("X-Denied2".to_string(), None)
186 .unwrap()
187 .try_build()
188 .unwrap();
189
190 assert!(acl.is_header_allowed("X-Allowed", "true").is_allowed());
191 assert!(acl.is_header_allowed("X-Allowed2", "false").is_allowed());
192 assert!(acl.is_header_allowed("X-Denied", "true").is_denied());
193 assert!(acl.is_header_allowed("X-Denied2", "false").is_denied());
194 }
195
196 #[test]
197 fn valid_acl() {
198 let acl = HttpAclBuilder::new()
199 .try_build_full(Some(Arc::new(|scheme, authority, headers, body| {
200 if scheme == "http" {
201 return AclClassification::DeniedUserAcl;
202 }
203
204 if authority.host.is_ip() {
205 return AclClassification::DeniedUserAcl;
206 }
207
208 for (header_name, header_value) in headers {
209 if header_name == "<dangerous-header>" && header_value == "<dangerous-value>" {
210 return AclClassification::DeniedUserAcl;
211 }
212 }
213
214 if let Some(body) = body
215 && body == b"<dangerous-body>"
216 {
217 return AclClassification::DeniedUserAcl;
218 }
219
220 AclClassification::AllowedDefault
221 })))
222 .unwrap();
223
224 assert!(
225 acl.is_valid(
226 "https",
227 &"example.com".into(),
228 [("<header>", "<value>")].into_iter(),
229 Some(b"body"),
230 )
231 .is_allowed()
232 );
233 assert!(
234 acl.is_valid(
235 "http",
236 &"example.com".into(),
237 [("<header>", "<value>")].into_iter(),
238 Some(b"body"),
239 )
240 .is_denied()
241 );
242 assert!(
243 acl.is_valid(
244 "https",
245 &"1.1.1.1".parse::<IpAddr>().unwrap().into(),
246 [("<header>", "<value>")].into_iter(),
247 Some(b"body"),
248 )
249 .is_denied()
250 );
251 assert!(
252 acl.is_valid(
253 "https",
254 &"example.com".into(),
255 [("<dangerous-header>", "<dangerous-value>")].into_iter(),
256 Some(b"body"),
257 )
258 .is_denied()
259 );
260 assert!(
261 acl.is_valid(
262 "https",
263 &"example.com".into(),
264 [("<header>", "<value>")].into_iter(),
265 Some(b"<dangerous-body>"),
266 )
267 .is_denied()
268 );
269 }
270}