http_acl/
lib.rs

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}