narrowlink_types/
policy.rs

1use std::net::IpAddr;
2
3use regex_lite::Regex;
4use serde::{Deserialize, Serialize};
5use validator::{Validate, ValidationError, ValidationErrors};
6use wildmatch::WildMatch;
7
8use crate::generic::{Connect, Protocol};
9
10#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
11pub enum Target {
12    Any,
13    Agent(String),
14}
15
16#[derive(Debug, Serialize, Deserialize, Clone)]
17pub enum PolicyItem {
18    Domain(Target, String, u16, Protocol),
19    Ip(Target, ipnet::IpNet, u16, Protocol),
20}
21#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
22pub enum PolicyType {
23    BlackList,
24    WhiteList,
25}
26
27#[derive(Debug, Serialize, Deserialize, Clone)]
28pub struct Policy {
29    #[serde(rename = "type")]
30    pub policy_type: PolicyType,
31    pub policies: Vec<PolicyItem>,
32}
33
34impl Policy {
35    pub fn permit(&self, con: &Connect) -> bool {
36        let contains = self.policies.iter().any(|p| p.contains(con));
37        match self.policy_type {
38            PolicyType::BlackList => !contains,
39            PolicyType::WhiteList => contains,
40        }
41    }
42    pub fn is_agent_visible(&self, peer_agent_name: &str) -> bool {
43        self.policies.iter().any(|p| {
44            let target = match p {
45                PolicyItem::Domain(t, _, _, _) => t,
46                PolicyItem::Ip(t, _, _, _) => t,
47            };
48            target == &Target::Any || target == &Target::Agent(peer_agent_name.to_string())
49        })
50    }
51    pub fn retain_ip(&mut self) {
52        self.policies
53            .retain(|p| matches!(p, PolicyItem::Ip(_, _, _, _)));
54    }
55    pub fn agent_policies(&self, peer_agent_name: &str) -> Self {
56        Self {
57            policy_type: self.policy_type.clone(),
58            policies: self
59                .policies
60                .iter()
61                .filter(|p| {
62                    let target = match p {
63                        PolicyItem::Domain(t, _, _, _) => t,
64                        PolicyItem::Ip(t, _, _, _) => t,
65                    };
66                    target == &Target::Any || target == &Target::Agent(peer_agent_name.to_string())
67                })
68                .cloned()
69                .collect(),
70        }
71    }
72}
73
74impl Validate for Policy {
75    fn validate(&self) -> Result<(), ValidationErrors> {
76        for policy in self.policies.iter() {
77            policy.validate()?;
78        }
79        Ok(())
80    }
81}
82
83impl PolicyItem {
84    pub fn contains(&self, con: &Connect) -> bool {
85        let (address_status, policy_port, protocol) = match self {
86            Self::Domain(_, domain, policy_port, protocol) => (
87                // todo: check ip address of domain
88                WildMatch::new(domain).matches(&con.host),
89                policy_port,
90                protocol,
91            ),
92            Self::Ip(_, ip, port, protocol) => (
93                if let Ok(state) = con.host.parse::<IpAddr>().map(|addr| ip.contains(&addr)) {
94                    state
95                } else {
96                    false
97                },
98                port,
99                protocol,
100            ),
101        };
102
103        address_status
104            && (policy_port == &con.port || policy_port == &0)
105            && protocol == &con.protocol
106    }
107}
108
109impl Validate for PolicyItem {
110    fn validate(&self) -> Result<(), ValidationErrors> {
111        if let PolicyItem::Domain(_agent_id, addr, _port, _protocol) = self {
112            if let Ok(re) = Regex::new(r"^[^.][a-z0-9-.*?]{1,256}$") {
113                if re.is_match(addr) {
114                    return Ok(());
115                }
116            }
117            let mut err = ValidationErrors::new();
118            err.add("Domain", ValidationError::new("Invalid Domain Name"));
119            return Err(err);
120        }
121        Ok(())
122    }
123}