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 private_ip_acl() {
113 let acl = HttpAclBuilder::new()
114 .private_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 }
124
125 #[test]
126 fn default_ip_acl() {
127 let acl = HttpAclBuilder::new().try_build().unwrap();
128
129 assert!(
130 acl.is_ip_allowed(&"192.168.1.1".parse().unwrap())
131 .is_denied()
132 );
133 assert!(acl.is_ip_allowed(&"1.1.1.1".parse().unwrap()).is_denied());
134 assert!(!acl.is_port_allowed(8080).is_allowed());
135 }
136
137 #[test]
138 fn url_path_acl() {
139 let acl = HttpAclBuilder::new()
140 .add_allowed_url_path("/allowed".to_string())
141 .unwrap()
142 .add_allowed_url_path("/allowed/:id".to_string())
143 .unwrap()
144 .add_denied_url_path("/denied".to_string())
145 .unwrap()
146 .add_denied_url_path("/denied/{*path}".to_string())
147 .unwrap()
148 .try_build()
149 .unwrap();
150
151 assert!(acl.is_url_path_allowed("/allowed").is_allowed());
152 assert!(acl.is_url_path_allowed("/allowed/allowed").is_allowed());
153 assert!(acl.is_url_path_allowed("/denied").is_denied());
154 assert!(acl.is_url_path_allowed("/denied/denied").is_denied());
155 assert!(acl.is_url_path_allowed("/denied/denied/denied").is_denied());
156 }
157
158 #[test]
159 fn header_acl() {
160 let acl = HttpAclBuilder::new()
161 .add_allowed_header("X-Allowed".to_string(), Some("true".to_string()))
162 .unwrap()
163 .add_allowed_header("X-Allowed2".to_string(), None)
164 .unwrap()
165 .add_denied_header("X-Denied".to_string(), Some("true".to_string()))
166 .unwrap()
167 .add_denied_header("X-Denied2".to_string(), None)
168 .unwrap()
169 .try_build()
170 .unwrap();
171
172 assert!(acl.is_header_allowed("X-Allowed", "true").is_allowed());
173 assert!(acl.is_header_allowed("X-Allowed2", "false").is_allowed());
174 assert!(acl.is_header_allowed("X-Denied", "true").is_denied());
175 assert!(acl.is_header_allowed("X-Denied2", "false").is_denied());
176 }
177
178 #[test]
179 fn valid_acl() {
180 let acl = HttpAclBuilder::new()
181 .try_build_full(Some(Arc::new(|scheme, authority, headers, body| {
182 if scheme == "http" {
183 return AclClassification::DeniedUserAcl;
184 }
185
186 if authority.host.is_ip() {
187 return AclClassification::DeniedUserAcl;
188 }
189
190 for (header_name, header_value) in headers {
191 if header_name == "<dangerous-header>" && header_value == "<dangerous-value>" {
192 return AclClassification::DeniedUserAcl;
193 }
194 }
195
196 if let Some(body) = body {
197 if body == b"<dangerous-body>" {
198 return AclClassification::DeniedUserAcl;
199 }
200 }
201
202 AclClassification::AllowedDefault
203 })))
204 .unwrap();
205
206 assert!(
207 acl.is_valid(
208 "https",
209 &"example.com".into(),
210 [("<header>", "<value>")].into_iter(),
211 Some(b"body"),
212 )
213 .is_allowed()
214 );
215 assert!(
216 acl.is_valid(
217 "http",
218 &"example.com".into(),
219 [("<header>", "<value>")].into_iter(),
220 Some(b"body"),
221 )
222 .is_denied()
223 );
224 assert!(
225 acl.is_valid(
226 "https",
227 &"1.1.1.1".parse::<IpAddr>().unwrap().into(),
228 [("<header>", "<value>")].into_iter(),
229 Some(b"body"),
230 )
231 .is_denied()
232 );
233 assert!(
234 acl.is_valid(
235 "https",
236 &"example.com".into(),
237 [("<dangerous-header>", "<dangerous-value>")].into_iter(),
238 Some(b"body"),
239 )
240 .is_denied()
241 );
242 assert!(
243 acl.is_valid(
244 "https",
245 &"example.com".into(),
246 [("<header>", "<value>")].into_iter(),
247 Some(b"<dangerous-body>"),
248 )
249 .is_denied()
250 );
251 }
252}