use std::net::IpAddr;
use super::IpNet;
pub(crate) enum AccessResult {
Allow,
Deny,
Restrict,
}
#[derive(Clone, Debug, Default)]
pub struct AccessControl {
allow_list: Option<Vec<IpNet>>,
deny_list: Option<Vec<IpNet>>,
}
impl AccessControl {
pub fn new(allow_list: Option<Vec<IpNet>>, deny_list: Option<Vec<IpNet>>) -> Self {
AccessControl {
allow_list,
deny_list,
}
}
pub fn set_allow_list(&mut self, list: Option<Vec<IpNet>>) {
self.allow_list = list;
}
pub fn set_deny_list(&mut self, list: Option<Vec<IpNet>>) {
self.deny_list = list;
}
pub(crate) fn check(&self, client_ip: &IpAddr) -> AccessResult {
if let Some(deny) = &self.deny_list
&& deny.iter().any(|net| net.contains(client_ip))
{
return AccessResult::Deny;
}
if let Some(allow) = &self.allow_list
&& !allow.iter().any(|net| net.contains(client_ip))
{
return AccessResult::Restrict;
}
AccessResult::Allow
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_access_no_lists() {
let ac = AccessControl::new(None, None);
assert!(matches!(
ac.check(&"1.2.3.4".parse().unwrap()),
AccessResult::Allow
));
}
#[test]
fn test_access_deny_list() {
let deny = vec![IpNet::new("10.0.0.0".parse().unwrap(), 8)];
let ac = AccessControl::new(None, Some(deny));
assert!(matches!(
ac.check(&"10.1.2.3".parse().unwrap()),
AccessResult::Deny
));
assert!(matches!(
ac.check(&"192.168.1.1".parse().unwrap()),
AccessResult::Allow
));
}
#[test]
fn test_access_allow_list() {
let allow = vec![IpNet::new("192.168.0.0".parse().unwrap(), 16)];
let ac = AccessControl::new(Some(allow), None);
assert!(matches!(
ac.check(&"192.168.1.1".parse().unwrap()),
AccessResult::Allow
));
assert!(matches!(
ac.check(&"10.0.0.1".parse().unwrap()),
AccessResult::Restrict
));
}
#[test]
fn test_access_deny_overrides_allow() {
let allow = vec![IpNet::new("10.0.0.0".parse().unwrap(), 8)];
let deny = vec![IpNet::new("10.0.0.1".parse().unwrap(), 32)];
let ac = AccessControl::new(Some(allow), Some(deny));
assert!(matches!(
ac.check(&"10.0.0.1".parse().unwrap()),
AccessResult::Deny
));
assert!(matches!(
ac.check(&"10.0.0.2".parse().unwrap()),
AccessResult::Allow
));
}
}