1#[cfg(all(feature = "runtime-async-std", feature = "ip"))]
2use async_std::net::IpAddr;
3
4#[cfg(all(feature = "runtime-tokio", feature = "ip"))]
5use std::net::IpAddr;
6
7#[cfg(feature = "glob")]
8use globset::GlobBuilder;
9#[cfg(feature = "ip")]
10use ip_network::IpNetwork;
11use once_cell::sync::Lazy;
12use regex::Regex;
13use rhai::Dynamic;
14
15static MAT_B: Lazy<Regex> = Lazy::new(|| Regex::new(r":[^/]*").unwrap());
16static MAT_P: Lazy<Regex> = Lazy::new(|| Regex::new(r"\{[^/]*\}").unwrap());
17
18use std::{borrow::Cow, collections::HashMap};
19
20#[derive(Clone, Copy)]
55pub enum OperatorFunction {
56 Arg0(fn() -> Dynamic),
57 Arg1(fn(Dynamic) -> Dynamic),
58 Arg2(fn(Dynamic, Dynamic) -> Dynamic),
59 Arg3(fn(Dynamic, Dynamic, Dynamic) -> Dynamic),
60 Arg4(fn(Dynamic, Dynamic, Dynamic, Dynamic) -> Dynamic),
61 Arg5(fn(Dynamic, Dynamic, Dynamic, Dynamic, Dynamic) -> Dynamic),
62 Arg6(fn(Dynamic, Dynamic, Dynamic, Dynamic, Dynamic, Dynamic) -> Dynamic),
63}
64
65pub struct FunctionMap {
66 pub(crate) fm: HashMap<String, OperatorFunction>,
67}
68
69impl Default for FunctionMap {
70 fn default() -> FunctionMap {
71 let mut fm: HashMap<String, OperatorFunction> = HashMap::new();
72 fm.insert(
73 "keyMatch".to_owned(),
74 OperatorFunction::Arg2(|s1: Dynamic, s2: Dynamic| {
75 key_match(&dynamic_to_str(&s1), &dynamic_to_str(&s2)).into()
76 }),
77 );
78 fm.insert(
79 "keyGet".to_owned(),
80 OperatorFunction::Arg2(|s1: Dynamic, s2: Dynamic| {
81 key_get(&dynamic_to_str(&s1), &dynamic_to_str(&s2)).into()
82 }),
83 );
84 fm.insert(
85 "keyMatch2".to_owned(),
86 OperatorFunction::Arg2(|s1: Dynamic, s2: Dynamic| {
87 key_match2(&dynamic_to_str(&s1), &dynamic_to_str(&s2)).into()
88 }),
89 );
90 fm.insert(
91 "keyGet2".to_owned(),
92 OperatorFunction::Arg3(|s1: Dynamic, s2: Dynamic, s3: Dynamic| {
93 key_get2(
94 &dynamic_to_str(&s1),
95 &dynamic_to_str(&s2),
96 &dynamic_to_str(&s3),
97 )
98 .into()
99 }),
100 );
101 fm.insert(
102 "keyMatch3".to_owned(),
103 OperatorFunction::Arg2(|s1: Dynamic, s2: Dynamic| {
104 key_match3(&dynamic_to_str(&s1), &dynamic_to_str(&s2)).into()
105 }),
106 );
107 fm.insert(
108 "keyGet3".to_owned(),
109 OperatorFunction::Arg3(|s1: Dynamic, s2: Dynamic, s3: Dynamic| {
110 key_get3(
111 &dynamic_to_str(&s1),
112 &dynamic_to_str(&s2),
113 &dynamic_to_str(&s3),
114 )
115 .into()
116 }),
117 );
118 fm.insert(
119 "keyMatch4".to_owned(),
120 OperatorFunction::Arg2(|s1: Dynamic, s2: Dynamic| {
121 key_match4(&dynamic_to_str(&s1), &dynamic_to_str(&s2)).into()
122 }),
123 );
124 fm.insert(
125 "keyMatch5".to_owned(),
126 OperatorFunction::Arg2(|s1: Dynamic, s2: Dynamic| {
127 key_match5(&dynamic_to_str(&s1), &dynamic_to_str(&s2)).into()
128 }),
129 );
130 fm.insert(
131 "regexMatch".to_owned(),
132 OperatorFunction::Arg2(|s1: Dynamic, s2: Dynamic| {
133 regex_match(&dynamic_to_str(&s1), &dynamic_to_str(&s2)).into()
134 }),
135 );
136
137 #[cfg(feature = "glob")]
138 fm.insert(
139 "globMatch".to_owned(),
140 OperatorFunction::Arg2(|s1: Dynamic, s2: Dynamic| {
141 glob_match(&dynamic_to_str(&s1), &dynamic_to_str(&s2)).into()
142 }),
143 );
144
145 #[cfg(feature = "ip")]
146 fm.insert(
147 "ipMatch".to_owned(),
148 OperatorFunction::Arg2(|s1: Dynamic, s2: Dynamic| {
149 ip_match(&dynamic_to_str(&s1), &dynamic_to_str(&s2)).into()
150 }),
151 );
152
153 FunctionMap { fm }
154 }
155}
156
157impl FunctionMap {
158 #[inline]
159 pub fn add_function(&mut self, fname: &str, f: OperatorFunction) {
160 self.fm.insert(fname.to_owned(), f);
161 }
162
163 #[inline]
164 pub fn get_functions(
165 &self,
166 ) -> impl Iterator<Item = (&String, &OperatorFunction)> {
167 self.fm.iter()
168 }
169}
170
171pub fn dynamic_to_str(d: &Dynamic) -> Cow<'_, str> {
192 if d.is_string() {
193 match d.clone().into_immutable_string() {
194 Ok(s) => Cow::Owned(s.to_string()),
195 Err(_) => Cow::Owned(d.to_string()),
196 }
197 } else {
198 Cow::Owned(d.to_string())
199 }
200}
201
202pub fn key_match(key1: &str, key2: &str) -> bool {
205 if let Some(i) = key2.find('*') {
206 if key1.len() > i {
207 return key1[..i] == key2[..i];
208 }
209 key1[..] == key2[..i]
210 } else {
211 key1 == key2
212 }
213}
214
215pub fn key_get(key1: &str, key2: &str) -> String {
219 if let Some(i) = key2.find('*') {
220 if key1.len() > i && key1[..i] == key2[..i] {
221 return key1[i..].to_string();
222 }
223 }
224 "".to_string()
225}
226
227pub fn key_match2(key1: &str, key2: &str) -> bool {
230 let mut key2: Cow<str> = if key2.contains("/*") {
231 key2.replace("/*", "/.*").into()
232 } else {
233 key2.into()
234 };
235
236 key2 = MAT_B.replace_all(&key2, "[^/]+").to_string().into();
237
238 regex_match(key1, &format!("^{}$", key2))
239}
240
241pub fn key_get2(key1: &str, key2: &str, path_var: &str) -> String {
245 let key2: Cow<str> = if key2.contains("/*") {
246 key2.replace("/*", "/.*").into()
247 } else {
248 key2.into()
249 };
250
251 let re = Regex::new(r":[^/]+").unwrap();
252 let keys: Vec<_> = re.find_iter(&key2).collect();
253 let key2 = re.replace_all(&key2, "([^/]+)").to_string();
254 let key2 = format!("^{}$", key2);
255
256 if let Ok(re2) = Regex::new(&key2) {
257 if let Some(caps) = re2.captures(key1) {
258 for (i, key) in keys.iter().enumerate() {
259 if path_var == &key.as_str()[1..] {
260 return caps
261 .get(i + 1)
262 .map_or("".to_string(), |m| m.as_str().to_string());
263 }
264 }
265 }
266 }
267 "".to_string()
268}
269
270pub fn key_match3(key1: &str, key2: &str) -> bool {
273 let mut key2: Cow<str> = if key2.contains("/*") {
274 key2.replace("/*", "/.*").into()
275 } else {
276 key2.into()
277 };
278
279 key2 = MAT_P.replace_all(&key2, "[^/]+").to_string().into();
280
281 regex_match(key1, &format!("^{}$", key2))
282}
283
284pub fn key_get3(key1: &str, key2: &str, path_var: &str) -> String {
288 let key2: Cow<str> = if key2.contains("/*") {
289 key2.replace("/*", "/.*").into()
290 } else {
291 key2.into()
292 };
293
294 let re = Regex::new(r"\{[^/]+?\}").unwrap();
295 let keys: Vec<_> = re.find_iter(&key2).collect();
296 let key2 = re.replace_all(&key2, "([^/]+?)").to_string();
297 let key2 = Regex::new(r"\{")
298 .unwrap()
299 .replace_all(&key2, "\\{")
300 .to_string();
301 let key2 = format!("^{}$", key2);
302
303 let re2 = Regex::new(&key2).unwrap();
304 if let Some(caps) = re2.captures(key1) {
305 for (i, key) in keys.iter().enumerate() {
306 if path_var == &key.as_str()[1..key.as_str().len() - 1] {
307 return caps
308 .get(i + 1)
309 .map_or("".to_string(), |m| m.as_str().to_string());
310 }
311 }
312 }
313 "".to_string()
314}
315
316pub fn key_match4(key1: &str, key2: &str) -> bool {
323 let mut key2 = key2.replace("/*", "/.*");
324 let mut tokens = Vec::new();
325
326 let re = Regex::new(r"\{[^/]+?\}").unwrap();
327 key2 = re
328 .replace_all(&key2, |caps: ®ex::Captures| {
329 tokens.push(caps[0][1..caps[0].len() - 1].to_string());
330 "([^/]+)".to_string()
331 })
332 .to_string();
333
334 let re = match Regex::new(&format!("^{}$", key2)) {
335 Ok(re) => re,
336 Err(_) => return false,
337 };
338 if let Some(caps) = re.captures(key1) {
339 let matches: Vec<_> =
340 caps.iter().skip(1).map(|m| m.unwrap().as_str()).collect();
341 if tokens.len() != matches.len() {
342 panic!(
343 "KeyMatch4: number of tokens is not equal to number of values"
344 );
345 }
346
347 let mut values = HashMap::new();
348 for (token, value) in tokens.iter().zip(matches.iter()) {
349 if let Some(existing_value) = values.get(token) {
350 if *existing_value != value {
351 return false;
352 }
353 } else {
354 values.insert(token, value);
355 }
356 }
357 true
358 } else {
359 false
360 }
361}
362
363pub fn key_match5(key1: &str, key2: &str) -> bool {
369 let key1 = if let Some(i) = key1.find('?') {
370 &key1[..i]
371 } else {
372 key1
373 };
374
375 let key2 = key2.replace("/*", "/.*");
376 let key2 = Regex::new(r"(\{[^/]+?\})")
377 .unwrap()
378 .replace_all(&key2, "[^/]+");
379
380 regex_match(key1, &format!("^{}$", key2))
381}
382
383pub fn regex_match(key1: &str, key2: &str) -> bool {
385 Regex::new(key2).unwrap().is_match(key1)
386}
387
388#[cfg(feature = "ip")]
391pub fn ip_match(key1: &str, key2: &str) -> bool {
392 let key2_split = key2.splitn(2, '/').collect::<Vec<&str>>();
393 let ip_addr2 = key2_split[0];
394
395 if let (Ok(ip_addr1), Ok(ip_addr2)) =
396 (key1.parse::<IpAddr>(), ip_addr2.parse::<IpAddr>())
397 {
398 if key2_split.len() == 2 {
399 match key2_split[1].parse::<u8>() {
400 Ok(ip_netmask) => {
401 match IpNetwork::new_truncate(ip_addr2, ip_netmask) {
402 Ok(ip_network) => ip_network.contains(ip_addr1),
403 Err(err) => panic!("invalid ip network {}", err),
404 }
405 }
406 _ => panic!("invalid netmask {}", key2_split[1]),
407 }
408 } else {
409 if let (IpAddr::V4(ip_addr1_new), IpAddr::V6(ip_addr2_new)) =
410 (ip_addr1, ip_addr2)
411 {
412 if let Some(ip_addr2_new) = ip_addr2_new.to_ipv4() {
413 return ip_addr2_new == ip_addr1_new;
414 }
415 }
416
417 ip_addr1 == ip_addr2
418 }
419 } else {
420 panic!("invalid argument {} {}", key1, key2)
421 }
422}
423
424#[cfg(feature = "glob")]
426pub fn glob_match(key1: &str, key2: &str) -> bool {
427 GlobBuilder::new(key2)
428 .literal_separator(true)
429 .build()
430 .unwrap()
431 .compile_matcher()
432 .is_match(key1)
433}
434
435#[cfg(test)]
436mod tests {
437 use super::*;
438
439 #[test]
440 fn test_key_match() {
441 assert!(key_match("/foo/bar", "/foo/*"));
442 assert!(!key_match("/bar/foo", "/foo/*"));
443 assert!(key_match("/bar", "/ba*"));
444 }
445
446 #[test]
447 fn test_key_get() {
448 assert_eq!(key_get("/foo", "/foo"), "");
449 assert_eq!(key_get("/foo", "/foo*"), "");
450 assert_eq!(key_get("/foo", "/foo/*"), "");
451 assert_eq!(key_get("/foo/bar", "/foo"), "");
452 assert_eq!(key_get("/foo/bar", "/foo*"), "/bar");
453 assert_eq!(key_get("/foo/bar", "/foo/*"), "bar");
454 assert_eq!(key_get("/foobar", "/foo"), "");
455 assert_eq!(key_get("/foobar", "/foo*"), "bar");
456 assert_eq!(key_get("/foobar", "/foo/*"), "");
457 }
458
459 #[test]
460 fn test_key_match2() {
461 assert!(key_match2("/foo/bar", "/foo/*"));
462 assert!(key_match2("/foo/bar/baz", "/foo/*"));
463 assert!(key_match2("/foo/baz", "/foo/:bar"));
464 assert!(key_match2("/foo/baz", "/:foo/:bar"));
465 assert!(key_match2("/foo/baz/foo", "/foo/:bar/foo"));
466 assert!(!key_match2("/baz", "/foo"));
467
468 assert!(key_match2("/foo/bar", "/foo/:"));
470 assert!(!key_match2("/foo/bar/baz", "/foo/:"));
471 assert!(key_match2("/foo/bar/baz", "/foo/:/baz"));
472 assert!(!key_match2("/foo/bar", "/foo/:/baz"));
473 }
474
475 #[test]
476 fn test_key_get2() {
477 assert_eq!(key_get2("/foo", "/foo", "id"), "");
478 assert_eq!(key_get2("/foo", "/foo*", "id"), "");
479 assert_eq!(key_get2("/foo", "/foo/*", "id"), "");
480 assert_eq!(key_get2("/foo/bar", "/foo", "id"), "");
481 assert_eq!(key_get2("/foo/bar", "/foo*", "id"), "");
482 assert_eq!(key_get2("/foo/bar", "/foo/*", "id"), "");
483 assert_eq!(key_get2("/foobar", "/foo", "id"), "");
484 assert_eq!(key_get2("/foobar", "/foo*", "id"), "");
485 assert_eq!(key_get2("/foobar", "/foo/*", "id"), "");
486
487 assert_eq!(key_get2("/", "/:resource", "resource"), "");
488 assert_eq!(
489 key_get2("/resource1", "/:resource", "resource"),
490 "resource1"
491 );
492 assert_eq!(key_get2("/myid", "/:id/using/:resId", "id"), "");
493 assert_eq!(
494 key_get2("/myid/using/myresid", "/:id/using/:resId", "id"),
495 "myid"
496 );
497 assert_eq!(
498 key_get2("/myid/using/myresid", "/:id/using/:resId", "resId"),
499 "myresid"
500 );
501
502 assert_eq!(key_get2("/proxy/myid", "/proxy/:id/*", "id"), "");
503 assert_eq!(key_get2("/proxy/myid/", "/proxy/:id/*", "id"), "myid");
504 assert_eq!(key_get2("/proxy/myid/res", "/proxy/:id/*", "id"), "myid");
505 assert_eq!(
506 key_get2("/proxy/myid/res/res2", "/proxy/:id/*", "id"),
507 "myid"
508 );
509 assert_eq!(
510 key_get2("/proxy/myid/res/res2/res3", "/proxy/:id/*", "id"),
511 "myid"
512 );
513 assert_eq!(
514 key_get2("/proxy/myid/res/res2/res3", "/proxy/:id/res/*", "id"),
515 "myid"
516 );
517 assert_eq!(key_get2("/proxy/", "/proxy/:id/*", "id"), "");
518
519 assert_eq!(key_get2("/alice", "/:id", "id"), "alice");
520 assert_eq!(key_get2("/alice/all", "/:id/all", "id"), "alice");
521 assert_eq!(key_get2("/alice", "/:id/all", "id"), "");
522 assert_eq!(key_get2("/alice/all", "/:id", "id"), "");
523
524 assert_eq!(key_get2("/alice/all", "/:/all", ""), "");
525 }
526
527 #[test]
528 fn test_regex_match() {
529 assert!(regex_match("foobar", "^foo*"));
530 assert!(!regex_match("barfoo", "^foo*"));
531 }
532
533 #[test]
534 fn test_key_match3() {
535 assert!(key_match3("/foo/bar", "/foo/*"));
536 assert!(key_match3("/foo/bar/baz", "/foo/*"));
537 assert!(key_match3("/foo/baz", "/foo/{bar}"));
538 assert!(key_match3("/foo/baz/foo", "/foo/{bar}/foo"));
539 assert!(!key_match3("/baz", "/foo"));
540
541 assert!(key_match3("/foo/bar", "/foo/{}"));
543 assert!(key_match3("/foo/{}", "/foo/{}"));
544 assert!(!key_match3("/foo/bar/baz", "/foo/{}"));
545 assert!(!key_match3("/foo/bar", "/foo/{}/baz"));
546 assert!(key_match3("/foo/bar/baz", "/foo/{}/baz"));
547 }
548
549 #[test]
550 fn test_key_get3() {
551 assert_eq!(key_get3("/foo", "/foo", "id"), "");
552 assert_eq!(key_get3("/foo", "/foo*", "id"), "");
553 assert_eq!(key_get3("/foo", "/foo/*", "id"), "");
554 assert_eq!(key_get3("/foo/bar", "/foo", "id"), "");
555 assert_eq!(key_get3("/foo/bar", "/foo*", "id"), "");
556 assert_eq!(key_get3("/foo/bar", "/foo/*", "id"), "");
557 assert_eq!(key_get3("/foobar", "/foo", "id"), "");
558 assert_eq!(key_get3("/foobar", "/foo*", "id"), "");
559 assert_eq!(key_get3("/foobar", "/foo/*", "id"), "");
560
561 assert_eq!(key_get3("/", "/{resource}", "resource"), "");
562 assert_eq!(
563 key_get3("/resource1", "/{resource}", "resource"),
564 "resource1"
565 );
566 assert_eq!(key_get3("/myid", "/{id}/using/{resId}", "id"), "");
567 assert_eq!(
568 key_get3("/myid/using/myresid", "/{id}/using/{resId}", "id"),
569 "myid"
570 );
571 assert_eq!(
572 key_get3("/myid/using/myresid", "/{id}/using/{resId}", "resId"),
573 "myresid"
574 );
575
576 assert_eq!(key_get3("/proxy/myid", "/proxy/{id}/*", "id"), "");
577 assert_eq!(key_get3("/proxy/myid/", "/proxy/{id}/*", "id"), "myid");
578 assert_eq!(key_get3("/proxy/myid/res", "/proxy/{id}/*", "id"), "myid");
579 assert_eq!(
580 key_get3("/proxy/myid/res/res2", "/proxy/{id}/*", "id"),
581 "myid"
582 );
583 assert_eq!(
584 key_get3("/proxy/myid/res/res2/res3", "/proxy/{id}/*", "id"),
585 "myid"
586 );
587 assert_eq!(
588 key_get3("/proxy/myid/res/res2/res3", "/proxy/{id}/res/*", "id"),
589 "myid"
590 );
591 assert_eq!(key_get3("/proxy/", "/proxy/{id}/*", "id"), "");
592
593 assert_eq!(
594 key_get3(
595 "/api/group1_group_name/project1_admin/info",
596 "/api/{proj}_admin/info",
597 "proj"
598 ),
599 ""
600 );
601 assert_eq!(
602 key_get3("/{id/using/myresid", "/{id/using/{resId}", "resId"),
603 "myresid"
604 );
605 assert_eq!(
606 key_get3(
607 "/{id/using/myresid/status}",
608 "/{id/using/{resId}/status}",
609 "resId"
610 ),
611 "myresid"
612 );
613
614 assert_eq!(
615 key_get3("/proxy/myid/res/res2/res3", "/proxy/{id}/*/{res}", "res"),
616 "res3"
617 );
618 assert_eq!(
619 key_get3(
620 "/api/project1_admin/info",
621 "/api/{proj}_admin/info",
622 "proj"
623 ),
624 "project1"
625 );
626 assert_eq!(
627 key_get3(
628 "/api/group1_group_name/project1_admin/info",
629 "/api/{g}_{gn}/{proj}_admin/info",
630 "g"
631 ),
632 "group1"
633 );
634 assert_eq!(
635 key_get3(
636 "/api/group1_group_name/project1_admin/info",
637 "/api/{g}_{gn}/{proj}_admin/info",
638 "gn"
639 ),
640 "group_name"
641 );
642 assert_eq!(
643 key_get3(
644 "/api/group1_group_name/project1_admin/info",
645 "/api/{g}_{gn}/{proj}_admin/info",
646 "proj"
647 ),
648 "project1"
649 );
650 }
651
652 #[test]
653 fn test_key_match4() {
654 assert!(key_match4(
655 "/parent/123/child/123",
656 "/parent/{id}/child/{id}"
657 ));
658 assert!(!key_match4(
659 "/parent/123/child/456",
660 "/parent/{id}/child/{id}"
661 ));
662 assert!(key_match4(
663 "/parent/123/child/123",
664 "/parent/{id}/child/{another_id}"
665 ));
666 assert!(key_match4(
667 "/parent/123/child/456",
668 "/parent/{id}/child/{another_id}"
669 ));
670 assert!(key_match4(
671 "/parent/123/child/123",
672 "/parent/{id}/child/{id}"
673 ));
674 assert!(!key_match4(
675 "/parent/123/child/456",
676 "/parent/{id}/child/{id}"
677 ));
678 assert!(key_match4(
679 "/parent/123/child/123",
680 "/parent/{id}/child/{another_id}"
681 ));
682 assert!(key_match4(
683 "/parent/123/child/123/book/123",
684 "/parent/{id}/child/{id}/book/{id}"
685 ));
686 assert!(!key_match4(
687 "/parent/123/child/123/book/456",
688 "/parent/{id}/child/{id}/book/{id}"
689 ));
690 assert!(!key_match4(
691 "/parent/123/child/456/book/123",
692 "/parent/{id}/child/{id}/book/{id}"
693 ));
694 assert!(!key_match4(
695 "/parent/123/child/456/book/",
696 "/parent/{id}/child/{id}/book/{id}"
697 ));
698 assert!(!key_match4(
699 "/parent/123/child/456",
700 "/parent/{id}/child/{id}/book/{id}"
701 ));
702 assert!(!key_match4(
703 "/parent/123/child/123",
704 "/parent/{i/d}/child/{i/d}"
705 ));
706 }
707
708 #[test]
709 fn test_key_match5() {
710 assert!(key_match5("/foo/bar?status=1&type=2", "/foo/bar"));
711 assert!(key_match5("/parent/child1", "/parent/*"));
712 assert!(key_match5("/parent/child1?status=1", "/parent/*"));
713 assert!(key_match5("/parent/child1?status=1", "/parent/child1"));
714 }
715
716 #[cfg(feature = "ip")]
717 #[test]
718 fn test_ip_match() {
719 assert!(ip_match("::1", "::0:1"));
720 assert!(ip_match("192.168.1.1", "192.168.1.1"));
721 assert!(ip_match("127.0.0.1", "::ffff:127.0.0.1"));
722 assert!(ip_match("192.168.2.123", "192.168.2.0/24"));
723 assert!(!ip_match("::1", "127.0.0.2"));
724 assert!(!ip_match("192.168.2.189", "192.168.1.134/26"));
725 }
726
727 #[cfg(feature = "ip")]
728 #[test]
729 #[should_panic]
730 fn test_ip_match_panic_1() {
731 assert!(ip_match("I am alice", "127.0.0.1"));
732 }
733
734 #[cfg(feature = "ip")]
735 #[test]
736 #[should_panic]
737 fn test_ip_match_panic_2() {
738 assert!(ip_match("127.0.0.1", "I am alice"));
739 }
740
741 #[cfg(feature = "glob")]
742 #[test]
743 fn test_glob_match() {
744 assert!(glob_match("/abc/123", "/abc/*"));
745 assert!(!glob_match("/abc/123/456", "/abc/*"));
746 assert!(glob_match("/abc/123/456", "/abc/**"));
747 }
748}